[WIP] Moving ModelRender to new arch.

TODO: ImageView
This commit is contained in:
Anish Bhobe 2025-03-31 21:05:09 +02:00
parent afec1e3e32
commit 73c96dc56b
37 changed files with 1447 additions and 1906 deletions

View File

@ -15,6 +15,7 @@ INTERFACE
"queue_allocation.h"
"buffer.h"
"image.h"
"image_view.h"
"surface.h"
"size.h"
"type_traits.h"

View File

@ -26,7 +26,9 @@
#if !defined(NDEBUG)
#define VULKAN_HPP_ASSERT(expr) DEBUG_IF(!(expr), "Vulkan assert failed")
#endif
#include "type_traits.h"
#include "EASTL/intrusive_ptr.h"
#include <EASTL/fixed_string.h>
#include <EASTL/string.h>
@ -51,6 +53,7 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
#define Take(ELEMENT) eastl::exchange(ELEMENT, {})
#define TODO(MSG) assert(false && ("Unimplemented: " MSG))
#define FIX(MSG) static_assert(false && ("Unimplemented: " MSG))
[[nodiscard]] inline bool
Failed(const vk::Result result)
@ -212,12 +215,5 @@ struct fmt::formatter<eastl::fixed_string<TType, TCount, TOverflow>> : nested_fo
}
};
namespace aster
{
template <typename TRef, typename TVal>
TVal &
Deref(TRef ref)
{
return ref.Deref();
}
}
template <concepts::Manageable T>
using Ref = eastl::intrusive_ptr<T>;

View File

@ -7,6 +7,7 @@
#include "global.h"
struct StorageTexture;
struct Device;
[[nodiscard]] inline vk::Extent2D
@ -46,6 +47,28 @@ struct Image
u8 m_LayerCount = 0;
u8 m_MipLevels = 0;
constexpr static u8 SAMPLED_BIT = 1 << 7;
constexpr static u8 STORAGE_BIT = 1 << 6;
constexpr static u8 CUBE_BIT = 1 << 5;
[[nodiscard]] bool
IsSampled() const
{
return m_Flags_ & SAMPLED_BIT;
}
[[nodiscard]] bool
IsStorage() const
{
return m_Flags_ & STORAGE_BIT;
}
[[nodiscard]] bool
IsCube() const
{
return m_Flags_ & CUBE_BIT;
}
[[nodiscard]] bool
IsValid() const
{
@ -83,48 +106,114 @@ struct Image
}
void Destroy();
static bool
Conforms(const Image &)
{
return true;
}
};
namespace concepts
{
template <typename T>
concept Image = std::derived_from<T, ::Image> and Manageable<T>;
template <typename T>
concept ImageRef = Derefencable<T> and Image<DereferencesTo<T>>;
template <typename T>
concept SampledImage = requires() {
{ T::SAMPLED } -> std::convertible_to<bool>;
} and T::SAMPLED and Image<T>;
template <typename T>
concept SampledImageRef = Derefencable<T> and SampledImage<DereferencesTo<T>>;
template <typename T>
concept ImageCube = requires() {
{ T::CUBED } -> std::convertible_to<bool>;
} and T::CUBED and Image<T>;
template <typename T>
concept ImageCubeRef = Derefencable<T> and ImageCube<DereferencesTo<T>>;
template <typename T>
concept StorageImage = requires() {
{ T::STORAGE } -> std::convertible_to<bool>;
} and T::STORAGE and Image<T>;
template <typename T>
concept StorageImageRef = Derefencable<T> and StorageImage<DereferencesTo<T>>;
} // namespace concepts
struct Texture : Image
{
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipMapped, cstr name = nullptr);
constexpr static bool SAMPLED = true;
static bool
Conforms(const Image &other)
{
return other.IsSampled();
}
};
static_assert(sizeof(Texture) == sizeof(Image));
struct TextureCube : Texture
struct ImageCube : Image
{
void
Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped = false, cstr name = nullptr);
constexpr static bool CUBE = true;
static bool
Conforms(const Image &other)
{
return other.IsCube();
}
};
static_assert(sizeof(TextureCube) == sizeof(Image));
struct AttachmentImage : Image
struct TextureCube : Image
{
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, cstr name = nullptr);
constexpr static bool SAMPLED = true;
constexpr static bool CUBE = true;
static bool
Conforms(const Image &other)
{
return other.IsSampled() && other.IsCube();
}
};
static_assert(sizeof(AttachmentImage) == sizeof(Image));
struct DepthImage : Image
struct StorageImage : Image
{
void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr);
constexpr static bool STORAGE = true;
static bool
Conforms(const Image &other)
{
return other.IsStorage();
}
};
static_assert(sizeof(DepthImage) == sizeof(Image));
struct StorageTexture : Texture
struct StorageTexture : StorageImage
{
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isSampled, cstr name = nullptr);
constexpr static bool SAMPLED = true;
constexpr static bool STORAGE = true;
static bool
Conforms(const Image &other)
{
return other.IsStorage() && other.IsSampled();
}
};
static_assert(sizeof(StorageTexture) == sizeof(Image));
struct StorageTextureCube : StorageTexture
struct StorageTextureCube : StorageImage
{
void Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isSampled, bool isMipMapped = false,
cstr name = nullptr);
};
constexpr static bool SAMPLED = true;
constexpr static bool CUBE = true;
constexpr static bool STORAGE = true;
static_assert(sizeof(StorageTextureCube) == sizeof(Image));
static bool
Conforms(const Image &other)
{
return other.IsStorage() && other.IsSampled() && other.IsCube();
}
};

View File

@ -0,0 +1,26 @@
// =============================================
// Aster: image_view.h
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
#include "image.h"
template <concepts::Image TImage = Image>
struct View
{
using ImageType = TImage;
Ref<ImageType> m_Image;
vk::ImageView m_View = nullptr;
vk::Extent3D m_Extent;
std::atomic<u32> m_RefCount;
u8 m_EmptyPadding_ = 0;
u8 m_Flags_ = 0;
u8 m_LayerCount = 0;
u8 m_MipLevels = 0;
};
using ImageView = View<>;

View File

@ -6,6 +6,7 @@
#pragma once
struct Device;
struct Image;
namespace concepts
{
@ -16,6 +17,14 @@ concept RefCounted = requires(T a) {
{ a.IsReferenced() } -> std::convertible_to<bool>;
};
template <typename T>
concept Derefencable = requires(T a) {
{ *a };
};
template <Derefencable T>
using DereferencesTo = std::remove_cvref_t<decltype(*std::declval<T>())>;
template <typename T>
concept DeviceDestructible = requires(T a, Device *p) {
{ a.Destroy(p) } -> std::same_as<void>;

View File

@ -7,5 +7,8 @@ INTERFACE
"manager.h"
"buffer_manager.h"
"image_manager.h"
"image_view_manager.h"
"sampler_manager.h"
"render_resource_manager.h")
"resource.h"
"resource_manager.h"
"commit_manager.h")

View File

@ -5,20 +5,20 @@
#pragma once
#include "manager.h"
#include "aster/aster.h"
#include "aster/core/buffer.h"
#include "manager.h"
namespace systems
{
using BufferHandle = Manager<Buffer>::Handle;
class BufferManager final : public Manager<Buffer>
{
public:
BufferManager(const Device *device, const u32 maxCount, const u8 binding);
BufferManager(const Device *device, u32 maxCount);
[[nodiscard]] Handle CreateStorageBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Handle CreateUniformBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Handle CreateStagingBuffer(usize size, cstr name = nullptr);
};
} // namespace systems

View File

@ -16,73 +16,12 @@
#include "EASTL/intrusive_hash_map.h"
#include "EASTL/bonus/fixed_ring_buffer.h"
#include "resource.h"
namespace systems
{
class RenderResourceManager;
/**
* CommitEntry manages the lifetime of the committed resource.
* @tparam T Type of the committed resource.
*/
template <concepts::Manageable T>
class CommitEntry
{
private:
u32 m_Index;
explicit CommitEntry(const u32 index)
: m_Index{index}
{
AddRef();
}
friend class RenderResourceManager;
public:
CommitEntry(const CommitEntry &other)
: m_Index{other.m_Index}
{
AddRef();
}
CommitEntry(CommitEntry &&other) noexcept
: m_Index{other.m_Index}
{
AddRef();
}
CommitEntry &
operator=(const CommitEntry &other)
{
if (this == &other)
return *this;
m_Index = other.m_Index;
AddRef();
return *this;
}
CommitEntry &
operator=(CommitEntry &&other) noexcept
{
if (this == &other)
return *this;
m_Index = other.m_Index;
AddRef();
return *this;
}
~CommitEntry()
{
Release();
}
private:
void AddRef() const; ///< Increases the refcount in the RenderResourceManager.
void Release() const; ///< Decreases the refcount in the RenderResourceManager.
};
class RenderResourceManager
class CommitManager
{
private:
template <concepts::Manageable T>
@ -91,7 +30,7 @@ class RenderResourceManager
using Type = T;
using Manager = Manager<Type>;
using Handle = typename Manager::Handle;
using CommitE = CommitEntry<Type>;
using Resource = ResId<Type>;
struct Entry : public eastl::intrusive_hash_node_key<Handle>
{
@ -173,7 +112,7 @@ class RenderResourceManager
PIN_MEMORY(HandleMapper);
/// Returns a commit, and a bool signifying if it is a new commit.
std::tuple<CommitE, bool>
std::tuple<Resource, bool>
Create(const Handle &object)
{
// Get-from freelist
@ -184,7 +123,7 @@ class RenderResourceManager
{
it->AddRef();
auto i = GetIndex(*it);
return {CommitE{i}, false};
return {Resource{i}, false};
}
Entry &data = m_FreeList.Pop();
@ -196,17 +135,23 @@ class RenderResourceManager
auto i = GetIndex(data);
return {CommitE{i}, true};
return {Resource{i}, true};
}
Handle
GetHandle(const Resource &res)
{
return m_Data[res.m_Index].mKey;
}
void
AddRef(const CommitE &commit)
AddRef(const Resource &commit)
{
m_Data.at(commit.m_Index).AddRef();
}
void
Release(const CommitE &commit)
Release(const Resource &commit)
{
auto &entry = m_Data.at(commit.m_Index);
entry.Release();
@ -271,18 +216,58 @@ class RenderResourceManager
public:
const Device *m_Device;
RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages,
const SamplerHandle &defaultSampler);
~RenderResourceManager();
CommitManager(const Device *device, u32 maxBuffers, u32 maxImages, u32 maxStorageImages,
Ref<Sampler> defaultSampler);
~CommitManager();
PIN_MEMORY(RenderResourceManager);
PIN_MEMORY(CommitManager);
CommitEntry<Buffer> Commit(const BufferHandle &buffer);
CommitEntry<Image> Commit(const ImageHandle &handle);
CommitEntry<Image> Commit(const ImageHandle &image, const SamplerHandle &sampler);
// Commit Buffer
ResId<Buffer> CommitBuffer(const Ref<Buffer> &buffer);
// Commit Storage Images
ResId<StorageImage> CommitStorageImage(const concepts::StorageImageRef auto &image)
{
return CommitStorageImage(CastImage<StorageImage>(image));
}
ResId<StorageImage> CommitStorageImage(const Ref<StorageImage> &image);
// Sampled Images
ResId<Texture>
CommitTexture(const concepts::SampledImageRef auto &image, const Ref<Sampler> &sampler)
{
return CommitTexture(CastImage<Texture>(image), sampler);
}
ResId<Texture>
CommitTexture(const concepts::SampledImageRef auto &image)
{
return CommitTexture(CastImage<Texture>(image));
}
ResId<Texture> CommitTexture(const Ref<Texture> &handle);
ResId<Texture> CommitTexture(const Ref<Texture> &image, const Ref<Sampler> &sampler);
void Update();
Ref<Buffer>
FetchHandle(const ResId<Buffer> &id)
{
return m_Buffers.GetHandle(id);
}
Ref<Texture>
FetchHandle(const ResId<Texture> &id)
{
return m_Images.GetHandle(id);
}
Ref<StorageImage>
FetchHandle(const ResId<StorageImage> &id)
{
return m_StorageImages.GetHandle(id);
}
[[nodiscard]] const vk::DescriptorSetLayout &
GetDescriptorSetLayout() const
{
@ -295,10 +280,10 @@ class RenderResourceManager
return m_DescriptorSet;
}
static RenderResourceManager *
Instance()
static CommitManager &Instance()
{
return m_Instance;
assert(m_Instance);
return *m_Instance;
}
private:
@ -308,54 +293,68 @@ class RenderResourceManager
constexpr static u8 BUFFER_BINDING_INDEX = 0x0;
constexpr static u8 IMAGE_BINDING_INDEX = 0x1;
constexpr static u8 STORAGE_IMAGE_BINDING_INDEX = 0x2;
HandleMapper<Buffer> m_Buffers;
HandleMapper<Image> m_Images;
SamplerHandle m_DefaultSampler;
HandleMapper<Texture> m_Images;
HandleMapper<StorageImage> m_StorageImages;
Ref<Sampler> m_DefaultSampler;
eastl::vector<vk::WriteDescriptorSet> m_Writes;
eastl::deque<WriteInfo> m_WriteInfos;
// eastl::vector<WriteOwner> m_WriteOwner;
static RenderResourceManager *m_Instance;
friend CommitEntry<Buffer>;
friend CommitEntry<Image>;
static CommitManager *m_Instance;
friend ResId<Buffer>;
friend ResId<Texture>;
friend ResId<StorageImage>;
void
AddRef(const CommitEntry<Buffer> &handle)
AddRef(const ResId<Buffer> &handle)
{
m_Buffers.AddRef(handle);
}
void
AddRef(const CommitEntry<Image> &handle)
AddRef(const ResId<Texture> &handle)
{
m_Images.AddRef(handle);
}
void
AddRef(const ResId<StorageImage> &handle)
{
m_StorageImages.AddRef(handle);
}
void
Release(const CommitEntry<Buffer> &handle)
Release(const ResId<Buffer> &handle)
{
m_Buffers.Release(handle);
}
void
Release(const CommitEntry<Image> &handle)
Release(const ResId<Texture> &handle)
{
m_Images.Release(handle);
}
void
Release(const ResId<StorageImage> &handle)
{
m_StorageImages.Release(handle);
}
};
template <concepts::Manageable T>
void
CommitEntry<T>::AddRef() const
ResId<T>::AddRef() const
{
RenderResourceManager::Instance()->AddRef(*this);
CommitManager::Instance().AddRef(*this);
}
template <concepts::Manageable T>
void
CommitEntry<T>::Release() const
ResId<T>::Release() const
{
RenderResourceManager::Instance()->Release(*this);
CommitManager::Instance().Release(*this);
}
} // namespace systems

View File

@ -5,13 +5,22 @@
#pragma once
#include "manager.h"
#include "aster/aster.h"
#include "aster/core/image.h"
#include "manager.h"
namespace systems
{
template <std::derived_from<Image> TTo>
static Ref<TTo>
CastImage(const concepts::ImageRef auto &from)
{
assert(TTo::Conforms(*from.get()));
return Recast<TTo *>(from.get());
}
struct Texture2DCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
@ -45,16 +54,30 @@ struct DepthStencilImageCreateInfo
cstr m_Name = nullptr;
};
using ImageHandle = Manager<Image>::Handle;
class ImageManager final : public Manager<Image>
{
public:
ImageManager(const Device *device, const u32 maxCount, const u8 binding);
ImageManager(const Device *device, const u32 maxCount);
[[nodiscard]] Handle CreateTexture2D(const Texture2DCreateInfo &createInfo);
[[nodiscard]] Handle CreateTextureCube(const TextureCubeCreateInfo &createInfo);
[[nodiscard]] Handle CreateAttachment(const AttachmentCreateInfo &createInfo);
[[nodiscard]] Handle CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo);
template <concepts::Image T>
[[nodiscard]] Ref<T>
CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
auto handle = CreateTexture2D(createInfo);
return CastImage<T>(handle);
}
template <concepts::Image T>
[[nodiscard]] Ref<T>
CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
auto handle = CreateTextureCube(createInfo);
return CastImage<T>(handle);
}
[[nodiscard]] Ref<Image> CreateTexture2D(const Texture2DCreateInfo &createInfo);
[[nodiscard]] Ref<ImageCube> CreateTextureCube(const TextureCubeCreateInfo &createInfo);
[[nodiscard]] Ref<Image> CreateAttachment(const AttachmentCreateInfo &createInfo);
[[nodiscard]] Ref<Image> CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo);
};
} // namespace systems

View File

@ -0,0 +1,32 @@
// =============================================
// Aster: image_manager.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "manager.h"
#include "aster/aster.h"
#include "aster/core/image_view.h"
namespace systems
{
//
//template <std::derived_from<Image> TTo>
//static Ref<TTo>
//CastView(const concepts::ImageRef auto &from)
//{
// assert(TTo::Conforms(*from.get()));
// return Recast<TTo *>(from.get());
//}
class ImageViewManager final : public Manager<ImageView>
{
public:
ImageViewManager(const Device *device, const u32 maxCount);
};
} // namespace systems

View File

@ -21,28 +21,22 @@ class Manager
{
public:
using Type = T;
using Handle = eastl::intrusive_ptr<Type>;
using Handle = Ref<T>;
/**
* Constructor for the Manager class template.
* @param device Device with which resources are created.
* @param maxCount Max number of resources that can be created (maxCount <= Handle::INDEX_MASK)
* @param binding The shader binding at which this manager will bind its resources.
*/
explicit Manager(const Device *device, const u32 maxCount, const u8 binding)
explicit Manager(const Device *device, const u32 maxCount)
: m_Data{maxCount}
, m_Binding{binding}
, m_Device{device}
{
assert(!m_Instance && "Attempting to initialize a second Manager");
u32 i = 0;
for (auto& element : m_Data)
{
*Recast<u32 *>(&element) = ++i;
}
m_Instance = this;
}
virtual ~Manager()
@ -61,8 +55,6 @@ class Manager
m_FreeHead = 0;
m_Device = nullptr;
m_Instance = nullptr;
}
void
@ -85,9 +77,6 @@ class Manager
private:
eastl::vector<Type> m_Data; // Data also keeps the freelist during 'not use'.
u32 m_FreeHead = 0;
u8 m_Binding = 0;
static Manager *m_Instance;
protected:
const Device *m_Device;

View File

@ -0,0 +1,92 @@
// =============================================
// Aster: resource.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include <EASTL/intrusive_ptr.h>
namespace systems
{
/**
* ResId manages the lifetime of the committed resource.
* @tparam T Type of the committed resource.
*/
template <concepts::Manageable T>
class ResId
{
public:
constexpr static u32 INVALID = MaxValue<u32>;
private:
u32 m_Index;
explicit ResId(const u32 index)
: m_Index{index}
{
AddRef();
}
friend class CommitManager;
public:
static ResId
Null()
{
return ResId{INVALID};
}
ResId(const ResId &other)
: m_Index{other.m_Index}
{
AddRef();
}
ResId(ResId &&other) noexcept
: m_Index{other.m_Index}
{
AddRef();
}
ResId &
operator=(const ResId &other)
{
if (this == &other)
return *this;
m_Index = other.m_Index;
AddRef();
return *this;
}
ResId &
operator=(ResId &&other) noexcept
{
if (this == &other)
return *this;
m_Index = other.m_Index;
AddRef();
return *this;
}
~ResId()
{
Release();
}
private:
void AddRef() const; ///< Increases the refcount in the CommitManager.
void Release() const; ///< Decreases the refcount in the CommitManager.
};
struct NullId
{
template <concepts::Manageable T>
operator ResId<T>()
{
return ResId<T>::Null();
}
};
} // namespace systems

View File

@ -0,0 +1,52 @@
// =============================================
// Aster: resource_manager.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "aster/aster.h"
#include "buffer_manager.h"
#include "image_manager.h"
#include "sampler_manager.h"
namespace systems
{
class ResourceManager
{
BufferManager m_Buffers;
ImageManager m_Images;
SamplerManager m_Samplers;
public:
ResourceManager(const Device *device, u32 maxBufferCount, u32 maxImageCount, u32 maxSamplerCount)
: m_Buffers{device, maxBufferCount}
, m_Images{device, maxImageCount}
, m_Samplers{device, maxSamplerCount}
{
}
BufferManager &
Buffers()
{
return m_Buffers;
}
ImageManager &
Images()
{
return m_Images;
}
SamplerManager &
Samplers()
{
return m_Samplers;
}
~ResourceManager() = default;
PIN_MEMORY(ResourceManager);
};
} // namespace systems

View File

@ -5,37 +5,88 @@
#pragma once
#include "EASTL/vector_map.h"
#include "manager.h"
#include "EASTL/hash_map.h"
#include "aster/aster.h"
#include "aster/core/sampler.h"
#include "manager.h"
#include "EASTL/vector_map.h"
template <>
struct eastl::hash<vk::SamplerCreateInfo>
{
usize
operator()(const vk::SamplerCreateInfo &createInfo) const noexcept
{
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;
}
};
namespace systems
{
using SamplerHandle = Manager<Sampler>::Handle;
struct SamplerCreateInfo : vk::SamplerCreateInfo
struct SamplerCreateInfo
{
cstr m_Name = nullptr;
vk::SamplerCreateFlags m_Flags = {};
vk::Filter m_MagFilter = vk::Filter::eLinear;
vk::Filter m_MinFilter = vk::Filter::eLinear;
vk::SamplerMipmapMode m_MipmapMode = vk::SamplerMipmapMode::eLinear;
vk::SamplerAddressMode m_AddressModeU = vk::SamplerAddressMode::eRepeat;
vk::SamplerAddressMode m_AddressModeV = vk::SamplerAddressMode::eRepeat;
vk::SamplerAddressMode m_AddressModeW = vk::SamplerAddressMode::eRepeat;
vk::BorderColor m_BorderColor = vk::BorderColor::eFloatOpaqueBlack;
vk::CompareOp m_CompareOp = vk::CompareOp::eNever;
f32 m_MipLodBias = 0.0f;
f32 m_MaxAnisotropy = 16.0f;
f32 m_MinLod = 0;
f32 m_MaxLod = VK_LOD_CLAMP_NONE;
bool m_AnisotropyEnable = true;
bool m_CompareEnable = false;
bool m_NormalizedCoordinates = true;
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,
}
explicit operator vk::SamplerCreateInfo() const
{
return {
.flags = m_Flags,
.magFilter = m_MagFilter,
.minFilter = m_MinFilter,
.mipmapMode = m_MipmapMode,
.addressModeU = m_AddressModeU,
.addressModeV = m_AddressModeV,
.addressModeW = m_AddressModeW,
.mipLodBias = m_MipLodBias,
.anisotropyEnable = m_AnisotropyEnable,
.maxAnisotropy = m_MaxAnisotropy,
.compareEnable = m_CompareEnable,
.compareOp = m_CompareOp,
.minLod = m_MinLod,
.maxLod = m_MaxLod,
.borderColor = m_BorderColor,
.unnormalizedCoordinates = !m_NormalizedCoordinates,
};
}
};
@ -46,12 +97,12 @@ struct SamplerCreateInfo : vk::SamplerCreateInfo
*/
class SamplerManager final : public Manager<Sampler>
{
eastl::vector_map<usize, Handle> m_HashToSamplerIdx;
eastl::hash_map<vk::SamplerCreateInfo, Handle> m_HashToSamplerIdx;
public:
SamplerManager(const Device *device, const u32 maxCount, const u8 typeId);
SamplerManager(const Device *device, const u32 maxCount);
~SamplerManager() override;
SamplerHandle Create(const SamplerCreateInfo &createInfo);
Ref<Sampler> CreateSampler(const SamplerCreateInfo &createInfo);
};
} // namespace systems

View File

@ -3,4 +3,6 @@
cmake_minimum_required(VERSION 3.13)
target_sources(aster_core
INTERFACE "logger.h" "intrusive_slist.h")
INTERFACE
"logger.h"
"intrusive_slist.h")

View File

@ -17,407 +17,407 @@ Image::Destroy()
vmaDestroyImage(m_Device->m_Allocator, Take(m_Image), m_Allocation);
m_Flags_ = 0;
}
void
Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageFormat, const bool isMipMapped,
const cstr name)
{
WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
extent.width, extent.height, name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
if (isMipMapped)
{
usage |= vk::ImageUsageFlagBits::eTransferSrc;
}
vk::ImageCreateInfo imageCreateInfo = {
.imageType = vk::ImageType::e2D,
.format = imageFormat,
.extent = ToExtent3D(extent, 1),
.mipLevels = mipLevels,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = usage,
.sharingMode = vk::SharingMode::eExclusive,
.initialLayout = vk::ImageLayout::eUndefined,
};
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
vk::ImageView view;
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageFormat,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image;
m_View = view;
m_Allocation = allocation;
m_Extent = imageCreateInfo.extent;
m_LayerCount = 1;
m_MipLevels = mipLevels;
device->SetName(m_Image, name);
}
/*
Cube map Faces info.
TODO: Correct this based on the actual layout for upside down viewport.
| Axis | Layer | Up |
|:----:|:-----:|:--:|
| +x | 0 | -y |
| -x | 1 | -y |
| +y | 2 | +z |
| -y | 3 | -z |
| +z | 4 | -y |
| -z | 5 | -y |
Remember, we use upside down viewport.
*/
void
TextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped, cstr name)
{
WARN_IF(!IsPowerOfTwo(cubeSide), "Image Cube {1} has side {0}x{0} (Non Power of Two)", cubeSide,
name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(cubeSide))) : 1;
auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
if (isMipMapped)
{
usage |= vk::ImageUsageFlagBits::eTransferSrc;
}
const vk::Extent3D extent = {.width = cubeSide, .height = cubeSide, .depth = 1};
vk::ImageCreateInfo imageCreateInfo = {
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
.imageType = vk::ImageType::e2D,
.format = imageFormat,
.extent = extent,
.mipLevels = mipLevels,
.arrayLayers = 6,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = usage,
.sharingMode = vk::SharingMode::eExclusive,
.initialLayout = vk::ImageLayout::eUndefined,
};
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
vk::ImageView view;
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::eCube,
.format = imageFormat,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = 6,
},
};
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image;
m_View = view;
m_Allocation = allocation;
m_Extent = extent;
m_MipLevels = mipLevels;
m_LayerCount = 6;
device->SetName(m_Image, name);
}
void
AttachmentImage::Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, cstr name)
{
vk::ImageCreateInfo imageCreateInfo = {
.imageType = vk::ImageType::e2D,
.format = imageFormat,
.extent = ToExtent3D(extent, 1),
.mipLevels = 1,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc,
.sharingMode = vk::SharingMode::eExclusive,
.initialLayout = vk::ImageLayout::eUndefined,
};
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate depth buffer. Cause: {}", result) THEN_ABORT(result);
vk::ImageView view;
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageFormat,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create attachment image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image;
m_View = view;
m_Allocation = allocation;
m_Extent = imageCreateInfo.extent;
m_MipLevels = 1;
m_LayerCount = 1;
device->SetName(m_Image, name);
}
void
DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
{
constexpr vk::Format imageFormat = vk::Format::eD24UnormS8Uint;
vk::ImageCreateInfo imageCreateInfo = {
.imageType = vk::ImageType::e2D,
.format = imageFormat,
.extent = ToExtent3D(extent, 1),
.mipLevels = 1,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment,
.sharingMode = vk::SharingMode::eExclusive,
.initialLayout = vk::ImageLayout::eUndefined,
};
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate depth buffer. Cause: {}", result) THEN_ABORT(result);
vk::ImageView view;
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageFormat,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eDepth,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create depth image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image;
m_View = view;
m_Allocation = allocation;
m_Extent = imageCreateInfo.extent;
m_MipLevels = 1;
m_LayerCount = 1;
device->SetName(m_Image, name);
}
void
StorageTexture::Init(const Device *device, vk::Extent2D extent, const vk::Format imageFormat, const bool isSampled,
cstr name)
{
// Reasoning:
// Transfer Src and Dst to copy to and from the buffer since Storage will often be loaded with info, and read for
// results.
auto usage =
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
if (isSampled)
{
WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
extent.width, extent.height, name ? name : "<unnamed>");
usage |= vk::ImageUsageFlagBits::eSampled;
}
vk::ImageCreateInfo imageCreateInfo = {
.imageType = vk::ImageType::e2D,
.format = imageFormat,
.extent = ToExtent3D(extent, 1),
.mipLevels = 1,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = usage,
.sharingMode = vk::SharingMode::eExclusive,
.initialLayout = vk::ImageLayout::eUndefined,
};
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageFormat,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image;
m_View = view;
m_Allocation = allocation;
m_Extent = imageCreateInfo.extent;
m_MipLevels = 1;
m_LayerCount = 1;
device->SetName(m_Image, name);
}
void
StorageTextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isSampled, bool isMipMapped,
cstr name)
{
// Reasoning:
// Transfer Src and Dst to copy to and from the buffer since Storage will often be loaded with info, and read for
// results.
auto usage =
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
if (isSampled)
{
WARN_IF(!IsPowerOfTwo(cubeSide), "Image {1} is {0}x{0} (Non Power of Two)", cubeSide,
name ? name : "<unnamed>");
usage |= vk::ImageUsageFlagBits::eSampled;
}
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(cubeSide))) : 1;
vk::ImageCreateInfo imageCreateInfo = {
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
.imageType = vk::ImageType::e2D,
.format = imageFormat,
.extent = {cubeSide, cubeSide, 1},
.mipLevels = mipLevels,
.arrayLayers = 6,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = usage,
.sharingMode = vk::SharingMode::eExclusive,
.initialLayout = vk::ImageLayout::eUndefined,
};
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::eCube,
.format = imageFormat,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = 6,
},
};
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image;
m_View = view;
m_Allocation = allocation;
m_Extent = imageCreateInfo.extent;
m_MipLevels = mipLevels;
m_LayerCount = 6;
device->SetName(m_Image, name);
}
//
//void
//Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageFormat, const bool isMipMapped,
// const cstr name)
//{
// WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
// extent.width, extent.height, name ? name : "<unnamed>");
//
// const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
//
// auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
// if (isMipMapped)
// {
// usage |= vk::ImageUsageFlagBits::eTransferSrc;
// }
//
// vk::ImageCreateInfo imageCreateInfo = {
// .imageType = vk::ImageType::e2D,
// .format = imageFormat,
// .extent = ToExtent3D(extent, 1),
// .mipLevels = mipLevels,
// .arrayLayers = 1,
// .samples = vk::SampleCountFlagBits::e1,
// .tiling = vk::ImageTiling::eOptimal,
// .usage = usage,
// .sharingMode = vk::SharingMode::eExclusive,
// .initialLayout = vk::ImageLayout::eUndefined,
// };
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
// .flags = {},
// .usage = VMA_MEMORY_USAGE_AUTO,
// };
//
// VkImage image;
// VmaAllocation allocation;
// auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
// &allocationCreateInfo, &image, &allocation, nullptr));
// ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
//
// vk::ImageView view;
// vk::ImageViewCreateInfo imageViewCreateInfo = {
// .image = image,
// .viewType = vk::ImageViewType::e2D,
// .format = imageFormat,
// .components = {},
// .subresourceRange =
// {
// .aspectMask = vk::ImageAspectFlagBits::eColor,
// .baseMipLevel = 0,
// .levelCount = mipLevels,
// .baseArrayLayer = 0,
// .layerCount = 1,
// },
// };
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
// ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
//
// m_Device = device;
// m_Image = image;
// m_View = view;
// m_Allocation = allocation;
// m_Extent = imageCreateInfo.extent;
// m_LayerCount = 1;
// m_MipLevels = mipLevels;
//
// device->SetName(m_Image, name);
//}
//
///*
//Cube map Faces info.
//
//TODO: Correct this based on the actual layout for upside down viewport.
//
//| Axis | Layer | Up |
//|:----:|:-----:|:--:|
//| +x | 0 | -y |
//| -x | 1 | -y |
//| +y | 2 | +z |
//| -y | 3 | -z |
//| +z | 4 | -y |
//| -z | 5 | -y |
//
//Remember, we use upside down viewport.
//
//*/
//
//void
//TextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped, cstr name)
//{
// WARN_IF(!IsPowerOfTwo(cubeSide), "Image Cube {1} has side {0}x{0} (Non Power of Two)", cubeSide,
// name ? name : "<unnamed>");
//
// const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(cubeSide))) : 1;
//
// auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
// if (isMipMapped)
// {
// usage |= vk::ImageUsageFlagBits::eTransferSrc;
// }
//
// const vk::Extent3D extent = {.width = cubeSide, .height = cubeSide, .depth = 1};
//
// vk::ImageCreateInfo imageCreateInfo = {
// .flags = vk::ImageCreateFlagBits::eCubeCompatible,
// .imageType = vk::ImageType::e2D,
// .format = imageFormat,
// .extent = extent,
// .mipLevels = mipLevels,
// .arrayLayers = 6,
// .samples = vk::SampleCountFlagBits::e1,
// .tiling = vk::ImageTiling::eOptimal,
// .usage = usage,
// .sharingMode = vk::SharingMode::eExclusive,
// .initialLayout = vk::ImageLayout::eUndefined,
// };
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
// .flags = {},
// .usage = VMA_MEMORY_USAGE_AUTO,
// };
//
// VkImage image;
// VmaAllocation allocation;
// auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
// &allocationCreateInfo, &image, &allocation, nullptr));
// ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
//
// vk::ImageView view;
// vk::ImageViewCreateInfo imageViewCreateInfo = {
// .image = image,
// .viewType = vk::ImageViewType::eCube,
// .format = imageFormat,
// .components = {},
// .subresourceRange =
// {
// .aspectMask = vk::ImageAspectFlagBits::eColor,
// .baseMipLevel = 0,
// .levelCount = mipLevels,
// .baseArrayLayer = 0,
// .layerCount = 6,
// },
// };
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
// ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
//
// m_Device = device;
// m_Image = image;
// m_View = view;
// m_Allocation = allocation;
// m_Extent = extent;
// m_MipLevels = mipLevels;
// m_LayerCount = 6;
//
// device->SetName(m_Image, name);
//}
//
//void
//AttachmentImage::Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, cstr name)
//{
// vk::ImageCreateInfo imageCreateInfo = {
// .imageType = vk::ImageType::e2D,
// .format = imageFormat,
// .extent = ToExtent3D(extent, 1),
// .mipLevels = 1,
// .arrayLayers = 1,
// .samples = vk::SampleCountFlagBits::e1,
// .tiling = vk::ImageTiling::eOptimal,
// .usage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc,
// .sharingMode = vk::SharingMode::eExclusive,
// .initialLayout = vk::ImageLayout::eUndefined,
// };
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
// .flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
// .usage = VMA_MEMORY_USAGE_AUTO,
// };
//
// VkImage image;
// VmaAllocation allocation;
// auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
// &allocationCreateInfo, &image, &allocation, nullptr));
// ERROR_IF(Failed(result), "Could not allocate depth buffer. Cause: {}", result) THEN_ABORT(result);
//
// vk::ImageView view;
// vk::ImageViewCreateInfo imageViewCreateInfo = {
// .image = image,
// .viewType = vk::ImageViewType::e2D,
// .format = imageFormat,
// .components = {},
// .subresourceRange =
// {
// .aspectMask = vk::ImageAspectFlagBits::eColor,
// .baseMipLevel = 0,
// .levelCount = 1,
// .baseArrayLayer = 0,
// .layerCount = 1,
// },
// };
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
// ERROR_IF(Failed(result), "Could not create attachment image view {}. Cause: {}", name, result) THEN_ABORT(result);
//
// m_Device = device;
// m_Image = image;
// m_View = view;
// m_Allocation = allocation;
// m_Extent = imageCreateInfo.extent;
// m_MipLevels = 1;
// m_LayerCount = 1;
//
// device->SetName(m_Image, name);
//}
//
//void
//DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
//{
// constexpr vk::Format imageFormat = vk::Format::eD24UnormS8Uint;
// vk::ImageCreateInfo imageCreateInfo = {
// .imageType = vk::ImageType::e2D,
// .format = imageFormat,
// .extent = ToExtent3D(extent, 1),
// .mipLevels = 1,
// .arrayLayers = 1,
// .samples = vk::SampleCountFlagBits::e1,
// .tiling = vk::ImageTiling::eOptimal,
// .usage = vk::ImageUsageFlagBits::eDepthStencilAttachment,
// .sharingMode = vk::SharingMode::eExclusive,
// .initialLayout = vk::ImageLayout::eUndefined,
// };
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
// .flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
// .usage = VMA_MEMORY_USAGE_AUTO,
// };
//
// VkImage image;
// VmaAllocation allocation;
// auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
// &allocationCreateInfo, &image, &allocation, nullptr));
// ERROR_IF(Failed(result), "Could not allocate depth buffer. Cause: {}", result) THEN_ABORT(result);
//
// vk::ImageView view;
// vk::ImageViewCreateInfo imageViewCreateInfo = {
// .image = image,
// .viewType = vk::ImageViewType::e2D,
// .format = imageFormat,
// .components = {},
// .subresourceRange =
// {
// .aspectMask = vk::ImageAspectFlagBits::eDepth,
// .baseMipLevel = 0,
// .levelCount = 1,
// .baseArrayLayer = 0,
// .layerCount = 1,
// },
// };
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
// ERROR_IF(Failed(result), "Could not create depth image view {}. Cause: {}", name, result) THEN_ABORT(result);
//
// m_Device = device;
// m_Image = image;
// m_View = view;
// m_Allocation = allocation;
// m_Extent = imageCreateInfo.extent;
// m_MipLevels = 1;
// m_LayerCount = 1;
//
// device->SetName(m_Image, name);
//}
//
//void
//StorageTexture::Init(const Device *device, vk::Extent2D extent, const vk::Format imageFormat, const bool isSampled,
// cstr name)
//{
// // Reasoning:
// // Transfer Src and Dst to copy to and from the buffer since Storage will often be loaded with info, and read for
// // results.
// auto usage =
// vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
// if (isSampled)
// {
// WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
// extent.width, extent.height, name ? name : "<unnamed>");
// usage |= vk::ImageUsageFlagBits::eSampled;
// }
//
// vk::ImageCreateInfo imageCreateInfo = {
// .imageType = vk::ImageType::e2D,
// .format = imageFormat,
// .extent = ToExtent3D(extent, 1),
// .mipLevels = 1,
// .arrayLayers = 1,
// .samples = vk::SampleCountFlagBits::e1,
// .tiling = vk::ImageTiling::eOptimal,
// .usage = usage,
// .sharingMode = vk::SharingMode::eExclusive,
// .initialLayout = vk::ImageLayout::eUndefined,
// };
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
// .flags = {},
// .usage = VMA_MEMORY_USAGE_AUTO,
// };
//
// VkImage image;
// VmaAllocation allocation;
// auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
// &allocationCreateInfo, &image, &allocation, nullptr));
// ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
//
// vk::ImageView view;
// const vk::ImageViewCreateInfo imageViewCreateInfo = {
// .image = image,
// .viewType = vk::ImageViewType::e2D,
// .format = imageFormat,
// .components = {},
// .subresourceRange =
// {
// .aspectMask = vk::ImageAspectFlagBits::eColor,
// .baseMipLevel = 0,
// .levelCount = 1,
// .baseArrayLayer = 0,
// .layerCount = 1,
// },
// };
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
// ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
//
// m_Device = device;
// m_Image = image;
// m_View = view;
// m_Allocation = allocation;
// m_Extent = imageCreateInfo.extent;
// m_MipLevels = 1;
// m_LayerCount = 1;
//
// device->SetName(m_Image, name);
//}
//
//void
//StorageTextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isSampled, bool isMipMapped,
// cstr name)
//{
// // Reasoning:
// // Transfer Src and Dst to copy to and from the buffer since Storage will often be loaded with info, and read for
// // results.
// auto usage =
// vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
// if (isSampled)
// {
// WARN_IF(!IsPowerOfTwo(cubeSide), "Image {1} is {0}x{0} (Non Power of Two)", cubeSide,
// name ? name : "<unnamed>");
// usage |= vk::ImageUsageFlagBits::eSampled;
// }
//
// const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(cubeSide))) : 1;
//
// vk::ImageCreateInfo imageCreateInfo = {
// .flags = vk::ImageCreateFlagBits::eCubeCompatible,
// .imageType = vk::ImageType::e2D,
// .format = imageFormat,
// .extent = {cubeSide, cubeSide, 1},
// .mipLevels = mipLevels,
// .arrayLayers = 6,
// .samples = vk::SampleCountFlagBits::e1,
// .tiling = vk::ImageTiling::eOptimal,
// .usage = usage,
// .sharingMode = vk::SharingMode::eExclusive,
// .initialLayout = vk::ImageLayout::eUndefined,
// };
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
// .flags = {},
// .usage = VMA_MEMORY_USAGE_AUTO,
// };
//
// VkImage image;
// VmaAllocation allocation;
// auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
// &allocationCreateInfo, &image, &allocation, nullptr));
// ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
//
// vk::ImageView view;
// const vk::ImageViewCreateInfo imageViewCreateInfo = {
// .image = image,
// .viewType = vk::ImageViewType::eCube,
// .format = imageFormat,
// .components = {},
// .subresourceRange =
// {
// .aspectMask = vk::ImageAspectFlagBits::eColor,
// .baseMipLevel = 0,
// .levelCount = mipLevels,
// .baseArrayLayer = 0,
// .layerCount = 6,
// },
// };
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
// ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
//
// m_Device = device;
// m_Image = image;
// m_View = view;
// m_Allocation = allocation;
// m_Extent = imageCreateInfo.extent;
// m_MipLevels = mipLevels;
// m_LayerCount = 6;
//
// device->SetName(m_Image, name);
//}

View File

@ -8,4 +8,4 @@ PRIVATE
"buffer_manager.cpp"
"image_manager.cpp"
"sampler_manager.cpp"
"render_resource_manager.cpp")
"commit_manager.cpp")

View File

@ -7,16 +7,15 @@
using namespace systems;
Manager<Buffer> *Manager<Buffer>::m_Instance = nullptr;
BufferHandle
Ref<Buffer>
BufferManager::CreateStorageBuffer(const usize size, const cstr name)
{
auto object = Alloc();
// TODO: Storage and Index buffer are set.
// This is hacky and should be improved.
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer |
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer |
vk::BufferUsageFlagBits::eIndexBuffer |
vk::BufferUsageFlagBits::eShaderDeviceAddress;
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
@ -27,7 +26,7 @@ BufferManager::CreateStorageBuffer(const usize size, const cstr name)
return object;
}
Manager<Buffer>::Handle
Ref<Buffer>
BufferManager::CreateUniformBuffer(const usize size, const cstr name)
{
auto object = Alloc();
@ -42,7 +41,21 @@ BufferManager::CreateUniformBuffer(const usize size, const cstr name)
return object;
}
BufferManager::BufferManager(const Device *device, const u32 maxCount, const u8 binding)
: Manager{device, maxCount, binding}
Manager<Buffer>::Handle
BufferManager::CreateStagingBuffer(const usize size, const cstr name)
{
auto object = Alloc();
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eTransferSrc;
constexpr VmaAllocationCreateFlags createFlags =
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
object->Allocate(m_Device, size, usage, createFlags, memoryUsage, name);
return object;
}
BufferManager::BufferManager(const Device *device, const u32 maxCount)
: Manager{device, maxCount}
{
}

View File

@ -3,7 +3,7 @@
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/render_resource_manager.h"
#include "systems/commit_manager.h"
#include "EASTL/array.h"
#include "core/device.h"
@ -33,14 +33,15 @@
using namespace systems;
RenderResourceManager *RenderResourceManager::m_Instance = nullptr;
CommitManager *CommitManager::m_Instance = nullptr;
RenderResourceManager::RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages,
const SamplerHandle &defaultSampler)
CommitManager::CommitManager(const Device *device, u32 const maxBuffers, const u32 maxImages, const u32 maxStorageImages,
Ref<Sampler> defaultSampler)
: m_Device{device}
, m_Buffers{maxBuffers}
, m_Images{maxImages}
, m_DefaultSampler{defaultSampler}
, m_StorageImages{maxStorageImages}
, m_DefaultSampler{std::move(defaultSampler)}
{
assert(!m_Instance);
@ -80,12 +81,12 @@ RenderResourceManager::RenderResourceManager(const Device *device, u32 const max
.descriptorCount = Cast<u32>(maxImages),
.stageFlags = vk::ShaderStageFlagBits::eAll,
},
// vk::DescriptorSetLayoutBinding{
// .binding = STORAGE_TEXTURE_BINDING_INDEX,
// .descriptorType = vk::DescriptorType::eStorageImage,
// .descriptorCount = Cast<u32>(storageTexturesCount),
// .stageFlags = vk::ShaderStageFlagBits::eAll,
// },
vk::DescriptorSetLayoutBinding{
.binding = STORAGE_IMAGE_BINDING_INDEX,
.descriptorType = vk::DescriptorType::eStorageImage,
.descriptorCount = Cast<u32>(maxStorageImages),
.stageFlags = vk::ShaderStageFlagBits::eAll,
},
};
vk::DescriptorBindingFlags bindingFlags =
@ -125,7 +126,7 @@ RenderResourceManager::RenderResourceManager(const Device *device, u32 const max
m_Instance = this;
}
RenderResourceManager::~RenderResourceManager()
CommitManager::~CommitManager()
{
m_Device->m_Device.destroy(m_SetLayout, nullptr);
m_Device->m_Device.destroy(m_DescriptorPool, nullptr);
@ -148,8 +149,8 @@ RenderResourceManager::~RenderResourceManager()
#endif
}
CommitEntry<Buffer>
RenderResourceManager::Commit(const BufferHandle &buffer)
ResId<Buffer>
CommitManager::CommitBuffer(const Ref<Buffer> &buffer)
{
auto [commit, isNew] = m_Buffers.Create(buffer);
@ -173,14 +174,38 @@ RenderResourceManager::Commit(const BufferHandle &buffer)
return commit;
}
CommitEntry<Image>
RenderResourceManager::Commit(const ImageHandle &handle)
ResId<StorageImage>
CommitManager::CommitStorageImage(const Ref<StorageImage> &image)
{
return Commit(handle, m_DefaultSampler);
auto [commit, isNew] = m_StorageImages.Create(image);
if (!isNew)
return commit;
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
.sampler = nullptr,
.imageView = image->m_View,
.imageLayout = vk::ImageLayout::eGeneral,
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = STORAGE_IMAGE_BINDING_INDEX,
.dstArrayElement = commit.m_Index,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageImage,
.pImageInfo = &m_WriteInfos.back().uImageInfo,
});
return commit;
}
CommitEntry<Image>
RenderResourceManager::Commit(const ImageHandle &image, const SamplerHandle &sampler)
ResId<Texture>
CommitManager::CommitTexture(const Ref<Texture> &handle)
{
return CommitTexture(handle, m_DefaultSampler);
}
ResId<Texture>
CommitManager::CommitTexture(const Ref<Texture> &image, const Ref<Sampler> &sampler)
{
auto [commit, isNew] = m_Images.Create(image);
if (!isNew)
@ -203,23 +228,23 @@ RenderResourceManager::Commit(const ImageHandle &image, const SamplerHandle &sam
return commit;
}
RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info)
CommitManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info)
: uBufferInfo{info}
{
}
RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorImageInfo &info)
CommitManager::WriteInfo::WriteInfo(const vk::DescriptorImageInfo &info)
: uImageInfo{info}
{
}
RenderResourceManager::WriteInfo::WriteInfo(const vk::BufferView &info)
CommitManager::WriteInfo::WriteInfo(const vk::BufferView &info)
: uBufferView{info}
{
}
void
RenderResourceManager::Update()
CommitManager::Update()
{
// Descriptor Updates
if (!m_Writes.empty())

View File

@ -9,8 +9,6 @@
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);
@ -27,7 +25,7 @@ constexpr vk::ImageUsageFlags COLOR_ATTACHMENT =
constexpr vk::ImageUsageFlags DEPTH_STENCIL_ATTACHMENT = vk::ImageUsageFlagBits::eDepthStencilAttachment;
} // namespace usage_flags
ImageHandle
Ref<Image>
ImageManager::CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
@ -68,13 +66,18 @@ ImageManager::CreateTexture2D(const Texture2DCreateInfo &createInfo)
object->m_Extent = imageCreateInfo.extent;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
object->m_Flags_ = {};
if (createInfo.m_IsSampled)
object->m_Flags_ |= Image::SAMPLED_BIT;
if (createInfo.m_IsStorage)
object->m_Flags_ |= Image::STORAGE_BIT;
m_Device->SetName(object->m_Image, createInfo.m_Name);
return object;
}
ImageHandle
Ref<ImageCube>
ImageManager::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
@ -115,13 +118,18 @@ ImageManager::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
object->m_Extent = imageCreateInfo.extent;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
object->m_Flags_ = Image::CUBE_BIT;
if (createInfo.m_IsSampled)
object->m_Flags_ |= Image::SAMPLED_BIT;
if (createInfo.m_IsStorage)
object->m_Flags_ |= Image::STORAGE_BIT;
m_Device->SetName(object->m_Image, createInfo.m_Name);
return object;
return CastImage<ImageCube>(object);
}
ImageHandle
Ref<Image>
ImageManager::CreateAttachment(const AttachmentCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
@ -168,7 +176,7 @@ ImageManager::CreateAttachment(const AttachmentCreateInfo &createInfo)
return object;
}
ImageHandle
Ref<Image>
ImageManager::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
@ -306,7 +314,7 @@ ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo)
};
}
ImageManager::ImageManager(const Device *device, const u32 maxCount, const u8 binding)
: Manager{device, maxCount, binding}
ImageManager::ImageManager(const Device *device, const u32 maxCount)
: Manager{device, maxCount}
{
}

