RenderResourceManager handles images and bindless.

This commit is contained in:
Anish Bhobe 2025-03-02 19:19:43 +01:00
parent 3a7bea902f
commit 396810d203
21 changed files with 602 additions and 395 deletions

View File

@ -18,4 +18,5 @@ INTERFACE
"surface.h" "surface.h"
"size.h" "size.h"
"type_traits.h" "type_traits.h"
"window.h") "window.h"
"sampler.h")

View File

@ -47,7 +47,7 @@ struct Buffer
}; };
template <> template <>
constexpr bool concepts::GpuResource<Buffer> = true; constexpr bool concepts::Resource<Buffer> = true;
// Ensure that m_Size doesn't get used intrusively since it manages the state. // Ensure that m_Size doesn't get used intrusively since it manages the state.
static_assert(offsetof(Buffer, m_Size_) > sizeof(usize)); static_assert(offsetof(Buffer, m_Size_) > sizeof(usize));

View File

@ -211,3 +211,13 @@ struct fmt::formatter<eastl::fixed_string<TType, TCount, TOverflow>> : nested_fo
return write_padded(ctx, [this, str](auto out) { return v10::format_to(out, "{}", nested(str.c_str())); }); return write_padded(ctx, [this, str](auto out) { return v10::format_to(out, "{}", nested(str.c_str())); });
} }
}; };
namespace aster
{
template <typename TRef, typename TVal>
TVal &
Deref(TRef ref)
{
return ref.Deref();
}
}

View File

@ -60,7 +60,7 @@ struct Image
}; };
template <> template <>
constexpr bool concepts::GpuResource<Image> = true; constexpr bool concepts::Resource<Image> = true;
struct Texture : Image struct Texture : Image
{ {

View File

@ -0,0 +1,46 @@
// =============================================
// Aster: sampler.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
struct Device;
// TODO Refactor the Buffer Hierarchy
struct Sampler
{
vk::Sampler m_Sampler = nullptr;
bool m_Committed = false;
void Init(const Device *device, const vk::SamplerCreateInfo &samplerCreateInfo, cstr name);
void Destroy(const Device *device);
[[nodiscard]] bool IsValid() const;
[[nodiscard]] bool IsCommitted() const;
void SetCommitted(bool committed);
};
template <>
constexpr bool concepts::Opaque<Sampler> = true;
inline bool
Sampler::IsValid() const
{
return m_Sampler;
}
inline bool
Sampler::IsCommitted() const
{
return m_Committed;
}
inline void
Sampler::SetCommitted(const bool committed)
{
m_Committed = committed;
}

View File

@ -9,6 +9,12 @@ struct Device;
namespace concepts namespace concepts
{ {
template <typename THandle, typename TValue>
concept Fetches = requires(THandle h)
{
{ h.Fetch() } -> std::same_as<TValue*>;
};
template <typename T> template <typename T>
concept DeviceDestructible = requires(T a, Device *p) { concept DeviceDestructible = requires(T a, Device *p) {
{ a.Destroy(p) } -> std::convertible_to<void>; { a.Destroy(p) } -> std::convertible_to<void>;
@ -21,16 +27,24 @@ concept Committable = requires(T a, bool v) {
}; };
template <typename T> template <typename T>
constexpr bool GpuResource = false; constexpr bool Resource = false;
template <typename T> template <typename T>
concept RenderResource = GpuResource<T> and std::is_default_constructible_v<T> and std::is_trivially_copyable_v<T> and constexpr bool Opaque = false;
DeviceDestructible<T> and Committable<T>;
template <typename T> template <typename T>
constexpr bool IsHandle = false; concept Consistent = (Resource<T> and !Opaque<T>) or (Opaque<T> and !Resource<T>);
template <typename THandle> template <typename T>
concept HandleType = IsHandle<THandle> and RenderResource<typename THandle::Type>; concept Manageable = std::is_default_constructible_v<T> and std::is_trivially_copyable_v<T> and DeviceDestructible<T>;
template <typename T>
concept ShaderObject = Consistent<T> and Manageable<T> and Committable<T>;
template <typename T>
concept ShaderResource = ShaderObject<T> and Resource<T>;
template <typename T>
concept ShaderOpaque = ShaderObject<T> and Opaque<T>;
} // namespace concepts } // namespace concepts

View File

@ -7,4 +7,5 @@ INTERFACE
"manager.h" "manager.h"
"buffer_manager.h" "buffer_manager.h"
"image_manager.h" "image_manager.h"
"sampler_manager.h"
"render_resource_manager.h") "render_resource_manager.h")

View File

@ -10,10 +10,13 @@
struct Device; struct Device;
template <concepts::RenderResource T> namespace systems
{
template <concepts::ShaderObject T>
class Handle; class Handle;
template <concepts::RenderResource T> template <concepts::ShaderObject T>
class Manager class Manager
{ {
friend Handle<T>; friend Handle<T>;
@ -35,7 +38,7 @@ class Manager
, m_Binding{binding} , m_Binding{binding}
, m_Device{device} , m_Device{device}
{ {
assert(!m_Instance); assert(!m_Instance && "Attempting to initialize a second Manager");
assert(maxCount <= MAX_HANDLES); assert(maxCount <= MAX_HANDLES);
m_Data = new Type[m_MaxCount]; m_Data = new Type[m_MaxCount];
@ -77,7 +80,7 @@ class Manager
static Manager * static Manager *
Instance() Instance()
{ {
assert(m_Instance); assert(m_Instance && "Not initialized yet.");
return m_Instance; return m_Instance;
} }
@ -151,7 +154,7 @@ class Manager
} }
}; };
template <concepts::RenderResource T> template <concepts::ShaderObject T>
class Ref class Ref
{ {
public: public:
@ -173,14 +176,14 @@ class Ref
public: public:
Type * Type *
Get() Fetch()
{ {
assert(m_Pointer); assert(m_Pointer);
return m_Pointer; return m_Pointer;
} }
const Type * const Type *
Get() const Fetch() const
{ {
assert(m_Pointer); assert(m_Pointer);
return m_Pointer; return m_Pointer;
@ -189,25 +192,35 @@ class Ref
Type * Type *
operator->() operator->()
{ {
return Get(); return Fetch();
} }
const Type * const Type *
operator->() const operator->() const
{ {
return Get(); return Fetch();
} }
Type & Type &
operator*() operator*()
{ {
return *Get(); return *Fetch();
} }
const Type & const Type &
operator*() const operator*() const
{ {
return Get(); return Fetch();
}
operator Handle &()
{
return m_Handle;
}
operator const Handle &() const
{
return m_Handle;
} }
// The only constructor requires a valid construction. // The only constructor requires a valid construction.
@ -231,8 +244,13 @@ class Ref
~Ref() = default; ~Ref() = default;
}; };
class RawHandle template <concepts::ShaderObject T>
class Handle
{ {
public:
using Type = T;
using Manager = Manager<Type>;
protected: protected:
constexpr static u32 INVALID_HANDLE = MaxValue<u32>; constexpr static u32 INVALID_HANDLE = MaxValue<u32>;
constexpr static u32 INDEX_MASK = 0x0FFFFFFF; constexpr static u32 INDEX_MASK = 0x0FFFFFFF;
@ -240,17 +258,29 @@ class RawHandle
constexpr static u32 TYPE_OFFSET = GetMaskOffset(TYPE_MASK); constexpr static u32 TYPE_OFFSET = GetMaskOffset(TYPE_MASK);
u32 m_Internal = INVALID_HANDLE; u32 m_Internal = INVALID_HANDLE;
RawHandle(const u32 index, const u8 typeId) // The only constructor requires a valid construction.
: m_Internal{(index & INDEX_MASK) | (typeId & TYPE_MASK)} Handle(const u32 index, const u8 typeId)
: m_Internal{(index & INDEX_MASK) | ((typeId << TYPE_OFFSET) & TYPE_MASK)}
{ {
AddRef();
} }
explicit RawHandle(const u32 internal) friend Manager;
: m_Internal{internal} friend Ref<T>;
{
}
public: public:
[[nodiscard]] Ref<T>
ToPointer()
{
return Ref{std::move(*this)};
}
[[nodiscard]] Type *
Fetch() const
{
return Manager::Instance()->Fetch(GetIndex());
}
[[nodiscard]] bool [[nodiscard]] bool
IsValid() const IsValid() const
@ -271,52 +301,21 @@ class RawHandle
} }
bool bool
operator==(const RawHandle &other) const operator==(const Handle &other) const
{ {
return m_Internal == other.m_Internal; return m_Internal == other.m_Internal;
} }
};
template <concepts::RenderResource T>
class Handle : public RawHandle
{
public:
using Type = T;
using Manager = Manager<Type>;
protected:
// The only constructor requires a valid construction.
Handle(const u32 index, const u8 typeId)
: RawHandle{index, typeId}
{
AddRef();
}
friend Manager;
friend Ref<T>;
public:
Handle(const Handle &other) Handle(const Handle &other)
: RawHandle{other}
{ {
m_Internal = other.m_Internal;
AddRef(); AddRef();
} }
Handle(Handle &&other) noexcept Handle(Handle &&other) noexcept
: RawHandle{std::exchange(other.m_Internal, m_Internal)}
{ {
} m_Internal = other.m_Internal;
other.m_Internal = INVALID_HANDLE;
[[nodiscard]] Ref<T>
ToPointer()
{
return Ref{std::move(*this)};
}
[[nodiscard]] Type *
Fetch() const
{
return Manager::Instance()->Fetch(m_Internal);
} }
Handle & Handle &
@ -359,3 +358,5 @@ class Handle : public RawHandle
Manager::Instance()->Release(GetIndex()); Manager::Instance()->Release(GetIndex());
} }
}; };
} // namespace systems

View File

@ -8,9 +8,11 @@
#include "aster/aster.h" #include "aster/aster.h"
#include "buffer_manager.h" #include "buffer_manager.h"
#include "image_manager.h" #include "image_manager.h"
#include "sampler_manager.h"
#include "EASTL/deque.h" #include "EASTL/deque.h"
#include "EASTL/vector.h" #include "EASTL/vector.h"
#include <variant>
namespace systems namespace systems
{ {
@ -30,122 +32,63 @@ class RenderResourceManager
using WriteCommand = vk::WriteDescriptorSet; using WriteCommand = vk::WriteDescriptorSet;
union WriteOwner { //using WriteOwner = std::variant<Handle<Buffer>, Handle<Image>>;
Handle<Buffer> uBufferHandle;
Handle<Image> uImageHandle;
explicit WriteOwner(const Handle<Buffer> &handle);
explicit WriteOwner(const Handle<Image> &handle);
WriteOwner(const WriteOwner &other)
{
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle = other.uBufferHandle;
break;
case IMAGE_BINDING_INDEX:
uImageHandle = other.uImageHandle;
break;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
}
WriteOwner(WriteOwner &&other) noexcept
{
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle = std::move(other.uBufferHandle);
break;
case IMAGE_BINDING_INDEX:
uImageHandle = std::move(other.uImageHandle);
break;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
}
WriteOwner &
operator=(const WriteOwner &other)
{
if (this == &other)
return *this;
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle = other.uBufferHandle;
break;
case IMAGE_BINDING_INDEX:
uImageHandle = other.uImageHandle;
break;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
return *this;
}
WriteOwner &
operator=(WriteOwner &&other) noexcept
{
if (this == &other)
return *this;
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle = std::move(other.uBufferHandle);
break;
case IMAGE_BINDING_INDEX:
uImageHandle = std::move(other.uImageHandle);
break;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
return *this;
}
~WriteOwner()
{
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle.~Handle();
return;
case IMAGE_BINDING_INDEX:
uImageHandle.~Handle();
return;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
}
private:
RawHandle uRawHandle;
};
public: public:
RenderResourceManager(const Device *device, u32 maxBuffers, u32 maxImages); const Device *m_Device;
void Commit(concepts::HandleType auto &handle); RenderResourceManager(const Device *device, u32 maxBuffers, u32 maxImages, u32 maxSamplers);
~RenderResourceManager();
private: PIN_MEMORY(RenderResourceManager);
// Buffer
[[nodiscard]] BufferHandle CreateStorageBuffer(usize size, cstr name = nullptr);
[[nodiscard]] BufferHandle CreateUniformBuffer(usize size, cstr name = nullptr);
void Write(const BufferHandle &handle, usize offset, usize size, const void *data) const;
void Commit(const BufferHandle &handle);
void Release(const BufferHandle &handle);
[[nodiscard]] ImageHandle CreateTexture2D(const Texture2DCreateInfo &createInfo);
[[nodiscard]] ImageHandle CreateTextureCube(const TextureCubeCreateInfo &createInfo);
[[nodiscard]] ImageHandle CreateAttachment(const AttachmentCreateInfo &createInfo);
[[nodiscard]] ImageHandle CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo);
[[nodiscard]] SamplerHandle CreateSampler(const SamplerCreateInfo &createInfo);
void Commit(const ImageHandle &handle);
void Commit(const ImageHandle &handle, const SamplerHandle &samplerHandle);
void Update();
[[nodiscard]] const vk::DescriptorSetLayout&
GetDescriptorSetLayout() const
{
return m_SetLayout;
}
[[nodiscard]] const vk::DescriptorSet& GetDescriptorSet() const
{
return m_DescriptorSet;
}
private:
BufferManager m_BufferManager; BufferManager m_BufferManager;
ImageManager m_ImageManager; ImageManager m_ImageManager;
SamplerManager m_SamplerManager;
vk::DescriptorPool m_DescriptorPool; vk::DescriptorPool m_DescriptorPool;
vk::DescriptorSetLayout m_SetLayout; vk::DescriptorSetLayout m_SetLayout;
vk::DescriptorSet m_DescriptorSet; vk::DescriptorSet m_DescriptorSet;
constexpr static u8 BUFFER_BINDING_INDEX = 0; constexpr static u8 BUFFER_BINDING_INDEX = 0x0;
constexpr static u8 IMAGE_BINDING_INDEX = 1; constexpr static u8 IMAGE_BINDING_INDEX = 0x1;
constexpr static u8 SAMPLER_TYPE_INDEX = 0xF;
eastl::vector<vk::WriteDescriptorSet> m_Writes; eastl::vector<vk::WriteDescriptorSet> m_Writes;
eastl::deque<WriteInfo> m_WriteInfos; eastl::deque<WriteInfo> m_WriteInfos;
eastl::vector<WriteOwner> m_WriteOwner; // eastl::vector<WriteOwner> m_WriteOwner;
#if !defined(ASTER_NDEBUG) #if !defined(ASTER_NDEBUG)
usize m_CommitedBufferCount = 0; usize m_CommitedBufferCount = 0;
@ -153,5 +96,4 @@ class RenderResourceManager
usize m_CommitedStorageTextureCount = 0; usize m_CommitedStorageTextureCount = 0;
#endif #endif
}; };
} // namespace systems } // namespace systems

View File

@ -0,0 +1,57 @@
// =============================================
// Aster: sampler_manager.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "EASTL/vector_map.h"
#include "aster/aster.h"
#include "aster/core/sampler.h"
#include "manager.h"
namespace systems
{
using SamplerHandle = Handle<Sampler>;
struct SamplerCreateInfo : vk::SamplerCreateInfo
{
cstr m_Name = nullptr;
SamplerCreateInfo()
: vk::SamplerCreateInfo{
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eLinear,
.addressModeU = vk::SamplerAddressMode::eRepeat,
.addressModeV = vk::SamplerAddressMode::eRepeat,
.addressModeW = vk::SamplerAddressMode::eRepeat,
.mipLodBias = 0.0f,
.anisotropyEnable = true,
.maxAnisotropy = 16,
.compareEnable = false,
.minLod = 0,
.maxLod = VK_LOD_CLAMP_NONE,
.borderColor = vk::BorderColor::eFloatOpaqueBlack,
.unnormalizedCoordinates = false,
}
{
}
};
/**
* @class SamplerManager sampler_manager.h
*
* Manages (and caches) objects of sampler. Currently Samplers are never deleted.
*/
class SamplerManager final : public Manager<Sampler>
{
eastl::vector_map<usize, Handle> m_HashToSamplerIdx;
public:
SamplerManager(const Device *device, const u32 maxCount, const u8 typeId);
~SamplerManager() override;
SamplerHandle Create(const SamplerCreateInfo &createInfo);
};
} // namespace systems

