3
0

Buffer.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Atom/RPI.Public/Buffer/Buffer.h>
  9. #include <Atom/RHI/Factory.h>
  10. #include <Atom/RHI/Fence.h>
  11. #include <Atom/RHI/BufferView.h>
  12. #include <Atom/RHI/BufferPool.h>
  13. #include <Atom/RHI.Reflect/BufferViewDescriptor.h>
  14. #include <Atom/RPI.Public/Buffer/BufferPool.h>
  15. #include <Atom/RPI.Public/Buffer/BufferSystemInterface.h>
  16. #include <AtomCore/Instance/InstanceDatabase.h>
  17. #include <AzCore/Component/TickBus.h>
  18. AZ_DECLARE_BUDGET(RPI);
  19. namespace AZ
  20. {
  21. namespace RPI
  22. {
  23. Data::Instance<Buffer> Buffer::FindOrCreate(const Data::Asset<BufferAsset>& bufferAsset)
  24. {
  25. return Data::InstanceDatabase<Buffer>::Instance().FindOrCreate(Data::InstanceId::CreateFromAsset(bufferAsset), bufferAsset);
  26. }
  27. Buffer::Buffer()
  28. {
  29. /**
  30. * Buffer views are persistently initialized on their parent buffer, and
  31. * shader resource groups hold buffer view references. If we re-create the buffer
  32. * view instance entirely, that will not automatically propagate to dependent
  33. * shader resource groups.
  34. *
  35. * buffer views remain valid when their host buffer shuts down and re-initializes
  36. * (it will force a rebuild), so the best course of action is to keep a persistent
  37. * pointer around at all times, and then only initialize the buffer view once.
  38. */
  39. auto& factory = RHI::Factory::Get();
  40. m_rhiBuffer = factory.CreateBuffer();
  41. AZ_Assert(m_rhiBuffer, "Failed to acquire an buffer instance from the RHI. Is the RHI initialized?");
  42. }
  43. Buffer::~Buffer()
  44. {
  45. WaitForUpload();
  46. }
  47. RHI::Buffer* Buffer::GetRHIBuffer()
  48. {
  49. return m_rhiBuffer.get();
  50. }
  51. const RHI::Buffer* Buffer::GetRHIBuffer() const
  52. {
  53. return m_rhiBuffer.get();
  54. }
  55. const RHI::BufferView* Buffer::GetBufferView() const
  56. {
  57. if (m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::InputAssembly ||
  58. m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::DynamicInputAssembly)
  59. {
  60. AZ_Assert(false, "Input assembly buffer doesn't need a regular buffer view, it requires a stream or index buffer view.");
  61. return nullptr;
  62. }
  63. return m_bufferView.get();
  64. }
  65. Data::Instance<Buffer> Buffer::CreateInternal(BufferAsset& bufferAsset)
  66. {
  67. Data::Instance<Buffer> buffer = aznew Buffer();
  68. const RHI::ResultCode resultCode = buffer->Init(bufferAsset);
  69. if (resultCode == RHI::ResultCode::Success)
  70. {
  71. return buffer;
  72. }
  73. return nullptr;
  74. }
  75. RHI::ResultCode Buffer::Init(BufferAsset& bufferAsset)
  76. {
  77. AZ_PROFILE_FUNCTION(RPI);
  78. RHI::ResultCode resultCode = RHI::ResultCode::Fail;
  79. m_rhiBufferPool = nullptr;
  80. if (bufferAsset.GetPoolAsset().GetId().IsValid())
  81. {
  82. Data::Instance<BufferPool> pool = BufferPool::FindOrCreate(bufferAsset.GetPoolAsset());
  83. if (pool)
  84. {
  85. // Keep the reference so it won't be released
  86. m_bufferPool = pool;
  87. m_rhiBufferPool = pool->GetRHIPool();
  88. }
  89. else
  90. {
  91. AZ_Error("RPI::Buffer", false, "Failed to acquire the buffer pool instance from asset.");
  92. return resultCode;
  93. }
  94. }
  95. else if (bufferAsset.GetCommonPoolType() != CommonBufferPoolType::Invalid)
  96. {
  97. m_rhiBufferPool = BufferSystemInterface::Get()->GetCommonBufferPool(bufferAsset.GetCommonPoolType()).get();
  98. }
  99. if (!m_rhiBufferPool)
  100. {
  101. AZ_Error("RPI::Buffer", false, "Failed to acquire the buffer pool.");
  102. return resultCode;
  103. }
  104. m_bufferViewDescriptor = bufferAsset.GetBufferViewDescriptor();
  105. 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.
  106. bool initWithData = (bufferAsset.GetBuffer().size() > 0 && bufferAsset.GetBuffer().size() <= MinStreamSize);
  107. RHI::BufferInitRequest request;
  108. request.m_buffer = m_rhiBuffer.get();
  109. request.m_descriptor = bufferAsset.GetBufferDescriptor();
  110. request.m_initialData = initWithData ? bufferAsset.GetBuffer().data() : nullptr;
  111. resultCode = m_rhiBufferPool->InitBuffer(request);
  112. if (resultCode == RHI::ResultCode::Success)
  113. {
  114. m_bufferAsset = { &bufferAsset, AZ::Data::AssetLoadBehavior::PreLoad };
  115. InitBufferView();
  116. if (bufferAsset.GetBuffer().size() > 0 && !initWithData)
  117. {
  118. AZ_PROFILE_SCOPE(RPI, "Stream Upload");
  119. m_streamFence = RHI::Factory::Get().CreateFence();
  120. if (m_streamFence)
  121. {
  122. m_streamFence->Init(m_rhiBufferPool->GetDevice(), RHI::FenceState::Reset);
  123. }
  124. RHI::BufferDescriptor bufferDescriptor = bufferAsset.GetBufferDescriptor();
  125. RHI::BufferStreamRequest request2;
  126. request2.m_buffer = m_rhiBuffer.get();
  127. request2.m_fenceToSignal = m_streamFence.get();
  128. request2.m_byteCount = bufferDescriptor.m_byteCount;
  129. request2.m_sourceData = bufferAsset.GetBuffer().data();
  130. resultCode = m_rhiBufferPool->StreamBuffer(request2);
  131. if (resultCode != RHI::ResultCode::Success)
  132. {
  133. AZ_Error("Buffer", false, "Buffer::Init() failed to stream buffer contents to GPU.");
  134. return resultCode;
  135. }
  136. }
  137. m_rhiBuffer->SetName(Name(bufferAsset.GetName()));
  138. // Only generate buffer's attachment id if the buffer is writable
  139. if (RHI::CheckBitsAny(m_rhiBuffer->GetDescriptor().m_bindFlags,
  140. RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::DynamicInputAssembly))
  141. {
  142. // attachment id = bufferName_bufferInstanceId
  143. m_attachmentId = Name(bufferAsset.GetName() + "_" + bufferAsset.GetId().m_guid.ToString<AZStd::string>(false, false));
  144. }
  145. return RHI::ResultCode::Success;
  146. }
  147. AZ_Error("Buffer", false, "Buffer::Init() failed to initialize RHI buffer. Error code: %d", static_cast<uint32_t>(resultCode));
  148. return resultCode;
  149. }
  150. void Buffer::Resize(uint64_t bufferSize)
  151. {
  152. RHI::BufferDescriptor desc = m_rhiBuffer->GetDescriptor();
  153. m_rhiBuffer = RHI::Factory::Get().CreateBuffer();
  154. AZ_Assert(m_rhiBuffer, "Failed to acquire an buffer instance from the RHI. Is the RHI initialized?");
  155. desc.m_byteCount = bufferSize;
  156. RHI::BufferInitRequest request;
  157. request.m_buffer = m_rhiBuffer.get();
  158. request.m_descriptor = desc;
  159. RHI::ResultCode resultCode = m_rhiBufferPool->InitBuffer(request);
  160. if (resultCode != RHI::ResultCode::Success)
  161. {
  162. AZ_Error("Buffer", false, "Buffer::Resize() failed to resize buffer. Error code: %d", static_cast<uint32_t>(resultCode));
  163. return;
  164. }
  165. //update buffer view
  166. m_bufferViewDescriptor.m_elementCount = aznumeric_cast<uint32_t>(bufferSize / m_bufferViewDescriptor.m_elementSize);
  167. InitBufferView();
  168. }
  169. void Buffer::InitBufferView()
  170. {
  171. // Skip buffer view creation for input assembly buffers
  172. if (m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::InputAssembly ||
  173. m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::DynamicInputAssembly)
  174. {
  175. return;
  176. }
  177. m_bufferView = m_rhiBuffer->GetBufferView(m_bufferViewDescriptor);
  178. if(!m_bufferView.get())
  179. {
  180. AZ_Assert(false, "Buffer::InitBufferView() failed to initialize RHI buffer view.");
  181. }
  182. }
  183. void* Buffer::Map(size_t byteCount, uint64_t byteOffset)
  184. {
  185. if (byteOffset + byteCount > m_rhiBuffer->GetDescriptor().m_byteCount)
  186. {
  187. AZ_Error("Buffer", false, "Map out of range");
  188. return nullptr;
  189. }
  190. RHI::BufferMapRequest request;
  191. request.m_buffer = m_rhiBuffer.get();
  192. request.m_byteCount = byteCount;
  193. request.m_byteOffset = byteOffset;
  194. RHI::BufferMapResponse response;
  195. RHI::ResultCode result = m_rhiBufferPool->MapBuffer(request, response);
  196. if (result == RHI::ResultCode::Success)
  197. {
  198. return response.m_data;
  199. }
  200. else
  201. {
  202. AZ_Error("RPI::Buffer", false, "Failed to update RHI buffer. Error code: %d", result);
  203. return nullptr;
  204. }
  205. }
  206. void Buffer::Unmap()
  207. {
  208. m_rhiBufferPool->UnmapBuffer(*m_rhiBuffer);
  209. }
  210. void Buffer::WaitForUpload()
  211. {
  212. if (m_streamFence)
  213. {
  214. m_streamFence->WaitOnCpu();
  215. // Guard against calling release twice on the same pointer from different threads,
  216. // which would decrement the ref count twice and result in a crash
  217. m_pendingUploadMutex.lock();
  218. // Release buffer asset reference now that the upload is complete.
  219. m_bufferAsset.Reset();
  220. m_pendingUploadMutex.unlock();
  221. }
  222. }
  223. bool Buffer::Orphan()
  224. {
  225. if (m_rhiBufferPool->GetDescriptor().m_heapMemoryLevel != RHI::HeapMemoryLevel::Host)
  226. {
  227. return false;
  228. }
  229. else
  230. {
  231. return m_rhiBufferPool->OrphanBuffer(*m_rhiBuffer) == RHI::ResultCode::Success;
  232. }
  233. }
  234. bool Buffer::OrphanAndUpdateData(const void* sourceData, uint64_t sourceDataSize)
  235. {
  236. if (sourceDataSize > m_rhiBuffer->GetDescriptor().m_byteCount || !Orphan())
  237. {
  238. return false;
  239. }
  240. return UpdateData(sourceData, sourceDataSize, 0);
  241. }
  242. bool Buffer::UpdateData(const void* sourceData, uint64_t sourceDataSize, uint64_t bufferByteOffset)
  243. {
  244. if (sourceDataSize == 0)
  245. {
  246. return true;
  247. }
  248. if (void* buf = Map(sourceDataSize, bufferByteOffset))
  249. {
  250. memcpy(buf, sourceData, sourceDataSize);
  251. Unmap();
  252. return true;
  253. }
  254. return false;
  255. }
  256. const RHI::AttachmentId& Buffer::GetAttachmentId() const
  257. {
  258. AZ_Assert(!m_attachmentId.GetStringView().empty(), "Read-only buffer doesn't need attachment id");
  259. return m_attachmentId;
  260. }
  261. const RHI::BufferViewDescriptor& Buffer::GetBufferViewDescriptor() const
  262. {
  263. return m_bufferViewDescriptor;
  264. }
  265. uint64_t Buffer::GetBufferSize() const
  266. {
  267. return m_rhiBuffer->GetDescriptor().m_byteCount;
  268. }
  269. } // namespace RPI
  270. } // namespace AZ