WebGPU wrapper wip: add device methods
This commit is contained in:
parent
407df9a21d
commit
97ee3c1134
4 changed files with 296 additions and 105 deletions
|
|
@ -9,6 +9,7 @@
|
||||||
#include <psemek/wgpu/compute_pipeline.hpp>
|
#include <psemek/wgpu/compute_pipeline.hpp>
|
||||||
#include <psemek/wgpu/pipeline_layout.hpp>
|
#include <psemek/wgpu/pipeline_layout.hpp>
|
||||||
#include <psemek/wgpu/query_set.hpp>
|
#include <psemek/wgpu/query_set.hpp>
|
||||||
|
#include <psemek/wgpu/render_bundle_encoder.hpp>
|
||||||
#include <psemek/wgpu/render_pipeline.hpp>
|
#include <psemek/wgpu/render_pipeline.hpp>
|
||||||
#include <psemek/wgpu/sampler.hpp>
|
#include <psemek/wgpu/sampler.hpp>
|
||||||
#include <psemek/wgpu/shader_module.hpp>
|
#include <psemek/wgpu/shader_module.hpp>
|
||||||
|
|
@ -73,6 +74,33 @@ namespace psemek::wgpu
|
||||||
std::uint32_t max_compute_workgroups_per_dimension;
|
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
|
struct device
|
||||||
: detail::object<device>
|
: detail::object<device>
|
||||||
{
|
{
|
||||||
|
|
@ -110,6 +138,9 @@ namespace psemek::wgpu
|
||||||
};
|
};
|
||||||
|
|
||||||
using request_callback = std::function<void(request_status, device, std::string const & message)>;
|
using request_callback = std::function<void(request_status, device, std::string const & message)>;
|
||||||
|
using create_compute_pipeline_async_callback = std::function<void(create_pipeline_async_status, compute_pipeline, std::string const &)>;
|
||||||
|
using create_render_pipeline_async_callback = std::function<void(create_pipeline_async_status, render_pipeline, std::string const &)>;
|
||||||
|
using error_callback = std::function<void(error_type, std::string const &)>;
|
||||||
|
|
||||||
queue get_queue();
|
queue get_queue();
|
||||||
bind_group create_bind_group(bind_group::descriptor const & desc);
|
bind_group create_bind_group(bind_group::descriptor const & desc);
|
||||||
|
|
@ -117,12 +148,23 @@ namespace psemek::wgpu
|
||||||
buffer create_buffer(buffer::descriptor const & desc);
|
buffer create_buffer(buffer::descriptor const & desc);
|
||||||
command_encoder create_command_encoder(command_encoder::descriptor const & desc);
|
command_encoder create_command_encoder(command_encoder::descriptor const & desc);
|
||||||
compute_pipeline create_compute_pipeline(compute_pipeline::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);
|
pipeline_layout create_pipeline_layout(pipeline_layout::descriptor const & desc);
|
||||||
query_set create_query_set(query_set::descriptor const & desc);
|
query_set create_query_set(query_set::descriptor const & desc);
|
||||||
render_pipeline create_render_pipeline(render_pipeline::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);
|
sampler create_sampler(sampler::descriptor const & desc);
|
||||||
shader_module create_shader_module(shader_module::descriptor const & desc);
|
shader_module create_shader_module(shader_module::descriptor const & desc);
|
||||||
texture create_texture(texture::descriptor const & desc);
|
texture create_texture(texture::descriptor const & desc);
|
||||||
|
void destroy();
|
||||||
|
std::vector<feature> 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 reference(void * ptr);
|
||||||
static void release(void * ptr);
|
static void release(void * ptr);
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ namespace psemek::wgpu
|
||||||
std::vector<texture::format> color_formats;
|
std::vector<texture::format> color_formats;
|
||||||
texture::format depth_stencil_format;
|
texture::format depth_stencil_format;
|
||||||
std::uint32_t sample_count = 1;
|
std::uint32_t sample_count = 1;
|
||||||
bool depthReadOnly = false;
|
bool depth_read_only = false;
|
||||||
bool stencilReadOnly = true;
|
bool stencil_read_only = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
void set_pipeline(render_pipeline const & pipeline);
|
void set_pipeline(render_pipeline const & pipeline);
|
||||||
|
|
@ -51,6 +51,8 @@ namespace psemek::wgpu
|
||||||
explicit render_bundle_encoder(void * ptr)
|
explicit render_bundle_encoder(void * ptr)
|
||||||
: detail::object<render_bundle_encoder>(ptr)
|
: detail::object<render_bundle_encoder>(ptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
friend struct device;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
- WGPUCommandEncoder
|
- WGPUCommandEncoder
|
||||||
+ WGPUComputePassEncoder
|
+ WGPUComputePassEncoder
|
||||||
+ WGPUComputePipeline
|
+ WGPUComputePipeline
|
||||||
- WGPUDevice
|
+ WGPUDevice
|
||||||
+ WGPUInstance
|
+ WGPUInstance
|
||||||
+ WGPUPipelineLayout
|
+ WGPUPipelineLayout
|
||||||
+ WGPUQuerySet
|
+ WGPUQuerySet
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,11 @@ namespace psemek::wgpu
|
||||||
return command_encoder(wgpuDeviceCreateCommandEncoder((WGPUDevice)get(), &descriptor));
|
return command_encoder(wgpuDeviceCreateCommandEncoder((WGPUDevice)get(), &descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
compute_pipeline device::create_compute_pipeline(compute_pipeline::descriptor const & desc)
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
void fill_compute_pipeline_descriptor(compute_pipeline::descriptor const & desc, std::vector<WGPUConstantEntry> & constants, WGPUComputePipelineDescriptor & descriptor)
|
||||||
{
|
{
|
||||||
std::vector<WGPUConstantEntry> constants;
|
|
||||||
for (auto const & constant_in : desc.compute.constants)
|
for (auto const & constant_in : desc.compute.constants)
|
||||||
{
|
{
|
||||||
auto & constant_out = constants.emplace_back();
|
auto & constant_out = constants.emplace_back();
|
||||||
|
|
@ -102,7 +104,6 @@ namespace psemek::wgpu
|
||||||
constant_out.value = constant_in.value;
|
constant_out.value = constant_in.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
WGPUComputePipelineDescriptor descriptor = {};
|
|
||||||
descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
|
descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
|
||||||
descriptor.label = desc.label.data();
|
descriptor.label = desc.label.data();
|
||||||
descriptor.layout = (WGPUPipelineLayout)desc.layout.get();
|
descriptor.layout = (WGPUPipelineLayout)desc.layout.get();
|
||||||
|
|
@ -111,10 +112,36 @@ namespace psemek::wgpu
|
||||||
descriptor.compute.entryPoint = desc.compute.entry_point.data();
|
descriptor.compute.entryPoint = desc.compute.entry_point.data();
|
||||||
descriptor.compute.constantCount = constants.size();
|
descriptor.compute.constantCount = constants.size();
|
||||||
descriptor.compute.constants = constants.data();
|
descriptor.compute.constants = constants.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_pipeline device::create_compute_pipeline(compute_pipeline::descriptor const & desc)
|
||||||
|
{
|
||||||
|
std::vector<WGPUConstantEntry> constants;
|
||||||
|
WGPUComputePipelineDescriptor descriptor = {};
|
||||||
|
fill_compute_pipeline_descriptor(desc, constants, descriptor);
|
||||||
|
|
||||||
return compute_pipeline(wgpuDeviceCreateComputePipeline((WGPUDevice)get(), &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<WGPUConstantEntry> 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<create_compute_pipeline_async_callback> 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)
|
pipeline_layout device::create_pipeline_layout(pipeline_layout::descriptor const & desc)
|
||||||
{
|
{
|
||||||
WGPUPipelineLayoutDescriptor descriptor = {};
|
WGPUPipelineLayoutDescriptor descriptor = {};
|
||||||
|
|
@ -136,11 +163,18 @@ namespace psemek::wgpu
|
||||||
return query_set(wgpuDeviceCreateQuerySet((WGPUDevice)get(), &descriptor));
|
return query_set(wgpuDeviceCreateQuerySet((WGPUDevice)get(), &descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
render_pipeline device::create_render_pipeline(render_pipeline::descriptor const & desc)
|
namespace
|
||||||
{
|
{
|
||||||
WGPURenderPipelineDescriptor descriptor = {};
|
|
||||||
|
|
||||||
std::vector<WGPUConstantEntry> vertex_constants;
|
void fill_render_pipeline_descriptor(render_pipeline::descriptor const & desc,
|
||||||
|
WGPURenderPipelineDescriptor & descriptor,
|
||||||
|
std::vector<WGPUConstantEntry> & vertex_constants,
|
||||||
|
std::vector<WGPUVertexBufferLayout> & vertex_buffers,
|
||||||
|
WGPUDepthStencilState & depth_stencil_state,
|
||||||
|
WGPUFragmentState & fragment_state,
|
||||||
|
std::vector<WGPUConstantEntry> & fragment_constants,
|
||||||
|
std::vector<WGPUColorTargetState> & color_targets)
|
||||||
|
{
|
||||||
for (auto const & constant_in : desc.vertex.constants)
|
for (auto const & constant_in : desc.vertex.constants)
|
||||||
{
|
{
|
||||||
auto & constant_out = vertex_constants.emplace_back();
|
auto & constant_out = vertex_constants.emplace_back();
|
||||||
|
|
@ -149,7 +183,6 @@ namespace psemek::wgpu
|
||||||
constant_out.value = constant_in.value;
|
constant_out.value = constant_in.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<WGPUVertexBufferLayout> vertex_buffers;
|
|
||||||
for (auto const & buffer_in : desc.vertex.buffers)
|
for (auto const & buffer_in : desc.vertex.buffers)
|
||||||
{
|
{
|
||||||
auto & buffer_out = vertex_buffers.emplace_back();
|
auto & buffer_out = vertex_buffers.emplace_back();
|
||||||
|
|
@ -174,7 +207,6 @@ namespace psemek::wgpu
|
||||||
descriptor.primitive.frontFace = (WGPUFrontFace)desc.primitive.front_face;
|
descriptor.primitive.frontFace = (WGPUFrontFace)desc.primitive.front_face;
|
||||||
descriptor.primitive.cullMode = (WGPUCullMode)desc.primitive.cull_mode;
|
descriptor.primitive.cullMode = (WGPUCullMode)desc.primitive.cull_mode;
|
||||||
|
|
||||||
WGPUDepthStencilState depth_stencil_state = {};
|
|
||||||
if (desc.depth_stencil)
|
if (desc.depth_stencil)
|
||||||
{
|
{
|
||||||
depth_stencil_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.depth_stencil->chain);
|
depth_stencil_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.depth_stencil->chain);
|
||||||
|
|
@ -203,9 +235,6 @@ namespace psemek::wgpu
|
||||||
descriptor.multisample.mask = desc.multisample.mask;
|
descriptor.multisample.mask = desc.multisample.mask;
|
||||||
descriptor.multisample.alphaToCoverageEnabled = desc.multisample.alpha_to_coverage;
|
descriptor.multisample.alphaToCoverageEnabled = desc.multisample.alpha_to_coverage;
|
||||||
|
|
||||||
WGPUFragmentState fragment_state = {};
|
|
||||||
std::vector<WGPUConstantEntry> fragment_constants;
|
|
||||||
std::vector<WGPUColorTargetState> color_targets;
|
|
||||||
if (desc.fragment)
|
if (desc.fragment)
|
||||||
{
|
{
|
||||||
for (auto const & constant_in : desc.fragment->constants)
|
for (auto const & constant_in : desc.fragment->constants)
|
||||||
|
|
@ -236,10 +265,62 @@ namespace psemek::wgpu
|
||||||
|
|
||||||
descriptor.fragment = &fragment_state;
|
descriptor.fragment = &fragment_state;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render_pipeline device::create_render_pipeline(render_pipeline::descriptor const & desc)
|
||||||
|
{
|
||||||
|
WGPURenderPipelineDescriptor descriptor = {};
|
||||||
|
std::vector<WGPUConstantEntry> vertex_constants;
|
||||||
|
std::vector<WGPUVertexBufferLayout> vertex_buffers;
|
||||||
|
WGPUDepthStencilState depth_stencil_state = {};
|
||||||
|
WGPUFragmentState fragment_state = {};
|
||||||
|
std::vector<WGPUConstantEntry> fragment_constants;
|
||||||
|
std::vector<WGPUColorTargetState> 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));
|
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<WGPUConstantEntry> vertex_constants;
|
||||||
|
std::vector<WGPUVertexBufferLayout> vertex_buffers;
|
||||||
|
WGPUDepthStencilState depth_stencil_state = {};
|
||||||
|
WGPUFragmentState fragment_state = {};
|
||||||
|
std::vector<WGPUConstantEntry> fragment_constants;
|
||||||
|
std::vector<WGPUColorTargetState> 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<create_render_pipeline_async_callback> 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)
|
sampler device::create_sampler(sampler::descriptor const & desc)
|
||||||
{
|
{
|
||||||
WGPUSamplerDescriptor descriptor = {};
|
WGPUSamplerDescriptor descriptor = {};
|
||||||
|
|
@ -293,6 +374,72 @@ namespace psemek::wgpu
|
||||||
return texture(wgpuDeviceCreateTexture((WGPUDevice)get(), &descriptor));
|
return texture(wgpuDeviceCreateTexture((WGPUDevice)get(), &descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void device::destroy()
|
||||||
|
{
|
||||||
|
wgpuDeviceDestroy((WGPUDevice)get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<feature> device::enumerate_features()
|
||||||
|
{
|
||||||
|
std::size_t count = wgpuDeviceEnumerateFeatures((WGPUDevice)get(), nullptr);
|
||||||
|
std::vector<feature> 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<error_callback> 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<error_callback> 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)
|
void device::reference(void * ptr)
|
||||||
{
|
{
|
||||||
wgpuDeviceReference((WGPUDevice)ptr);
|
wgpuDeviceReference((WGPUDevice)ptr);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue