Add GPU data upload allocator with shared page pool
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#endif
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
@@ -44,6 +45,81 @@ GPUVertexLayoutWebGPU::GPUVertexLayoutWebGPU(GPUDeviceWebGPU* device, const Elem
|
||||
}
|
||||
}
|
||||
|
||||
GPUDataUploaderWebGPU::Allocation GPUDataUploaderWebGPU::Allocate(uint32 size, uint32 alignment, WGPUBufferUsage usage)
|
||||
{
|
||||
// Find a free buffer from the current frame
|
||||
for (auto& e : _entries)
|
||||
{
|
||||
uint32 alignedOffset = Math::AlignUp(e.ActiveOffset, alignment);
|
||||
if (e.ActiveFrame == _frame && (usage ? (e.Usage & usage) == usage : e.Usage == WGPUBufferUsage_CopyDst) && alignedOffset + size <= e.Size)
|
||||
{
|
||||
e.ActiveOffset = alignedOffset + size;
|
||||
return { e.Buffer, alignedOffset };
|
||||
}
|
||||
}
|
||||
|
||||
// Find an unused buffer from the old frames
|
||||
for (auto& e : _entries)
|
||||
{
|
||||
if (e.ActiveFrame < _frame - 3 && (e.Usage & usage) == usage && size <= e.Size)
|
||||
{
|
||||
e.ActiveOffset = size;
|
||||
e.ActiveFrame = _frame;
|
||||
return { e.Buffer, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a new buffer
|
||||
{
|
||||
WGPUBufferDescriptor desc = WGPU_BUFFER_DESCRIPTOR_INIT;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
if (usage & WGPUBufferUsage_Uniform)
|
||||
desc.label = WEBGPU_STR("Upload Uniforms");
|
||||
else
|
||||
desc.label = WEBGPU_STR("Upload Buffer");
|
||||
#endif
|
||||
desc.size = Math::Max<uint32>(16 * 1024, Math::RoundUpToPowerOf2(size)); // Allocate larger pages for good suballocations
|
||||
desc.usage = WGPUBufferUsage_CopyDst | usage;
|
||||
WGPUBuffer buffer = wgpuDeviceCreateBuffer(_device, &desc);
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
LOG(Error, "Failed to create buffer of size {} bytes", size);
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
_entries.Insert(0, { buffer, (uint32)desc.size, size, _frame, desc.usage });
|
||||
PROFILE_MEM_INC(GraphicsBuffers, desc.usage);
|
||||
return { buffer, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
void GPUDataUploaderWebGPU::DrawBegin()
|
||||
{
|
||||
// Free old buffers and recycle unused ones
|
||||
uint64 frame = Engine::FrameCount;
|
||||
for (int32 i = _entries.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
auto& e = _entries[i];
|
||||
if (frame - e.ActiveFrame > 100)
|
||||
{
|
||||
wgpuBufferRelease(e.Buffer);
|
||||
PROFILE_MEM_DEC(GraphicsBuffers, e.Size);
|
||||
_entries.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
_frame = frame;
|
||||
}
|
||||
|
||||
void GPUDataUploaderWebGPU::ReleaseGPU()
|
||||
{
|
||||
// Free data
|
||||
for (auto& e : _entries)
|
||||
{
|
||||
wgpuBufferRelease(e.Buffer);
|
||||
PROFILE_MEM_DEC(GraphicsBuffers, e.Size);
|
||||
}
|
||||
_entries.Clear();
|
||||
}
|
||||
|
||||
GPUDeviceWebGPU::GPUDeviceWebGPU(WGPUInstance instance, GPUAdapterWebGPU* adapter)
|
||||
: GPUDevice(RendererType::WebGPU, ShaderProfile::WebGPU)
|
||||
, Adapter(adapter)
|
||||
@@ -105,6 +181,7 @@ bool GPUDeviceWebGPU::Init()
|
||||
WGPULimits limits = WGPU_LIMITS_INIT;
|
||||
if (wgpuAdapterGetLimits(Adapter->Adapter, &limits) == WGPUStatus_Success)
|
||||
{
|
||||
MinUniformBufferOffsetAlignment = limits.minUniformBufferOffsetAlignment;
|
||||
Limits.HasDepthClip = features.Contains(WGPUFeatureName_DepthClipControl);
|
||||
Limits.HasReadOnlyDepth = true;
|
||||
Limits.MaximumTexture1DSize = Math::Min<int32>(GPU_MAX_TEXTURE_SIZE, limits.maxTextureDimension1D);
|
||||
@@ -400,6 +477,7 @@ bool GPUDeviceWebGPU::Init()
|
||||
#undef INIT_SAMPLER
|
||||
|
||||
// Setup commands processing
|
||||
DataUploader._device = Device;
|
||||
Queue = wgpuDeviceGetQueue(Device);
|
||||
_mainContext = New<GPUContextWebGPU>(this);
|
||||
|
||||
@@ -407,6 +485,13 @@ bool GPUDeviceWebGPU::Init()
|
||||
return GPUDevice::Init();
|
||||
}
|
||||
|
||||
void GPUDeviceWebGPU::DrawBegin()
|
||||
{
|
||||
GPUDevice::DrawBegin();
|
||||
|
||||
DataUploader.DrawBegin();
|
||||
}
|
||||
|
||||
GPUDeviceWebGPU::~GPUDeviceWebGPU()
|
||||
{
|
||||
// Ensure to be disposed
|
||||
@@ -479,6 +564,7 @@ void GPUDeviceWebGPU::Dispose()
|
||||
preDispose();
|
||||
|
||||
// Clear device resources
|
||||
DataUploader.ReleaseGPU();
|
||||
SAFE_DELETE_GPU_RESOURCES(DefaultSamplers);
|
||||
SAFE_DELETE(_mainContext);
|
||||
SAFE_DELETE(Adapter);
|
||||
@@ -556,23 +642,7 @@ GPUSwapChain* GPUDeviceWebGPU::CreateSwapChain(Window* window)
|
||||
GPUConstantBuffer* GPUDeviceWebGPU::CreateConstantBuffer(uint32 size, const StringView& name)
|
||||
{
|
||||
PROFILE_MEM(GraphicsShaders);
|
||||
WGPUBuffer buffer = nullptr;
|
||||
if (size)
|
||||
{
|
||||
WGPUBufferDescriptor desc = WGPU_BUFFER_DESCRIPTOR_INIT;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
desc.label = WEBGPU_STR("Uniform");
|
||||
#endif
|
||||
desc.size = size;
|
||||
desc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
|
||||
buffer = wgpuDeviceCreateBuffer(Device, &desc);
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
LOG(Error, "Failed to create uniform buffer '{}' of size {} bytes", name, size);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return New<GPUConstantBufferWebGPU>(this, size, buffer, name);
|
||||
return New<GPUConstantBufferWebGPU>(this, size, name);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user