From befa36c7f138696a34d6f7b16ffb3d517027cc4c Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sun, 18 May 2025 00:06:06 +0200 Subject: [PATCH] ContextPool support for unordered contexts. --- aster/CMakeLists.txt | 2 + aster/include/aster/systems/context.h | 204 +++++++++++++++++----- aster/include/aster/systems/device.h | 14 +- aster/include/aster/systems/sync_server.h | 12 +- aster/src/aster/systems/context.cpp | 25 +-- aster/src/aster/systems/device.cpp | 45 ++--- aster/src/aster/systems/sync_server.cpp | 28 +-- samples/02_box/box.cpp | 2 +- 8 files changed, 217 insertions(+), 115 deletions(-) diff --git a/aster/CMakeLists.txt b/aster/CMakeLists.txt index 5bc1e67..4644fe9 100644 --- a/aster/CMakeLists.txt +++ b/aster/CMakeLists.txt @@ -10,6 +10,7 @@ find_package(fmt CONFIG REQUIRED) find_package(VulkanMemoryAllocator CONFIG REQUIRED) find_package(EASTL CONFIG REQUIRED) find_library(slang NAMES "slang" CONFIG REQUIRED) +find_package(foonathan_memory CONFIG REQUIRED) add_library(aster_core STATIC) @@ -28,4 +29,5 @@ target_link_libraries(aster_core PRIVATE fmt::fmt) target_link_libraries(aster_core PRIVATE EASTL) target_link_libraries(aster_core PUBLIC Vulkan::Headers GPUOpen::VulkanMemoryAllocator) target_link_libraries(aster_core PUBLIC ${slang}) +target_link_libraries(aster_core PRIVATE foonathan_memory) diff --git a/aster/include/aster/systems/context.h b/aster/include/aster/systems/context.h index 53fc70c..a325929 100644 --- a/aster/include/aster/systems/context.h +++ b/aster/include/aster/systems/context.h @@ -5,16 +5,23 @@ #pragma once +#include "context.h" + #include #include #include #include +#include #include -#include +#include +#include #include +#include +#include + namespace systems { @@ -26,6 +33,7 @@ namespace _internal class GraphicsContextPool; class TransferContextPool; class ContextPool; +class OrderlessTransferContextPool; } // namespace _internal #define DEPRECATE_RAW_CALLS @@ -38,6 +46,7 @@ class Context friend Device; friend _internal::ContextPool; + friend _internal::OrderlessTransferContextPool; explicit Context(_internal::ContextPool &pool, const vk::CommandBuffer cmd) : m_Pool{&pool} @@ -132,12 +141,21 @@ class ContextPool eastl::vector m_CommandBuffers; u32 m_BuffersAllocated; + public: + u16 m_ExtraData; + + enum class ManagedBy : u8 + { + eFrame, + eDevice, + } m_ManagedBy; + + protected: eastl::vector> m_OwnedBuffers; eastl::vector> m_OwnedImages; eastl::vector> m_OwnedImageViews; vk::CommandBuffer AllocateCommandBuffer(); - void Destroy(); public: [[nodiscard]] Device & @@ -147,6 +165,8 @@ class ContextPool return *m_Device; } + eastl::function m_ResetCallback; + /// Keep the resource alive while the command buffers are acting. void KeepAlive(const Ref &buffer); /// Keep the resource alive while the command buffers are acting. @@ -159,11 +179,17 @@ class ContextPool void Reset(); ContextPool() = default; - ContextPool(Device &device, u32 queueFamilyIndex); + ContextPool(Device &device, u32 queueFamilyIndex, ManagedBy managedBy); ContextPool(ContextPool &&other) noexcept; ContextPool &operator=(ContextPool &&other) noexcept; + bool + operator==(const ContextPool &other) const + { + return m_Pool == other.m_Pool; + } + ~ContextPool(); DISALLOW_COPY_AND_ASSIGN(ContextPool); @@ -175,37 +201,19 @@ class TransferContextPool : public ContextPool TransferContext CreateTransferContext(); TransferContextPool() = default; - TransferContextPool(Device &device, const u32 queueFamilyIndex) - : ContextPool{device, queueFamilyIndex} + + TransferContextPool(Device &device, const u32 queueFamilyIndex, const ManagedBy managedBy) + : ContextPool{device, queueFamilyIndex, managedBy} { } - TransferContextPool(TransferContextPool &&other) noexcept - : ContextPool{std::move(other)} - { - } + TransferContextPool(TransferContextPool &&other) noexcept = default; + TransferContextPool &operator=(TransferContextPool &&other) noexcept = default; - TransferContextPool & - operator=(TransferContextPool &&other) noexcept - { - if (this == &other) - return *this; - ContextPool::operator=(std::move(other)); - return *this; - } - - ~TransferContextPool() - { - Destroy(); - } + ~TransferContextPool() = default; DISALLOW_COPY_AND_ASSIGN(TransferContextPool); }; -// -// class ComputeContextPool : public TransferContextPool -//{ -// ComputeCon CreateTransferContext(); -//}; class GraphicsContextPool : public TransferContextPool { @@ -213,32 +221,136 @@ class GraphicsContextPool : public TransferContextPool GraphicsContext CreateGraphicsContext(); GraphicsContextPool() = default; - GraphicsContextPool(Device &device, const u32 queueFamilyIndex) - : TransferContextPool{device, queueFamilyIndex} + GraphicsContextPool(Device &device, const u32 queueFamilyIndex, const ManagedBy managedBy) + : TransferContextPool{device, queueFamilyIndex, managedBy} { } - GraphicsContextPool(GraphicsContextPool &&other) noexcept - : TransferContextPool{std::move(other)} - { - } + GraphicsContextPool(GraphicsContextPool &&other) noexcept = default; + GraphicsContextPool &operator=(GraphicsContextPool &&other) noexcept = default; - GraphicsContextPool & - operator=(GraphicsContextPool &&other) noexcept - { - if (this == &other) - return *this; - TransferContextPool::operator=(std::move(other)); - return *this; - } - - ~GraphicsContextPool() - { - Destroy(); - } + ~GraphicsContextPool() = default; DISALLOW_COPY_AND_ASSIGN(GraphicsContextPool); }; +class OrderlessTransferContextPool +{ + struct TransferContextEntry : eastl::intrusive_list_node + { + TransferContextPool m_Pool; + + bool + Contains(const _internal::ContextPool &other) const + { + return m_Pool == other; + } + }; + + using TransferContextList = eastl::intrusive_list; + + Device *m_Device; + memory::memory_pool<> m_TransferContextMemoryPool; + TransferContextList m_FreeTransferContexts; + TransferContextList m_TransferContexts; + u32 m_QueueFamilyIndex; + + constexpr static usize ENTRY_SIZE = sizeof(TransferContextEntry); + constexpr static usize ENTRIES_PER_BLOCK = 5; + constexpr static usize BLOCK_SIZE = ENTRIES_PER_BLOCK * ENTRY_SIZE; + + public: + OrderlessTransferContextPool() + : m_Device{nullptr} + , m_TransferContextMemoryPool{ENTRY_SIZE, BLOCK_SIZE} + , m_QueueFamilyIndex{0} + { + } + + void + Init(Device &device, const u32 queueFamilyIndex) + { + m_Device = &device; + m_QueueFamilyIndex = queueFamilyIndex; + } + + TransferContext + CreateTransferContext() + { + if (!m_FreeTransferContexts.empty()) + { + TransferContextEntry &entry = m_FreeTransferContexts.back(); + m_FreeTransferContexts.pop_back(); + m_TransferContexts.push_back(entry); + + return entry.m_Pool.CreateTransferContext(); + } + + TransferContextEntry &entry = *static_cast(m_TransferContextMemoryPool.allocate_node()); + auto pool = TransferContextPool{*m_Device, m_QueueFamilyIndex, ContextPool::ManagedBy::eDevice}; + pool.m_ResetCallback = [this](ContextPool &resetPool) { this->ReleasePool(resetPool); }; + new (&entry) TransferContextEntry{ + .m_Pool = eastl::move(pool), + }; + m_TransferContexts.push_back(entry); + + return entry.m_Pool.CreateTransferContext(); + } + + void + ReleasePool(ContextPool &pool) + { + auto const found = eastl::find_if(m_TransferContexts.begin(), m_TransferContexts.end(), + [&pool](const TransferContextEntry &v) { return v.Contains(pool); }); + auto &v = *found; + TransferContextList::remove(v); + + pool.Reset(); + + m_FreeTransferContexts.push_back(v); + } + + OrderlessTransferContextPool(OrderlessTransferContextPool &&other) noexcept + : m_Device{other.m_Device} + , m_TransferContextMemoryPool{std::move(other.m_TransferContextMemoryPool)} + , m_FreeTransferContexts{other.m_FreeTransferContexts} + , m_TransferContexts{other.m_TransferContexts} + , m_QueueFamilyIndex{other.m_QueueFamilyIndex} + { + other.m_FreeTransferContexts.clear(); + other.m_TransferContexts.clear(); + } + + OrderlessTransferContextPool & + operator=(OrderlessTransferContextPool &&other) noexcept + { + if (this == &other) + return *this; + m_Device = other.m_Device; + m_TransferContextMemoryPool = std::move(other.m_TransferContextMemoryPool); + m_FreeTransferContexts = other.m_FreeTransferContexts; + other.m_FreeTransferContexts.clear(); + m_TransferContexts = other.m_TransferContexts; + other.m_TransferContexts.clear(); + m_QueueFamilyIndex = other.m_QueueFamilyIndex; + return *this; + } + + ~OrderlessTransferContextPool() + { + for (auto &entry : m_FreeTransferContexts) + { + entry.m_Pool.~TransferContextPool(); + } + for (auto &entry : m_TransferContexts) + { + entry.m_Pool.~TransferContextPool(); + } + // The allocations will 'wink' away. + } + + DISALLOW_COPY_AND_ASSIGN(OrderlessTransferContextPool); +}; + } // namespace _internal } // namespace systems \ No newline at end of file diff --git a/aster/include/aster/systems/device.h b/aster/include/aster/systems/device.h index e5aecca..d3280a9 100644 --- a/aster/include/aster/systems/device.h +++ b/aster/include/aster/systems/device.h @@ -21,7 +21,6 @@ #include "aster/core/size.h" #include "aster/core/swapchain.h" -#include "EASTL/deque.h" #include #include #include @@ -29,8 +28,6 @@ #include #include -#include - constexpr static u32 MAX_FRAMES_IN_FLIGHT = 3; struct Window; @@ -404,6 +401,8 @@ class Device final vk::Queue m_ComputeQueue; u32 m_ComputeQueueFamily; + _internal::OrderlessTransferContextPool m_TransferContextPool; + std::array m_Frames; u32 m_CurrentFrameIdx = 0; @@ -515,9 +514,6 @@ class Device final // Frames // ---------------------------------------------------------------------------------------------------- - private: - Frame CreateFrame(u32 frameIndex); - public: Frame &GetNextFrame(); Size2D @@ -535,11 +531,7 @@ class Device final friend GraphicsContext; friend TransferContext; - vk::CommandPool m_TransferPool; - eastl::deque m_TransferContexts; - eastl::vector m_TransferContextFreeList; - - TransferContext &CreateTransferContext(); + TransferContext CreateTransferContext(); Receipt Submit(Context &context); // diff --git a/aster/include/aster/systems/sync_server.h b/aster/include/aster/systems/sync_server.h index cb5bfc6..e6bc9e7 100644 --- a/aster/include/aster/systems/sync_server.h +++ b/aster/include/aster/systems/sync_server.h @@ -4,6 +4,7 @@ // ============================================= #include "aster/aster.h" +#include "context.h" #include #include @@ -28,11 +29,20 @@ class SyncServer { vk::Semaphore m_Semaphore; TimelinePoint m_CurrentPoint; + ContextPool *m_AttachedPool; - static Entry Create(Device &device); + explicit Entry(Device &device); void Destroy(Device &device); void Wait(Device &device); void Next(); + void AttachPool(ContextPool *pool); + + Entry(Entry &&) = default; + Entry &operator=(Entry &&) = default; + + ~Entry() = default; + + DISALLOW_COPY_AND_ASSIGN(Entry); }; Device *m_Device; diff --git a/aster/src/aster/systems/context.cpp b/aster/src/aster/systems/context.cpp index 61b2951..9dd8a6f 100644 --- a/aster/src/aster/systems/context.cpp +++ b/aster/src/aster/systems/context.cpp @@ -277,9 +277,12 @@ systems::TransferContext::operator=(TransferContext &&other) noexcept using namespace systems::_internal; -ContextPool::ContextPool(Device &device, const u32 queueFamilyIndex) +ContextPool::ContextPool(Device &device, const u32 queueFamilyIndex, const ManagedBy managedBy) : m_Device{&device} , m_BuffersAllocated{0} + , m_ExtraData{0} + , m_ManagedBy{managedBy} + , m_ResetCallback{} { const vk::CommandPoolCreateInfo commandPoolCreateInfo = { .flags = vk::CommandPoolCreateFlagBits::eTransient, @@ -293,9 +296,12 @@ ContextPool::ContextPool(ContextPool &&other) noexcept , m_Pool{Take(other.m_Pool)} , m_CommandBuffers{std::move(other.m_CommandBuffers)} , m_BuffersAllocated{other.m_BuffersAllocated} + , m_ExtraData{other.m_ExtraData} + , m_ManagedBy{other.m_ManagedBy} , m_OwnedBuffers{std::move(other.m_OwnedBuffers)} , m_OwnedImages{std::move(other.m_OwnedImages)} , m_OwnedImageViews{std::move(other.m_OwnedImageViews)} + , m_ResetCallback{std::move(other.m_ResetCallback)} { } @@ -308,16 +314,22 @@ ContextPool::operator=(ContextPool &&other) noexcept swap(m_Device, other.m_Device); swap(m_Pool, other.m_Pool); swap(m_CommandBuffers, other.m_CommandBuffers); + swap(m_ExtraData, other.m_ExtraData); + swap(m_ManagedBy, other.m_ManagedBy); swap(m_BuffersAllocated, other.m_BuffersAllocated); swap(m_OwnedBuffers, other.m_OwnedBuffers); swap(m_OwnedImages, other.m_OwnedImages); swap(m_OwnedImageViews, other.m_OwnedImageViews); + swap(m_ResetCallback, other.m_ResetCallback); return *this; } ContextPool::~ContextPool() { - Destroy(); + if (!m_Pool) + return; + + m_Device->m_Device->destroy(Take(m_Pool), nullptr); } void @@ -358,15 +370,6 @@ ContextPool::AllocateCommandBuffer() return cmd; } -void -ContextPool::Destroy() -{ - if (!m_Pool) - return; - - m_Device->m_Device->destroy(Take(m_Pool), nullptr); -} - systems::Context ContextPool::CreateContext() { diff --git a/aster/src/aster/systems/device.cpp b/aster/src/aster/systems/device.cpp index 120b51e..7717542 100644 --- a/aster/src/aster/systems/device.cpp +++ b/aster/src/aster/systems/device.cpp @@ -12,6 +12,7 @@ #include "aster/systems/sync_server.h" #include "aster/util/files.h" #include "systems/commit_manager.h" +#include "systems/context.h" #include #include @@ -938,11 +939,7 @@ systems::Device::Device(const DeviceCreateInfo &createInfo) m_CommitManager = std::make_unique(this, 1000, 1000, 1000, CreateSampler({})); } - const vk::CommandPoolCreateInfo transferPoolCreateInfo = { - .flags = vk::CommandPoolCreateFlagBits::eTransient | vk::CommandPoolCreateFlagBits::eResetCommandBuffer, - .queueFamilyIndex = m_TransferQueueFamily, - }; - AbortIfFailed(m_Device->createCommandPool(&transferPoolCreateInfo, nullptr, &m_TransferPool)); + m_TransferContextPool.Init(*this, m_TransferQueueFamily); constexpr SlangGlobalSessionDesc globalSessionDesc = {}; auto result = slang::createGlobalSession(&globalSessionDesc, m_GlobalSlangSession.writeRef()); @@ -996,8 +993,6 @@ systems::Device::~Device() m_Device->destroy(Take(frame.m_ImageAcquireSem), nullptr); m_Device->destroy(Take(frame.m_RenderFinishSem), nullptr); } - m_Device->destroy(Take(m_TransferPool), nullptr); - m_TransferContexts.clear(); m_HashToSamplerIdx.clear(); } @@ -1082,38 +1077,20 @@ systems::Device::Present(Frame &frame, GraphicsContext &graphicsContext) } } -systems::TransferContext & +systems::TransferContext systems::Device::CreateTransferContext() { - /*if (!m_TransferContextFreeList.empty()) - { - u32 freeIndex = m_TransferContextFreeList.back(); - m_TransferContextFreeList.pop_back(); - - TransferContext &context = m_TransferContexts[freeIndex]; - - return context; - } - - vk::CommandBuffer cmd; - vk::CommandBufferAllocateInfo allocateInfo = { - .commandPool = m_TransferPool, - .level = vk::CommandBufferLevel::ePrimary, - .commandBufferCount = 1, - }; - AbortIfFailed(m_Device->allocateCommandBuffers(&allocateInfo, &cmd)); - - m_TransferContexts.push_back(TransferContext{*this, cmd}); - return m_TransferContexts.back();*/ - TODO(); - return m_TransferContexts.back(); + return m_TransferContextPool.CreateTransferContext(); } systems::Receipt systems::Device::Submit(Context &context) { const auto rect = m_SyncServer->Allocate(); - auto entry = m_SyncServer->GetEntry(rect); + auto &entry = m_SyncServer->GetEntry(rect); + + if (context.m_Pool->m_ManagedBy == _internal::ContextPool::ManagedBy::eDevice) + entry.AttachPool(context.m_Pool); auto [wait, next] = entry.m_CurrentPoint; vk::TimelineSemaphoreSubmitInfo timelineSubmit = { @@ -1205,9 +1182,9 @@ systems::Frame::operator=(Frame &&other) noexcept systems::Frame::Frame(Device &device, u32 frameIndex, u32 const primaryQueueFamily, u32 const asyncTransferQueue, u32 const asyncComputeQueue) : m_Device{&device} - , m_PrimaryPool{device, primaryQueueFamily} - , m_AsyncTransferPool{device, asyncTransferQueue} - , m_AsyncComputePool{device, asyncComputeQueue} + , m_PrimaryPool{device, primaryQueueFamily, _internal::ContextPool::ManagedBy::eFrame} + , m_AsyncTransferPool{device, asyncTransferQueue, _internal::ContextPool::ManagedBy::eFrame} + , m_AsyncComputePool{device, asyncComputeQueue, _internal::ContextPool::ManagedBy::eFrame} , m_FrameIdx{frameIndex} , m_ImageIdx{0} { diff --git a/aster/src/aster/systems/sync_server.cpp b/aster/src/aster/systems/sync_server.cpp index 9ebf7b8..a293566 100644 --- a/aster/src/aster/systems/sync_server.cpp +++ b/aster/src/aster/systems/sync_server.cpp @@ -9,8 +9,9 @@ using namespace systems::_internal; -SyncServer::Entry -SyncServer::Entry::Create(Device &device) +SyncServer::Entry::Entry(Device &device) + : m_CurrentPoint{0, 1} + , m_AttachedPool{nullptr} { constexpr static vk::SemaphoreTypeCreateInfo TYPE_CREATE_INFO = { .semaphoreType = vk::SemaphoreType::eTimeline, @@ -18,13 +19,7 @@ SyncServer::Entry::Create(Device &device) }; constexpr static vk::SemaphoreCreateInfo SEMAPHORE_CREATE_INFO = {.pNext = &TYPE_CREATE_INFO}; - vk::Semaphore semaphore; - AbortIfFailed(device.m_Device->createSemaphore(&SEMAPHORE_CREATE_INFO, nullptr, &semaphore)); - - return { - .m_Semaphore = semaphore, - .m_CurrentPoint = {.m_WaitValue = 0, .m_NextValue = 1}, - }; + AbortIfFailed(device.m_Device->createSemaphore(&SEMAPHORE_CREATE_INFO, nullptr, &m_Semaphore)); } void @@ -51,6 +46,11 @@ SyncServer::Entry::Wait(Device &device) // Thus, this is safe. m_CurrentPoint.m_WaitValue = m_CurrentPoint.m_NextValue; m_CurrentPoint.m_WaitValue = m_CurrentPoint.m_NextValue + 1; + if (m_AttachedPool) + { + m_AttachedPool->Reset(); + m_AttachedPool = nullptr; + } } void @@ -60,6 +60,13 @@ SyncServer::Entry::Next() ++m_CurrentPoint.m_NextValue; } +void +SyncServer::Entry::AttachPool(ContextPool *pool) +{ + assert(!m_AttachedPool); + m_AttachedPool = pool; +} + systems::Receipt SyncServer::Allocate() { @@ -91,8 +98,7 @@ SyncServer::AllocateEntry() return alloc; } - m_Allocations.push_back(Entry::Create(*m_Device)); - return m_Allocations.back(); + return m_Allocations.emplace_back(*m_Device); } void diff --git a/samples/02_box/box.cpp b/samples/02_box/box.cpp index 1149c68..2697c7f 100644 --- a/samples/02_box/box.cpp +++ b/samples/02_box/box.cpp @@ -246,7 +246,7 @@ main(int, char **) .pImageMemoryBarriers = &imageReadyToRead, }; - auto &context = device.CreateTransferContext(); + auto context = device.CreateTransferContext(); context.Begin(); context.Dependency(imageReadyToWriteDependency);