Model Render updated.

This commit is contained in:
Anish Bhobe 2025-05-24 23:01:50 +02:00
parent befa36c7f1
commit 4f71df797c
33 changed files with 2023 additions and 820 deletions

View File

@ -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.

View File

@ -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 *;

View File

@ -81,6 +81,7 @@ struct PhysicalDevice final
eastl::vector<vk::PresentModeKHR> m_PresentModes;
eastl::vector<QueueFamilyInfo> m_QueueFamilies;
PhysicalDevice() = default;
PhysicalDevice(vk::SurfaceKHR surface, vk::PhysicalDevice physicalDevice);
};

View File

@ -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<vk::DescriptorSetLayout> m_SetLayouts;
Kind m_Kind;
Pipeline() = default;
Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline,
eastl::vector<vk::DescriptorSetLayout> &&setLayouts);
eastl::vector<vk::DescriptorSetLayout> &&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;
}
};

View File

@ -5,6 +5,7 @@
#pragma once
#include "EASTL/span.h"
#include "context.h"
#include <aster/aster.h>
@ -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> &buffer, usize size, const void *data);
void UploadTexture(const Ref<Image> &image, const ImageData &data);
public:
void UploadTexture(const Ref<Image> &image, const eastl::span<u8> &data);
void
UploadBuffer(const Ref<Buffer> &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> &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> &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 <std::derived_from<ContextPool> 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<TransferContextEntry>;
using ContextListType = eastl::intrusive_list<ContextListEntry>;
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<TContextPool, TransferContextPool>
{
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<TransferContextEntry *>(m_TransferContextMemoryPool.allocate_node());
auto pool = TransferContextPool{*m_Device, m_QueueFamilyIndex, ContextPool::ManagedBy::eDevice};
ContextListEntry &entry = *static_cast<ContextListEntry *>(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<TContextPool, ComputeContextPool>
{
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<ContextListEntry *>(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<TransferContextPool>;
using OrderlessComputeContextPool = OrderlessContextPool<ComputeContextPool>;
} // namespace _internal
} // namespace systems

View File

@ -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<VertexInput, 4, false> m_VertexInputs;
eastl::fixed_vector<ShaderInfo, 4, false> 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<Frame, MAX_FRAMES_IN_FLIGHT> m_Frames;
u32 m_CurrentFrameIdx = 0;
@ -416,6 +451,7 @@ class Device final
// ----------------------------------------------------------------------------------------------------
[[nodiscard]] Ref<StorageBuffer> CreateStorageBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Ref<IndexBuffer> CreateIndexBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Ref<UniformBuffer> CreateUniformBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Ref<StagingBuffer> CreateStagingBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Ref<VertexBuffer> CreateVertexBuffer(usize size, cstr name = nullptr);
@ -464,7 +500,7 @@ class Device final
[[nodiscard]] Ref<T>
CreateTexture2DWithView(const Texture2DCreateInfo &createInfo)
{
auto handle = CreateTexture2D(createInfo);
auto handle = CreateTexture2DWithView(createInfo);
return CastView<T>(handle);
}
@ -472,7 +508,7 @@ class Device final
[[nodiscard]] Ref<T>
CreateTextureCubeWithView(const TextureCubeCreateInfo &createInfo)
{
auto handle = CreateTextureCube(createInfo);
auto handle = CreateTextureCubeWithView(createInfo);
return CastView<T>(handle);
}
@ -503,12 +539,16 @@ class Device final
PipelineCreationError
CreateShaders(eastl::fixed_vector<vk::PipelineShaderStageCreateInfo, ShaderTypeCount, false> &shadersOut,
Slang::ComPtr<slang::IComponentType> &program, const std::span<const ShaderInfo> &shaders);
systems::PipelineCreationError
CreateShader(vk::PipelineShaderStageCreateInfo &shadersOut, Slang::ComPtr<slang::IComponentType> &program,
const ShaderInfo &shaders);
PipelineCreationError
CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, const Slang::ComPtr<slang::IComponentType> &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<Swapchain::FnResizeCallback>(callback));
}
void Present(Frame &frame, GraphicsContext &graphicsContext);
//
@ -532,6 +578,7 @@ class Device final
friend TransferContext;
TransferContext CreateTransferContext();
ComputeContext CreateComputeContext();
Receipt Submit(Context &context);
//

View File

@ -27,7 +27,7 @@ CastBuffer(const Ref<TFrom> &from)
{
if constexpr (not concepts::BufferInto<TFrom, TTo>)
assert(TTo::FLAGS & from->m_Flags);
return std::reinterpret_pointer_cast<TTo>(from);
return eastl::reinterpret_pointer_cast<TTo>(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}

View File

@ -7,17 +7,21 @@
#include "core/device.h"
Pipeline::Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline,
eastl::vector<vk::DescriptorSetLayout> &&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<vk::DescriptorSetLayout> &&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);

View File

@ -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> &vertexBuffer
}
void
systems::GraphicsContext::BindPipeline(const Pipeline &pipeline)
systems::GraphicsContext::BindIndexBuffer(const Ref<IndexBuffer> &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<u32>(indexCount), 1, 0, 0, 0);
}
void
systems::GraphicsContext::DrawIndexed(const usize indexCount, const usize firstIndex, const usize firstVertex)
{
m_Cmd.drawIndexed(static_cast<u32>(indexCount), 1, static_cast<u32>(firstIndex), static_cast<i32>(firstVertex), 0);
}
void
systems::GraphicsContext::BeginRendering(const vk::RenderingInfo &renderingInfo)
{
@ -227,18 +279,18 @@ systems::GraphicsContext::EndRendering()
}
void
systems::TransferContext::UploadTexture(const Ref<Image> &image, const ImageData &data)
systems::TransferContext::UploadTexture(const Ref<Image> &image, const eastl::span<u8> &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<u64>(w) * static_cast<u64>(h) * static_cast<u64>(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> stagingBuffer = m_Pool->GetDevice().CreateStagingBuffer(data.m_NumBytes);
stagingBuffer->Write(0, data.m_NumBytes, data.m_Data);
const Ref<StagingBuffer> 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> &image, const ImageData
KeepAlive(image);
}
void
systems::TransferContext::UploadBuffer(const Ref<Buffer> &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> 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<u32>(offset),
static_cast<u32>(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()
{

View File

@ -17,10 +17,65 @@
#include <EASTL/vector_map.h>
#include <fmt/ranges.h>
#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<StorageBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
}
Ref<IndexBuffer>
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<IndexBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
}
Ref<UniformBuffer>
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<vk::VertexInputBindingDescription, 4, false> inputBindingDescriptions;
eastl::fixed_vector<vk::VertexInputAttributeDescription, 4, false> 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<vk::PipelineShaderStageCreateInfo, ShaderTypeCount, false> shaders;
Slang::ComPtr<slang::IComponentType> 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<slang::IModule> shaderModule;
shaderModule = m_SlangSession->loadModule(shaderInfo.m_ShaderFile.data(), shaderDiagnostics.writeRef());
if (shaderDiagnostics)
if (!shaderModule)
{
ERROR("{}", static_cast<cstr>(shaderDiagnostics->getBufferPointer()));
return SLANG_FAIL;
}
WARN_IF(shaderDiagnostics, "{}", static_cast<cstr>(shaderDiagnostics->getBufferPointer()));
components.push_back(shaderModule);
@ -620,12 +736,21 @@ systems::Device::CreateShaders(
ComPtr<slang::IEntryPoint> 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<cstr>(shaderDiagnostics->getBufferPointer()));
return SLANG_FAIL;
}
WARN_IF(shaderDiagnostics, "{}", static_cast<cstr>(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<cstr>(shaderDiagnostics->getBufferPointer()));
return slangResult;
}
WARN_IF(shaderDiagnostics, "{}", static_cast<cstr>(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<cstr>(shaderDiagnostics->getBufferPointer()));
return slangResult;
}
WARN_IF(shaderDiagnostics, "{}", static_cast<cstr>(shaderDiagnostics->getBufferPointer()));
if (auto progLayout = program->getLayout(0))
{
@ -789,12 +909,13 @@ systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout,
ComPtr<slang::IBlob> layoutDiagnostics;
slang::ProgramLayout *layout = program->getLayout(0, layoutDiagnostics.writeRef());
if (layoutDiagnostics)
if (!layout)
{
ERROR_IF(!layout, "{}", static_cast<cstr>(layoutDiagnostics->getBufferPointer()));
return SLANG_FAIL;
ERROR("{}", static_cast<cstr>(layoutDiagnostics->getBufferPointer()));
}
WARN_IF(layoutDiagnostics, "{}", static_cast<cstr>(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<u32>(compilerOptions.size()),
};
@ -974,6 +1093,12 @@ systems::Device::Device(const DeviceCreateInfo &createInfo)
.targetCount = 1,
.searchPaths = createInfo.m_ShaderSearchPaths.data(),
.searchPathCount = static_cast<u32>(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 &currentFrame = 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)
{

View File

@ -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 <imgui_impl_glfw.h>
@ -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<vk::DescriptorPoolSize> 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<u32>(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<u32>(device.m_Swapchain.m_Images.size()),
.ImageCount = static_cast<u32>(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()
{

View File

@ -6,6 +6,7 @@
#pragma once
#include "aster/aster.h"
#include "aster/core/device.h"
#include <imgui.h>
@ -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();

View File

@ -34,6 +34,12 @@ struct ImageFile
bool Load(cstr fileName);
[[nodiscard]] usize GetSize() const;
operator eastl::span<u8>() const
{
return {static_cast<u8*>(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<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
ubo->Write(0, sizeof camera, &camera);
auto& currentFrame = device.GetNextFrame();
auto &currentFrame = device.GetNextFrame();
prevSwapchainSize = swapchainSize;
swapchainSize = currentFrame.m_SwapchainSize;

View File

@ -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)

View File

@ -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 <EASTL/fixed_vector.h>
#include <EASTL/hash_map.h>
@ -37,10 +37,20 @@ VectorToVec4(const std::vector<double> &vec)
return vec4{0.0f};
}
assert(vec.size() == 4);
return {vec[0], vec[1], vec[2], vec[3]};
}
vec4
VectorToVec4(const std::vector<double> &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<double> &vec)
{
@ -65,7 +75,7 @@ AssetLoader::LoadHdrImage(cstr path, cstr name) const
u32 width = static_cast<u32>(x);
u32 height = static_cast<u32>(y);
auto texture = m_ResourceManager->CombinedImageViews().CreateTexture2D<TextureView>({
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 = &copyRegion,
};
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<u8 *>(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<u32>));
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> &texture, vk::ImageLayout initialLayout,
GenerateMipMaps(systems::TransferContext &context, const Ref<Texture> &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> &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> &texture, vk
// Mip Mapping
commandBuffer.pipelineBarrier2(&imageStartDependency);
context.Dependency(imageStartDependency);
i32 prevMipWidth = static_cast<i32>(texture->m_Extent.width);
i32 prevMipHeight = static_cast<i32>(texture->m_Extent.height);
@ -354,21 +317,21 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, const Ref<Texture> &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<systems::ResId<TextureView>, Ref<StagingBuffer>>
AssetLoader::LoadImageToGpu(tinygltf::Image *image, bool isSrgb, cstr name) const
systems::ResId<TextureView>
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<u32>(image->height);
u32 width = static_cast<u32>(image->width);
vk::Format imageFormat = isSrgb ? vk::Format::eR8G8B8A8Srgb : vk::Format::eR8G8B8A8Unorm;
usize byteSize = image->image.size();
auto texture = m_ResourceManager->Images().CreateTexture2D<Texture>({
auto texture = m_Device->CreateTexture2D<Texture>({
.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<u32>(image->width),
.bufferImageHeight = static_cast<u32>(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<TextureView>(
auto textureView = m_Device->CreateView<TextureView>(
{.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<Ref<StagingBuffer>> stagingBuffers;
context.BeginDebugRegion(loadActionName.c_str());
eastl::hash_map<i32, systems::ResId<TextureView>> 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<TextureView> {
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<f32>(material.pbrMetallicRoughness.metallicFactor),
.m_RoughFactor = static_cast<f32>(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<f32>(material.pbrMetallicRoughness.metallicFactor),
.m_RoughFactor = static_cast<f32>(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<Buffer> nodeHandle = m_CommitManager->CommitBuffer(nodeBuffer);
systems::ResId<Buffer> nodeHandle = m_Device->m_CommitManager->CommitBuffer(nodeBuffer);
#pragma region Staging / Transfer / Uploads
systems::ResId<Buffer> positionBufferHandle = systems::ResId<Buffer>::Null();
systems::ResId<Buffer> vertexDataHandle = systems::ResId<Buffer>::Null();
Ref<Buffer> indexBuffer;
Ref<IndexBuffer> indexBuffer;
{
auto uploadBufferData = [cmd = this->m_CommandBuffer, &stagingBuffers, resMan = this->m_ResourceManager,
pDevice](const Ref<Buffer> &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_ResourceManager->Buffers().CreateStorageBuffer(vertexData.size() * sizeof vertexData[0]);
vertexDataHandle = m_CommitManager->CommitBuffer(vertexDataBuffer);
uploadBufferData(vertexDataBuffer, vertexData.data());
auto vertexDataBuffer = m_Device->CreateStorageBuffer(vertexData.size() * sizeof vertexData[0]);
vertexDataHandle = m_Device->m_CommitManager->CommitBuffer(vertexDataBuffer);
context.UploadBuffer(vertexDataBuffer, vertexData);
// TODO: Index buffer needs to be separated.
indexBuffer =
m_ResourceManager->Buffers().CreateStorageBuffer(indices.size() * sizeof indices[0], "Index Buffer");
uploadBufferData(indexBuffer, indices.data());
}
indexBuffer = systems::CastBuffer<IndexBuffer>(
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<u32>));
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<systems::ResId<TextureView>> &textureHandles,
Nodes &&nodes, Ref<Buffer> nodeBuffer, ModelHandles &handles, Ref<Buffer> indexBuffer,
const eastl::vector<MeshPrimitive> &meshPrimitives)
: m_ResourceManager(resourceManager)
, m_TextureHandles(std::move(textureHandles))
Model::Model(eastl::vector<systems::ResId<TextureView>> &textureHandles, Nodes &&nodes, Ref<Buffer> nodeBuffer,
ModelHandles &handles, Ref<IndexBuffer> indexBuffer, const eastl::vector<MeshPrimitive> &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)
{
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(systems::Device &device)
: m_Device{&device}
{
}
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;
}

View File

@ -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<TextureView> m_AlbedoTex; // 04 40
systems::ResId<TextureView> m_NormalTex; // 04 44
systems::ResId<TextureView> m_MetalRoughTex; // 04 48
systems::ResId<TextureView> m_OcclusionTex; // 04 52
systems::ResId<TextureView> m_EmissionTex; // 04 56
vec4 m_EmissionFactor; // 16 32
systems::ResId<TextureView> m_AlbedoTex; // 08 40
systems::ResId<TextureView> m_NormalTex; // 08 48
systems::ResId<TextureView> m_MetalRoughTex; // 08 56
systems::ResId<TextureView> m_OcclusionTex; // 08 64
systems::ResId<TextureView> 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<systems::ResId<TextureView>> m_TextureHandles;
Nodes m_Nodes;
@ -81,15 +83,15 @@ struct Model
} m_Handles;
Ref<Buffer> m_NodeBuffer;
Ref<Buffer> m_IndexBuffer;
Ref<IndexBuffer> m_IndexBuffer;
eastl::vector<MeshPrimitive> m_MeshPrimitives;
[[nodiscard]] const mat4 &GetModelTransform() const;
void SetModelTransform(const mat4 &transform);
void Update();
Model(systems::CommitManager *resourceManager, eastl::vector<systems::ResId<TextureView>> &textureHandles,
Nodes &&nodes, Ref<Buffer> nodeBuffer, ModelHandles &handles, Ref<Buffer> indexBuffer,
Model(eastl::vector<systems::ResId<TextureView>> &textureHandles,
Nodes &&nodes, Ref<Buffer> nodeBuffer, ModelHandles &handles, Ref<IndexBuffer> indexBuffer,
const eastl::vector<MeshPrimitive> &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<TextureView> 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<systems::ResId<TextureView>, Ref<StagingBuffer>>
LoadImageToGpu(tinygltf::Image *image, bool isSrgb, cstr name = nullptr) const;
private:
systems::ResId<TextureView>
LoadImageToGpu(systems::TransferContext &context, tinygltf::Image *image, bool isSrgb, cstr name = nullptr) const;
};
void
GenerateMipMaps(vk::CommandBuffer commandBuffer, const Ref<Texture> &textureView, vk::ImageLayout initialLayout,
GenerateMipMaps(systems::TransferContext &context, const Ref<Texture> &textureView, vk::ImageLayout initialLayout,
vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage);
void
GenerateMipMaps(vk::CommandBuffer commandBuffer, concepts::ImageRefTo<Texture> 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>(texture), initialLayout, finalLayout, prevStage,
finalStage);
}
void
GenerateMipMaps(vk::CommandBuffer commandBuffer, concepts::ViewRefTo<Texture> auto &texture,
GenerateMipMaps(systems::TransferContext &context, concepts::ImageRefTo<Texture> 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>(texture->m_Image), initialLayout, finalLayout, prevStage,
GenerateMipMaps(context, systems::CastImage<Texture>(texture), initialLayout, finalLayout, prevStage,
finalStage);
}
void
GenerateMipMaps(systems::TransferContext &context, concepts::ViewRefTo<Texture> 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>(texture->m_Image), initialLayout, finalLayout, prevStage,
finalStage);
}

View File

@ -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 <EASTL/fixed_vector.h>
#include <EASTL/tuple.h>
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<TextureView> hdrEnv, const cstr name)
CreateCubeFromHdrEnv(AssetLoader &assetLoader, const u32 cubeSide, systems::ResId<TextureView> 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<StorageTextureCubeView>({
auto skybox = device.CreateTextureCubeWithView<StorageTextureCubeView>({
.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<StorageTextureCubeView>({
auto diffuseIrradiance = device.CreateTextureCubeWithView<StorageTextureCubeView>({
.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<StorageTextureCubeView>({
auto prefilterCube = device.CreateTextureCubeWithView<StorageTextureCubeView>({
.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<StorageTextureCubeView>({
auto view = device.CreateView<StorageTextureCubeView>({
.m_Image = systems::CastImage<StorageTextureCube>(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<StorageTextureView>({
auto brdfLut = device.CreateTexture2DWithView<StorageTextureView>({
.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<u32>(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<u32>(
// 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<vk::Pipeline, computePipelineCreateInfo.size()> pipelines;
AbortIfFailed(
pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, static_cast<u32>(computePipelineCreateInfo.size()),
computePipelineCreateInfo.data(), nullptr, pipelines.data()));
// eastl::array<vk::Pipeline, computePipelineCreateInfo.size()> pipelines;
// AbortIfFailed(pDevice->m_Device.createComputePipelines(
// pDevice->m_PipelineCache, static_cast<u32>(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 =
{
pDevice->m_Device.destroy(createInfos.stage.module, nullptr);
.m_ShaderFile = EQUIRECT_TO_CUBE_SHADER_FILE,
.m_EntryPoints = {"main"},
},
.m_Name = "EqRect -> Cubemap",
}))
{
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 : "<unknown env>";
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<f32>(mipCount) / static_cast<f32>(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<u32>));
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,

View File

@ -23,5 +23,4 @@ struct Environment
systems::ResId<TextureView> m_BrdfLut;
};
Environment CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide,
systems::ResId<TextureView> hdrEnv, cstr name = nullptr);
Environment CreateCubeFromHdrEnv(AssetLoader &assetLoader, u32 cubeSide, systems::ResId<TextureView> hdrEnv);

View File

@ -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<u16>(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());
}
}

View File

@ -13,6 +13,11 @@
#include <EASTL/vector.h>
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<Light> 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;

View File

@ -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 <EASTL/array.h>
#include <aster/systems/commit_manager.h>
#include <aster/systems/resource_manager.h>
#include <aster/systems/device.h>
#include <tiny_gltf.h>
#include <filesystem>
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<u32>(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<f32>(swapchain.m_Extent.width) / static_cast<f32>(swapchain.m_Extent.height)};
static_cast<f32>(swapchainSize.m_Width) /
static_cast<f32>(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<u32>(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<u32>(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<vk::ImageMemoryBarrier2> &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<Ref<ImageView>, MAX_FRAMES_IN_FLIGHT> depthImages;
eastl::fixed_vector<Ref<ImageView>, 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<u32>(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<f32>(extent.width) / static_cast<f32>(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<i32>(cameraController.m_AspectRatio * static_cast<f32>(inputResolution.height));
inputResolution.width =
static_cast<i32>(cameraController.m_AspectRatio * static_cast<f32>(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<f32>(internalResolution.width);
viewport.height = -static_cast<f32>(internalResolution.height);
viewport.y = static_cast<f32>(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 &currentFrame = 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 &currentDepthImage = depthImages[currentFrame->m_FrameIdx];
auto &currentAttachment = attachmentImages[currentFrame->m_FrameIdx];
auto &currentDepthImage = depthImages[currentFrame.m_FrameIdx];
auto &currentAttachment = 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<u32>(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<i32>(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<i32>(swapchain.m_Extent.width), static_cast<i32>(swapchain.m_Extent.height), 1},
vk::Offset3D{static_cast<i32>(swapchainSize.m_Width), static_cast<i32>(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 = &currentFrame->m_ImageAcquireSem,
.pWaitDstStageMask = &waitDstStage,
.commandBufferCount = 1,
.pCommandBuffers = &cmd,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &currentFrame->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;
}

View File

@ -11,6 +11,7 @@
#include "helpers.h"
#include "aster/systems/commit_manager.h"
#include "aster/util/files.h"
#include <EASTL/array.h>
@ -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

View File

@ -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);
}

View File

@ -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<T>(DescriptorHandle<T> handle) where T : IOpaqueDescriptor
{
var handleVal = (uint2)handle;
return handleVal.x != 0xFFFFFFFF;
}
export T getDescriptorFromHandle<T>(DescriptorHandle<T> handle) where T : IOpaqueDescriptor
{
__target_switch
{
case spirv:
switch (T.kind) {
case DescriptorKind.Buffer:
return gBuffers[((uint2)handle).x].asOpaqueDescriptor<T>();
case DescriptorKind.CombinedTextureSampler:
return gSamplers[((uint2)handle).x].asOpaqueDescriptor<T>();
case DescriptorKind.Texture:
return gStorageTextures[((uint2)handle).x].asOpaqueDescriptor<T>();
default:
return defaultGetDescriptorFromHandle(handle);
}
default:
return defaultGetDescriptorFromHandle(handle);
}
}

View File

@ -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<float2>.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<float4>.Handle vertexBuffer; // 8
StructuredBuffer<VertexData>.Handle vertexData; // 16
StructuredBuffer<MaterialData>.Handle materialBuffer; // 24
StructuredBuffer<TransformData>.Handle nodeBuffer; // 32
StructuredBuffer<CameraData>.Handle camera; // 40
StructuredBuffer<Lighting>.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;
}

View File

@ -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<float4>.Handle skybox;
RWTexture2DArray<float4>.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<float4>.Handle skybox;
RWTexture2DArray<float4>.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<float2>.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;
}

View File

@ -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<float4>.Handle hdrEnv;
RWTexture2DArray<float4>.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);
}

View File

@ -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);
}

View File

@ -0,0 +1,377 @@
import bindless;
import ibl_common;
import common_structs;
struct ModelHandles
{
StructuredBuffer<float4>.Handle vertexBuffer; // 8
StructuredBuffer<VertexData>.Handle vertexData; // 16
StructuredBuffer<MaterialData>.Handle materialBuffer; // 24
StructuredBuffer<TransformData>.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<PointLight>.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<DirectionalLight>.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;
}

View File

@ -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")

2
vcpkg

@ -1 +1 @@
Subproject commit 0ca64b4e1c70fa6d9f53b369b8f3f0843797c20c
Subproject commit f26ec398c25c4980f33a50391f00a75f7ad62ef7

View File

@ -1,7 +1,7 @@
{
"default-registry": {
"kind": "git",
"baseline": "41c447cc210dc39aa85d4a5f58b4a1b9e573b3dc",
"baseline": "f26ec398c25c4980f33a50391f00a75f7ad62ef7",
"repository": "https://github.com/microsoft/vcpkg"
},
"registries": [

View File

@ -16,6 +16,7 @@
"tinygltf",
"vulkan-memory-allocator",
"entt",
"shader-slang"
"shader-slang",
"foonathan-memory"
]
}