WebGPU wrapper wip: add device methods

This commit is contained in:
Nikita Lisitsa 2024-01-01 16:43:46 +03:00
parent 407df9a21d
commit 97ee3c1134
4 changed files with 296 additions and 105 deletions

View file

@ -9,6 +9,7 @@
#include <psemek/wgpu/compute_pipeline.hpp>
#include <psemek/wgpu/pipeline_layout.hpp>
#include <psemek/wgpu/query_set.hpp>
#include <psemek/wgpu/render_bundle_encoder.hpp>
#include <psemek/wgpu/render_pipeline.hpp>
#include <psemek/wgpu/sampler.hpp>
#include <psemek/wgpu/shader_module.hpp>
@ -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<device>
{
@ -110,6 +138,9 @@ namespace psemek::wgpu
};
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();
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<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 release(void * ptr);

View file

@ -26,8 +26,8 @@ namespace psemek::wgpu
std::vector<texture::format> 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<render_bundle_encoder>(ptr)
{}
friend struct device;
};
}

View file

@ -6,7 +6,7 @@
- WGPUCommandEncoder
+ WGPUComputePassEncoder
+ WGPUComputePipeline
- WGPUDevice
+ WGPUDevice
+ WGPUInstance
+ WGPUPipelineLayout
+ WGPUQuerySet

View file

@ -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<WGPUConstantEntry> & 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<WGPUConstantEntry> 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<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)
{
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<WGPUConstantEntry> vertex_constants;
for (auto const & constant_in : desc.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)
{
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<WGPUVertexBufferLayout> 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<WGPUConstantEntry> fragment_constants;
std::vector<WGPUColorTargetState> 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<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));
}
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)
{
WGPUSamplerDescriptor descriptor = {};
@ -293,6 +374,72 @@ namespace psemek::wgpu
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)
{
wgpuDeviceReference((WGPUDevice)ptr);