View File

@ -13,4 +13,5 @@ PRIVATE
"buffer.cpp" "buffer.cpp"
"image.cpp" "image.cpp"
"surface.cpp" "surface.cpp"
"window.cpp") "window.cpp"
"sampler.cpp")

View File

@ -0,0 +1,24 @@
// =============================================
// Aster: sampler.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "core/sampler.h"
#include "core/device.h"
void
Sampler::Destroy(const Device *device)
{
if (!IsValid())
return;
device->m_Device.destroy(Take(m_Sampler), nullptr);
}
void
Sampler::Init(const Device *device, const vk::SamplerCreateInfo &samplerCreateInfo, cstr name)
{
const auto result = device->m_Device.createSampler(&samplerCreateInfo, nullptr, &m_Sampler);
ERROR_IF(Failed(result), "Could not create a sampler {}", name ? name : "<unnamed>") THEN_ABORT(-1);
}

View File

@ -7,4 +7,5 @@ PRIVATE
"manager.cpp" "manager.cpp"
"buffer_manager.cpp" "buffer_manager.cpp"
"image_manager.cpp" "image_manager.cpp"
"sampler_manager.cpp"
"render_resource_manager.cpp") "render_resource_manager.cpp")

View File

@ -5,10 +5,10 @@
#include "systems/buffer_manager.h" #include "systems/buffer_manager.h"
Manager<Buffer> *Manager<Buffer>::m_Instance = nullptr;
using namespace systems; using namespace systems;
Manager<Buffer> *Manager<Buffer>::m_Instance = nullptr;
BufferHandle BufferHandle
BufferManager::CreateStorageBuffer(const usize size, const cstr name) BufferManager::CreateStorageBuffer(const usize size, const cstr name)
{ {

View File

@ -7,10 +7,10 @@
#include "core/device.h" #include "core/device.h"
Manager<Image> *Manager<Image>::m_Instance = nullptr;
using namespace systems; using namespace systems;
Manager<Image> *Manager<Image>::m_Instance = nullptr;
vk::ImageCreateInfo ToImageCreateInfo(const Texture2DCreateInfo &createInfo); vk::ImageCreateInfo ToImageCreateInfo(const Texture2DCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const TextureCubeCreateInfo &createInfo); vk::ImageCreateInfo ToImageCreateInfo(const TextureCubeCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const AttachmentCreateInfo &createInfo); vk::ImageCreateInfo ToImageCreateInfo(const AttachmentCreateInfo &createInfo);

View File

@ -5,8 +5,8 @@
#include "systems/render_resource_manager.h" #include "systems/render_resource_manager.h"
#include "EASTL/array.h"
#include "core/device.h" #include "core/device.h"
#include "EASTL/array.h"
#define AbortIfFailed(RESULT) \ #define AbortIfFailed(RESULT) \
do \ do \
@ -31,28 +31,14 @@
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \ ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
} while (false) } while (false)
using namespace systems; using namespace systems;
u32 RenderResourceManager::RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages,
GetHandleInternal(concepts::HandleType auto &handle) const u32 maxSamplers)
{ : m_Device{device}
return *Recast<u32 *>(&handle); , m_BufferManager{device, maxBuffers, BUFFER_BINDING_INDEX}
}
RenderResourceManager::WriteOwner::WriteOwner(const Handle<Buffer> &handle)
: uBufferHandle(handle)
{
}
RenderResourceManager::WriteOwner::WriteOwner(const Handle<Image> &handle)
: uImageHandle(handle)
{
}
RenderResourceManager::RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages)
: m_BufferManager{device, maxBuffers, BUFFER_BINDING_INDEX}
, m_ImageManager{device, maxImages, IMAGE_BINDING_INDEX} , m_ImageManager{device, maxImages, IMAGE_BINDING_INDEX}
, m_SamplerManager{device, maxSamplers, SAMPLER_TYPE_INDEX}
{ {
eastl::array poolSizes = { eastl::array poolSizes = {
vk::DescriptorPoolSize{ vk::DescriptorPoolSize{
@ -63,10 +49,10 @@ RenderResourceManager::RenderResourceManager(const Device *device, u32 const max
.type = vk::DescriptorType::eCombinedImageSampler, .type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = maxImages, .descriptorCount = maxImages,
}, },
//vk::DescriptorPoolSize{ // vk::DescriptorPoolSize{
// .type = vk::DescriptorType::eStorageImage, // .type = vk::DescriptorType::eStorageImage,
// .descriptorCount = storageTexturesCount, // .descriptorCount = storageTexturesCount,
//}, // },
}; };
const vk::DescriptorPoolCreateInfo poolCreateInfo = { const vk::DescriptorPoolCreateInfo poolCreateInfo = {
@ -90,12 +76,12 @@ RenderResourceManager::RenderResourceManager(const Device *device, u32 const max
.descriptorCount = Cast<u32>(maxImages), .descriptorCount = Cast<u32>(maxImages),
.stageFlags = vk::ShaderStageFlagBits::eAll, .stageFlags = vk::ShaderStageFlagBits::eAll,
}, },
//vk::DescriptorSetLayoutBinding{ // vk::DescriptorSetLayoutBinding{
// .binding = STORAGE_TEXTURE_BINDING_INDEX, // .binding = STORAGE_TEXTURE_BINDING_INDEX,
// .descriptorType = vk::DescriptorType::eStorageImage, // .descriptorType = vk::DescriptorType::eStorageImage,
// .descriptorCount = Cast<u32>(storageTexturesCount), // .descriptorCount = Cast<u32>(storageTexturesCount),
// .stageFlags = vk::ShaderStageFlagBits::eAll, // .stageFlags = vk::ShaderStageFlagBits::eAll,
//}, // },
}; };
vk::DescriptorBindingFlags bindingFlags = vk::DescriptorBindingFlags bindingFlags =
@ -133,50 +119,128 @@ RenderResourceManager::RenderResourceManager(const Device *device, u32 const max
device->SetName(m_DescriptorSet, "Bindless Set"); device->SetName(m_DescriptorSet, "Bindless Set");
} }
void RenderResourceManager::~RenderResourceManager()
systems::RenderResourceManager::Commit(concepts::HandleType auto &handle)
{ {
using HandleType = decltype(handle)::Type;
if constexpr (std::is_same_v<HandleType, Buffer>)
{
const Buffer *buffer = handle.Fetch();
m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{ #if !defined(ASTER_NDEBUG)
.buffer = buffer->m_Buffer, WARN_IF(m_CommitedBufferCount > 0 || m_CommitedTextureCount > 0 || m_CommitedStorageTextureCount > 0,
.offset = 0, "Resources alive: SSBO = {}, Textures = {}, RWTexture = {}", m_CommitedBufferCount, m_CommitedTextureCount,
.range = buffer->GetSize(), m_CommitedStorageTextureCount);
}); #endif
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = BUFFER_BINDING_INDEX,
.dstArrayElement = handle.GetIndex(),
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
});
}
else if constexpr (std::is_same_v<HandleType, Image>)
{
const Image *image = handle.Fetch();
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ m_Device->m_Device.destroy(m_SetLayout, nullptr);
.sampler = nullptr /* TODO Sampler */, m_Device->m_Device.destroy(m_DescriptorPool, nullptr);
.imageView = image->m_View, }
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
}); void
m_Writes.push_back({ RenderResourceManager::Write(const BufferHandle &handle, const usize offset, const usize size, const void *data) const
.dstSet = m_DescriptorSet, {
.dstBinding = IMAGE_BINDING_INDEX, handle.Fetch()->Write(m_Device, offset, size, data);
.dstArrayElement = handle.GetIndex(), }
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eSampledImage, BufferHandle RenderResourceManager::CreateStorageBuffer(const usize size, const cstr name)
.pImageInfo = &m_WriteInfos.back().uImageInfo, {
}); return m_BufferManager.CreateStorageBuffer(size, name);
} else { }
static_assert(false && "Type is currently unsupported");
BufferHandle
RenderResourceManager::CreateUniformBuffer(const usize size, const cstr name)
{
return m_BufferManager.CreateUniformBuffer(size, name);
}
ImageHandle
RenderResourceManager::CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
return m_ImageManager.CreateTexture2D(createInfo);
}
ImageHandle
RenderResourceManager::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
return m_ImageManager.CreateTextureCube(createInfo);
}
ImageHandle
RenderResourceManager::CreateAttachment(const AttachmentCreateInfo &createInfo)
{
return m_ImageManager.CreateAttachment(createInfo);
}
ImageHandle
RenderResourceManager::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo)
{
return m_ImageManager.CreateDepthStencilImage(createInfo);
}
void
RenderResourceManager::Commit(const BufferHandle &handle)
{
Buffer *buffer = handle.Fetch();
if (buffer->IsCommitted())
{
WARN("Buffer is already committed");
return;
} }
m_WriteOwner.emplace_back(handle); m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{
.buffer = buffer->m_Buffer,
.offset = 0,
.range = buffer->GetSize(),
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = BUFFER_BINDING_INDEX,
.dstArrayElement = handle.GetIndex(),
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
});
buffer->SetCommitted(true);
#if !defined(ASTER_NDEBUG)
++m_CommitedBufferCount;
#endif
}
void
RenderResourceManager::Commit(const ImageHandle &handle)
{
const auto sampler = m_SamplerManager.Create({});
Commit(handle, sampler);
}
void
RenderResourceManager::Commit(const ImageHandle &handle, const SamplerHandle &samplerHandle)
{
Image *image = handle.Fetch();
if (image->IsCommitted())
{
WARN("Image is already committed");
return;
}
Sampler *sampler = samplerHandle.Fetch();
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
.sampler = sampler->m_Sampler,
.imageView = image->m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = IMAGE_BINDING_INDEX,
.dstArrayElement = handle.GetIndex(),
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &m_WriteInfos.back().uImageInfo,
});
image->SetCommitted(true);
sampler->SetCommitted(true);
#if !defined(ASTER_NDEBUG)
++m_CommitedTextureCount;
#endif
} }
RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info) RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info)
@ -192,4 +256,23 @@ RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorImageInfo &info)
RenderResourceManager::WriteInfo::WriteInfo(const vk::BufferView &info) RenderResourceManager::WriteInfo::WriteInfo(const vk::BufferView &info)
: uBufferView{info} : uBufferView{info}
{ {
} }
SamplerHandle
RenderResourceManager::CreateSampler(const SamplerCreateInfo &createInfo)
{
return m_SamplerManager.Create(createInfo);
}
void
RenderResourceManager::Update()
{
// Descriptor Updates
if (!m_Writes.empty())
{
m_Device->m_Device.updateDescriptorSets(Cast<u32>(m_Writes.size()), m_Writes.data(), 0, nullptr);
m_Writes.clear();
m_WriteInfos.clear();
}
}

