diff --git a/src/Video/CMakeLists.txt b/src/Video/CMakeLists.txt index 640dd064..d5f2cba9 100644 --- a/src/Video/CMakeLists.txt +++ b/src/Video/CMakeLists.txt @@ -168,6 +168,7 @@ if (WebGPURenderer) ${SRCS} LowLevelRenderer/WebGPU/WebGPURenderer.cpp LowLevelRenderer/WebGPU/WebGPUBuffer.cpp + LowLevelRenderer/WebGPU/WebGPUBufferAllocator.cpp LowLevelRenderer/WebGPU/WebGPUCommandBuffer.cpp LowLevelRenderer/WebGPU/WebGPUComputePipeline.cpp LowLevelRenderer/WebGPU/WebGPUGeometryBinding.cpp @@ -185,6 +186,7 @@ if (WebGPURenderer) LowLevelRenderer/WebGPU/WebGPU.hpp LowLevelRenderer/WebGPU/WebGPURenderer.hpp LowLevelRenderer/WebGPU/WebGPUBuffer.hpp + LowLevelRenderer/WebGPU/WebGPUBufferAllocator.hpp LowLevelRenderer/WebGPU/WebGPUCommandBuffer.hpp LowLevelRenderer/WebGPU/WebGPUComputePipeline.hpp LowLevelRenderer/WebGPU/WebGPUGeometryBinding.hpp diff --git a/src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.cpp b/src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.cpp index 4d263a40..35aa5067 100644 --- a/src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.cpp +++ b/src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.cpp @@ -1,65 +1,29 @@ #include "WebGPUBuffer.hpp" +#include "WebGPUBufferAllocator.hpp" #include #include namespace Video { -WebGPUBuffer::WebGPUBuffer(WGPUDevice device, Buffer::BufferUsage bufferUsage, uint32_t size, const void* data) : Buffer(bufferUsage) { - WGPUBufferDescriptor bufferDescriptor = {}; - switch (bufferUsage) { - case Buffer::BufferUsage::VERTEX_BUFFER: - bufferDescriptor.usage = WGPUBufferUsage_Vertex; - break; - case Buffer::BufferUsage::INDEX_BUFFER: - bufferDescriptor.usage = WGPUBufferUsage_Index; - break; - case Buffer::BufferUsage::UNIFORM_BUFFER: - bufferDescriptor.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst; - // Buffer binding sizes must be a multiple of 16. - size = (size + 16 - 1) / 16 * 16; - break; - case Buffer::BufferUsage::STORAGE_BUFFER: - bufferDescriptor.usage = WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst; - // Buffer binding sizes must be a multiple of 16. - size = (size + 16 - 1) / 16 * 16; - break; - case Buffer::BufferUsage::VERTEX_STORAGE_BUFFER: - bufferDescriptor.usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst; - // Buffer binding sizes must be a multiple of 16. - size = (size + 16 - 1) / 16 * 16; - break; - } - - if (data != nullptr) { - // We are allowed to set mappedAtCreation even without MAP_READ or MAP_WRITE usage flags. This allows us to initialize the buffer with data. - bufferDescriptor.mappedAtCreation = true; - - // Buffers that are mapped at creation must have a size that is a multiple of 4. - size = (size + 4 - 1) / 4 * 4; - } - - this->size = size; - bufferDescriptor.size = size; - - buffer = wgpuDeviceCreateBuffer(device, &bufferDescriptor); - - if (data != nullptr) { - // Copy the initial data. - void* mappedData = wgpuBufferGetMappedRange(buffer, 0, size); - memcpy(mappedData, data, size); - wgpuBufferUnmap(buffer); - } +WebGPUBuffer::WebGPUBuffer(Buffer::BufferUsage bufferUsage, const BufferAllocation& allocation) : Buffer(bufferUsage) { + Reset(bufferUsage, allocation); } WebGPUBuffer::~WebGPUBuffer() { - wgpuBufferRelease(buffer); + if (!temporaryAllocation) { + delete rawBuffer; + } } void WebGPUBuffer::Reset(BufferUsage bufferUsage, const BufferAllocation& allocation) { - assert(false); - - Log(Log::ERR) << "WebGPUBuffer::Reset should never be called.\n"; + this->bufferUsage = bufferUsage; + rawBuffer = allocation.buffer; + WebGPURawBuffer* webGPURawBuffer = static_cast(rawBuffer); + buffer = webGPURawBuffer->GetBuffer(); + offset = allocation.offset; + size = allocation.size; + temporaryAllocation = allocation.temporaryAllocation; } unsigned int WebGPUBuffer::GetSize() const { @@ -70,4 +34,8 @@ WGPUBuffer WebGPUBuffer::GetBuffer() const { return buffer; } +uint32_t WebGPUBuffer::GetOffset() const { + return offset; +} + } // namespace Video diff --git a/src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.hpp b/src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.hpp index f50dc7d3..696d0e67 100644 --- a/src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.hpp +++ b/src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.hpp @@ -13,12 +13,10 @@ class WebGPUBuffer : public Buffer { public: /// Create new WebGPU buffer. /** - * @param device The WebGPU device. * @param bufferUsage How the buffer will be used. - * @param size The size of the buffer. - * @param data Data to initialie the buffer with. + * @param allocation The allocation to encapsulate. */ - WebGPUBuffer(WGPUDevice device, Buffer::BufferUsage bufferUsage, uint32_t size, const void* data = nullptr); + WebGPUBuffer(Buffer::BufferUsage bufferUsage, const BufferAllocation& allocation); /// Destructor. ~WebGPUBuffer() final; @@ -32,11 +30,20 @@ class WebGPUBuffer : public Buffer { */ WGPUBuffer GetBuffer() const; + /// Get the offset into the raw buffer. + /** + * @return The offset into the raw buffer. + */ + uint32_t GetOffset() const; + private: WebGPUBuffer(const WebGPUBuffer& other) = delete; + RawBuffer* rawBuffer; WGPUBuffer buffer; + uint32_t offset = 0; uint32_t size = 0; + bool temporaryAllocation; }; } diff --git a/src/Video/LowLevelRenderer/WebGPU/WebGPUBufferAllocator.cpp b/src/Video/LowLevelRenderer/WebGPU/WebGPUBufferAllocator.cpp new file mode 100644 index 00000000..3fdbd5f0 --- /dev/null +++ b/src/Video/LowLevelRenderer/WebGPU/WebGPUBufferAllocator.cpp @@ -0,0 +1,85 @@ +#include "WebGPUBufferAllocator.hpp" + +#include "WebGPUBuffer.hpp" +#include "WebGPURenderer.hpp" + +namespace Video { + +static WGPUBufferUsageFlags GetBufferUsage(Buffer::BufferUsage bufferUsage) { + WGPUBufferUsageFlags usage; + + switch (bufferUsage) { + case Buffer::BufferUsage::VERTEX_BUFFER: + usage = WGPUBufferUsage_Vertex; + break; + case Buffer::BufferUsage::INDEX_BUFFER: + usage = WGPUBufferUsage_Index; + break; + case Buffer::BufferUsage::UNIFORM_BUFFER: + usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst; + break; + case Buffer::BufferUsage::STORAGE_BUFFER: + usage = WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst; + break; + case Buffer::BufferUsage::VERTEX_STORAGE_BUFFER: + usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst; + break; + } + + // For wgpuQueueWriteBuffer. + usage |= WGPUBufferUsage_CopyDst; + + return usage; +} + +WebGPURawBuffer::WebGPURawBuffer(WebGPURenderer& webGPURenderer, Buffer::BufferUsage bufferUsage, bool temporary, unsigned int size) { + this->temporary = temporary; + queue = webGPURenderer.GetQueue(); + + // Create buffer. + WGPUBufferDescriptor bufferDescriptor = {}; + bufferDescriptor.usage = GetBufferUsage(bufferUsage); + bufferDescriptor.size = size; + + buffer = wgpuDeviceCreateBuffer(webGPURenderer.GetDevice(), &bufferDescriptor); +} + +WebGPURawBuffer::~WebGPURawBuffer() { + wgpuBufferRelease(buffer); +} + +void WebGPURawBuffer::Write(uint32_t offset, uint32_t size, const void* data) { + /// @todo Could use mapOnCreate for non-temporary buffers? + wgpuQueueWriteBuffer(queue, buffer, offset, data, size); +} + +WGPUBuffer WebGPURawBuffer::GetBuffer() const { + return buffer; +} + +WebGPUBufferAllocator::WebGPUBufferAllocator(WebGPURenderer& webGPURenderer) : BufferAllocator(1), webGPURenderer(webGPURenderer) { + +} + +WebGPUBufferAllocator::~WebGPUBufferAllocator() { + +} + +uint32_t WebGPUBufferAllocator::GetAlignment(Buffer::BufferUsage bufferUsage) { + // Both minUniformBufferOffsetAlignment and minStorageBufferOffsetAlignment are guaranteed to be at most 256. + // https://www.w3.org/TR/webgpu/#dom-gpusupportedlimits-minuniformbufferoffsetalignment + return 256; +} + +RawBuffer* WebGPUBufferAllocator::Allocate(Buffer::BufferUsage bufferUsage, bool temporary, unsigned int size) { + // Buffer binding sizes must be a multiple of 16. + size = (size + 16 - 1) / 16 * 16; + + return new WebGPURawBuffer(webGPURenderer, bufferUsage, temporary, size); +} + +Buffer* WebGPUBufferAllocator::CreateBufferObject(Buffer::BufferUsage bufferUsage, const BufferAllocation& allocation) { + return new WebGPUBuffer(bufferUsage, allocation); +} + +} diff --git a/src/Video/LowLevelRenderer/WebGPU/WebGPUBufferAllocator.hpp b/src/Video/LowLevelRenderer/WebGPU/WebGPUBufferAllocator.hpp new file mode 100644 index 00000000..ec0b8b49 --- /dev/null +++ b/src/Video/LowLevelRenderer/WebGPU/WebGPUBufferAllocator.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "../Interface/BufferAllocator.hpp" +#include "WebGPU.hpp" + +namespace Video { + +class WebGPURenderer; + +/// WebGPU implementation of RawBuffer. +class WebGPURawBuffer : public RawBuffer { + public: + /// Create new raw buffer used to satisfy allocations. + /** + * @param webGPURenderer The WebGPU renderer. + * @param bufferUsage How the buffer will be used. + * @param temporary Whether the buffer is going to be used to satisfy temporary allocations. + * @param size The size of the buffer in bytes. + */ + WebGPURawBuffer(WebGPURenderer& webGPURenderer, Buffer::BufferUsage bufferUsage, bool temporary, unsigned int size); + + /// Destructor. + ~WebGPURawBuffer() final; + + void Write(uint32_t offset, uint32_t size, const void* data) final; + + /// Get the internal WebGPU buffer. + /** + * @return The internal WebGPU buffer. + */ + WGPUBuffer GetBuffer() const; + + private: + WGPUBuffer buffer; + WGPUQueue queue; + bool temporary; +}; + +/// WebGPU implementation of BufferAllocator. +class WebGPUBufferAllocator : public BufferAllocator { + public: + /// Create a new buffer allocator. + /** + * @param webGPURenderer The WebGPU renderer. + */ + explicit WebGPUBufferAllocator(WebGPURenderer& webGPURenderer); + + /// Destructor. + ~WebGPUBufferAllocator() final; + + private: + WebGPUBufferAllocator(const WebGPUBufferAllocator& other) = delete; + + uint32_t GetAlignment(Buffer::BufferUsage bufferUsage) final; + RawBuffer* Allocate(Buffer::BufferUsage bufferUsage, bool temporary, unsigned int size) final; + Buffer* CreateBufferObject(Buffer::BufferUsage bufferUsage, const BufferAllocation& allocation) final; + + WebGPURenderer& webGPURenderer; +}; + +} diff --git a/src/Video/LowLevelRenderer/WebGPU/WebGPUCommandBuffer.cpp b/src/Video/LowLevelRenderer/WebGPU/WebGPUCommandBuffer.cpp index bc57f97d..34095d41 100644 --- a/src/Video/LowLevelRenderer/WebGPU/WebGPUCommandBuffer.cpp +++ b/src/Video/LowLevelRenderer/WebGPU/WebGPUCommandBuffer.cpp @@ -253,6 +253,7 @@ void WebGPUCommandBuffer::BindUniformBuffer(ShaderProgram::BindingType bindingTy entry.binding = 0; entry.buffer = static_cast(uniformBuffer)->GetBuffer(); entry.size = uniformBuffer->GetSize(); + entry.offset = static_cast(uniformBuffer)->GetOffset(); WGPUBindGroupDescriptor bindGroupDescriptor = {}; bindGroupDescriptor.layout = currentGraphicsPipeline->GetShaderProgram()->GetBindGroupLayouts()[bindingType]; @@ -280,6 +281,7 @@ void WebGPUCommandBuffer::BindStorageBuffers(std::initializer_list buff entries[i].binding = i; entries[i].buffer = static_cast(buffer)->GetBuffer(); entries[i].size = buffer->GetSize(); + entries[i].offset = static_cast(buffer)->GetOffset(); ++i; } @@ -554,6 +556,7 @@ void WebGPUCommandBuffer::UpdateUniforms() { entries[bindGroupDescriptor.entryCount].binding = 0; entries[bindGroupDescriptor.entryCount].buffer = currentUniformBuffer->GetBuffer(); entries[bindGroupDescriptor.entryCount].size = currentUniformBuffer->GetSize(); + entries[bindGroupDescriptor.entryCount].offset = currentUniformBuffer->GetOffset(); ++bindGroupDescriptor.entryCount; } diff --git a/src/Video/LowLevelRenderer/WebGPU/WebGPURenderer.cpp b/src/Video/LowLevelRenderer/WebGPU/WebGPURenderer.cpp index 8a0585ce..3929ea22 100644 --- a/src/Video/LowLevelRenderer/WebGPU/WebGPURenderer.cpp +++ b/src/Video/LowLevelRenderer/WebGPU/WebGPURenderer.cpp @@ -21,6 +21,7 @@ #include "WebGPUSampler.hpp" #include "WebGPUGraphicsPipeline.hpp" #include "WebGPUComputePipeline.hpp" +#include "WebGPUBufferAllocator.hpp" #include "WebGPURenderTargetAllocator.hpp" #if ANDROID @@ -54,6 +55,7 @@ WebGPURenderer::WebGPURenderer(::Utility::Window* window) { CreateSwapChain(window); CreateSamplers(); + bufferAllocator = new WebGPUBufferAllocator(*this); renderTargetAllocator = new WebGPURenderTargetAllocator(*this); // Blitting. @@ -68,16 +70,13 @@ WebGPURenderer::WebGPURenderer(::Utility::Window* window) { configuration.blendMode = BlendMode::NONE; configuration.depthMode = DepthMode::NONE; blitGraphicsPipeline = CreateGraphicsPipeline(blitShaderProgram, configuration); - - //TestRendering(); } WebGPURenderer::~WebGPURenderer() { - ReleaseTemporaryBuffers(); - for (uint32_t i = 0; i < static_cast(Sampler::Filter::COUNT) * static_cast(Sampler::Clamping::COUNT); ++i) { delete samplers[i]; } + delete bufferAllocator; delete renderTargetAllocator; delete blitGraphicsPipeline; @@ -100,7 +99,7 @@ CommandBuffer* WebGPURenderer::CreateCommandBuffer() { } void WebGPURenderer::BeginFrame() { - ReleaseTemporaryBuffers(); + bufferAllocator->BeginFrame(); // Acquire next swap chain image. currentFrame = wgpuSwapChainGetCurrentTextureView(swapChain); @@ -126,13 +125,11 @@ void WebGPURenderer::Present() { } Buffer* WebGPURenderer::CreateBuffer(Buffer::BufferUsage bufferUsage, uint32_t size, const void* data) { - return new WebGPUBuffer(device, bufferUsage, size, data); + return bufferAllocator->CreateBuffer(bufferUsage, size, data); } Buffer* WebGPURenderer::CreateTemporaryBuffer(Buffer::BufferUsage bufferUsage, uint32_t size, const void* data) { - WebGPUBuffer* buffer = new WebGPUBuffer(device, bufferUsage, size ,data); - temporaryBuffers.push_back(buffer); - return buffer; + return bufferAllocator->CreateTemporaryBuffer(bufferUsage, size, data); } VertexDescription* WebGPURenderer::CreateVertexDescription(unsigned int attributeCount, const VertexDescription::Attribute* attributes, bool indexBuffer) { @@ -536,11 +533,4 @@ void WebGPURenderer::CreateSamplers() { } } -void WebGPURenderer::ReleaseTemporaryBuffers() { - for (WebGPUBuffer* buffer : temporaryBuffers) { - delete buffer; - } - temporaryBuffers.clear(); -} - } // namespace Video diff --git a/src/Video/LowLevelRenderer/WebGPU/WebGPURenderer.hpp b/src/Video/LowLevelRenderer/WebGPU/WebGPURenderer.hpp index c42e829e..ca4b0fc5 100644 --- a/src/Video/LowLevelRenderer/WebGPU/WebGPURenderer.hpp +++ b/src/Video/LowLevelRenderer/WebGPU/WebGPURenderer.hpp @@ -13,6 +13,7 @@ class Window; namespace Video { class WebGPUBuffer; +class WebGPUBufferAllocator; class WebGPUSampler; class WebGPURenderTargetAllocator; class WebGPUTexture; @@ -116,8 +117,6 @@ class WebGPURenderer : public LowLevelRenderer { void CreateSwapChain(::Utility::Window* window); void CreateSamplers(); - void ReleaseTemporaryBuffers(); - WGPUInstance instance; WGPUSurface surface; WGPUAdapter adapter; @@ -128,6 +127,7 @@ class WebGPURenderer : public LowLevelRenderer { WebGPUSampler* samplers[static_cast(Sampler::Filter::COUNT) * static_cast(Sampler::Clamping::COUNT)]; + WebGPUBufferAllocator* bufferAllocator; WebGPURenderTargetAllocator* renderTargetAllocator; OptionalFeatures optionalFeatures; @@ -135,8 +135,6 @@ class WebGPURenderer : public LowLevelRenderer { glm::uvec2 swapChainSize; WGPUTextureFormat swapChainFormat; - std::vector temporaryBuffers; - // Blitting to swap chain. Shader* blitVertexShader; Shader* blitFragmentShader;