You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicBufferAllocator.cpp

179 lines
6.6 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Atom/RPI.Public/Buffer/BufferSystemInterface.h>
#include <Atom/RPI.Public/Buffer/Buffer.h>
#include <Atom/RPI.Public/DynamicDraw/DynamicBufferAllocator.h>
#include <Atom/RPI.Public/DynamicDraw/DynamicBuffer.h>
namespace AZ
{
namespace RPI
{
void DynamicBufferAllocator::Init(uint32_t ringBufferSize)
{
if (m_ringBuffer)
{
AZ_Assert(false, "DynamicBufferAllocator was already initialized");
return;
}
// Create the ring buffer from common pool
RPI::CommonBufferDescriptor desc;
desc.m_poolType = RPI::CommonBufferPoolType::DynamicInputAssembly;
desc.m_bufferName = "DyanmicBufferRing";
desc.m_elementSize = 1;
desc.m_byteCount = ringBufferSize;
m_ringBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc);
if (m_ringBuffer.get() == nullptr)
{
AZ_Assert(false, "Failed to initialize DyanmicBufferAllocator");
return;
}
m_ringBufferSize = ringBufferSize;
m_ringBufferStartAddress = m_ringBuffer->Map(m_ringBufferSize, 0);
m_currentPosition = 0;
m_endPositionLimit = 0;
m_currentFrame = 0;
for (uint32_t frame = 0; frame < AZ::RHI::Limits::Device::FrameCountMax; frame++)
{
m_frameStartPositions[frame] = 0;
}
}
void DynamicBufferAllocator::Shutdown()
{
m_ringBuffer->Unmap();
m_ringBuffer = nullptr;
m_ringBufferStartAddress = nullptr;
}
// [GFX TODO][ATOM-13182] Add unit tests for DynamicBufferAllocator's Allocate function
RHI::Ptr<DynamicBuffer> DynamicBufferAllocator::Allocate(uint32_t size, [[maybe_unused]]uint32_t alignment)
{
size = RHI::AlignUp(size, alignment);
uint32_t allocatePosition = 0;
//m_ringBufferStartAddress can be null for Null back end
if (!m_ringBufferStartAddress)
{
return nullptr;
}
if (size > m_ringBufferSize)
{
AZ_WarningOnce("RPI", !m_enableAllocationWarning, "DynamicBufferAllocator::Allocate: try to allocate buffer which size is larger than the ring buffer size");
return nullptr;
}
// Return if the allocation of current frame has reached limit
if (m_endPositionLimit == m_currentPosition && m_currentAllocatedSize > 0)
{
AZ_WarningOnce("RPI", !m_enableAllocationWarning, "DynamicBufferAllocator::Allocate: no more buffer is available");
return nullptr;
}
if (m_endPositionLimit > m_currentPosition)
{
if (m_endPositionLimit - m_currentPosition >= size)
{
allocatePosition = m_currentPosition;
m_currentPosition += size;
}
else
{
AZ_WarningOnce("RPI", !m_enableAllocationWarning, "DynamicBufferAllocator::Allocate: requested size (%d bytes) is larger than the size left (%d bytes)", size, m_endPositionLimit - m_currentPosition);
return nullptr;
}
}
else
{
if (m_ringBufferSize - m_currentPosition >= size)
{
allocatePosition = m_currentPosition;
m_currentPosition += size;
if (m_ringBufferSize == m_currentPosition)
{
m_currentPosition = 0;
}
}
else
{
if (m_endPositionLimit >= size)
{
allocatePosition = 0;
m_currentPosition = size;
}
else
{
AZ_WarningOnce("RPI", !m_enableAllocationWarning, "DynamicBufferAllocator::Allocate: requested size (%d bytes) is larger than the size left (%d bytes)", size, m_endPositionLimit);
return nullptr;
}
}
}
m_currentAllocatedSize += size;
RHI::Ptr<DynamicBuffer> allocatedBuffer = aznew DynamicBuffer();
allocatedBuffer->m_address = (uint8_t*)m_ringBufferStartAddress + allocatePosition;
allocatedBuffer->m_size = size;
allocatedBuffer->m_allocator = this;
return allocatedBuffer;
}
RHI::IndexBufferView DynamicBufferAllocator::GetIndexBufferView(RHI::Ptr<DynamicBuffer> dynamicBuffer, RHI::IndexFormat format)
{
return RHI::IndexBufferView(
*m_ringBuffer->GetRHIBuffer(),
GetBufferAddressOffset(dynamicBuffer),
dynamicBuffer->m_size,
format
);
}
RHI::StreamBufferView DynamicBufferAllocator::GetStreamBufferView(RHI::Ptr<DynamicBuffer> dynamicBuffer, uint32_t strideByteCount)
{
return RHI::StreamBufferView(
*m_ringBuffer->GetRHIBuffer(),
GetBufferAddressOffset(dynamicBuffer),
dynamicBuffer->m_size,
strideByteCount
);
}
uint32_t DynamicBufferAllocator::GetBufferAddressOffset(RHI::Ptr<DynamicBuffer> dynamicBuffer)
{
return aznumeric_cast<uint32_t>((uint8_t*)dynamicBuffer->m_address - (uint8_t*)m_ringBufferStartAddress);
}
void DynamicBufferAllocator::SetEnableAllocationWarning(bool enable)
{
m_enableAllocationWarning = enable;
}
void DynamicBufferAllocator::FrameEnd()
{
uint32_t nextFrame = (m_currentFrame + 1) % AZ::RHI::Limits::Device::FrameCountMax;
// The saved frame start position will become available since it's old than FrameCountMax. The saved start position of next frame is the new limit
m_endPositionLimit = m_frameStartPositions[nextFrame];
// Save start position for current frame
m_frameStartPositions[m_currentFrame] = m_currentPosition;
m_currentFrame = nextFrame;
m_currentAllocatedSize = 0;
}
}
}