From 70c89b91c590da381599c7da761d1228138d73b2 Mon Sep 17 00:00:00 2001 From: Johann Muszynski Date: Fri, 29 Mar 2024 14:47:19 +0200 Subject: [PATCH] Support read-only storage buffers --- src/pt/gpu_buffer.cpp | 43 +++++------ src/pt/gpu_buffer.hpp | 122 ++++++++++++++++++++++++------ src/pt/hybrid_renderer.cpp | 14 ++-- src/pt/reference_path_tracer.cpp | 24 +++--- src/pt/reference_path_tracer.wgsl | 12 +-- src/pt/shader_source.hpp | 18 ++--- src/pt/texture_blit_renderer.cpp | 2 +- 7 files changed, 155 insertions(+), 80 deletions(-) diff --git a/src/pt/gpu_buffer.cpp b/src/pt/gpu_buffer.cpp index bff4d82..55272a4 100644 --- a/src/pt/gpu_buffer.cpp +++ b/src/pt/gpu_buffer.cpp @@ -13,23 +13,22 @@ void bufferSafeRelease(WGPUBuffer buffer) } } -WGPUBufferBindingType bufferUsageToBufferBindingType(const WGPUBufferUsageFlags usage) +inline WGPUBufferBindingType gpuBufferUsageToWGPUBufferBindingType(const GpuBufferUsage usage) { - assert(usage != WGPUBufferUsage_None); - - // BufferUsage flags contains redundant binding type information that we can reuse, so that the - // user doesn't have to provide the additional flag. - - if (usage & WGPUBufferUsage_Uniform) + if ((usage & GpuBufferUsage::Storage) == GpuBufferUsage::Storage) { - return WGPUBufferBindingType_Uniform; + return WGPUBufferBindingType_Storage; } - else if (usage & WGPUBufferUsage_Storage) + else if ((usage & GpuBufferUsage::ReadOnlyStorage) == GpuBufferUsage::ReadOnlyStorage) { - return WGPUBufferBindingType_Storage; + return WGPUBufferBindingType_ReadOnlyStorage; + } + else if ((usage & GpuBufferUsage::Uniform) == GpuBufferUsage::Uniform) + { + return WGPUBufferBindingType_Uniform; } - assert(!"No matching WGPUBufferBindingType for WGPUBufferUsage."); + NLRS_ASSERT("Invalid buffer usage for binding type!"); return WGPUBufferBindingType_Undefined; } @@ -45,7 +44,7 @@ GpuBuffer::GpuBuffer(GpuBuffer&& other) noexcept other.mBuffer = nullptr; other.mByteSize = 0; - other.mUsage = WGPUBufferUsage_None; + other.mUsage = GpuBufferUsage::None; } } @@ -61,26 +60,26 @@ GpuBuffer& GpuBuffer::operator=(GpuBuffer&& other) noexcept other.mBuffer = nullptr; other.mByteSize = 0; - other.mUsage = WGPUBufferUsage_None; + other.mUsage = GpuBufferUsage::None; } return *this; } GpuBuffer::GpuBuffer( - const WGPUDevice device, - const char* const label, - const WGPUBufferUsageFlags usage, - const std::size_t byteSize) + const WGPUDevice device, + const char* const label, + const GpuBufferUsage usage, + const std::size_t byteSize) : mBuffer(nullptr), mByteSize(byteSize), mUsage(usage) { - assert(device != nullptr); + NLRS_ASSERT(device != nullptr); const WGPUBufferDescriptor bufferDesc{ .nextInChain = nullptr, .label = label, - .usage = mUsage, + .usage = gpuBufferUsageToWGPUBufferUsage(usage), .size = mByteSize, .mappedAtCreation = false, }; @@ -103,14 +102,14 @@ WGPUBindGroupLayoutEntry GpuBuffer::bindGroupLayoutEntry( const WGPUShaderStageFlags visibility, const std::size_t minBindingSize) const { - assert(mBuffer != nullptr); - const WGPUBufferBindingType bindingType = bufferUsageToBufferBindingType(mUsage); + NLRS_ASSERT(mBuffer != nullptr); + const WGPUBufferBindingType bindingType = gpuBufferUsageToWGPUBufferBindingType(mUsage); return bufferBindGroupLayoutEntry(bindingIdx, visibility, bindingType, minBindingSize); } WGPUBindGroupEntry GpuBuffer::bindGroupEntry(const std::uint32_t bindingIdx) const { - assert(mBuffer != nullptr); + NLRS_ASSERT(mBuffer != nullptr); return bufferBindGroupEntry(bindingIdx, mBuffer, mByteSize); } } // namespace nlrs diff --git a/src/pt/gpu_buffer.hpp b/src/pt/gpu_buffer.hpp index e3559f4..b5302f5 100644 --- a/src/pt/gpu_buffer.hpp +++ b/src/pt/gpu_buffer.hpp @@ -2,10 +2,11 @@ #include "webgpu_utils.hpp" +#include + #include #include -#include #include #include #include @@ -14,6 +15,22 @@ namespace nlrs { +enum class GpuBufferUsage : uint32_t +{ + None = 0, + CopySrc = 1 << 0, + CopyDst = 1 << 1, + MapRead = 1 << 2, + MapWrite = 1 << 3, + Index = 1 << 4, + Vertex = 1 << 5, + Uniform = 1 << 6, + Storage = 1 << 7, + ReadOnlyStorage = 1 << 8, + Indirect = 1 << 9, + QueryResolve = 1 << 10, +}; + // A wrapper around WGPUBuffer with unique ownership semantics. class GpuBuffer { @@ -26,18 +43,10 @@ class GpuBuffer GpuBuffer(GpuBuffer&&) noexcept; GpuBuffer& operator=(GpuBuffer&&) noexcept; - GpuBuffer( - WGPUDevice device, - const char* label, - WGPUBufferUsageFlags usage, - std::size_t byteSize); + GpuBuffer(WGPUDevice device, const char* label, GpuBufferUsage usage, std::size_t byteSize); template - GpuBuffer( - WGPUDevice device, - const char* label, - WGPUBufferUsageFlags usage, - std::span data); + GpuBuffer(WGPUDevice device, const char* label, GpuBufferUsage usage, std::span data); ~GpuBuffer(); @@ -45,12 +54,12 @@ class GpuBuffer inline WGPUBuffer ptr() const noexcept { - assert(mBuffer != nullptr); + NLRS_ASSERT(mBuffer != nullptr); return mBuffer; } inline std::size_t byteSize() const noexcept { - assert(mByteSize > 0); + NLRS_ASSERT(mByteSize > 0); return mByteSize; } @@ -63,27 +72,94 @@ class GpuBuffer WGPUBindGroupEntry bindGroupEntry(std::uint32_t bindingIndex) const; private: - WGPUBuffer mBuffer = nullptr; - std::size_t mByteSize = 0; - WGPUBufferUsageFlags mUsage = WGPUBufferUsage_None; + WGPUBuffer mBuffer = nullptr; + std::size_t mByteSize = 0; + GpuBufferUsage mUsage = GpuBufferUsage::None; }; +inline GpuBufferUsage operator|(GpuBufferUsage lhs, GpuBufferUsage rhs) noexcept +{ + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +inline GpuBufferUsage operator|=(GpuBufferUsage& lhs, GpuBufferUsage rhs) noexcept +{ + return lhs = lhs | rhs; +} + +inline GpuBufferUsage operator&(GpuBufferUsage lhs, GpuBufferUsage rhs) noexcept +{ + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + +inline WGPUBufferUsageFlags gpuBufferUsageToWGPUBufferUsage(const GpuBufferUsage usage) noexcept +{ + WGPUBufferUsageFlags wgpuUsage = WGPUBufferUsage_None; + + if ((usage & GpuBufferUsage::CopySrc) == GpuBufferUsage::CopySrc) + { + wgpuUsage |= WGPUBufferUsage_CopySrc; + } + if ((usage & GpuBufferUsage::CopyDst) == GpuBufferUsage::CopyDst) + { + wgpuUsage |= WGPUBufferUsage_CopyDst; + } + if ((usage & GpuBufferUsage::MapRead) == GpuBufferUsage::MapRead) + { + wgpuUsage |= WGPUBufferUsage_MapRead; + } + if ((usage & GpuBufferUsage::MapWrite) == GpuBufferUsage::MapWrite) + { + wgpuUsage |= WGPUBufferUsage_MapWrite; + } + if ((usage & GpuBufferUsage::Index) == GpuBufferUsage::Index) + { + wgpuUsage |= WGPUBufferUsage_Index; + } + if ((usage & GpuBufferUsage::Vertex) == GpuBufferUsage::Vertex) + { + wgpuUsage |= WGPUBufferUsage_Vertex; + } + if ((usage & GpuBufferUsage::Uniform) == GpuBufferUsage::Uniform) + { + wgpuUsage |= WGPUBufferUsage_Uniform; + } + if ((usage & GpuBufferUsage::Storage) == GpuBufferUsage::Storage) + { + wgpuUsage |= WGPUBufferUsage_Storage; + } + if ((usage & GpuBufferUsage::ReadOnlyStorage) == GpuBufferUsage::ReadOnlyStorage) + { + wgpuUsage |= WGPUBufferUsage_Storage; + } + if ((usage & GpuBufferUsage::Indirect) == GpuBufferUsage::Indirect) + { + wgpuUsage |= WGPUBufferUsage_Indirect; + } + if ((usage & GpuBufferUsage::QueryResolve) == GpuBufferUsage::QueryResolve) + { + wgpuUsage |= WGPUBufferUsage_QueryResolve; + } + + return wgpuUsage; +} + template GpuBuffer::GpuBuffer( - const WGPUDevice device, - const char* const label, - const WGPUBufferUsageFlags usage, - const std::span data) + const WGPUDevice device, + const char* const label, + const GpuBufferUsage usage, + const std::span data) : mBuffer(nullptr), mByteSize(sizeof(T) * data.size()), mUsage(usage) { - assert(device != nullptr); + NLRS_ASSERT(device != nullptr); const WGPUBufferDescriptor bufferDesc{ .nextInChain = nullptr, .label = label, - .usage = mUsage, + .usage = gpuBufferUsageToWGPUBufferUsage(usage), .size = mByteSize, .mappedAtCreation = true, }; @@ -99,7 +175,7 @@ GpuBuffer::GpuBuffer( // https://www.w3.org/TR/webgpu/#dom-gpubufferdescriptor-mappedatcreation void* const mappedData = wgpuBufferGetMappedRange(mBuffer, 0, mByteSize); - assert(mappedData); + NLRS_ASSERT(mappedData); std::memcpy(mappedData, data.data(), mByteSize); wgpuBufferUnmap(mBuffer); } diff --git a/src/pt/hybrid_renderer.cpp b/src/pt/hybrid_renderer.cpp index 7cf316c..926e15f 100644 --- a/src/pt/hybrid_renderer.cpp +++ b/src/pt/hybrid_renderer.cpp @@ -301,7 +301,7 @@ HybridRenderer::GbufferPass::GbufferPass( std::back_inserter(buffers), [&gpuContext](const std::span vertices) -> GpuBuffer { return GpuBuffer( - gpuContext.device, "Mesh Vertex Buffer", WGPUBufferUsage_Vertex, vertices); + gpuContext.device, "Mesh Vertex Buffer", GpuBufferUsage::Vertex, vertices); }); return buffers; }()), @@ -313,7 +313,7 @@ HybridRenderer::GbufferPass::GbufferPass( std::back_inserter(buffers), [&gpuContext](const std::span normals) -> GpuBuffer { return GpuBuffer{ - gpuContext.device, "Mesh normal buffer", WGPUBufferUsage_Vertex, normals}; + gpuContext.device, "Mesh normal buffer", GpuBufferUsage::Vertex, normals}; }); return buffers; }()), @@ -325,7 +325,7 @@ HybridRenderer::GbufferPass::GbufferPass( std::back_inserter(buffers), [&gpuContext](const std::span texCoords) { return GpuBuffer( - gpuContext.device, "Mesh TexCoord Buffer", WGPUBufferUsage_Vertex, texCoords); + gpuContext.device, "Mesh TexCoord Buffer", GpuBufferUsage::Vertex, texCoords); }); return buffers; }()), @@ -338,7 +338,7 @@ HybridRenderer::GbufferPass::GbufferPass( [&gpuContext](const std::span indices) { return IndexBuffer{ .buffer = GpuBuffer( - gpuContext.device, "Mesh Index Buffer", WGPUBufferUsage_Index, indices), + gpuContext.device, "Mesh Index Buffer", GpuBufferUsage::Index, indices), .count = static_cast(indices.size()), .format = WGPUIndexFormat_Uint32, }; @@ -425,7 +425,7 @@ HybridRenderer::GbufferPass::GbufferPass( mUniformBuffer( gpuContext.device, "Uniform buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, + GpuBufferUsage::Uniform | GpuBufferUsage::CopyDst, sizeof(glm::mat4)), mUniformBindGroup(), mSamplerBindGroup(), @@ -790,7 +790,7 @@ HybridRenderer::DebugPass::DebugPass( : mVertexBuffer( gpuContext.device, "Vertex buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, + GpuBufferUsage::Vertex | GpuBufferUsage::CopyDst, std::span(quadVertexData)), mUniformBuffer(), mUniformBindGroup(), @@ -801,7 +801,7 @@ HybridRenderer::DebugPass::DebugPass( mUniformBuffer = GpuBuffer{ gpuContext.device, "Uniform buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, + GpuBufferUsage::Uniform | GpuBufferUsage::CopyDst, std::span(&uniformData.x, sizeof(Extent2))}; } diff --git a/src/pt/reference_path_tracer.cpp b/src/pt/reference_path_tracer.cpp index d87e9cb..2e26952 100644 --- a/src/pt/reference_path_tracer.cpp +++ b/src/pt/reference_path_tracer.cpp @@ -172,38 +172,38 @@ ReferencePathTracer::ReferencePathTracer( : mVertexBuffer( gpuContext.device, "Vertex buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, + GpuBufferUsage::Vertex | GpuBufferUsage::CopyDst, std::span(quadVertexData)), mRenderParamsBuffer( gpuContext.device, "render params buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, + GpuBufferUsage::Uniform | GpuBufferUsage::CopyDst, sizeof(RenderParamsLayout)), mPostProcessingParamsBuffer( gpuContext.device, "post processing params buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, + GpuBufferUsage::Uniform | GpuBufferUsage::CopyDst, sizeof(PostProcessingParameters)), mSkyStateBuffer( gpuContext.device, "sky state buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage, + GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst, sizeof(SkyStateLayout)), mRenderParamsBindGroup(), mBvhNodeBuffer( gpuContext.device, "bvh nodes buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage, + GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst, std::span(scene.bvhNodes)), mPositionAttributesBuffer( gpuContext.device, "position attributes buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage, + GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst, std::span(scene.positionAttributes)), mVertexAttributesBuffer( gpuContext.device, "vertex attributes buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage, + GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst, std::span(scene.vertexAttributes)), mTextureDescriptorBuffer(), mTextureBuffer(), @@ -211,19 +211,19 @@ ReferencePathTracer::ReferencePathTracer( mImageBuffer( gpuContext.device, "image buffer", - WGPUBufferUsage_Storage, + GpuBufferUsage::Storage, sizeof(float[4]) * rendererDesc.maxFramebufferSize.x * rendererDesc.maxFramebufferSize.y), mImageBindGroup(), mQuerySet(nullptr), mQueryBuffer( gpuContext.device, "render pass query buffer", - WGPUBufferUsage_QueryResolve | WGPUBufferUsage_CopySrc, + GpuBufferUsage::QueryResolve | GpuBufferUsage::CopySrc, sizeof(TimestampsLayout)), mTimestampBuffer( gpuContext.device, "render pass timestamp buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead, + GpuBufferUsage::CopyDst | GpuBufferUsage::MapRead, sizeof(TimestampsLayout)), mRenderPipeline(nullptr), mCurrentRenderParams(rendererDesc.renderParams), @@ -273,7 +273,7 @@ ReferencePathTracer::ReferencePathTracer( mTextureDescriptorBuffer = GpuBuffer( gpuContext.device, "texture descriptor buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage, + GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst, std::span(textureDescriptors)); const std::size_t textureDataNumBytes = textureData.size() * sizeof(Texture::BgraPixel); @@ -291,7 +291,7 @@ ReferencePathTracer::ReferencePathTracer( mTextureBuffer = GpuBuffer( gpuContext.device, "texture buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage, + GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst, std::span(textureData)); } diff --git a/src/pt/reference_path_tracer.wgsl b/src/pt/reference_path_tracer.wgsl index c165d9b..39c728a 100644 --- a/src/pt/reference_path_tracer.wgsl +++ b/src/pt/reference_path_tracer.wgsl @@ -18,16 +18,16 @@ fn vsMain(in: VertexInput) -> VertexOutput { // render params bind group @group(0) @binding(0) var renderParams: RenderParams; @group(0) @binding(1) var postProcessingParams: PostProcessingParams; -@group(0) @binding(2) var skyState: SkyState; +@group(0) @binding(2) var skyState: SkyState; // scene bind group // TODO: these are `read` only buffers. How can I create a buffer layout type which allows this? // Annotating these as read causes validation failures. -@group(1) @binding(0) var bvhNodes: array; -@group(1) @binding(1) var positionAttributes: array; -@group(1) @binding(2) var vertexAttributes: array; -@group(1) @binding(3) var textureDescriptors: array; -@group(1) @binding(4) var textures: array; +@group(1) @binding(0) var bvhNodes: array; +@group(1) @binding(1) var positionAttributes: array; +@group(1) @binding(2) var vertexAttributes: array; +@group(1) @binding(3) var textureDescriptors: array; +@group(1) @binding(4) var textures: array; // image bind group @group(2) @binding(0) var imageBuffer: array; diff --git a/src/pt/shader_source.hpp b/src/pt/shader_source.hpp index efe2f98..69208fa 100644 --- a/src/pt/shader_source.hpp +++ b/src/pt/shader_source.hpp @@ -24,16 +24,16 @@ fn vsMain(in: VertexInput) -> VertexOutput { // render params bind group @group(0) @binding(0) var renderParams: RenderParams; @group(0) @binding(1) var postProcessingParams: PostProcessingParams; -@group(0) @binding(2) var skyState: SkyState; +@group(0) @binding(2) var skyState: SkyState; // scene bind group // TODO: these are `read` only buffers. How can I create a buffer layout type which allows this? // Annotating these as read causes validation failures. -@group(1) @binding(0) var bvhNodes: array; -@group(1) @binding(1) var positionAttributes: array; -@group(1) @binding(2) var vertexAttributes: array; -@group(1) @binding(3) var textureDescriptors: array; -@group(1) @binding(4) var textures: array; +@group(1) @binding(0) var bvhNodes: array; +@group(1) @binding(1) var positionAttributes: array; +@group(1) @binding(2) var vertexAttributes: array; +@group(1) @binding(3) var textureDescriptors: array; +@group(1) @binding(4) var textures: array; // image bind group @group(2) @binding(0) var imageBuffer: array; @@ -549,9 +549,9 @@ const INT_SCALE = 256f; @must_use fn offsetRay(p: vec3f, n: vec3f) -> vec3f { - // Source: A Fast and Robust Method for Avoiding Self-Intersection, Ray Tracing Ge)" -R"(ms - let offset = vec3i(i32(INT_SCALE * n.x), i32(INT_SCALE * n.y), i32(INT_SCALE * n.z)); + // Source: A Fast and Robust Method for Avoiding Self-Intersection, Ray Tracing Gems + let offset = vec3i(i32(INT_SC)" +R"(ALE * n.x), i32(INT_SCALE * n.y), i32(INT_SCALE * n.z)); // Offset added straight into the mantissa bits to ensure the offset is scale-invariant, // except for when close to the origin, where we use FLOAT_SCALE as a small epsilon. let po = vec3f( diff --git a/src/pt/texture_blit_renderer.cpp b/src/pt/texture_blit_renderer.cpp index 593bcab..d57c511 100644 --- a/src/pt/texture_blit_renderer.cpp +++ b/src/pt/texture_blit_renderer.cpp @@ -23,7 +23,7 @@ TextureBlitRenderer::TextureBlitRenderer( : mVertexBuffer( gpuContext.device, "Vertex buffer", - WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, + GpuBufferUsage::Vertex | GpuBufferUsage::CopyDst, std::span(quadVertexData)), mTexture(nullptr), mTextureView(nullptr),