diff --git a/add_shader.cmake b/add_shader.cmake index 50b61e0..df17bc7 100644 --- a/add_shader.cmake +++ b/add_shader.cmake @@ -12,6 +12,7 @@ function(add_shader TARGET SHADER) set(current-shader-path ${CMAKE_CURRENT_SOURCE_DIR}/${SHADER}) set(current-output-path ${CMAKE_CURRENT_BINARY_DIR}/${SHADER}.spv) + set(current-copy-path ${CMAKE_CURRENT_BINARY_DIR}/${SHADER}) get_filename_component(current-output-dir ${current-output-path} DIRECTORY) file(MAKE_DIRECTORY ${current-output-dir}) @@ -39,12 +40,19 @@ function(add_shader TARGET SHADER) VERBATIM) elseif (${shader-ext} STREQUAL ".slang") message("Marked as slang file. ${current-output-path}") +# add_custom_command( +# OUTPUT ${current-output-path} +# COMMAND ${slangc_exe} -target spirv -o ${current-output-path} ${current-shader-path} +# DEPENDS ${current-shader-path} +# IMPLICIT_DEPENDS CXX ${current-shader-path} +# VERBATIM) add_custom_command( - OUTPUT ${current-output-path} - COMMAND ${slangc_exe} -target spirv -o ${current-output-path} ${current-shader-path} + TARGET ${TARGET} POST_BUILD +# OUTPUT ${current-copy-path} + COMMAND ${CMAKE_COMMAND} -E copy ${current-shader-path} ${CMAKE_CURRENT_BINARY_DIR}/${SHADER} DEPENDS ${current-shader-path} IMPLICIT_DEPENDS CXX ${current-shader-path} - VERBATIM) + COMMENT "Copying ${SHADER}") endif () # Make sure our build depends on this output. diff --git a/aster/include/aster/core/constants.h b/aster/include/aster/core/constants.h index e21ccc8..b67d160 100644 --- a/aster/include/aster/core/constants.h +++ b/aster/include/aster/core/constants.h @@ -30,6 +30,7 @@ using f128 = long double; using b8 = bool; using b32 = u32; using usize = size_t; +using isize = intptr_t; using uptr = uintptr_t; using cstr = const char *; diff --git a/aster/include/aster/core/physical_device.h b/aster/include/aster/core/physical_device.h index 289e1f5..4af909d 100644 --- a/aster/include/aster/core/physical_device.h +++ b/aster/include/aster/core/physical_device.h @@ -81,6 +81,7 @@ struct PhysicalDevice final eastl::vector m_PresentModes; eastl::vector m_QueueFamilies; + PhysicalDevice() = default; PhysicalDevice(vk::SurfaceKHR surface, vk::PhysicalDevice physicalDevice); }; diff --git a/aster/include/aster/core/pipeline.h b/aster/include/aster/core/pipeline.h index bee1f23..1c74596 100644 --- a/aster/include/aster/core/pipeline.h +++ b/aster/include/aster/core/pipeline.h @@ -13,23 +13,31 @@ struct Device; struct Pipeline { - const Device *m_Device; + enum class Kind + { + eGraphics, + eCompute, + }; + + const Device *m_Device = nullptr; vk::PipelineLayout m_Layout; - vk::Pipeline m_Pipeline; + vk::Pipeline m_Pipeline = nullptr; eastl::vector m_SetLayouts; + Kind m_Kind; Pipeline() = default; Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline, - eastl::vector &&setLayouts); + eastl::vector &&setLayouts, Kind kind); ~Pipeline(); DISALLOW_COPY_AND_ASSIGN(Pipeline); Pipeline(Pipeline &&other) noexcept - : m_Device{other.m_Device}, - m_Layout{Take(other.m_Layout)}, - m_Pipeline{Take(other.m_Pipeline)}, - m_SetLayouts{std::move(other.m_SetLayouts)} + : m_Device{other.m_Device} + , m_Layout{Take(other.m_Layout)} + , m_Pipeline{Take(other.m_Pipeline)} + , m_SetLayouts{std::move(other.m_SetLayouts)} + , m_Kind{other.m_Kind} { } @@ -38,10 +46,12 @@ struct Pipeline { if (this == &other) return *this; - m_Device = other.m_Device; - m_Layout = Take(other.m_Layout); - m_Pipeline = Take(other.m_Pipeline); - m_SetLayouts = std::move(other.m_SetLayouts); + using eastl::swap; + swap(m_Device, other.m_Device); + swap(m_Layout, other.m_Layout); + swap(m_Pipeline, other.m_Pipeline); + swap(m_SetLayouts, other.m_SetLayouts); + swap(m_Kind, other.m_Kind); return *this; } -}; \ No newline at end of file +}; diff --git a/aster/include/aster/systems/context.h b/aster/include/aster/systems/context.h index a325929..ad2f6cc 100644 --- a/aster/include/aster/systems/context.h +++ b/aster/include/aster/systems/context.h @@ -5,6 +5,7 @@ #pragma once +#include "EASTL/span.h" #include "context.h" #include @@ -30,10 +31,10 @@ struct Frame; namespace _internal { +class ComputeContextPool; class GraphicsContextPool; class TransferContextPool; class ContextPool; -class OrderlessTransferContextPool; } // namespace _internal #define DEPRECATE_RAW_CALLS @@ -46,7 +47,6 @@ class Context friend Device; friend _internal::ContextPool; - friend _internal::OrderlessTransferContextPool; explicit Context(_internal::ContextPool &pool, const vk::CommandBuffer cmd) : m_Pool{&pool} @@ -66,8 +66,24 @@ class Context void Begin(); void End(); + + void BeginDebugRegion(cstr name, vec4 color = {}); + void EndDebugRegion(); }; +// Inline the no-op if not debug. +#if defined(ASTER_NDEBUG) +inline void +Context::BeginDebugRegion(cstr name, vec4 color) +{ +} + +inline void +Context::EndDebugRegion() +{ +} +#endif + class TransferContext : public Context { protected: @@ -79,14 +95,19 @@ class TransferContext : public Context { } - public: - struct ImageData - { - void *m_Data; - usize m_NumBytes; - }; + void UploadBuffer(const Ref &buffer, usize size, const void *data); - void UploadTexture(const Ref &image, const ImageData &data); + public: + void UploadTexture(const Ref &image, const eastl::span &data); + + void + UploadBuffer(const Ref &buffer, const std::ranges::range auto &data) + { + const auto span = eastl::span{data.begin(), data.end()}; + UploadBuffer(buffer, span.size_bytes(), span.data()); + } + + DEPRECATE_RAW_CALLS void Blit(const vk::BlitImageInfo2 &mipBlitInfo); TransferContext(TransferContext &&other) noexcept; TransferContext &operator=(TransferContext &&other) noexcept; @@ -96,38 +117,79 @@ class TransferContext : public Context DISALLOW_COPY_AND_ASSIGN(TransferContext); }; -class GraphicsContext : public Context +class ComputeContext : public TransferContext +{ + protected: + friend Device; + friend _internal::ComputeContextPool; + + const Pipeline *m_PipelineInUse; + + explicit ComputeContext(_internal::ContextPool &pool, const vk::CommandBuffer cmd) + : TransferContext{pool, cmd} + , m_PipelineInUse{nullptr} + { + } + + void PushConstantBlock(usize offset, usize size, const void *data); + + void Dispatch(const Pipeline &pipeline, u32 x, u32 y, u32 z, usize size, void *data); + + public: + void BindPipeline(const Pipeline &pipeline); + void + PushConstantBlock(const auto &block) + { + if constexpr (sizeof block > 128) + WARN("Vulkan only guarantees 128 bytes of Push Constants. Size of PCB is {}", sizeof block); + PushConstantBlock(0, sizeof block, &block); + } + + void + PushConstantBlock(const usize offset, const auto &block) + { + if (offset + sizeof block > 128) + WARN("Vulkan only guarantees 128 bytes of Push Constants. Size of PCB is {}, at offset {}", sizeof block, + offset); + PushConstantBlock(offset, sizeof block, &block); + } + + void + Dispatch(const Pipeline &pipeline, const u32 x, const u32 y, const u32 z, auto &pushConstantBlock) + { + if constexpr (sizeof pushConstantBlock > 128) + WARN("Vulkan only guarantees 128 bytes of Push Constants. Size of PCB is {}", sizeof pushConstantBlock); + Dispatch(pipeline, x, y, z, sizeof pushConstantBlock, &pushConstantBlock); + } +}; + +class GraphicsContext : public ComputeContext { protected: friend Device; friend _internal::GraphicsContextPool; - const Pipeline *m_PipelineInUse; - explicit GraphicsContext(_internal::ContextPool &pool, const vk::CommandBuffer cmd) - : Context{pool, cmd} - , m_PipelineInUse{nullptr} + : ComputeContext{pool, cmd} { } public: DEPRECATE_RAW_CALLS void SetViewport(const vk::Viewport &viewport); void BindVertexBuffer(const Ref &vertexBuffer); - void BindPipeline(const Pipeline &pipeline); - void - PushConstantBlock(auto &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 BindIndexBuffer(const Ref &indexBuffer); void Draw(usize vertexCount); void DrawIndexed(usize indexCount); + void DrawIndexed(usize indexCount, usize firstIndex, usize firstVertex); DEPRECATE_RAW_CALLS void BeginRendering(const vk::RenderingInfo &renderingInfo); void EndRendering(); + + DEPRECATE_RAW_CALLS vk::CommandBuffer + GetCommandBuffer() const + { + return m_Cmd; + } }; namespace _internal @@ -215,14 +277,33 @@ class TransferContextPool : public ContextPool DISALLOW_COPY_AND_ASSIGN(TransferContextPool); }; -class GraphicsContextPool : public TransferContextPool +class ComputeContextPool : public TransferContextPool +{ + public: + ComputeContext CreateComputeContext(); + + ComputeContextPool() = default; + ComputeContextPool(Device &device, const u32 queueFamilyIndex, const ManagedBy managedBy) + : TransferContextPool{device, queueFamilyIndex, managedBy} + { + } + + ComputeContextPool(ComputeContextPool &&other) noexcept = default; + ComputeContextPool &operator=(ComputeContextPool &&other) noexcept = default; + + ~ComputeContextPool() = default; + + DISALLOW_COPY_AND_ASSIGN(ComputeContextPool); +}; + +class GraphicsContextPool : public ComputeContextPool { public: GraphicsContext CreateGraphicsContext(); GraphicsContextPool() = default; GraphicsContextPool(Device &device, const u32 queueFamilyIndex, const ManagedBy managedBy) - : TransferContextPool{device, queueFamilyIndex, managedBy} + : ComputeContextPool{device, queueFamilyIndex, managedBy} { } @@ -234,35 +315,38 @@ class GraphicsContextPool : public TransferContextPool DISALLOW_COPY_AND_ASSIGN(GraphicsContextPool); }; -class OrderlessTransferContextPool +template TContextPool> +class OrderlessContextPool { - struct TransferContextEntry : eastl::intrusive_list_node + using ContextPoolType = TContextPool; + + struct ContextListEntry : eastl::intrusive_list_node { - TransferContextPool m_Pool; + ContextPoolType m_Pool; bool - Contains(const _internal::ContextPool &other) const + Contains(const ContextPool &other) const { return m_Pool == other; } }; - using TransferContextList = eastl::intrusive_list; + using ContextListType = eastl::intrusive_list; Device *m_Device; - memory::memory_pool<> m_TransferContextMemoryPool; - TransferContextList m_FreeTransferContexts; - TransferContextList m_TransferContexts; + memory::memory_pool<> m_ContextPoolEntryMemory; + ContextListType m_FreeContextPools; + ContextListType m_UsedContextPools; u32 m_QueueFamilyIndex; - constexpr static usize ENTRY_SIZE = sizeof(TransferContextEntry); + constexpr static usize ENTRY_SIZE = sizeof(ContextListEntry); constexpr static usize ENTRIES_PER_BLOCK = 5; constexpr static usize BLOCK_SIZE = ENTRIES_PER_BLOCK * ENTRY_SIZE; public: - OrderlessTransferContextPool() + OrderlessContextPool() : m_Device{nullptr} - , m_TransferContextMemoryPool{ENTRY_SIZE, BLOCK_SIZE} + , m_ContextPoolEntryMemory{ENTRY_SIZE, BLOCK_SIZE} , m_QueueFamilyIndex{0} { } @@ -276,81 +360,109 @@ class OrderlessTransferContextPool TransferContext CreateTransferContext() + requires std::derived_from { - if (!m_FreeTransferContexts.empty()) + if (!m_FreeContextPools.empty()) { - TransferContextEntry &entry = m_FreeTransferContexts.back(); - m_FreeTransferContexts.pop_back(); - m_TransferContexts.push_back(entry); + ContextListEntry &entry = m_FreeContextPools.back(); + m_FreeContextPools.pop_back(); + m_UsedContextPools.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}; + ContextListEntry &entry = *static_cast(m_ContextPoolEntryMemory.allocate_node()); + auto pool = ContextPoolType{*m_Device, m_QueueFamilyIndex, ContextPool::ManagedBy::eDevice}; pool.m_ResetCallback = [this](ContextPool &resetPool) { this->ReleasePool(resetPool); }; - new (&entry) TransferContextEntry{ + new (&entry) ContextListEntry{ .m_Pool = eastl::move(pool), }; - m_TransferContexts.push_back(entry); + m_UsedContextPools.push_back(entry); return entry.m_Pool.CreateTransferContext(); } + ComputeContext + CreateComputeContext() + requires std::derived_from + { + if (!m_FreeContextPools.empty()) + { + ContextListEntry &entry = m_FreeContextPools.back(); + m_FreeContextPools.pop_back(); + m_UsedContextPools.push_back(entry); + + return entry.m_Pool.CreateComputeContext(); + } + + ContextListEntry &entry = *static_cast(m_ContextPoolEntryMemory.allocate_node()); + auto pool = ContextPoolType{*m_Device, m_QueueFamilyIndex, ContextPool::ManagedBy::eDevice}; + pool.m_ResetCallback = [this](ContextPool &resetPool) { this->ReleasePool(resetPool); }; + new (&entry) ContextListEntry{ + .m_Pool = eastl::move(pool), + }; + m_UsedContextPools.push_back(entry); + + return entry.m_Pool.CreateComputeContext(); + } + 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 const found = eastl::find_if(m_UsedContextPools.begin(), m_UsedContextPools.end(), + [&pool](const ContextListEntry &v) { return v.Contains(pool); }); auto &v = *found; - TransferContextList::remove(v); + ContextListType::remove(v); pool.Reset(); - m_FreeTransferContexts.push_back(v); + m_FreeContextPools.push_back(v); } - OrderlessTransferContextPool(OrderlessTransferContextPool &&other) noexcept + OrderlessContextPool(OrderlessContextPool &&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_ContextPoolEntryMemory{std::move(other.m_ContextPoolEntryMemory)} + , m_FreeContextPools{other.m_FreeContextPools} + , m_UsedContextPools{other.m_UsedContextPools} , m_QueueFamilyIndex{other.m_QueueFamilyIndex} { - other.m_FreeTransferContexts.clear(); - other.m_TransferContexts.clear(); + other.m_FreeContextPools.clear(); + other.m_UsedContextPools.clear(); } - OrderlessTransferContextPool & - operator=(OrderlessTransferContextPool &&other) noexcept + OrderlessContextPool & + operator=(OrderlessContextPool &&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_ContextPoolEntryMemory = std::move(other.m_ContextPoolEntryMemory); + m_FreeContextPools = other.m_FreeContextPools; + other.m_FreeContextPools.clear(); + m_UsedContextPools = other.m_UsedContextPools; + other.m_UsedContextPools.clear(); m_QueueFamilyIndex = other.m_QueueFamilyIndex; return *this; } - ~OrderlessTransferContextPool() + ~OrderlessContextPool() { - for (auto &entry : m_FreeTransferContexts) + for (auto &entry : m_FreeContextPools) { - entry.m_Pool.~TransferContextPool(); + entry.m_Pool.~ContextPoolType(); } - for (auto &entry : m_TransferContexts) + for (auto &entry : m_UsedContextPools) { - entry.m_Pool.~TransferContextPool(); + entry.m_Pool.~ContextPoolType(); } // The allocations will 'wink' away. } - DISALLOW_COPY_AND_ASSIGN(OrderlessTransferContextPool); + DISALLOW_COPY_AND_ASSIGN(OrderlessContextPool); }; +using OrderlessTransferContextPool = OrderlessContextPool; +using OrderlessComputeContextPool = OrderlessContextPool; + } // 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 d3280a9..111147b 100644 --- a/aster/include/aster/systems/device.h +++ b/aster/include/aster/systems/device.h @@ -294,8 +294,42 @@ struct ShaderInfo struct GraphicsPipelineCreateInfo { + enum class DepthTest + { + eEnabled, + eReadOnly, + eDisabled, + }; + + enum class CompareOp + { + eNever = 0x0, + eLessThan = 0x1, + eEqualTo = 0x2, + eGreaterThan = 0x4, + eLessThanOrEqualTo = eLessThan | eEqualTo, + eGreaterThanOrEqualTo = eGreaterThan | eEqualTo, + eNotEqualTo = eLessThan | eGreaterThan, + eAlways = eLessThan | eEqualTo | eGreaterThan, + }; + eastl::fixed_vector m_VertexInputs; eastl::fixed_vector m_Shaders; + + DepthTest m_DepthTest = DepthTest::eEnabled; + CompareOp m_DepthOp = CompareOp::eLessThan; + + cstr m_Name; + +private: + friend Device; + [[nodiscard]] vk::PipelineDepthStencilStateCreateInfo GetDepthStencilStateCreateInfo() const; +}; + +struct ComputePipelineCreateInfo +{ + ShaderInfo m_Shader; + cstr m_Name; }; #pragma endregion @@ -402,6 +436,7 @@ class Device final u32 m_ComputeQueueFamily; _internal::OrderlessTransferContextPool m_TransferContextPool; + _internal::OrderlessComputeContextPool m_ComputeContextPool; std::array m_Frames; u32 m_CurrentFrameIdx = 0; @@ -416,6 +451,7 @@ class Device final // ---------------------------------------------------------------------------------------------------- [[nodiscard]] Ref CreateStorageBuffer(usize size, cstr name = nullptr); + [[nodiscard]] Ref CreateIndexBuffer(usize size, cstr name = nullptr); [[nodiscard]] Ref CreateUniformBuffer(usize size, cstr name = nullptr); [[nodiscard]] Ref CreateStagingBuffer(usize size, cstr name = nullptr); [[nodiscard]] Ref CreateVertexBuffer(usize size, cstr name = nullptr); @@ -464,7 +500,7 @@ class Device final [[nodiscard]] Ref CreateTexture2DWithView(const Texture2DCreateInfo &createInfo) { - auto handle = CreateTexture2D(createInfo); + auto handle = CreateTexture2DWithView(createInfo); return CastView(handle); } @@ -472,7 +508,7 @@ class Device final [[nodiscard]] Ref CreateTextureCubeWithView(const TextureCubeCreateInfo &createInfo) { - auto handle = CreateTextureCube(createInfo); + auto handle = CreateTextureCubeWithView(createInfo); return CastView(handle); } @@ -503,12 +539,16 @@ class Device final PipelineCreationError CreateShaders(eastl::fixed_vector &shadersOut, Slang::ComPtr &program, const std::span &shaders); + systems::PipelineCreationError + CreateShader(vk::PipelineShaderStageCreateInfo &shadersOut, Slang::ComPtr &program, + const ShaderInfo &shaders); PipelineCreationError CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, const Slang::ComPtr &program); public: // Pipelines, unlike the other resources, are not ref-counted. PipelineCreationError CreatePipeline(Pipeline &pipeline, const GraphicsPipelineCreateInfo &createInfo); + PipelineCreationError CreateComputePipeline(Pipeline &pipeline, const ComputePipelineCreateInfo &createInfo); // // Frames @@ -522,6 +562,12 @@ class Device final return {m_Swapchain.m_Extent.width, m_Swapchain.m_Extent.height}; } + void + RegisterResizeCallback(Swapchain::FnResizeCallback &&callback) + { + m_Swapchain.RegisterResizeCallback(eastl::forward(callback)); + } + void Present(Frame &frame, GraphicsContext &graphicsContext); // @@ -532,6 +578,7 @@ class Device final friend TransferContext; TransferContext CreateTransferContext(); + ComputeContext CreateComputeContext(); Receipt Submit(Context &context); // diff --git a/aster/include/aster/systems/resource.h b/aster/include/aster/systems/resource.h index 814b3d8..be158ae 100644 --- a/aster/include/aster/systems/resource.h +++ b/aster/include/aster/systems/resource.h @@ -27,7 +27,7 @@ CastBuffer(const Ref &from) { if constexpr (not concepts::BufferInto) assert(TTo::FLAGS & from->m_Flags); - return std::reinterpret_pointer_cast(from); + return eastl::reinterpret_pointer_cast(from); } #pragma endregion @@ -75,7 +75,7 @@ class ResId private: IdType m_Index; - u32 m_Padding; //< Slang DescriptorHandle are a pair of u32. TODO: Use as validation. + u32 m_Padding = 0; //< Slang DescriptorHandle are a pair of u32. TODO: Use as validation. explicit ResId(const IdType index) : m_Index{index} diff --git a/aster/src/aster/core/pipeline.cpp b/aster/src/aster/core/pipeline.cpp index 276f8bc..ab39e84 100644 --- a/aster/src/aster/core/pipeline.cpp +++ b/aster/src/aster/core/pipeline.cpp @@ -7,17 +7,21 @@ #include "core/device.h" -Pipeline::Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline, - eastl::vector &&setLayouts) - : m_Device(device) - , m_Layout(layout) - , m_Pipeline(pipeline) - , m_SetLayouts(std::move(setLayouts)) +Pipeline::Pipeline(const Device *device, const vk::PipelineLayout layout, const vk::Pipeline pipeline, + eastl::vector &&setLayouts, const Kind kind) + : m_Device{device} + , m_Layout{layout} + , m_Pipeline{pipeline} + , m_SetLayouts{std::move(setLayouts)} + , m_Kind{kind} { } Pipeline::~Pipeline() { + if (!m_Device || !m_Pipeline) + return; + for (const auto setLayout : m_SetLayouts) { m_Device->m_Device.destroy(setLayout, nullptr); diff --git a/aster/src/aster/systems/context.cpp b/aster/src/aster/systems/context.cpp index 9dd8a6f..b4c9a09 100644 --- a/aster/src/aster/systems/context.cpp +++ b/aster/src/aster/systems/context.cpp @@ -166,6 +166,25 @@ systems::Context::Begin() ERROR_IF(Failed(result), "Could not begin context") THEN_ABORT(result); } +// Release versions inline 'no-op'. +#if !defined(ASTER_NDEBUG) +void +systems::Context::BeginDebugRegion(const cstr name, const vec4 color) +{ + const vk::DebugUtilsLabelEXT label = { + .pLabelName = name, + .color = std::array{color.r, color.g, color.b, color.a}, + }; + m_Cmd.beginDebugUtilsLabelEXT(&label); +} + +void +systems::Context::EndDebugRegion() +{ + m_Cmd.endDebugUtilsLabelEXT(); +} +#endif + void systems::Context::End() { @@ -173,6 +192,42 @@ systems::Context::End() ERROR_IF(Failed(result), "Could not end context") THEN_ABORT(result); } +void +systems::ComputeContext::Dispatch(const Pipeline &pipeline, u32 x, u32 y, u32 z, usize size, void *data) +{ + BindPipeline(pipeline); + PushConstantBlock(0, size, data); + + m_Cmd.dispatch(x, y, z); +} + +void +systems::ComputeContext::BindPipeline(const Pipeline &pipeline) +{ + vk::PipelineBindPoint bindPoint = vk::PipelineBindPoint::eGraphics; + switch (pipeline.m_Kind) + { + case Pipeline::Kind::eGraphics: + bindPoint = vk::PipelineBindPoint::eGraphics; + break; + case Pipeline::Kind::eCompute: + bindPoint = vk::PipelineBindPoint::eCompute; + break; + default: + UNREACHABLE("No additional bind points"); + } + m_Cmd.bindPipeline(bindPoint, pipeline.m_Pipeline); + + // TODO: Maybe find a smarter place to host this. + if (CommitManager::IsInit()) + { + m_Cmd.bindDescriptorSets(bindPoint, pipeline.m_Layout, 0, 1, &CommitManager::Instance().GetDescriptorSet(), 0, + nullptr); + } + + m_PipelineInUse = &pipeline; +} + void systems::GraphicsContext::SetViewport(const vk::Viewport &viewport) { @@ -187,18 +242,9 @@ systems::GraphicsContext::BindVertexBuffer(const Ref &vertexBuffer } void -systems::GraphicsContext::BindPipeline(const Pipeline &pipeline) +systems::GraphicsContext::BindIndexBuffer(const Ref &indexBuffer) { - m_Cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); - - // TODO: Maybe find a smarter place to host this. - if (CommitManager::IsInit()) - { - m_Cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, - &CommitManager::Instance().GetDescriptorSet(), 0, nullptr); - } - - m_PipelineInUse = &pipeline; + m_Cmd.bindIndexBuffer(indexBuffer->m_Buffer, 0, vk::IndexType::eUint32); } void @@ -213,6 +259,12 @@ systems::GraphicsContext::DrawIndexed(usize indexCount) m_Cmd.drawIndexed(static_cast(indexCount), 1, 0, 0, 0); } +void +systems::GraphicsContext::DrawIndexed(const usize indexCount, const usize firstIndex, const usize firstVertex) +{ + m_Cmd.drawIndexed(static_cast(indexCount), 1, static_cast(firstIndex), static_cast(firstVertex), 0); +} + void systems::GraphicsContext::BeginRendering(const vk::RenderingInfo &renderingInfo) { @@ -227,18 +279,18 @@ systems::GraphicsContext::EndRendering() } void -systems::TransferContext::UploadTexture(const Ref &image, const ImageData &data) +systems::TransferContext::UploadTexture(const Ref &image, const eastl::span &data) { ERROR_IF(not(image and image->IsValid()), "Invalid image"); auto [w, h, d] = image->m_Extent; auto formatSize = GetFormatSize(image->m_Format); auto expectedByteSize = static_cast(w) * static_cast(h) * static_cast(d) * formatSize; - ERROR_IF(expectedByteSize != data.m_NumBytes, "Mismatch in data size {} vs image size {} ({}x{}x{}x{})", - data.m_NumBytes, expectedByteSize, w, h, d, formatSize); + ERROR_IF(expectedByteSize != data.size_bytes(), "Mismatch in data size {} vs image size {} ({}x{}x{}x{})", + data.size_bytes(), expectedByteSize, w, h, d, formatSize); - const Ref stagingBuffer = m_Pool->GetDevice().CreateStagingBuffer(data.m_NumBytes); - stagingBuffer->Write(0, data.m_NumBytes, data.m_Data); + const Ref stagingBuffer = m_Pool->GetDevice().CreateStagingBuffer(data.size_bytes()); + stagingBuffer->Write(0, data.size_bytes(), data.data()); const vk::BufferImageCopy bufferImageCopy = { .bufferOffset = 0, @@ -261,6 +313,31 @@ systems::TransferContext::UploadTexture(const Ref &image, const ImageData KeepAlive(image); } +void +systems::TransferContext::UploadBuffer(const Ref &buffer, usize size, const void *data) +{ + ERROR_IF(not(buffer and buffer->IsValid()), "Invalid buffer"); + + auto expectedByteSize = buffer->m_Size; + ERROR_IF(expectedByteSize != size, "Mismatch in data size {} vs buffer size {}", size, expectedByteSize); + + const Ref stagingBuffer = m_Pool->GetDevice().CreateStagingBuffer(size); + stagingBuffer->Write(0, size, data); + + const vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = expectedByteSize}; + + m_Cmd.copyBuffer(stagingBuffer->m_Buffer, buffer->m_Buffer, 1, &bufferCopy); + + KeepAlive(stagingBuffer); + KeepAlive(buffer); +} + +void +systems::TransferContext::Blit(const vk::BlitImageInfo2 &mipBlitInfo) +{ + m_Cmd.blitImage2(&mipBlitInfo); +} + systems::TransferContext::TransferContext(TransferContext &&other) noexcept : Context{std::move(other)} { @@ -275,6 +352,26 @@ systems::TransferContext::operator=(TransferContext &&other) noexcept return *this; } +void +systems::ComputeContext::PushConstantBlock(const usize offset, const usize size, const void *data) +{ + assert(m_PipelineInUse); + + vk::ShaderStageFlags stage; + switch (m_PipelineInUse->m_Kind) + { + case Pipeline::Kind::eGraphics: + stage = vk::ShaderStageFlagBits::eAll; + break; + case Pipeline::Kind::eCompute: + stage = vk::ShaderStageFlagBits::eCompute; + break; + } + + m_Cmd.pushConstants(m_PipelineInUse->m_Layout, stage, static_cast(offset), + static_cast(size), data); +} + using namespace systems::_internal; ContextPool::ContextPool(Device &device, const u32 queueFamilyIndex, const ManagedBy managedBy) @@ -367,6 +464,8 @@ ContextPool::AllocateCommandBuffer() }; vk::CommandBuffer &cmd = m_CommandBuffers.emplace_back(); AbortIfFailed(m_Device->m_Device->allocateCommandBuffers(&allocateInfo, &cmd)); + ++m_BuffersAllocated; + return cmd; } @@ -395,6 +494,12 @@ TransferContextPool::CreateTransferContext() return TransferContext{*this, AllocateCommandBuffer()}; } +systems::ComputeContext +ComputeContextPool::CreateComputeContext() +{ + return ComputeContext{*this, AllocateCommandBuffer()}; +} + systems::GraphicsContext GraphicsContextPool::CreateGraphicsContext() { diff --git a/aster/src/aster/systems/device.cpp b/aster/src/aster/systems/device.cpp index 7717542..6c43b3a 100644 --- a/aster/src/aster/systems/device.cpp +++ b/aster/src/aster/systems/device.cpp @@ -17,10 +17,65 @@ #include #include +#if defined(DOMAIN) +#undef DOMAIN +#endif + static constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::eCompute | QueueSupportFlagBits::ePresent | QueueSupportFlagBits::eTransfer; +vk::CompareOp +DepthOpToVulkan(systems::GraphicsPipelineCreateInfo::CompareOp depthOp) +{ + switch (depthOp) + { + case systems::GraphicsPipelineCreateInfo::CompareOp::eNever: + return vk::CompareOp::eNever; + case systems::GraphicsPipelineCreateInfo::CompareOp::eLessThan: + return vk::CompareOp::eLess; + case systems::GraphicsPipelineCreateInfo::CompareOp::eEqualTo: + return vk::CompareOp::eEqual; + case systems::GraphicsPipelineCreateInfo::CompareOp::eGreaterThan: + return vk::CompareOp::eGreater; + case systems::GraphicsPipelineCreateInfo::CompareOp::eLessThanOrEqualTo: + return vk::CompareOp::eLessOrEqual; + case systems::GraphicsPipelineCreateInfo::CompareOp::eGreaterThanOrEqualTo: + return vk::CompareOp::eGreaterOrEqual; + case systems::GraphicsPipelineCreateInfo::CompareOp::eNotEqualTo: + return vk::CompareOp::eNotEqual; + case systems::GraphicsPipelineCreateInfo::CompareOp::eAlways: + return vk::CompareOp::eAlways; + } + return vk::CompareOp::eAlways; +} + +vk::PipelineDepthStencilStateCreateInfo +systems::GraphicsPipelineCreateInfo::GetDepthStencilStateCreateInfo() const +{ + bool depthEnabled = false; + bool depthWriteEnabled = false; + + switch (m_DepthTest) + { + case DepthTest::eEnabled: + depthEnabled = true; + depthWriteEnabled = true; + break; + case DepthTest::eReadOnly: + depthEnabled = true; + break; + case DepthTest::eDisabled: + break; + } + + return { + .depthTestEnable = depthEnabled, + .depthWriteEnable = depthWriteEnabled, + .depthCompareOp = DepthOpToVulkan(m_DepthOp), + }; +} + PhysicalDevice systems::DefaultPhysicalDeviceSelector(const PhysicalDevices &physicalDevices) { @@ -66,6 +121,21 @@ systems::Device::CreateStorageBuffer(const usize size, const cstr name) return eastl::make_shared(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name}); } +Ref +systems::Device::CreateIndexBuffer(usize size, cstr name) +{ + // TODO: Storage and Index buffer are set. + // This is hacky and should be improved. + constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eIndexBuffer | + vk::BufferUsageFlagBits::eShaderDeviceAddress | + vk::BufferUsageFlagBits::eTransferDst; + constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO; + return eastl::make_shared(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name}); +} + Ref systems::Device::CreateUniformBuffer(const usize size, const cstr name) { @@ -447,6 +517,8 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre { return result; } + if (createInfo.m_Name) + SetName(pipelineLayout, createInfo.m_Name); eastl::fixed_vector inputBindingDescriptions; eastl::fixed_vector inputAttributeDescriptions; @@ -499,11 +571,7 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre .rasterizationSamples = vk::SampleCountFlagBits::e1, .sampleShadingEnable = false, }; - vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = { - .depthTestEnable = true, - .depthWriteEnable = true, - .depthCompareOp = vk::CompareOp::eLess, - }; + vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = createInfo.GetDepthStencilStateCreateInfo(); vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = { .blendEnable = false, .srcColorBlendFactor = vk::BlendFactor::eSrcColor, @@ -554,17 +622,64 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre .layout = pipelineLayout, }; vk::Pipeline pipeline; - auto vresult = m_Device->createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline); - ERROR_IF(Failed(vresult), "Could not create a graphics pipeline. Cause: {}", vresult) - THEN_ABORT(vresult); - SetName(pipeline, "Triangle Pipeline"); + if (auto result = m_Device->createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline); + Failed(result)) + { + ERROR("Could not create a graphics pipeline. Cause: {}", result); + return result; + } + if (createInfo.m_Name) + SetName(pipeline, createInfo.m_Name); for (auto &shader : shaders) { m_Device->destroy(shader.module, nullptr); } - pipelineOut = {&m_Device, pipelineLayout, pipeline, {}}; + pipelineOut = {&m_Device, pipelineLayout, pipeline, {}, Pipeline::Kind::eGraphics}; + return {}; +} + +systems::PipelineCreationError +systems::Device::CreateComputePipeline(Pipeline &pipelineOut, const ComputePipelineCreateInfo &createInfo) +{ + eastl::fixed_vector shaders; + Slang::ComPtr program; + if (auto shaderResult = CreateShaders(shaders, program, {&createInfo.m_Shader, 1}); shaderResult) + { + return shaderResult; + } + + vk::PipelineLayout pipelineLayout; + if (auto result = CreatePipelineLayout(pipelineLayout, program)) + { + return result; + } + if (createInfo.m_Name) + SetName(pipelineLayout, createInfo.m_Name); + + vk::ComputePipelineCreateInfo pipelineCreateInfo = { + .stage = shaders[0], + .layout = pipelineLayout, + }; + + vk::Pipeline pipeline; + if (auto result = m_Device->createComputePipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline); + Failed(result)) + { + ERROR("Could not create a graphics pipeline. Cause: {}", result); + return result; + } + + if (createInfo.m_Name) + SetName(pipeline, createInfo.m_Name); + + for (auto &shader : shaders) + { + m_Device->destroy(shader.module, nullptr); + } + + pipelineOut = Pipeline{&m_Device, pipelineLayout, pipeline, {}, Pipeline::Kind::eCompute}; return {}; } @@ -607,11 +722,12 @@ systems::Device::CreateShaders( { ComPtr shaderModule; shaderModule = m_SlangSession->loadModule(shaderInfo.m_ShaderFile.data(), shaderDiagnostics.writeRef()); - if (shaderDiagnostics) + if (!shaderModule) { ERROR("{}", static_cast(shaderDiagnostics->getBufferPointer())); return SLANG_FAIL; } + WARN_IF(shaderDiagnostics, "{}", static_cast(shaderDiagnostics->getBufferPointer())); components.push_back(shaderModule); @@ -620,12 +736,21 @@ systems::Device::CreateShaders( ComPtr actualEntryPoint; auto slangResult = shaderModule->findEntryPointByName(entryPointName.data(), actualEntryPoint.writeRef()); + if (slangResult < 0) + { + ERROR("Could not find entry point '{}' in '{}'. Cause: {}", entryPointName, shaderInfo.m_ShaderFile, + slangResult); + return slangResult; + } + slang::ProgramLayout *entryProgramLayout = actualEntryPoint->getLayout(0, shaderDiagnostics.writeRef()); - if (shaderDiagnostics) + if (!entryProgramLayout) { ERROR("{}", static_cast(shaderDiagnostics->getBufferPointer())); return SLANG_FAIL; } + WARN_IF(shaderDiagnostics, "{}", static_cast(shaderDiagnostics->getBufferPointer())); + switch (entryProgramLayout->getEntryPointByIndex(0)->getStage()) { MARK_FOUND_AND_VALIDATE_UNIQUE(vertexFound, VERTEX); @@ -647,13 +772,6 @@ systems::Device::CreateShaders( ERROR("Invalid Stage.") THEN_ABORT(-1); } - if (slangResult < 0) - { - ERROR("Could not find entry point '{}' in '{}'. Cause: {}", entryPointName, shaderInfo.m_ShaderFile, - slangResult); - return slangResult; - } - components.push_back(actualEntryPoint); ++entryPointCount; } @@ -697,6 +815,7 @@ systems::Device::CreateShaders( ERROR("{}", static_cast(shaderDiagnostics->getBufferPointer())); return slangResult; } + WARN_IF(shaderDiagnostics, "{}", static_cast(shaderDiagnostics->getBufferPointer())); vk::Result result = vk::Result::eSuccess; for (u32 entryPoint = 0; entryPoint < entryPointCount; ++entryPoint) @@ -710,6 +829,7 @@ systems::Device::CreateShaders( ERROR("{}", static_cast(shaderDiagnostics->getBufferPointer())); return slangResult; } + WARN_IF(shaderDiagnostics, "{}", static_cast(shaderDiagnostics->getBufferPointer())); if (auto progLayout = program->getLayout(0)) { @@ -789,12 +909,13 @@ systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, ComPtr layoutDiagnostics; slang::ProgramLayout *layout = program->getLayout(0, layoutDiagnostics.writeRef()); - if (layoutDiagnostics) + if (!layout) { - ERROR_IF(!layout, "{}", static_cast(layoutDiagnostics->getBufferPointer())); - return SLANG_FAIL; + ERROR("{}", static_cast(layoutDiagnostics->getBufferPointer())); } + WARN_IF(layoutDiagnostics, "{}", static_cast(layoutDiagnostics->getBufferPointer())); + vk::DescriptorSetLayout setLayout = {}; if (m_CommitManager) setLayout = m_CommitManager->GetDescriptorSetLayout(); @@ -940,6 +1061,7 @@ systems::Device::Device(const DeviceCreateInfo &createInfo) } m_TransferContextPool.Init(*this, m_TransferQueueFamily); + m_ComputeContextPool.Init(*this, m_ComputeQueueFamily); constexpr SlangGlobalSessionDesc globalSessionDesc = {}; auto result = slang::createGlobalSession(&globalSessionDesc, m_GlobalSlangSession.writeRef()); @@ -957,15 +1079,12 @@ systems::Device::Device(const DeviceCreateInfo &createInfo) .name = slang::CompilerOptionName::GLSLForceScalarLayout, .value = slang::CompilerOptionValue{.kind = slang::CompilerOptionValueKind::Int, .intValue0 = 1}, }; - std::array compilerOptions = { - useOriginalEntrypointNames, - bindlessSpaceIndex, - scalarLayout, - }; + eastl::array compilerOptions = {useOriginalEntrypointNames, bindlessSpaceIndex, scalarLayout}; + slang::PreprocessorMacroDesc fancyFlag = {"_DEBUG", "1"}; const slang::TargetDesc spirvTargetDesc = { .format = SLANG_SPIRV, - .profile = m_GlobalSlangSession->findProfile("glsl_450"), + .profile = m_GlobalSlangSession->findProfile("sm_6_6"), .compilerOptionEntries = compilerOptions.data(), .compilerOptionEntryCount = static_cast(compilerOptions.size()), }; @@ -974,6 +1093,12 @@ systems::Device::Device(const DeviceCreateInfo &createInfo) .targetCount = 1, .searchPaths = createInfo.m_ShaderSearchPaths.data(), .searchPathCount = static_cast(createInfo.m_ShaderSearchPaths.size()), + .preprocessorMacros = &fancyFlag, +#if !defined(ASTER_NDEBUG) + .preprocessorMacroCount = 1, +#else + .preprocessorMacroCount = 0, +#endif }; result = m_GlobalSlangSession->createSession(sessionDesc, m_SlangSession.writeRef()); ERROR_IF(result < 0, "Could not create a slang session.") THEN_ABORT(result); @@ -1003,6 +1128,9 @@ systems::Device::~Device() systems::Frame & systems::Device::GetNextFrame() { + if (m_CommitManager) + m_CommitManager->Update(); + Frame ¤tFrame = m_Frames[m_CurrentFrameIdx]; u32 frameIndex = m_CurrentFrameIdx; @@ -1083,6 +1211,12 @@ systems::Device::CreateTransferContext() return m_TransferContextPool.CreateTransferContext(); } +systems::ComputeContext +systems::Device::CreateComputeContext() +{ + return m_ComputeContextPool.CreateComputeContext(); +} + systems::Receipt systems::Device::Submit(Context &context) { diff --git a/samples/00_util/gui.cpp b/samples/00_util/gui.cpp index e94a880..075cebd 100644 --- a/samples/00_util/gui.cpp +++ b/samples/00_util/gui.cpp @@ -5,9 +5,10 @@ #include "gui.h" -#include "aster/core/instance.h" #include "aster/core/device.h" +#include "aster/core/instance.h" #include "aster/core/window.h" +#include "aster/systems/device.h" #include "helpers.h" #include @@ -25,6 +26,72 @@ VulkanAssert(VkResult result) AbortIfFailed(result); } +void +Init(systems::Device &device, Window &window) +{ + g_AttachmentFormat = device.m_Swapchain.m_Format; + + eastl::vector poolSizes = { + {vk::DescriptorType::eSampler, 1000}, + {vk::DescriptorType::eCombinedImageSampler, 1000}, + {vk::DescriptorType::eSampledImage, 1000}, + {vk::DescriptorType::eStorageImage, 1000}, + {vk::DescriptorType::eUniformTexelBuffer, 1000}, + {vk::DescriptorType::eStorageTexelBuffer, 1000}, + {vk::DescriptorType::eUniformBuffer, 1000}, + {vk::DescriptorType::eStorageBuffer, 1000}, + {vk::DescriptorType::eUniformBufferDynamic, 1000}, + {vk::DescriptorType::eStorageBufferDynamic, 1000}, + {vk::DescriptorType::eInputAttachment, 1000}, + }; + + const vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = { + .flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, + .maxSets = 1000, + .poolSizeCount = static_cast(poolSizes.size()), + .pPoolSizes = poolSizes.data(), + }; + + AbortIfFailed(device.m_Device->createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &g_DescriptorPool)); + + IMGUI_CHECKVERSION(); + CreateContext(); + ImGuiIO &io = GetIO(); + (void)io; + // io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + // io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Viewports bad + + StyleColorsDark(); + + ImGui_ImplGlfw_InitForVulkan(window.m_Window, true); + + vk::PipelineRenderingCreateInfo renderingCreateInfo = { + .colorAttachmentCount = 1, + .pColorAttachmentFormats = &g_AttachmentFormat, + }; + + // TODO: Switch this into being managed by Device. + // m_Instance etc should private. + ImGui_ImplVulkan_InitInfo imguiVulkanInitInfo = { + .Instance = device.m_Instance.m_Instance, + .PhysicalDevice = device.m_Device.m_PhysicalDevice, + .Device = device.m_Device.m_Device, + .QueueFamily = device.m_PrimaryQueueFamily, + .Queue = device.m_PrimaryQueue, + .DescriptorPool = g_DescriptorPool, + .MinImageCount = static_cast(device.m_Swapchain.m_Images.size()), + .ImageCount = static_cast(device.m_Swapchain.m_Images.size()), + .PipelineCache = nullptr, + .UseDynamicRendering = true, + .PipelineRenderingCreateInfo = renderingCreateInfo, + .Allocator = nullptr, + .CheckVkResultFn = VulkanAssert, + }; + ImGui_ImplVulkan_Init(&imguiVulkanInitInfo); + + ImGui_ImplVulkan_CreateFontsTexture(); +} + void Init(const Instance *context, const Device *device, const Window *window, vk::Format attachmentFormat, const u32 imageCount, const u32 queueFamily, const vk::Queue queue) @@ -90,10 +157,19 @@ Init(const Instance *context, const Device *device, const Window *window, vk::Fo ImGui_ImplVulkan_CreateFontsTexture(); } +void +Destroy(const systems::Device &device) +{ + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + DestroyContext(); + + device.m_Device->destroy(Take(g_DescriptorPool), nullptr); +} + void Destroy(const Device *device) { - ImGui_ImplVulkan_Shutdown(); ImGui_ImplGlfw_Shutdown(); DestroyContext(); @@ -200,6 +276,36 @@ Draw(const vk::CommandBuffer commandBuffer, const vk::Extent2D extent, const vk: #endif } +void +Draw(systems::Frame &frame, systems::GraphicsContext &context) +{ + context.BeginDebugRegion("UI Pass", {0.9f, 0.9f, 1.0f, 1.0f}); + + vk::RenderingAttachmentInfo attachmentInfo = { + .imageView = frame.m_SwapchainImageView, + .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, + .resolveMode = vk::ResolveModeFlagBits::eNone, + .loadOp = vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f}, + }; + + const vk::RenderingInfo renderingInfo = { + .renderArea = {.extent = frame.m_SwapchainSize}, + .layerCount = 1, + .colorAttachmentCount = 1, + .pColorAttachments = &attachmentInfo, + .pDepthAttachment = nullptr, + }; + + context.BeginRendering(renderingInfo); + + ImGui_ImplVulkan_RenderDrawData(GetDrawData(), context.GetCommandBuffer()); + + context.EndRendering(); + context.EndDebugRegion(); +} + void PushDisable() { diff --git a/samples/00_util/gui.h b/samples/00_util/gui.h index 2aad929..86edb39 100644 --- a/samples/00_util/gui.h +++ b/samples/00_util/gui.h @@ -6,6 +6,7 @@ #pragma once #include "aster/aster.h" +#include "aster/core/device.h" #include @@ -14,17 +15,27 @@ struct Instance; struct Window; struct Swapchain; +namespace systems +{ +class Device; +class GraphicsContext; +struct Frame; +} + // ReSharper disable once CppInconsistentNaming namespace ImGui { +void Init(systems::Device &device, Window &window); void Init(const Instance *context, const Device *device, const Window *window, vk::Format attachmentFormat, u32 imageCount, u32 queueFamily, vk::Queue queue); +void Destroy(const systems::Device &device); void Destroy(const Device *device); void Recreate(); void StartBuild(); void EndBuild(); void Draw(vk::CommandBuffer commandBuffer, vk::Extent2D extent, vk::ImageView view); +void Draw(systems::Frame &frame, systems::GraphicsContext &context); void PushDisable(); void PopDisable(); diff --git a/samples/02_box/box.cpp b/samples/02_box/box.cpp index 2697c7f..bfbf046 100644 --- a/samples/02_box/box.cpp +++ b/samples/02_box/box.cpp @@ -34,6 +34,12 @@ struct ImageFile bool Load(cstr fileName); [[nodiscard]] usize GetSize() const; + + operator eastl::span() const + { + return {static_cast(m_Data), GetSize()}; + } + ~ImageFile(); }; @@ -251,7 +257,7 @@ main(int, char **) context.Dependency(imageReadyToWriteDependency); - context.UploadTexture(crate->m_Image, {.m_Data = imageFile.m_Data, .m_NumBytes = imageFile.GetSize()}); + context.UploadTexture(crate->m_Image, imageFile); context.Dependency(imageReadyToReadDependency); @@ -347,12 +353,11 @@ main(int, char **) while (window.Poll()) { Time::Update(); - commitManager.Update(); camera.m_Model *= rotate(mat4{1.0f}, static_cast(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); ubo->Write(0, sizeof camera, &camera); - auto& currentFrame = device.GetNextFrame(); + auto ¤tFrame = device.GetNextFrame(); prevSwapchainSize = swapchainSize; swapchainSize = currentFrame.m_SwapchainSize; diff --git a/samples/03_model_render/CMakeLists.txt b/samples/03_model_render/CMakeLists.txt index b632100..e465585 100644 --- a/samples/03_model_render/CMakeLists.txt +++ b/samples/03_model_render/CMakeLists.txt @@ -27,6 +27,14 @@ add_shader(model_render "shader/diffuse_irradiance.cs.hlsl") add_shader(model_render "shader/prefilter.cs.hlsl") add_shader(model_render "shader/brdf_lut.cs.hlsl") +add_shader(model_render "shader/slang/background.slang") +add_shader(model_render "shader/slang/bindless.slang") +add_shader(model_render "shader/slang/common_structs.slang") +add_shader(model_render "shader/slang/environment.slang") +add_shader(model_render "shader/slang/eqrect_to_cube.slang") +add_shader(model_render "shader/slang/ibl_common.slang") +add_shader(model_render "shader/slang/model.slang") + target_link_libraries(model_render PRIVATE aster_core) target_link_libraries(model_render PRIVATE util_helper) diff --git a/samples/03_model_render/asset_loader.cpp b/samples/03_model_render/asset_loader.cpp index 6bdf686..729bf57 100644 --- a/samples/03_model_render/asset_loader.cpp +++ b/samples/03_model_render/asset_loader.cpp @@ -11,7 +11,7 @@ #include "helpers.h" #include "aster/systems/commit_manager.h" -#include "aster/systems/resource_manager.h" +#include "aster/systems/device.h" #include #include @@ -37,10 +37,20 @@ VectorToVec4(const std::vector &vec) return vec4{0.0f}; } assert(vec.size() == 4); - return {vec[0], vec[1], vec[2], vec[3]}; } +vec4 +VectorToVec4(const std::vector &vec, float w) +{ + if (vec.empty()) + { + return vec4{0.0f}; + } + assert(vec.size() == 3); + return {vec[0], vec[1], vec[2], w}; +} + vec3 VectorToVec3(const std::vector &vec) { @@ -65,7 +75,7 @@ AssetLoader::LoadHdrImage(cstr path, cstr name) const u32 width = static_cast(x); u32 height = static_cast(y); - auto texture = m_ResourceManager->CombinedImageViews().CreateTexture2D({ + auto texture = m_Device->CreateTexture2DWithView({ .m_Format = vk::Format::eR32G32B32A32Sfloat, .m_Extent = {width, height}, .m_Name = path, @@ -74,36 +84,7 @@ AssetLoader::LoadHdrImage(cstr path, cstr name) const .m_IsStorage = false, }); - auto *pDevice = m_CommitManager->m_Device; - - auto stagingBuffer = - m_ResourceManager->Buffers().CreateStagingBuffer((sizeof *data) * x * y * 4, "HDR Staging Buffer"); - stagingBuffer->Write(0, stagingBuffer->m_Size, data); - - stbi_image_free(data); - #pragma region Setup Copy/Sync primitives - vk::BufferImageCopy2 copyRegion = { - .bufferOffset = 0, - .bufferRowLength = width, - .bufferImageHeight = height, - .imageSubresource = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .imageOffset = {0, 0, 0}, - .imageExtent = texture->m_Extent, - }; - vk::CopyBufferToImageInfo2 stagingInfo = { - .srcBuffer = stagingBuffer->m_Buffer, - .dstImage = texture->GetImage(), - .dstImageLayout = vk::ImageLayout::eTransferDstOptimal, - .regionCount = 1, - .pRegions = ©Region, - }; vk::ImageMemoryBarrier2 readyToStageBarrier = { .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, .srcAccessMask = vk::AccessFlagBits2::eNone, @@ -136,8 +117,8 @@ AssetLoader::LoadHdrImage(cstr path, cstr name) const .dstAccessMask = vk::AccessFlagBits2::eShaderRead, .oldLayout = vk::ImageLayout::eTransferDstOptimal, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - .srcQueueFamilyIndex = m_TransferQueueIndex, - .dstQueueFamilyIndex = m_GraphicsQueueIndex, + .srcQueueFamilyIndex = m_Device->m_TransferQueueFamily, + .dstQueueFamilyIndex = m_Device->m_PrimaryQueueFamily, .image = texture->GetImage(), .subresourceRange = { @@ -156,49 +137,31 @@ AssetLoader::LoadHdrImage(cstr path, cstr name) const }; #pragma endregion - AbortIfFailed(m_CommandBuffer.begin(&OneTimeCmdBeginInfo)); + auto context = m_Device->CreateTransferContext(); + context.Begin(); -#if !defined(ASTER_NDEBUG) StackString<128> loadActionName = "Load: "; loadActionName += name ? name : path; - vk::DebugUtilsLabelEXT debugLabel = { - .pLabelName = loadActionName.c_str(), - .color = std::array{1.0f, 1.0f, 1.0f, 1.0f}, - }; - m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel); -#endif + context.BeginDebugRegion(loadActionName.c_str()); - m_CommandBuffer.pipelineBarrier2(&readyToStageDependency); - m_CommandBuffer.copyBufferToImage2(&stagingInfo); - m_CommandBuffer.pipelineBarrier2(&postStagingDependency); + context.Dependency(readyToStageDependency); + context.UploadTexture(texture->m_Image, {reinterpret_cast(data), (sizeof *data) * x * y * 4}); + context.Dependency(postStagingDependency); -#if !defined(ASTER_NDEBUG) - m_CommandBuffer.endDebugUtilsLabelEXT(); -#endif + context.EndDebugRegion(); - AbortIfFailed(m_CommandBuffer.end()); + context.End(); - vk::SubmitInfo submitInfo = { - .waitSemaphoreCount = 0, - .pWaitDstStageMask = nullptr, - .commandBufferCount = 1, - .pCommandBuffers = &m_CommandBuffer, - }; + auto rcpt = m_Device->Submit(context); + stbi_image_free(data); - vk::Fence fence; - vk::FenceCreateInfo fenceCreateInfo = {}; - AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence)); - AbortIfFailed(m_TransferQueue.submit(1, &submitInfo, fence)); - AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue)); - pDevice->m_Device.destroy(fence, nullptr); - - AbortIfFailed(pDevice->m_Device.resetCommandPool(m_CommandPool, {})); + m_Device->WaitOn(rcpt); return texture; } void -GenerateMipMaps(vk::CommandBuffer commandBuffer, const Ref &texture, vk::ImageLayout initialLayout, +GenerateMipMaps(systems::TransferContext &context, const Ref &texture, vk::ImageLayout initialLayout, vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage) { #if !defined(ASTER_NDEBUG) @@ -206,7 +169,7 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, const Ref &texture, vk .pLabelName = "Generate Mipmap", .color = std::array{0.9f, 0.9f, 0.9f, 1.0f}, }; - commandBuffer.beginDebugUtilsLabelEXT(&label); + context.BeginDebugRegion("Generate MipMap", {0.9, 0.9, 0.9, 1.0}); #endif vk::ImageMemoryBarrier2 imageStartBarrier = { @@ -329,7 +292,7 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, const Ref &texture, vk // Mip Mapping - commandBuffer.pipelineBarrier2(&imageStartDependency); + context.Dependency(imageStartDependency); i32 prevMipWidth = static_cast(texture->m_Extent.width); i32 prevMipHeight = static_cast(texture->m_Extent.height); @@ -354,21 +317,21 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, const Ref &texture, vk nextMipBarrier.subresourceRange.baseMipLevel = currentMipLevel; - commandBuffer.blitImage2(&mipBlitInfo); - commandBuffer.pipelineBarrier2(&interMipDependency); + context.Blit(mipBlitInfo); + context.Dependency(interMipDependency); prevMipHeight = currentMipHeight; prevMipWidth = currentMipWidth; } - commandBuffer.pipelineBarrier2(&imageReadyDependency); + context.Dependency(imageReadyDependency); #if !defined(ASTER_NDEBUG) - commandBuffer.endDebugUtilsLabelEXT(); + context.EndDebugRegion(); #endif } -std::tuple, Ref> -AssetLoader::LoadImageToGpu(tinygltf::Image *image, bool isSrgb, cstr name) const +systems::ResId +AssetLoader::LoadImageToGpu(systems::TransferContext &context, tinygltf::Image *image, bool isSrgb, cstr name) const { // TODO(Something not loading properly). @@ -381,14 +344,12 @@ AssetLoader::LoadImageToGpu(tinygltf::Image *image, bool isSrgb, cstr name) cons auto assignedName = nullptr; #endif - u32 height = static_cast(image->height); u32 width = static_cast(image->width); vk::Format imageFormat = isSrgb ? vk::Format::eR8G8B8A8Srgb : vk::Format::eR8G8B8A8Unorm; - usize byteSize = image->image.size(); - auto texture = m_ResourceManager->Images().CreateTexture2D({ + auto texture = m_Device->CreateTexture2D({ .m_Format = imageFormat, .m_Extent = {width, height}, .m_Name = assignedName, @@ -397,18 +358,9 @@ AssetLoader::LoadImageToGpu(tinygltf::Image *image, bool isSrgb, cstr name) cons .m_IsStorage = false, }); - auto stagingBuffer = m_ResourceManager->Buffers().CreateStagingBuffer(byteSize); - stagingBuffer->Write(0, byteSize, image->image.data()); - -#if !defined(ASTER_NDEBUG) StackString<128> loadActionName = "Load: "; loadActionName += assignedName; - vk::DebugUtilsLabelEXT debugLabel = { - .pLabelName = loadActionName.c_str(), - .color = std::array{1.0f, 1.0f, 1.0f, 1.0f}, - }; - m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel); -#endif + context.BeginDebugRegion(loadActionName.c_str()); #pragma region Barriers and Blits @@ -445,8 +397,8 @@ AssetLoader::LoadImageToGpu(tinygltf::Image *image, bool isSrgb, cstr name) cons .dstAccessMask = vk::AccessFlagBits2::eTransferRead, .oldLayout = vk::ImageLayout::eTransferDstOptimal, .newLayout = vk::ImageLayout::eTransferSrcOptimal, - .srcQueueFamilyIndex = m_TransferQueueIndex, - .dstQueueFamilyIndex = m_GraphicsQueueIndex, + .srcQueueFamilyIndex = m_Device->m_TransferQueueFamily, + .dstQueueFamilyIndex = m_Device->m_PrimaryQueueFamily, .image = texture->m_Image, .subresourceRange = { @@ -463,45 +415,22 @@ AssetLoader::LoadImageToGpu(tinygltf::Image *image, bool isSrgb, cstr name) cons .pImageMemoryBarriers = &postStagingBarrier, }; - vk::BufferImageCopy2 imageCopy = { - .bufferOffset = 0, - .bufferRowLength = static_cast(image->width), - .bufferImageHeight = static_cast(image->height), - .imageSubresource = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .imageOffset = {}, - .imageExtent = texture->m_Extent, - }; - vk::CopyBufferToImageInfo2 stagingCopyInfo = { - .srcBuffer = stagingBuffer->m_Buffer, - .dstImage = texture->m_Image, - .dstImageLayout = vk::ImageLayout::eTransferDstOptimal, - .regionCount = 1, - .pRegions = &imageCopy, - }; - #pragma endregion - m_CommandBuffer.pipelineBarrier2(&imageStartDependency); - m_CommandBuffer.copyBufferToImage2(&stagingCopyInfo); - m_CommandBuffer.pipelineBarrier2(&postStagingDependency); + context.Dependency(imageStartDependency); + context.UploadTexture(texture, {image->image.data(), image->image.size()}); + context.Dependency(postStagingDependency); - GenerateMipMaps(m_CommandBuffer, texture, vk::ImageLayout::eTransferSrcOptimal, - vk::ImageLayout::eShaderReadOnlyOptimal); + GenerateMipMaps(context, texture, vk::ImageLayout::eTransferSrcOptimal, vk::ImageLayout::eShaderReadOnlyOptimal); #if !defined(ASTER_NDEBUG) - m_CommandBuffer.endDebugUtilsLabelEXT(); + context.EndDebugRegion(); #endif - auto textureView = m_ResourceManager->Views().CreateView( + auto textureView = m_Device->CreateView( {.m_Image = texture, .m_Name = image->name.data(), .m_AspectMask = vk::ImageAspectFlagBits::eColor}); - return {m_CommitManager->CommitTexture(textureView), stagingBuffer}; + return m_Device->m_CommitManager->CommitTexture(textureView); } Model @@ -511,8 +440,6 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) tinygltf::Model model; tinygltf::TinyGLTF loader; - const Device *pDevice = m_CommitManager->m_Device; - const auto fsPath = fs::absolute(path); const auto ext = fsPath.extension(); if (ext == GLTF_ASCII_FILE_EXTENSION) @@ -536,19 +463,13 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) } } - AbortIfFailed(m_CommandBuffer.begin(&OneTimeCmdBeginInfo)); + auto context = m_Device->CreateTransferContext(); + + context.Begin(); -#if !defined(ASTER_NDEBUG) StackString<128> loadActionName = "Load: "; loadActionName += name ? name : path; - vk::DebugUtilsLabelEXT debugLabel = { - .pLabelName = loadActionName.c_str(), - .color = std::array{1.0f, 1.0f, 1.0f, 1.0f}, - }; - m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel); -#endif - - eastl::vector> stagingBuffers; + context.BeginDebugRegion(loadActionName.c_str()); eastl::hash_map> textureHandleMap; @@ -558,7 +479,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) if (!model.materials.empty()) { // TODO("Something broken on load here."); - auto getTextureHandle = [this, &textureHandleMap, &stagingBuffers, + auto getTextureHandle = [this, &context, &textureHandleMap, &model](i32 index, const bool isSrgb) -> systems::ResId { if (index < 0) { @@ -571,9 +492,8 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) const auto &texture = model.textures[index]; auto *image = &model.images[texture.source]; - auto [handle, staging] = LoadImageToGpu(image, isSrgb, texture.name.empty() ? nullptr : texture.name.c_str()); + auto handle = LoadImageToGpu(context, image, isSrgb, texture.name.empty() ? nullptr : texture.name.c_str()); textureHandleMap.emplace(index, handle); - stagingBuffers.emplace_back(std::move(staging)); return handle; }; @@ -582,29 +502,23 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) { materials.push_back({ .m_AlbedoFactor = VectorToVec4(material.pbrMetallicRoughness.baseColorFactor), - .m_EmissionFactor = VectorToVec3(material.emissiveFactor), - .m_MetalFactor = static_cast(material.pbrMetallicRoughness.metallicFactor), - .m_RoughFactor = static_cast(material.pbrMetallicRoughness.roughnessFactor), + .m_EmissionFactor = VectorToVec4(material.emissiveFactor, 0.0f), .m_AlbedoTex = getTextureHandle(material.pbrMetallicRoughness.baseColorTexture.index, true), .m_NormalTex = getTextureHandle(material.normalTexture.index, false), .m_MetalRoughTex = getTextureHandle(material.pbrMetallicRoughness.metallicRoughnessTexture.index, false), .m_OcclusionTex = getTextureHandle(material.occlusionTexture.index, false), .m_EmissionTex = getTextureHandle(material.emissiveTexture.index, true), + .m_MetalFactor = static_cast(material.pbrMetallicRoughness.metallicFactor), + .m_RoughFactor = static_cast(material.pbrMetallicRoughness.roughnessFactor), }); } usize materialsByteSize = materials.size() * sizeof materials[0]; - auto materialsBuffer = m_ResourceManager->Buffers().CreateStorageBuffer(materialsByteSize, name); - materialsHandle = m_CommitManager->CommitBuffer(materialsBuffer); + auto materialsBuffer = m_Device->CreateStorageBuffer(materialsByteSize, name); + materialsHandle = m_Device->m_CommitManager->CommitBuffer(materialsBuffer); - auto materialStaging = m_ResourceManager->Buffers().CreateStagingBuffer(materialsByteSize); - materialStaging->Write(0, materialsByteSize, materials.data()); - - vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = materialsByteSize}; - m_CommandBuffer.copyBuffer(materialStaging->m_Buffer, materialsBuffer->m_Buffer, 1, &bufferCopy); - - stagingBuffers.emplace_back(std::move(materialStaging)); + context.UploadBuffer(materialsBuffer, materials); } // TODO: Mesh reordering based on nodes AND OR meshoptimizer @@ -887,63 +801,37 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) nodes.Update(); - auto nodeBuffer = m_ResourceManager->Buffers().CreateStorageBuffer(nodes.GetGlobalTransformByteSize()); + auto nodeBuffer = m_Device->CreateStorageBuffer(nodes.GetGlobalTransformByteSize()); nodeBuffer->Write(0, nodes.GetGlobalTransformByteSize(), nodes.GetGlobalTransformPtr()); - systems::ResId nodeHandle = m_CommitManager->CommitBuffer(nodeBuffer); + systems::ResId nodeHandle = m_Device->m_CommitManager->CommitBuffer(nodeBuffer); #pragma region Staging / Transfer / Uploads systems::ResId positionBufferHandle = systems::ResId::Null(); systems::ResId vertexDataHandle = systems::ResId::Null(); - Ref indexBuffer; + Ref indexBuffer; - { - auto uploadBufferData = [cmd = this->m_CommandBuffer, &stagingBuffers, resMan = this->m_ResourceManager, - pDevice](const Ref &buffer, const void *data) { - const vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = buffer->m_Size}; - auto stagingBuffer = resMan->Buffers().CreateStagingBuffer(bufferCopy.size); - stagingBuffer->Write(0, bufferCopy.size, data); - cmd.copyBuffer(stagingBuffer->m_Buffer, buffer->m_Buffer, 1, &bufferCopy); - stagingBuffers.emplace_back(std::move(stagingBuffer)); - }; + auto positionBuffer = m_Device->CreateStorageBuffer(vertexPositions.size() * sizeof vertexPositions[0]); + positionBufferHandle = m_Device->m_CommitManager->CommitBuffer(positionBuffer); + context.UploadBuffer(positionBuffer, vertexPositions); - auto positionBuffer = - m_ResourceManager->Buffers().CreateStorageBuffer(vertexPositions.size() * sizeof vertexPositions[0]); - positionBufferHandle = m_CommitManager->CommitBuffer(positionBuffer); - uploadBufferData(positionBuffer, vertexPositions.data()); + auto vertexDataBuffer = m_Device->CreateStorageBuffer(vertexData.size() * sizeof vertexData[0]); + vertexDataHandle = m_Device->m_CommitManager->CommitBuffer(vertexDataBuffer); + context.UploadBuffer(vertexDataBuffer, vertexData); - auto vertexDataBuffer = - m_ResourceManager->Buffers().CreateStorageBuffer(vertexData.size() * sizeof vertexData[0]); - vertexDataHandle = m_CommitManager->CommitBuffer(vertexDataBuffer); - uploadBufferData(vertexDataBuffer, vertexData.data()); - - // TODO: Index buffer needs to be separated. - indexBuffer = - m_ResourceManager->Buffers().CreateStorageBuffer(indices.size() * sizeof indices[0], "Index Buffer"); - uploadBufferData(indexBuffer, indices.data()); - } + // TODO: Index buffer needs to be separated. + indexBuffer = systems::CastBuffer( + m_Device->CreateIndexBuffer(indices.size() * sizeof indices[0], "Index Buffer")); + context.UploadBuffer(indexBuffer, indices); #pragma endregion #if !defined(ASTER_NDEBUG) - m_CommandBuffer.endDebugUtilsLabelEXT(); + context.EndDebugRegion(); #endif - AbortIfFailed(m_CommandBuffer.end()); + context.End(); - vk::SubmitInfo submitInfo = { - .waitSemaphoreCount = 0, - .pWaitDstStageMask = nullptr, - .commandBufferCount = 1, - .pCommandBuffers = &m_CommandBuffer, - }; - - vk::Fence fence; - vk::FenceCreateInfo fenceCreateInfo = {}; - AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence)); - AbortIfFailed(m_TransferQueue.submit(1, &submitInfo, fence)); - AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue)); - pDevice->m_Device.destroy(fence, nullptr); - - AbortIfFailed(pDevice->m_Device.resetCommandPool(m_CommandPool, {})); + auto rcpt = m_Device->Submit(context); + m_Device->WaitOn(rcpt); Model::ModelHandles handles = { .m_VertexPositionHandle = positionBufferHandle, @@ -961,15 +849,13 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) } return Model{ - m_CommitManager, textureHandles, std::move(nodes), nodeBuffer, handles, indexBuffer, meshPrimitives, + textureHandles, std::move(nodes), nodeBuffer, handles, indexBuffer, meshPrimitives, }; } -Model::Model(systems::CommitManager *resourceManager, eastl::vector> &textureHandles, - Nodes &&nodes, Ref nodeBuffer, ModelHandles &handles, Ref indexBuffer, - const eastl::vector &meshPrimitives) - : m_ResourceManager(resourceManager) - , m_TextureHandles(std::move(textureHandles)) +Model::Model(eastl::vector> &textureHandles, Nodes &&nodes, Ref nodeBuffer, + ModelHandles &handles, Ref indexBuffer, const eastl::vector &meshPrimitives) + : m_TextureHandles(std::move(textureHandles)) , m_Nodes(std::move(nodes)) , m_Handles(std::move(handles)) , m_NodeBuffer(std::move(nodeBuffer)) @@ -999,64 +885,7 @@ Model::Update() } } -AssetLoader::AssetLoader(systems::ResourceManager *resourceManager, systems::CommitManager *commitManager, - vk::Queue transferQueue, u32 transferQueueIndex, u32 graphicsQueueIndex) - : m_ResourceManager(resourceManager) - , m_CommitManager(commitManager) - , m_TransferQueue(transferQueue) - , m_TransferQueueIndex(transferQueueIndex) - , m_GraphicsQueueIndex(graphicsQueueIndex) +AssetLoader::AssetLoader(systems::Device &device) + : m_Device{&device} { - const Device *pDevice = commitManager->m_Device; - const vk::CommandPoolCreateInfo poolCreateInfo = { - .flags = vk::CommandPoolCreateFlagBits::eTransient, - .queueFamilyIndex = transferQueueIndex, - }; - AbortIfFailedM(pDevice->m_Device.createCommandPool(&poolCreateInfo, nullptr, &m_CommandPool), - "Transfer command pool creation failed."); - - pDevice->SetName(m_CommandPool, "Asset Loader Command Pool"); - - const vk::CommandBufferAllocateInfo commandBufferAllocateInfo = { - .commandPool = m_CommandPool, - .level = vk::CommandBufferLevel::ePrimary, - .commandBufferCount = 1, - }; - AbortIfFailed(pDevice->m_Device.allocateCommandBuffers(&commandBufferAllocateInfo, &m_CommandBuffer)); - - pDevice->SetName(m_CommandBuffer, "Asset Loader Command Buffer"); -} - -AssetLoader::~AssetLoader() -{ - if (m_CommitManager && m_CommandPool) - { - m_CommitManager->m_Device->m_Device.destroy(m_CommandPool, nullptr); - } -} - -AssetLoader::AssetLoader(AssetLoader &&other) noexcept - : m_ResourceManager(Take(other.m_ResourceManager)) - , m_CommitManager(Take(other.m_CommitManager)) - , m_CommandPool(Take(other.m_CommandPool)) - , m_CommandBuffer(other.m_CommandBuffer) - , m_TransferQueue(other.m_TransferQueue) - , m_TransferQueueIndex(other.m_TransferQueueIndex) - , m_GraphicsQueueIndex(other.m_GraphicsQueueIndex) -{ -} - -AssetLoader & -AssetLoader::operator=(AssetLoader &&other) noexcept -{ - if (this == &other) - return *this; - m_ResourceManager = Take(other.m_ResourceManager); - m_CommitManager = Take(other.m_CommitManager); - m_CommandPool = Take(other.m_CommandPool); - m_CommandBuffer = other.m_CommandBuffer; - m_TransferQueue = other.m_TransferQueue; - m_TransferQueueIndex = other.m_TransferQueueIndex; - m_GraphicsQueueIndex = other.m_GraphicsQueueIndex; - return *this; } \ No newline at end of file diff --git a/samples/03_model_render/asset_loader.h b/samples/03_model_render/asset_loader.h index 26f6384..f8bbfa2 100644 --- a/samples/03_model_render/asset_loader.h +++ b/samples/03_model_render/asset_loader.h @@ -9,14 +9,18 @@ #include "aster/core/buffer.h" -#include "aster/systems/image_manager.h" #include "aster/systems/resource.h" -#include "aster/systems/view_manager.h" #include "nodes.h" #include "tiny_gltf.h" namespace systems { +class TransferContext; +} + +namespace systems +{ +class Device; class ResourceManager; class SamplerManager; class BufferManager; @@ -47,14 +51,14 @@ struct MeshPrimitive struct Material { vec4 m_AlbedoFactor; // 16 16 - vec3 m_EmissionFactor; // 12 28 - f32 m_MetalFactor; // 04 32 - f32 m_RoughFactor; // 04 36 - systems::ResId m_AlbedoTex; // 04 40 - systems::ResId m_NormalTex; // 04 44 - systems::ResId m_MetalRoughTex; // 04 48 - systems::ResId m_OcclusionTex; // 04 52 - systems::ResId m_EmissionTex; // 04 56 + vec4 m_EmissionFactor; // 16 32 + systems::ResId m_AlbedoTex; // 08 40 + systems::ResId m_NormalTex; // 08 48 + systems::ResId m_MetalRoughTex; // 08 56 + systems::ResId m_OcclusionTex; // 08 64 + systems::ResId m_EmissionTex; // 08 72 + f32 m_MetalFactor; // 04 76 + f32 m_RoughFactor; // 04 80 }; struct VertexData @@ -67,8 +71,6 @@ struct VertexData struct Model { - systems::CommitManager *m_ResourceManager; - eastl::vector> m_TextureHandles; Nodes m_Nodes; @@ -81,15 +83,15 @@ struct Model } m_Handles; Ref m_NodeBuffer; - Ref m_IndexBuffer; + Ref m_IndexBuffer; eastl::vector m_MeshPrimitives; [[nodiscard]] const mat4 &GetModelTransform() const; void SetModelTransform(const mat4 &transform); void Update(); - Model(systems::CommitManager *resourceManager, eastl::vector> &textureHandles, - Nodes &&nodes, Ref nodeBuffer, ModelHandles &handles, Ref indexBuffer, + Model(eastl::vector> &textureHandles, + Nodes &&nodes, Ref nodeBuffer, ModelHandles &handles, Ref indexBuffer, const eastl::vector &meshPrimitives); ~Model() = default; @@ -102,14 +104,7 @@ struct Model struct AssetLoader { - systems::ResourceManager *m_ResourceManager; - systems::CommitManager *m_CommitManager; - - vk::CommandPool m_CommandPool; - vk::CommandBuffer m_CommandBuffer; - vk::Queue m_TransferQueue; - u32 m_TransferQueueIndex; - u32 m_GraphicsQueueIndex; + systems::Device *m_Device; Ref LoadHdrImage(cstr path, cstr name = nullptr) const; Model LoadModelToGpu(cstr path, cstr name = nullptr); @@ -123,40 +118,33 @@ struct AssetLoader constexpr static auto AJoints0 = "JOINTS_0"; constexpr static auto AWeights0 = "WEIGHTS_0"; - AssetLoader(systems::ResourceManager *resourceManager, systems::CommitManager *commitManager, - vk::Queue transferQueue, u32 transferQueueIndex, u32 graphicsQueueIndex); - ~AssetLoader(); + explicit AssetLoader(systems::Device &device); - AssetLoader(AssetLoader &&other) noexcept; - AssetLoader &operator=(AssetLoader &&other) noexcept; - - DISALLOW_COPY_AND_ASSIGN(AssetLoader); - -private: - std::tuple, Ref> - LoadImageToGpu(tinygltf::Image *image, bool isSrgb, cstr name = nullptr) const; + private: + systems::ResId + LoadImageToGpu(systems::TransferContext &context, tinygltf::Image *image, bool isSrgb, cstr name = nullptr) const; }; void -GenerateMipMaps(vk::CommandBuffer commandBuffer, const Ref &textureView, vk::ImageLayout initialLayout, +GenerateMipMaps(systems::TransferContext &context, const Ref &textureView, vk::ImageLayout initialLayout, vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage); void -GenerateMipMaps(vk::CommandBuffer commandBuffer, concepts::ImageRefTo auto &texture, vk::ImageLayout initialLayout, - vk::ImageLayout finalLayout, - vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands, - vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands) -{ - GenerateMipMaps(commandBuffer, systems::CastImage(texture), initialLayout, finalLayout, prevStage, - finalStage); -} - -void -GenerateMipMaps(vk::CommandBuffer commandBuffer, concepts::ViewRefTo auto &texture, +GenerateMipMaps(systems::TransferContext &context, concepts::ImageRefTo auto &texture, vk::ImageLayout initialLayout, vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands, vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands) { - GenerateMipMaps(commandBuffer, systems::CastImage(texture->m_Image), initialLayout, finalLayout, prevStage, + GenerateMipMaps(context, systems::CastImage(texture), initialLayout, finalLayout, prevStage, + finalStage); +} + +void +GenerateMipMaps(systems::TransferContext &context, concepts::ViewRefTo auto &texture, + vk::ImageLayout initialLayout, vk::ImageLayout finalLayout, + vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands, + vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands) +{ + GenerateMipMaps(context, systems::CastImage(texture->m_Image), initialLayout, finalLayout, prevStage, finalStage); } diff --git a/samples/03_model_render/ibl_helpers.cpp b/samples/03_model_render/ibl_helpers.cpp index 04bbd9c..51402d7 100644 --- a/samples/03_model_render/ibl_helpers.cpp +++ b/samples/03_model_render/ibl_helpers.cpp @@ -13,25 +13,24 @@ #include "pipeline_utils.h" #include "aster/systems/commit_manager.h" -#include "aster/systems/resource_manager.h" +#include "aster/systems/device.h" #include #include -constexpr auto EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrect_to_cube.cs.hlsl.spv"; -constexpr auto DIFFUSE_IRRADIANCE_SHADER_FILE = "shader/diffuse_irradiance.cs.hlsl.spv"; -constexpr auto PREFILTER_SHADER_FILE = "shader/prefilter.cs.hlsl.spv"; -constexpr auto BRDF_LUT_SHADER_FILE = "shader/brdf_lut.cs.hlsl.spv"; +constexpr auto EQUIRECT_TO_CUBE_SHADER_FILE = "eqrect_to_cube"; +constexpr auto ENVIRONMENT_SHADER_FILE = "environment"; +constexpr auto DIFFUSE_IRRADIANCE_ENTRY = "diffuseIrradiance"; +constexpr auto PREFILTER_ENTRY = "prefilter"; +constexpr auto BRDF_LUT_ENTRY = "brdfLut"; Environment -CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, - systems::ResId hdrEnv, const cstr name) +CreateCubeFromHdrEnv(AssetLoader &assetLoader, const u32 cubeSide, systems::ResId hdrEnv) { - systems::ResourceManager *resourceManager = assetLoader->m_ResourceManager; - systems::CommitManager *commitManager = assetLoader->m_CommitManager; - const Device *pDevice = commitManager->m_Device; + systems::Device &device = *assetLoader.m_Device; + auto *commitManager = device.m_CommitManager.get(); - auto skybox = resourceManager->CombinedImageViews().CreateTextureCube({ + auto skybox = device.CreateTextureCubeWithView({ .m_Format = vk::Format::eR16G16B16A16Sfloat, .m_Side = cubeSide, .m_Name = "Skybox", @@ -43,7 +42,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 auto skyboxHandle = commitManager->CommitTexture(skybox); auto skyboxStorageHandle = commitManager->CommitStorageImage(skybox); - auto diffuseIrradiance = resourceManager->CombinedImageViews().CreateTextureCube({ + auto diffuseIrradiance = device.CreateTextureCubeWithView({ .m_Format = vk::Format::eR16G16B16A16Sfloat, .m_Side = 64, .m_Name = "Diffuse Irradiance", @@ -54,7 +53,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 auto diffuseIrradianceHandle = commitManager->CommitTexture(diffuseIrradiance); auto diffuseIrradianceStorageHandle = commitManager->CommitStorageImage(diffuseIrradiance); - auto prefilterCube = resourceManager->CombinedImageViews().CreateTextureCube({ + auto prefilterCube = device.CreateTextureCubeWithView({ .m_Format = vk::Format::eR16G16B16A16Sfloat, .m_Side = cubeSide, .m_Name = "Prefilter", @@ -68,7 +67,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 // All non-owning copies. for (u8 mipLevel = 0; mipLevel < prefilterMipCountMax; ++mipLevel) { - auto view = resourceManager->Views().CreateView({ + auto view = device.CreateView({ .m_Image = systems::CastImage(prefilterCube->m_Image), .m_ViewType = vk::ImageViewType::eCube, .m_AspectMask = vk::ImageAspectFlagBits::eColor, @@ -80,7 +79,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 prefilterStorageHandles.push_back(commitManager->CommitStorageImage(view)); } - auto brdfLut = resourceManager->CombinedImageViews().CreateTexture2D({ + auto brdfLut = device.CreateTexture2DWithView({ .m_Format = vk::Format::eR16G16Sfloat, .m_Extent = {512, 512}, .m_Name = "BRDF LUT", @@ -89,7 +88,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 .m_IsStorage = true, }); - auto brdfLutSampler = resourceManager->Samplers().CreateSampler({ + auto brdfLutSampler = device.CreateSampler({ .m_AddressModeU = vk::SamplerAddressMode::eClampToEdge, .m_AddressModeV = vk::SamplerAddressMode::eClampToEdge, .m_AddressModeW = vk::SamplerAddressMode::eClampToEdge, @@ -205,79 +204,119 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 #pragma region Pipeline Creation etc - vk::PushConstantRange pcr = { - .stageFlags = vk::ShaderStageFlagBits::eCompute, - .offset = 0, - .size = - static_cast(eastl::max(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)), - eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants)))), - }; + // vk::PushConstantRange pcr = { + // .stageFlags = vk::ShaderStageFlagBits::eCompute, + // .offset = 0, + // .size = static_cast( + // eastl::max(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)), + // eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants)))), + // }; - vk::PipelineLayout pipelineLayout; - const vk::PipelineLayoutCreateInfo layoutCreateInfo = { - .setLayoutCount = 1, - .pSetLayouts = &commitManager->GetDescriptorSetLayout(), - .pushConstantRangeCount = 1, - .pPushConstantRanges = &pcr, - }; - AbortIfFailed(pDevice->m_Device.createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout)); + // vk::PipelineLayout pipelineLayout; + // const vk::PipelineLayoutCreateInfo layoutCreateInfo = { + // .setLayoutCount = 1, + // .pSetLayouts = &commitManager->GetDescriptorSetLayout(), + // .pushConstantRangeCount = 1, + // .pPushConstantRanges = &pcr, + // }; + // AbortIfFailed(device.m_Device->createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout)); - const auto eqRectToCubeShader = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE); - const auto diffuseRadianceShader = CreateShader(pDevice, DIFFUSE_IRRADIANCE_SHADER_FILE); - const auto prefilterShader = CreateShader(pDevice, PREFILTER_SHADER_FILE); - const auto brdfLutShader = CreateShader(pDevice, BRDF_LUT_SHADER_FILE); - eastl::array computePipelineCreateInfo = { - vk::ComputePipelineCreateInfo{ - .stage = - { - .stage = vk::ShaderStageFlagBits::eCompute, - .module = eqRectToCubeShader, - .pName = "main", - }, - .layout = pipelineLayout, - }, - vk::ComputePipelineCreateInfo{ - .stage = - { - .stage = vk::ShaderStageFlagBits::eCompute, - .module = diffuseRadianceShader, - .pName = "main", - }, - .layout = pipelineLayout, - }, - vk::ComputePipelineCreateInfo{ - .stage = - { - .stage = vk::ShaderStageFlagBits::eCompute, - .module = prefilterShader, - .pName = "main", - }, - .layout = pipelineLayout, - }, - vk::ComputePipelineCreateInfo{ - .stage = - { - .stage = vk::ShaderStageFlagBits::eCompute, - .module = brdfLutShader, - .pName = "main", - }, - .layout = pipelineLayout, - }, - }; + // const auto eqRectToCubeShader = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE); + // const auto diffuseRadianceShader = CreateShader(pDevice, DIFFUSE_IRRADIANCE_SHADER_FILE); + // const auto prefilterShader = CreateShader(pDevice, PREFILTER_SHADER_FILE); + // const auto brdfLutShader = CreateShader(pDevice, BRDF_LUT_SHADER_FILE); + // eastl::array computePipelineCreateInfo = { + // vk::ComputePipelineCreateInfo{ + // .stage = + // { + // .stage = vk::ShaderStageFlagBits::eCompute, + // .module = eqRectToCubeShader, + // .pName = "main", + // }, + // .layout = pipelineLayout, + // }, + // vk::ComputePipelineCreateInfo{ + // .stage = + // { + // .stage = vk::ShaderStageFlagBits::eCompute, + // .module = diffuseRadianceShader, + // .pName = "main", + // }, + // .layout = pipelineLayout, + // }, + // vk::ComputePipelineCreateInfo{ + // .stage = + // { + // .stage = vk::ShaderStageFlagBits::eCompute, + // .module = prefilterShader, + // .pName = "main", + // }, + // .layout = pipelineLayout, + // }, + // vk::ComputePipelineCreateInfo{ + // .stage = + // { + // .stage = vk::ShaderStageFlagBits::eCompute, + // .module = brdfLutShader, + // .pName = "main", + // }, + // .layout = pipelineLayout, + // }, + // }; - eastl::array pipelines; - AbortIfFailed( - pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, static_cast(computePipelineCreateInfo.size()), - computePipelineCreateInfo.data(), nullptr, pipelines.data())); + // eastl::array pipelines; + // AbortIfFailed(pDevice->m_Device.createComputePipelines( + // pDevice->m_PipelineCache, static_cast(computePipelineCreateInfo.size()), + // computePipelineCreateInfo.data(), nullptr, pipelines.data())); - vk::Pipeline eqRectToCubePipeline = pipelines[0]; - vk::Pipeline diffuseIrradiancePipeline = pipelines[1]; - vk::Pipeline prefilterPipeline = pipelines[2]; - vk::Pipeline brdfLutPipeline = pipelines[3]; + // vk::Pipeline eqRectToCubePipeline = pipelines[0]; + // vk::Pipeline diffuseIrradiancePipeline = pipelines[1]; + // vk::Pipeline prefilterPipeline = pipelines[2]; + // vk::Pipeline brdfLutPipeline = pipelines[3]; - for (auto &createInfos : computePipelineCreateInfo) + Pipeline eqRectToCubePipeline; + + if (auto result = + device.CreateComputePipeline(eqRectToCubePipeline, { + .m_Shader = + { + .m_ShaderFile = EQUIRECT_TO_CUBE_SHADER_FILE, + .m_EntryPoints = {"main"}, + }, + .m_Name = "EqRect -> Cubemap", + })) { - pDevice->m_Device.destroy(createInfos.stage.module, nullptr); + ERROR("EqRect -> Cubemap Pipeline Creation failed. Cause: {}", result.What()) THEN_ABORT(result.Value()); + } + + Pipeline diffuseIrradiancePipeline; + + if (auto result = device.CreateComputePipeline( + diffuseIrradiancePipeline, + {{.m_ShaderFile = ENVIRONMENT_SHADER_FILE, .m_EntryPoints = {DIFFUSE_IRRADIANCE_ENTRY}}, + "DiffuseIrradiance"})) + { + ERROR("Diffuse Irradiance compute pipeline creation failed. Cause: {}", result.What()) + THEN_ABORT(result.Value()); + } + + Pipeline prefilterPipeline; + + if (auto result = device.CreateComputePipeline( + prefilterPipeline, + {{.m_ShaderFile = ENVIRONMENT_SHADER_FILE, .m_EntryPoints = {PREFILTER_ENTRY}}, "Prefilter"})) + { + ERROR("Prefilter compute pipeline creation failed. Cause: {}", result.What()) + THEN_ABORT(result.Value()); + } + + Pipeline brdfLutPipeline; + + if (auto result = device.CreateComputePipeline( + brdfLutPipeline, {{.m_ShaderFile = ENVIRONMENT_SHADER_FILE, .m_EntryPoints = {BRDF_LUT_ENTRY}}, "BRDF"})) + { + ERROR("BRDF LUT compute pipeline creation failed. Cause: {}", result.What()) + THEN_ABORT(result.Value()); } #pragma endregion @@ -303,42 +342,27 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 commitManager->Update(); - auto cmd = assetLoader->m_CommandBuffer; - constexpr vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; - AbortIfFailed(cmd.begin(&beginInfo)); + auto context = assetLoader.m_Device->CreateComputeContext(); -#if !defined(ASTER_NDEBUG) - StackString<128> labelName = "Eqrect -> Cubemap: "; - labelName += name ? name : ""; - vk::DebugUtilsLabelEXT label = { - .pLabelName = labelName.c_str(), - .color = std::array{1.0f, 1.0f, 1.0f, 1.0f}, - }; - cmd.beginDebugUtilsLabelEXT(&label); -#endif + context.Begin(); - cmd.pipelineBarrier2(&readyToWriteDependency); + context.BeginDebugRegion("Eqrect -> Cubemap"); + + context.Dependency(readyToWriteDependency); - cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipelineLayout, 0, 1, &commitManager->GetDescriptorSet(), 0, - nullptr); - cmd.bindPipeline(vk::PipelineBindPoint::eCompute, eqRectToCubePipeline); - cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, - &skyboxPushConstant); assert(skybox->m_Extent.width % 16 == 0 && skybox->m_Extent.height % 16 == 0); - cmd.dispatch(skybox->m_Extent.width / 16, skybox->m_Extent.height / 16, 6); + context.Dispatch(eqRectToCubePipeline, skybox->m_Extent.width / 16, skybox->m_Extent.height / 16, 6, + skyboxPushConstant); - GenerateMipMaps(cmd, skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral, + GenerateMipMaps(context, skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral, vk::PipelineStageFlagBits2::eComputeShader, vk::PipelineStageFlagBits2::eComputeShader); - cmd.bindPipeline(vk::PipelineBindPoint::eCompute, diffuseIrradiancePipeline); - cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, - &diffuseIrradiancePushConstants); assert(diffuseIrradiance->m_Extent.width % 16 == 0 && diffuseIrradiance->m_Extent.height % 16 == 0); - cmd.dispatch(diffuseIrradiance->m_Extent.width / 16, diffuseIrradiance->m_Extent.width / 16, 6); + context.Dispatch(diffuseIrradiancePipeline, diffuseIrradiance->m_Extent.width / 16, + diffuseIrradiance->m_Extent.width / 16, 6, diffuseIrradiancePushConstants); - cmd.pipelineBarrier2(&diffIrrToSampleDependency); + context.Dependency(diffIrrToSampleDependency); - cmd.bindPipeline(vk::PipelineBindPoint::eCompute, prefilterPipeline); u32 mipSize = prefilterCube->m_Extent.width; assert(mipSize % 16 == 0); for (u32 mipCount = 0; auto &tex : prefilterStorageHandles) @@ -346,52 +370,27 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 prefilterPushConstants.m_OutputTexture = tex; prefilterPushConstants.m_CubeSide = mipSize; prefilterPushConstants.m_Roughness = static_cast(mipCount) / static_cast(prefilterMipCountMax - 1); - cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof prefilterPushConstants, - &prefilterPushConstants); + u32 groupCount = eastl::max(mipSize / 16u, 1u); - cmd.dispatch(groupCount, groupCount, 6); + context.Dispatch(prefilterPipeline, groupCount, groupCount, 6, prefilterPushConstants); ++mipCount; mipSize = mipSize >> 1; } - cmd.pipelineBarrier2(&skyboxToSampleDependency); - cmd.pipelineBarrier2(&prefilterToSampleDependency); + context.Dependency(skyboxToSampleDependency); + context.Dependency(prefilterToSampleDependency); - cmd.bindPipeline(vk::PipelineBindPoint::eCompute, brdfLutPipeline); - cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof brdfLutPushConstants, - &brdfLutPushConstants); assert(brdfLut->m_Extent.width % 16 == 0 && brdfLut->m_Extent.height % 16 == 0); - cmd.dispatch(brdfLut->m_Extent.width / 16, brdfLut->m_Extent.height / 16, 1); + context.Dispatch(brdfLutPipeline, brdfLut->m_Extent.width / 16, brdfLut->m_Extent.height / 16, 1, + brdfLutPushConstants); -#if !defined(ASTER_NDEBUG) - cmd.endDebugUtilsLabelEXT(); -#endif + context.EndDebugRegion(); - AbortIfFailed(cmd.end()); + context.End(); - vk::SubmitInfo submitInfo = { - .waitSemaphoreCount = 0, - .pWaitDstStageMask = nullptr, - .commandBufferCount = 1, - .pCommandBuffers = &cmd, - }; - - vk::Fence fence; - vk::FenceCreateInfo fenceCreateInfo = {}; - AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence)); - AbortIfFailed(computeQueue.submit(1, &submitInfo, fence)); - AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue)); - pDevice->m_Device.destroy(fence, nullptr); - - AbortIfFailed(pDevice->m_Device.resetCommandPool(assetLoader->m_CommandPool, {})); - - skybox = {}; - for (auto &pipeline : pipelines) - { - pDevice->m_Device.destroy(pipeline, nullptr); - } - pDevice->m_Device.destroy(pipelineLayout, nullptr); + auto receipt = device.Submit(context); + device.WaitOn(receipt); return { .m_Skybox = skyboxHandle, diff --git a/samples/03_model_render/ibl_helpers.h b/samples/03_model_render/ibl_helpers.h index 6d27f77..0ce616c 100644 --- a/samples/03_model_render/ibl_helpers.h +++ b/samples/03_model_render/ibl_helpers.h @@ -23,5 +23,4 @@ struct Environment systems::ResId m_BrdfLut; }; -Environment CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, - systems::ResId hdrEnv, cstr name = nullptr); \ No newline at end of file +Environment CreateCubeFromHdrEnv(AssetLoader &assetLoader, u32 cubeSide, systems::ResId hdrEnv); \ No newline at end of file diff --git a/samples/03_model_render/light_manager.cpp b/samples/03_model_render/light_manager.cpp index d04dc40..ec29e56 100644 --- a/samples/03_model_render/light_manager.cpp +++ b/samples/03_model_render/light_manager.cpp @@ -7,8 +7,8 @@ #include "aster/core/buffer.h" #include "aster/systems/commit_manager.h" +#include "aster/systems/device.h" #include "aster/systems/resource.h" -#include "aster/systems/resource_manager.h" #include "glm/ext/matrix_transform.hpp" // Static Checks @@ -55,9 +55,8 @@ ToColor32(const vec3 &col) return r << 24 | g << 16 | b << 8 | a; } -LightManager::LightManager(systems::ResourceManager *resourceManager, systems::CommitManager *commitManager) - : m_ResourceManager{resourceManager} - , m_CommitManager{commitManager} +LightManager::LightManager(systems::Device &device) + : m_Device{&device} , m_DirectionalLightCount{} , m_PointLightCount{} , m_MetaInfo{.m_LightBuffer = systems::NullId()} @@ -66,8 +65,7 @@ LightManager::LightManager(systems::ResourceManager *resourceManager, systems::C } LightManager::LightManager(LightManager &&other) noexcept - : m_ResourceManager(other.m_ResourceManager) - , m_CommitManager(other.m_CommitManager) + : m_Device{Take(other.m_Device)} , m_Lights(std::move(other.m_Lights)) , m_DirectionalLightCount(other.m_DirectionalLightCount) , m_PointLightCount(other.m_PointLightCount) @@ -81,7 +79,7 @@ LightManager::operator=(LightManager &&other) noexcept { if (this == &other) return *this; - m_ResourceManager = other.m_ResourceManager; + m_Device = Take(other.m_Device); m_Lights = std::move(other.m_Lights); m_DirectionalLightCount = other.m_DirectionalLightCount; m_PointLightCount = other.m_PointLightCount; @@ -210,15 +208,14 @@ LightManager::Update() const u16 requiredBufferCapacity = eastl::min(static_cast(m_Lights.capacity()), MAX_LIGHTS); if ((m_GpuBufferCapacity_ & CAPACITY_MASK) < requiredBufferCapacity) { - auto newBuffer = m_ResourceManager->Buffers().CreateStorageBuffer(requiredBufferCapacity * sizeof m_Lights[0], - "Light Buffer"); + auto newBuffer = m_Device->CreateStorageBuffer(requiredBufferCapacity * sizeof m_Lights[0], "Light Buffer"); m_GpuBufferCapacity_ = requiredBufferCapacity | UPDATE_REQUIRED_BIT; - m_MetaInfo.m_LightBuffer = m_CommitManager->CommitBuffer(newBuffer); + m_MetaInfo.m_LightBuffer = m_Device->m_CommitManager->CommitBuffer(newBuffer); } if (m_GpuBufferCapacity_ & UPDATE_REQUIRED_BIT) { - const auto ref = m_CommitManager->FetchHandle(m_MetaInfo.m_LightBuffer); + const auto ref = m_Device->m_CommitManager->FetchHandle(m_MetaInfo.m_LightBuffer); ref->Write(0, m_Lights.size() * sizeof m_Lights[0], m_Lights.data()); } } diff --git a/samples/03_model_render/light_manager.h b/samples/03_model_render/light_manager.h index 0608675..5518061 100644 --- a/samples/03_model_render/light_manager.h +++ b/samples/03_model_render/light_manager.h @@ -13,6 +13,11 @@ #include +namespace systems +{ +class Device; +} + namespace systems { class ResourceManager; @@ -52,6 +57,8 @@ struct Light f32 m_Range; // < 0.0 for invalid u32 m_Color_; // LSB is used for flags. (R G B Flags) f32 m_Intensity; + u32 m_Pad0; + u32 m_Pad1; constexpr static u32 MAX_GEN = 0x40; constexpr static u32 GEN_MASK = MAX_GEN - 1; @@ -82,8 +89,7 @@ struct LightManager u16 m_UnusedPadding0 = 0; // 02 12 }; - systems::ResourceManager *m_ResourceManager; - systems::CommitManager *m_CommitManager; + systems::Device *m_Device; eastl::vector m_Lights; // We don't need a Directional Light free list. We will just brute force iterate. @@ -107,7 +113,7 @@ struct LightManager ~LightManager() = default; - LightManager(systems::ResourceManager *resourceManager, systems::CommitManager *commitManager); + LightManager(systems::Device &device); LightManager(LightManager &&other) noexcept; LightManager &operator=(LightManager &&other) noexcept; diff --git a/samples/03_model_render/model_render.cpp b/samples/03_model_render/model_render.cpp index e9e735b..d658429 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -7,9 +7,9 @@ #include "aster/core/buffer.h" #include "aster/core/constants.h" -#include "aster/core/instance.h" #include "aster/core/device.h" #include "aster/core/image.h" +#include "aster/core/instance.h" #include "aster/core/physical_device.h" #include "aster/core/pipeline.h" #include "aster/core/swapchain.h" @@ -20,21 +20,24 @@ #include "helpers.h" #include "light_manager.h" -#include "aster/systems/buffer_manager.h" +#include "aster/util/files.h" #include "gui.h" #include "ibl_helpers.h" #include "pipeline_utils.h" #include #include -#include +#include #include #include -constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; constexpr auto PIPELINE_CACHE_FILE = "PipelineCacheData.bin"; -constexpr auto MODEL_FILE = "model/Sponza/Sponza.gltf"; +constexpr auto MODEL_FILE = "model/DamagedHelmet.glb"; constexpr auto BACKDROP_FILE = "image/photo_studio_loft_hall_4k.hdr"; + +constexpr auto MODEL_SHADER_FILE = "model"; +constexpr auto BACKGROUND_SHADER_FILE = "background"; + constexpr u32 INIT_WIDTH = 1280; constexpr u32 INIT_HEIGHT = 720; @@ -136,18 +139,9 @@ main(int, char **) MIN_LOG_LEVEL(Logger::LogType::eInfo); Window window = {"ModelRender (Aster)", {INIT_WIDTH, INIT_HEIGHT}}; - Instance context = {"ModelRender", VERSION}; - Surface surface = {&context, &window, "Primary Surface"}; - - PhysicalDevices physicalDevices = {&surface, &context}; - PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices); - - usize physicalDeviceOffsetAlignment = deviceToUse.m_DeviceProperties.limits.minUniformBufferOffsetAlignment; - - INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data()); Features enabledDeviceFeatures = { - .m_Vulkan10Features = {.samplerAnisotropy = true}, + .m_Vulkan10Features = {.samplerAnisotropy = true, .shaderInt16 = true}, .m_Vulkan12Features = { .descriptorIndexing = true, @@ -160,6 +154,7 @@ main(int, char **) .descriptorBindingStorageBufferUpdateAfterBind = true, .descriptorBindingPartiallyBound = true, .runtimeDescriptorArray = true, + .timelineSemaphore = true, .bufferDeviceAddress = true, .bufferDeviceAddressCaptureReplay = true, }, @@ -171,31 +166,52 @@ main(int, char **) }; auto pipelineCacheData = ReadFileBytes(PIPELINE_CACHE_FILE, false); + systems::Device device{{ + .m_Window = window, + .m_Features = enabledDeviceFeatures, + .m_AppName = "ModelRender", + .m_AppVersion = VERSION, + .m_PipelineCacheData = pipelineCacheData, + .m_ShaderSearchPaths = {"shader/slang/"}, + }}; - QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); - Device device = {&context, &deviceToUse, &enabledDeviceFeatures, - {queueAllocation}, pipelineCacheData, "Primary Device"}; - vk::Queue graphicsQueue = device.GetQueue(queueAllocation.m_Family, 0); - Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"}; - - auto resourceManager = systems::ResourceManager{&device}; - systems::CommitManager commitManager = {&device, 1000, 1000, 1000, - resourceManager.Samplers().CreateSampler({.m_Name = "Default Sampler"})}; - - AssetLoader assetLoader = {&resourceManager, &commitManager, graphicsQueue, queueAllocation.m_Family, - queueAllocation.m_Family}; - auto lightManager = LightManager{&resourceManager, &commitManager}; + AssetLoader assetLoader{device}; + LightManager lightManager{device}; Model model = assetLoader.LoadModelToGpu(MODEL_FILE); auto environmentHdri = assetLoader.LoadHdrImage(BACKDROP_FILE); - auto envHdriHandle = commitManager.CommitTexture(environmentHdri); + auto envHdriHandle = device.m_CommitManager->CommitTexture(environmentHdri); - auto environment = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 512, envHdriHandle, "Cube Env"); + auto environment = CreateCubeFromHdrEnv(assetLoader, 512, envHdriHandle); - auto attachmentFormat = vk::Format::eR8G8B8A8Srgb; + auto attachmentFormat = device.m_Swapchain.m_Format; - Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &commitManager); - Pipeline backGroundPipeline = CreateBackgroundPipeline(&device, attachmentFormat, &commitManager); + Pipeline pipeline; + if (auto result = device.CreatePipeline(pipeline, { + .m_Shaders = {{ + .m_ShaderFile = MODEL_SHADER_FILE, + .m_EntryPoints = {"vsmain", "fsmain"}, + }}, + .m_Name = "Primary", + })) + { + ERROR("Could not create model pipeline. Cause: {}", result.What()) THEN_ABORT(result.Value()); + } + + Pipeline backgroundPipeline; + if (auto result = device.CreatePipeline( + backgroundPipeline, { + .m_Shaders = {{ + .m_ShaderFile = BACKGROUND_SHADER_FILE, + .m_EntryPoints = {"vsmain", "fsmain"}, + }}, + .m_DepthTest = systems::GraphicsPipelineCreateInfo::DepthTest::eReadOnly, + .m_DepthOp = systems::GraphicsPipelineCreateInfo::CompareOp::eLessThanOrEqualTo, + .m_Name = "Background", + })) + { + ERROR("Could not create background pipeline. Cause: {}", result.What()) THEN_ABORT(result.Value()); + } lightManager.AddPoint(vec3{-5.0f, -5.0f, 5.0f}, vec3{1.0f}, 30.0f, 16.0f); lightManager.AddPoint(vec3{5.0f, -5.0f, 5.0f}, vec3{1.0f}, 30.0f, 16.0f); @@ -204,82 +220,21 @@ main(int, char **) lightManager.Update(); - vk::DescriptorPool descriptorPool; - vk::DescriptorSet perFrameDescriptor; - - { - vk::DescriptorSetLayout descriptorSetLayout = pipeline.m_SetLayouts[1]; - eastl::array poolSizes = { - vk::DescriptorPoolSize{ - .type = vk::DescriptorType::eUniformBuffer, - .descriptorCount = 3, - }, - }; - vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = { - .maxSets = 1, .poolSizeCount = static_cast(poolSizes.size()), .pPoolSizes = poolSizes.data()}; - AbortIfFailed(device.m_Device.createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &descriptorPool)); - - vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = { - .descriptorPool = descriptorPool, - .descriptorSetCount = 1, - .pSetLayouts = &descriptorSetLayout, - }; - AbortIfFailed(device.m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &perFrameDescriptor)); - } - vk::Extent2D internalResolution = {1920, 1080}; + auto swapchainSize = device.GetSwapchainSize(); + CameraController cameraController = {vec3{0.0f, 0.0f, 2.0f}, vec3{0.0f}, 70_deg, - static_cast(swapchain.m_Extent.width) / static_cast(swapchain.m_Extent.height)}; + static_cast(swapchainSize.m_Width) / + static_cast(swapchainSize.m_Height)}; - usize uboSize = 0; - usize cameraSize = sizeof cameraController.m_Camera; - uboSize += ClosestMultiple(cameraSize, physicalDeviceOffsetAlignment); - usize lightOffset = uboSize; - usize lightingSize = sizeof environment + sizeof lightManager.m_MetaInfo; - uboSize += ClosestMultiple(lightingSize, physicalDeviceOffsetAlignment); - - auto data = new u8[uboSize]; - memcpy(data, &cameraController.m_Camera, cameraSize); - memcpy(data + lightOffset, &environment, sizeof environment); - memcpy(data + lightOffset + sizeof environment, &lightManager.m_MetaInfo, sizeof lightManager.m_MetaInfo); - - auto ubo = resourceManager.Buffers().CreateUniformBuffer(uboSize, "Desc1 UBO"); - ubo->Write(0, ubo->m_Size, data); - - delete[] data; - - vk::DescriptorBufferInfo cameraBufferInfo = { - .buffer = ubo->m_Buffer, - .offset = 0, - .range = cameraSize, - }; - vk::DescriptorBufferInfo lightingBufferInfo = { - .buffer = ubo->m_Buffer, - .offset = lightOffset, - .range = lightingSize, - }; - eastl::array writeDescriptors = { - vk::WriteDescriptorSet{ - .dstSet = perFrameDescriptor, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &cameraBufferInfo, - }, - vk::WriteDescriptorSet{ - .dstSet = perFrameDescriptor, - .dstBinding = 1, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &lightingBufferInfo, - }, - }; - device.m_Device.updateDescriptorSets(static_cast(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr); - - commitManager.Update(); + auto cameraBuffer = device.CreateStorageBuffer(sizeof cameraController.m_Camera, "Camera Info"); + auto cameraBufferId = device.m_CommitManager->CommitBuffer(cameraBuffer); + auto lightManagerBuffer = + device.CreateStorageBuffer(sizeof environment + sizeof lightManager.m_MetaInfo, "Light Info"); + auto lightsBufferId = device.m_CommitManager->CommitBuffer(lightManagerBuffer); + lightManagerBuffer->Write(0, sizeof environment, &environment); + lightManagerBuffer->Write(sizeof environment, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo); // Persistent variables vk::Viewport viewport = { @@ -304,7 +259,7 @@ main(int, char **) .layerCount = 1, }; - vk::ImageMemoryBarrier2 preRenderBarrier = { + vk::ImageMemoryBarrier2 attachmentPreRenderBarrier = { .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, .srcAccessMask = vk::AccessFlagBits2::eTransferRead, .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, @@ -315,12 +270,8 @@ main(int, char **) .dstQueueFamilyIndex = vk::QueueFamilyIgnored, .subresourceRange = subresourceRange, }; - vk::DependencyInfo preRenderDependencies = { - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &preRenderBarrier, - }; - vk::ImageMemoryBarrier2 renderToBlitBarrier = { + vk::ImageMemoryBarrier2 attachmentRenderToBlitBarrier = { .srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, .srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, .dstStageMask = vk::PipelineStageFlagBits2::eBlit, @@ -331,29 +282,24 @@ main(int, char **) .dstQueueFamilyIndex = vk::QueueFamilyIgnored, .subresourceRange = subresourceRange, }; - vk::ImageMemoryBarrier2 acquireToTransferDstBarrier = { - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + + // Dependency from Acquire to Blit + vk::ImageMemoryBarrier2 swapchainPreBlitBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, .srcAccessMask = vk::AccessFlagBits2::eNone, .dstStageMask = vk::PipelineStageFlagBits2::eBlit, - .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstAccessMask = vk::AccessFlagBits2::eTransferRead | vk::AccessFlagBits2::eTransferWrite, .oldLayout = vk::ImageLayout::eUndefined, .newLayout = vk::ImageLayout::eTransferDstOptimal, .srcQueueFamilyIndex = vk::QueueFamilyIgnored, .dstQueueFamilyIndex = vk::QueueFamilyIgnored, .subresourceRange = subresourceRange, }; - eastl::array postRenderBarriers = { - renderToBlitBarrier, - acquireToTransferDstBarrier, - }; - vk::DependencyInfo postRenderDependencies = { - .imageMemoryBarrierCount = static_cast(postRenderBarriers.size()), - .pImageMemoryBarriers = postRenderBarriers.data(), - }; - vk::ImageMemoryBarrier2 transferDstToGuiRenderBarrier = { + // Execution dependency between blit and GUI render. + vk::ImageMemoryBarrier2 swapchainBlitToGuiBarrier = { .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite | vk::AccessFlagBits2::eTransferRead, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentRead, .oldLayout = vk::ImageLayout::eTransferDstOptimal, @@ -362,12 +308,8 @@ main(int, char **) .dstQueueFamilyIndex = vk::QueueFamilyIgnored, .subresourceRange = subresourceRange, }; - vk::DependencyInfo preGuiDependencies = { - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &transferDstToGuiRenderBarrier, - }; - vk::ImageMemoryBarrier2 prePresentBarrier = { + vk::ImageMemoryBarrier2 swapchainPrePresentBarrier = { .srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, .srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, .dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe, @@ -378,33 +320,34 @@ main(int, char **) .dstQueueFamilyIndex = vk::QueueFamilyIgnored, .subresourceRange = subresourceRange, }; - vk::DependencyInfo prePresentDependencies = { - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &prePresentBarrier, + + auto primeBarriers = [](const eastl::span &barriers, const vk::Image image) { + for (auto &bar : barriers) + { + bar.image = image; + } }; - FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT}; eastl::fixed_vector, MAX_FRAMES_IN_FLIGHT> depthImages; eastl::fixed_vector, MAX_FRAMES_IN_FLIGHT> attachmentImages; - for (u32 index = 0; index < frameManager.m_FramesInFlight; ++index) + for (u32 index = 0; index < MAX_FRAMES_IN_FLIGHT; ++index) { auto name = fmt::format("Depth Frame{}", index); - depthImages.emplace_back(resourceManager.CombinedImageViews().CreateDepthStencilImage({ + depthImages.emplace_back(device.CreateDepthStencilImageWithView({ .m_Extent = internalResolution, .m_Name = name.c_str(), })); name = fmt::format("Attachment0 Frame{}", index); - attachmentImages.emplace_back(resourceManager.CombinedImageViews().CreateAttachment({ + attachmentImages.emplace_back(device.CreateAttachmentWithView({ .m_Format = attachmentFormat, .m_Extent = internalResolution, .m_Name = name.c_str(), })); } - gui::Init(&context, &device, &window, swapchain.m_Format, static_cast(swapchain.m_ImageViews.size()), - queueAllocation.m_Family, graphicsQueue); + gui::Init(device, window); bool rotating = false; bool lockToScreen = true; bool showDiffuse = false; @@ -423,7 +366,7 @@ main(int, char **) vec3 camPosition = cameraController.m_Camera.m_Position; vk::Extent2D inputResolution = internalResolution; - swapchain.RegisterResizeCallback([&cameraController](vk::Extent2D extent) { + device.RegisterResizeCallback([&cameraController](vk::Extent2D extent) { cameraController.SetAspectRatio(static_cast(extent.width) / static_cast(extent.height)); }); @@ -433,12 +376,11 @@ main(int, char **) while (window.Poll()) { Time::Update(); - commitManager.Update(); gui::StartBuild(); gui::Begin("Settings"); - gui::Text("Window Resolution: %ux%u", swapchain.m_Extent.width, swapchain.m_Extent.height); + gui::Text("Window Resolution: %ux%u", swapchainSize.m_Width, swapchainSize.m_Height); gui::Text("Render Resolution: %ux%u", internalResolution.width, internalResolution.height); gui::Checkbox("Lock Resolution to Window", &lockToScreen); if (!lockToScreen) @@ -449,7 +391,8 @@ main(int, char **) } inputResolution.height = height; - inputResolution.width = static_cast(cameraController.m_AspectRatio * static_cast(inputResolution.height)); + inputResolution.width = + static_cast(cameraController.m_AspectRatio * static_cast(inputResolution.height)); if (gui::Button("Change Resolution")) { @@ -466,10 +409,10 @@ main(int, char **) } else { - if (swapchain.m_Extent.width != internalResolution.width || - swapchain.m_Extent.height != internalResolution.height) + if (swapchainSize.m_Width != internalResolution.width || + swapchainSize.m_Height != internalResolution.height) { - internalResolution = swapchain.m_Extent; + internalResolution = swapchainSize; viewport.width = static_cast(internalResolution.width); viewport.height = -static_cast(internalResolution.height); viewport.y = static_cast(internalResolution.height); @@ -516,49 +459,51 @@ main(int, char **) } model.Update(); cameraController.m_Camera.CalculateInverses(); - ubo->Write(0, sizeof cameraController.m_Camera, &cameraController.m_Camera); + cameraBuffer->Write(0, sizeof cameraController.m_Camera, &cameraController.m_Camera); - Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize()); + auto ¤tFrame = device.GetNextFrame(); - u32 imageIndex = currentFrame->m_ImageIdx; - vk::Image currentSwapchainImage = swapchain.m_Images[imageIndex]; - vk::ImageView currentSwapchainImageView = swapchain.m_ImageViews[imageIndex]; - vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; + auto context = currentFrame.CreateGraphicsContext(); - auto ¤tDepthImage = depthImages[currentFrame->m_FrameIdx]; - auto ¤tAttachment = attachmentImages[currentFrame->m_FrameIdx]; + auto ¤tDepthImage = depthImages[currentFrame.m_FrameIdx]; + auto ¤tAttachment = attachmentImages[currentFrame.m_FrameIdx]; if (currentAttachment->m_Extent.width != internalResolution.width || currentAttachment->m_Extent.height != internalResolution.height) { - auto name = fmt::format("Depth Frame{}", currentFrame->m_FrameIdx); - currentDepthImage = resourceManager.CombinedImageViews().CreateDepthStencilImage({ + auto name = fmt::format("Depth Frame{}", currentFrame.m_FrameIdx); + currentDepthImage = device.CreateDepthStencilImageWithView({ .m_Extent = internalResolution, .m_Name = name.c_str(), }); - name = fmt::format("Attachment0 Frame{}", currentFrame->m_FrameIdx); - currentAttachment = resourceManager.CombinedImageViews().CreateAttachment({ + name = fmt::format("Attachment0 Frame{}", currentFrame.m_FrameIdx); + currentAttachment = device.CreateAttachmentWithView({ .m_Format = attachmentFormat, .m_Extent = internalResolution, .m_Name = name.c_str(), }); } + vk::Image currentSwapchainImage = currentFrame.m_SwapchainImage; + vk::ImageView currentDepthImageView = currentDepthImage->m_View; vk::Image currentImage = currentAttachment->m_Image->m_Image; vk::ImageView currentImageView = currentAttachment->m_View; - preRenderBarrier.image = currentImage; - postRenderBarriers[0].image = currentImage; - postRenderBarriers[1].image = currentSwapchainImage; - transferDstToGuiRenderBarrier.image = currentSwapchainImage; - prePresentBarrier.image = currentSwapchainImage; + attachmentPreRenderBarrier.image = currentImage; + attachmentRenderToBlitBarrier.image = currentImage; + swapchainPreBlitBarrier.image = currentSwapchainImage; + swapchainBlitToGuiBarrier.image = currentSwapchainImage; + swapchainPrePresentBarrier.image = currentSwapchainImage; - vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; - AbortIfFailed(cmd.begin(&beginInfo)); + context.Begin(); - cmd.pipelineBarrier2(&preRenderDependencies); + eastl::array firstBarriers = {attachmentPreRenderBarrier, swapchainPreBlitBarrier}; + context.Dependency({ + .imageMemoryBarrierCount = static_cast(firstBarriers.size()), + .pImageMemoryBarriers = firstBarriers.data(), + }); // Render eastl::array attachmentInfos = { @@ -589,19 +534,13 @@ main(int, char **) .pDepthAttachment = &depthAttachment, }; - cmd.beginRendering(&renderingInfo); + context.BeginRendering(renderingInfo); - cmd.setViewport(0, 1, &viewport); - cmd.setScissor(0, 1, &scissor); - cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, - &commitManager.GetDescriptorSet(), 0, nullptr); + context.SetViewport(viewport); - cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &perFrameDescriptor, 0, - nullptr); + context.BindIndexBuffer(model.m_IndexBuffer); - cmd.bindIndexBuffer(model.m_IndexBuffer->m_Buffer, 0, vk::IndexType::eUint32); - - cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); + context.BindPipeline(pipeline); u32 flags = 0; if (useSpecular) @@ -622,32 +561,33 @@ main(int, char **) } u32 pcbOffset = 0; - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof model.m_Handles, - &model.m_Handles); + context.PushConstantBlock(model.m_Handles); pcbOffset += sizeof model.m_Handles; - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof flags, &flags); + context.PushConstantBlock(pcbOffset, cameraBufferId); + pcbOffset += sizeof cameraBufferId; + context.PushConstantBlock(pcbOffset, lightsBufferId); + pcbOffset += sizeof lightsBufferId; + context.PushConstantBlock(pcbOffset, flags); pcbOffset += sizeof flags; for (auto &prim : model.m_MeshPrimitives) { u32 innerPcbOffset = pcbOffset; - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, innerPcbOffset, - sizeof prim.m_MaterialIdx, &prim.m_MaterialIdx); + context.PushConstantBlock(innerPcbOffset, prim.m_MaterialIdx); innerPcbOffset += sizeof prim.m_MaterialIdx; - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, innerPcbOffset, - sizeof prim.m_TransformIdx, &prim.m_TransformIdx); + context.PushConstantBlock(innerPcbOffset, prim.m_TransformIdx); innerPcbOffset += sizeof prim.m_TransformIdx; - cmd.drawIndexed(prim.m_IndexCount, 1, prim.m_FirstIndex, static_cast(prim.m_VertexOffset), 0); + context.DrawIndexed(prim.m_IndexCount, prim.m_FirstIndex, prim.m_VertexOffset); } - cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, backGroundPipeline.m_Pipeline); - cmd.draw(3, 1, 0, 0); + context.BindPipeline(backgroundPipeline); + context.Draw(3); - cmd.endRendering(); + context.EndRendering(); - cmd.pipelineBarrier2(&postRenderDependencies); + context.Dependency({.imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &attachmentRenderToBlitBarrier}); - vk::ImageBlit blitRegion = { + vk::ImageBlit2 blitRegion = { .srcSubresource = { .aspectMask = vk::ImageAspectFlagBits::eColor, @@ -670,33 +610,31 @@ main(int, char **) .dstOffsets = std::array{ vk::Offset3D{0, 0, 0}, - vk::Offset3D{static_cast(swapchain.m_Extent.width), static_cast(swapchain.m_Extent.height), 1}, + vk::Offset3D{static_cast(swapchainSize.m_Width), static_cast(swapchainSize.m_Height), 1}, }, }; - cmd.blitImage(currentImage, postRenderBarriers[0].newLayout, currentSwapchainImage, - postRenderBarriers[1].newLayout, 1, &blitRegion, vk::Filter::eLinear); - cmd.pipelineBarrier2(&preGuiDependencies); - - gui::Draw(cmd, swapchain.m_Extent, currentSwapchainImageView); - - cmd.pipelineBarrier2(&prePresentDependencies); - - AbortIfFailed(cmd.end()); - - vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eTransfer; - vk::SubmitInfo submitInfo = { - .waitSemaphoreCount = 1, - .pWaitSemaphores = ¤tFrame->m_ImageAcquireSem, - .pWaitDstStageMask = &waitDstStage, - .commandBufferCount = 1, - .pCommandBuffers = &cmd, - .signalSemaphoreCount = 1, - .pSignalSemaphores = ¤tFrame->m_RenderFinishSem, + vk::BlitImageInfo2 blit = { + .srcImage = currentImage, + .srcImageLayout = vk::ImageLayout::eTransferSrcOptimal, + .dstImage = currentSwapchainImage, + .dstImageLayout = vk::ImageLayout::eTransferDstOptimal, + .regionCount = 1, + .pRegions = &blitRegion, + .filter = vk::Filter::eLinear, }; - AbortIfFailed(graphicsQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence)); - currentFrame->Present(graphicsQueue, &swapchain, &surface, window.GetSize()); + context.Blit(blit); + + context.Dependency({.imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &swapchainBlitToGuiBarrier}); + + gui::Draw(currentFrame, context); + + context.Dependency({.imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &swapchainPrePresentBarrier}); + + context.End(); + + device.Present(currentFrame, context); } device.WaitIdle(); @@ -704,9 +642,7 @@ main(int, char **) pipelineCacheData = device.DumpPipelineCache(); ERROR_IF(!WriteFileBytes(PIPELINE_CACHE_FILE, pipelineCacheData), "Pipeline Cache incorrectly written"); - gui::Destroy(&device); - - device.m_Device.destroy(descriptorPool, nullptr); + gui::Destroy(device); return 0; } \ No newline at end of file diff --git a/samples/03_model_render/pipeline_utils.cpp b/samples/03_model_render/pipeline_utils.cpp index b970c25..7199440 100644 --- a/samples/03_model_render/pipeline_utils.cpp +++ b/samples/03_model_render/pipeline_utils.cpp @@ -11,6 +11,7 @@ #include "helpers.h" #include "aster/systems/commit_manager.h" +#include "aster/util/files.h" #include @@ -172,7 +173,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const systems: device->m_Device.destroy(vertexShaderModule, nullptr); device->m_Device.destroy(fragmentShaderModule, nullptr); - return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts)}; + return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts), Pipeline::Kind::eGraphics}; } Pipeline @@ -333,7 +334,7 @@ CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, cons device->m_Device.destroy(vertexShaderModule, nullptr); device->m_Device.destroy(fragmentShaderModule, nullptr); - return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts)}; + return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts), Pipeline::Kind::eGraphics}; } vk::ShaderModule diff --git a/samples/03_model_render/shader/slang/background.slang b/samples/03_model_render/shader/slang/background.slang new file mode 100644 index 0000000..fd20eaf --- /dev/null +++ b/samples/03_model_render/shader/slang/background.slang @@ -0,0 +1,57 @@ +import bindless; +import ibl_common; +import common_structs; + +const static float3 trianglePoints[] = { + float3(-1.0f, -1.0f, 1.0f), + float3(3.0f, -1.0f, 1.0f), + float3(-1.0f, 3.0f, 1.0f), +}; + +[vk::push_constant] +uniform Block pcb; + +struct VS_Output +{ + float4 vertexPosition : SV_Position; + float3 worldPosition : World_Position; +}; + +[shader("vertex")] +VS_Output vsmain( + uint vertexId : SV_VertexID +) +{ + VS_Output stageOutput; + stageOutput.vertexPosition = float4(trianglePoints[vertexId], 1.0f); + + float4 clipSpace = mul(float4(trianglePoints[vertexId], 1.0f), pcb.camera[0].invProj); + float4 worldSpace = mul(clipSpace / clipSpace.wwww, pcb.camera[0].invView); + stageOutput.worldPosition = worldSpace.xyz; + + return stageOutput; +} + +[shader("fragment")] +float4 fsmain(float3 worldPosition: World_Position) : SV_Target0 +{ + float3 direction = normalize(worldPosition - pcb.camera[0].position.xyz); +#ifndef _DEBUG + float4 color = pcb.lights[0].environment.Sample(direction); +#else + float4 color; + if (pcb.showDiffuse) + { + color = pcb.lights[0].diffuseIrradiance.Sample(direction); + } + else if (pcb.showPrefilter) + { + color = pcb.lights[0].prefilter.Sample(direction); + } + else + { + color = pcb.lights[0].environment.Sample(direction); + } +#endif + return float4(Uncharted2Tonemap(color.rgb), color.a); +} \ No newline at end of file diff --git a/samples/03_model_render/shader/slang/bindless.slang b/samples/03_model_render/shader/slang/bindless.slang new file mode 100644 index 0000000..9d4f350 --- /dev/null +++ b/samples/03_model_render/shader/slang/bindless.slang @@ -0,0 +1,29 @@ +[vk::binding(0, 0)] __DynamicResource<__DynamicResourceKind.General> gBuffers[]; +[vk::binding(1, 0)] __DynamicResource<__DynamicResourceKind.Sampler> gSamplers[]; +[vk::binding(2, 0)] __DynamicResource<__DynamicResourceKind.General> gStorageTextures[]; + +bool IsValid(DescriptorHandle handle) where T : IOpaqueDescriptor +{ + var handleVal = (uint2)handle; + return handleVal.x != 0xFFFFFFFF; +} + +export T getDescriptorFromHandle(DescriptorHandle handle) where T : IOpaqueDescriptor +{ + __target_switch + { + case spirv: + switch (T.kind) { + case DescriptorKind.Buffer: + return gBuffers[((uint2)handle).x].asOpaqueDescriptor(); + case DescriptorKind.CombinedTextureSampler: + return gSamplers[((uint2)handle).x].asOpaqueDescriptor(); + case DescriptorKind.Texture: + return gStorageTextures[((uint2)handle).x].asOpaqueDescriptor(); + default: + return defaultGetDescriptorFromHandle(handle); + } + default: + return defaultGetDescriptorFromHandle(handle); + } +} \ No newline at end of file diff --git a/samples/03_model_render/shader/slang/common_structs.slang b/samples/03_model_render/shader/slang/common_structs.slang new file mode 100644 index 0000000..084c951 --- /dev/null +++ b/samples/03_model_render/shader/slang/common_structs.slang @@ -0,0 +1,136 @@ +static const float HALF_PI = 1.57079633f; +static const float PI = 3.14159265f; +static const float TAU = 6.28318530f; + +struct VertexData +{ + float4 normal; + float2 uv0; + float2 uv1; + float4 color0; +}; + +struct TransformData +{ + float4x4 transform; + float4x4 normalTransform; +}; + +struct MaterialData +{ + float4 albedoFactor; + float4 emissionFactor; + Sampler2D.Handle albedoTex; + Sampler2D.Handle normalTex; + Sampler2D.Handle metalRoughTex; + Sampler2D.Handle occlusionTex; + Sampler2D.Handle emissionTex; + float metalFactor; + float roughFactor; +}; + +struct PointLight +{ + float3 Position; // 12 + float Range; // 16 + uint Color; // 20 + float Intensity; // 24 + uint pad0; + uint pad1; +}; + +struct DirectionalLight +{ + float3 Direction; + float Validity_; + uint Color; + float Intensity; + uint pad0; + uint pad1; +}; + +struct CameraData +{ + float4x4 view; // 64 + float4x4 proj; // 128 + float4x4 invView; // 192 + float4x4 invProj; // 256 + float4 position; // 272 +}; + +struct Indexer +{ + uint16_t count; + uint16_t offset; +}; + +struct Lighting +{ + SamplerCube.Handle environment; // 8 + SamplerCube.Handle diffuseIrradiance; // 16 + SamplerCube.Handle prefilter; // 24 + Sampler2D.Handle brdfLUT; // 32 + ByteAddressBuffer.Handle lights; // 40 + Indexer pointLightIndexer; // 44 + Indexer dirLightIndexer; // 48 +}; + +struct Block +{ + static const uint USE_DIFFUSE_BIT = 1; + static const uint USE_SPECULAR_BIT = 2; + static const uint SHOW_DIFFUSE_BIT = 4; + static const uint SHOW_PREFILTER_BIT = 8; + + StructuredBuffer.Handle vertexBuffer; // 8 + StructuredBuffer.Handle vertexData; // 16 + StructuredBuffer.Handle materialBuffer; // 24 + StructuredBuffer.Handle nodeBuffer; // 32 + StructuredBuffer.Handle camera; // 40 + StructuredBuffer.Handle lights; // 48 + + uint debugFlags; // 52 + + int materialIdx; // 56 + uint nodeIdx; // 60 + + property ignoreDiffuse : bool + { + get { + return (debugFlags & USE_DIFFUSE_BIT) == 0; + } + } + + property ignoreSpecular : bool + { + get { + return (debugFlags & USE_SPECULAR_BIT) == 0; + } + } + + property showDiffuse : bool + { + get { + return (debugFlags & SHOW_DIFFUSE_BIT) != 0; + } + } + + property showPrefilter : bool + { + get { + return (debugFlags & SHOW_PREFILTER_BIT) != 0; + } + } +}; + +float3 Uncharted2Tonemap(float3 color) +{ + float A = 0.15f; + float B = 0.50f; + float C = 0.10f; + float D = 0.20f; + float E = 0.02f; + float F = 0.30f; + float W = 11.2f; + return ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; +} \ No newline at end of file diff --git a/samples/03_model_render/shader/slang/environment.slang b/samples/03_model_render/shader/slang/environment.slang new file mode 100644 index 0000000..be3459e --- /dev/null +++ b/samples/03_model_render/shader/slang/environment.slang @@ -0,0 +1,114 @@ +import bindless; +import common_structs; +import ibl_common; + +/* +| Axis | Layer | Up | +|:----:|:-----:|:--:| +| +x | 0 | -y | +| -x | 1 | -y | +| +y | 2 | +z | +| -y | 3 | -z | +| -z | 4 | -y | +| +z | 5 | -y | +*/ + +struct DiffuseBlock { + SamplerCube.Handle skybox; + RWTexture2DArray.Handle outputTexture; + uint cubeSide; +}; + + +[shader("compute")] +[numthreads(16, 16, 1)] +void diffuseIrradiance( + uint3 GlobalInvocationID: SV_DispatchThreadID, + uniform DiffuseBlock diffusePcb, + ) +{ + float3 Forward, Up, Right; + + Forward = GetCubeDir(GlobalInvocationID, diffusePcb.cubeSide); + Up = abs(Forward.y) < 1.0f ? float3(0.0f, 1.0f, 0.0f) : float3(1.0f, 0.0f, 0.0f); // 0.01f offset to + Right = normalize(cross(Up, Forward)); + Up = normalize(cross(Forward, Right)); + + float3 Irradiance = 0.0f.xxx; + float3 IrrDirr = 0.0f.xxx; + float SampleStep = 0.005f; + float SampleCount = 0.0f; + + for (float Azimuth = 0.0f; Azimuth < TAU; Azimuth += SampleStep) + { + for (float Zenith = 0.0f; Zenith < HALF_PI; Zenith += SampleStep) + { + float3 DirectionTanSpace = float3(sin(Zenith) * cos(Azimuth), sin(Zenith) * sin(Azimuth), cos(Zenith)); + float3 DirectionWorld = DirectionTanSpace.x * Right + DirectionTanSpace.y * Up + DirectionTanSpace.z * Forward; + + Irradiance += diffusePcb.skybox.SampleLevel(DirectionWorld, 0).xyz * (cos(Zenith) * sin(Zenith)); + SampleCount++; + } + } + + diffusePcb.outputTexture[GlobalInvocationID.xyz] = PI * float4(Irradiance * (1.0f / SampleCount), 1.0f); +} + +struct PrefilterBlock { + SamplerCube.Handle skybox; + RWTexture2DArray.Handle outputTexture; + uint cubeSide; + float roughness; + uint envRes; +}; + +[shader("compute")] +[numthreads(16, 16, 1)] +void prefilter(uint3 GlobalInvocationID: SV_DispatchThreadID, uniform PrefilterBlock prefilterPcb) +{ + float3 Normal = GetCubeDir(GlobalInvocationID, prefilterPcb.cubeSide); + float3 ViewDir = Normal; + + const uint SAMPLE_COUNT = 2048u; + float TotalWeight = 0.0f; + float3 PrefilterColor = 0.0f.xxx; + for (uint i = 0u; i < SAMPLE_COUNT; ++i) + { + float2 Xi = Hammersley(i, SAMPLE_COUNT); + float3 Halfway = ImportanceSampleGGX(Xi, Normal, prefilterPcb.roughness); + float3 LightDir = normalize(2.0 * dot(ViewDir, Halfway) * Halfway - ViewDir); + + float NdotH = max(dot(Normal, Halfway), 0.0f); + + float MipLevel = GetSampleMipLevel(NdotH, NdotH /* N = V :: NdotH = HdotV */, SAMPLE_COUNT, prefilterPcb.roughness, prefilterPcb.envRes); + + float NdotL = max(dot(Normal, LightDir), 0.0); + if (NdotL > 0.0) + { + PrefilterColor += prefilterPcb.skybox.SampleLevel(LightDir, MipLevel).rgb * NdotL; + TotalWeight += NdotL; + } + } + PrefilterColor = PrefilterColor / TotalWeight; + + prefilterPcb.outputTexture[GlobalInvocationID] = float4(PrefilterColor, 1.0f); +} + +struct BrdfBlock { + RWTexture2D.Handle outputTexture; +}; + +[shader("compute")] +[numthreads(16, 16, 1)] +void brdfLut( + uint3 GlobalInvocationID: SV_DispatchThreadID, + uniform BrdfBlock brdfPcb) +{ + float Width, Height; + brdfPcb.outputTexture.GetDimensions(Width, Height); + + float2 UV = GlobalInvocationID.xy / float2(Width, Height); + + float2 IntegratedBRDF = IntegrateBRDF(UV.x, UV.y); + brdfPcb.outputTexture[GlobalInvocationID.xy] = IntegratedBRDF; +} \ No newline at end of file diff --git a/samples/03_model_render/shader/slang/eqrect_to_cube.slang b/samples/03_model_render/shader/slang/eqrect_to_cube.slang new file mode 100644 index 0000000..2a5017a --- /dev/null +++ b/samples/03_model_render/shader/slang/eqrect_to_cube.slang @@ -0,0 +1,31 @@ +import bindless; +import ibl_common; + +float2 SampleSphericalMap(float3 v) +{ + const float2 InvTan = float2(0.1591f, 0.3183f); // (1/2PI, 1/PI) + float2 UV = float2(atan2(-v.x, v.z), asin(-v.y)); // (-PI, -PI/2) to (PI, PI/2) + UV *= InvTan; // (-1/2, -1/2) to (1/2, 1/2) + UV += 0.5f.xx; // (0, 0) to (1, 1) + return UV; +} + +struct Block { + Sampler2D.Handle hdrEnv; + RWTexture2DArray.Handle outputTexture; + uint cubeSide; +}; + +[shader("compute")] +[numthreads(16, 16, 1)] +void main( + uint3 GlobalInvocationID: SV_DispatchThreadID, + uniform Block pcb +) +{ + float3 LocalDir = GetCubeDir(GlobalInvocationID, pcb.cubeSide); + + float2 UV = SampleSphericalMap(LocalDir); + + pcb.outputTexture[GlobalInvocationID.xyz] = pcb.hdrEnv.SampleLevel(UV, 0); +} \ No newline at end of file diff --git a/samples/03_model_render/shader/slang/ibl_common.slang b/samples/03_model_render/shader/slang/ibl_common.slang new file mode 100644 index 0000000..8c6ac51 --- /dev/null +++ b/samples/03_model_render/shader/slang/ibl_common.slang @@ -0,0 +1,151 @@ +import common_structs; + +float3 GetCubeDir(uint3 GlobalInvocationID, float SideLength) +{ + float2 FaceUV = float2(GlobalInvocationID.xy) / SideLength; // (0, SideLength) -> (0, 1) + FaceUV = 2.0f * FaceUV - 1.0f; // (0, 1) -> (-1, 1) + + switch (GlobalInvocationID.z) + { + case 0: + return normalize(float3(1.0f, -FaceUV.y, -FaceUV.x)); // Face +X; x = 1, y = -v, z = -u + case 1: + return normalize(float3(-1.0f, -FaceUV.y, FaceUV.x)); // Face -X; x = -1, y = -v, z = u + case 2: + return normalize(float3(FaceUV.x, 1.0f, FaceUV.y)); // Face +Y; x = u, y = 1, z = v + case 3: + return normalize(float3(FaceUV.x, -1.0f, -FaceUV.y)); // Face -Y; x=u, y=-1, z=-v + case 4: + return normalize(float3(FaceUV.x, -FaceUV.y, 1.0f)); // Face +Z; x=u,y=-v, z=1 + case 5: + return normalize(float3(-FaceUV.x, -FaceUV.y, -1.0f)); // Face -Z; x=u,y=-v, z=-1 + default: + // Never reach here. + return 0.0f.xxx; + } +} + +float RadicalInverse_VdC(uint Bits) +{ + Bits = (Bits << 16u) | (Bits >> 16u); + Bits = ((Bits & 0x55555555u) << 1u) | ((Bits & 0xAAAAAAAAu) >> 1u); + Bits = ((Bits & 0x33333333u) << 2u) | ((Bits & 0xCCCCCCCCu) >> 2u); + Bits = ((Bits & 0x0F0F0F0Fu) << 4u) | ((Bits & 0xF0F0F0F0u) >> 4u); + Bits = ((Bits & 0x00FF00FFu) << 8u) | ((Bits & 0xFF00FF00u) >> 8u); + return float(Bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +float2 Hammersley(uint SampleIndex, uint SampleCount) +{ + return float2(float(SampleIndex) / float(SampleCount), RadicalInverse_VdC(SampleIndex)); +} + +float3 ImportanceSampleGGX(float2 Xi, float3 Normal, float Roughness) +{ + float A = Roughness * Roughness; + + float Phi = 2.0f * PI * Xi.x; + float CosTheta = sqrt((1.0f - Xi.y) / (1.0f + (A * A - 1.0f) * Xi.y)); + float SinTheta = sqrt(1.0f - CosTheta * CosTheta); + + // from spherical coordinates to cartesian coordinates + float3 H; + H.x = cos(Phi) * SinTheta; + H.y = sin(Phi) * SinTheta; + H.z = CosTheta; + + // from tangent-space vector to world-space sample vector + float3 Up = abs(Normal.z) < 0.999f ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); + float3 Tangent = normalize(cross(Up, Normal)); + float3 Bitangent = cross(Normal, Tangent); + + float3 SampleVec = Tangent * H.x + Bitangent * H.y + Normal * H.z; + return normalize(SampleVec); +} + +float TrowbridgeReitzGGX(float NdotH, float Roughness) +{ + float NdotH = max(NdotH, 0.0f); + + float Coeff = Roughness * Roughness; + float Alpha2 = Coeff * Coeff; + float NdotH2 = NdotH * NdotH; + + float Numerator = Alpha2; + float Denominator = NdotH2 * (Alpha2 - 1.0f) + 1.0f; + Denominator = PI * Denominator * Denominator; + + return Numerator / Denominator; +} + +float GetSampleMipLevel(float NdotH, float HdotV, float SampleCount, float roughness, float envRes) +{ + float D = TrowbridgeReitzGGX(NdotH, roughness); + float Pdf = (D * NdotH / (4.0f * HdotV)) + 0.0001f; + + float SurfAreaTexel = 4.0f * PI / (6.0f * envRes * envRes); + float SurfAreaSample = 1.0f / (SampleCount * Pdf + 0.0001f); + + return roughness == 0.0f ? 0.0f : 0.5f * log2(SurfAreaSample / SurfAreaTexel); +} + +float GeometrySchlickGGX(float NdotV, float Roughness) +{ + float R = Roughness; + // (Rough + 1)^2 / 8 for Punctual Lights + // Rough^2 / 2 for IBL + float K = (R * R) / 2.0; + + float Numerator = NdotV; + float Denominator = NdotV * (1.0f - K) + K; + + return Numerator / Denominator; +} + +float GeometrySmith(float NdotV, float NdotL, float Roughness) +{ + float GGX1 = GeometrySchlickGGX(NdotV, Roughness); + float GGX2 = GeometrySchlickGGX(NdotL, Roughness); + + return GGX1 * GGX2; +} + +float2 IntegrateBRDF(float NdotV, float Roughness) +{ + float3 ViewDir; + ViewDir.x = sqrt(1.0f - NdotV * NdotV); + ViewDir.y = 0.0f; + ViewDir.z = NdotV; + + float A = 0.0f; + float B = 0.0f; + + float3 Normal = float3(0.0f, 0.0f, 1.0f); + + const uint SAMPLE_COUNT = 1024u; + + for (uint i = 0u; i < SAMPLE_COUNT; ++i) + { + float2 Xi = Hammersley(i, SAMPLE_COUNT); + float3 Halfway = ImportanceSampleGGX(Xi, Normal, Roughness); + float3 LightDir = normalize(2.0f * dot(ViewDir, Halfway) * Halfway - ViewDir); + + float NdotL = max(LightDir.z, 0.0f); + float NdotH = max(Halfway.z, 0.0f); + float VdotH = max(dot(ViewDir, Halfway), 0.0f); + + if (NdotL > 0.0f) + { + float G = GeometrySmith(NdotV, NdotL, Roughness); + float G_Vis = (G * VdotH) / max((NdotH * NdotV), 0.0001f); + float Fc = pow(1.0f - VdotH, 5.0f); + + A += (1.0f - Fc) * G_Vis; + B += Fc * G_Vis; + } + } + A /= float(SAMPLE_COUNT); + B /= float(SAMPLE_COUNT); + + return float2(A, B); +} diff --git a/samples/03_model_render/shader/slang/model.slang b/samples/03_model_render/shader/slang/model.slang new file mode 100644 index 0000000..6bfbaf8 --- /dev/null +++ b/samples/03_model_render/shader/slang/model.slang @@ -0,0 +1,377 @@ +import bindless; +import ibl_common; +import common_structs; + +struct ModelHandles +{ + StructuredBuffer.Handle vertexBuffer; // 8 + StructuredBuffer.Handle vertexData; // 16 + StructuredBuffer.Handle materialBuffer; // 24 + StructuredBuffer.Handle nodeBuffer; // 32 +}; + +[vk::push_constant] +uniform Block pcb; + +struct VS_Input +{ + uint vertexId : SV_VertexID; +}; + +struct VS_Output +{ + float4 worldPosition : POSITION; + float4 worldNormal : NORMAL; + float4 vertexColor : COLOR0; + float2 uv0 : TEXCOORD0; + float4 vertexPosition : SV_Position; +}; + +float2 GetUV(uint vertexId) +{ + return pcb.vertexData[vertexId].uv0.xy; +} + +float4 GetPosition(uint vertexId) +{ + return float4(pcb.vertexBuffer[vertexId].xyz, 1.0f); +} + +float4 GetNormal(uint vertexId) +{ + return pcb.vertexData[vertexId].normal; +} + +float4 GetColor(uint vertexId) +{ + return pcb.vertexData[vertexId].color0; +} + +float4x4 GetNodeTransform(uint nodeIdx) +{ + return pcb.nodeBuffer[nodeIdx].transform; +} + +float4x4 GetNormalTransform(uint nodeIdx) +{ + return pcb.nodeBuffer[nodeIdx].normalTransform; +} + +[shader("vertex")] +VS_Output vsmain(VS_Input stageInput) +{ + VS_Output stageOutput; + + float4 worldPosition = mul(GetPosition(stageInput.vertexId), GetNodeTransform(pcb.nodeIdx)); + float4 clipSpace = mul(worldPosition, pcb.camera[0].view); + + stageOutput.vertexPosition = mul(clipSpace, pcb.camera[0].proj); + stageOutput.worldPosition = worldPosition; + stageOutput.uv0 = GetUV(stageInput.vertexId); + stageOutput.vertexColor = GetColor(stageInput.vertexId); + + stageOutput.worldNormal = mul(GetNormal(stageInput.vertexId), GetNormalTransform(pcb.nodeIdx)); + return stageOutput; +} + +struct FS_Input +{ + float4 InPosition : POSITION; + float4 InNormal : NORMAL; + float4 InColor : COLOR0; + float2 InUV0 : TEXCOORD0; +}; + +struct FS_Output +{ + float4 ColorTarget : SV_Target0; +}; + +float4 GetAlbedo(float2 uv, float4 color) +{ + float4 albedoFactor = pcb.materialBuffer[NonUniformResourceIndex(pcb.materialIdx)].albedoFactor; + var albedoTex = pcb.materialBuffer[NonUniformResourceIndex(pcb.materialIdx)].albedoTex; + + return albedoFactor * color * (IsValid(albedoTex) ? albedoTex.Sample(uv) : 1.0f.xxxx); +} + +float GetOcclusion(float2 uv) +{ + var occlusionTex = pcb.materialBuffer[NonUniformResourceIndex(pcb.materialIdx)].occlusionTex; + + return IsValid(occlusionTex) ? occlusionTex.Sample(uv).r : 1.0f; +} + +float3 GetEmissive(float2 uv) +{ + float3 emissionFactor = pcb.materialBuffer[NonUniformResourceIndex(pcb.materialIdx)].emissionFactor.rgb; + var emissionTex = pcb.materialBuffer[NonUniformResourceIndex(pcb.materialIdx)].emissionTex; + + return emissionFactor * (IsValid(emissionTex) ? emissionTex.Sample(uv).rgb : 1.0f.xxx); +} + +float3 GetNormal(float3 position, float3 normal, float2 uv) +{ + float3 N = normalize(normal); + + var normalTex = pcb.materialBuffer[NonUniformResourceIndex(pcb.materialIdx)].normalTex; + if (!IsValid(normalTex)) + { + return N; + } + + float3 tangentSpaceNormal = normalTex.Sample(uv).xyz * 2.0f - 1.0f; + + float3 q1 = ddx(position); + float3 q2 = ddy(position); + float2 st1 = ddx(uv); + float2 st2 = ddy(uv); + + float3 T = normalize(q1 * st2.y - q2 * st1.y).xyz; + float3 B = -normalize(cross(N, T)); + float3x3 TBN = float3x3(T, B, N); // Construction is Row by Row. + + return normalize(mul(tangentSpaceNormal, TBN)); // Post multiple to avoid transpose. +} + +float2 GetMetalRough(float2 uv) +{ + var material = pcb.materialBuffer[NonUniformResourceIndex(pcb.materialIdx)]; + float2 metalRough = float2(material.metalFactor, material.roughFactor); + var metalRoughTex = pcb.materialBuffer[NonUniformResourceIndex(pcb.materialIdx)].metalRoughTex; + + return metalRough * (IsValid(metalRoughTex) ? metalRoughTex.Sample(uv).bg : 1.0f.xx); // Metal is B, Rough is G. +} + +float3 SampleIrradiance(float3 direction) +{ + var diffuseIrradiance = pcb.lights[0].diffuseIrradiance; + if (IsValid(diffuseIrradiance)) + { + return diffuseIrradiance.Sample(direction).rgb; + } + return 0.04f.xxx; +} + +float3 SamplePrefiltered(float3 direction, float roughness) +{ + var prefilter = pcb.lights[0].prefilter; + if (!IsValid(prefilter)) + return 0.0f.xxx; + + const float MAX_MIP_LEVEL = 5.0f; + float mip = MAX_MIP_LEVEL * roughness; + + return prefilter.SampleLevel(direction, mip).rgb; +} + +float2 SampleBrdfLut(float nDotV, float roughness) +{ + var brdfLut = pcb.lights[0].brdfLUT; + if (IsValid(brdfLut)) + { + return brdfLut.Sample(float2(nDotV, roughness)); + } + return 0.0f.xx; +} + +// https://en.wikipedia.org/wiki/Schlick%27s_approximation +float3 FresnelSchlick(float cosine, float3 F_0) +{ + return F_0 + (1.0f - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. +} + +// Sebastian Lagarde +float3 FresnelSchlickRoughness(float cosine, float3 F_0, float Roughness) +{ + return F_0 + (max((1.0f - Roughness).xxx, F_0) - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. +} + +float3 GetPBRContrib(float3 Albedo, float3 LightColor, float3 ViewDir, float3 Normal, float Metallic, float Roughness, float3 F_0, float3 LightDir, float LightDistance) +{ + float Attenuation = 1.0f / (LightDistance * LightDistance); // TODO: Controlled Attenuation + float3 Halfway = normalize(ViewDir + LightDir); + + // If the dot is negative, then it we just us 0. + // Light from the back is irrelevant. + float CosineFactor = max(dot(Halfway, ViewDir), 0.0f); + float NdotV = max(dot(Normal, ViewDir), 0.0f); + float NdotL = max(dot(Normal, LightDir), 0.0f); + float NdotH = max(dot(Normal, Halfway), 0.0f); + + float3 Radiance = LightColor * Attenuation; + + float NormalDistribution = TrowbridgeReitzGGX(NdotH, Roughness); + float Geometry = GeometrySmith(NdotV, NdotL, Roughness); + float3 Fresnel = FresnelSchlickRoughness(CosineFactor, F_0, Roughness); + + float3 Numerator = (NormalDistribution * Geometry) * Fresnel; + float Denominator = 4.0f * NdotV * NdotL; + float3 Specular = Numerator / (Denominator + 0.00001f); + + float3 K_Specular = Fresnel; + float3 K_Diffuse = 1.0f.xxx - K_Specular; + + K_Diffuse *= 1.0f - Metallic; + + return NdotL * Radiance * (K_Diffuse * Albedo / PI + Specular); +} + +float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal) +{ + var lightData = pcb.lights[0]; + var pointLightBuffer = (StructuredBuffer.Handle)lightData.lights; + + if (!IsValid(pointLightBuffer)) + return 0.0f.xxx; + + uint offset = lightData.pointLightIndexer.offset; + uint count = lightData.pointLightIndexer.count; + + float3 ViewDir = normalize(pcb.camera[0].position.xyz - Position); + + float Metallic = MetalRough.r; + float Roughness = MetalRough.g; + + // Dielectric F_0 based on LearnOpenGL. + // TODO: Cite + float3 F_0 = 0.04f.xxx; + F_0 = lerp(F_0, Albedo, Metallic); + + float3 Contrib = 0.0f; + for (uint i = 0; i < count; ++i) + { + PointLight Light = pointLightBuffer[i + offset]; + + if (Light.Range < 0.0f) + continue; + + float3 LightDir = Light.Position - Position; + float LightDistance = length(LightDir); + + if (LightDistance > Light.Range) + continue; + + LightDir /= LightDistance; // Normalization + + // Color Unpack + float R = (Light.Color & 0xFF000000) >> 24; + float G = (Light.Color & 0x00FF0000) >> 16; + float B = (Light.Color & 0x0000FF00) >> 8; + + float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 + + Contrib += GetPBRContrib(Albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, LightDistance); + } + + return Contrib; +} + +float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal) +{ + var lightData = pcb.lights[0]; + var dirLightBuffer = (StructuredBuffer.Handle)lightData.lights; + + if (!IsValid(dirLightBuffer)) + return 0.0f.xxx; + + uint count = lightData.dirLightIndexer.count; + + float3 ViewDir = normalize(pcb.camera[0].position.xyz - Position); + + float Metallic = MetalRough.r; + float Roughness = MetalRough.g; + + // Dielectric F_0 based on LearnOpenGL. + // TODO: Cite + float3 F_0 = 0.04f.xxx; + F_0 = lerp(F_0, Albedo, Metallic); + + float3 Contrib = 0.0f; + for (uint i = 0; i < count; ++i) + { + DirectionalLight Light = dirLightBuffer[i]; + + if (Light.Validity_ < 0.0f) + continue; + + float3 LightDir = -normalize(float3(Light.Direction)); + + // Color Unpack + float R = (Light.Color & 0xFF000000) >> 24; + float G = (Light.Color & 0x00FF0000) >> 16; + float B = (Light.Color & 0x0000FF00) >> 8; + + float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 + + Contrib += GetPBRContrib(Albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, 1.0f); + } + + return Contrib; +} + +float3 GetAmbientInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal, float Occlusion) +{ + float3 ViewDir = normalize(pcb.camera[0].position.xyz - Position); + float CosineFactor = max(dot(Normal, ViewDir), 0.0f); // Normal instead of Halfway since there's no halfway in ambient. + + float Metal = MetalRough.r; + float Roughness = MetalRough.g; + + float3 F_0 = 0.04f.xxx; + F_0 = lerp(F_0, Albedo, MetalRough.r); + float3 K_Specular = FresnelSchlickRoughness(CosineFactor, F_0, Roughness); + float3 K_Diffuse = 1.0f.xxx - K_Specular; + + K_Diffuse *= 1.0f - Metal; // Metals don't have diffuse/refractions. + + float3 ReflectionDir = reflect(-ViewDir, Normal); + + float NdotV = max(dot(Normal, ViewDir), 0.0f); + float3 PrefilteredColor = SamplePrefiltered(ReflectionDir, Roughness).rgb; + float2 EnvBRDF = SampleBrdfLut(NdotV, Roughness); + float3 Specular = PrefilteredColor * (K_Specular * EnvBRDF.x + EnvBRDF.y); + + float3 DiffuseIrradiance = Albedo * SampleIrradiance(Normal); +#if defined(_DEBUG) + if (pcb.ignoreDiffuse) { + DiffuseIrradiance = 0.0f.xxx; + } + if (pcb.ignoreSpecular) { + Specular = 0.0f.xxx; + } +#endif + + return (K_Diffuse * DiffuseIrradiance + Specular) * Occlusion; +} + +[shader("fragment")] +FS_Output fsmain(FS_Input StageInput) +{ + FS_Output Output; + + // TODO: This should be invalid on the CPU side. + if (pcb.materialIdx < 0) + { + Output.ColorTarget = float4(1.0f, 0.0f, 1.0f, 1.0f); + return Output; + } + + float3 Position = StageInput.InPosition.xyz; + float3 Normal = GetNormal(Position, StageInput.InNormal.xyz, StageInput.InUV0); + + float4 AlbedoAlpha = GetAlbedo(StageInput.InUV0, StageInput.InColor); + float3 Albedo = AlbedoAlpha.rgb; + float Alpha = AlbedoAlpha.a; + float2 MetalRough = GetMetalRough(StageInput.InUV0); + float3 Emission = GetEmissive(StageInput.InUV0); + float Occlusion = GetOcclusion(StageInput.InUV0); + + float3 DirectionalLightLum = GetDirectionalLightInfluence(Albedo, MetalRough, Position, Normal); + float3 PointLighLum = GetPointLightInfluence(Albedo, MetalRough, Position, Normal); + float3 AmbientLum = GetAmbientInfluence(Albedo, MetalRough, Position, Normal, Occlusion); + float3 Color = DirectionalLightLum + /*PointLighLum +*/ AmbientLum; + + Output.ColorTarget = float4(Uncharted2Tonemap(Color + Emission), Alpha); + return Output; +} diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 33fa944..a20107f 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -5,5 +5,5 @@ cmake_minimum_required(VERSION 3.13) add_subdirectory("00_util") add_subdirectory("01_triangle") add_subdirectory("02_box") -# add_subdirectory("03_model_render") +add_subdirectory("03_model_render") # add_subdirectory("04_scenes") diff --git a/vcpkg b/vcpkg index 0ca64b4..f26ec39 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 0ca64b4e1c70fa6d9f53b369b8f3f0843797c20c +Subproject commit f26ec398c25c4980f33a50391f00a75f7ad62ef7 diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json index b01bcf0..6b7001c 100644 --- a/vcpkg-configuration.json +++ b/vcpkg-configuration.json @@ -1,7 +1,7 @@ { "default-registry": { "kind": "git", - "baseline": "41c447cc210dc39aa85d4a5f58b4a1b9e573b3dc", + "baseline": "f26ec398c25c4980f33a50391f00a75f7ad62ef7", "repository": "https://github.com/microsoft/vcpkg" }, "registries": [ diff --git a/vcpkg.json b/vcpkg.json index bb1b95a..4ce8d85 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -16,6 +16,7 @@ "tinygltf", "vulkan-memory-allocator", "entt", - "shader-slang" + "shader-slang", + "foonathan-memory" ] }