Shader reflection added.

This commit is contained in:
Anish Bhobe 2025-05-10 18:00:25 +02:00
parent 3a7a2b4ab7
commit 41c91058b6
10 changed files with 578 additions and 157 deletions

View File

@ -52,8 +52,9 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
#define Take(ELEMENT) eastl::exchange(ELEMENT, {}) #define Take(ELEMENT) eastl::exchange(ELEMENT, {})
#define TODO(MSG) assert(false && ("Unimplemented: " MSG)) #define TODO(...) assert(!("Unimplemented: " __VA_ARGS__))
#define FIX(MSG) static_assert(false && ("Unimplemented: " MSG)) #define FIX(...) static_assert(!("Unimplemented: " __VA_ARGS__))
#define UNREACHABLE(...) assert(!("Unreachable: " __VA_ARGS__))
#define AbortIfFailed(RESULT) \ #define AbortIfFailed(RESULT) \
do \ do \
@ -67,7 +68,8 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
do \ do \
{ \ { \
vk::Result _checkResultValue_; \ vk::Result _checkResultValue_; \
ERROR_IF(Failed(_checkResultValue_ = static_cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \ ERROR_IF(Failed(_checkResultValue_ = static_cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, \
_checkResultValue_) \
THEN_ABORT(_checkResultValue_); \ THEN_ABORT(_checkResultValue_); \
} while (false) } while (false)
@ -221,15 +223,20 @@ GetMaskOffset(u32 val)
return count; return count;
} }
template <> template <typename T>
struct fmt::formatter<vk::Result> : nested_formatter<std::string> concept VkToString = requires(T a) {
{ vk::to_string(a) } -> std::convertible_to<std::string>;
};
template <VkToString T>
struct fmt::formatter<T> : nested_formatter<std::string>
{ {
auto auto
// ReSharper disable once CppInconsistentNaming // ReSharper disable once CppInconsistentNaming
format(vk::Result result, format_context &ctx) const format(T result, format_context &ctx) const
{ {
return write_padded(ctx, 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))); });
} }
}; };

View File

@ -8,6 +8,8 @@
#include "global.h" #include "global.h"
#include "surface.h" #include "surface.h"
#include <sstream>
#include <EASTL/fixed_vector.h> #include <EASTL/fixed_vector.h>
struct Window; struct Window;
@ -23,6 +25,31 @@ enum class QueueSupportFlagBits
using QueueSupportFlags = vk::Flags<QueueSupportFlagBits>; using QueueSupportFlags = vk::Flags<QueueSupportFlagBits>;
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 struct QueueFamilyInfo
{ {
u32 m_Index; u32 m_Index;
@ -30,6 +57,12 @@ struct QueueFamilyInfo
QueueSupportFlags m_Support; 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 [[nodiscard]] vk::SurfaceCapabilitiesKHR
GetSurfaceCapabilities(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface); GetSurfaceCapabilities(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface);

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include "pipeline_helpers.h"
#include "resource.h" #include "resource.h"
#include "aster/aster.h" #include "aster/aster.h"
@ -231,59 +232,6 @@ struct SamplerCreateInfo
#pragma region Pipeline #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 struct AttributeInfo
{ {
u32 m_Location; u32 m_Location;
@ -402,7 +350,7 @@ struct Frame;
class Context class Context
{ {
protected: protected:
vk::CommandBuffer m_Cmd; vk::CommandBuffer m_Cmd;
friend Device; friend Device;
@ -426,13 +374,14 @@ class GraphicsContext : public Context
friend Device; friend Device;
friend Frame; friend Frame;
const Pipeline *m_PipelineInUse;
explicit GraphicsContext(const vk::CommandBuffer cmd) explicit GraphicsContext(const vk::CommandBuffer cmd)
: Context{cmd} : Context{cmd}
, m_PipelineInUse{nullptr}
{ {
} }
const Pipeline *m_PipelineInUse;
public: public:
DEPRECATE_RAW_CALLS void SetViewport(const vk::Viewport &viewport); DEPRECATE_RAW_CALLS void SetViewport(const vk::Viewport &viewport);
void BindVertexBuffer(const Ref<VertexBuffer> &vertexBuffer); void BindVertexBuffer(const Ref<VertexBuffer> &vertexBuffer);
@ -440,10 +389,14 @@ class GraphicsContext : public Context
void void
PushConstantBlock(auto &block) 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);
} }
void Draw(u32 vertexCount); m_Cmd.pushConstants(m_PipelineInUse->m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof block, &block);
void DrawIndexed(u32 indexCount); }
void Draw(usize vertexCount);
void DrawIndexed(usize indexCount);
DEPRECATE_RAW_CALLS void BeginRendering(const vk::RenderingInfo &renderingInfo); DEPRECATE_RAW_CALLS void BeginRendering(const vk::RenderingInfo &renderingInfo);
void EndRendering(); void EndRendering();
@ -480,6 +433,8 @@ class TransferContext : public Context
TransferContext &operator=(TransferContext &&other) noexcept; TransferContext &operator=(TransferContext &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(TransferContext); DISALLOW_COPY_AND_ASSIGN(TransferContext);
~TransferContext() = default;
}; };
struct Frame struct Frame
@ -514,6 +469,8 @@ struct Frame
Frame &operator=(Frame &&other) noexcept; Frame &operator=(Frame &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(Frame); DISALLOW_COPY_AND_ASSIGN(Frame);
~Frame() = default;
}; };
class Device final class Device final