View File

@ -9,35 +9,8 @@
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(const Device *device, const u32 maxCount)
: Manager{device, maxCount}
{
}
@ -46,21 +19,20 @@ SamplerManager::~SamplerManager()
m_HashToSamplerIdx.clear();
}
SamplerHandle
SamplerManager::Create(const SamplerCreateInfo &createInfo)
Ref<Sampler>
SamplerManager::CreateSampler(const SamplerCreateInfo &createInfo)
{
const auto hash = HashSamplerCreateInfo(createInfo);
auto vkCreateInfo = Cast<vk::SamplerCreateInfo>(createInfo);
if (const auto iter = m_HashToSamplerIdx.find(hash); iter != m_HashToSamplerIdx.end())
if (const auto iter = m_HashToSamplerIdx.find(vkCreateInfo); iter != m_HashToSamplerIdx.end())
{
return iter->second;
}
auto object = Alloc();
object->Init(m_Device, createInfo, createInfo.m_Name ? createInfo.m_Name : nullptr);
m_HashToSamplerIdx.emplace(hash, object);
object->Init(m_Device, vkCreateInfo, createInfo.m_Name ? createInfo.m_Name : nullptr);
m_HashToSamplerIdx.emplace(vkCreateInfo, object);
return object;
}

View File

@ -9,7 +9,6 @@
#include <imgui.h>
struct AttachmentImage;
struct Device;
struct Context;
struct Window;

View File

@ -20,7 +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 "aster/systems/commit_manager.h"
#include "frame.h"
#include "stb_image.h"
@ -74,7 +74,7 @@ ImageFile::~ImageFile()
}
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
Pipeline CreatePipeline(const systems::RenderResourceManager *resourceManager, const Swapchain *swapchain);
Pipeline CreatePipeline(const systems::CommitManager *resourceManager, const Swapchain *swapchain);
struct Vertex
{
@ -129,13 +129,13 @@ main(int, char **)
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
systems::BufferManager bufferManager{&device, 12, 0};
systems::ImageManager imageManager{&device, 12, 1};
systems::SamplerManager samplerManager{&device, 1, 0xF};
systems::BufferManager bufferManager{&device, 12};
systems::ImageManager imageManager{&device, 12};
systems::SamplerManager samplerManager{&device, 1};
systems::RenderResourceManager renderResourceManager{&device, 12, 12, samplerManager.Create({})};
systems::CommitManager commitManager{&device, 12, 12, 12, samplerManager.CreateSampler({})};
Pipeline pipeline = CreatePipeline(&renderResourceManager, &swapchain);
Pipeline pipeline = CreatePipeline(&commitManager, &swapchain);
Camera camera = {
.m_Model = {1.0f},
@ -389,7 +389,7 @@ main(int, char **)
};
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<systems::ImageHandle, MAX_FRAMES_IN_FLIGHT> depthImages;
eastl::fixed_vector<Ref<Image>, MAX_FRAMES_IN_FLIGHT> depthImages;
auto initDepthImages = [&depthImages, &frameManager, &imageManager](const vk::Extent2D extent) {
for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i)
@ -409,15 +409,15 @@ main(int, char **)
struct PCB
{
systems::CommitEntry<Buffer> m_Camera;
systems::CommitEntry<Buffer> m_VertexBuffer;
systems::CommitEntry<Image> m_Texture;
systems::ResId<Buffer> m_Camera;
systems::ResId<Buffer> m_VertexBuffer;
systems::ResId<Texture> m_Texture;
};
PCB pcb = {
.m_Camera = renderResourceManager.Commit(ubo),
.m_VertexBuffer = renderResourceManager.Commit(vbo),
.m_Texture = renderResourceManager.Commit(crate),
.m_Camera = commitManager.CommitBuffer(ubo),
.m_VertexBuffer = commitManager.CommitBuffer(vbo),
.m_Texture = commitManager.CommitTexture(systems::CastImage<Texture>(crate)),
};
Time::Init();
@ -426,7 +426,7 @@ main(int, char **)
while (window.Poll())
{
Time::Update();
renderResourceManager.Update();
commitManager.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(0, sizeof camera, &camera);
@ -482,7 +482,7 @@ main(int, char **)
cmd.setScissor(0, 1, &scissor);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1,
&renderResourceManager.GetDescriptorSet(), 0, nullptr);
&commitManager.GetDescriptorSet(), 0, nullptr);
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAllGraphics, 0, 12, &pcb);
cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0);
@ -514,7 +514,7 @@ main(int, char **)
}
Pipeline
CreatePipeline(const systems::RenderResourceManager *resourceManager, const Swapchain *swapchain)
CreatePipeline(const systems::CommitManager *resourceManager, const Swapchain *swapchain)
{
// Pipeline Setup
auto *device = resourceManager->m_Device;

View File

@ -12,12 +12,11 @@ add_executable(model_render "model_render.cpp"
"asset_loader.h"
"light_manager.cpp"
"light_manager.h"
"gpu_resource_manager.cpp"
"gpu_resource_manager.h"
"nodes.cpp"
"nodes.h"
"ibl_helpers.cpp"
"ibl_helpers.h")
"ibl_helpers.h"
"tiny_gltf_setup.cpp")
add_shader(model_render "shader/model.vs.hlsl")
add_shader(model_render "shader/model.ps.hlsl")

View File

