Optimize DynamicTypedBuffer and DynamicStructuredBuffer to use GPU-memory for commonly used single-frame uploads

This commit is contained in:
Wojtek Figat
2024-07-19 14:27:50 +02:00
parent 3296337f40
commit b14ac354bb
2 changed files with 62 additions and 73 deletions

View File

@@ -22,69 +22,43 @@ DynamicBuffer::~DynamicBuffer()
SAFE_DELETE_GPU_RESOURCE(_buffer);
}
void DynamicBuffer::Flush()
{
// Check if has sth to flush
const uint32 size = Data.Count();
if (size > 0)
{
// Check if has no buffer
if (_buffer == nullptr)
_buffer = GPUDevice::Instance->CreateBuffer(_name);
// Check if need to resize buffer
if (_buffer->GetSize() < size)
{
const uint32 numElements = Math::AlignUp<uint32>(static_cast<uint32>((size / _stride) * 1.3f), 32);
GPUBufferDescription desc;
InitDesc(desc, numElements);
if (_buffer->Init(desc))
{
LOG(Fatal, "Cannot setup dynamic buffer '{0}'! Size: {1}", _name, Utilities::BytesToText(size));
return;
}
}
// Upload data to the buffer
if (GPUDevice::Instance->IsRendering())
{
RenderContext::GPULocker.Lock();
GPUDevice::Instance->GetMainContext()->UpdateBuffer(_buffer, Data.Get(), size);
RenderContext::GPULocker.Unlock();
}
else
{
_buffer->SetData(Data.Get(), size);
}
}
}
void DynamicBuffer::Flush(GPUContext* context)
{
// Check if has sth to flush
const uint32 size = Data.Count();
if (size > 0)
if (size == 0)
return;
// Lazy-resize buffer
if (_buffer == nullptr)
_buffer = GPUDevice::Instance->CreateBuffer(_name);
if (_buffer->GetSize() < size)
{
// Check if has no buffer
if (_buffer == nullptr)
_buffer = GPUDevice::Instance->CreateBuffer(_name);
// Check if need to resize buffer
if (_buffer->GetSize() < size)
const int32 numElements = Math::AlignUp<int32>(static_cast<int32>((size / _stride) * 1.3f), 32);
GPUBufferDescription desc;
InitDesc(desc, numElements);
desc.Usage = SingleFrame ? GPUResourceUsage::Default : GPUResourceUsage::Dynamic;
if (_buffer->Init(desc))
{
const uint32 numElements = Math::AlignUp<uint32>(static_cast<uint32>((size / _stride) * 1.3f), 32);
GPUBufferDescription desc;
InitDesc(desc, numElements);
if (_buffer->Init(desc))
{
LOG(Fatal, "Cannot setup dynamic buffer '{0}'! Size: {1}", _name, Utilities::BytesToText(size));
return;
}
LOG(Fatal, "Cannot setup dynamic buffer '{0}'! Size: {1}", _name, Utilities::BytesToText(size));
return;
}
}
// Upload data to the buffer
// Upload data to the buffer
if (context)
{
context->UpdateBuffer(_buffer, Data.Get(), size);
}
else if (GPUDevice::Instance->IsRendering())
{
RenderContext::GPULocker.Lock();
GPUDevice::Instance->GetMainContext()->UpdateBuffer(_buffer, Data.Get(), size);
RenderContext::GPULocker.Unlock();
}
else
{
_buffer->SetData(Data.Get(), size);
}
}
void DynamicBuffer::Dispose()
@@ -93,10 +67,26 @@ void DynamicBuffer::Dispose()
Data.Resize(0);
}
void DynamicVertexBuffer::InitDesc(GPUBufferDescription& desc, int32 numElements)
{
desc = GPUBufferDescription::Vertex(_stride, numElements, GPUResourceUsage::Dynamic);
}
void DynamicIndexBuffer::InitDesc(GPUBufferDescription& desc, int32 numElements)
{
desc = GPUBufferDescription::Index(_stride, numElements, GPUResourceUsage::Dynamic);
}
DynamicStructuredBuffer::DynamicStructuredBuffer(uint32 initialCapacity, uint32 stride, bool isUnorderedAccess, const String& name)
: DynamicBuffer(initialCapacity, stride, name)
, _isUnorderedAccess(isUnorderedAccess)
{
SingleFrame = true; // The most common use-case is just for a single upload of data prepared by CPU
}
void DynamicStructuredBuffer::InitDesc(GPUBufferDescription& desc, int32 numElements)
{
desc = GPUBufferDescription::Structured(numElements, _stride, _isUnorderedAccess);
desc.Usage = GPUResourceUsage::Dynamic;
}
DynamicTypedBuffer::DynamicTypedBuffer(uint32 initialCapacity, PixelFormat format, bool isUnorderedAccess, const String& name)
@@ -104,6 +94,7 @@ DynamicTypedBuffer::DynamicTypedBuffer(uint32 initialCapacity, PixelFormat forma
, _format(format)
, _isUnorderedAccess(isUnorderedAccess)
{
SingleFrame = true; // The most common use-case is just for a single upload of data prepared by CPU
}
void DynamicTypedBuffer::InitDesc(GPUBufferDescription& desc, int32 numElements)
@@ -111,5 +102,5 @@ void DynamicTypedBuffer::InitDesc(GPUBufferDescription& desc, int32 numElements)
auto bufferFlags = GPUBufferFlags::ShaderResource;
if (_isUnorderedAccess)
bufferFlags |= GPUBufferFlags::UnorderedAccess;
desc = GPUBufferDescription::Buffer(numElements * _stride, bufferFlags, _format, nullptr, _stride, GPUResourceUsage::Dynamic);
desc = GPUBufferDescription::Buffer(numElements * _stride, bufferFlags, _format, nullptr, _stride);
}

