123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- /*
- * 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/RHI.Reflect/ShaderSemantic.h>
- #include <Atom/RHI/RHIUtils.h>
- #include <Atom/RHI/Factory.h>
- #include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
- #include <Atom/RPI.Reflect/ResourcePoolAssetCreator.h>
- #include <Rendering/SharedBuffer.h>
- #include <Rendering/HairCommon.h>
- #include <numeric>
- namespace AZ::Render
- {
- //! Setting the constructor as private will create compile error to remind the developer to set
- //! the buffer Init in the FeatureProcessor and initialize properly
- SharedBuffer::SharedBuffer()
- {
- AZ_Warning("SharedBuffer", false, "Missing information to properly create SharedBuffer. Init is required");
- }
- SharedBuffer::SharedBuffer(AZStd::string bufferName, AZStd::vector<SrgBufferDescriptor>& buffersDescriptors)
- {
- m_bufferName = bufferName;
- Init(bufferName, buffersDescriptors);
- }
- SharedBuffer::~SharedBuffer()
- {
- m_bufferAsset = {};
- }
- //! Crucial method that will ensure that the alignment for the BufferViews is always kept.
- //! This is important when requesting a BufferView as the offset needs to be aligned according
- //! to the element type of the buffer.
- void SharedBuffer::CalculateAlignment(AZStd::vector<SrgBufferDescriptor>& buffersDescriptors)
- {
- m_alignment = 1;
- for (uint8_t bufferIndex = 0; bufferIndex < buffersDescriptors.size() ; ++bufferIndex)
- {
- // Using the least common multiple enables resource views to be typed and ensures they can get
- // an offset in bytes that is a multiple of an element count
- m_alignment = std::lcm(m_alignment, buffersDescriptors[bufferIndex].m_elementSize);
- }
- }
- void SharedBuffer::InitAllocator()
- {
- RHI::FreeListAllocator::Descriptor allocatorDescriptor;
- allocatorDescriptor.m_alignmentInBytes = m_alignment;
- allocatorDescriptor.m_capacityInBytes = m_sizeInBytes;
- allocatorDescriptor.m_policy = RHI::FreeListAllocatorPolicy::BestFit;
- allocatorDescriptor.m_garbageCollectLatency = 0;
- m_freeListAllocator.Init(allocatorDescriptor);
- }
- void SharedBuffer::CreateBuffer()
- {
- SrgBufferDescriptor descriptor = SrgBufferDescriptor(
- RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown,
- sizeof(float), uint32_t(m_sizeInBytes / sizeof(float)),
- Name{ "HairSharedDynamicBuffer" }, Name{ "m_skinnedHairSharedBuffer" }, 0, 0
- );
- m_buffer = Hair::UtilityClass::CreateBuffer("Hair Gem", descriptor, nullptr);
- }
- void SharedBuffer::CreateBufferAsset()
- {
- // Create the shared buffer pool
- {
- auto bufferPoolDesc = AZStd::make_unique<RHI::BufferPoolDescriptor>();
- // Output buffers are both written to during skinning and used as input assembly buffers
- bufferPoolDesc->m_bindFlags = RHI::BufferBindFlags::ShaderReadWrite | RHI::BufferBindFlags::Indirect;
- bufferPoolDesc->m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
- bufferPoolDesc->m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
- RPI::ResourcePoolAssetCreator creator;
- creator.Begin(Uuid::CreateRandom());
- creator.SetPoolDescriptor(AZStd::move(bufferPoolDesc));
- creator.SetPoolName("SharedBufferPool");
- creator.End(m_bufferPoolAsset);
- }
- // Create the shared buffer
- {
- RPI::BufferAssetCreator creator;
- Uuid uuid = Uuid::CreateRandom();
- creator.Begin(uuid);
- creator.SetBufferName(m_bufferName);
- creator.SetPoolAsset(m_bufferPoolAsset);
- RHI::BufferDescriptor bufferDescriptor;
- bufferDescriptor.m_bindFlags = RHI::BufferBindFlags::ShaderReadWrite | RHI::BufferBindFlags::Indirect;
- bufferDescriptor.m_byteCount = m_sizeInBytes;
- bufferDescriptor.m_alignment = m_alignment;
- creator.SetBuffer(nullptr, 0, bufferDescriptor);
- RHI::BufferViewDescriptor viewDescriptor;
- viewDescriptor.m_elementFormat = RHI::Format::Unknown;
- // [To Do] - set this as AZ::Vector4 for offset approach shader code optimization
- viewDescriptor.m_elementSize = sizeof(float);
- viewDescriptor.m_elementCount = aznumeric_cast<uint32_t>(m_sizeInBytes) / sizeof(float);
- viewDescriptor.m_elementOffset = 0;
- creator.SetBufferViewDescriptor(viewDescriptor);
- creator.End(m_bufferAsset);
- }
- }
- void SharedBuffer::Init(AZStd::string bufferName, AZStd::vector<SrgBufferDescriptor>& buffersDescriptors)
- {
- m_bufferName = bufferName;
- // m_sizeInBytes = 256u * (1024u * 1024u);
- //
- // [To Do] replace this with max size request for allocation that can be given by the calling function
- // This has the following problems:
- // 1. The need to have this aggregated size in advance
- // 2. The size might grow dynamically between frames
- // 3. Due to having several stream buffers (position, tangent, structured), alignment padding
- // size calculation must be added.
- // Requirement: the buffer already has an assert on allocation beyond the memory. In the future it should
- // support greedy memory allocation when memory has reached its end. This must not invalidate the buffer during
- // the current frame, hence allocation of second buffer, fence and a copy must take place.
- CalculateAlignment(buffersDescriptors);
- InitAllocator();
- CreateBuffer();
- SystemTickBus::Handler::BusConnect();
- }
- AZStd::intrusive_ptr<HairSharedBufferAllocation> SharedBuffer::Allocate(size_t byteCount)
- {
- RHI::VirtualAddress result;
- {
- AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
- result = m_freeListAllocator.Allocate(byteCount, m_alignment);
- }
- if (result.IsValid())
- {
- return aznew HairSharedBufferAllocation(result);
- }
- return nullptr;
- }
- void SharedBuffer::DeAllocate(RHI::VirtualAddress allocation)
- {
- if (allocation.IsValid())
- {
- {
- AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
- m_freeListAllocator.DeAllocate(allocation);
- }
- m_memoryWasFreed = true;
- m_broadcastMemoryAvailableEvent = true;
- }
- }
- void SharedBuffer::DeAllocateNoSignal(RHI::VirtualAddress allocation)
- {
- if (allocation.IsValid())
- {
- {
- AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
- m_freeListAllocator.DeAllocate(allocation);
- }
- m_memoryWasFreed = true;
- }
- }
- Data::Asset<RPI::BufferAsset> SharedBuffer::GetBufferAsset() const
- {
- return m_bufferAsset;
- }
- Data::Instance<RPI::Buffer> SharedBuffer::GetBuffer()
- {
- if (!m_buffer)
- {
- m_buffer = RPI::Buffer::FindOrCreate(m_bufferAsset);
- }
- return m_buffer;
- }
- //! Update buffer's content with sourceData at an offset of bufferByteOffset
- bool SharedBuffer::UpdateData(const void* sourceData, uint64_t sourceDataSizeInBytes, uint64_t bufferByteOffset)
- {
- AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
- if (m_buffer.get())
- {
- return m_buffer->UpdateData(sourceData, sourceDataSizeInBytes, bufferByteOffset);
- }
- AZ_Assert(false, "SharedBuffer error in data allocation - the buffer doesn't exist yet");
- return false;
- }
- void SharedBuffer::OnSystemTick()
- {
- GarbageCollect();
- }
- void SharedBuffer::GarbageCollect()
- {
- if (m_memoryWasFreed)
- {
- m_memoryWasFreed = false;
- {
- AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
- m_freeListAllocator.GarbageCollect();
- }
- if (m_broadcastMemoryAvailableEvent)
- {
- SharedBufferNotificationBus::Broadcast(&SharedBufferNotificationBus::Events::OnSharedBufferMemoryAvailable);
- m_broadcastMemoryAvailableEvent = false;
- }
- }
- }
- //! Utility function to create a resource view of different type than the shared buffer data.
- //! Since this class is sub-buffer container, this method should be used after creating
- //! a new allocation to be used as a sub-buffer.
- //! Notice the alignment required according to the element size - this might need
- RHI::BufferViewDescriptor SharedBuffer::CreateResourceViewWithDifferentFormat(
- uint32_t offsetInBytes, uint32_t elementCount, uint32_t elementSize,
- RHI::Format format, RHI::BufferBindFlags overrideBindFlags)
- {
- RHI::BufferViewDescriptor viewDescriptor;
- // In the following line I use the element size and not the size based of the
- // element format since in the more interesting case of structured buffer, the
- // size will result in an error.
- uint32_t elementOffset = offsetInBytes / elementSize;
- viewDescriptor.m_elementOffset = elementOffset;
- viewDescriptor.m_elementCount = elementCount;
- viewDescriptor.m_elementFormat = format;
- viewDescriptor.m_elementSize = elementSize;
- viewDescriptor.m_overrideBindFlags = overrideBindFlags;
- return viewDescriptor;
- }
- } // namespace AZ::Render
|