@ -3,26 +3,25 @@
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#define TINYGLTF_NOEXCEPTION
#define JSON_NOEXCEPTION
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "aster/core/buffer.h"
#include "aster/core/device.h"
#include "aster/core/image.h"
#include "gpu_resource_manager.h"
#include "helpers.h"
#include "asset_loader.h"
#include "helpers.h"
#include "aster/systems/commit_manager.h"
#include "aster/systems/resource_manager.h"
#include <EASTL/fixed_vector.h>
#include <EASTL/hash_map.h>
#include <glm/gtc/type_ptr.hpp>
#include <filesystem>
#include <tiny_gltf.h>
#include <stb_image.h>
#if defined(LoadImage)
#undef LoadImage
@ -54,12 +53,9 @@ VectorToVec3(const std::vector<double> &vec)
return {vec[0], vec[1], vec[2]};
}
void
AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
Ref<Texture>
AssetLoader::LoadHdrImage(cstr path, cstr name) const
{
const Device *pDevice = m_ResourceManager->m_Device;
ERROR_IF(texture->IsValid(), "Expected invalid image.") THEN_ABORT(-1);
i32 x, y, nChannels;
f32 *data = stbi_loadf(path, &x, &y, &nChannels, 4);
assert(nChannels == 3);
@ -69,11 +65,20 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
u32 width = Cast<u32>(x);
u32 height = Cast<u32>(y);
auto texture = m_ResourceManager->Images().CreateTexture2D<Texture>({
.m_Format = vk::Format::eR32G32B32A32Sfloat,
.m_Extent = {width, height},
.m_Name = path,
.m_IsSampled = true,
.m_IsMipMapped = false,
.m_IsStorage = false,
});
auto *pDevice = m_CommitManager->m_Device;
StagingBuffer stagingBuffer;
texture->Init(m_ResourceManager->m_Device, {width, height}, vk::Format::eR32G32B32A32Sfloat, false, path);
assert(texture->IsValid());
stagingBuffer.Init(m_ResourceManager->m_Device, (sizeof *data) * x * y * 4, "HDR Staging Buffer");
stagingBuffer.Write(m_ResourceManager->m_Device, 0, stagingBuffer.GetSize(), data);
stagingBuffer.Init(pDevice, (sizeof *data) * x * y * 4, "HDR Staging Buffer");
stagingBuffer.Write(0, stagingBuffer.m_Size, data);
stbi_image_free(data);
@ -189,11 +194,13 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
AbortIfFailed(pDevice->m_Device.resetCommandPool(m_CommandPool, {}));
stagingBuffer.Destroy(pDevice);
stagingBuffer.Destroy();
return texture;
}
void
GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout,
GenerateMipMaps(vk::CommandBuffer commandBuffer, const Ref<Texture> &texture, vk::ImageLayout initialLayout,
vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage)
{
#if !defined(ASTER_NDEBUG)
@ -362,8 +369,8 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayo
#endif
}
TextureHandle
AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const
std::tuple<systems::ResId<Texture>, Ref<Buffer>>
AssetLoader::LoadImageToGpu(tinygltf::Image* image, bool isSrgb) const
{
assert(image->component == 4);
assert(image->height > 0 && image->width > 0);
@ -373,13 +380,17 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
vk::Format imageFormat = isSrgb ? vk::Format::eR8G8B8A8Srgb : vk::Format::eR8G8B8A8Unorm;
Texture texture;
usize byteSize = image->image.size();
texture.Init(m_ResourceManager->m_Device, {.width = width, .height = height}, imageFormat, true,
image->name.data());
stagingBuffer->Init(m_ResourceManager->m_Device, byteSize);
stagingBuffer->Write(m_ResourceManager->m_Device, 0, byteSize, image->image.data());
auto texture = m_ResourceManager->Images().CreateTexture2D<Texture>({
.m_Format = imageFormat,
.m_Extent = {width, height},
.m_Name = image->name.c_str(),
.m_IsSampled = true,
.m_IsMipMapped = true,
.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: ";
@ -402,7 +413,7 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = texture.m_Image,
.image = texture->m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
@ -426,7 +437,7 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
.srcQueueFamilyIndex = m_TransferQueueIndex,
.dstQueueFamilyIndex = m_GraphicsQueueIndex,
.image = texture.m_Image,
.image = texture->m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
@ -455,11 +466,11 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
.layerCount = 1,
},
.imageOffset = {},
.imageExtent = texture.m_Extent,
.imageExtent = texture->m_Extent,
};
vk::CopyBufferToImageInfo2 stagingCopyInfo = {
.srcBuffer = stagingBuffer->m_Buffer,
.dstImage = texture.m_Image,
.dstImage = texture->m_Image,
.dstImageLayout = vk::ImageLayout::eTransferDstOptimal,
.regionCount = 1,
.pRegions = &imageCopy,
@ -471,14 +482,14 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
m_CommandBuffer.copyBufferToImage2(&stagingCopyInfo);
m_CommandBuffer.pipelineBarrier2(&postStagingDependency);
GenerateMipMaps(m_CommandBuffer, &texture, vk::ImageLayout::eTransferSrcOptimal,
GenerateMipMaps(m_CommandBuffer, texture, vk::ImageLayout::eTransferSrcOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal);
#if !defined(ASTER_NDEBUG)
m_CommandBuffer.endDebugUtilsLabelEXT();
#endif
return m_ResourceManager->CommitTexture(&texture);
return {m_CommitManager->CommitTexture(texture), stagingBuffer};
}
Model
@ -488,7 +499,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
tinygltf::Model model;
tinygltf::TinyGLTF loader;
const Device *pDevice = m_ResourceManager->m_Device;
const Device *pDevice = m_CommitManager->m_Device;
const auto fsPath = fs::absolute(path);
const auto ext = fsPath.extension();
@ -525,30 +536,28 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel);
#endif
eastl::vector<StagingBuffer> stagingBuffers;
eastl::vector<Ref<Buffer>> stagingBuffers;
eastl::hash_map<i32, TextureHandle> textureHandleMap;
eastl::hash_map<i32, systems::ResId<Texture>> textureHandleMap;
eastl::vector<Material> materials;
StorageBuffer materialsBuffer;
BufferHandle materialsHandle;
systems::ResId<Buffer> materialsHandle = systems::ResId<Buffer>::Null();
if (!model.materials.empty())
{
auto getTextureHandle = [this, &textureHandleMap, &stagingBuffers, &model](i32 index,
bool isSrgb) -> TextureHandle {
auto getTextureHandle = [this, &textureHandleMap, &stagingBuffers,
&model](i32 index, const bool isSrgb) -> systems::ResId<Texture> {
if (index < 0)
{
return {};
return systems::NullId{};
}
const auto iter = textureHandleMap.find(index);
if (iter != textureHandleMap.end())
if (const auto iter = textureHandleMap.find(index); iter != textureHandleMap.end())
{
return iter->second;
}
auto *image = &model.images[index];
TextureHandle handle = LoadImageToGpu(&stagingBuffers.push_back(), image, isSrgb);
auto [handle , staging] = LoadImageToGpu(image, isSrgb);
textureHandleMap.emplace(index, handle);
return handle;
};
@ -571,15 +580,15 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
}
usize materialsByteSize = materials.size() * sizeof materials[0];
materialsBuffer.Init(pDevice, materialsByteSize, false, name);
materialsHandle = m_ResourceManager->Commit(&materialsBuffer);
auto materialsBuffer = m_ResourceManager->Buffers().CreateStorageBuffer(materialsByteSize, name);
materialsHandle = m_CommitManager->CommitBuffer(materialsBuffer);
StagingBuffer &materialStaging = stagingBuffers.push_back();
materialStaging.Init(pDevice, materialsByteSize);
materialStaging.Write(pDevice, 0, materialsByteSize, materials.data());
auto materialStaging = m_ResourceManager->Buffers().CreateStagingBuffer(materialsByteSize);
materialStaging->Write(0, materialsByteSize, materials.data());
stagingBuffers.emplace_back(std::move(materialStaging));
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = materialsByteSize};
m_CommandBuffer.copyBuffer(materialStaging.m_Buffer, materialsBuffer.m_Buffer, 1, &bufferCopy);
m_CommandBuffer.copyBuffer(materialStaging->m_Buffer, materialsBuffer->m_Buffer, 1, &bufferCopy);
}
// TODO: Mesh reordering based on nodes AND OR meshoptimizer
@ -862,38 +871,39 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
nodes.Update();
StorageBuffer nodeBuffer;
nodeBuffer.Init(pDevice, nodes.GetGlobalTransformByteSize(), true);
nodeBuffer.Write(pDevice, 0, nodes.GetGlobalTransformByteSize(), nodes.GetGlobalTransformPtr());
BufferHandle nodeHandle = m_ResourceManager->Commit(&nodeBuffer);
auto nodeBuffer = m_ResourceManager->Buffers().CreateStorageBuffer(nodes.GetGlobalTransformByteSize());
nodeBuffer->Write(0, nodes.GetGlobalTransformByteSize(), nodes.GetGlobalTransformPtr());
systems::ResId<Buffer> nodeHandle = m_CommitManager->CommitBuffer(nodeBuffer);
#pragma region Staging / Transfer / Uploads
BufferHandle positionBufferHandle;
BufferHandle vertexDataHandle;
IndexBuffer indexBuffer;
systems::ResId<Buffer> positionBufferHandle = systems::ResId<Buffer>::Null();
systems::ResId<Buffer> vertexDataHandle = systems::ResId<Buffer>::Null();
Ref<Buffer> indexBuffer;
{
auto uploadBufferData = [cmd = this->m_CommandBuffer, &stagingBuffers, pDevice](const Buffer *buffer,
const void *data) {
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = buffer->GetSize()};
StagingBuffer &stagingBuffer = stagingBuffers.push_back();
stagingBuffer.Init(pDevice, bufferCopy.size);
stagingBuffer.Write(pDevice, 0, bufferCopy.size, data);
cmd.copyBuffer(stagingBuffer.m_Buffer, buffer->m_Buffer, 1, &bufferCopy);
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));
};
StorageBuffer positionBuffer;
positionBuffer.Init(pDevice, vertexPositions.size() * sizeof vertexPositions[0], false);
positionBufferHandle = m_ResourceManager->Commit(&positionBuffer);
uploadBufferData(&positionBuffer, vertexPositions.data());
auto positionBuffer =
m_ResourceManager->Buffers().CreateStorageBuffer(vertexPositions.size() * sizeof vertexPositions[0]);
positionBufferHandle = m_CommitManager->CommitBuffer(positionBuffer);
uploadBufferData(positionBuffer, vertexPositions.data());
StorageBuffer vertexDataBuffer;
vertexDataBuffer.Init(pDevice, vertexData.size() * sizeof vertexData[0], false);
vertexDataHandle = m_ResourceManager->Commit(&vertexDataBuffer);
uploadBufferData(&vertexDataBuffer, vertexData.data());
auto vertexDataBuffer =
m_ResourceManager->Buffers().CreateStorageBuffer(vertexData.size() * sizeof vertexData[0]);
vertexDataHandle = m_CommitManager->CommitBuffer(vertexDataBuffer);
uploadBufferData(vertexDataBuffer, vertexData.data());
indexBuffer.Init(pDevice, indices.size() * sizeof indices[0]);
uploadBufferData(&indexBuffer, indices.data());
// TODO: Index buffer needs to be separated.
indexBuffer =
m_ResourceManager->Buffers().CreateStorageBuffer(indices.size() * sizeof indices[0], "Index Buffer");
uploadBufferData(indexBuffer, indices.data());
}
#pragma endregion
@ -919,11 +929,6 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
AbortIfFailed(pDevice->m_Device.resetCommandPool(m_CommandPool, {}));
for (auto &buffer : stagingBuffers)
{
buffer.Destroy(pDevice);
}
Model::ModelHandles handles = {
.m_VertexPositionHandle = positionBufferHandle,
.m_VertexDataHandle = vertexDataHandle,
@ -931,7 +936,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
.m_NodeHandle = nodeHandle,
};
eastl::vector<TextureHandle> textureHandles;
eastl::vector<systems::ResId<Texture>> textureHandles;
textureHandles.reserve(textureHandleMap.size());
for (auto &[key, val] : textureHandleMap)
@ -940,44 +945,23 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
}
return Model{
m_ResourceManager, std::move(textureHandles), std::move(nodes), handles, indexBuffer, meshPrimitives,
m_CommitManager, textureHandles, std::move(nodes), nodeBuffer, handles, indexBuffer, meshPrimitives,
};
}
Model::Model(GpuResourceManager *resourceManager, eastl::vector<TextureHandle> &&textureHandles, Nodes &&nodes,
const ModelHandles &handles, const IndexBuffer &indexBuffer,
Model::Model(systems::CommitManager *resourceManager, eastl::vector<systems::ResId<Texture>> &textureHandles,
Nodes &&nodes, Ref<Buffer> nodeBuffer, ModelHandles &handles, Ref<Buffer> indexBuffer,
const eastl::vector<MeshPrimitive> &meshPrimitives)
: m_ResourceManager(resourceManager)
, m_TextureHandles(std::move(textureHandles))
, m_Nodes(std::move(nodes))
, m_Handles(handles)
, m_IndexBuffer(indexBuffer)
, m_Handles(std::move(handles))
, m_NodeBuffer(std::move(nodeBuffer))
, m_IndexBuffer(std::move(indexBuffer))
, m_MeshPrimitives(meshPrimitives)
{
}
Model::Model(Model &&other) noexcept
: m_ResourceManager(Take(other.m_ResourceManager))
, m_TextureHandles(std::move(other.m_TextureHandles))
, m_Handles(other.m_Handles)
, m_IndexBuffer(other.m_IndexBuffer)
, m_MeshPrimitives(std::move(other.m_MeshPrimitives))
{
}
Model &
Model::operator=(Model &&other) noexcept
{
if (this == &other)
return *this;
m_ResourceManager = Take(other.m_ResourceManager);
m_TextureHandles = std::move(other.m_TextureHandles);
m_Handles = other.m_Handles;
m_IndexBuffer = other.m_IndexBuffer;
m_MeshPrimitives = std::move(other.m_MeshPrimitives);
return *this;
}
const mat4 &
Model::GetModelTransform() const
{
@ -990,42 +974,24 @@ Model::SetModelTransform(const mat4 &transform)
m_Nodes.Set(0, transform);
}
Model::~Model()
{
if (!m_ResourceManager)
return;
m_IndexBuffer.Destroy(m_ResourceManager->m_Device);
m_ResourceManager->Release(m_Handles.m_VertexDataHandle);
m_ResourceManager->Release(m_Handles.m_NodeHandle);
m_ResourceManager->Release(m_Handles.m_VertexPositionHandle);
m_ResourceManager->Release(m_Handles.m_MaterialsHandle);
for (const TextureHandle &handle : m_TextureHandles)
{
m_ResourceManager->Release(handle);
}
}
void
Model::Update()
{
if (m_Nodes.Update())
{
m_ResourceManager->Write(m_Handles.m_NodeHandle, 0, m_Nodes.GetGlobalTransformByteSize(),
m_Nodes.GetGlobalTransformPtr());
m_NodeBuffer->Write(0, m_Nodes.GetGlobalTransformByteSize(), m_Nodes.GetGlobalTransformPtr());
}
}
AssetLoader::AssetLoader(GpuResourceManager *resourceManager, vk::Queue transferQueue, u32 transferQueueIndex,
u32 graphicsQueueIndex)
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 = resourceManager->m_Device;
const Device *pDevice = commitManager->m_Device;
const vk::CommandPoolCreateInfo poolCreateInfo = {
.flags = vk::CommandPoolCreateFlagBits::eTransient,
.queueFamilyIndex = transferQueueIndex,
@ -1047,15 +1013,16 @@ AssetLoader::AssetLoader(GpuResourceManager *resourceManager, vk::Queue transfer
AssetLoader::~AssetLoader()
{
if (m_ResourceManager)
if (m_CommitManager && m_CommandPool)
{
m_ResourceManager->m_Device->m_Device.destroy(m_CommandPool, nullptr);
m_CommitManager->m_Device->m_Device.destroy(m_CommandPool, nullptr);
}
}
AssetLoader::AssetLoader(AssetLoader &&other) noexcept
: m_ResourceManager(Take(other.m_ResourceManager))
, m_CommandPool(other.m_CommandPool)
, 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)
@ -1069,7 +1036,8 @@ AssetLoader::operator=(AssetLoader &&other) noexcept
if (this == &other)
return *this;
m_ResourceManager = Take(other.m_ResourceManager);
m_CommandPool = other.m_CommandPool;
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;

View File

@ -9,8 +9,19 @@
#include "aster/core/buffer.h"
#include "gpu_resource_manager.h"
#include "nodes.h"
#include "tiny_gltf.h"
#include "aster/systems/image_manager.h"
#include "aster/systems/resource.h"
namespace systems
{
class ResourceManager;
class SamplerManager;
class BufferManager;
class ImageManager;
class CommitManager;
}
namespace tinygltf
{
@ -18,7 +29,6 @@ struct Image;
}
struct Image;
struct TextureHandle;
struct Texture;
constexpr auto GLTF_ASCII_FILE_EXTENSION = ".gltf";
@ -39,11 +49,11 @@ struct Material
vec3 m_EmissionFactor; // 12 28
f32 m_MetalFactor; // 04 32
f32 m_RoughFactor; // 04 36
TextureHandle m_AlbedoTex; // 04 40
TextureHandle m_NormalTex; // 04 44
TextureHandle m_MetalRoughTex; // 04 48
TextureHandle m_OcclusionTex; // 04 52
TextureHandle m_EmissionTex; // 04 56
systems::ResId<Texture> m_AlbedoTex; // 04 40
systems::ResId<Texture> m_NormalTex; // 04 44
systems::ResId<Texture> m_MetalRoughTex; // 04 48
systems::ResId<Texture> m_OcclusionTex; // 04 52
systems::ResId<Texture> m_EmissionTex; // 04 56
};
struct VertexData
@ -56,33 +66,33 @@ struct VertexData
struct Model
{
GpuResourceManager *m_ResourceManager;
systems::CommitManager *m_ResourceManager;
eastl::vector<TextureHandle> m_TextureHandles;
eastl::vector<systems::ResId<Texture>> m_TextureHandles;
Nodes m_Nodes;
struct ModelHandles
{
BufferHandle m_VertexPositionHandle;
BufferHandle m_VertexDataHandle;
BufferHandle m_MaterialsHandle;
BufferHandle m_NodeHandle;
systems::ResId<Buffer> m_VertexPositionHandle;
systems::ResId<Buffer> m_VertexDataHandle;
systems::ResId<Buffer> m_MaterialsHandle;
systems::ResId<Buffer> m_NodeHandle;
} m_Handles;
IndexBuffer m_IndexBuffer;
Ref<Buffer> m_NodeBuffer;
Ref<Buffer> m_IndexBuffer;
eastl::vector<MeshPrimitive> m_MeshPrimitives;
[[nodiscard]] const mat4 &GetModelTransform() const;
void SetModelTransform(const mat4 &transform);
void Update();
Model(GpuResourceManager *resourceManager, eastl::vector<TextureHandle> &&textureHandles, Nodes &&nodes,
const ModelHandles &handles, const IndexBuffer &indexBuffer,
const eastl::vector<MeshPrimitive> &meshPrimitives);
~Model();
Model(systems::CommitManager *resourceManager, eastl::vector<systems::ResId<Texture>> &textureHandles, Nodes &&nodes, Ref<Buffer> nodeBuffer,
ModelHandles &handles, Ref<Buffer> indexBuffer, const eastl::vector<MeshPrimitive> &meshPrimitives);
~Model() = default;
Model(Model &&other) noexcept;
Model &operator=(Model &&other) noexcept;
Model(Model &&other) noexcept = default;
Model &operator=(Model &&other) noexcept = default;
Model(const Model &) = delete;
const Model &operator=(const Model &) = delete;
@ -90,15 +100,17 @@ struct Model
struct AssetLoader
{
GpuResourceManager *m_ResourceManager;
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;
void LoadHdrImage(Texture *texture, cstr path, cstr name = nullptr) const;
TextureHandle LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const;
Ref<Texture> LoadHdrImage(cstr path, cstr name = nullptr) const;
std::tuple<systems::ResId<Texture>, Ref<Buffer>> LoadImageToGpu(tinygltf::Image *image, bool isSrgb) const;
Model LoadModelToGpu(cstr path, cstr name = nullptr);
constexpr static auto ANormal = "NORMAL";
@ -110,7 +122,8 @@ struct AssetLoader
constexpr static auto AJoints0 = "JOINTS_0";
constexpr static auto AWeights0 = "WEIGHTS_0";
AssetLoader(GpuResourceManager *resourceManager, vk::Queue transferQueue, u32 transferQueueIndex,
AssetLoader(systems::ResourceManager *resourceManager, systems::CommitManager *commitManager,
vk::Queue transferQueue, u32 transferQueueIndex,
u32 graphicsQueueIndex);
~AssetLoader();
@ -120,7 +133,18 @@ struct AssetLoader
DISALLOW_COPY_AND_ASSIGN(AssetLoader);
};
void GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout,
void
GenerateMipMaps(vk::CommandBuffer commandBuffer, const Ref<Texture> &texture, vk::ImageLayout initialLayout,
vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage);
void GenerateMipMaps(vk::CommandBuffer commandBuffer, concepts::SampledImageRef auto& texture, vk::ImageLayout initialLayout,
vk::ImageLayout finalLayout,
vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands,
vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands);
vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands)
{
GenerateMipMaps(commandBuffer, systems::CastImage<Texture>(texture), initialLayout, finalLayout, prevStage,
finalStage);
}
static_assert(concepts::SampledImageRef<Ref<Texture>>);

View File

@ -1,688 +0,0 @@
// =============================================
// Aster: gpu_resource_manager.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "gpu_resource_manager.h"
#include "helpers.h"
#include "aster/core/buffer.h"
#include "aster/core/device.h"
#include "aster/core/image.h"
#include <EASTL/array.h>
void
TextureManager::Init(const u32 maxCapacity)
{
m_MaxCapacity = maxCapacity;
m_FreeHead = GpuResourceHandle::INVALID_HANDLE;
}
TextureHandle
TextureManager::Commit(Texture *texture)
{
ERROR_IF(!texture || !texture->IsValid(), "Texture must be valid for committal")
THEN_ABORT(-1);
if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE)
{
const u32 index = m_FreeHead;
Texture *allocatedTexture = &m_Textures[index];
assert(!allocatedTexture->IsValid());
m_FreeHead = *Recast<u32 *>(allocatedTexture);
// Ensure it is copyable.
static_assert(std::is_trivially_copyable_v<Texture>);
*allocatedTexture = *texture;
// Take ownership of the texture.
texture->m_Flags_ &= ~Texture::OWNED_BIT;
return {index};
}
const u32 index = Cast<u32>(m_Textures.size());
if (index < m_MaxCapacity)
{
Texture *allocatedTexture = &m_Textures.push_back();
// Ensure it is copyable.
static_assert(std::is_trivially_copyable_v<Texture>);
*allocatedTexture = *texture;
texture->m_Flags_ &= ~Texture::OWNED_BIT;
return {index};
}
ERROR("Out of Buffers") THEN_ABORT(-1);
}
Texture *
TextureManager::Fetch(const TextureHandle handle)
{
assert(!handle.IsInvalid());
return &m_Textures[handle.m_Index];
}
void
TextureManager::Release(const Device *device, const TextureHandle handle)
{
assert(!handle.IsInvalid());
Texture *allocatedTexture = &m_Textures[handle.m_Index];
allocatedTexture->Destroy(device);
assert(!allocatedTexture->IsValid());
*Recast<u32 *>(allocatedTexture) = m_FreeHead;
m_FreeHead = handle.m_Index;
}
void
TextureManager::Destroy(const Device *device)
{
for (auto &texture : m_Textures)
{
texture.Destroy(device);
}
}
void
BufferManager::Init(const u32 maxCapacity)
{
m_MaxCapacity = maxCapacity;
m_FreeHead = GpuResourceHandle::INVALID_HANDLE;
}
BufferHandle
BufferManager::Commit(StorageBuffer *buffer)
{
ERROR_IF(!buffer || !buffer->IsValid() || !buffer->IsOwned(), "Buffer must be valid and owned for commital")
THEN_ABORT(-1);
if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE)
{
const u32 index = m_FreeHead;
StorageBuffer *allocatedBuffer = &m_Buffers[index];
assert(!allocatedBuffer->IsValid());
m_FreeHead = *Recast<u32 *>(allocatedBuffer);
// Ensure it is copyable.
static_assert(std::is_trivially_copyable_v<StorageBuffer>);
*allocatedBuffer = *buffer;
// Take ownership of the buffer.
buffer->m_Size_ &= ~StorageBuffer::OWNED_BIT;
return {index};
}
const u32 index = Cast<u32>(m_Buffers.size());
if (index < m_MaxCapacity)
{
StorageBuffer *allocatedBuffer = &m_Buffers.push_back();
// Ensure it is copyable.
static_assert(std::is_trivially_copyable_v<StorageBuffer>);
*allocatedBuffer = *buffer;
buffer->m_Size_ &= ~StorageBuffer::OWNED_BIT;
return {index};
}
ERROR("Out of Buffers") THEN_ABORT(-1);
}
StorageBuffer *
BufferManager::Fetch(const BufferHandle handle)
{
assert(!handle.IsInvalid());
return &m_Buffers[handle.m_Index];
}
void
BufferManager::Release(const Device *device, const BufferHandle handle)
{
assert(!handle.IsInvalid());
StorageBuffer *allocatedBuffer = &m_Buffers[handle.m_Index];
allocatedBuffer->Destroy(device);
assert(!allocatedBuffer->IsValid());
*Recast<u32 *>(allocatedBuffer) = m_FreeHead;
m_FreeHead = handle.m_Index;
}
void
BufferManager::Destroy(const Device *device)
{
for (auto &buffer : m_Buffers)
{
buffer.Destroy(device);
}
}
StorageTextureHandle
StorageTextureManager::Commit(StorageTexture *texture)
{
const TextureHandle tx = TextureManager::Commit(texture);
return {tx.m_Index};
}
StorageTexture *
StorageTextureManager::Fetch(const StorageTextureHandle handle)
{
assert(!handle.IsInvalid());
return Recast<StorageTexture *>(&m_Textures[handle.m_Index]);
}
void
StorageTextureManager::Release(const Device *device, const StorageTextureHandle handle)
{
TextureManager::Release(device, {handle.m_Index});
}
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 * 0x10))); // 16: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;
}
void
SamplerManager::Init(usize size)
{
m_Samplers.reserve(size);
m_SamplerHashes.reserve(size);
}
SamplerHandle
SamplerManager::Create(const Device *device, const vk::SamplerCreateInfo *createInfo)
{
const usize hash = HashSamplerCreateInfo(createInfo);
for (u32 index = 0; usize samplerHash : m_SamplerHashes)
{
if (samplerHash == hash)
{
return {index};
}
++index;
}
vk::Sampler sampler;
AbortIfFailed(device->m_Device.createSampler(createInfo, nullptr, &sampler));
const u32 index = Cast<u32>(m_SamplerHashes.size());
m_SamplerHashes.push_back(hash);
m_Samplers.push_back(sampler);
return {index};
}
vk::Sampler
SamplerManager::Fetch(const SamplerHandle handle)
{
assert(!handle.IsInvalid());
return m_Samplers[handle.m_Index];
}
void
SamplerManager::Destroy(const Device *device)
{
for (const auto &sampler : m_Samplers)
{
device->m_Device.destroy(sampler, nullptr);
}
m_Samplers.clear();
m_SamplerHashes.clear();
}
GpuResourceManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo info)
: uBufferInfo(info)
{
}
GpuResourceManager::WriteInfo::WriteInfo(vk::DescriptorImageInfo info)
: uImageInfo(info)
{
}
GpuResourceManager::WriteInfo::WriteInfo(vk::BufferView info)
: uBufferView(info)
{
}
BufferHandle
GpuResourceManager::Commit(StorageBuffer *storageBuffer)
{
const BufferHandle handle = m_BufferManager.Commit(storageBuffer);
m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{
.buffer = storageBuffer->m_Buffer,
.offset = 0,
.range = storageBuffer->GetSize(),
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = BUFFER_BINDING_INDEX,
.dstArrayElement = handle.m_Index,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
});
m_WriteOwner.emplace_back(HandleType::eBuffer, handle.m_Index);
#if !defined(ASTER_NDEBUG)
++m_CommitedBufferCount;
#endif
return handle;
}
void
GpuResourceManager::Write(const BufferHandle handle, const usize offset, const usize size, const void *data)
{
m_BufferManager.Fetch(handle)->Write(m_Device, offset, size, data);
}
void
GpuResourceManager::EraseWrites(u32 handleIndex, HandleType handleType)
{
auto writeIter = m_Writes.begin();
auto ownerIter = m_WriteOwner.begin();
const auto ownerEnd = m_WriteOwner.end();
while (ownerIter != ownerEnd)
{
if (ownerIter->first == handleType && ownerIter->second == handleIndex)
{
*writeIter = m_Writes.back();
*ownerIter = m_WriteOwner.back();
m_Writes.pop_back();
m_WriteOwner.pop_back();
return;
}
++ownerIter;
++writeIter;
}
}
void
GpuResourceManager::Release(BufferHandle handle)
{
if (handle.IsInvalid())
return;
EraseWrites(handle.m_Index, HandleType::eBuffer);
m_BufferManager.Release(m_Device, handle);
#if !defined(ASTER_NDEBUG)
--m_CommitedBufferCount;
#endif
}
void
GpuResourceManager::Release(StorageBuffer *storageBuffer, const BufferHandle handle)
{
assert(storageBuffer);
assert(!storageBuffer->IsValid());
StorageBuffer *internal = m_BufferManager.Fetch(handle);
*storageBuffer = *internal;
internal->m_Size_ &= ~StorageBuffer::OWNED_BIT;
Release(handle);
}
void
GpuResourceManager::Release(TextureHandle handle)
{
if (handle.IsInvalid())
return;
EraseWrites(handle.m_Index, HandleType::eTexture);
m_TextureManager.Release(m_Device, handle);
#if !defined(ASTER_NDEBUG)
--m_CommitedTextureCount;
#endif
}
void
GpuResourceManager::Release(Texture *texture, TextureHandle handle)
{
assert(texture);
assert(!texture->IsValid());
Texture *internal = m_TextureManager.Fetch(handle);
*texture = *internal;
internal->m_Flags_ &= ~Texture::OWNED_BIT;
Release(handle);
}
TextureHandle
GpuResourceManager::CommitTexture(Texture *texture, const SamplerHandle sampler)
{
TextureHandle handle = m_TextureManager.Commit(texture);
const vk::Sampler samplerImpl = sampler.IsInvalid() ? m_DefaultSampler : m_SamplerManager.Fetch(sampler);
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
.sampler = samplerImpl,
.imageView = texture->m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = TEXTURE_BINDING_INDEX,
.dstArrayElement = handle.m_Index,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &m_WriteInfos.back().uImageInfo,
});
m_WriteOwner.emplace_back(HandleType::eTexture, handle.m_Index);
#if !defined(ASTER_NDEBUG)
++m_CommitedTextureCount;
#endif
return {handle};
}
StorageTextureHandle
GpuResourceManager::CommitStorageTexture(StorageTexture *storageTexture, SamplerHandle sampler)
{
StorageTextureHandle handle = m_StorageTextureManager.Commit(storageTexture);
vk::Sampler samplerImpl = sampler.IsInvalid() ? m_DefaultSampler : m_SamplerManager.Fetch(sampler);
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
.sampler = samplerImpl,
.imageView = storageTexture->m_View,
.imageLayout = vk::ImageLayout::eGeneral,
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = STORAGE_TEXTURE_BINDING_INDEX,
.dstArrayElement = handle.m_Index,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageImage,
.pImageInfo = &m_WriteInfos.back().uImageInfo,
});
m_WriteOwner.emplace_back(HandleType::eStorageTexture, handle.m_Index);
#if !defined(ASTER_NDEBUG)
++m_CommitedStorageTextureCount;
#endif
return {handle};
}
void
GpuResourceManager::Release(StorageTextureHandle handle)
{
if (handle.IsInvalid())
return;
EraseWrites(handle.m_Index, HandleType::eTexture);
m_StorageTextureManager.Release(m_Device, handle);
#if !defined(ASTER_NDEBUG)
--m_CommitedStorageTextureCount;
#endif
}
void
GpuResourceManager::Release(StorageTexture *texture, const StorageTextureHandle handle)
{
assert(texture);
assert(!texture->IsValid());
StorageTexture *internal = m_StorageTextureManager.Fetch(handle);
*texture = *internal;
internal->m_Flags_ &= ~StorageTexture::OWNED_BIT;
Release(handle);
}
void
GpuResourceManager::Update()
{
if (m_Writes.empty() || m_WriteInfos.empty())
return;
m_Device->m_Device.updateDescriptorSets(Cast<u32>(m_Writes.size()), m_Writes.data(), 0, nullptr);
m_Writes.clear();
m_WriteInfos.clear();
m_WriteOwner.clear();
}
GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
: m_Device(device)
{
vk::PhysicalDeviceProperties properties;
m_Device->m_PhysicalDevice.getProperties(&properties);
u32 buffersCount = eastl::min(properties.limits.maxPerStageDescriptorStorageBuffers - 1024, Cast<u32>(maxSize));
u32 texturesCount = eastl::min(properties.limits.maxPerStageDescriptorSampledImages - 1024, Cast<u32>(maxSize));
u32 storageTexturesCount =
eastl::min(properties.limits.maxPerStageDescriptorStorageImages - 1024, Cast<u32>(maxSize));
INFO("Max Buffer Count: {}", buffersCount);
INFO("Max Texture Count: {}", texturesCount);
INFO("Max Storage Texture Count: {}", storageTexturesCount);
m_BufferManager.Init(buffersCount);
m_TextureManager.Init(texturesCount);
m_StorageTextureManager.Init(storageTexturesCount);
m_SamplerManager.Init(storageTexturesCount);
m_DefaultSamplerCreateInfo = {
.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 = properties.limits.maxSamplerAnisotropy,
.compareEnable = false,
.minLod = 0,
.maxLod = VK_LOD_CLAMP_NONE,
.borderColor = vk::BorderColor::eFloatOpaqueBlack,
.unnormalizedCoordinates = false,
};
m_DefaultSampler = m_SamplerManager.Fetch(m_SamplerManager.Create(device, &m_DefaultSamplerCreateInfo));
eastl::array poolSizes = {
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eStorageBuffer,
.descriptorCount = buffersCount,
},
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = texturesCount,
},
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eStorageImage,
.descriptorCount = storageTexturesCount,
},
};
const vk::DescriptorPoolCreateInfo poolCreateInfo = {
.flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind,
.maxSets = 1,
.poolSizeCount = Cast<u32>(poolSizes.size()),
.pPoolSizes = poolSizes.data(),
};
AbortIfFailed(device->m_Device.createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool));
vk::DescriptorBindingFlags bindingFlags =
vk::DescriptorBindingFlagBits::ePartiallyBound | vk::DescriptorBindingFlagBits::eUpdateAfterBind;
eastl::array layoutBindingFlags = {
bindingFlags,
bindingFlags,
bindingFlags,
};
vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = {
.bindingCount = Cast<u32>(layoutBindingFlags.size()),
.pBindingFlags = layoutBindingFlags.data(),
};
eastl::array descriptorLayoutBindings = {
vk::DescriptorSetLayoutBinding{
.binding = BUFFER_BINDING_INDEX,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.descriptorCount = Cast<u32>(buffersCount),
.stageFlags = vk::ShaderStageFlagBits::eAll,
},
vk::DescriptorSetLayoutBinding{
.binding = TEXTURE_BINDING_INDEX,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = Cast<u32>(texturesCount),
.stageFlags = vk::ShaderStageFlagBits::eAll,
},
vk::DescriptorSetLayoutBinding{
.binding = STORAGE_TEXTURE_BINDING_INDEX,
.descriptorType = vk::DescriptorType::eStorageImage,
.descriptorCount = Cast<u32>(storageTexturesCount),
.stageFlags = vk::ShaderStageFlagBits::eAll,
},
};
static_assert(layoutBindingFlags.size() == descriptorLayoutBindings.size());
const vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.pNext = &bindingFlagsCreateInfo,
.flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
.bindingCount = Cast<u32>(descriptorLayoutBindings.size()),
.pBindings = descriptorLayoutBindings.data(),
};
AbortIfFailed(device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout));
// One descriptor is enough. Updating it at any time is safe. (Update until submit, data held when pending)
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html
// https://github.com/KhronosGroup/Vulkan-Guide/blob/main/chapters/extensions/VK_EXT_descriptor_indexing.adoc
const vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
.descriptorPool = m_DescriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &m_SetLayout,
};
AbortIfFailed(device->m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet));
m_Device->SetName(m_SetLayout, "Bindless Layout");
m_Device->SetName(m_DescriptorPool, "Bindless Pool");
m_Device->SetName(m_DescriptorSet, "Bindless Set");
}
GpuResourceManager::~GpuResourceManager()
{
#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_BufferManager.Destroy(m_Device);
m_TextureManager.Destroy(m_Device);
m_StorageTextureManager.Destroy(m_Device);
m_SamplerManager.Destroy(m_Device);
m_Device->m_Device.destroy(m_DescriptorPool, nullptr);
m_Device->m_Device.destroy(m_SetLayout, nullptr);
}
GpuResourceManager::GpuResourceManager(GpuResourceManager &&other) noexcept
: m_WriteInfos(std::move(other.m_WriteInfos))
, m_Writes(std::move(other.m_Writes))
, m_WriteOwner(std::move(other.m_WriteOwner))
, m_BufferManager(std::move(other.m_BufferManager))
, m_TextureManager(std::move(other.m_TextureManager))
, m_StorageTextureManager(std::move(other.m_StorageTextureManager))
, m_SamplerManager(std::move(other.m_SamplerManager))
, m_Device(Take(other.m_Device))
, m_DescriptorPool(other.m_DescriptorPool)
, m_SetLayout(other.m_SetLayout)
, m_DescriptorSet(other.m_DescriptorSet)
#if !defined(ASTER_NDEBUG)
, m_CommitedBufferCount(other.m_CommitedBufferCount)
, m_CommitedTextureCount(other.m_CommitedTextureCount)
, m_CommitedStorageTextureCount(other.m_CommitedStorageTextureCount)
#endif
{
assert(!other.m_Device);
}
GpuResourceManager &
GpuResourceManager::operator=(GpuResourceManager &&other) noexcept
{
if (this == &other)
return *this;
m_WriteInfos = std::move(other.m_WriteInfos);
m_Writes = std::move(other.m_Writes);
m_WriteOwner = std::move(other.m_WriteOwner);
m_BufferManager = std::move(other.m_BufferManager);
m_TextureManager = std::move(other.m_TextureManager);
m_StorageTextureManager = std::move(other.m_StorageTextureManager);
m_SamplerManager = std::move(other.m_SamplerManager);
m_Device = Take(other.m_Device); // Ensure taken.
m_DescriptorPool = other.m_DescriptorPool;
m_SetLayout = other.m_SetLayout;
m_DescriptorSet = other.m_DescriptorSet;
#if !defined(ASTER_NDEBUG)
m_CommitedBufferCount = other.m_CommitedBufferCount;
m_CommitedTextureCount = other.m_CommitedTextureCount;
m_CommitedStorageTextureCount = other.m_CommitedStorageTextureCount;
#endif
assert(!other.m_Device);
return *this;
}
SamplerHandle
GpuResourceManager::CreateSampler(const vk::SamplerCreateInfo *samplerCreateInfo)
{
return m_SamplerManager.Create(m_Device, samplerCreateInfo);
}

View File

@ -1,175 +0,0 @@
// =============================================
// Aster: gpu_resource_manager.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "aster/aster.h"
#include <EASTL/deque.h>
#include <EASTL/vector_map.h>
struct Device;
struct Texture;
struct StorageTexture;
struct StorageBuffer;
struct GpuResourceHandle
{
constexpr static u32 INVALID_HANDLE = MaxValue<u32>;
u32 m_Index = INVALID_HANDLE; // Default = invalid
[[nodiscard]] bool
IsInvalid() const
{
return m_Index == INVALID_HANDLE;
}
};
struct BufferHandle : GpuResourceHandle
{
};
struct TextureHandle : GpuResourceHandle
{
};
struct StorageTextureHandle : GpuResourceHandle
{
};
struct SamplerHandle : GpuResourceHandle
{
};
struct TextureManager
{
eastl::vector<Texture> m_Textures;
u32 m_MaxCapacity;
u32 m_FreeHead;
void Init(u32 maxCapacity);
TextureHandle Commit(Texture *texture);
Texture *Fetch(TextureHandle handle);
void Release(const Device *device, TextureHandle handle);
void Destroy(const Device *device);
};
struct BufferManager
{
eastl::vector<StorageBuffer> m_Buffers;
u32 m_MaxCapacity;
u32 m_FreeHead;
void Init(u32 maxCapacity);
BufferHandle Commit(StorageBuffer *buffer);
StorageBuffer *Fetch(BufferHandle handle);
void Release(const Device *device, BufferHandle handle);
void Destroy(const Device *device);
};
struct StorageTextureManager : TextureManager
{
StorageTextureHandle Commit(StorageTexture *texture);
StorageTexture *Fetch(StorageTextureHandle handle);
void Release(const Device *device, StorageTextureHandle handle);
};
struct SamplerManager
{
// There can only be so many samplers.
eastl::vector<vk::Sampler> m_Samplers;
eastl::vector<usize> m_SamplerHashes;
void Init(usize size);
SamplerHandle Create(const Device *device, const vk::SamplerCreateInfo *createInfo);
vk::Sampler Fetch(SamplerHandle handle);
void Destroy(const Device *device);
};
struct GpuResourceManager
{
private:
union WriteInfo {
vk::DescriptorBufferInfo uBufferInfo;
vk::DescriptorImageInfo uImageInfo;
vk::BufferView uBufferView;
WriteInfo()
{
}
explicit WriteInfo(vk::DescriptorBufferInfo info);
explicit WriteInfo(vk::DescriptorImageInfo info);
explicit WriteInfo(vk::BufferView info);
};
enum class HandleType
{
eBuffer,
eTexture,
eStorageTexture,
};
using WriteOwner = eastl::pair<HandleType, u32>;
eastl::deque<WriteInfo> m_WriteInfos;
eastl::vector<vk::WriteDescriptorSet> m_Writes;
eastl::vector<WriteOwner> m_WriteOwner;
vk::Sampler m_DefaultSampler;
BufferManager m_BufferManager;
TextureManager m_TextureManager;
StorageTextureManager m_StorageTextureManager;
SamplerManager m_SamplerManager;
void EraseWrites(u32 handleIndex, HandleType handleType);
public:
Device *m_Device;
constexpr static u32 BUFFER_BINDING_INDEX = 0;
constexpr static u32 TEXTURE_BINDING_INDEX = 1;
constexpr static u32 STORAGE_TEXTURE_BINDING_INDEX = 2;
vk::SamplerCreateInfo m_DefaultSamplerCreateInfo;
vk::DescriptorPool m_DescriptorPool;
vk::DescriptorSetLayout m_SetLayout;
vk::DescriptorSet m_DescriptorSet;
BufferHandle Commit(StorageBuffer *storageBuffer); // Commit to GPU and take Ownership
void Write(BufferHandle handle, usize offset, usize size, const void *data); // Write to buffer
void Release(BufferHandle handle); // Release and Destroy
void Release(StorageBuffer *storageBuffer, BufferHandle handle); // Release and Return
TextureHandle CommitTexture(Texture *texture, SamplerHandle sampler = {}); // Commit to GPU and take Ownership
void Release(TextureHandle handle); // Release and Destroy
void Release(Texture *texture, TextureHandle handle); // Release and Return
StorageTextureHandle
CommitStorageTexture(StorageTexture *storageTexture, SamplerHandle sampler = {}); // Commit to GPU and take Ownership
void Release(StorageTextureHandle handle); // Release and Destroy
void Release(StorageTexture *texture, StorageTextureHandle handle); // Release and Return
SamplerHandle CreateSampler(const vk::SamplerCreateInfo *samplerCreateInfo);
void Update(); // Update all the descriptors required.
// Ctor/Dtor
GpuResourceManager(Device *device, u16 maxSize);
~GpuResourceManager();
GpuResourceManager(GpuResourceManager &&other) noexcept;
GpuResourceManager &operator=(GpuResourceManager &&other) noexcept;
#if !defined(ASTER_NDEBUG)
usize m_CommitedBufferCount = 0;
usize m_CommitedTextureCount = 0;
usize m_CommitedStorageTextureCount = 0;
#endif
DISALLOW_COPY_AND_ASSIGN(GpuResourceManager);
};

