Skip to content

Commit

Permalink
Suballocate buffers
Browse files Browse the repository at this point in the history
Use the same buffer suballocation strategy in WebGPU as in Vulkan and OpenGL.
This means we will reuse buffers to satisfy temporary buffer requests instead of allocating new ones every frame.
  • Loading branch information
Chainsawkitten committed May 3, 2023
1 parent c5744b1 commit 762d43c
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 73 deletions.
2 changes: 2 additions & 0 deletions src/Video/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
66 changes: 17 additions & 49 deletions src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.cpp
Original file line number Diff line number Diff line change
@@ -1,65 +1,29 @@
#include "WebGPUBuffer.hpp"

#include "WebGPUBufferAllocator.hpp"
#include <Utility/Log.hpp>
#include <cstring>

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<WebGPURawBuffer*>(rawBuffer);
buffer = webGPURawBuffer->GetBuffer();
offset = allocation.offset;
size = allocation.size;
temporaryAllocation = allocation.temporaryAllocation;
}

unsigned int WebGPUBuffer::GetSize() const {
Expand All @@ -70,4 +34,8 @@ WGPUBuffer WebGPUBuffer::GetBuffer() const {
return buffer;
}

uint32_t WebGPUBuffer::GetOffset() const {
return offset;
}

} // namespace Video
15 changes: 11 additions & 4 deletions src/Video/LowLevelRenderer/WebGPU/WebGPUBuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
};

}
85 changes: 85 additions & 0 deletions src/Video/LowLevelRenderer/WebGPU/WebGPUBufferAllocator.cpp
Original file line number Diff line number Diff line change
@@ -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);
}

}
61 changes: 61 additions & 0 deletions src/Video/LowLevelRenderer/WebGPU/WebGPUBufferAllocator.hpp
Original file line number Diff line number Diff line change
@@ -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;
};

}
3 changes: 3 additions & 0 deletions src/Video/LowLevelRenderer/WebGPU/WebGPUCommandBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ void WebGPUCommandBuffer::BindUniformBuffer(ShaderProgram::BindingType bindingTy
entry.binding = 0;
entry.buffer = static_cast<const WebGPUBuffer*>(uniformBuffer)->GetBuffer();
entry.size = uniformBuffer->GetSize();
entry.offset = static_cast<const WebGPUBuffer*>(uniformBuffer)->GetOffset();

WGPUBindGroupDescriptor bindGroupDescriptor = {};
bindGroupDescriptor.layout = currentGraphicsPipeline->GetShaderProgram()->GetBindGroupLayouts()[bindingType];
Expand Down Expand Up @@ -280,6 +281,7 @@ void WebGPUCommandBuffer::BindStorageBuffers(std::initializer_list<Buffer*> buff
entries[i].binding = i;
entries[i].buffer = static_cast<const WebGPUBuffer*>(buffer)->GetBuffer();
entries[i].size = buffer->GetSize();
entries[i].offset = static_cast<const WebGPUBuffer*>(buffer)->GetOffset();

++i;
}
Expand Down Expand Up @@ -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;
}
Expand Down
22 changes: 6 additions & 16 deletions src/Video/LowLevelRenderer/WebGPU/WebGPURenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "WebGPUSampler.hpp"
#include "WebGPUGraphicsPipeline.hpp"
#include "WebGPUComputePipeline.hpp"
#include "WebGPUBufferAllocator.hpp"
#include "WebGPURenderTargetAllocator.hpp"

#if ANDROID
Expand Down Expand Up @@ -54,6 +55,7 @@ WebGPURenderer::WebGPURenderer(::Utility::Window* window) {
CreateSwapChain(window);
CreateSamplers();

bufferAllocator = new WebGPUBufferAllocator(*this);
renderTargetAllocator = new WebGPURenderTargetAllocator(*this);

// Blitting.
Expand All @@ -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<uint32_t>(Sampler::Filter::COUNT) * static_cast<uint32_t>(Sampler::Clamping::COUNT); ++i) {
delete samplers[i];
}
delete bufferAllocator;
delete renderTargetAllocator;

delete blitGraphicsPipeline;
Expand All @@ -100,7 +99,7 @@ CommandBuffer* WebGPURenderer::CreateCommandBuffer() {
}

void WebGPURenderer::BeginFrame() {
ReleaseTemporaryBuffers();
bufferAllocator->BeginFrame();

// Acquire next swap chain image.
currentFrame = wgpuSwapChainGetCurrentTextureView(swapChain);
Expand All @@ -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) {
Expand Down Expand Up @@ -536,11 +533,4 @@ void WebGPURenderer::CreateSamplers() {
}
}

void WebGPURenderer::ReleaseTemporaryBuffers() {
for (WebGPUBuffer* buffer : temporaryBuffers) {
delete buffer;
}
temporaryBuffers.clear();
}

} // namespace Video
Loading

0 comments on commit 762d43c

Please sign in to comment.