View File

@ -0,0 +1,74 @@
// =============================================
// Aster: pipeline_helpers.h
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#pragma once
#include <aster/aster.h>
#include <EASTL/vector.h>
#include <slang.h>
#include <variant>
namespace systems
{
class Device;
struct PipelineCreationError
{
std::variant<std::monostate, vk::Result, SlangResult> 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<vk::DescriptorSetLayout> m_DescriptorSetLayouts;
eastl::vector<vk::PushConstantRange> 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<vk::DescriptorSetLayoutBinding> 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

View File

@ -69,13 +69,15 @@ CastView(const Ref<View<TFrom>> &from)
template <typename T> template <typename T>
class ResId class ResId
{ {
using IdType = u32;
public: public:
constexpr static u32 INVALID = MaxValue<u32>; constexpr static IdType INVALID = MaxValue<IdType>;
private: 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} : m_Index{index}
{ {
AddRef(); AddRef();

View File

@ -6,4 +6,5 @@ target_sources(aster_core
PRIVATE PRIVATE
"device.cpp" "device.cpp"
"commit_manager.cpp" "commit_manager.cpp"
"pipeline_helpers.cpp"
"sync_server.cpp") "sync_server.cpp")

View File

@ -562,7 +562,10 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre
} }
vk::PipelineLayout pipelineLayout; vk::PipelineLayout pipelineLayout;
auto result = CreatePipelineLayout(pipelineLayout, program); if (auto result = CreatePipelineLayout(pipelineLayout, program))
{
return result;
}
eastl::fixed_vector<vk::VertexInputBindingDescription, 4, false> inputBindingDescriptions; eastl::fixed_vector<vk::VertexInputBindingDescription, 4, false> inputBindingDescriptions;
eastl::fixed_vector<vk::VertexInputAttributeDescription, 4, false> inputAttributeDescriptions; eastl::fixed_vector<vk::VertexInputAttributeDescription, 4, false> inputAttributeDescriptions;
@ -699,47 +702,6 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre
ERROR("Unimplemented Stage " #STAGE); \ ERROR("Unimplemented Stage " #STAGE); \
return SLANG_FAIL 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::PipelineCreationError
systems::Device::CreateShaders( systems::Device::CreateShaders(
eastl::fixed_vector<vk::PipelineShaderStageCreateInfo, ShaderTypeCount, false> &shadersOut, eastl::fixed_vector<vk::PipelineShaderStageCreateInfo, ShaderTypeCount, false> &shadersOut,
@ -905,6 +867,39 @@ systems::Device::CreateShaders(
return {}; return {};
} }
struct PipelineLayoutBuilder
{
systems::Device *m_Device;
eastl::vector<vk::DescriptorSetLayout> m_DescriptorSetLayouts;
eastl::vector<vk::PushConstantRange> m_PushConstantRanges;
explicit PipelineLayoutBuilder(systems::Device *device)
: m_Device{device}
{
}
systems::PipelineCreationError
Build(vk::PipelineLayout &pipelineLayout, eastl::vector<vk::DescriptorSetLayout> &descriptorSetLayouts);
};
systems::PipelineCreationError
PipelineLayoutBuilder::Build(vk::PipelineLayout &pipelineLayout,
eastl::vector<vk::DescriptorSetLayout> &descriptorSetLayouts)
{
const vk::PipelineLayoutCreateInfo layoutCreateInfo = {
.setLayoutCount = static_cast<u32>(m_DescriptorSetLayouts.size()),
.pSetLayouts = m_DescriptorSetLayouts.data(),
.pushConstantRangeCount = static_cast<u32>(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::PipelineCreationError
systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout,
const Slang::ComPtr<slang::IComponentType> &program) const Slang::ComPtr<slang::IComponentType> &program)
@ -919,39 +914,20 @@ systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout,
return SLANG_FAIL; return SLANG_FAIL;
} }
// TODO: Reflect to create the push constants and descriptor sets. vk::DescriptorSetLayout setLayout = {};
// TODO: Hackery. To FIX.
vk::PushConstantRange pcr{
.stageFlags = vk::ShaderStageFlagBits::eAllGraphics,
.offset = 0,
.size = 24,
};
u32 setLayoutCount = 0;
const vk::DescriptorSetLayout *setLayout = nullptr;
if (m_CommitManager) if (m_CommitManager)
{ setLayout = m_CommitManager->GetDescriptorSetLayout();
setLayoutCount = 1;
setLayout = &m_CommitManager->GetDescriptorSetLayout();
}
// TODO: END EXPERIMENT _internal::PipelineLayoutBuilder pipelineLayoutBuilder{
this,
const vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { setLayout,
.setLayoutCount = setLayoutCount,
.pSetLayouts = setLayout,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pcr,
}; };
vk::Result result = m_Device->createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout); _internal::DescriptorLayoutBuilder descriptorLayoutBuilder{&pipelineLayoutBuilder};
if (Failed(result)) descriptorLayoutBuilder.AddGlobalScopeParameters(layout);
{ descriptorLayoutBuilder.AddEntryPointParameters(layout);
ERROR("Could not create a pipeline layout. Cause: {}", result); descriptorLayoutBuilder.Build();
return result;
}
pipelineLayout = pipelineLayoutBuilder.Build();
return {}; 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; return std::nullopt;
} }
@ -1002,7 +978,8 @@ FindAsyncComputeQueue(const PhysicalDevice &physicalDevice, u32 primaryQueueFami
if (queueFamilyInfo.m_Index == primaryQueueFamilyIndex) if (queueFamilyInfo.m_Index == primaryQueueFamilyIndex)
continue; continue;
if (queueFamilyInfo.m_Support == QueueSupportFlagBits::eCompute) if (queueFamilyInfo.m_Support ==
(QueueSupportFlags{QueueSupportFlagBits::eCompute} | QueueSupportFlagBits::eTransfer))
{ {
return QueueAllocation{ return QueueAllocation{
.m_Family = queueFamilyInfo.m_Index, .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; return std::nullopt;
} }
@ -1471,15 +1448,15 @@ systems::GraphicsContext::BindPipeline(const Pipeline &pipeline)
} }
void void
systems::GraphicsContext::Draw(u32 vertexCount) systems::GraphicsContext::Draw(const usize vertexCount)
{ {
m_Cmd.draw(vertexCount, 1, 0, 0); m_Cmd.draw(static_cast<u32>(vertexCount), 1, 0, 0);
} }
void void
systems::GraphicsContext::DrawIndexed(u32 indexCount) systems::GraphicsContext::DrawIndexed(usize indexCount)
{ {
m_Cmd.drawIndexed(indexCount, 1, 0, 0, 0); m_Cmd.drawIndexed(static_cast<u32>(indexCount), 1, 0, 0, 0);
} }
void void

View File

@ -0,0 +1,374 @@
// =============================================
// Aster: pipeline_helpers.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/device.h"
#include <aster/systems/pipeline_helpers.h>
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<i32>(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<std::monostate>(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<vk::DescriptorSetLayout> filteredDescriptorSetLayouts;
filteredDescriptorSetLayouts.reserve(m_DescriptorSetLayouts.size());
for (auto dsl : m_DescriptorSetLayouts)
{
if (dsl)
{
filteredDescriptorSetLayouts.push_back(dsl);
}
}
const vk::PipelineLayoutCreateInfo createInfo = {
.setLayoutCount = static_cast<u32>(filteredDescriptorSetLayouts.size()),
.pSetLayouts = filteredDescriptorSetLayouts.data(),
.pushConstantRangeCount = static_cast<u32>(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<u32>(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<u32>(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<u32>(layout->getDescriptorSetDescriptorRangeDescriptorCount(relativeSetIndex, rangeIndex));
const u32 bindingIndex = static_cast<u32>(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<u32>(m_LayoutBindings.size()),
.pBindings = m_LayoutBindings.data(),
});
m_PipelineLayoutBuilder->m_DescriptorSetLayouts[m_SetIndex] = dsl;
}
void
DescriptorLayoutBuilder::AddAutomaticallyIntroducedUniformBuffer()
{
const auto vulkanBindingIndex = static_cast<u32>(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);
}
}

View File

@ -86,7 +86,7 @@ struct Camera
int int
main(int, char **) main(int, char **)
{ {
MIN_LOG_LEVEL(Logger::LogType::eInfo); MIN_LOG_LEVEL(Logger::LogType::eDebug);
Window window = {"Box (Aster)", {640, 480}}; Window window = {"Box (Aster)", {640, 480}};
@ -125,7 +125,7 @@ main(int, char **)
{.m_ShaderFile = SHADER_FILE, .m_EntryPoints = {"vsmain", "fsmain"}}, {.m_ShaderFile = SHADER_FILE, .m_EntryPoints = {"vsmain", "fsmain"}},
}}); }});
ERROR_IF(pipelineResult, "Could not create pipeline. Cause: {}", pipelineResult.What()) ERROR_IF(pipelineResult, "Could not create pipeline. Cause: {}", pipelineResult.What())
THEN_ABORT(pipelineResult.uNone); THEN_ABORT(pipelineResult.Value());
auto swapchainSize = device.GetSwapchainSize(); auto swapchainSize = device.GetSwapchainSize();
Camera camera = { Camera camera = {
@ -229,8 +229,8 @@ main(int, char **)
.dstAccessMask = vk::AccessFlagBits2::eShaderRead, .dstAccessMask = vk::AccessFlagBits2::eShaderRead,
.oldLayout = vk::ImageLayout::eTransferDstOptimal, .oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.srcQueueFamilyIndex = vk::QueueFamilyIgnored, .srcQueueFamilyIndex = device.m_TransferQueueFamily,
.dstQueueFamilyIndex = vk::QueueFamilyIgnored, .dstQueueFamilyIndex = device.m_PrimaryQueueFamily,
.image = crate->GetImage(), .image = crate->GetImage(),
.subresourceRange = .subresourceRange =
{ {
@ -326,11 +326,8 @@ main(int, char **)
struct PCB struct PCB
{ {
systems::ResId<Buffer> m_VertexBuffer; systems::ResId<Buffer> m_VertexBuffer;
u32 m_Pad0 = 0;
systems::ResId<Buffer> m_Camera; systems::ResId<Buffer> m_Camera;
u32 m_Pad1 = 0;
systems::ResId<TextureView> m_Texture; systems::ResId<TextureView> m_Texture;
u32 m_Pad2 = 0;
}; };
static_assert(sizeof(PCB) == 24); static_assert(sizeof(PCB) == 24);

View File

@ -20,7 +20,6 @@ struct PCB {
DescriptorHandle<Sampler2D> texture; DescriptorHandle<Sampler2D> texture;
}; };
//uniform CameraData camera;
[vk::push_constant] [vk::push_constant]
uniform PCB pcb; uniform PCB pcb;