View File

@ -0,0 +1,66 @@
// =============================================
// Aster: sampler_manager.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/sampler_manager.h"
#include "core/device.h"
using namespace systems;
Manager<Sampler> *Manager<Sampler>::m_Instance = nullptr;
usize
HashSamplerCreateInfo(const vk::SamplerCreateInfo &createInfo)
{
usize hash = HashAny(createInfo.flags);
hash = HashCombine(hash, HashAny(createInfo.magFilter));
hash = HashCombine(hash, HashAny(createInfo.minFilter));
hash = HashCombine(hash, HashAny(createInfo.mipmapMode));
hash = HashCombine(hash, HashAny(createInfo.addressModeU));
hash = HashCombine(hash, HashAny(createInfo.addressModeV));
hash = HashCombine(hash, HashAny(createInfo.addressModeW));
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo.mipLodBias * 1000))); // Resolution of 10^-3
hash = HashCombine(hash, HashAny(createInfo.anisotropyEnable));
hash = HashCombine(hash,
HashAny(Cast<usize>(createInfo.maxAnisotropy * 0x20))); // 32:1 Anisotropy is enough resolution
hash = HashCombine(hash, HashAny(createInfo.compareEnable));
hash = HashCombine(hash, HashAny(createInfo.compareOp));
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo.minLod * 1000))); // 0.001 resolution is enough.
hash = HashCombine(hash,
HashAny(Cast<usize>(createInfo.maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp)
hash = HashCombine(hash, HashAny(createInfo.borderColor));
hash = HashCombine(hash, HashAny(createInfo.unnormalizedCoordinates));
return hash;
}
SamplerManager::SamplerManager(const Device *device, const u32 maxCount, const u8 typeId)
: Manager{device, maxCount, typeId}
{
}
SamplerManager::~SamplerManager()
{
m_HashToSamplerIdx.clear();
}
SamplerHandle
SamplerManager::Create(const SamplerCreateInfo &createInfo)
{
const auto hash = HashSamplerCreateInfo(createInfo);
if (const auto iter = m_HashToSamplerIdx.find(hash); iter != m_HashToSamplerIdx.end())
{
return iter->second;
}
auto [handle, object] = Alloc();
object->Init(m_Device, createInfo, createInfo.m_Name ? createInfo.m_Name : nullptr);
m_HashToSamplerIdx.emplace(hash, handle);
return handle;
}

View File

@ -20,6 +20,7 @@
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "aster/systems/buffer_manager.h" #include "aster/systems/buffer_manager.h"
#include "aster/systems/image_manager.h" #include "aster/systems/image_manager.h"
#include "aster/systems/render_resource_manager.h"
#include "frame.h" #include "frame.h"
#include "stb_image.h" #include "stb_image.h"
@ -73,7 +74,7 @@ ImageFile::~ImageFile()
} }
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile); vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain); Pipeline CreatePipeline(const systems::RenderResourceManager *resourceManager, const Swapchain *swapchain);
struct Vertex struct Vertex
{ {
@ -106,17 +107,30 @@ main(int, char **)
Features enabledDeviceFeatures = { Features enabledDeviceFeatures = {
.m_Vulkan10Features = {.samplerAnisotropy = true}, .m_Vulkan10Features = {.samplerAnisotropy = true},
.m_Vulkan12Features = {.bufferDeviceAddress = true}, .m_Vulkan12Features =
{
.descriptorIndexing = true,
.shaderSampledImageArrayNonUniformIndexing = true,
.shaderStorageBufferArrayNonUniformIndexing = true,
.shaderStorageImageArrayNonUniformIndexing = true,
.descriptorBindingUniformBufferUpdateAfterBind = true, // Not related to Bindless
.descriptorBindingSampledImageUpdateAfterBind = true,
.descriptorBindingStorageImageUpdateAfterBind = true,
.descriptorBindingStorageBufferUpdateAfterBind = true,
.descriptorBindingPartiallyBound = true,
.runtimeDescriptorArray = true,
.bufferDeviceAddress = true,
.bufferDeviceAddressCaptureReplay = true,
},
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true}, .m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
}; };
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"}; Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0); vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"}; Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
Pipeline pipeline = CreatePipeline(&device, &swapchain); systems::RenderResourceManager renderResourceManager{&device, 12, 12, 1};
systems::BufferManager bufferManager{&device, 12, 0}; Pipeline pipeline = CreatePipeline(&renderResourceManager, &swapchain);
systems::ImageManager imageManager{&device, 12, 1};
Camera camera = { Camera camera = {
.m_Model = {1.0f}, .m_Model = {1.0f},
@ -125,36 +139,6 @@ main(int, char **)
70_deg, Cast<f32>(swapchain.m_Extent.width) / Cast<f32>(swapchain.m_Extent.height), 0.1f, 100.0f), 70_deg, Cast<f32>(swapchain.m_Extent.width) / Cast<f32>(swapchain.m_Extent.height), 0.1f, 100.0f),
}; };
vk::DescriptorPool descriptorPool;
vk::DescriptorSet descriptorSet;
{
vk::DescriptorSetLayout descriptorSetLayout = pipeline.m_SetLayouts.front();
eastl::array poolSizes = {
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
},
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
},
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eStorageBuffer,
.descriptorCount = 1,
},
};
vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
.maxSets = 1, .poolSizeCount = 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, &descriptorSet));
}
vk::CommandPool copyPool; vk::CommandPool copyPool;
vk::CommandBuffer copyBuffer; vk::CommandBuffer copyBuffer;
{ {
@ -222,15 +206,16 @@ main(int, char **)
assert(loaded); assert(loaded);
INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels); INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels);
auto vbo = bufferManager.CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer").ToPointer(); auto vbo = renderResourceManager.CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer");
auto crate = imageManager renderResourceManager.Write(vbo, 0, vertices.size() * sizeof vertices[0], vertices.data());
auto crate = renderResourceManager
.CreateTexture2D({ .CreateTexture2D({
.m_Format = vk::Format::eR8G8B8A8Srgb, .m_Format = vk::Format::eR8G8B8A8Srgb,
.m_Extent = {imageFile.m_Width, imageFile.m_Height}, .m_Extent = {imageFile.m_Width, imageFile.m_Height},
.m_Name = "Crate Texture", .m_Name = "Crate Texture",
}) })
.ToPointer(); .ToPointer();
vbo->Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
{ {
StagingBuffer imageStaging; StagingBuffer imageStaging;
@ -333,70 +318,14 @@ main(int, char **)
imageStaging.Destroy(&device); imageStaging.Destroy(&device);
} }
vk::Sampler sampler; auto ubo = renderResourceManager.CreateStorageBuffer(sizeof camera, "Camera UBO");
{ renderResourceManager.Write(ubo, 0, sizeof camera, &camera);
vk::SamplerCreateInfo samplerCreateInfo = {
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eLinear,
.addressModeU = vk::SamplerAddressMode::eRepeat,
.addressModeV = vk::SamplerAddressMode::eRepeat,
.addressModeW = vk::SamplerAddressMode::eRepeat,
.mipLodBias = 0.2f,
.anisotropyEnable = true,
.maxAnisotropy = 1.0f,
.compareEnable = false,
.minLod = 0,
.maxLod = 4,
.unnormalizedCoordinates = false,
};
AbortIfFailed(device.m_Device.createSampler(&samplerCreateInfo, nullptr, &sampler));
}
auto ubo = bufferManager.CreateUniformBuffer(sizeof camera, "Camera UBO").ToPointer(); renderResourceManager.Commit(ubo);
ubo->Write(&device, 0, sizeof camera, &camera); renderResourceManager.Commit(vbo);
vk::DescriptorBufferInfo descriptorBufferInfo = { renderResourceManager.Commit(crate);
.buffer = ubo->m_Buffer,
.offset = 0, renderResourceManager.Update();
.range = ubo->GetSize(),
};
vk::DescriptorImageInfo descriptorImageInfo = {
.sampler = sampler,
.imageView = crate->m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
};
vk::DescriptorBufferInfo descriptorStorageBufferInfo = {
.buffer = vbo->m_Buffer,
.offset = 0,
.range = vbo->GetSize(),
};
eastl::array writeDescriptors = {
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &descriptorBufferInfo,
},
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &descriptorImageInfo,
},
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
.dstBinding = 2,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &descriptorStorageBufferInfo,
},
};
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
// Persistent variables // Persistent variables
vk::Viewport viewport = { vk::Viewport viewport = {
@ -462,13 +391,13 @@ main(int, char **)
}; };
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT}; FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<Ref<Image>, MAX_FRAMES_IN_FLIGHT> depthImages; eastl::fixed_vector<systems::Ref<Image>, MAX_FRAMES_IN_FLIGHT> depthImages;
auto initDepthImages = [&imageManager, &depthImages, &frameManager] (const vk::Extent2D extent) { auto initDepthImages = [&depthImages, &frameManager, &renderResourceManager](const vk::Extent2D extent) {
for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i) for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i)
{ {
depthImages.push_back( depthImages.push_back(
imageManager.CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"}).ToPointer()); renderResourceManager.CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"}).ToPointer());
} }
}; };
@ -480,6 +409,19 @@ main(int, char **)
}; };
swapchain.RegisterResizeCallback(recreateDepthBuffers); swapchain.RegisterResizeCallback(recreateDepthBuffers);
struct PCB
{
systems::BufferHandle m_Camera;
systems::BufferHandle m_VertexBuffer;
systems::ImageHandle m_Texture;
};
PCB pcb = {
.m_Camera = std::move(ubo),
.m_VertexBuffer = std::move(vbo),
.m_Texture = std::move(crate),
};
Time::Init(); Time::Init();
INFO("Starting loop"); INFO("Starting loop");
@ -488,7 +430,7 @@ main(int, char **)
Time::Update(); Time::Update();
camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
ubo->Write(&device, 0, sizeof camera, &camera); renderResourceManager.Write(pcb.m_Camera, 0, sizeof camera, &camera);
Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize()); Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize());
@ -540,7 +482,9 @@ main(int, char **)
cmd.setViewport(0, 1, &viewport); cmd.setViewport(0, 1, &viewport);
cmd.setScissor(0, 1, &scissor); cmd.setScissor(0, 1, &scissor);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, &descriptorSet, 0, nullptr); cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1,
&renderResourceManager.GetDescriptorSet(), 0, nullptr);
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAllGraphics, 0, 12, &pcb);
cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0); cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0);
cmd.endRendering(); cmd.endRendering();
@ -565,17 +509,16 @@ main(int, char **)
} }
device.WaitIdle(); device.WaitIdle();
device.m_Device.destroy(sampler, nullptr);
device.m_Device.destroy(descriptorPool, nullptr);
device.m_Device.destroy(copyPool, nullptr); device.m_Device.destroy(copyPool, nullptr);
return 0; return 0;
} }
Pipeline Pipeline
CreatePipeline(const Device *device, const Swapchain *swapchain) CreatePipeline(const systems::RenderResourceManager *resourceManager, const Swapchain *swapchain)
{ {
// Pipeline Setup // Pipeline Setup
auto *device = resourceManager->m_Device;
auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE); auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE);
auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE); auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE);
@ -592,39 +535,18 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
}, },
}}; }};
eastl::array descriptorSetLayoutBinding = { auto descriptorSetLayout = resourceManager->GetDescriptorSetLayout();
vk::DescriptorSetLayoutBinding{
.binding = 0,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex,
},
vk::DescriptorSetLayoutBinding{
.binding = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eFragment,
},
vk::DescriptorSetLayoutBinding{
.binding = 2,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex,
},
};
vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.bindingCount = Cast<u32>(descriptorSetLayoutBinding.size()),
.pBindings = descriptorSetLayoutBinding.data(),
};
vk::DescriptorSetLayout descriptorSetLayout;
AbortIfFailed(
device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout));
vk::PushConstantRange pcr = {
.stageFlags = vk::ShaderStageFlagBits::eAllGraphics,
.offset = 0,
.size = 12,
};
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
.setLayoutCount = 1, .setLayoutCount = 1,
.pSetLayouts = &descriptorSetLayout, .pSetLayouts = &descriptorSetLayout,
.pushConstantRangeCount = 0, .pushConstantRangeCount = 1,
.pPushConstantRanges = nullptr, .pPushConstantRanges = &pcr,
}; };
vk::PipelineLayout pipelineLayout; vk::PipelineLayout pipelineLayout;
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
@ -714,7 +636,7 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
device->m_Device.destroy(vertexShaderModule, nullptr); device->m_Device.destroy(vertexShaderModule, nullptr);
device->m_Device.destroy(fragmentShaderModule, nullptr); device->m_Device.destroy(fragmentShaderModule, nullptr);
return {device, pipelineLayout, pipeline, {descriptorSetLayout}}; return {device, pipelineLayout, pipeline, {}};
} }
vk::ShaderModule vk::ShaderModule

