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/Buffer/Buffer.cpp

327 lines
12 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/Buffer.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RHI/Fence.h>
#include <Atom/RHI/BufferView.h>
#include <Atom/RHI/BufferPool.h>
#include <Atom/RHI.Reflect/BufferViewDescriptor.h>
#include <Atom/RPI.Public/Buffer/BufferPool.h>
#include <Atom/RPI.Public/Buffer/BufferSystemInterface.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/EventTrace.h>
namespace AZ
{
namespace RPI
{
Data::Instance<Buffer> Buffer::FindOrCreate(const Data::Asset<BufferAsset>& bufferAsset)
{
auto buffer = Data::InstanceDatabase<Buffer>::Instance().FindOrCreate(
Data::InstanceId::CreateFromAssetId(bufferAsset.GetId()),
bufferAsset);
return buffer;
}
Buffer::Buffer()
{
/**
* Buffer views are persistently initialized on their parent buffer, and
* shader resource groups hold buffer view references. If we re-create the buffer
* view instance entirely, that will not automatically propagate to dependent
* shader resource groups.
*
* buffer views remain valid when their host buffer shuts down and re-initializes
* (it will force a rebuild), so the best course of action is to keep a persistent
* pointer around at all times, and then only initialize the buffer view once.
*/
auto& factory = RHI::Factory::Get();
m_rhiBuffer = factory.CreateBuffer();
AZ_Assert(m_rhiBuffer, "Failed to acquire an buffer instance from the RHI. Is the RHI initialized?");
}
Buffer::~Buffer()
{
WaitForUpload();
}
RHI::Buffer* Buffer::GetRHIBuffer()
{
return m_rhiBuffer.get();
}
const RHI::Buffer* Buffer::GetRHIBuffer() const
{
return m_rhiBuffer.get();
}
const RHI::BufferView* Buffer::GetBufferView() const
{
if (m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::InputAssembly ||
m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::DynamicInputAssembly)
{
AZ_Assert(false, "Input assembly buffer doesn't need a regular buffer view, it requires a stream or index buffer view.");
return nullptr;
}
return m_bufferView.get();
}
Data::Instance<Buffer> Buffer::CreateInternal(BufferAsset& bufferAsset)
{
Data::Instance<Buffer> buffer = aznew Buffer();
const RHI::ResultCode resultCode = buffer->Init(bufferAsset);
if (resultCode == RHI::ResultCode::Success)
{
return buffer;
}
return nullptr;
}
RHI::ResultCode Buffer::Init(BufferAsset& bufferAsset)
{
AZ_TRACE_METHOD();
RHI::ResultCode resultCode = RHI::ResultCode::Fail;
m_rhiBufferPool = nullptr;
if (bufferAsset.GetPoolAsset().GetId().IsValid())
{
Data::Instance<BufferPool> pool = BufferPool::FindOrCreate(bufferAsset.GetPoolAsset());
if (pool)
{
// Keep the reference so it won't be released
m_bufferPool = pool;
m_rhiBufferPool = pool->GetRHIPool();
}
else
{
AZ_Error("RPI::Buffer", false, "Failed to acquire the buffer pool instance from asset.");
return resultCode;
}
}
else if (bufferAsset.GetCommonPoolType() != CommonBufferPoolType::Invalid)
{
m_rhiBufferPool = BufferSystemInterface::Get()->GetCommonBufferPool(bufferAsset.GetCommonPoolType()).get();
}
if (!m_rhiBufferPool)
{
AZ_Error("RPI::Buffer", false, "Failed to acquire the buffer pool.");
return resultCode;
}
m_bufferViewDescriptor = bufferAsset.GetBufferViewDescriptor();
const uint32_t MinStreamSize = 64*1024; // Using streaming if the buffer data size is large than this size. Otherwise use init request to upload the data.
bool initWithData = (bufferAsset.GetBuffer().size() > 0 && bufferAsset.GetBuffer().size() <= MinStreamSize);
RHI::BufferInitRequest request;
request.m_buffer = m_rhiBuffer.get();
request.m_descriptor = bufferAsset.GetBufferDescriptor();
request.m_initialData = initWithData ? bufferAsset.GetBuffer().data() : nullptr;
resultCode = m_rhiBufferPool->InitBuffer(request);
if (resultCode == RHI::ResultCode::Success)
{
m_bufferAsset = { &bufferAsset, AZ::Data::AssetLoadBehavior::PreLoad };
InitBufferView();
if (bufferAsset.GetBuffer().size() > 0 && !initWithData)
{
AZ_TRACE_METHOD_NAME("Stream Upload");
m_streamFence = RHI::Factory::Get().CreateFence();
if (m_streamFence)
{
m_streamFence->Init(m_rhiBufferPool->GetDevice(), RHI::FenceState::Reset);
}
RHI::BufferDescriptor bufferDescriptor = bufferAsset.GetBufferDescriptor();
RHI::BufferStreamRequest request2;
request2.m_buffer = m_rhiBuffer.get();
request2.m_fenceToSignal = m_streamFence.get();
request2.m_byteCount = bufferDescriptor.m_byteCount;
request2.m_sourceData = bufferAsset.GetBuffer().data();
resultCode = m_rhiBufferPool->StreamBuffer(request2);
if (resultCode != RHI::ResultCode::Success)
{
AZ_Error("Buffer", false, "Buffer::Init() failed to stream buffer contents to GPU.");
return resultCode;
}
}
m_rhiBuffer->SetName(Name(bufferAsset.GetName()));
// Only generate buffer's attachment id if the buffer is writable
if (RHI::CheckBitsAny(m_rhiBuffer->GetDescriptor().m_bindFlags,
RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::DynamicInputAssembly))
{
// attachment id = bufferName_bufferInstanceId
m_attachmentId = Name(bufferAsset.GetName() + "_" + bufferAsset.GetId().m_guid.ToString<AZStd::string>(false, false));
}
return RHI::ResultCode::Success;
}
AZ_Error("Buffer", false, "Buffer::Init() failed to initialize RHI buffer. Error code: %d", static_cast<uint32_t>(resultCode));
return resultCode;
}
void Buffer::Resize(uint64_t bufferSize)
{
RHI::BufferDescriptor desc = m_rhiBuffer->GetDescriptor();
m_rhiBuffer = RHI::Factory::Get().CreateBuffer();
AZ_Assert(m_rhiBuffer, "Failed to acquire an buffer instance from the RHI. Is the RHI initialized?");
desc.m_byteCount = bufferSize;
RHI::BufferInitRequest request;
request.m_buffer = m_rhiBuffer.get();
request.m_descriptor = desc;
RHI::ResultCode resultCode = m_rhiBufferPool->InitBuffer(request);
if (resultCode != RHI::ResultCode::Success)
{
AZ_Error("Buffer", false, "Buffer::Resize() failed to resize buffer. Error code: %d", static_cast<uint32_t>(resultCode));
return;
}
//update buffer view
m_bufferViewDescriptor.m_elementCount = aznumeric_cast<uint32_t>(bufferSize / m_bufferViewDescriptor.m_elementSize);
InitBufferView();
}
void Buffer::InitBufferView()
{
// Skip buffer view creation for input assembly buffers
if (m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::InputAssembly ||
m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::DynamicInputAssembly)
{
return;
}
m_bufferView = m_rhiBuffer->GetBufferView(m_bufferViewDescriptor);
if(!m_bufferView.get())
{
AZ_Assert(false, "Buffer::InitBufferView() failed to initialize RHI buffer view.");
}
}
void* Buffer::Map(size_t byteCount, uint64_t byteOffset)
{
if (byteOffset + byteCount > m_rhiBuffer->GetDescriptor().m_byteCount)
{
AZ_Error("Buffer", false, "Map out of range");
return nullptr;
}
RHI::BufferMapRequest request;
request.m_buffer = m_rhiBuffer.get();
request.m_byteCount = byteCount;
request.m_byteOffset = byteOffset;
RHI::BufferMapResponse response;
RHI::ResultCode result = m_rhiBufferPool->MapBuffer(request, response);
if (result == RHI::ResultCode::Success)
{
return response.m_data;
}
else
{
AZ_Error("RPI::Buffer", false, "Failed to update RHI buffer. Error code: %d", result);
return nullptr;
}
}
void Buffer::Unmap()
{
m_rhiBufferPool->UnmapBuffer(*m_rhiBuffer);
}
void Buffer::WaitForUpload()
{
if (m_streamFence)
{
m_streamFence->WaitOnCpu();
// Guard against calling release twice on the same pointer from different threads,
// which would decrement the ref count twice and result in a crash
m_pendingUploadMutex.lock();
// Release buffer asset reference now that the upload is complete.
m_bufferAsset.Reset();
m_pendingUploadMutex.unlock();
}
}
bool Buffer::Orphan()
{
if (m_rhiBufferPool->GetDescriptor().m_heapMemoryLevel != RHI::HeapMemoryLevel::Host)
{
return false;
}
else
{
return m_rhiBufferPool->OrphanBuffer(*m_rhiBuffer) == RHI::ResultCode::Success;
}
}
bool Buffer::OrphanAndUpdateData(const void* sourceData, uint64_t sourceDataSize)
{
if (sourceDataSize > m_rhiBuffer->GetDescriptor().m_byteCount || !Orphan())
{
return false;
}
return UpdateData(sourceData, sourceDataSize, 0);
}
bool Buffer::UpdateData(const void* sourceData, uint64_t sourceDataSize, uint64_t bufferByteOffset)
{
if (sourceDataSize == 0)
{
return true;
}
if (void* buf = Map(sourceDataSize, bufferByteOffset))
{
memcpy(buf, sourceData, sourceDataSize);
Unmap();
return true;
}
return false;
}
const RHI::AttachmentId& Buffer::GetAttachmentId() const
{
AZ_Assert(!m_attachmentId.GetStringView().empty(), "Read-only buffer doesn't need attachment id");
return m_attachmentId;
}
const RHI::BufferViewDescriptor& Buffer::GetBufferViewDescriptor() const
{
return m_bufferViewDescriptor;
}
uint64_t Buffer::GetBufferSize() const
{
return m_rhiBuffer->GetDescriptor().m_byteCount;
}
} // namespace RPI
} // namespace AZ