Optimize DynamicTypedBuffer and DynamicStructuredBuffer to use GPU-memory for commonly used single-frame uploads
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user