diff --git a/aster/include/aster/core/CMakeLists.txt b/aster/include/aster/core/CMakeLists.txt index 3c5500e..968c60c 100644 --- a/aster/include/aster/core/CMakeLists.txt +++ b/aster/include/aster/core/CMakeLists.txt @@ -17,4 +17,5 @@ INTERFACE "image.h" "surface.h" "size.h" + "type_traits.h" "window.h") diff --git a/aster/include/aster/core/buffer.h b/aster/include/aster/core/buffer.h index 45075d3..e4f65ed 100644 --- a/aster/include/aster/core/buffer.h +++ b/aster/include/aster/core/buffer.h @@ -24,6 +24,8 @@ struct Buffer [[nodiscard]] bool IsValid() const; [[nodiscard]] bool IsMapped() const; [[nodiscard]] bool IsOwned() const; + [[nodiscard]] bool IsCommitted() const; + void SetCommitted(bool committed); void Destroy(const Device *device); void Write(const Device *device, usize offset, usize size, const void *data); @@ -40,9 +42,13 @@ struct Buffer constexpr static usize VALID_BUFFER_BIT = Cast(1llu << 63); constexpr static usize OWNED_BIT = 1llu << 62; - constexpr static usize SIZE_MASK = ~(VALID_BUFFER_BIT | OWNED_BIT); + constexpr static usize COMMITTED_BIT = 1llu << 61; + constexpr static usize SIZE_MASK = ~(VALID_BUFFER_BIT | OWNED_BIT | COMMITTED_BIT); }; +template <> +constexpr bool concepts::GpuResource = true; + // Ensure that m_Size doesn't get used intrusively since it manages the state. static_assert(offsetof(Buffer, m_Size_) > sizeof(usize)); @@ -113,3 +119,15 @@ Buffer::IsOwned() const { return m_Size_ & OWNED_BIT; } + +inline bool +Buffer::IsCommitted() const +{ + return m_Size_ & COMMITTED_BIT; +} + +inline void +Buffer::SetCommitted(const bool committed) +{ + m_Size_ = committed ? (m_Size_ | COMMITTED_BIT) : (m_Size_ & ~COMMITTED_BIT); +} diff --git a/aster/include/aster/core/global.h b/aster/include/aster/core/global.h index fc9b114..a79c575 100644 --- a/aster/include/aster/core/global.h +++ b/aster/include/aster/core/global.h @@ -26,6 +26,8 @@ #if !defined(NDEBUG) #define VULKAN_HPP_ASSERT(expr) DEBUG_IF(!(expr), "Vulkan assert failed") #endif +#include "type_traits.h" + #include #include #include @@ -175,6 +177,18 @@ ClosestPowerOfTwo(const u32 val) return (smallerPo2 + largerPo2 <= (val << 1)) ? largerPo2 : smallerPo2; } +[[nodiscard]] constexpr u32 +GetMaskOffset(u32 val) +{ + u32 count = 0; + while ((val & 1) == 0) + { + count++; + val = val >> 1; + } + return count; +} + template <> struct fmt::formatter : nested_formatter { diff --git a/aster/include/aster/core/image.h b/aster/include/aster/core/image.h index 592ba82..c4e0b49 100644 --- a/aster/include/aster/core/image.h +++ b/aster/include/aster/core/image.h @@ -49,13 +49,19 @@ struct Image [[nodiscard]] bool IsValid() const; [[nodiscard]] bool IsOwned() const; [[nodiscard]] u32 GetMipLevels() const; + [[nodiscard]] bool IsCommitted() const; + void SetCommitted(bool committed); void Destroy(const Device *device); constexpr static u8 VALID_BIT = 1u << 7; constexpr static u8 OWNED_BIT = 1u << 6; + constexpr static u8 COMMITTED_BIT = 1u << 5; }; +template <> +constexpr bool concepts::GpuResource = true; + struct Texture : Image { void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipMapped, cstr name = nullptr); @@ -116,4 +122,16 @@ inline u32 Image::GetMipLevels() const { return m_MipLevels; -} \ No newline at end of file +} + +inline bool +Image::IsCommitted() const +{ + return m_Flags_ & COMMITTED_BIT; +} + +inline void +Image::SetCommitted(const bool committed) +{ + m_Flags_ = committed ? (m_Flags_ | COMMITTED_BIT) : (m_Flags_ & ~COMMITTED_BIT); +} diff --git a/aster/include/aster/core/type_traits.h b/aster/include/aster/core/type_traits.h new file mode 100644 index 0000000..59abf85 --- /dev/null +++ b/aster/include/aster/core/type_traits.h @@ -0,0 +1,36 @@ +// ============================================= +// Aster: type_traits.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +struct Device; + +namespace concepts +{ +template +concept DeviceDestructible = requires(T a, Device *p) { + { a.Destroy(p) } -> std::convertible_to; +}; + +template +concept Committable = requires(T a, bool v) { + { a.IsCommitted() } -> std::convertible_to; + { a.SetCommitted(v) } -> std::convertible_to; +}; + +template +constexpr bool GpuResource = false; + +template +concept RenderResource = GpuResource and std::is_default_constructible_v and std::is_trivially_copyable_v and + DeviceDestructible and Committable; + +template +constexpr bool IsHandle = false; + +template +concept HandleType = IsHandle and RenderResource; + +} // namespace concepts \ No newline at end of file diff --git a/aster/include/aster/systems/CMakeLists.txt b/aster/include/aster/systems/CMakeLists.txt index 381d233..38a40e7 100644 --- a/aster/include/aster/systems/CMakeLists.txt +++ b/aster/include/aster/systems/CMakeLists.txt @@ -6,4 +6,5 @@ target_sources(aster_core INTERFACE "manager.h" "buffer_manager.h" - "image_manager.h") + "image_manager.h" + "render_resource_manager.h") diff --git a/aster/include/aster/systems/buffer_manager.h b/aster/include/aster/systems/buffer_manager.h index bcfd8eb..363b6c1 100644 --- a/aster/include/aster/systems/buffer_manager.h +++ b/aster/include/aster/systems/buffer_manager.h @@ -11,15 +11,14 @@ namespace systems { - using BufferHandle = Handle; class BufferManager final : public Manager { public: - BufferManager(const Device *device, const u32 maxCount); + BufferManager(const Device *device, const u32 maxCount, const u8 binding); - Handle CreateStorageBuffer(usize size, cstr name = nullptr); - Handle CreateUniformBuffer(usize size, cstr name = nullptr); + [[nodiscard]] Handle CreateStorageBuffer(usize size, cstr name = nullptr); + [[nodiscard]] Handle CreateUniformBuffer(usize size, cstr name = nullptr); }; } // namespace systems diff --git a/aster/include/aster/systems/image_manager.h b/aster/include/aster/systems/image_manager.h index 0da9fd6..77a7d13 100644 --- a/aster/include/aster/systems/image_manager.h +++ b/aster/include/aster/systems/image_manager.h @@ -50,11 +50,11 @@ using ImageHandle = Handle; class ImageManager final : public Manager { public: - ImageManager(const Device *device, const u32 maxCount); + ImageManager(const Device *device, const u32 maxCount, const u8 binding); - Handle CreateTexture2D(const Texture2DCreateInfo& createInfo); - Handle CreateTextureCube(const TextureCubeCreateInfo &createInfo); - Handle CreateAttachment(const AttachmentCreateInfo &createInfo); - Handle CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo); + [[nodiscard]] Handle CreateTexture2D(const Texture2DCreateInfo &createInfo); + [[nodiscard]] Handle CreateTextureCube(const TextureCubeCreateInfo &createInfo); + [[nodiscard]] Handle CreateAttachment(const AttachmentCreateInfo &createInfo); + [[nodiscard]] Handle CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo); }; } // namespace systems diff --git a/aster/include/aster/systems/manager.h b/aster/include/aster/systems/manager.h index ee6c954..3abd077 100644 --- a/aster/include/aster/systems/manager.h +++ b/aster/include/aster/systems/manager.h @@ -6,21 +6,14 @@ #pragma once #include "aster/aster.h" +#include "aster/core/type_traits.h" struct Device; - -template -concept IsDeviceDestructible = requires(T a, Device *p) { - { a.Destroy(p) } -> std::convertible_to; -}; - -template - requires IsDeviceDestructible +template class Handle; -template - requires std::is_default_constructible_v && IsDeviceDestructible +template class Manager { friend Handle; @@ -29,21 +22,21 @@ class Manager using Type = T; using Handle = Handle; static_assert(sizeof(Handle) == sizeof(u32)); + constexpr static u32 MAX_HANDLES = Handle::INDEX_MASK + 1; - static Manager * - Instance() - { - assert(m_Instance); - - return m_Instance; - } - - explicit Manager(const Device *device, const u32 maxCount) + /** + * Constructor for the Manager class template. + * @param device Device with which resources are created. + * @param maxCount Max number of resources that can be created (maxCount <= Handle::INDEX_MASK) + * @param binding The shader binding at which this manager will bind its resources. + */ + explicit Manager(const Device *device, const u32 maxCount, const u8 binding) : m_MaxCount{maxCount} - , m_FreeHead{0} + , m_Binding{binding} , m_Device{device} { assert(!m_Instance); + assert(maxCount <= MAX_HANDLES); m_Data = new Type[m_MaxCount]; m_RefCount = new std::atomic[m_MaxCount]; @@ -73,18 +66,36 @@ class Manager m_MaxCount = 0; m_FreeHead = 0; m_Device = nullptr; + + m_Instance = nullptr; + } + + /** + * @warning only to be used internally. + * @return The only constructed instance of this manager. + */ + static Manager * + Instance() + { + assert(m_Instance); + return m_Instance; } PIN_MEMORY(Manager); private: - Type *m_Data = nullptr; // Data also keeps the freelist during 'not use'. - std::atomic *m_RefCount = nullptr; - u32 m_MaxCount = 0; + Type *m_Data = nullptr; // Data also keeps the freelist during 'not use'. + std::atomic *m_RefCount = nullptr; // Associated reference count for each of the instances in Data. + u32 m_MaxCount = 0; // Max number of resources supported. u32 m_FreeHead = 0; + u8 m_Binding = 0; static Manager *m_Instance; + /** + * User is expected to type-check. + * @param index Actual index of the resource in the m_Data array. Not type checked. + */ void AddRef(const u32 index) { @@ -92,6 +103,10 @@ class Manager ++m_RefCount[index]; } + /** + * User is expected to type-check. + * @param index Actual index of the resource in the m_Data array. Not type checked. + */ void Release(const u32 index) { @@ -100,10 +115,16 @@ class Manager assert(rc != MaxValue); if (rc == 0) { + // TODO: Don't destroy here. Separate out to a cleanup routine. m_Data[index].Destroy(m_Device); } } + /** + * User is expected to type-check. + * @param index Actual index of the resource in the m_Data array. Not type checked. + * @return Pointer to the resource at the index. + */ Type * Fetch(const u32 index) { @@ -114,7 +135,11 @@ class Manager protected: const Device *m_Device; - std::pair + /** + * Internal Method to Allocate a resource on the manager. + * @return [Handle, Type*] Where Type* is available to initialize the resource. + */ + [[nodiscard]] std::pair Alloc() { ERROR_IF(m_FreeHead >= m_MaxCount, "Max buffers allocated.") THEN_ABORT(-1); @@ -122,12 +147,11 @@ class Manager const auto index = m_FreeHead; Type *pAlloc = &m_Data[index]; m_FreeHead = *Recast(pAlloc); - return {Handle{index}, pAlloc}; + return {Handle{index, m_Binding}, pAlloc}; } }; -template - requires IsDeviceDestructible +template class Ref { public: @@ -188,7 +212,7 @@ class Ref // The only constructor requires a valid construction. explicit Ref(Handle &&handle) - : m_Handle{handle} + : m_Handle{std::forward(handle)} { InitPtr(); } @@ -207,21 +231,63 @@ class Ref ~Ref() = default; }; -template - requires IsDeviceDestructible -class Handle +class RawHandle +{ + protected: + constexpr static u32 INVALID_HANDLE = MaxValue; + constexpr static u32 INDEX_MASK = 0x0FFFFFFF; + constexpr static u32 TYPE_MASK = ~INDEX_MASK; + constexpr static u32 TYPE_OFFSET = GetMaskOffset(TYPE_MASK); + u32 m_Internal = INVALID_HANDLE; + + RawHandle(const u32 index, const u8 typeId) + : m_Internal{(index & INDEX_MASK) | (typeId & TYPE_MASK)} + { + } + + explicit RawHandle(const u32 internal) + : m_Internal{internal} + { + } + + public: + + [[nodiscard]] bool + IsValid() const + { + return m_Internal != INVALID_HANDLE; + } + + [[nodiscard]] u32 + GetIndex() const + { + return m_Internal & INDEX_MASK; + } + + [[nodiscard]] u32 + GetType() const + { + return (m_Internal & TYPE_MASK) >> TYPE_OFFSET; + } + + bool + operator==(const RawHandle &other) const + { + return m_Internal == other.m_Internal; + } +}; + +template +class Handle : public RawHandle { public: using Type = T; using Manager = Manager; protected: - constexpr static u32 INVALID_HANDLE = MaxValue; - u32 m_Internal = INVALID_HANDLE; - // The only constructor requires a valid construction. - explicit Handle(const u32 index) - : m_Internal{index} + Handle(const u32 index, const u8 typeId) + : RawHandle{index, typeId} { AddRef(); } @@ -231,14 +297,26 @@ class Handle public: Handle(const Handle &other) - : m_Internal(other.m_Internal) + : RawHandle{other} { AddRef(); } Handle(Handle &&other) noexcept + : RawHandle{std::exchange(other.m_Internal, m_Internal)} { - std::swap(this->m_Internal, other.m_Internal); + } + + [[nodiscard]] Ref + ToPointer() + { + return Ref{std::move(*this)}; + } + + [[nodiscard]] Type * + Fetch() const + { + return Manager::Instance()->Fetch(m_Internal); } Handle & @@ -268,28 +346,16 @@ class Handle } } - Ref - ToPointer() - { - return Ref{std::move(*this)}; - } - protected: void AddRef() { - Manager::Instance()->AddRef(m_Internal); + Manager::Instance()->AddRef(GetIndex()); } void Release() { - Manager::Instance()->Release(m_Internal); - } - - Type * - Fetch() - { - return Manager::Instance()->Fetch(m_Internal); + Manager::Instance()->Release(GetIndex()); } }; diff --git a/aster/include/aster/systems/render_resource_manager.h b/aster/include/aster/systems/render_resource_manager.h new file mode 100644 index 0000000..753ad3f --- /dev/null +++ b/aster/include/aster/systems/render_resource_manager.h @@ -0,0 +1,157 @@ +// ============================================= +// Aster: render_resource_manager.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "aster/aster.h" +#include "buffer_manager.h" +#include "image_manager.h" + +#include "EASTL/deque.h" +#include "EASTL/vector.h" + +namespace systems +{ + +class RenderResourceManager +{ + private: + union WriteInfo { + vk::DescriptorBufferInfo uBufferInfo; + vk::DescriptorImageInfo uImageInfo; + vk::BufferView uBufferView; + + explicit WriteInfo(const vk::DescriptorBufferInfo &info); + explicit WriteInfo(const vk::DescriptorImageInfo &info); + explicit WriteInfo(const vk::BufferView &info); + }; + + using WriteCommand = vk::WriteDescriptorSet; + + union WriteOwner { + Handle uBufferHandle; + Handle uImageHandle; + + explicit WriteOwner(const Handle &handle); + explicit WriteOwner(const Handle &handle); + + WriteOwner(const WriteOwner &other) + { + switch (uRawHandle.GetType()) + { + case BUFFER_BINDING_INDEX: + uBufferHandle = other.uBufferHandle; + break; + case IMAGE_BINDING_INDEX: + uImageHandle = other.uImageHandle; + break; + default: + ERROR("Invalid Handle type.") THEN_ABORT(-1); + } + } + + WriteOwner(WriteOwner &&other) noexcept + { + switch (uRawHandle.GetType()) + { + case BUFFER_BINDING_INDEX: + uBufferHandle = std::move(other.uBufferHandle); + break; + case IMAGE_BINDING_INDEX: + uImageHandle = std::move(other.uImageHandle); + break; + default: + ERROR("Invalid Handle type.") THEN_ABORT(-1); + } + } + + WriteOwner & + operator=(const WriteOwner &other) + { + if (this == &other) + return *this; + + switch (uRawHandle.GetType()) + { + case BUFFER_BINDING_INDEX: + uBufferHandle = other.uBufferHandle; + break; + case IMAGE_BINDING_INDEX: + uImageHandle = other.uImageHandle; + break; + default: + ERROR("Invalid Handle type.") THEN_ABORT(-1); + } + + return *this; + } + + WriteOwner & + operator=(WriteOwner &&other) noexcept + { + if (this == &other) + return *this; + + switch (uRawHandle.GetType()) + { + case BUFFER_BINDING_INDEX: + uBufferHandle = std::move(other.uBufferHandle); + break; + case IMAGE_BINDING_INDEX: + uImageHandle = std::move(other.uImageHandle); + break; + default: + ERROR("Invalid Handle type.") THEN_ABORT(-1); + } + return *this; + } + + ~WriteOwner() + { + switch (uRawHandle.GetType()) + { + case BUFFER_BINDING_INDEX: + uBufferHandle.~Handle(); + return; + case IMAGE_BINDING_INDEX: + uImageHandle.~Handle(); + return; + default: + ERROR("Invalid Handle type.") THEN_ABORT(-1); + } + } + + private: + RawHandle uRawHandle; + }; + + public: + RenderResourceManager(const Device *device, u32 maxBuffers, u32 maxImages); + + void Commit(concepts::HandleType auto &handle); + + private: + BufferManager m_BufferManager; + ImageManager m_ImageManager; + + vk::DescriptorPool m_DescriptorPool; + vk::DescriptorSetLayout m_SetLayout; + vk::DescriptorSet m_DescriptorSet; + + constexpr static u8 BUFFER_BINDING_INDEX = 0; + constexpr static u8 IMAGE_BINDING_INDEX = 1; + + eastl::vector m_Writes; + eastl::deque m_WriteInfos; + eastl::vector m_WriteOwner; + +#if !defined(ASTER_NDEBUG) + usize m_CommitedBufferCount = 0; + usize m_CommitedTextureCount = 0; + usize m_CommitedStorageTextureCount = 0; +#endif +}; + +} // namespace systems \ No newline at end of file diff --git a/aster/src/aster/systems/CMakeLists.txt b/aster/src/aster/systems/CMakeLists.txt index 18afab3..1b1215b 100644 --- a/aster/src/aster/systems/CMakeLists.txt +++ b/aster/src/aster/systems/CMakeLists.txt @@ -6,4 +6,5 @@ target_sources(aster_core PRIVATE "manager.cpp" "buffer_manager.cpp" -"image_manager.cpp") +"image_manager.cpp" +"render_resource_manager.cpp") diff --git a/aster/src/aster/systems/buffer_manager.cpp b/aster/src/aster/systems/buffer_manager.cpp index bf4b4e1..580f004 100644 --- a/aster/src/aster/systems/buffer_manager.cpp +++ b/aster/src/aster/systems/buffer_manager.cpp @@ -5,7 +5,7 @@ #include "systems/buffer_manager.h" -Manager *Manager::m_Instance; +Manager *Manager::m_Instance = nullptr; using namespace systems; @@ -28,7 +28,7 @@ BufferManager::CreateStorageBuffer(const usize size, const cstr name) } Manager::Handle -BufferManager::CreateUniformBuffer(usize size, cstr name) +BufferManager::CreateUniformBuffer(const usize size, const cstr name) { auto [handle, object] = Alloc(); @@ -42,7 +42,7 @@ BufferManager::CreateUniformBuffer(usize size, cstr name) return std::move(handle); } -BufferManager::BufferManager(const Device *device, const u32 maxCount) - : Manager{device, maxCount} +BufferManager::BufferManager(const Device *device, const u32 maxCount, const u8 binding) + : Manager{device, maxCount, binding} { } \ No newline at end of file diff --git a/aster/src/aster/systems/image_manager.cpp b/aster/src/aster/systems/image_manager.cpp index 7469892..adfe232 100644 --- a/aster/src/aster/systems/image_manager.cpp +++ b/aster/src/aster/systems/image_manager.cpp @@ -7,7 +7,7 @@ #include "core/device.h" -Manager *Manager::m_Instance; +Manager *Manager::m_Instance = nullptr; using namespace systems; @@ -310,7 +310,7 @@ ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo) }; } -ImageManager::ImageManager(const Device *device, const u32 maxCount) - : Manager{device, maxCount} +ImageManager::ImageManager(const Device *device, const u32 maxCount, const u8 binding) + : Manager{device, maxCount, binding} { } \ No newline at end of file diff --git a/aster/src/aster/systems/render_resource_manager.cpp b/aster/src/aster/systems/render_resource_manager.cpp new file mode 100644 index 0000000..1534470 --- /dev/null +++ b/aster/src/aster/systems/render_resource_manager.cpp @@ -0,0 +1,195 @@ +// ============================================= +// Aster: render_resource_manager.cpp +// Copyright (c) 2020-2025 Anish Bhobe +// ============================================= + +#include "systems/render_resource_manager.h" + +#include "EASTL/array.h" +#include "core/device.h" + +#define AbortIfFailed(RESULT) \ + do \ + { \ + vk::Result _checkResultValue_; \ + ERROR_IF(Failed(_checkResultValue_ = Cast(RESULT)), "Cause: {}", _checkResultValue_) \ + THEN_ABORT(_checkResultValue_); \ + } while (false) + +#define AbortIfFailedMV(RESULT, MSG, EXTRA) \ + do \ + { \ + vk::Result _checkResultValue_; \ + ERROR_IF(Failed(_checkResultValue_ = Cast(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \ + THEN_ABORT(_checkResultValue_); \ + } while (false) + +#define AbortIfFailedM(RESULT, MSG) \ + do \ + { \ + auto _checkResultValue_ = Cast(RESULT); \ + ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \ + } while (false) + + +using namespace systems; + +u32 +GetHandleInternal(concepts::HandleType auto &handle) +{ + return *Recast(&handle); +} + +RenderResourceManager::WriteOwner::WriteOwner(const Handle &handle) + : uBufferHandle(handle) +{ +} + +RenderResourceManager::WriteOwner::WriteOwner(const Handle &handle) + : uImageHandle(handle) +{ +} + +RenderResourceManager::RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages) + : m_BufferManager{device, maxBuffers, BUFFER_BINDING_INDEX} + , m_ImageManager{device, maxImages, IMAGE_BINDING_INDEX} +{ + eastl::array poolSizes = { + vk::DescriptorPoolSize{ + .type = vk::DescriptorType::eStorageBuffer, + .descriptorCount = maxBuffers, + }, + vk::DescriptorPoolSize{ + .type = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = maxImages, + }, + //vk::DescriptorPoolSize{ + // .type = vk::DescriptorType::eStorageImage, + // .descriptorCount = storageTexturesCount, + //}, + }; + + const vk::DescriptorPoolCreateInfo poolCreateInfo = { + .flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind, + .maxSets = 1, + .poolSizeCount = Cast(poolSizes.size()), + .pPoolSizes = poolSizes.data(), + }; + AbortIfFailed(device->m_Device.createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool)); + + eastl::array descriptorLayoutBindings = { + vk::DescriptorSetLayoutBinding{ + .binding = BUFFER_BINDING_INDEX, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .descriptorCount = Cast(maxBuffers), + .stageFlags = vk::ShaderStageFlagBits::eAll, + }, + vk::DescriptorSetLayoutBinding{ + .binding = IMAGE_BINDING_INDEX, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = Cast(maxImages), + .stageFlags = vk::ShaderStageFlagBits::eAll, + }, + //vk::DescriptorSetLayoutBinding{ + // .binding = STORAGE_TEXTURE_BINDING_INDEX, + // .descriptorType = vk::DescriptorType::eStorageImage, + // .descriptorCount = Cast(storageTexturesCount), + // .stageFlags = vk::ShaderStageFlagBits::eAll, + //}, + }; + + vk::DescriptorBindingFlags bindingFlags = + vk::DescriptorBindingFlagBits::ePartiallyBound | vk::DescriptorBindingFlagBits::eUpdateAfterBind; + + eastl::array layoutBindingFlags; + layoutBindingFlags.fill(bindingFlags); + + vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = { + .bindingCount = Cast(layoutBindingFlags.size()), + .pBindingFlags = layoutBindingFlags.data(), + }; + + static_assert(layoutBindingFlags.size() == descriptorLayoutBindings.size()); + const vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { + .pNext = &bindingFlagsCreateInfo, + .flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool, + .bindingCount = Cast(descriptorLayoutBindings.size()), + .pBindings = descriptorLayoutBindings.data(), + }; + AbortIfFailed(device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout)); + + // One descriptor is enough. Updating it at any time is safe. (Update until submit, data held when pending) + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html + // https://github.com/KhronosGroup/Vulkan-Guide/blob/main/chapters/extensions/VK_EXT_descriptor_indexing.adoc + const vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = { + .descriptorPool = m_DescriptorPool, + .descriptorSetCount = 1, + .pSetLayouts = &m_SetLayout, + }; + AbortIfFailed(device->m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet)); + + device->SetName(m_SetLayout, "Bindless Layout"); + device->SetName(m_DescriptorPool, "Bindless Pool"); + device->SetName(m_DescriptorSet, "Bindless Set"); +} + +void +systems::RenderResourceManager::Commit(concepts::HandleType auto &handle) +{ + using HandleType = decltype(handle)::Type; + if constexpr (std::is_same_v) + { + const Buffer *buffer = handle.Fetch(); + + m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{ + .buffer = buffer->m_Buffer, + .offset = 0, + .range = buffer->GetSize(), + }); + m_Writes.push_back({ + .dstSet = m_DescriptorSet, + .dstBinding = BUFFER_BINDING_INDEX, + .dstArrayElement = handle.GetIndex(), + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .pBufferInfo = &m_WriteInfos.back().uBufferInfo, + }); + } + else if constexpr (std::is_same_v) + { + const Image *image = handle.Fetch(); + + m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ + .sampler = nullptr /* TODO Sampler */, + .imageView = image->m_View, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }); + m_Writes.push_back({ + .dstSet = m_DescriptorSet, + .dstBinding = IMAGE_BINDING_INDEX, + .dstArrayElement = handle.GetIndex(), + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampledImage, + .pImageInfo = &m_WriteInfos.back().uImageInfo, + }); + } else { + static_assert(false && "Type is currently unsupported"); + } + + m_WriteOwner.emplace_back(handle); +} + +RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info) + : uBufferInfo{info} +{ +} + +RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorImageInfo &info) + : uImageInfo{info} +{ +} + +RenderResourceManager::WriteInfo::WriteInfo(const vk::BufferView &info) + : uBufferView{info} +{ +} \ No newline at end of file diff --git a/samples/02_box/box.cpp b/samples/02_box/box.cpp index 3d7da96..f26a436 100644 --- a/samples/02_box/box.cpp +++ b/samples/02_box/box.cpp @@ -115,8 +115,8 @@ main(int, char **) Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"}; Pipeline pipeline = CreatePipeline(&device, &swapchain); - systems::BufferManager bufferManager{&device, 12}; - systems::ImageManager imageManager{&device, 12}; + systems::BufferManager bufferManager{&device, 12, 0}; + systems::ImageManager imageManager{&device, 12, 1}; Camera camera = { .m_Model = {1.0f}, diff --git a/samples/03_model_render/gpu_resource_manager.cpp b/samples/03_model_render/gpu_resource_manager.cpp index ffc050d..3a439d9 100644 --- a/samples/03_model_render/gpu_resource_manager.cpp +++ b/samples/03_model_render/gpu_resource_manager.cpp @@ -23,7 +23,7 @@ TextureManager::Init(const u32 maxCapacity) TextureHandle TextureManager::Commit(Texture *texture) { - ERROR_IF(!texture || !texture->IsValid(), "Texture must be valid for commital") + ERROR_IF(!texture || !texture->IsValid(), "Texture must be valid for committal") THEN_ABORT(-1); if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE)