SharedBuffer.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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/RHI.Reflect/ShaderSemantic.h>
  9. #include <Atom/RHI/RHIUtils.h>
  10. #include <Atom/RHI/Factory.h>
  11. #include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
  12. #include <Atom/RPI.Reflect/ResourcePoolAssetCreator.h>
  13. #include <Rendering/SharedBuffer.h>
  14. #include <Rendering/HairCommon.h>
  15. #include <numeric>
  16. namespace AZ::Render
  17. {
  18. //! Setting the constructor as private will create compile error to remind the developer to set
  19. //! the buffer Init in the FeatureProcessor and initialize properly
  20. SharedBuffer::SharedBuffer()
  21. {
  22. AZ_Warning("SharedBuffer", false, "Missing information to properly create SharedBuffer. Init is required");
  23. }
  24. SharedBuffer::SharedBuffer(AZStd::string bufferName, AZStd::vector<SrgBufferDescriptor>& buffersDescriptors)
  25. {
  26. m_bufferName = bufferName;
  27. Init(bufferName, buffersDescriptors);
  28. }
  29. SharedBuffer::~SharedBuffer()
  30. {
  31. m_bufferAsset = {};
  32. }
  33. //! Crucial method that will ensure that the alignment for the BufferViews is always kept.
  34. //! This is important when requesting a BufferView as the offset needs to be aligned according
  35. //! to the element type of the buffer.
  36. void SharedBuffer::CalculateAlignment(AZStd::vector<SrgBufferDescriptor>& buffersDescriptors)
  37. {
  38. m_alignment = 1;
  39. for (uint8_t bufferIndex = 0; bufferIndex < buffersDescriptors.size() ; ++bufferIndex)
  40. {
  41. // Using the least common multiple enables resource views to be typed and ensures they can get
  42. // an offset in bytes that is a multiple of an element count
  43. m_alignment = std::lcm(m_alignment, buffersDescriptors[bufferIndex].m_elementSize);
  44. }
  45. }
  46. void SharedBuffer::InitAllocator()
  47. {
  48. RHI::FreeListAllocator::Descriptor allocatorDescriptor;
  49. allocatorDescriptor.m_alignmentInBytes = m_alignment;
  50. allocatorDescriptor.m_capacityInBytes = m_sizeInBytes;
  51. allocatorDescriptor.m_policy = RHI::FreeListAllocatorPolicy::BestFit;
  52. allocatorDescriptor.m_garbageCollectLatency = 0;
  53. m_freeListAllocator.Init(allocatorDescriptor);
  54. }
  55. void SharedBuffer::CreateBuffer()
  56. {
  57. SrgBufferDescriptor descriptor = SrgBufferDescriptor(
  58. RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown,
  59. sizeof(float), uint32_t(m_sizeInBytes / sizeof(float)),
  60. Name{ "HairSharedDynamicBuffer" }, Name{ "m_skinnedHairSharedBuffer" }, 0, 0
  61. );
  62. m_buffer = Hair::UtilityClass::CreateBuffer("Hair Gem", descriptor, nullptr);
  63. }
  64. void SharedBuffer::CreateBufferAsset()
  65. {
  66. // Create the shared buffer pool
  67. {
  68. auto bufferPoolDesc = AZStd::make_unique<RHI::BufferPoolDescriptor>();
  69. // Output buffers are both written to during skinning and used as input assembly buffers
  70. bufferPoolDesc->m_bindFlags = RHI::BufferBindFlags::ShaderReadWrite | RHI::BufferBindFlags::Indirect;
  71. bufferPoolDesc->m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  72. bufferPoolDesc->m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
  73. RPI::ResourcePoolAssetCreator creator;
  74. creator.Begin(Uuid::CreateRandom());
  75. creator.SetPoolDescriptor(AZStd::move(bufferPoolDesc));
  76. creator.SetPoolName("SharedBufferPool");
  77. creator.End(m_bufferPoolAsset);
  78. }
  79. // Create the shared buffer
  80. {
  81. RPI::BufferAssetCreator creator;
  82. Uuid uuid = Uuid::CreateRandom();
  83. creator.Begin(uuid);
  84. creator.SetBufferName(m_bufferName);
  85. creator.SetPoolAsset(m_bufferPoolAsset);
  86. RHI::BufferDescriptor bufferDescriptor;
  87. bufferDescriptor.m_bindFlags = RHI::BufferBindFlags::ShaderReadWrite | RHI::BufferBindFlags::Indirect;
  88. bufferDescriptor.m_byteCount = m_sizeInBytes;
  89. bufferDescriptor.m_alignment = m_alignment;
  90. creator.SetBuffer(nullptr, 0, bufferDescriptor);
  91. RHI::BufferViewDescriptor viewDescriptor;
  92. viewDescriptor.m_elementFormat = RHI::Format::Unknown;
  93. // [To Do] - set this as AZ::Vector4 for offset approach shader code optimization
  94. viewDescriptor.m_elementSize = sizeof(float);
  95. viewDescriptor.m_elementCount = aznumeric_cast<uint32_t>(m_sizeInBytes) / sizeof(float);
  96. viewDescriptor.m_elementOffset = 0;
  97. creator.SetBufferViewDescriptor(viewDescriptor);
  98. creator.End(m_bufferAsset);
  99. }
  100. }
  101. void SharedBuffer::Init(AZStd::string bufferName, AZStd::vector<SrgBufferDescriptor>& buffersDescriptors)
  102. {
  103. m_bufferName = bufferName;
  104. // m_sizeInBytes = 256u * (1024u * 1024u);
  105. //
  106. // [To Do] replace this with max size request for allocation that can be given by the calling function
  107. // This has the following problems:
  108. // 1. The need to have this aggregated size in advance
  109. // 2. The size might grow dynamically between frames
  110. // 3. Due to having several stream buffers (position, tangent, structured), alignment padding
  111. // size calculation must be added.
  112. // Requirement: the buffer already has an assert on allocation beyond the memory. In the future it should
  113. // support greedy memory allocation when memory has reached its end. This must not invalidate the buffer during
  114. // the current frame, hence allocation of second buffer, fence and a copy must take place.
  115. CalculateAlignment(buffersDescriptors);
  116. InitAllocator();
  117. CreateBuffer();
  118. SystemTickBus::Handler::BusConnect();
  119. }
  120. AZStd::intrusive_ptr<HairSharedBufferAllocation> SharedBuffer::Allocate(size_t byteCount)
  121. {
  122. RHI::VirtualAddress result;
  123. {
  124. AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
  125. result = m_freeListAllocator.Allocate(byteCount, m_alignment);
  126. }
  127. if (result.IsValid())
  128. {
  129. return aznew HairSharedBufferAllocation(result);
  130. }
  131. return nullptr;
  132. }
  133. void SharedBuffer::DeAllocate(RHI::VirtualAddress allocation)
  134. {
  135. if (allocation.IsValid())
  136. {
  137. {
  138. AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
  139. m_freeListAllocator.DeAllocate(allocation);
  140. }
  141. m_memoryWasFreed = true;
  142. m_broadcastMemoryAvailableEvent = true;
  143. }
  144. }
  145. void SharedBuffer::DeAllocateNoSignal(RHI::VirtualAddress allocation)
  146. {
  147. if (allocation.IsValid())
  148. {
  149. {
  150. AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
  151. m_freeListAllocator.DeAllocate(allocation);
  152. }
  153. m_memoryWasFreed = true;
  154. }
  155. }
  156. Data::Asset<RPI::BufferAsset> SharedBuffer::GetBufferAsset() const
  157. {
  158. return m_bufferAsset;
  159. }
  160. Data::Instance<RPI::Buffer> SharedBuffer::GetBuffer()
  161. {
  162. if (!m_buffer)
  163. {
  164. m_buffer = RPI::Buffer::FindOrCreate(m_bufferAsset);
  165. }
  166. return m_buffer;
  167. }
  168. //! Update buffer's content with sourceData at an offset of bufferByteOffset
  169. bool SharedBuffer::UpdateData(const void* sourceData, uint64_t sourceDataSizeInBytes, uint64_t bufferByteOffset)
  170. {
  171. AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
  172. if (m_buffer.get())
  173. {
  174. return m_buffer->UpdateData(sourceData, sourceDataSizeInBytes, bufferByteOffset);
  175. }
  176. AZ_Assert(false, "SharedBuffer error in data allocation - the buffer doesn't exist yet");
  177. return false;
  178. }
  179. void SharedBuffer::OnSystemTick()
  180. {
  181. GarbageCollect();
  182. }
  183. void SharedBuffer::GarbageCollect()
  184. {
  185. if (m_memoryWasFreed)
  186. {
  187. m_memoryWasFreed = false;
  188. {
  189. AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
  190. m_freeListAllocator.GarbageCollect();
  191. }
  192. if (m_broadcastMemoryAvailableEvent)
  193. {
  194. SharedBufferNotificationBus::Broadcast(&SharedBufferNotificationBus::Events::OnSharedBufferMemoryAvailable);
  195. m_broadcastMemoryAvailableEvent = false;
  196. }
  197. }
  198. }
  199. //! Utility function to create a resource view of different type than the shared buffer data.
  200. //! Since this class is sub-buffer container, this method should be used after creating
  201. //! a new allocation to be used as a sub-buffer.
  202. //! Notice the alignment required according to the element size - this might need
  203. RHI::BufferViewDescriptor SharedBuffer::CreateResourceViewWithDifferentFormat(
  204. uint32_t offsetInBytes, uint32_t elementCount, uint32_t elementSize,
  205. RHI::Format format, RHI::BufferBindFlags overrideBindFlags)
  206. {
  207. RHI::BufferViewDescriptor viewDescriptor;
  208. // In the following line I use the element size and not the size based of the
  209. // element format since in the more interesting case of structured buffer, the
  210. // size will result in an error.
  211. uint32_t elementOffset = offsetInBytes / elementSize;
  212. viewDescriptor.m_elementOffset = elementOffset;
  213. viewDescriptor.m_elementCount = elementCount;
  214. viewDescriptor.m_elementFormat = format;
  215. viewDescriptor.m_elementSize = elementSize;
  216. viewDescriptor.m_overrideBindFlags = overrideBindFlags;
  217. return viewDescriptor;
  218. }
  219. } // namespace AZ::Render