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"
"size.h"
"type_traits.h"
"window.h")
"window.h"
"sampler.h")

View File

@ -47,7 +47,7 @@ struct Buffer
};
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.
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())); });
}
};
namespace aster
{
template <typename TRef, typename TVal>
TVal &
Deref(TRef ref)
{
return ref.Deref();
}
}

View File

@ -60,7 +60,7 @@ struct Image
};
template <>
constexpr bool concepts::GpuResource<Image> = true;
constexpr bool concepts::Resource<Image> = true;
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
{
template <typename THandle, typename TValue>
concept Fetches = requires(THandle h)
{
{ h.Fetch() } -> std::same_as<TValue*>;
};
template <typename T>
concept DeviceDestructible = requires(T a, Device *p) {
{ a.Destroy(p) } -> std::convertible_to<void>;
@ -21,16 +27,24 @@ concept Committable = requires(T a, bool v) {
};
template <typename T>
constexpr bool GpuResource = false;
constexpr bool Resource = false;
template <typename T>
concept RenderResource = GpuResource<T> and std::is_default_constructible_v<T> and std::is_trivially_copyable_v<T> and
DeviceDestructible<T> and Committable<T>;
constexpr bool Opaque = false;
template <typename T>
constexpr bool IsHandle = false;
concept Consistent = (Resource<T> and !Opaque<T>) or (Opaque<T> and !Resource<T>);
template <typename THandle>
concept HandleType = IsHandle<THandle> and RenderResource<typename THandle::Type>;
template <typename T>
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

View File

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

View File

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

View File

@ -8,9 +8,11 @@
#include "aster/aster.h"
#include "buffer_manager.h"
#include "image_manager.h"
#include "sampler_manager.h"
#include "EASTL/deque.h"
#include "EASTL/vector.h"
#include <variant>
namespace systems
{
@ -30,122 +32,63 @@ class RenderResourceManager
using WriteCommand = vk::WriteDescriptorSet;
union WriteOwner {
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;
};
//using WriteOwner = std::variant<Handle<Buffer>, Handle<Image>>;
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();
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;
ImageManager m_ImageManager;
SamplerManager m_SamplerManager;
vk::DescriptorPool m_DescriptorPool;
vk::DescriptorSetLayout m_SetLayout;
vk::DescriptorSet m_DescriptorSet;
constexpr static u8 BUFFER_BINDING_INDEX = 0;
constexpr static u8 IMAGE_BINDING_INDEX = 1;
constexpr static u8 BUFFER_BINDING_INDEX = 0x0;
constexpr static u8 IMAGE_BINDING_INDEX = 0x1;
constexpr static u8 SAMPLER_TYPE_INDEX = 0xF;
eastl::vector<vk::WriteDescriptorSet> m_Writes;
eastl::deque<WriteInfo> m_WriteInfos;
eastl::vector<WriteOwner> m_WriteOwner;
// eastl::vector<WriteOwner> m_WriteOwner;
#if !defined(ASTER_NDEBUG)
usize m_CommitedBufferCount = 0;
@ -153,5 +96,4 @@ class RenderResourceManager
usize m_CommitedStorageTextureCount = 0;
#endif
};
} // 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"
"image.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"
"buffer_manager.cpp"
"image_manager.cpp"
"sampler_manager.cpp"
"render_resource_manager.cpp")

View File

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

View File

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

View File

@ -5,8 +5,8 @@
#include "systems/render_resource_manager.h"
#include "EASTL/array.h"
#include "core/device.h"
#include "EASTL/array.h"
#define AbortIfFailed(RESULT) \
do \
@ -31,28 +31,14 @@
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
} while (false)
using namespace systems;
u32
GetHandleInternal(concepts::HandleType auto &handle)
{
return *Recast<u32 *>(&handle);
}
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}
RenderResourceManager::RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages,
const u32 maxSamplers)
: m_Device{device}
, m_BufferManager{device, maxBuffers, BUFFER_BINDING_INDEX}
, m_ImageManager{device, maxImages, IMAGE_BINDING_INDEX}
, m_SamplerManager{device, maxSamplers, SAMPLER_TYPE_INDEX}
{
eastl::array poolSizes = {
vk::DescriptorPoolSize{
@ -133,13 +119,69 @@ RenderResourceManager::RenderResourceManager(const Device *device, u32 const max
device->SetName(m_DescriptorSet, "Bindless Set");
}
RenderResourceManager::~RenderResourceManager()
{
#if !defined(ASTER_NDEBUG)
WARN_IF(m_CommitedBufferCount > 0 || m_CommitedTextureCount > 0 || m_CommitedStorageTextureCount > 0,
"Resources alive: SSBO = {}, Textures = {}, RWTexture = {}", m_CommitedBufferCount, m_CommitedTextureCount,
m_CommitedStorageTextureCount);
#endif
m_Device->m_Device.destroy(m_SetLayout, nullptr);
m_Device->m_Device.destroy(m_DescriptorPool, nullptr);
}
void
systems::RenderResourceManager::Commit(concepts::HandleType auto &handle)
RenderResourceManager::Write(const BufferHandle &handle, const usize offset, const usize size, const void *data) const
{
using HandleType = decltype(handle)::Type;
if constexpr (std::is_same_v<HandleType, Buffer>)
handle.Fetch()->Write(m_Device, offset, size, data);
}
BufferHandle RenderResourceManager::CreateStorageBuffer(const usize size, const cstr name)
{
const Buffer *buffer = handle.Fetch();
return m_BufferManager.CreateStorageBuffer(size, name);
}
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_WriteInfos.emplace_back(vk::DescriptorBufferInfo{
.buffer = buffer->m_Buffer,
@ -154,13 +196,34 @@ systems::RenderResourceManager::Commit(concepts::HandleType auto &handle)
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
});
buffer->SetCommitted(true);
#if !defined(ASTER_NDEBUG)
++m_CommitedBufferCount;
#endif
}
else if constexpr (std::is_same_v<HandleType, Image>)
void
RenderResourceManager::Commit(const ImageHandle &handle)
{
const Image *image = handle.Fetch();
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 = nullptr /* TODO Sampler */,
.sampler = sampler->m_Sampler,
.imageView = image->m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
});
@ -169,14 +232,15 @@ systems::RenderResourceManager::Commit(concepts::HandleType auto &handle)
.dstBinding = IMAGE_BINDING_INDEX,
.dstArrayElement = handle.GetIndex(),
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eSampledImage,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &m_WriteInfos.back().uImageInfo,
});
} else {
static_assert(false && "Type is currently unsupported");
}
m_WriteOwner.emplace_back(handle);
image->SetCommitted(true);
sampler->SetCommitted(true);
#if !defined(ASTER_NDEBUG)
++m_CommitedTextureCount;
#endif
}
RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info)
@ -193,3 +257,22 @@ RenderResourceManager::WriteInfo::WriteInfo(const vk::BufferView &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
#include "aster/systems/buffer_manager.h"
#include "aster/systems/image_manager.h"
#include "aster/systems/render_resource_manager.h"
#include "frame.h"
#include "stb_image.h"
@ -73,7 +74,7 @@ ImageFile::~ImageFile()
}
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
{
@ -106,17 +107,30 @@ main(int, char **)
Features enabledDeviceFeatures = {
.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},
};
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
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};
systems::ImageManager imageManager{&device, 12, 1};
Pipeline pipeline = CreatePipeline(&renderResourceManager, &swapchain);
Camera camera = {
.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),
};
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::CommandBuffer copyBuffer;
{
@ -222,15 +206,16 @@ main(int, char **)
assert(loaded);
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 crate = imageManager
auto vbo = renderResourceManager.CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer");
renderResourceManager.Write(vbo, 0, vertices.size() * sizeof vertices[0], vertices.data());
auto crate = renderResourceManager
.CreateTexture2D({
.m_Format = vk::Format::eR8G8B8A8Srgb,
.m_Extent = {imageFile.m_Width, imageFile.m_Height},
.m_Name = "Crate Texture",
})
.ToPointer();
vbo->Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
{
StagingBuffer imageStaging;
@ -333,70 +318,14 @@ main(int, char **)
imageStaging.Destroy(&device);
}
vk::Sampler sampler;
{
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 = renderResourceManager.CreateStorageBuffer(sizeof camera, "Camera UBO");
renderResourceManager.Write(ubo, 0, sizeof camera, &camera);
auto ubo = bufferManager.CreateUniformBuffer(sizeof camera, "Camera UBO").ToPointer();
ubo->Write(&device, 0, sizeof camera, &camera);
vk::DescriptorBufferInfo descriptorBufferInfo = {
.buffer = ubo->m_Buffer,
.offset = 0,
.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);
renderResourceManager.Commit(ubo);
renderResourceManager.Commit(vbo);
renderResourceManager.Commit(crate);
renderResourceManager.Update();
// Persistent variables
vk::Viewport viewport = {
@ -462,13 +391,13 @@ main(int, char **)
};
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)
{
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);
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();
INFO("Starting loop");
@ -488,7 +430,7 @@ main(int, char **)
Time::Update();
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());
@ -540,7 +482,9 @@ main(int, char **)
cmd.setViewport(0, 1, &viewport);
cmd.setScissor(0, 1, &scissor);
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.endRendering();
@ -565,17 +509,16 @@ main(int, char **)
}
device.WaitIdle();
device.m_Device.destroy(sampler, nullptr);
device.m_Device.destroy(descriptorPool, nullptr);
device.m_Device.destroy(copyPool, nullptr);
return 0;
}
Pipeline
CreatePipeline(const Device *device, const Swapchain *swapchain)
CreatePipeline(const systems::RenderResourceManager *resourceManager, const Swapchain *swapchain)
{
// Pipeline Setup
auto *device = resourceManager->m_Device;
auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE);
auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE);
@ -592,39 +535,18 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
},
}};
eastl::array descriptorSetLayoutBinding = {
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));
auto descriptorSetLayout = resourceManager->GetDescriptorSetLayout();
vk::PushConstantRange pcr = {
.stageFlags = vk::ShaderStageFlagBits::eAllGraphics,
.offset = 0,
.size = 12,
};
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
.setLayoutCount = 1,
.pSetLayouts = &descriptorSetLayout,
.pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pcr,
};
vk::PipelineLayout 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(fragmentShaderModule, nullptr);
return {device, pipelineLayout, pipeline, {descriptorSetLayout}};
return {device, pipelineLayout, pipeline, {}};
}
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 {
float2 UV0 : TEXCOORD0;
};
@ -7,13 +9,19 @@ struct FS_Output
float4 ColorTarget : SV_Target0;
};
[[vk::binding(1, 0)]] Texture2D<float4> Texture;
[[vk::binding(1, 0)]] SamplerState Sampler;
struct PCB
{
Handle CameraBuffer;
Handle VertexBuffer;
Handle Texture;
};
[[vk::push_constant]] PCB Block;
FS_Output main(FS_Input StageInput) {
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;
}

View File

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