View File

@ -0,0 +1,31 @@
struct VertexData
{
float4 Position;
float2 TexCoord0;
float2 _pad0;
};
struct CameraData
{
float4x4 Model;
float4x4 View;
float4x4 Projection;
};
struct Handle
{
uint value;
uint GetIndex()
{
return value & 0x0FFFFFFF;
}
};
[[vk::binding(0, 0)]] StructuredBuffer<VertexData> VertexDataBuffer[];
[[vk::binding(0, 0)]] StructuredBuffer<CameraData> CameraDataBuffer[];
[[vk::binding(1, 0)]] Texture2D<float4> Textures[];
[[vk::binding(1, 0)]] SamplerState Samplers[];

View File

@ -1,3 +1,5 @@
#include "bindless.hlsli"
struct FS_Input { struct FS_Input {
float2 UV0 : TEXCOORD0; float2 UV0 : TEXCOORD0;
}; };
@ -7,13 +9,19 @@ struct FS_Output
float4 ColorTarget : SV_Target0; float4 ColorTarget : SV_Target0;
}; };
[[vk::binding(1, 0)]] Texture2D<float4> Texture; struct PCB
[[vk::binding(1, 0)]] SamplerState Sampler; {
Handle CameraBuffer;
Handle VertexBuffer;
Handle Texture;
};
[[vk::push_constant]] PCB Block;
FS_Output main(FS_Input StageInput) { FS_Output main(FS_Input StageInput) {
FS_Output output; FS_Output output;
output.ColorTarget = float4(Texture.Sample(Sampler, StageInput.UV0).rgb, 1.0); output.ColorTarget = float4(Textures[Block.Texture.GetIndex()].Sample(Samplers[Block.Texture.GetIndex()], StageInput.UV0).rgb, 1.0);
return output; return output;
} }