View File

@@ -6,7 +6,7 @@
#include "GPUBuffer.h"
/// <summary>
/// Dynamic GPU buffer that allows to update and use GPU data (index/vertex/other) during single frame (supports dynamic resizing)
/// Dynamic GPU buffer that allows to update and use GPU data (index/vertex/other) during single frame (supports dynamic resizing).
/// </summary>
class FLAXENGINE_API DynamicBuffer
{
@@ -32,13 +32,18 @@ public:
virtual ~DynamicBuffer();
public:
/// <summary>
/// True if buffer will be used once per-frame, otherwise it should support uploading data multiple times per-frame. If true 'GPUResourceUsage::Dynamic' will be used, otherwise 'GPUResourceUsage::Default'.
/// </summary>
bool SingleFrame = false;
/// <summary>
/// The data container (raw bytes storage).
/// </summary>
Array<byte> Data;
/// <summary>
/// Gets buffer (may be null since it's using 'late init' feature)
/// Gets buffer (can be null due to 'late init' feature).
/// </summary>
FORCE_INLINE GPUBuffer* GetBuffer() const
{
@@ -70,7 +75,7 @@ public:
/// <param name="size">Amount of data to write (in bytes)</param>
FORCE_INLINE void Write(const void* bytes, int32 size)
{
Data.Add((byte*)bytes, size);
Data.Add((const byte*)bytes, size);
}
/// <summary>
@@ -97,7 +102,10 @@ public:
/// <summary>
/// Unlock buffer and flush data with a buffer (it will be ready for an immediate draw).
/// </summary>
void Flush();
void Flush()
{
Flush(nullptr);
}
/// <summary>
/// Unlock buffer and flush data with a buffer (it will be ready for a during next frame draw).
@@ -133,10 +141,7 @@ public:
protected:
// [DynamicBuffer]
void InitDesc(GPUBufferDescription& desc, int32 numElements) override
{
desc = GPUBufferDescription::Vertex(_stride, numElements, GPUResourceUsage::Dynamic);
}
void InitDesc(GPUBufferDescription& desc, int32 numElements) override;
};
/// <summary>
@@ -158,10 +163,7 @@ public:
protected:
// [DynamicBuffer]
void InitDesc(GPUBufferDescription& desc, int32 numElements) override
{
desc = GPUBufferDescription::Index(_stride, numElements, GPUResourceUsage::Dynamic);
}
void InitDesc(GPUBufferDescription& desc, int32 numElements) override;
};
/// <summary>
@@ -180,11 +182,7 @@ public:
/// <param name="stride">Stride in bytes.</param>
/// <param name="isUnorderedAccess">True if unordered access usage.</param>
/// <param name="name">Buffer name.</param>
DynamicStructuredBuffer(uint32 initialCapacity, uint32 stride, bool isUnorderedAccess = false, const String& name = String::Empty)
: DynamicBuffer(initialCapacity, stride, name)
, _isUnorderedAccess(isUnorderedAccess)
{
}
DynamicStructuredBuffer(uint32 initialCapacity, uint32 stride, bool isUnorderedAccess = false, const String& name = String::Empty);
protected:
// [DynamicBuffer]