View File

@ -9,10 +9,12 @@
#include "aster/core/image.h"
#include "asset_loader.h"
#include "gpu_resource_manager.h"
#include "helpers.h"
#include "pipeline_utils.h"
#include "aster/systems/commit_manager.h"
#include "aster/systems/resource_manager.h"
#include <EASTL/fixed_vector.h>
#include <EASTL/tuple.h>
@ -21,70 +23,86 @@ constexpr cstr DIFFUSE_IRRADIANCE_SHADER_FILE = "shader/diffuse_irradiance.cs.hl
constexpr cstr PREFILTER_SHADER_FILE = "shader/prefilter.cs.hlsl.spv";
constexpr cstr BRDF_LUT_SHADER_FILE = "shader/brdf_lut.cs.hlsl.spv";
void
Environment::Destroy(GpuResourceManager *resourceManager)
{
resourceManager->Release(Take(m_Skybox));
resourceManager->Release(Take(m_Diffuse));
resourceManager->Release(Take(m_Prefilter));
resourceManager->Release(Take(m_BrdfLut));
}
Environment
CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, TextureHandle hdrEnv,
const cstr name)
CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide,
systems::ResId<Texture> hdrEnv, const cstr name)
{
GpuResourceManager *resMan = assetLoader->m_ResourceManager;
const Device *pDevice = resMan->m_Device;
systems::ResourceManager *resourceManager = assetLoader->m_ResourceManager;
systems::CommitManager *commitManager = assetLoader->m_CommitManager;
const Device *pDevice = commitManager->m_Device;
vk::SamplerCreateInfo brdfLutSamplerCreateInfo = resMan->m_DefaultSamplerCreateInfo;
brdfLutSamplerCreateInfo.addressModeU = vk::SamplerAddressMode::eClampToEdge;
brdfLutSamplerCreateInfo.addressModeV = vk::SamplerAddressMode::eClampToEdge;
brdfLutSamplerCreateInfo.addressModeW = vk::SamplerAddressMode::eClampToEdge;
auto skybox = resourceManager->Images().CreateTextureCube<StorageTexture>({
.m_Format = vk::Format::eR16G16B16A16Sfloat,
.m_Side = cubeSide,
.m_Name = "Skybox",
.m_IsSampled = true,
.m_IsMipMapped = true,
.m_IsStorage = true,
});
StorageTextureCube skybox;
StorageTextureCube diffuseIrradiance;
StorageTextureCube prefilterCube;
StorageTexture brdfLut;
SamplerHandle brdfLutSampler;
auto skyboxHandle = commitManager->CommitTexture(skybox);
auto skyboxStorageHandle = commitManager->CommitStorageImage(skybox);
skybox.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Skybox");
TextureHandle skyboxHandle = resMan->CommitTexture(&skybox);
StorageTextureHandle skyboxStorageHandle = resMan->CommitStorageTexture(&skybox);
auto diffuseIrradiance = resourceManager->Images().CreateTextureCube<StorageTexture>({
.m_Format = vk::Format::eR16G16B16A16Sfloat,
.m_Side = 64,
.m_Name = "Diffuse Irradiance",
.m_IsSampled = true,
.m_IsMipMapped = false,
.m_IsStorage = true,
});
auto diffuseIrradianceHandle = commitManager->CommitTexture(diffuseIrradiance);
auto diffuseIrradianceStorageHandle = commitManager->CommitStorageImage(diffuseIrradiance);
diffuseIrradiance.Init(pDevice, 64, vk::Format::eR16G16B16A16Sfloat, true, false, "Diffuse Irradiance");
TextureHandle diffuseIrradianceHandle = resMan->CommitTexture(&diffuseIrradiance);
StorageTextureHandle diffuseIrradianceStorageHandle = resMan->CommitStorageTexture(&diffuseIrradiance);
prefilterCube.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Prefilter");
TextureHandle prefilterHandle = resMan->CommitTexture(&prefilterCube); // This stores the original view for us.
auto prefilterCube = resourceManager->Images().CreateTextureCube<StorageTextureCube>({
.m_Format = vk::Format::eR16G16B16A16Sfloat,
.m_Side = cubeSide,
.m_Name = "Prefilter",
.m_IsSampled = true,
.m_IsMipMapped = true,
.m_IsStorage = true,
});
auto prefilterHandle = commitManager->CommitTexture(prefilterCube); // This stores the original view for us.
constexpr u32 prefilterMipCountMax = 6;
eastl::array<StorageTextureHandle, prefilterMipCountMax> prefilterStorageHandles;
eastl::fixed_vector<systems::ResId<StorageImage>, prefilterMipCountMax> prefilterStorageHandles;
// All non-owning copies.
for (u32 mipLevel = 0; auto &tex : prefilterStorageHandles)
for (u32 mipLevel = 0; mipLevel < prefilterMipCountMax; ++mipLevel)
{
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = prefilterCube.m_Image,
.image = prefilterCube->m_Image,
.viewType = vk::ImageViewType::eCube,
.format = vk::Format::eR16G16B16A16Sfloat,
.components = vk::ComponentMapping{},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = mipLevel++,
.baseMipLevel = mipLevel,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 6,
},
};
AbortIfFailed(pDevice->m_Device.createImageView(&imageViewCreateInfo, nullptr, &prefilterCube.m_View));
tex = resMan->CommitStorageTexture(&prefilterCube);
AbortIfFailed(pDevice->m_Device.createImageView(&imageViewCreateInfo, nullptr, &prefilterCube->m_View));
// TODO: FIXME: This is an issue. This needs copying but we don't do that anymore.
// The views need to be separated from the images.
TODO("This is bad");
prefilterStorageHandles.push_back(commitManager->CommitStorageImage(prefilterCube));
}
brdfLut.Init(pDevice, {512, 512}, vk::Format::eR16G16Sfloat, true, "BRDF LUT");
brdfLutSampler = resMan->CreateSampler(&brdfLutSamplerCreateInfo);
TextureHandle brdfLutHandle = resMan->CommitTexture(&brdfLut, brdfLutSampler);
StorageTextureHandle brdfLutStorageHandle = resMan->CommitStorageTexture(&brdfLut);
auto brdfLut = resourceManager->Images().CreateTexture2D<StorageTexture>({.m_Format = vk::Format::eR16G16Sfloat,
.m_Extent = {512, 512},
.m_Name = "BRDF LUT",
.m_IsSampled = true,
.m_IsMipMapped = true,
.m_IsStorage = true});
auto brdfLutSampler = resourceManager->Samplers().CreateSampler({
.m_AddressModeU = vk::SamplerAddressMode::eClampToEdge,
.m_AddressModeV = vk::SamplerAddressMode::eClampToEdge,
.m_AddressModeW = vk::SamplerAddressMode::eClampToEdge,
});
auto brdfLutHandle = commitManager->CommitTexture(brdfLut, brdfLutSampler);
auto brdfLutStorageHandle = commitManager->CommitStorageImage(brdfLut);
#pragma region Dependencies and Copies
vk::ImageSubresourceRange cubeSubresRange = {
@ -114,10 +132,10 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
.subresourceRange = cubeSubresRange,
};
eastl::fixed_vector<vk::ImageMemoryBarrier2, 4> readyToWriteBarriers(4, readyToWriteBarrierTemplate);
readyToWriteBarriers[0].image = skybox.m_Image;
readyToWriteBarriers[1].image = diffuseIrradiance.m_Image;
readyToWriteBarriers[2].image = prefilterCube.m_Image;
readyToWriteBarriers[3].image = brdfLut.m_Image;
readyToWriteBarriers[0].image = skybox->m_Image;
readyToWriteBarriers[1].image = diffuseIrradiance->m_Image;
readyToWriteBarriers[2].image = prefilterCube->m_Image;
readyToWriteBarriers[3].image = brdfLut->m_Image;
readyToWriteBarriers[3].subresourceRange = lutSubresRange;
vk::DependencyInfo readyToWriteDependency = {
@ -136,16 +154,16 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
.subresourceRange = cubeSubresRange,
};
auto skyboxToSampleBarrier = readyToSampleBarrierTemplate;
skyboxToSampleBarrier.image = skybox.m_Image;
skyboxToSampleBarrier.image = skybox->m_Image;
auto diffIrrToSampleBarrier = readyToSampleBarrierTemplate;
diffIrrToSampleBarrier.image = diffuseIrradiance.m_Image;
diffIrrToSampleBarrier.image = diffuseIrradiance->m_Image;
auto prefilterToSampleBarrier = readyToSampleBarrierTemplate;
prefilterToSampleBarrier.image = prefilterCube.m_Image;
prefilterToSampleBarrier.image = prefilterCube->m_Image;
auto brdfToSampleBarrier = readyToSampleBarrierTemplate;
prefilterToSampleBarrier.image = brdfLut.m_Image;
prefilterToSampleBarrier.image = brdfLut->m_Image;
prefilterToSampleBarrier.subresourceRange = lutSubresRange;
vk::DependencyInfo skyboxToSampleDependency = {
@ -169,27 +187,27 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
struct SkyboxPushConstants
{
TextureHandle m_HdrEnvHandle;
StorageTextureHandle m_OutputTexture;
systems::ResId<Texture> m_HdrEnvHandle;
systems::ResId<StorageImage> m_OutputTexture;
u32 m_CubeSide;
};
struct DiffuseIrradiancePushConstants
{
TextureHandle m_SkyboxHandle;
StorageTextureHandle m_OutputTexture;
systems::ResId<Texture> m_SkyboxHandle;
systems::ResId<StorageImage> m_OutputTexture;
u32 m_CubeSide;
};
struct PrefilterPushConstants
{
TextureHandle m_SkyboxHandle;
StorageTextureHandle m_OutputTexture;
systems::ResId<Texture> m_SkyboxHandle;
systems::ResId<StorageImage> m_OutputTexture;
u32 m_CubeSide;
f32 m_Roughness;
u32 m_EnvSide;
};
struct BrdfLutPushConstants
{
StorageTextureHandle m_OutputTexture;
systems::ResId<StorageImage> m_OutputTexture;
};
#pragma region Pipeline Creation etc
@ -197,14 +215,15 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
vk::PushConstantRange pcr = {
.stageFlags = vk::ShaderStageFlagBits::eCompute,
.offset = 0,
.size = Cast<u32>(eastl::max(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)),
.size =
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 = &resMan->m_SetLayout,
.pSetLayouts = &commitManager->GetDescriptorSetLayout(),
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pcr,
};
@ -254,9 +273,9 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
};
eastl::array<vk::Pipeline, computePipelineCreateInfo.size()> pipelines;
AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, Cast<u32>(computePipelineCreateInfo.size()),
computePipelineCreateInfo.data(), nullptr,
pipelines.data()));
AbortIfFailed(
pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, Cast<u32>(computePipelineCreateInfo.size()),
computePipelineCreateInfo.data(), nullptr, pipelines.data()));
vk::Pipeline eqRectToCubePipeline = pipelines[0];
vk::Pipeline diffuseIrradiancePipeline = pipelines[1];
@ -278,17 +297,18 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
DiffuseIrradiancePushConstants diffuseIrradiancePushConstants = {
.m_SkyboxHandle = skyboxHandle,
.m_OutputTexture = diffuseIrradianceStorageHandle,
.m_CubeSide = diffuseIrradiance.m_Extent.width,
.m_CubeSide = diffuseIrradiance->m_Extent.width,
};
PrefilterPushConstants prefilterPushConstants = {
.m_SkyboxHandle = skyboxHandle,
.m_OutputTexture = systems::NullId{},
.m_EnvSide = cubeSide,
};
BrdfLutPushConstants brdfLutPushConstants = {
.m_OutputTexture = brdfLutStorageHandle,
};
resMan->Update();
commitManager->Update();
auto cmd = assetLoader->m_CommandBuffer;
constexpr vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
@ -306,26 +326,27 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
cmd.pipelineBarrier2(&readyToWriteDependency);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipelineLayout, 0, 1, &resMan->m_DescriptorSet, 0, nullptr);
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);
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);
GenerateMipMaps(cmd, &skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral,
GenerateMipMaps(cmd, 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);
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);
cmd.pipelineBarrier2(&diffIrrToSampleDependency);
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, prefilterPipeline);
u32 mipSize = prefilterCube.m_Extent.width;
u32 mipSize = prefilterCube->m_Extent.width;
assert(mipSize % 16 == 0);
for (u32 mipCount = 0; auto &tex : prefilterStorageHandles)
{
@ -347,8 +368,8 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
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);
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);
#if !defined(ASTER_NDEBUG)
cmd.endDebugUtilsLabelEXT();
@ -373,13 +394,10 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
AbortIfFailed(pDevice->m_Device.resetCommandPool(assetLoader->m_CommandPool, {}));
skybox = {};
resMan->Release(skyboxStorageHandle);
resMan->Release(diffuseIrradianceStorageHandle);
resMan->Release(brdfLutStorageHandle);
for (auto &texHandles : prefilterStorageHandles)
for (auto &_ : prefilterStorageHandles)
{
StorageTextureCube st;
resMan->Release(&st, texHandles);
// TODO: This needs fixing
pDevice->m_Device.destroy(st.m_View, nullptr);
}
for (auto &pipeline : pipelines)

View File

@ -6,7 +6,8 @@
#pragma once
#include "aster/aster.h"
#include "gpu_resource_manager.h"
#include "aster/systems/resource.h"
#include "aster/core/image.h"
struct Pipeline;
struct Texture;
@ -15,14 +16,11 @@ struct AssetLoader;
struct Environment
{
TextureHandle m_Skybox;
TextureHandle m_Diffuse;
TextureHandle m_Prefilter;
TextureHandle m_BrdfLut;
void Destroy(GpuResourceManager *resourceManager);
systems::ResId<Texture> m_Skybox;
systems::ResId<Texture> m_Diffuse;
systems::ResId<Texture> m_Prefilter;
systems::ResId<Texture> m_BrdfLut;
};
Environment
CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv,
Environment CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, systems::ResId<Texture> hdrEnv,
cstr name = nullptr);

View File

