3
0

Buffer.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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. InitBufferView();
  115. if (bufferAsset.GetBuffer().size() > 0 && !initWithData)
  116. {
  117. m_bufferAsset = { &bufferAsset, AZ::Data::AssetLoadBehavior::PreLoad };
  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. if (m_streamFence)
  137. {
  138. m_streamFence->WaitOnCpuAsync(
  139. [this]()
  140. {
  141. // Once the uploading to gpu process is done, we shouldn't need to keep the reference of the m_bufferAsset
  142. this->m_pendingUploadMutex.lock();
  143. this->m_bufferAsset.Reset();
  144. this->m_pendingUploadMutex.unlock();
  145. });
  146. }
  147. }
  148. m_rhiBuffer->SetName(Name(bufferAsset.GetName()));
  149. // Only generate buffer's attachment id if the buffer is writable
  150. if (RHI::CheckBitsAny(m_rhiBuffer->GetDescriptor().m_bindFlags,
  151. RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::DynamicInputAssembly))
  152. {
  153. // attachment id = bufferName_bufferInstanceId
  154. m_attachmentId = Name(bufferAsset.GetName() + "_" + bufferAsset.GetId().m_guid.ToString<AZStd::string>(false, false));
  155. }
  156. return RHI::ResultCode::Success;
  157. }
  158. AZ_Error("Buffer", false, "Buffer::Init() failed to initialize RHI buffer. Error code: %d", static_cast<uint32_t>(resultCode));
  159. return resultCode;
  160. }
  161. void Buffer::Resize(uint64_t bufferSize)
  162. {
  163. RHI::BufferDescriptor desc = m_rhiBuffer->GetDescriptor();
  164. m_rhiBuffer = RHI::Factory::Get().CreateBuffer();
  165. AZ_Assert(m_rhiBuffer, "Failed to acquire an buffer instance from the RHI. Is the RHI initialized?");
  166. desc.m_byteCount = bufferSize;
  167. RHI::BufferInitRequest request;
  168. request.m_buffer = m_rhiBuffer.get();
  169. request.m_descriptor = desc;
  170. RHI::ResultCode resultCode = m_rhiBufferPool->InitBuffer(request);
  171. if (resultCode != RHI::ResultCode::Success)
  172. {
  173. AZ_Error("Buffer", false, "Buffer::Resize() failed to resize buffer. Error code: %d", static_cast<uint32_t>(resultCode));
  174. return;
  175. }
  176. //update buffer view
  177. m_bufferViewDescriptor.m_elementCount = aznumeric_cast<uint32_t>(bufferSize / m_bufferViewDescriptor.m_elementSize);
  178. InitBufferView();
  179. }
  180. void Buffer::InitBufferView()
  181. {
  182. // Skip buffer view creation for input assembly buffers
  183. if (m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::InputAssembly ||
  184. m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::DynamicInputAssembly)
  185. {
  186. return;
  187. }
  188. m_bufferView = m_rhiBuffer->GetBufferView(m_bufferViewDescriptor);
  189. if(!m_bufferView.get())
  190. {
  191. AZ_Assert(false, "Buffer::InitBufferView() failed to initialize RHI buffer view.");
  192. }
  193. }
  194. void* Buffer::Map(size_t byteCount, uint64_t byteOffset)
  195. {
  196. if (byteOffset + byteCount > m_rhiBuffer->GetDescriptor().m_byteCount)
  197. {
  198. AZ_Error("Buffer", false, "Map out of range");
  199. return nullptr;
  200. }
  201. RHI::BufferMapRequest request;
  202. request.m_buffer = m_rhiBuffer.get();
  203. request.m_byteCount = byteCount;
  204. request.m_byteOffset = byteOffset;
  205. RHI::BufferMapResponse response;
  206. RHI::ResultCode result = m_rhiBufferPool->MapBuffer(request, response);
  207. if (result == RHI::ResultCode::Success)
  208. {
  209. return response.m_data;
  210. }
  211. else
  212. {
  213. AZ_Error("RPI::Buffer", false, "Failed to update RHI buffer. Error code: %d", result);
  214. return nullptr;
  215. }
  216. }
  217. void Buffer::Unmap()
  218. {
  219. m_rhiBufferPool->UnmapBuffer(*m_rhiBuffer);
  220. }
  221. void Buffer::WaitForUpload()
  222. {
  223. if (m_streamFence)
  224. {
  225. m_streamFence->WaitOnCpu();
  226. // Guard against calling release twice on the same pointer from different threads,
  227. // which would decrement the ref count twice and result in a crash
  228. m_pendingUploadMutex.lock();
  229. // Release buffer asset reference now that the upload is complete.
  230. m_bufferAsset.Reset();
  231. m_pendingUploadMutex.unlock();
  232. }
  233. }
  234. bool Buffer::Orphan()
  235. {
  236. if (m_rhiBufferPool->GetDescriptor().m_heapMemoryLevel != RHI::HeapMemoryLevel::Host)
  237. {
  238. return false;
  239. }
  240. else
  241. {
  242. return m_rhiBufferPool->OrphanBuffer(*m_rhiBuffer) == RHI::ResultCode::Success;
  243. }
  244. }
  245. bool Buffer::OrphanAndUpdateData(const void* sourceData, uint64_t sourceDataSize)
  246. {
  247. if (sourceDataSize > m_rhiBuffer->GetDescriptor().m_byteCount || !Orphan())
  248. {
  249. return false;
  250. }
  251. return UpdateData(sourceData, sourceDataSize, 0);
  252. }
  253. bool Buffer::UpdateData(const void* sourceData, uint64_t sourceDataSize, uint64_t bufferByteOffset)
  254. {
  255. if (sourceDataSize == 0)
  256. {
  257. return true;
  258. }
  259. if (void* buf = Map(sourceDataSize, bufferByteOffset))
  260. {
  261. memcpy(buf, sourceData, sourceDataSize);
  262. Unmap();
  263. return true;
  264. }
  265. return false;
  266. }
  267. const RHI::AttachmentId& Buffer::GetAttachmentId() const
  268. {
  269. AZ_Assert(!m_attachmentId.GetStringView().empty(), "Read-only buffer doesn't need attachment id");
  270. return m_attachmentId;
  271. }
  272. const RHI::BufferViewDescriptor& Buffer::GetBufferViewDescriptor() const
  273. {
  274. return m_bufferViewDescriptor;
  275. }
  276. uint64_t Buffer::GetBufferSize() const
  277. {
  278. return m_rhiBuffer->GetDescriptor().m_byteCount;
  279. }
  280. } // namespace RPI
  281. } // namespace AZ