From de8081c0a760299633d0f3e1231de99994b86838 Mon Sep 17 00:00:00 2001 From: Ahmed Harmouche Date: Sat, 28 Dec 2024 17:33:40 +0100 Subject: [PATCH] Improved error handling --- pydawn/utils.py | 73 ++++++++++++++++++++++++++++++---------------- test/test_utils.py | 19 ++++++++++++ 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/pydawn/utils.py b/pydawn/utils.py index 4024a68..42224de 100644 --- a/pydawn/utils.py +++ b/pydawn/utils.py @@ -3,6 +3,8 @@ class ResultContainer: def __init__(self): + self.msg = None + self.status = None self.value = None instDesc = webgpu.WGPUInstanceDescriptor() @@ -28,20 +30,27 @@ def wait(future): status = webgpu.wgpuInstanceWaitAny(instance, 1, info, 2**64 - 1) assert status == webgpu.WGPUWaitStatus_Success, f"Future failed" -def request_adapter_sync(power_preference): +def request_adapter_sync(power_preference, backend_type = webgpu.WGPUBackendType_Metal): cb_info = webgpu.WGPURequestAdapterCallbackInfo() cb_info.nextInChain = None cb_info.mode = webgpu.WGPUCallbackMode_WaitAnyOnly - container = ResultContainer() + result = ResultContainer() - def cb(status, adapter, string, _): - container.value = adapter + def cb(status, adapter, msg, _): + result.status = status + result.value = adapter + result.msg = from_wgpu_str(msg) cb_info.callback = webgpu.WGPURequestAdapterCallback(cb) adapterOptions = webgpu.WGPURequestAdapterOptions() adapterOptions.powerPreference = power_preference + adapterOptions.backendType = backend_type wait(webgpu.wgpuInstanceRequestAdapterF(instance, adapterOptions, cb_info)) - return container.value + + if result.status != webgpu.WGPURequestAdapterStatus_Success: + raise RuntimeError(f"Error requesting adapter: [{webgpu.WGPURequestAdapterStatus__enumvalues[result.status]}] {result.msg}") + + return result.value def request_device_sync(adapter, required_features): assert adapter != None, "adapter should not be none" @@ -77,13 +86,17 @@ def request_device_sync(adapter, required_features): cb_info.mode = webgpu.WGPUCallbackMode_WaitAnyOnly result = ResultContainer() - def cb(status, deviceImpl, i1, i2): - assert status == webgpu.WGPURequestDeviceStatus_Success, f"Failed to request device={webgpu.WGPURequestDeviceStatus__enumvalues[status]}" - result.value = deviceImpl - + def cb(status, device_impl, msg, _): + result.status = status + result.value = device_impl + result.msg = from_wgpu_str(msg) + cb_info.callback = webgpu.WGPURequestDeviceCallback(cb) wait(webgpu.wgpuAdapterRequestDeviceF(adapter, device_desc, cb_info)) + if result.status != webgpu.WGPURequestDeviceStatus_Success: + raise RuntimeError(f"Failed to request device: [{webgpu.WGPURequestDeviceStatus__enumvalues[result.status]}] {result.msg}") + return result.value def create_buffer(device, size, usage): @@ -102,15 +115,18 @@ def map_buffer(buf, size): cb_info = webgpu.WGPUBufferMapCallbackInfo2() cb_info.nextInChain = None cb_info.mode = webgpu.WGPUCallbackMode_WaitAnyOnly + result = ResultContainer() - def cb(status, str, i1, i2): - assert status == webgpu.WGPUBufferMapAsyncStatus_Success, f"Failed to map buffer: {webgpu.WGPUBufferMapAsyncStatus__enumvalues[status]}" - if status != webgpu.WGPUBufferMapAsyncStatus_Success: - print(f"msg={from_wgpu_str(str)}") + def cb(status, msg, u1, u2): + result.status = status + result.msg = from_wgpu_str(msg) cb_info.callback = webgpu.WGPUBufferMapCallback2(cb) wait(webgpu.wgpuBufferMapAsync2(buf, webgpu.WGPUMapMode_Read, 0, size, cb_info)) + if result.status != webgpu.WGPUBufferMapAsyncStatus_Success: + raise RuntimeError(f"Failed to map buffer: [{webgpu.WGPUBufferMapAsyncStatus__enumvalues[result.status]}] {result.msg}") + def copy_buffer_to_buffer(cmd_encoder, src, src_offset, dst, dst_offset, size): webgpu.wgpuCommandEncoderCopyBufferToBuffer(cmd_encoder, src, src_offset, dst, dst_offset, size) @@ -146,7 +162,7 @@ def create_shader_module(device, source): # Check compiler error webgpu.wgpuDevicePushErrorScope(device, webgpu.WGPUErrorFilter_Validation) shader_module = webgpu.wgpuDeviceCreateShaderModule(device, module) - compiler_error = get_error(device) + compiler_error = pop_error(device) if compiler_error: raise RuntimeError(f"Shader compilation failed: {compiler_error}") @@ -213,20 +229,23 @@ def create_compute_pipeline(device, layout, compute): cb_info = webgpu.WGPUCreateComputePipelineAsyncCallbackInfo2() cb_info.nextInChain = None cb_info.mode = webgpu.WGPUCallbackMode_WaitAnyOnly - result_container = ResultContainer() + result = ResultContainer() - def cb(status, compute_pipeline_impl, msg, i1, i2): - if status != webgpu.WGPUCreatePipelineAsyncStatus_Success: - print(f"error={from_wgpu_str(msg)}") - assert status == webgpu.WGPUCreatePipelineAsyncStatus_Success, f"Validation error={webgpu.WGPUCreatePipelineAsyncStatus__enumvalues[status]}" - result_container.value = compute_pipeline_impl + def cb(status, compute_pipeline_impl, msg, u1, u2): + result.status = status + result.msg = from_wgpu_str(msg) + result.value = compute_pipeline_impl cb_info.callback = webgpu.WGPUCreateComputePipelineAsyncCallback2(cb) webgpu.wgpuDevicePushErrorScope(device, webgpu.WGPUErrorFilter_Validation) wait(webgpu.wgpuDeviceCreateComputePipelineAsync2(device, compute_desc, cb_info)) - get_error(device) - return result_container.value + maybe_error = pop_error(device) + + if result.status != webgpu.WGPUCreatePipelineAsyncStatus_Success: + raise RuntimeError(f"Error creating pipeline: [{webgpu.WGPUCreatePipelineAsyncStatus__enumvalues[result.status]}] {result.msg}, {maybe_error}") + + return result.value def create_command_encoder(device): encoder_desc = webgpu.WGPUCommandEncoderDescriptor() @@ -287,14 +306,18 @@ def sync(device): cb_info = webgpu.WGPUQueueWorkDoneCallbackInfo2() cb_info.nextInChain = None cb_info.mode = webgpu.WGPUCallbackMode_WaitAnyOnly + result = ResultContainer() - def cb(status, i1, i2): - assert status == webgpu.WGPUQueueWorkDoneStatus_Success, f"Submitted work failed: {webgpu.WGPUQueueWorkDoneStatus__enumvalues[status]}" + def cb(status, u1, u2): + result.status = status cb_info.callback = webgpu.WGPUQueueWorkDoneCallback2(cb) wait(webgpu.wgpuQueueOnSubmittedWorkDone2(webgpu.wgpuDeviceGetQueue(device), cb_info)) -def get_error(device): + if result.status != webgpu.WGPUQueueWorkDoneStatus_Success: + raise RuntimeError(f"Submitted work failed: [{webgpu.WGPUQueueWorkDoneStatus__enumvalues[result.status]}]") + +def pop_error(device): cb_info = webgpu.WGPUPopErrorScopeCallbackInfo() cb_info.nextInChain = None cb_info.mode = webgpu.WGPUCallbackMode_WaitAnyOnly diff --git a/test/test_utils.py b/test/test_utils.py index 3aad1be..fe018e2 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -58,5 +58,24 @@ def test_compiler_error(self): utils.create_shader_module(self.device, error_shader_source) self.assertIn("unresolved type 'f15'", str(ctx.exception)) + def test_request_adapter_error(self): + with self.assertRaises(RuntimeError) as ctx: + utils.request_adapter_sync(webgpu.WGPUPowerPreference_HighPerformance, webgpu.WGPUBackendType_Vulkan) + + self.assertIn("No supported adapters", str(ctx.exception)) + + def test_request_device_error(self): + with self.assertRaises(RuntimeError) as ctx: + utils.request_device_sync(self.adapter, [webgpu.WGPUFeatureName_D3D11MultithreadProtected]) + + self.assertIn("Invalid feature required", str(ctx.exception)) + + def test_map_buffer_error(self): + with self.assertRaises(RuntimeError) as ctx: + buf = utils.create_buffer(self.device, 4, webgpu.WGPUBufferUsage_Storage | webgpu.WGPUBufferUsage_MapRead | webgpu.WGPUBufferUsage_MapWrite) + utils.map_buffer(buf, 4) + + self.assertIn("Failed to map buffer", str(ctx.exception)) + if __name__ == "__main__": unittest.main()