View File

@ -1,3 +1,5 @@
#include "bindless.hlsli"
struct VS_Input struct VS_Input
{ {
uint VertexIndex : SV_VertexID; uint VertexIndex : SV_VertexID;
@ -9,26 +11,23 @@ struct VS_Output
float4 VertexPosition : SV_Position; float4 VertexPosition : SV_Position;
}; };
struct CameraData { struct PCB
float4x4 Model; {
float4x4 View; Handle CameraBuffer;
float4x4 Projection; Handle VertexBuffer;
Handle Texture;
}; };
struct VertexData { [[vk::push_constant]] PCB Block;
float4 Position;
float2 UV0;
};
[[vk::binding(0, 0)]] ConstantBuffer<CameraData> Camera;
[[vk::binding(2, 0)]] StructuredBuffer<VertexData> Vertices;
VS_Output main(VS_Input StageInput) { VS_Output main(VS_Input StageInput) {
VS_Output output; VS_Output output;
CameraData Camera = CameraDataBuffer[Block.CameraBuffer.GetIndex()][0];
output.UV0 = Vertices[StageInput.VertexIndex].UV0; output.UV0 = VertexDataBuffer[Block.VertexBuffer.GetIndex()][StageInput.VertexIndex].TexCoord0;
float4 position = Vertices[StageInput.VertexIndex].Position; float4 position = VertexDataBuffer[Block.VertexBuffer.GetIndex()][StageInput.VertexIndex].Position;
output.VertexPosition = mul(Camera.Projection, mul(Camera.View, mul(Camera.Model, position))); output.VertexPosition = mul(Camera.Projection, mul(Camera.View, mul(Camera.Model, position)));