From 41c91058b62d7c7f7529d1965f70c8eaa5df5c91 Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sat, 10 May 2025 18:00:25 +0200 Subject: [PATCH] Shader reflection added. --- aster/include/aster/core/global.h | 25 +- aster/include/aster/core/physical_device.h | 33 ++ aster/include/aster/systems/device.h | 75 +--- .../include/aster/systems/pipeline_helpers.h | 74 ++++ aster/include/aster/systems/resource.h | 8 +- aster/src/aster/systems/CMakeLists.txt | 1 + aster/src/aster/systems/device.cpp | 133 +++---- aster/src/aster/systems/pipeline_helpers.cpp | 374 ++++++++++++++++++ samples/02_box/box.cpp | 11 +- samples/02_box/shader/triangle.slang | 1 - 10 files changed, 578 insertions(+), 157 deletions(-) create mode 100644 aster/include/aster/systems/pipeline_helpers.h create mode 100644 aster/src/aster/systems/pipeline_helpers.cpp diff --git a/aster/include/aster/core/global.h b/aster/include/aster/core/global.h index 9865316..443db8b 100644 --- a/aster/include/aster/core/global.h +++ b/aster/include/aster/core/global.h @@ -52,14 +52,15 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3; #define Take(ELEMENT) eastl::exchange(ELEMENT, {}) -#define TODO(MSG) assert(false && ("Unimplemented: " MSG)) -#define FIX(MSG) static_assert(false && ("Unimplemented: " MSG)) +#define TODO(...) assert(!("Unimplemented: " __VA_ARGS__)) +#define FIX(...) static_assert(!("Unimplemented: " __VA_ARGS__)) +#define UNREACHABLE(...) assert(!("Unreachable: " __VA_ARGS__)) #define AbortIfFailed(RESULT) \ do \ { \ vk::Result _checkResultValue_; \ - ERROR_IF(Failed(_checkResultValue_ = static_cast(RESULT)), "Cause: {}", _checkResultValue_) \ + ERROR_IF(Failed(_checkResultValue_ = static_cast(RESULT)), "Cause: {}", _checkResultValue_) \ THEN_ABORT(_checkResultValue_); \ } while (false) @@ -67,14 +68,15 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3; do \ { \ vk::Result _checkResultValue_; \ - ERROR_IF(Failed(_checkResultValue_ = static_cast(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \ + ERROR_IF(Failed(_checkResultValue_ = static_cast(RESULT)), MSG " Cause: {}", EXTRA, \ + _checkResultValue_) \ THEN_ABORT(_checkResultValue_); \ } while (false) #define AbortIfFailedM(RESULT, MSG) \ do \ { \ - auto _checkResultValue_ = static_cast(RESULT); \ + auto _checkResultValue_ = static_cast(RESULT); \ ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \ } while (false) @@ -221,15 +223,20 @@ GetMaskOffset(u32 val) return count; } -template <> -struct fmt::formatter : nested_formatter +template +concept VkToString = requires(T a) { + { vk::to_string(a) } -> std::convertible_to; +}; + +template +struct fmt::formatter : nested_formatter { auto // ReSharper disable once CppInconsistentNaming - format(vk::Result result, format_context &ctx) const + format(T result, format_context &ctx) const { return write_padded(ctx, - [this, result](auto out) { return v11::format_to(out, "{}", nested(to_string(result))); }); + [this, result](auto out) { return fmt::format_to(out, "{}", nested(to_string(result))); }); } }; diff --git a/aster/include/aster/core/physical_device.h b/aster/include/aster/core/physical_device.h index fc1e7e2..289e1f5 100644 --- a/aster/include/aster/core/physical_device.h +++ b/aster/include/aster/core/physical_device.h @@ -8,6 +8,8 @@ #include "global.h" #include "surface.h" +#include + #include struct Window; @@ -23,6 +25,31 @@ enum class QueueSupportFlagBits using QueueSupportFlags = vk::Flags; +inline std::string +// ReSharper disable once CppInconsistentNaming +format_as(const QueueSupportFlags &qfi) +{ + std::stringstream sb; + if (qfi & QueueSupportFlagBits::eGraphics) + { + sb << "Graphics | "; + } + if (qfi & QueueSupportFlagBits::eTransfer) + { + sb << "Transfer | "; + } + if (qfi & QueueSupportFlagBits::eCompute) + { + sb << "Compute | "; + } + if (qfi & QueueSupportFlagBits::ePresent) + { + sb << "Present | "; + } + const auto sbv = sb.view(); + return std::string(sbv.substr(0, sbv.size() - 3)); +} + struct QueueFamilyInfo { u32 m_Index; @@ -30,6 +57,12 @@ struct QueueFamilyInfo QueueSupportFlags m_Support; }; +inline std::string +format_as(const QueueFamilyInfo &qfi) +{ + return fmt::format("Queue {}: Count={} Support={}", qfi.m_Index, qfi.m_Count, qfi.m_Support); +} + [[nodiscard]] vk::SurfaceCapabilitiesKHR GetSurfaceCapabilities(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface); diff --git a/aster/include/aster/systems/device.h b/aster/include/aster/systems/device.h index fbf1ddd..27d4b13 100644 --- a/aster/include/aster/systems/device.h +++ b/aster/include/aster/systems/device.h @@ -5,6 +5,7 @@ #pragma once +#include "pipeline_helpers.h" #include "resource.h" #include "aster/aster.h" @@ -231,59 +232,6 @@ struct SamplerCreateInfo #pragma region Pipeline // ---------------------------------------------------------------------------------------------------- -struct PipelineCreationError -{ - enum class Kind - { - eNone, - eVulkan, - eSlang, - } m_Kind; - union { - u32 uNone; - vk::Result uVulkanResult; - SlangResult uSlangResult; - }; - - operator bool() const - { - return m_Kind != Kind::eNone; - } - - std::string - What() - { - switch (m_Kind) - { - case Kind::eNone: - return "No Error"; - case Kind::eVulkan: - return fmt::format("{}", uVulkanResult); - case Kind::eSlang: - return fmt::format("{}", uSlangResult); - } - return "No Error"; - } - - PipelineCreationError() - : m_Kind{Kind::eNone} - , uNone{} - { - } - - PipelineCreationError(vk::Result result) - : m_Kind{Kind::eVulkan} - , uVulkanResult{result} - { - } - - PipelineCreationError(SlangResult result) - : m_Kind{Kind::eSlang} - , uSlangResult{result} - { - } -}; - struct AttributeInfo { u32 m_Location; @@ -402,7 +350,7 @@ struct Frame; class Context { -protected: + protected: vk::CommandBuffer m_Cmd; friend Device; @@ -426,13 +374,14 @@ class GraphicsContext : public Context friend Device; friend Frame; + const Pipeline *m_PipelineInUse; + explicit GraphicsContext(const vk::CommandBuffer cmd) : Context{cmd} + , m_PipelineInUse{nullptr} { } - const Pipeline *m_PipelineInUse; - public: DEPRECATE_RAW_CALLS void SetViewport(const vk::Viewport &viewport); void BindVertexBuffer(const Ref &vertexBuffer); @@ -440,10 +389,14 @@ class GraphicsContext : public Context void PushConstantBlock(auto &block) { - m_Cmd.pushConstants(m_PipelineInUse->m_Layout, vk::ShaderStageFlagBits::eAllGraphics, 0, sizeof block, &block); + if constexpr (sizeof(block) > 128) + { + WARN("Vulkan only guarantees 128 bytes of Push Constants. Size of PCB is {}", sizeof block); + } + m_Cmd.pushConstants(m_PipelineInUse->m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof block, &block); } - void Draw(u32 vertexCount); - void DrawIndexed(u32 indexCount); + void Draw(usize vertexCount); + void DrawIndexed(usize indexCount); DEPRECATE_RAW_CALLS void BeginRendering(const vk::RenderingInfo &renderingInfo); void EndRendering(); @@ -480,6 +433,8 @@ class TransferContext : public Context TransferContext &operator=(TransferContext &&other) noexcept; DISALLOW_COPY_AND_ASSIGN(TransferContext); + + ~TransferContext() = default; }; struct Frame @@ -514,6 +469,8 @@ struct Frame Frame &operator=(Frame &&other) noexcept; DISALLOW_COPY_AND_ASSIGN(Frame); + + ~Frame() = default; }; class Device final diff --git a/aster/include/aster/systems/pipeline_helpers.h b/aster/include/aster/systems/pipeline_helpers.h new file mode 100644 index 0000000..ff1d51b --- /dev/null +++ b/aster/include/aster/systems/pipeline_helpers.h @@ -0,0 +1,74 @@ +// ============================================= +// Aster: pipeline_helpers.h +// Copyright (c) 2020-2025 Anish Bhobe +// ============================================= + +#pragma once + +#include + +#include +#include +#include + +namespace systems +{ +class Device; + +struct PipelineCreationError +{ + std::variant m_Data; + + std::string What(); + i32 Value(); + + operator bool() const; + + PipelineCreationError(vk::Result res); + PipelineCreationError(SlangResult res); + PipelineCreationError(); +}; + +vk::ShaderStageFlagBits SlangToVulkanShaderStage(SlangStage stage); + +namespace _internal +{ +struct PipelineLayoutBuilder +{ + Device *m_Device; + eastl::vector m_DescriptorSetLayouts; + eastl::vector m_PushConstants; + vk::ShaderStageFlags m_Stage; + + explicit PipelineLayoutBuilder(Device *device, vk::DescriptorSetLayout bindlessLayout = {}); + + [[nodiscard]] vk::PipelineLayout Build(); + + [[nodiscard]] vk::DescriptorSetLayout CreateDescriptorSetLayout(const vk::DescriptorSetLayoutCreateInfo &createInfo) const; + void AddDescriptorSetForParameterBlock(slang::TypeLayoutReflection * layout); + void AddPushConstantRangeForConstantBuffer(slang::TypeLayoutReflection * layout); + void AddSubObjectRange(slang::TypeLayoutReflection * layout, i64 subObjectRangeIndex); + void AddSubObjectRanges(slang::TypeLayoutReflection * layout); +}; + +struct DescriptorLayoutBuilder +{ + PipelineLayoutBuilder *m_PipelineLayoutBuilder; + eastl::vector m_LayoutBindings; + u32 m_SetIndex; + + vk::ShaderStageFlags &Stage() const; + explicit DescriptorLayoutBuilder(PipelineLayoutBuilder *pipelineLayoutBuilder); + + void AddGlobalScopeParameters(slang::ProgramLayout *layout); + void AddEntryPointParameters(slang::ProgramLayout *layout); + void AddEntryPointParameters(slang::EntryPointLayout *layout); + void AddAutomaticallyIntroducedUniformBuffer(); + void AddRanges(slang::TypeLayoutReflection *layout); + void AddRangesForParamBlockElement(slang::TypeLayoutReflection *layout); + void AddDescriptorRange(slang::TypeLayoutReflection *layout, i64 relativeSetIndex, i64 rangeIndex); + void AddDescriptorRanges(slang::TypeLayoutReflection *layout); + void Build(); +}; +} // namespace _internal +} // namespace systems diff --git a/aster/include/aster/systems/resource.h b/aster/include/aster/systems/resource.h index e1cf952..b99fc16 100644 --- a/aster/include/aster/systems/resource.h +++ b/aster/include/aster/systems/resource.h @@ -69,13 +69,15 @@ CastView(const Ref> &from) template class ResId { + using IdType = u32; public: - constexpr static u32 INVALID = MaxValue; + constexpr static IdType INVALID = MaxValue; private: - u32 m_Index; + IdType m_Index; + u32 m_Padding; //< Slang DescriptorHandle are a pair of u32. TODO: Use as validation. - explicit ResId(const u32 index) + explicit ResId(const IdType index) : m_Index{index} { AddRef(); diff --git a/aster/src/aster/systems/CMakeLists.txt b/aster/src/aster/systems/CMakeLists.txt index 96a8bde..52d8ca4 100644 --- a/aster/src/aster/systems/CMakeLists.txt +++ b/aster/src/aster/systems/CMakeLists.txt @@ -6,4 +6,5 @@ target_sources(aster_core PRIVATE "device.cpp" "commit_manager.cpp" +"pipeline_helpers.cpp" "sync_server.cpp") diff --git a/aster/src/aster/systems/device.cpp b/aster/src/aster/systems/device.cpp index 182d66a..f46bc6c 100644 --- a/aster/src/aster/systems/device.cpp +++ b/aster/src/aster/systems/device.cpp @@ -562,7 +562,10 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre } vk::PipelineLayout pipelineLayout; - auto result = CreatePipelineLayout(pipelineLayout, program); + if (auto result = CreatePipelineLayout(pipelineLayout, program)) + { + return result; + } eastl::fixed_vector inputBindingDescriptions; eastl::fixed_vector inputAttributeDescriptions; @@ -699,47 +702,6 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre ERROR("Unimplemented Stage " #STAGE); \ return SLANG_FAIL -static vk::ShaderStageFlagBits -SlangToVulkanShaderStage(const SlangStage stage) -{ - switch (stage) - { - case SLANG_STAGE_VERTEX: - return vk::ShaderStageFlagBits::eVertex; - case SLANG_STAGE_HULL: - return vk::ShaderStageFlagBits::eTessellationControl; - case SLANG_STAGE_DOMAIN: - return vk::ShaderStageFlagBits::eTessellationEvaluation; - case SLANG_STAGE_GEOMETRY: - return vk::ShaderStageFlagBits::eGeometry; - case SLANG_STAGE_FRAGMENT: - return vk::ShaderStageFlagBits::eFragment; - case SLANG_STAGE_COMPUTE: - return vk::ShaderStageFlagBits::eCompute; - case SLANG_STAGE_RAY_GENERATION: - return vk::ShaderStageFlagBits::eRaygenKHR; - case SLANG_STAGE_INTERSECTION: - return vk::ShaderStageFlagBits::eIntersectionKHR; - case SLANG_STAGE_ANY_HIT: - return vk::ShaderStageFlagBits::eAnyHitKHR; - case SLANG_STAGE_CLOSEST_HIT: - return vk::ShaderStageFlagBits::eClosestHitKHR; - case SLANG_STAGE_MISS: - return vk::ShaderStageFlagBits::eMissKHR; - case SLANG_STAGE_CALLABLE: - return vk::ShaderStageFlagBits::eCallableKHR; - case SLANG_STAGE_MESH: - return vk::ShaderStageFlagBits::eMeshEXT; - case SLANG_STAGE_AMPLIFICATION: - return vk::ShaderStageFlagBits::eTaskEXT; - case SLANG_STAGE_NONE: - case SLANG_STAGE_COUNT: - ERROR("Invalid Shader Stage"); - return {}; - } - ERROR("Unreachable") THEN_ABORT(-1); -} - systems::PipelineCreationError systems::Device::CreateShaders( eastl::fixed_vector &shadersOut, @@ -905,6 +867,39 @@ systems::Device::CreateShaders( return {}; } +struct PipelineLayoutBuilder +{ + systems::Device *m_Device; + eastl::vector m_DescriptorSetLayouts; + eastl::vector m_PushConstantRanges; + + explicit PipelineLayoutBuilder(systems::Device *device) + : m_Device{device} + { + } + + systems::PipelineCreationError + Build(vk::PipelineLayout &pipelineLayout, eastl::vector &descriptorSetLayouts); +}; + +systems::PipelineCreationError +PipelineLayoutBuilder::Build(vk::PipelineLayout &pipelineLayout, + eastl::vector &descriptorSetLayouts) +{ + const vk::PipelineLayoutCreateInfo layoutCreateInfo = { + .setLayoutCount = static_cast(m_DescriptorSetLayouts.size()), + .pSetLayouts = m_DescriptorSetLayouts.data(), + .pushConstantRangeCount = static_cast(m_PushConstantRanges.size()), + .pPushConstantRanges = m_PushConstantRanges.data(), + }; + const auto result = m_Device->m_Device->createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout); + for (const auto &descSet : descriptorSetLayouts) + { + m_Device->m_Device->destroy(descSet, nullptr); + } + return result; +} + systems::PipelineCreationError systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, const Slang::ComPtr &program) @@ -919,39 +914,20 @@ systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, return SLANG_FAIL; } - // TODO: Reflect to create the push constants and descriptor sets. - - // TODO: Hackery. To FIX. - vk::PushConstantRange pcr{ - .stageFlags = vk::ShaderStageFlagBits::eAllGraphics, - .offset = 0, - .size = 24, - }; - - u32 setLayoutCount = 0; - const vk::DescriptorSetLayout *setLayout = nullptr; - + vk::DescriptorSetLayout setLayout = {}; if (m_CommitManager) - { - setLayoutCount = 1; - setLayout = &m_CommitManager->GetDescriptorSetLayout(); - } + setLayout = m_CommitManager->GetDescriptorSetLayout(); - // TODO: END EXPERIMENT - - const vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { - .setLayoutCount = setLayoutCount, - .pSetLayouts = setLayout, - .pushConstantRangeCount = 1, - .pPushConstantRanges = &pcr, + _internal::PipelineLayoutBuilder pipelineLayoutBuilder{ + this, + setLayout, }; - vk::Result result = m_Device->createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout); - if (Failed(result)) - { - ERROR("Could not create a pipeline layout. Cause: {}", result); - return result; - } + _internal::DescriptorLayoutBuilder descriptorLayoutBuilder{&pipelineLayoutBuilder}; + descriptorLayoutBuilder.AddGlobalScopeParameters(layout); + descriptorLayoutBuilder.AddEntryPointParameters(layout); + descriptorLayoutBuilder.Build(); + pipelineLayout = pipelineLayoutBuilder.Build(); return {}; } @@ -990,7 +966,7 @@ FindAsyncTransferQueue(const PhysicalDevice &physicalDevice, u32 primaryQueueFam }; } } - WARN("No async transfer queue. Falling back to primary queue"); + WARN("No dedicated async transfer queue. Falling back to primary queue family."); return std::nullopt; } @@ -1002,7 +978,8 @@ FindAsyncComputeQueue(const PhysicalDevice &physicalDevice, u32 primaryQueueFami if (queueFamilyInfo.m_Index == primaryQueueFamilyIndex) continue; - if (queueFamilyInfo.m_Support == QueueSupportFlagBits::eCompute) + if (queueFamilyInfo.m_Support == + (QueueSupportFlags{QueueSupportFlagBits::eCompute} | QueueSupportFlagBits::eTransfer)) { return QueueAllocation{ .m_Family = queueFamilyInfo.m_Index, @@ -1010,7 +987,7 @@ FindAsyncComputeQueue(const PhysicalDevice &physicalDevice, u32 primaryQueueFami }; } } - WARN("No async compute queue. Falling back to primary queue"); + WARN("No async compute queue. Falling back to primary queue family."); return std::nullopt; } @@ -1471,15 +1448,15 @@ systems::GraphicsContext::BindPipeline(const Pipeline &pipeline) } void -systems::GraphicsContext::Draw(u32 vertexCount) +systems::GraphicsContext::Draw(const usize vertexCount) { - m_Cmd.draw(vertexCount, 1, 0, 0); + m_Cmd.draw(static_cast(vertexCount), 1, 0, 0); } void -systems::GraphicsContext::DrawIndexed(u32 indexCount) +systems::GraphicsContext::DrawIndexed(usize indexCount) { - m_Cmd.drawIndexed(indexCount, 1, 0, 0, 0); + m_Cmd.drawIndexed(static_cast(indexCount), 1, 0, 0, 0); } void diff --git a/aster/src/aster/systems/pipeline_helpers.cpp b/aster/src/aster/systems/pipeline_helpers.cpp new file mode 100644 index 0000000..db6fdcb --- /dev/null +++ b/aster/src/aster/systems/pipeline_helpers.cpp @@ -0,0 +1,374 @@ +// ============================================= +// Aster: pipeline_helpers.cpp +// Copyright (c) 2020-2025 Anish Bhobe +// ============================================= + +#include "systems/device.h" + +#include + +using namespace systems::_internal; + +struct WhatVisitor +{ + std::string + operator()(std::monostate) const + { + return "No Error"; + } + std::string + operator()(vk::Result result) const + { + return fmt::format("Vulkan Error: {}", result); + } + std::string + operator()(SlangResult result) const + { + return fmt::format("Slang Error: {}", result); + } +}; + +struct ValueVisitor +{ + i32 + operator()(std::monostate) const + { + return 0; + } + i32 + operator()(vk::Result result) const + { + return static_cast(result); + } + i32 + operator()(SlangResult result) const + { + return result; + } +}; + +i32 +systems::PipelineCreationError::Value() +{ + return std::visit(ValueVisitor{}, m_Data); +} + +systems::PipelineCreationError::PipelineCreationError(vk::Result res) + : m_Data{res} +{ +} + +systems::PipelineCreationError::PipelineCreationError(SlangResult res) + : m_Data{res} +{ +} + +systems::PipelineCreationError::PipelineCreationError() + : m_Data{std::monostate{}} +{ +} + +systems::PipelineCreationError::operator bool() const +{ + return not std::holds_alternative(m_Data); +} + +std::string +systems::PipelineCreationError::What() +{ + return std::visit(WhatVisitor{}, m_Data); +} + +vk::ShaderStageFlagBits +systems::SlangToVulkanShaderStage(const SlangStage stage) +{ + switch (stage) + { + case SLANG_STAGE_VERTEX: + return vk::ShaderStageFlagBits::eVertex; + case SLANG_STAGE_HULL: + return vk::ShaderStageFlagBits::eTessellationControl; + case SLANG_STAGE_DOMAIN: + return vk::ShaderStageFlagBits::eTessellationEvaluation; + case SLANG_STAGE_GEOMETRY: + return vk::ShaderStageFlagBits::eGeometry; + case SLANG_STAGE_FRAGMENT: + return vk::ShaderStageFlagBits::eFragment; + case SLANG_STAGE_COMPUTE: + return vk::ShaderStageFlagBits::eCompute; + case SLANG_STAGE_RAY_GENERATION: + return vk::ShaderStageFlagBits::eRaygenKHR; + case SLANG_STAGE_INTERSECTION: + return vk::ShaderStageFlagBits::eIntersectionKHR; + case SLANG_STAGE_ANY_HIT: + return vk::ShaderStageFlagBits::eAnyHitKHR; + case SLANG_STAGE_CLOSEST_HIT: + return vk::ShaderStageFlagBits::eClosestHitKHR; + case SLANG_STAGE_MISS: + return vk::ShaderStageFlagBits::eMissKHR; + case SLANG_STAGE_CALLABLE: + return vk::ShaderStageFlagBits::eCallableKHR; + case SLANG_STAGE_MESH: + return vk::ShaderStageFlagBits::eMeshEXT; + case SLANG_STAGE_AMPLIFICATION: + return vk::ShaderStageFlagBits::eTaskEXT; + case SLANG_STAGE_NONE: + case SLANG_STAGE_COUNT: + UNREACHABLE(); + return {}; + } + UNREACHABLE(); + return {}; +} + +PipelineLayoutBuilder::PipelineLayoutBuilder(Device *device, vk::DescriptorSetLayout bindlessLayout) + : m_Device{device} + , m_DescriptorSetLayouts{bindlessLayout} // if `null` will be filtered out during build. +{ +} + +vk::PipelineLayout +PipelineLayoutBuilder::Build() +{ + vk::PipelineLayout pipelineLayout; + + eastl::vector filteredDescriptorSetLayouts; + filteredDescriptorSetLayouts.reserve(m_DescriptorSetLayouts.size()); + for (auto dsl : m_DescriptorSetLayouts) + { + if (dsl) + { + filteredDescriptorSetLayouts.push_back(dsl); + } + } + + const vk::PipelineLayoutCreateInfo createInfo = { + .setLayoutCount = static_cast(filteredDescriptorSetLayouts.size()), + .pSetLayouts = filteredDescriptorSetLayouts.data(), + .pushConstantRangeCount = static_cast(m_PushConstants.size()), + .pPushConstantRanges = m_PushConstants.data(), + }; + AbortIfFailed(m_Device->m_Device->createPipelineLayout(&createInfo, nullptr, &pipelineLayout)); + + return pipelineLayout; +} + +vk::DescriptorSetLayout +PipelineLayoutBuilder::CreateDescriptorSetLayout(const vk::DescriptorSetLayoutCreateInfo &createInfo) const +{ + vk::DescriptorSetLayout dsl; + // Failure Cases are OoM errors. No recovery. + AbortIfFailed(m_Device->m_Device->createDescriptorSetLayout(&createInfo, nullptr, &dsl)); + return dsl; +} + +void +PipelineLayoutBuilder::AddDescriptorSetForParameterBlock(slang::TypeLayoutReflection *layout) +{ + DescriptorLayoutBuilder descriptorLayoutBuilder{this}; + descriptorLayoutBuilder.AddRangesForParamBlockElement(layout->getElementTypeLayout()); + descriptorLayoutBuilder.Build(); +} + +void +PipelineLayoutBuilder::AddPushConstantRangeForConstantBuffer(slang::TypeLayoutReflection *layout) +{ + const auto elementTypeLayout = layout->getElementTypeLayout(); + const auto elementSize = elementTypeLayout->getSize(); + + if (elementSize == 0) + return; + + m_PushConstants.push_back({ + .stageFlags = m_Stage, + .offset = 0, + .size = static_cast(elementSize), + }); +} + +void +PipelineLayoutBuilder::AddSubObjectRange(slang::TypeLayoutReflection *layout, i64 subObjectRangeIndex) +{ + auto bindingRangeIndex = layout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex); + switch (layout->getBindingRangeType(bindingRangeIndex)) + { + case slang::BindingType::ParameterBlock: { + const auto parameterBlockTypeLayout = layout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + + AddDescriptorSetForParameterBlock(parameterBlockTypeLayout); + } + break; + case slang::BindingType::PushConstant: { + const auto constantBufferTypeLayout = layout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + AddPushConstantRangeForConstantBuffer(constantBufferTypeLayout); + } + break; + default: + UNREACHABLE("Unexpected types"); + } +} + +vk::DescriptorType +BindingTypeToDescriptorType(slang::BindingType binding) +{ + using vk::DescriptorType; + switch (binding) + { + case slang::BindingType::Sampler: + return DescriptorType::eSampler; + case slang::BindingType::Texture: + return DescriptorType::eSampledImage; + case slang::BindingType::ConstantBuffer: + return DescriptorType::eUniformBuffer; + case slang::BindingType::TypedBuffer: + return DescriptorType::eStorageBuffer; + case slang::BindingType::RawBuffer: + return DescriptorType::eStorageBuffer; + case slang::BindingType::CombinedTextureSampler: + return DescriptorType::eCombinedImageSampler; + case slang::BindingType::InlineUniformData: + return DescriptorType::eInlineUniformBlock; + case slang::BindingType::RayTracingAccelerationStructure: + return DescriptorType::eAccelerationStructureKHR; + case slang::BindingType::MutableTexture: + return DescriptorType::eStorageImage; + case slang::BindingType::MutableTypedBuffer: + return DescriptorType::eStorageBuffer; + case slang::BindingType::MutableRawBuffer: + return DescriptorType::eStorageBuffer; + default: + UNREACHABLE("Unsupported Types"); + } + return {}; +} + +vk::ShaderStageFlags & +DescriptorLayoutBuilder::Stage() const +{ + return m_PipelineLayoutBuilder->m_Stage; +} + +DescriptorLayoutBuilder::DescriptorLayoutBuilder(PipelineLayoutBuilder *pipelineLayoutBuilder) + : m_PipelineLayoutBuilder{pipelineLayoutBuilder} + , m_SetIndex{static_cast(pipelineLayoutBuilder->m_DescriptorSetLayouts.size())} +{ + m_PipelineLayoutBuilder->m_DescriptorSetLayouts.push_back(); +} + +void +DescriptorLayoutBuilder::AddDescriptorRange(slang::TypeLayoutReflection *layout, const i64 relativeSetIndex, + const i64 rangeIndex) +{ + const auto bindingType = layout->getDescriptorSetDescriptorRangeType(relativeSetIndex, rangeIndex); + + if (bindingType == slang::BindingType::PushConstant) + return; + + const u32 descriptorCount = + static_cast(layout->getDescriptorSetDescriptorRangeDescriptorCount(relativeSetIndex, rangeIndex)); + + const u32 bindingIndex = static_cast(m_LayoutBindings.size()); + const auto vkBindingType = BindingTypeToDescriptorType(bindingType); + + m_LayoutBindings.push_back({ + .binding = bindingIndex, + .descriptorType = vkBindingType, + .descriptorCount = descriptorCount, + .stageFlags = Stage(), + }); +} + +void +DescriptorLayoutBuilder::AddDescriptorRanges(slang::TypeLayoutReflection *layout) +{ + i64 nSets = layout->getDescriptorSetCount(); + for (i64 relativeSetIndex = 0; relativeSetIndex < nSets; ++relativeSetIndex) + { + i64 rangeCount = layout->getDescriptorSetDescriptorRangeCount(relativeSetIndex); + + for (i64 rangeIndex = 0; rangeIndex < rangeCount; ++rangeIndex) + { + AddDescriptorRange(layout, relativeSetIndex, rangeIndex); + } + } +} + +void +DescriptorLayoutBuilder::Build() +{ + if (m_LayoutBindings.empty()) + return; + + const auto dsl = m_PipelineLayoutBuilder->CreateDescriptorSetLayout({ + .bindingCount = static_cast(m_LayoutBindings.size()), + .pBindings = m_LayoutBindings.data(), + }); + m_PipelineLayoutBuilder->m_DescriptorSetLayouts[m_SetIndex] = dsl; +} + +void +DescriptorLayoutBuilder::AddAutomaticallyIntroducedUniformBuffer() +{ + const auto vulkanBindingIndex = static_cast(m_LayoutBindings.size()); + + m_LayoutBindings.push_back({ + .binding = vulkanBindingIndex, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eAll, + }); +} + +void +DescriptorLayoutBuilder::AddRanges(slang::TypeLayoutReflection *layout) +{ + AddDescriptorRanges(layout); + m_PipelineLayoutBuilder->AddSubObjectRanges(layout); +} + +void +DescriptorLayoutBuilder::AddRangesForParamBlockElement(slang::TypeLayoutReflection *layout) +{ + if (layout->getSize() > 0) + { + AddAutomaticallyIntroducedUniformBuffer(); + } + + AddRanges(layout); +} + +void +DescriptorLayoutBuilder::AddGlobalScopeParameters(slang::ProgramLayout *layout) +{ + Stage() = vk::ShaderStageFlagBits::eAll; + AddRangesForParamBlockElement(layout->getGlobalParamsTypeLayout()); +} + +void +DescriptorLayoutBuilder::AddEntryPointParameters(slang::ProgramLayout *layout) +{ + u64 entryPointCount = layout->getEntryPointCount(); + for (u64 i = 0; i < entryPointCount; ++i) + { + auto *entryPoint = layout->getEntryPointByIndex(i); + AddEntryPointParameters(entryPoint); + } +} + +void +DescriptorLayoutBuilder::AddEntryPointParameters(slang::EntryPointLayout *layout) +{ + Stage() = SlangToVulkanShaderStage(layout->getStage()); + + AddRangesForParamBlockElement(layout->getTypeLayout()); +} + +void +PipelineLayoutBuilder::AddSubObjectRanges(slang::TypeLayoutReflection *layout) +{ + i64 subObjectRangeCount = layout->getSubObjectRangeCount(); + for (i64 subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; ++subObjectRangeIndex) + { + AddSubObjectRange(layout, subObjectRangeIndex); + } +} \ No newline at end of file diff --git a/samples/02_box/box.cpp b/samples/02_box/box.cpp index b01b557..1149c68 100644 --- a/samples/02_box/box.cpp +++ b/samples/02_box/box.cpp @@ -86,7 +86,7 @@ struct Camera int main(int, char **) { - MIN_LOG_LEVEL(Logger::LogType::eInfo); + MIN_LOG_LEVEL(Logger::LogType::eDebug); Window window = {"Box (Aster)", {640, 480}}; @@ -125,7 +125,7 @@ main(int, char **) {.m_ShaderFile = SHADER_FILE, .m_EntryPoints = {"vsmain", "fsmain"}}, }}); ERROR_IF(pipelineResult, "Could not create pipeline. Cause: {}", pipelineResult.What()) - THEN_ABORT(pipelineResult.uNone); + THEN_ABORT(pipelineResult.Value()); auto swapchainSize = device.GetSwapchainSize(); Camera camera = { @@ -229,8 +229,8 @@ main(int, char **) .dstAccessMask = vk::AccessFlagBits2::eShaderRead, .oldLayout = vk::ImageLayout::eTransferDstOptimal, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - .srcQueueFamilyIndex = vk::QueueFamilyIgnored, - .dstQueueFamilyIndex = vk::QueueFamilyIgnored, + .srcQueueFamilyIndex = device.m_TransferQueueFamily, + .dstQueueFamilyIndex = device.m_PrimaryQueueFamily, .image = crate->GetImage(), .subresourceRange = { @@ -326,11 +326,8 @@ main(int, char **) struct PCB { systems::ResId m_VertexBuffer; - u32 m_Pad0 = 0; systems::ResId m_Camera; - u32 m_Pad1 = 0; systems::ResId m_Texture; - u32 m_Pad2 = 0; }; static_assert(sizeof(PCB) == 24); diff --git a/samples/02_box/shader/triangle.slang b/samples/02_box/shader/triangle.slang index cbad9e8..1f45865 100644 --- a/samples/02_box/shader/triangle.slang +++ b/samples/02_box/shader/triangle.slang @@ -20,7 +20,6 @@ struct PCB { DescriptorHandle texture; }; -//uniform CameraData camera; [vk::push_constant] uniform PCB pcb;