@ -6,30 +6,11 @@
#include "light_manager.h"
#include "aster/core/buffer.h"
#include "aster/systems/resource.h"
#include "aster/systems/resource_manager.h"
#include "aster/systems/commit_manager.h"
#include "glm/ext/matrix_transform.hpp"
struct Light
{
union {
vec3 um_Position;
vec3 um_Direction;
};
f32 m_Range; // < 0.0 for invalid
u32 m_Color_; // LSB is used for flags. (R G B Flags)
f32 m_Intensity;
constexpr static u32 MAX_GEN = 0x40;
constexpr static u32 GEN_MASK = MAX_GEN - 1;
constexpr static u32 TYPE_MASK = 0xC0;
constexpr static u32 TYPE_INVALID = 0x0;
constexpr static u32 TYPE_DIRECTIONAL = 1 << 6;
constexpr static u32 TYPE_POINT = 2 << 6;
constexpr static u32 TYPE_SPOT = 3 << 6; // Currently Unused
constexpr static u32 COLOR_MASK = ~(GEN_MASK | TYPE_MASK);
};
// Static Checks
// Ensure layouts are exact.
@ -74,29 +55,25 @@ ToColor32(const vec3 &col)
return r << 24 | g << 16 | b << 8 | a;
}
LightManager::LightManager(GpuResourceManager *resourceManager)
LightManager::LightManager(systems::ResourceManager *resourceManager, systems::CommitManager *commitManager)
: m_ResourceManager{resourceManager}
, m_CommitManager{commitManager}
, m_DirectionalLightCount{}
, m_PointLightCount{}
, m_MetaInfo{}
, m_MetaInfo{.m_LightBuffer = systems::ResId<Buffer>::Null() }
, m_GpuBufferCapacity_{0}
{
}
LightManager::~LightManager()
{
m_ResourceManager->Release(m_MetaInfo.m_LightBuffer);
}
LightManager::LightManager(LightManager &&other) noexcept
: m_ResourceManager(other.m_ResourceManager)
, m_CommitManager(other.m_CommitManager)
, m_Lights(std::move(other.m_Lights))
, m_DirectionalLightCount(other.m_DirectionalLightCount)
, m_PointLightCount(other.m_PointLightCount)
, m_MetaInfo(other.m_MetaInfo)
, m_MetaInfo(std::move(other.m_MetaInfo))
, m_GpuBufferCapacity_(other.m_GpuBufferCapacity_)
{
other.m_MetaInfo.m_LightBuffer = {};
}
LightManager &
@ -108,8 +85,7 @@ LightManager::operator=(LightManager &&other) noexcept
m_Lights = std::move(other.m_Lights);
m_DirectionalLightCount = other.m_DirectionalLightCount;
m_PointLightCount = other.m_PointLightCount;
m_MetaInfo = other.m_MetaInfo;
other.m_MetaInfo.m_LightBuffer = {};
m_MetaInfo = std::move(other.m_MetaInfo);
m_GpuBufferCapacity_ = other.m_GpuBufferCapacity_;
return *this;
}
@ -234,16 +210,15 @@ LightManager::Update()
const u16 requiredBufferCapacity = eastl::min(Cast<u16>(m_Lights.capacity()), MAX_LIGHTS);
if ((m_GpuBufferCapacity_ & CAPACITY_MASK) < requiredBufferCapacity)
{
StorageBuffer newBuffer;
newBuffer.Init(m_ResourceManager->m_Device, requiredBufferCapacity * sizeof m_Lights[0], true, "Light Buffer");
auto newBuffer = m_ResourceManager->Buffers().CreateStorageBuffer(requiredBufferCapacity * sizeof m_Lights[0], "Light Buffer");
m_GpuBufferCapacity_ = requiredBufferCapacity | UPDATE_REQUIRED_BIT;
m_ResourceManager->Release(m_MetaInfo.m_LightBuffer);
m_MetaInfo.m_LightBuffer = m_ResourceManager->Commit(&newBuffer);
m_MetaInfo.m_LightBuffer = m_CommitManager->CommitBuffer(newBuffer);
}
if (m_GpuBufferCapacity_ & UPDATE_REQUIRED_BIT)
{
m_ResourceManager->Write(m_MetaInfo.m_LightBuffer, 0, m_Lights.size() * sizeof m_Lights[0], m_Lights.data());
const auto ref = m_CommitManager->FetchHandle(m_MetaInfo.m_LightBuffer);
ref->Write(0, m_Lights.size() * sizeof m_Lights[0], m_Lights.data());
}
}

View File

@ -8,7 +8,16 @@
#include "aster/aster.h"
// TODO: Separate files so you only import handles.
#include "gpu_resource_manager.h"
#include "aster/systems/resource.h"
#include "aster/core/buffer.h"
#include <EASTL/vector.h>
namespace systems
{
class ResourceManager;
class CommitManager;
} // namespace systems
struct DirectionalLight
{
@ -33,7 +42,27 @@ struct LightHandle
u16 m_Index;
};
struct Light;
struct Light
{
union {
vec3 um_Position;
vec3 um_Direction;
};
f32 m_Range; // < 0.0 for invalid
u32 m_Color_; // LSB is used for flags. (R G B Flags)
f32 m_Intensity;
constexpr static u32 MAX_GEN = 0x40;
constexpr static u32 GEN_MASK = MAX_GEN - 1;
constexpr static u32 TYPE_MASK = 0xC0;
constexpr static u32 TYPE_INVALID = 0x0;
constexpr static u32 TYPE_DIRECTIONAL = 1 << 6;
constexpr static u32 TYPE_POINT = 2 << 6;
constexpr static u32 TYPE_SPOT = 3 << 6; // Currently Unused
constexpr static u32 COLOR_MASK = ~(GEN_MASK | TYPE_MASK);
};
struct LightManager
{
@ -44,14 +73,15 @@ struct LightManager
// We can use that with Offset = 0, and point light at further offsets.
// This way we don't need to move point lights often.
BufferHandle m_LightBuffer; // 04 04
systems::ResId<Buffer> m_LightBuffer; // 04 04
u16 m_PointLightMaxCount; // 02 06
u16 m_PointLightOffset; // 02 08
u16 m_DirectionalLightMaxCount; // 02 10
u16 m_UnusedPadding0 = 0; // 02 12
};
GpuResourceManager *m_ResourceManager;
systems::ResourceManager *m_ResourceManager;
systems::CommitManager *m_CommitManager;
eastl::vector<Light> m_Lights;
// We don't need a Directional Light free list. We will just brute force iterate.
@ -73,9 +103,9 @@ struct LightManager
void Update();
void RemoveLight(LightHandle handle);
explicit LightManager(GpuResourceManager *resourceManager);
~LightManager();
~LightManager() = default;
LightManager(systems::ResourceManager *resourceManager, systems::CommitManager *commitManager);
LightManager(LightManager &&other) noexcept;
LightManager &operator=(LightManager &&other) noexcept;

View File

@ -15,18 +15,19 @@
#include "aster/core/swapchain.h"
#include "aster/core/window.h"
#include "asset_loader.h"
#include "frame.h"
#include "helpers.h"
#include "light_manager.h"
#include "asset_loader.h"
#include "gpu_resource_manager.h"
#include "aster/systems/buffer_manager.h"
#include "gui.h"
#include "ibl_helpers.h"
#include "pipeline_utils.h"
#include <EASTL/array.h>
#include <stb_image.h>
#include <aster/systems/commit_manager.h>
#include <aster/systems/resource_manager.h>
#include <tiny_gltf.h>
#include <filesystem>
@ -176,24 +177,24 @@ main(int, char **)
{queueAllocation}, pipelineCacheData, "Primary Device"};
vk::Queue graphicsQueue = device.GetQueue(queueAllocation.m_Family, 0);
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
GpuResourceManager resourceManager = {&device, 1000};
AssetLoader assetLoader = {&resourceManager, graphicsQueue, queueAllocation.m_Family, queueAllocation.m_Family};
LightManager lightManager = LightManager{&resourceManager};
systems::ResourceManager resourceManager = {&device, 1000, 1000, 10};
systems::CommitManager commitManager = {&device, 1000, 1000, 1000, resourceManager.Samplers().CreateSampler({})};
AssetLoader assetLoader = {&resourceManager, &commitManager, graphicsQueue, queueAllocation.m_Family,
queueAllocation.m_Family};
LightManager lightManager = LightManager{&resourceManager, &commitManager};
Model model = assetLoader.LoadModelToGpu(MODEL_FILE);
Texture environmentHdri;
assetLoader.LoadHdrImage(&environmentHdri, BACKDROP_FILE);
auto envHdriHandle = resourceManager.CommitTexture(&environmentHdri);
auto environmentHdri = assetLoader.LoadHdrImage(BACKDROP_FILE);
auto envHdriHandle = commitManager.CommitTexture(environmentHdri);
auto environment = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 512, envHdriHandle, "Cube Env");
resourceManager.Release(envHdriHandle);
vk::Format attachmentFormat = vk::Format::eR8G8B8A8Srgb;
Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &resourceManager);
Pipeline backGroundPipeline = CreateBackgroundPipeline(&device, attachmentFormat, &resourceManager);
Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &commitManager);
Pipeline backGroundPipeline = CreateBackgroundPipeline(&device, attachmentFormat, &commitManager);
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);
@ -242,19 +243,18 @@ main(int, char **)
memcpy(data + lightOffset, &environment, sizeof environment);
memcpy(data + lightOffset + sizeof environment, &lightManager.m_MetaInfo, sizeof lightManager.m_MetaInfo);
UniformBuffer ubo;
ubo.Init(&device, uboSize, "Desc1 UBO");
ubo.Write(&device, 0, ubo.GetSize(), data);
auto ubo = resourceManager.Buffers().CreateUniformBuffer(uboSize, "Desc1 UBO");
ubo->Write(0, ubo->m_Size, data);
delete[] data;
vk::DescriptorBufferInfo cameraBufferInfo = {
.buffer = ubo.m_Buffer,
.buffer = ubo->m_Buffer,
.offset = 0,
.range = cameraSize,
};
vk::DescriptorBufferInfo lightingBufferInfo = {
.buffer = ubo.m_Buffer,
.buffer = ubo->m_Buffer,
.offset = lightOffset,
.range = lightingSize,
};
@ -278,7 +278,7 @@ main(int, char **)
};
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
resourceManager.Update();
commitManager.Update();
// Persistent variables
vk::Viewport viewport = {
@ -383,22 +383,23 @@ main(int, char **)
};
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<DepthImage, MAX_FRAMES_IN_FLIGHT> depthImages(frameManager.m_FramesInFlight);
eastl::fixed_vector<AttachmentImage, MAX_FRAMES_IN_FLIGHT> attachmentImages(frameManager.m_FramesInFlight);
{
auto depthIter = depthImages.begin();
auto attachmentIter = attachmentImages.begin();
eastl::fixed_vector<Ref<Image>, MAX_FRAMES_IN_FLIGHT> depthImages;
eastl::fixed_vector<Ref<Image>, MAX_FRAMES_IN_FLIGHT> attachmentImages;
for (u32 index = 0; index < frameManager.m_FramesInFlight; ++index)
{
auto name = fmt::format("Depth Frame{}", index);
depthIter->Init(&device, internalResolution, name.c_str());
depthImages.emplace_back(resourceManager.Images().CreateDepthStencilImage({
.m_Extent = internalResolution,
.m_Name = name.c_str(),
}));
name = fmt::format("Attachment0 Frame{}", index);
attachmentIter->Init(&device, internalResolution, attachmentFormat, name.c_str());
++depthIter;
++attachmentIter;
}
attachmentImages.emplace_back(resourceManager.Images().CreateAttachment({
.m_Format = attachmentFormat,
.m_Extent = internalResolution,
.m_Name = name.c_str(),
}));
}
gui::Init(&context, &device, &window, swapchain.m_Format, Cast<u32>(swapchain.m_ImageViews.size()),
@ -410,10 +411,10 @@ main(int, char **)
bool showPrefilter = false;
bool useSpecular = true;
constexpr u32 USE_DIFFUSE_BIT = 1;
constexpr u32 USE_SPECULAR_BIT = 1 << 1;
constexpr u32 SHOW_DIFFUSE_BIT = 1 << 2;
constexpr u32 SHOW_PREFILTER_BIT = 1 << 3;
constexpr static u32 USE_DIFFUSE_BIT = 1;
constexpr static u32 USE_SPECULAR_BIT = 1 << 1;
constexpr static u32 SHOW_DIFFUSE_BIT = 1 << 2;
constexpr static u32 SHOW_PREFILTER_BIT = 1 << 3;
i32 height = Cast<i32>(internalResolution.height);
f32 camPitch = glm::degrees(cameraController.m_Pitch);
@ -513,7 +514,7 @@ main(int, char **)
}
model.Update();
cameraController.m_Camera.CalculateInverses();
ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera);
ubo->Write(0, sizeof cameraController.m_Camera, &cameraController.m_Camera);
Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize());
@ -522,19 +523,24 @@ main(int, char **)
vk::ImageView currentSwapchainImageView = swapchain.m_ImageViews[imageIndex];
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
DepthImage *currentDepthImage = &depthImages[currentFrame->m_FrameIdx];
AttachmentImage *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->Destroy(&device);
currentDepthImage->Init(&device, internalResolution, name.c_str());
currentDepthImage = resourceManager.Images().CreateDepthStencilImage({
.m_Extent = internalResolution,
.m_Name = name.c_str(),
});
name = fmt::format("Attachment0 Frame{}", currentFrame->m_FrameIdx);
currentAttachment->Destroy(&device);
currentAttachment->Init(&device, internalResolution, attachmentFormat, name.c_str());
currentAttachment = resourceManager.Images().CreateAttachment({
.m_Format = attachmentFormat,
.m_Extent = internalResolution,
.m_Name = name.c_str(),
});
}
vk::ImageView currentDepthImageView = currentDepthImage->m_View;
@ -586,12 +592,12 @@ main(int, char **)
cmd.setViewport(0, 1, &viewport);
cmd.setScissor(0, 1, &scissor);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1,
&resourceManager.m_DescriptorSet, 0, nullptr);
&commitManager.GetDescriptorSet(), 0, nullptr);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &perFrameDescriptor, 0,
nullptr);
cmd.bindIndexBuffer(model.m_IndexBuffer.m_Buffer, 0, vk::IndexType::eUint32);
cmd.bindIndexBuffer(model.m_IndexBuffer->m_Buffer, 0, vk::IndexType::eUint32);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
@ -693,22 +699,11 @@ main(int, char **)
device.WaitIdle();
environment.Destroy(&resourceManager);
pipelineCacheData = device.DumpPipelineCache();
ERROR_IF(!WriteFileBytes(PIPELINE_CACHE_FILE, pipelineCacheData), "Pipeline Cache incorrectly written");
gui::Destroy(&device);
for (auto &depthImage : depthImages)
{
depthImage.Destroy(&device);
}
for (auto &attachmentImage : attachmentImages)
{
attachmentImage.Destroy(&device);
}
ubo.Destroy(&device);
device.m_Device.destroy(descriptorPool, nullptr);
return 0;

View File

@ -8,13 +8,14 @@
#include "aster/core/device.h"
#include "aster/core/pipeline.h"
#include "gpu_resource_manager.h"
#include "helpers.h"
#include "aster/systems/commit_manager.h"
#include <EASTL/array.h>
Pipeline
CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResourceManager *resourceManager)
CreatePipeline(const Device *device, vk::Format attachmentFormat, const systems::CommitManager *commitManager)
{
// Pipeline Setup
auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE);
@ -35,7 +36,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou
eastl::vector<vk::DescriptorSetLayout> descriptorSetLayouts;
descriptorSetLayouts.push_back(resourceManager->m_SetLayout);
descriptorSetLayouts.push_back(commitManager->GetDescriptorSetLayout());
{
eastl::array descriptorSetLayoutBindings = {
@ -175,7 +176,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou
}
Pipeline
CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const GpuResourceManager *resourceManager)
CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const systems::CommitManager *commitManager)
{
// Pipeline Setup
auto vertexShaderModule = CreateShader(device, BACKGROUND_VERTEX_SHADER_FILE);
@ -196,7 +197,7 @@ CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, cons
eastl::vector<vk::DescriptorSetLayout> descriptorSetLayouts;
descriptorSetLayouts.push_back(resourceManager->m_SetLayout);
descriptorSetLayouts.push_back(commitManager->GetDescriptorSetLayout());
{
eastl::array descriptorSetLayoutBindings = {

View File

@ -7,6 +7,11 @@
#include "aster/aster.h"
namespace systems
{
class CommitManager;
}
struct GpuResourceManager;
struct Swapchain;
struct Device;
@ -19,6 +24,6 @@ constexpr auto BACKGROUND_VERTEX_SHADER_FILE = "shader/background.vs.hlsl.spv";
constexpr auto BACKGROUND_FRAGMENT_SHADER_FILE = "shader/background.ps.hlsl.spv";
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
Pipeline CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResourceManager *resourceManager);
Pipeline CreatePipeline(const Device *device, vk::Format attachmentFormat, const systems::CommitManager *resourceManager);
Pipeline
CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const GpuResourceManager *resourceManager);
CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const systems::CommitManager *resourceManager);

View File

@ -0,0 +1,10 @@
#define TINYGLTF_NOEXCEPTION
#define JSON_NOEXCEPTION
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <tiny_gltf.h>

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