From ea71e1878e3f1c13546c307d29d188c24e13d9d2 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 31 Dec 2023 15:21:20 +0300 Subject: [PATCH] WebGPU wrapper wip: add render pipeline object --- .../include/psemek/wgpu/bind_group_layout.hpp | 25 ++ libs/wgpu/include/psemek/wgpu/device.hpp | 4 +- .../include/psemek/wgpu/render_pipeline.hpp | 268 ++++++++++++++++++ libs/wgpu/objects-todo | 4 +- libs/wgpu/source/bind_group_layout.cpp | 17 ++ libs/wgpu/source/device.cpp | 104 +++++++ libs/wgpu/source/render_pipeline.cpp | 27 ++ 7 files changed, 446 insertions(+), 3 deletions(-) create mode 100644 libs/wgpu/include/psemek/wgpu/bind_group_layout.hpp create mode 100644 libs/wgpu/include/psemek/wgpu/render_pipeline.hpp create mode 100644 libs/wgpu/source/bind_group_layout.cpp create mode 100644 libs/wgpu/source/render_pipeline.cpp diff --git a/libs/wgpu/include/psemek/wgpu/bind_group_layout.hpp b/libs/wgpu/include/psemek/wgpu/bind_group_layout.hpp new file mode 100644 index 00000000..b59d707a --- /dev/null +++ b/libs/wgpu/include/psemek/wgpu/bind_group_layout.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace psemek::wgpu +{ + + struct bind_group_layout + : detail::object + { + using detail::object::object; + + static void reference(void * ptr); + static void release(void * ptr); + + private: + explicit bind_group_layout(void * ptr) + : detail::object(ptr) + {} + + friend struct device; + friend struct render_pipeline; + }; + +} diff --git a/libs/wgpu/include/psemek/wgpu/device.hpp b/libs/wgpu/include/psemek/wgpu/device.hpp index 2b4b2d15..36fd711e 100644 --- a/libs/wgpu/include/psemek/wgpu/device.hpp +++ b/libs/wgpu/include/psemek/wgpu/device.hpp @@ -3,11 +3,12 @@ #include #include #include +#include #include +#include #include #include #include -#include #include #include @@ -110,6 +111,7 @@ namespace psemek::wgpu buffer create_buffer(buffer::descriptor const & desc); command_encoder create_command_encoder(command_encoder::descriptor const & desc); query_set create_query_set(query_set::descriptor const & desc); + render_pipeline create_render_pipeline(render_pipeline::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); diff --git a/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp b/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp new file mode 100644 index 00000000..1acb144a --- /dev/null +++ b/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp @@ -0,0 +1,268 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace psemek::wgpu +{ + + struct constant_entry + { + std::vector chain; + std::string key; + double value; + }; + + enum class vertex_step_mode : std::uint32_t + { + vertex = 0x00000000, + instance = 0x00000001, + vertex_buffer_not_used = 0x00000002, + }; + + enum class vertex_format : std::uint32_t + { + undefined = 0x00000000, + uint8x2 = 0x00000001, + uint8x4 = 0x00000002, + sint8x2 = 0x00000003, + sint8x4 = 0x00000004, + unorm8x2 = 0x00000005, + unorm8x4 = 0x00000006, + snorm8x2 = 0x00000007, + snorm8x4 = 0x00000008, + uint16x2 = 0x00000009, + uint16x4 = 0x0000000a, + sint16x2 = 0x0000000b, + sint16x4 = 0x0000000c, + unorm16x2 = 0x0000000d, + unorm16x4 = 0x0000000e, + snorm16x2 = 0x0000000f, + snorm16x4 = 0x00000010, + float16x2 = 0x00000011, + float16x4 = 0x00000012, + float32 = 0x00000013, + float32x2 = 0x00000014, + float32x3 = 0x00000015, + float32x4 = 0x00000016, + uint32 = 0x00000017, + uint32x2 = 0x00000018, + uint32x3 = 0x00000019, + uint32x4 = 0x0000001a, + sint32 = 0x0000001b, + sint32x2 = 0x0000001c, + sint32x3 = 0x0000001d, + sint32x4 = 0x0000001e, + }; + + struct vertex_attribute + { + vertex_format format; + std::uint64_t offset; + std::uint32_t shader_location; + }; + + struct vertex_buffer_layout + { + std::uint64_t array_stride; + vertex_step_mode step_mode; + std::vector attributes; + }; + + struct vertex_state + { + std::vector chain = {}; + shader_module module; + std::string entry_point; + std::vector constants = {}; + std::vector buffers; + }; + + enum class primitive_topology : std::uint32_t + { + point_list = 0x00000000, + line_list = 0x00000001, + line_strip = 0x00000002, + triangle_list = 0x00000003, + triangle_strip = 0x00000004, + }; + + enum class index_format : std::uint32_t + { + undefined = 0x00000000, + uint16 = 0x00000001, + uint32 = 0x00000002, + }; + + enum class front_face : std::uint32_t + { + ccw = 0x00000000, + cw = 0x00000001, + }; + + enum class cull_mode : std::uint32_t + { + none = 0x00000000, + front = 0x00000001, + back = 0x00000002, + }; + + struct primitive_state + { + std::vector chain = {}; + primitive_topology topology; + index_format strip_index_format = index_format::undefined; + enum front_face front_face; + enum cull_mode cull_mode; + }; + + enum class stencil_operation : std::uint32_t + { + keep = 0x00000000, + zero = 0x00000001, + replace = 0x00000002, + invert = 0x00000003, + increment_clamp = 0x00000004, + decrement_clamp = 0x00000005, + increment_wrap = 0x00000006, + decrement_wrap = 0x00000007, + }; + + struct stencil_face_state + { + compare_function compare = compare_function::always; + stencil_operation fail_op = stencil_operation::keep; + stencil_operation depth_fail_op = stencil_operation::keep; + stencil_operation pass_op = stencil_operation::keep; + }; + + struct depth_stencil_state + { + std::vector chain = {}; + texture::format format; + bool depth_write = true; + compare_function depth_compare; + stencil_face_state stencil_front = {}; + stencil_face_state stencil_back = {}; + std::uint32_t stencil_read_mask = 0; + std::uint32_t stencil_write_mask = 0; + std::int32_t depth_bias = 0; + float depth_bias_slope_scale = 0.f; + float depth_bias_clamp = 0.f; + }; + + struct multisample_state + { + std::vector chain = {}; + std::uint32_t count = 1; + std::uint32_t mask = -1; + bool alpha_to_coverage = false; + }; + + enum class blend_operation : std::uint32_t + { + add = 0x00000000, + subtract = 0x00000001, + reverse_subtract = 0x00000002, + min = 0x00000003, + max = 0x00000004, + }; + + enum class blend_factor : std::uint32_t + { + zero = 0x00000000, + one = 0x00000001, + src = 0x00000002, + one_minus_src = 0x00000003, + src_alpha = 0x00000004, + one_minus_src_alpha = 0x00000005, + dst = 0x00000006, + one_minus_dst = 0x00000007, + dst_alpha = 0x00000008, + one_minus_dst_alpha = 0x00000009, + src_alpha_saturated = 0x0000000a, + constant = 0x0000000b, + one_minus_constant = 0x0000000c, + }; + + struct blend_component + { + blend_operation operation; + blend_factor src_factor; + blend_factor dst_factor; + }; + + struct blend_state + { + blend_component color; + blend_component alpha; + }; + + enum color_write_mask : std::uint32_t + { + none = 0x00000000, + red = 0x00000001, + green = 0x00000002, + blue = 0x00000004, + alpha = 0x00000008, + all = 0x0000000f, + }; + + struct color_target_state + { + std::vector chain = {}; + texture::format format; + std::optional blend = {}; + color_write_mask write_mask = color_write_mask::all; + }; + + struct fragment_state { + std::vector chain = {}; + shader_module module; + std::string entry_point; + std::vector constants = {}; + std::vector targets; + }; + + struct render_pipeline + : detail::object + { + using detail::object::object; + + struct descriptor + { + std::vector chain = {}; + std::string label = {}; + pipeline_layout layout; + vertex_state vertex; + primitive_state primitive; + std::optional depth_stencil = {}; + multisample_state multisample = {}; + std::optional fragment = {}; + }; + + bind_group_layout get_bind_group_layout(std::uint32_t index); + void set_label(std::string const & label); + + static void reference(void * ptr); + static void release(void * ptr); + + private: + explicit render_pipeline(void * ptr) + : detail::object(ptr) + {} + + friend struct device; + }; + +} diff --git a/libs/wgpu/objects-todo b/libs/wgpu/objects-todo index e512434f..7e215ed1 100644 --- a/libs/wgpu/objects-todo +++ b/libs/wgpu/objects-todo @@ -1,6 +1,6 @@ + WGPUAdapter WGPUBindGroup - WGPUBindGroupLayout +- WGPUBindGroupLayout + WGPUBuffer + WGPUCommandBuffer - WGPUCommandEncoder @@ -14,7 +14,7 @@ WGPURenderBundle WGPURenderBundleEncoder - WGPURenderPassEncoder - WGPURenderPipeline ++ WGPURenderPipeline + WGPUSampler + WGPUShaderModule + WGPUSurface diff --git a/libs/wgpu/source/bind_group_layout.cpp b/libs/wgpu/source/bind_group_layout.cpp new file mode 100644 index 00000000..edeee21d --- /dev/null +++ b/libs/wgpu/source/bind_group_layout.cpp @@ -0,0 +1,17 @@ +#include +#include + +namespace psemek::wgpu +{ + + void bind_group_layout::reference(void * ptr) + { + wgpuBindGroupLayoutReference((WGPUBindGroupLayout)ptr); + } + + void bind_group_layout::release(void * ptr) + { + wgpuBindGroupLayoutRelease((WGPUBindGroupLayout)ptr); + } + +} diff --git a/libs/wgpu/source/device.cpp b/libs/wgpu/source/device.cpp index 6276be46..69b75e40 100644 --- a/libs/wgpu/source/device.cpp +++ b/libs/wgpu/source/device.cpp @@ -42,6 +42,110 @@ namespace psemek::wgpu return query_set(wgpuDeviceCreateQuerySet((WGPUDevice)get(), &descriptor)); } + render_pipeline device::create_render_pipeline(render_pipeline::descriptor const & desc) + { + WGPURenderPipelineDescriptor descriptor = {}; + + std::vector vertex_constants; + for (auto const & constant_in : desc.vertex.constants) + { + 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) + { + 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; + } + + return render_pipeline(wgpuDeviceCreateRenderPipeline((WGPUDevice)get(), &descriptor)); + } + sampler device::create_sampler(sampler::descriptor const & desc) { WGPUSamplerDescriptor descriptor = {}; diff --git a/libs/wgpu/source/render_pipeline.cpp b/libs/wgpu/source/render_pipeline.cpp new file mode 100644 index 00000000..d721c396 --- /dev/null +++ b/libs/wgpu/source/render_pipeline.cpp @@ -0,0 +1,27 @@ +#include +#include + +namespace psemek::wgpu +{ + + bind_group_layout render_pipeline::get_bind_group_layout(std::uint32_t index) + { + return bind_group_layout(wgpuRenderPipelineGetBindGroupLayout((WGPURenderPipeline)get(), index)); + } + + void render_pipeline::set_label(std::string const & label) + { + wgpuRenderPipelineSetLabel((WGPURenderPipeline)get(), label.data()); + } + + void render_pipeline::reference(void * ptr) + { + wgpuRenderPipelineReference((WGPURenderPipeline)ptr); + } + + void render_pipeline::release(void * ptr) + { + wgpuRenderPipelineRelease((WGPURenderPipeline)ptr); + } + +}