Buffer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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/RHISystemInterface.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. m_rhiBuffer = aznew RHI::Buffer;
  40. AZ_Assert(m_rhiBuffer, "Failed to acquire an buffer instance from the RHI. Is the RHI initialized?");
  41. }
  42. Buffer::~Buffer()
  43. {
  44. WaitForUpload();
  45. }
  46. RHI::Buffer* Buffer::GetRHIBuffer()
  47. {
  48. return m_rhiBuffer.get();
  49. }
  50. const RHI::Buffer* Buffer::GetRHIBuffer() const
  51. {
  52. return m_rhiBuffer.get();
  53. }
  54. const RHI::BufferView* Buffer::GetBufferView() const
  55. {
  56. if (m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::InputAssembly ||
  57. m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::DynamicInputAssembly)
  58. {
  59. AZ_Assert(false, "Input assembly buffer doesn't need a regular buffer view, it requires a stream or index buffer view.");
  60. return nullptr;
  61. }
  62. return m_bufferView.get();
  63. }
  64. Data::Instance<Buffer> Buffer::CreateInternal(BufferAsset& bufferAsset)
  65. {
  66. Data::Instance<Buffer> buffer = aznew Buffer();
  67. const RHI::ResultCode resultCode = buffer->Init(bufferAsset);
  68. if (resultCode == RHI::ResultCode::Success)
  69. {
  70. return buffer;
  71. }
  72. return nullptr;
  73. }
  74. RHI::ResultCode Buffer::Init(BufferAsset& bufferAsset)
  75. {
  76. AZ_PROFILE_FUNCTION(RPI);
  77. RHI::ResultCode resultCode = RHI::ResultCode::Fail;
  78. m_rhiBufferPool = nullptr;
  79. if (bufferAsset.GetPoolAsset().GetId().IsValid())
  80. {
  81. Data::Instance<BufferPool> pool = BufferPool::FindOrCreate(bufferAsset.GetPoolAsset());
  82. if (pool)
  83. {
  84. // Keep the reference so it won't be released
  85. m_bufferPool = pool;
  86. m_rhiBufferPool = pool->GetRHIPool();
  87. }
  88. else
  89. {
  90. AZ_Error("RPI::Buffer", false, "Failed to acquire the buffer pool instance from asset.");
  91. return resultCode;
  92. }
  93. }
  94. else if (bufferAsset.GetCommonPoolType() != CommonBufferPoolType::Invalid)
  95. {
  96. m_rhiBufferPool = BufferSystemInterface::Get()->GetCommonBufferPool(bufferAsset.GetCommonPoolType()).get();
  97. }
  98. if (!m_rhiBufferPool)
  99. {
  100. AZ_Error("RPI::Buffer", false, "Failed to acquire the buffer pool.");
  101. return resultCode;
  102. }
  103. m_bufferViewDescriptor = bufferAsset.GetBufferViewDescriptor();
  104. 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.
  105. bool initWithData = (bufferAsset.GetBuffer().size() > 0 && bufferAsset.GetBuffer().size() <= MinStreamSize);
  106. RHI::BufferInitRequest request;
  107. request.m_buffer = m_rhiBuffer.get();
  108. request.m_descriptor = bufferAsset.GetBufferDescriptor();
  109. request.m_initialData = initWithData ? bufferAsset.GetBuffer().data() : nullptr;
  110. resultCode = m_rhiBufferPool->InitBuffer(request);
  111. if (resultCode == RHI::ResultCode::Success)
  112. {
  113. InitBufferView();
  114. if (bufferAsset.GetBuffer().size() > 0 && !initWithData)
  115. {
  116. m_bufferAsset = { &bufferAsset, AZ::Data::AssetLoadBehavior::PreLoad };
  117. AZ_PROFILE_SCOPE(RPI, "Stream Upload");
  118. m_streamFence = aznew RHI::Fence;
  119. if (m_streamFence)
  120. {
  121. m_streamFence->Init(m_rhiBufferPool->GetDeviceMask(), RHI::FenceState::Reset);
  122. }
  123. RHI::BufferDescriptor bufferDescriptor = bufferAsset.GetBufferDescriptor();
  124. RHI::BufferStreamRequest request2;
  125. request2.m_buffer = m_rhiBuffer.get();
  126. request2.m_fenceToSignal = m_streamFence.get();
  127. request2.m_byteCount = bufferDescriptor.m_byteCount;
  128. request2.m_sourceData = bufferAsset.GetBuffer().data();
  129. resultCode = m_rhiBufferPool->StreamBuffer(request2);
  130. if (resultCode != RHI::ResultCode::Success)
  131. {
  132. AZ_Error("Buffer", false, "Buffer::Init() failed to stream buffer contents to GPU.");
  133. return resultCode;
  134. }
  135. if (m_streamFence)
  136. {
  137. auto deviceCount{RHI::RHISystemInterface::Get()->GetDeviceCount()};
  138. auto deviceMask{m_streamFence->GetDeviceMask()};
  139. m_initialUploadCount = az_popcnt_u32(AZStd::to_underlying(deviceMask) & ((1 << deviceCount) - 1));
  140. for (auto deviceIndex{0}; deviceIndex < deviceCount; ++deviceIndex)
  141. {
  142. if (AZStd::to_underlying(deviceMask) & (1 << deviceIndex))
  143. {
  144. m_streamFence->GetDeviceFence(deviceIndex)->WaitOnCpuAsync([this]()
  145. {
  146. if (--m_initialUploadCount == 0)
  147. {
  148. // Once the uploading to gpu process is done, we shouldn't need to keep the reference of the m_bufferAsset
  149. AZStd::lock_guard<AZStd::mutex> lock(m_pendingUploadMutex);
  150. m_bufferAsset.Reset();
  151. }
  152. });
  153. }
  154. }
  155. }
  156. }
  157. m_rhiBuffer->SetName(Name(bufferAsset.GetName()));
  158. // Only generate buffer's attachment id if the buffer is writable
  159. if (RHI::CheckBitsAny(m_rhiBuffer->GetDescriptor().m_bindFlags,
  160. RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::DynamicInputAssembly))
  161. {
  162. // attachment id = bufferName_bufferInstanceId
  163. m_attachmentId = Name(bufferAsset.GetName() + "_" + bufferAsset.GetId().m_guid.ToString<AZStd::string>(false, false));
  164. }
  165. return RHI::ResultCode::Success;
  166. }
  167. AZ_Error("Buffer", false, "Buffer::Init() failed to initialize RHI buffer. Error code: %d", static_cast<uint32_t>(resultCode));
  168. return resultCode;
  169. }
  170. void Buffer::Resize(uint64_t bufferSize)
  171. {
  172. RHI::BufferDescriptor desc = m_rhiBuffer->GetDescriptor();
  173. m_rhiBuffer = aznew RHI::Buffer;
  174. AZ_Assert(m_rhiBuffer, "Failed to acquire an buffer instance from the RHI. Is the RHI initialized?");
  175. desc.m_byteCount = bufferSize;
  176. RHI::BufferInitRequest request;
  177. request.m_buffer = m_rhiBuffer.get();
  178. request.m_descriptor = desc;
  179. RHI::ResultCode resultCode = m_rhiBufferPool->InitBuffer(request);
  180. if (resultCode != RHI::ResultCode::Success)
  181. {
  182. AZ_Error("Buffer", false, "Buffer::Resize() failed to resize buffer. Error code: %d", static_cast<uint32_t>(resultCode));
  183. return;
  184. }
  185. //update buffer view
  186. m_bufferViewDescriptor.m_elementCount = aznumeric_cast<uint32_t>(bufferSize / m_bufferViewDescriptor.m_elementSize);
  187. InitBufferView();
  188. }
  189. void Buffer::InitBufferView()
  190. {
  191. // Skip buffer view creation for input assembly buffers
  192. if (m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::InputAssembly ||
  193. m_rhiBuffer->GetDescriptor().m_bindFlags == RHI::BufferBindFlags::DynamicInputAssembly)
  194. {
  195. return;
  196. }
  197. m_bufferView = m_rhiBuffer->BuildBufferView(m_bufferViewDescriptor);
  198. if(!m_bufferView.get())
  199. {
  200. AZ_Assert(false, "Buffer::InitBufferView() failed to initialize RHI buffer view.");
  201. }
  202. }
  203. AZStd::unordered_map<int, void*> Buffer::Map(size_t byteCount, uint64_t byteOffset)
  204. {
  205. if (byteOffset + byteCount > m_rhiBuffer->GetDescriptor().m_byteCount)
  206. {
  207. AZ_Error("Buffer", false, "Map out of range");
  208. return {};
  209. }
  210. RHI::BufferMapRequest request;
  211. request.m_buffer = m_rhiBuffer.get();
  212. request.m_byteCount = byteCount;
  213. request.m_byteOffset = byteOffset;
  214. RHI::BufferMapResponse response;
  215. RHI::ResultCode result = m_rhiBufferPool->MapBuffer(request, response);
  216. if (result == RHI::ResultCode::Success)
  217. {
  218. return response.m_data;
  219. }
  220. else
  221. {
  222. AZ_Error("RPI::Buffer", false, "Failed to update RHI buffer. Error code: %d", result);
  223. return {};
  224. }
  225. }
  226. void Buffer::Unmap()
  227. {
  228. m_rhiBufferPool->UnmapBuffer(*m_rhiBuffer);
  229. }
  230. void Buffer::WaitForUpload()
  231. {
  232. if (m_streamFence)
  233. {
  234. auto deviceCount{RHI::RHISystemInterface::Get()->GetDeviceCount()};
  235. auto deviceMask{m_streamFence->GetDeviceMask()};
  236. for (auto deviceIndex{0}; deviceIndex < deviceCount; ++deviceIndex)
  237. {
  238. if (AZStd::to_underlying(deviceMask) & (1 << deviceIndex))
  239. {
  240. m_streamFence->GetDeviceFence(deviceIndex)->WaitOnCpu();
  241. }
  242. }
  243. // Guard against calling release twice on the same pointer from different threads,
  244. // which would decrement the ref count twice and result in a crash
  245. m_pendingUploadMutex.lock();
  246. // Release buffer asset reference now that the upload is complete.
  247. m_bufferAsset.Reset();
  248. m_pendingUploadMutex.unlock();
  249. }
  250. }
  251. bool Buffer::Orphan()
  252. {
  253. if (m_rhiBufferPool->GetDescriptor().m_heapMemoryLevel != RHI::HeapMemoryLevel::Host)
  254. {
  255. return false;
  256. }
  257. else
  258. {
  259. return m_rhiBufferPool->OrphanBuffer(*m_rhiBuffer) == RHI::ResultCode::Success;
  260. }
  261. }
  262. bool Buffer::OrphanAndUpdateData(const void* sourceData, uint64_t sourceDataSize)
  263. {
  264. if (sourceDataSize > m_rhiBuffer->GetDescriptor().m_byteCount || !Orphan())
  265. {
  266. return false;
  267. }
  268. return UpdateData(sourceData, sourceDataSize, 0);
  269. }
  270. bool Buffer::UpdateData(const void* sourceData, uint64_t sourceDataSize, uint64_t bufferByteOffset)
  271. {
  272. if (sourceDataSize == 0)
  273. {
  274. return true;
  275. }
  276. if (auto buf = Map(sourceDataSize, bufferByteOffset); buf.size())
  277. {
  278. auto partialResult{false};
  279. for (auto& [deviceIndex, buffer] : buf)
  280. {
  281. if(buffer != nullptr)
  282. {
  283. memcpy(buffer, sourceData, sourceDataSize);
  284. partialResult = true;
  285. }
  286. }
  287. if(partialResult)
  288. {
  289. Unmap();
  290. }
  291. return partialResult;
  292. }
  293. return false;
  294. }
  295. bool Buffer::UpdateData(const AZStd::unordered_map<int, const void*> sourceData, uint64_t sourceDataSize, uint64_t bufferByteOffset)
  296. {
  297. if (sourceDataSize == 0)
  298. {
  299. return true;
  300. }
  301. if (auto buf = Map(sourceDataSize, bufferByteOffset); buf.size())
  302. {
  303. auto partialResult{false};
  304. for (auto& [deviceIndex, source] : sourceData)
  305. {
  306. if(buf[deviceIndex] != nullptr)
  307. {
  308. memcpy(buf[deviceIndex], source, sourceDataSize);
  309. partialResult = true;
  310. }
  311. }
  312. if(partialResult)
  313. {
  314. Unmap();
  315. }
  316. return partialResult;
  317. }
  318. return false;
  319. }
  320. const RHI::AttachmentId& Buffer::GetAttachmentId() const
  321. {
  322. AZ_Assert(!m_attachmentId.GetStringView().empty(), "Read-only buffer doesn't need attachment id");
  323. return m_attachmentId;
  324. }
  325. const RHI::BufferViewDescriptor& Buffer::GetBufferViewDescriptor() const
  326. {
  327. return m_bufferViewDescriptor;
  328. }
  329. uint64_t Buffer::GetBufferSize() const
  330. {
  331. return m_rhiBuffer->GetDescriptor().m_byteCount;
  332. }
  333. } // namespace RPI
  334. } // namespace AZ