From 97ee3c1134084e01974c71f83da3c38764879351 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 1 Jan 2024 16:43:46 +0300 Subject: [PATCH] WebGPU wrapper wip: add device methods --- libs/wgpu/include/psemek/wgpu/device.hpp | 42 +++ .../psemek/wgpu/render_bundle_encoder.hpp | 6 +- libs/wgpu/objects-todo | 2 +- libs/wgpu/source/device.cpp | 351 +++++++++++++----- 4 files changed, 296 insertions(+), 105 deletions(-) diff --git a/libs/wgpu/include/psemek/wgpu/device.hpp b/libs/wgpu/include/psemek/wgpu/device.hpp index a294f7af..e91258c8 100644 --- a/libs/wgpu/include/psemek/wgpu/device.hpp +++ b/libs/wgpu/include/psemek/wgpu/device.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +74,33 @@ namespace psemek::wgpu std::uint32_t max_compute_workgroups_per_dimension; }; + enum class create_pipeline_async_status : std::uint32_t + { + success = 0x00000000, + validation_error = 0x00000001, + internal_error = 0x00000002, + device_lost = 0x00000003, + device_destroyed = 0x00000004, + unknown = 0x00000005, + }; + + enum class error_filter : std::uint32_t + { + validation = 0x00000000, + out_of_memory = 0x00000001, + internal = 0x00000002, + }; + + enum class error_type : std::uint32_t + { + no_error = 0x00000000, + validation = 0x00000001, + out_of_memory = 0x00000002, + internal = 0x00000003, + unknown = 0x00000004, + device_lost = 0x00000005, + }; + struct device : detail::object { @@ -110,6 +138,9 @@ namespace psemek::wgpu }; using request_callback = std::function; + using create_compute_pipeline_async_callback = std::function; + using create_render_pipeline_async_callback = std::function; + using error_callback = std::function; queue get_queue(); bind_group create_bind_group(bind_group::descriptor const & desc); @@ -117,12 +148,23 @@ namespace psemek::wgpu buffer create_buffer(buffer::descriptor const & desc); command_encoder create_command_encoder(command_encoder::descriptor const & desc); compute_pipeline create_compute_pipeline(compute_pipeline::descriptor const & desc); + void create_compute_pipeline_async(compute_pipeline::descriptor const & desc, create_compute_pipeline_async_callback const & callback); pipeline_layout create_pipeline_layout(pipeline_layout::descriptor const & desc); query_set create_query_set(query_set::descriptor const & desc); render_pipeline create_render_pipeline(render_pipeline::descriptor const & desc); + void create_render_pipeline_async(render_pipeline::descriptor const & desc, create_render_pipeline_async_callback const & callback); + render_bundle_encoder create_render_bundle_encoder(render_bundle_encoder::descriptor const & desc); sampler create_sampler(sampler::descriptor const & desc); shader_module create_shader_module(shader_module::descriptor const & desc); texture create_texture(texture::descriptor const & desc); + void destroy(); + std::vector enumerate_features(); + limits get_limits(); + bool has_feature(feature feature); + void push_error_scope(error_filter filter); + void pop_error_scope(error_callback const & callback); + void set_uncaptured_error_callback(error_callback const & callback); + void set_label(std::string const & label); static void reference(void * ptr); static void release(void * ptr); diff --git a/libs/wgpu/include/psemek/wgpu/render_bundle_encoder.hpp b/libs/wgpu/include/psemek/wgpu/render_bundle_encoder.hpp index e5b2d3d5..0d931df7 100644 --- a/libs/wgpu/include/psemek/wgpu/render_bundle_encoder.hpp +++ b/libs/wgpu/include/psemek/wgpu/render_bundle_encoder.hpp @@ -26,8 +26,8 @@ namespace psemek::wgpu std::vector color_formats; texture::format depth_stencil_format; std::uint32_t sample_count = 1; - bool depthReadOnly = false; - bool stencilReadOnly = true; + bool depth_read_only = false; + bool stencil_read_only = true; }; void set_pipeline(render_pipeline const & pipeline); @@ -51,6 +51,8 @@ namespace psemek::wgpu explicit render_bundle_encoder(void * ptr) : detail::object(ptr) {} + + friend struct device; }; } diff --git a/libs/wgpu/objects-todo b/libs/wgpu/objects-todo index de66e173..cfea79cf 100644 --- a/libs/wgpu/objects-todo +++ b/libs/wgpu/objects-todo @@ -6,7 +6,7 @@ - WGPUCommandEncoder + WGPUComputePassEncoder + WGPUComputePipeline -- WGPUDevice ++ WGPUDevice + WGPUInstance + WGPUPipelineLayout + WGPUQuerySet diff --git a/libs/wgpu/source/device.cpp b/libs/wgpu/source/device.cpp index aa9ea6b3..e3406a43 100644 --- a/libs/wgpu/source/device.cpp +++ b/libs/wgpu/source/device.cpp @@ -91,30 +91,57 @@ namespace psemek::wgpu return command_encoder(wgpuDeviceCreateCommandEncoder((WGPUDevice)get(), &descriptor)); } + namespace + { + + void fill_compute_pipeline_descriptor(compute_pipeline::descriptor const & desc, std::vector & constants, WGPUComputePipelineDescriptor & descriptor) + { + for (auto const & constant_in : desc.compute.constants) + { + auto & constant_out = constants.emplace_back(); + constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); + constant_out.key = constant_in.key.data(); + constant_out.value = constant_in.value; + } + + descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); + descriptor.label = desc.label.data(); + descriptor.layout = (WGPUPipelineLayout)desc.layout.get(); + descriptor.compute.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.compute.chain); + descriptor.compute.module = (WGPUShaderModule)desc.compute.module.get(); + descriptor.compute.entryPoint = desc.compute.entry_point.data(); + descriptor.compute.constantCount = constants.size(); + descriptor.compute.constants = constants.data(); + } + + } + compute_pipeline device::create_compute_pipeline(compute_pipeline::descriptor const & desc) { std::vector constants; - for (auto const & constant_in : desc.compute.constants) - { - auto & constant_out = constants.emplace_back(); - constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); - constant_out.key = constant_in.key.data(); - constant_out.value = constant_in.value; - } - WGPUComputePipelineDescriptor descriptor = {}; - descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); - descriptor.layout = (WGPUPipelineLayout)desc.layout.get(); - descriptor.compute.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.compute.chain); - descriptor.compute.module = (WGPUShaderModule)desc.compute.module.get(); - descriptor.compute.entryPoint = desc.compute.entry_point.data(); - descriptor.compute.constantCount = constants.size(); - descriptor.compute.constants = constants.data(); + fill_compute_pipeline_descriptor(desc, constants, descriptor); return compute_pipeline(wgpuDeviceCreateComputePipeline((WGPUDevice)get(), &descriptor)); } + void device::create_compute_pipeline_async(compute_pipeline::descriptor const & desc, create_compute_pipeline_async_callback const & callback) + { + std::vector constants; + WGPUComputePipelineDescriptor descriptor = {}; + fill_compute_pipeline_descriptor(desc, constants, descriptor); + + auto userdata = new create_compute_pipeline_async_callback(callback); + + auto real_callback = [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, char const * message, void * userdata) + { + std::unique_ptr callback((create_compute_pipeline_async_callback *)userdata); + if (*callback) (*callback)((create_pipeline_async_status)status, compute_pipeline(pipeline), std::string(message ? message : "")); + }; + + wgpuDeviceCreateComputePipelineAsync((WGPUDevice)get(), &descriptor, real_callback, userdata); + } + pipeline_layout device::create_pipeline_layout(pipeline_layout::descriptor const & desc) { WGPUPipelineLayoutDescriptor descriptor = {}; @@ -136,110 +163,164 @@ namespace psemek::wgpu return query_set(wgpuDeviceCreateQuerySet((WGPUDevice)get(), &descriptor)); } - render_pipeline device::create_render_pipeline(render_pipeline::descriptor const & desc) + namespace { - WGPURenderPipelineDescriptor descriptor = {}; - std::vector vertex_constants; - for (auto const & constant_in : desc.vertex.constants) + void fill_render_pipeline_descriptor(render_pipeline::descriptor const & desc, + WGPURenderPipelineDescriptor & descriptor, + std::vector & vertex_constants, + std::vector & vertex_buffers, + WGPUDepthStencilState & depth_stencil_state, + WGPUFragmentState & fragment_state, + std::vector & fragment_constants, + std::vector & color_targets) { - auto & constant_out = vertex_constants.emplace_back(); - constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); - constant_out.key = constant_in.key.data(); - constant_out.value = constant_in.value; - } - - std::vector vertex_buffers; - for (auto const & buffer_in : desc.vertex.buffers) - { - auto & buffer_out = vertex_buffers.emplace_back(); - buffer_out.arrayStride = buffer_in.array_stride; - buffer_out.stepMode = (WGPUVertexStepMode)buffer_in.step_mode; - buffer_out.attributeCount = buffer_in.attributes.size(); - static_assert(sizeof(WGPUVertexAttribute) == sizeof(vertex_attribute)); - buffer_out.attributes = (WGPUVertexAttribute const *)buffer_in.attributes.data(); - } - - descriptor.vertex.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.vertex.chain); - descriptor.vertex.module = (WGPUShaderModule)desc.vertex.module.get(); - descriptor.vertex.entryPoint = desc.vertex.entry_point.data(); - descriptor.vertex.constantCount = vertex_constants.size(); - descriptor.vertex.constants = vertex_constants.data(); - descriptor.vertex.bufferCount = vertex_buffers.size(); - descriptor.vertex.buffers = vertex_buffers.data(); - - descriptor.primitive.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.primitive.chain); - descriptor.primitive.topology = (WGPUPrimitiveTopology)desc.primitive.topology; - descriptor.primitive.stripIndexFormat = (WGPUIndexFormat)desc.primitive.strip_index_format; - descriptor.primitive.frontFace = (WGPUFrontFace)desc.primitive.front_face; - descriptor.primitive.cullMode = (WGPUCullMode)desc.primitive.cull_mode; - - WGPUDepthStencilState depth_stencil_state = {}; - if (desc.depth_stencil) - { - depth_stencil_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.depth_stencil->chain); - depth_stencil_state.format = (WGPUTextureFormat)desc.depth_stencil->format; - depth_stencil_state.depthWriteEnabled = desc.depth_stencil->depth_write; - depth_stencil_state.depthCompare = (WGPUCompareFunction)desc.depth_stencil->depth_compare; - depth_stencil_state.stencilFront.compare = (WGPUCompareFunction)desc.depth_stencil->stencil_front.compare; - depth_stencil_state.stencilFront.failOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.fail_op; - depth_stencil_state.stencilFront.depthFailOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.depth_fail_op; - depth_stencil_state.stencilFront.passOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.pass_op; - depth_stencil_state.stencilBack.compare = (WGPUCompareFunction)desc.depth_stencil->stencil_back.compare; - depth_stencil_state.stencilBack.failOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.fail_op; - depth_stencil_state.stencilBack.depthFailOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.depth_fail_op; - depth_stencil_state.stencilBack.passOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.pass_op; - depth_stencil_state.stencilReadMask = desc.depth_stencil->stencil_read_mask; - depth_stencil_state.stencilWriteMask = desc.depth_stencil->stencil_write_mask; - depth_stencil_state.depthBias = desc.depth_stencil->depth_bias; - depth_stencil_state.depthBiasSlopeScale = desc.depth_stencil->depth_bias_slope_scale; - depth_stencil_state.depthBiasClamp = desc.depth_stencil->depth_bias_clamp; - - descriptor.depthStencil = &depth_stencil_state; - } - - descriptor.multisample.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.multisample.chain); - descriptor.multisample.count = desc.multisample.count; - descriptor.multisample.mask = desc.multisample.mask; - descriptor.multisample.alphaToCoverageEnabled = desc.multisample.alpha_to_coverage; - - WGPUFragmentState fragment_state = {}; - std::vector fragment_constants; - std::vector color_targets; - if (desc.fragment) - { - for (auto const & constant_in : desc.fragment->constants) + for (auto const & constant_in : desc.vertex.constants) { - auto & constant_out = fragment_constants.emplace_back(); + auto & constant_out = vertex_constants.emplace_back(); constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); constant_out.key = constant_in.key.data(); constant_out.value = constant_in.value; } - for (auto const & target_in : desc.fragment->targets) + for (auto const & buffer_in : desc.vertex.buffers) { - auto & target_out = color_targets.emplace_back(); - target_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(target_in.chain); - target_out.format = (WGPUTextureFormat)target_in.format; - static_assert(sizeof(WGPUBlendState) == sizeof(blend_state)); - target_out.blend = (WGPUBlendState *)(target_in.blend ? &(*target_in.blend) : nullptr); - target_out.writeMask = (WGPUColorWriteMaskFlags)target_in.write_mask; + auto & buffer_out = vertex_buffers.emplace_back(); + buffer_out.arrayStride = buffer_in.array_stride; + buffer_out.stepMode = (WGPUVertexStepMode)buffer_in.step_mode; + buffer_out.attributeCount = buffer_in.attributes.size(); + static_assert(sizeof(WGPUVertexAttribute) == sizeof(vertex_attribute)); + buffer_out.attributes = (WGPUVertexAttribute const *)buffer_in.attributes.data(); } - fragment_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.fragment->chain); - fragment_state.module = (WGPUShaderModule)desc.fragment->module.get(); - fragment_state.entryPoint = desc.fragment->entry_point.data(); - fragment_state.constantCount = fragment_constants.size(); - fragment_state.constants = fragment_constants.data(); - fragment_state.targetCount = color_targets.size(); - fragment_state.targets = color_targets.data(); + descriptor.vertex.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.vertex.chain); + descriptor.vertex.module = (WGPUShaderModule)desc.vertex.module.get(); + descriptor.vertex.entryPoint = desc.vertex.entry_point.data(); + descriptor.vertex.constantCount = vertex_constants.size(); + descriptor.vertex.constants = vertex_constants.data(); + descriptor.vertex.bufferCount = vertex_buffers.size(); + descriptor.vertex.buffers = vertex_buffers.data(); - descriptor.fragment = &fragment_state; + descriptor.primitive.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.primitive.chain); + descriptor.primitive.topology = (WGPUPrimitiveTopology)desc.primitive.topology; + descriptor.primitive.stripIndexFormat = (WGPUIndexFormat)desc.primitive.strip_index_format; + descriptor.primitive.frontFace = (WGPUFrontFace)desc.primitive.front_face; + descriptor.primitive.cullMode = (WGPUCullMode)desc.primitive.cull_mode; + + if (desc.depth_stencil) + { + depth_stencil_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.depth_stencil->chain); + depth_stencil_state.format = (WGPUTextureFormat)desc.depth_stencil->format; + depth_stencil_state.depthWriteEnabled = desc.depth_stencil->depth_write; + depth_stencil_state.depthCompare = (WGPUCompareFunction)desc.depth_stencil->depth_compare; + depth_stencil_state.stencilFront.compare = (WGPUCompareFunction)desc.depth_stencil->stencil_front.compare; + depth_stencil_state.stencilFront.failOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.fail_op; + depth_stencil_state.stencilFront.depthFailOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.depth_fail_op; + depth_stencil_state.stencilFront.passOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.pass_op; + depth_stencil_state.stencilBack.compare = (WGPUCompareFunction)desc.depth_stencil->stencil_back.compare; + depth_stencil_state.stencilBack.failOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.fail_op; + depth_stencil_state.stencilBack.depthFailOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.depth_fail_op; + depth_stencil_state.stencilBack.passOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.pass_op; + depth_stencil_state.stencilReadMask = desc.depth_stencil->stencil_read_mask; + depth_stencil_state.stencilWriteMask = desc.depth_stencil->stencil_write_mask; + depth_stencil_state.depthBias = desc.depth_stencil->depth_bias; + depth_stencil_state.depthBiasSlopeScale = desc.depth_stencil->depth_bias_slope_scale; + depth_stencil_state.depthBiasClamp = desc.depth_stencil->depth_bias_clamp; + + descriptor.depthStencil = &depth_stencil_state; + } + + descriptor.multisample.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.multisample.chain); + descriptor.multisample.count = desc.multisample.count; + descriptor.multisample.mask = desc.multisample.mask; + descriptor.multisample.alphaToCoverageEnabled = desc.multisample.alpha_to_coverage; + + if (desc.fragment) + { + for (auto const & constant_in : desc.fragment->constants) + { + auto & constant_out = fragment_constants.emplace_back(); + constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); + constant_out.key = constant_in.key.data(); + constant_out.value = constant_in.value; + } + + for (auto const & target_in : desc.fragment->targets) + { + auto & target_out = color_targets.emplace_back(); + target_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(target_in.chain); + target_out.format = (WGPUTextureFormat)target_in.format; + static_assert(sizeof(WGPUBlendState) == sizeof(blend_state)); + target_out.blend = (WGPUBlendState *)(target_in.blend ? &(*target_in.blend) : nullptr); + target_out.writeMask = (WGPUColorWriteMaskFlags)target_in.write_mask; + } + + fragment_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.fragment->chain); + fragment_state.module = (WGPUShaderModule)desc.fragment->module.get(); + fragment_state.entryPoint = desc.fragment->entry_point.data(); + fragment_state.constantCount = fragment_constants.size(); + fragment_state.constants = fragment_constants.data(); + fragment_state.targetCount = color_targets.size(); + fragment_state.targets = color_targets.data(); + + descriptor.fragment = &fragment_state; + } } + } + + render_pipeline device::create_render_pipeline(render_pipeline::descriptor const & desc) + { + WGPURenderPipelineDescriptor descriptor = {}; + std::vector vertex_constants; + std::vector vertex_buffers; + WGPUDepthStencilState depth_stencil_state = {}; + WGPUFragmentState fragment_state = {}; + std::vector fragment_constants; + std::vector color_targets; + + fill_render_pipeline_descriptor(desc, descriptor, vertex_constants, vertex_buffers, depth_stencil_state, fragment_state, fragment_constants, color_targets); + return render_pipeline(wgpuDeviceCreateRenderPipeline((WGPUDevice)get(), &descriptor)); } + void device::create_render_pipeline_async(render_pipeline::descriptor const & desc, create_render_pipeline_async_callback const & callback) + { + WGPURenderPipelineDescriptor descriptor = {}; + std::vector vertex_constants; + std::vector vertex_buffers; + WGPUDepthStencilState depth_stencil_state = {}; + WGPUFragmentState fragment_state = {}; + std::vector fragment_constants; + std::vector color_targets; + + fill_render_pipeline_descriptor(desc, descriptor, vertex_constants, vertex_buffers, depth_stencil_state, fragment_state, fragment_constants, color_targets); + + auto userdata = new create_render_pipeline_async_callback(callback); + + auto real_callback = [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline, char const * message, void * userdata) + { + std::unique_ptr callback((create_render_pipeline_async_callback *)userdata); + if (*callback) (*callback)((create_pipeline_async_status)status, render_pipeline(pipeline), std::string(message ? message : "")); + }; + + wgpuDeviceCreateRenderPipelineAsync((WGPUDevice)get(), &descriptor, real_callback, userdata); + } + + render_bundle_encoder device::create_render_bundle_encoder(render_bundle_encoder::descriptor const & desc) + { + WGPURenderBundleEncoderDescriptor descriptor = {}; + descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); + descriptor.label = desc.label.data(); + descriptor.colorFormatCount = desc.color_formats.size(); + descriptor.colorFormats = (WGPUTextureFormat const *)desc.color_formats.data(); + descriptor.depthStencilFormat = (WGPUTextureFormat)desc.depth_stencil_format; + descriptor.sampleCount = desc.sample_count; + descriptor.depthReadOnly = desc.depth_read_only; + descriptor.stencilReadOnly = desc.stencil_read_only; + return render_bundle_encoder(wgpuDeviceCreateRenderBundleEncoder((WGPUDevice)get(), &descriptor)); + } + sampler device::create_sampler(sampler::descriptor const & desc) { WGPUSamplerDescriptor descriptor = {}; @@ -293,6 +374,72 @@ namespace psemek::wgpu return texture(wgpuDeviceCreateTexture((WGPUDevice)get(), &descriptor)); } + void device::destroy() + { + wgpuDeviceDestroy((WGPUDevice)get()); + } + + std::vector device::enumerate_features() + { + std::size_t count = wgpuDeviceEnumerateFeatures((WGPUDevice)get(), nullptr); + std::vector result(count); + wgpuDeviceEnumerateFeatures((WGPUDevice)get(), (WGPUFeatureName *)result.data()); + return result; + } + + limits device::get_limits() + { + WGPUSupportedLimits limits = {}; + wgpuDeviceGetLimits((WGPUDevice)get(), &limits); + + // TODO: support out chain + wgpu::limits result; + static_assert(sizeof(result) == sizeof(limits.limits)); + std::memcpy(&result, &limits.limits, sizeof(result)); + return result; + } + + bool device::has_feature(feature feature) + { + return wgpuDeviceHasFeature((WGPUDevice)get(), (WGPUFeatureName)feature); + } + + void device::push_error_scope(error_filter filter) + { + wgpuDevicePushErrorScope((WGPUDevice)get(), (WGPUErrorFilter)filter); + } + + void device::pop_error_scope(error_callback const & callback) + { + auto userdata = new error_callback(callback); + + auto real_callback = [](WGPUErrorType type, char const * message, void * userdata) + { + std::unique_ptr callback((error_callback *)userdata); + if (*callback) (*callback)((error_type)type, message ? message : ""); + }; + + wgpuDevicePopErrorScope((WGPUDevice)get(), real_callback, userdata); + } + + void device::set_uncaptured_error_callback(error_callback const & callback) + { + auto userdata = new error_callback(callback); + + auto real_callback = [](WGPUErrorType type, char const * message, void * userdata) + { + std::unique_ptr callback((error_callback *)userdata); + if (*callback) (*callback)((error_type)type, message ? message : ""); + }; + + wgpuDeviceSetUncapturedErrorCallback((WGPUDevice)get(), real_callback, userdata); + } + + void device::set_label(std::string const & label) + { + wgpuDeviceSetLabel((WGPUDevice)get(), label.data()); + } + void device::reference(void * ptr) { wgpuDeviceReference((WGPUDevice)ptr);