Begin Consolidation all objects under the systems::Device interface.
Currently clears a screen. - Merge all resource creation API under Device. - Begin a basic Context setup.
This commit is contained in:
parent
a790c26f1c
commit
d82e81d104
|
|
@ -40,10 +40,9 @@ struct Device final
|
||||||
void WaitIdle() const;
|
void WaitIdle() const;
|
||||||
|
|
||||||
// Ctor/Dtor
|
// Ctor/Dtor
|
||||||
Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
Device() = default;
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
|
Device(const Instance &context, PhysicalDevice &physicalDevice, Features &enabledFeatures,
|
||||||
Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
const eastl::span<QueueAllocation> &queueAllocations, const eastl::span<u8> &pipelineCacheData,
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, eastl::span<u8> &&pipelineCacheData,
|
|
||||||
NameString &&name);
|
NameString &&name);
|
||||||
~Device();
|
~Device();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,29 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
|
||||||
#define TODO(MSG) assert(false && ("Unimplemented: " MSG))
|
#define TODO(MSG) assert(false && ("Unimplemented: " MSG))
|
||||||
#define FIX(MSG) static_assert(false && ("Unimplemented: " MSG))
|
#define FIX(MSG) static_assert(false && ("Unimplemented: " MSG))
|
||||||
|
|
||||||
|
#define AbortIfFailed(RESULT) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
vk::Result _checkResultValue_; \
|
||||||
|
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), "Cause: {}", _checkResultValue_) \
|
||||||
|
THEN_ABORT(_checkResultValue_); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define AbortIfFailedMV(RESULT, MSG, EXTRA) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
vk::Result _checkResultValue_; \
|
||||||
|
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \
|
||||||
|
THEN_ABORT(_checkResultValue_); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define AbortIfFailedM(RESULT, MSG) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
auto _checkResultValue_ = Cast<vk::Result>(RESULT); \
|
||||||
|
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
[[nodiscard]] inline bool
|
[[nodiscard]] inline bool
|
||||||
Failed(const vk::Result result)
|
Failed(const vk::Result result)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ struct Instance final
|
||||||
vk::DebugUtilsMessengerEXT m_DebugMessenger = nullptr;
|
vk::DebugUtilsMessengerEXT m_DebugMessenger = nullptr;
|
||||||
|
|
||||||
// Ctor/Dtor
|
// Ctor/Dtor
|
||||||
|
Instance() = default;
|
||||||
Instance(cstr appName, Version version, bool enableValidation = ENABLE_LAYER_MESSAGES_DEFAULT_VALUE);
|
Instance(cstr appName, Version version, bool enableValidation = ENABLE_LAYER_MESSAGES_DEFAULT_VALUE);
|
||||||
~Instance();
|
~Instance();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,5 +54,5 @@ struct PhysicalDevice final
|
||||||
class PhysicalDevices : public eastl::fixed_vector<PhysicalDevice, 4>
|
class PhysicalDevices : public eastl::fixed_vector<PhysicalDevice, 4>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PhysicalDevices(const Surface *surface, const Instance *context);
|
PhysicalDevices(const Surface &surface, const Instance &context);
|
||||||
};
|
};
|
||||||
|
|
@ -17,7 +17,8 @@ struct Surface
|
||||||
NameString m_Name;
|
NameString m_Name;
|
||||||
|
|
||||||
// Ctor Dtor
|
// Ctor Dtor
|
||||||
Surface(Instance *context, const Window *window, cstr name);
|
Surface() = default;
|
||||||
|
Surface(Instance &context, const Window &window);
|
||||||
~Surface();
|
~Surface();
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ struct Swapchain final
|
||||||
|
|
||||||
const Device *m_Device;
|
const Device *m_Device;
|
||||||
vk::SwapchainKHR m_Swapchain;
|
vk::SwapchainKHR m_Swapchain;
|
||||||
NameString m_Name;
|
|
||||||
vk::Extent2D m_Extent;
|
vk::Extent2D m_Extent;
|
||||||
vk::Format m_Format;
|
vk::Format m_Format;
|
||||||
eastl::fixed_vector<vk::Image, 4> m_Images;
|
eastl::fixed_vector<vk::Image, 4> m_Images;
|
||||||
|
|
@ -29,11 +28,12 @@ struct Swapchain final
|
||||||
|
|
||||||
eastl::vector<FnResizeCallback> m_ResizeCallbacks;
|
eastl::vector<FnResizeCallback> m_ResizeCallbacks;
|
||||||
|
|
||||||
void Create(const Surface *window, Size2D size);
|
void Create(const Surface &surface, Size2D size);
|
||||||
void RegisterResizeCallback(FnResizeCallback &&callback);
|
void RegisterResizeCallback(FnResizeCallback &&callback);
|
||||||
|
|
||||||
// Ctor/Dtor
|
// Ctor/Dtor
|
||||||
Swapchain(const Surface *window, const Device *device, Size2D size, NameString &&name);
|
Swapchain() = default;
|
||||||
|
Swapchain(const Surface &surface, const Device &device, Size2D size);
|
||||||
~Swapchain();
|
~Swapchain();
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ struct Window final
|
||||||
|
|
||||||
static std::atomic_uint64_t m_WindowCount;
|
static std::atomic_uint64_t m_WindowCount;
|
||||||
static std::atomic_bool m_IsGlfwInit;
|
static std::atomic_bool m_IsGlfwInit;
|
||||||
|
static void SetupLibrary();
|
||||||
|
static cstr *GetInstanceExtensions(u32 *extensionCount);
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,6 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
target_sources(aster_core
|
target_sources(aster_core
|
||||||
INTERFACE
|
INTERFACE
|
||||||
"buffer_manager.h"
|
"device.h"
|
||||||
"image_manager.h"
|
|
||||||
"view_manager.h"
|
|
||||||
"sampler_manager.h"
|
|
||||||
"resource.h"
|
"resource.h"
|
||||||
"resource_manager.h"
|
|
||||||
"commit_manager.h")
|
"commit_manager.h")
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: buffer_manager.h
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
#include "aster/core/buffer.h"
|
|
||||||
|
|
||||||
namespace systems
|
|
||||||
{
|
|
||||||
|
|
||||||
template <std::derived_from<Buffer> TTo, std::derived_from<Buffer> TFrom>
|
|
||||||
static Ref<TTo>
|
|
||||||
CastBuffer(const Ref<TFrom> &from)
|
|
||||||
{
|
|
||||||
if constexpr (not concepts::BufferInto<TFrom, TTo>)
|
|
||||||
assert(TTo::FLAGS & from->m_Flags);
|
|
||||||
return std::reinterpret_pointer_cast<TTo>(from);
|
|
||||||
}
|
|
||||||
|
|
||||||
class BufferManager final
|
|
||||||
{
|
|
||||||
const Device *m_Device = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit BufferManager(const Device *device);
|
|
||||||
|
|
||||||
[[nodiscard]] Ref<StorageBuffer> CreateStorageBuffer(usize size, cstr name = nullptr) const;
|
|
||||||
[[nodiscard]] Ref<UniformBuffer> CreateUniformBuffer(usize size, cstr name = nullptr) const;
|
|
||||||
[[nodiscard]] Ref<StagingBuffer> CreateStagingBuffer(usize size, cstr name = nullptr) const;
|
|
||||||
[[nodiscard]] Ref<VertexBuffer> CreateVertexBuffer(usize size, cstr name = nullptr) const;
|
|
||||||
};
|
|
||||||
} // namespace systems
|
|
||||||
|
|
@ -7,16 +7,16 @@
|
||||||
|
|
||||||
#include "aster/aster.h"
|
#include "aster/aster.h"
|
||||||
|
|
||||||
#include "buffer_manager.h"
|
|
||||||
#include "sampler_manager.h"
|
|
||||||
#include "view_manager.h"
|
|
||||||
|
|
||||||
#include "aster/util/freelist.h"
|
#include "aster/util/freelist.h"
|
||||||
|
|
||||||
#include "EASTL/deque.h"
|
#include "EASTL/deque.h"
|
||||||
#include "EASTL/intrusive_hash_map.h"
|
#include "EASTL/intrusive_hash_map.h"
|
||||||
|
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
#include "EASTL/vector.h"
|
||||||
|
#include "aster/core/buffer.h"
|
||||||
|
#include "aster/core/image_view.h"
|
||||||
|
#include "aster/core/sampler.h"
|
||||||
|
|
||||||
namespace systems
|
namespace systems
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,627 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: device.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
#include "aster/aster.h"
|
||||||
|
#include "aster/core/buffer.h"
|
||||||
|
#include "aster/core/device.h"
|
||||||
|
#include "aster/core/image.h"
|
||||||
|
#include "aster/core/image_view.h"
|
||||||
|
#include "aster/core/instance.h"
|
||||||
|
#include "aster/core/physical_device.h"
|
||||||
|
#include "aster/core/sampler.h"
|
||||||
|
#include "aster/core/swapchain.h"
|
||||||
|
#include "aster/core/pipeline.h"
|
||||||
|
|
||||||
|
#include <EASTL/hash_map.h>
|
||||||
|
#include <EASTL/optional.h>
|
||||||
|
#include <EASTL/vector_map.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
constexpr static u32 MAX_FRAMES_IN_FLIGHT = 3;
|
||||||
|
|
||||||
|
struct Window;
|
||||||
|
using CoreDevice = Device;
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Creation Structs
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region Image
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct Texture2DCreateInfo
|
||||||
|
{
|
||||||
|
vk::Format m_Format = vk::Format::eUndefined;
|
||||||
|
vk::Extent2D m_Extent = {};
|
||||||
|
cstr m_Name = nullptr;
|
||||||
|
bool m_IsSampled = true;
|
||||||
|
bool m_IsMipMapped = false;
|
||||||
|
bool m_IsStorage = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TextureCubeCreateInfo
|
||||||
|
{
|
||||||
|
vk::Format m_Format = vk::Format::eUndefined;
|
||||||
|
u32 m_Side = 0;
|
||||||
|
cstr m_Name = nullptr;
|
||||||
|
bool m_IsSampled = true;
|
||||||
|
bool m_IsMipMapped = false;
|
||||||
|
bool m_IsStorage = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AttachmentCreateInfo
|
||||||
|
{
|
||||||
|
vk::Format m_Format = vk::Format::eUndefined;
|
||||||
|
vk::Extent2D m_Extent = {};
|
||||||
|
cstr m_Name = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DepthStencilImageCreateInfo
|
||||||
|
{
|
||||||
|
vk::Extent2D m_Extent = {};
|
||||||
|
cstr m_Name = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region View
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::AnyImage TImage>
|
||||||
|
struct ViewCreateInfo
|
||||||
|
{
|
||||||
|
using ImageType = TImage;
|
||||||
|
|
||||||
|
Ref<ImageType> m_Image;
|
||||||
|
cstr m_Name;
|
||||||
|
vk::ImageViewType m_ViewType = vk::ImageViewType::e2D;
|
||||||
|
vk::ComponentMapping m_Components = {};
|
||||||
|
vk::ImageAspectFlags m_AspectMask = {};
|
||||||
|
eastl::optional<u8> m_MipLevelCount = eastl::nullopt;
|
||||||
|
eastl::optional<u8> m_LayerCount = eastl::nullopt;
|
||||||
|
u8 m_BaseMipLevel = 0;
|
||||||
|
u8 m_BaseLayer = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] u8
|
||||||
|
GetMipLevelCount() const
|
||||||
|
{
|
||||||
|
return m_MipLevelCount.value_or(m_Image->m_MipLevels - m_BaseMipLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u8
|
||||||
|
GetLayerCount() const
|
||||||
|
{
|
||||||
|
return m_LayerCount.value_or(m_Image->m_LayerCount - m_BaseLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
operator vk::ImageViewCreateInfo() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.image = m_Image->m_Image,
|
||||||
|
.viewType = m_ViewType,
|
||||||
|
.format = m_Image->m_Format,
|
||||||
|
.components = m_Components,
|
||||||
|
.subresourceRange =
|
||||||
|
{
|
||||||
|
.aspectMask = m_AspectMask,
|
||||||
|
.baseMipLevel = m_BaseMipLevel,
|
||||||
|
.levelCount = GetMipLevelCount(),
|
||||||
|
.baseArrayLayer = m_BaseLayer,
|
||||||
|
.layerCount = GetLayerCount(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
operator ViewCreateInfo<Image>() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.m_Image = CastImage<Image>(m_Image),
|
||||||
|
.m_Name = m_Name,
|
||||||
|
.m_ViewType = m_ViewType,
|
||||||
|
.m_Components = m_Components,
|
||||||
|
.m_AspectMask = m_AspectMask,
|
||||||
|
.m_MipLevelCount = m_MipLevelCount,
|
||||||
|
.m_LayerCount = m_LayerCount,
|
||||||
|
.m_BaseMipLevel = m_BaseMipLevel,
|
||||||
|
.m_BaseLayer = m_BaseLayer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region Sampler
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region Pipeline
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region Device
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PhysicalDevice DefaultPhysicalDeviceSelector(const PhysicalDevices &physicalDevices);
|
||||||
|
|
||||||
|
using PhysicalDeviceSelectorFn = PhysicalDevice (*)(const PhysicalDevices &);
|
||||||
|
static_assert(std::convertible_to<decltype(DefaultPhysicalDeviceSelector), PhysicalDeviceSelectorFn>);
|
||||||
|
|
||||||
|
struct DeviceCreateInfo
|
||||||
|
{
|
||||||
|
cstr m_AppName = "Aster App";
|
||||||
|
Version m_AppVersion = {0, 1, 0};
|
||||||
|
PhysicalDeviceSelectorFn m_PhysicalDeviceSelector = DefaultPhysicalDeviceSelector;
|
||||||
|
std::span<u8> m_PipelineCacheData = {};
|
||||||
|
std::reference_wrapper<Window> m_Window;
|
||||||
|
cstr m_Name = "Primary";
|
||||||
|
Features m_Features;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
struct Frame;
|
||||||
|
|
||||||
|
class Context
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
vk::CommandBuffer m_Cmd;
|
||||||
|
|
||||||
|
friend Device;
|
||||||
|
friend Frame;
|
||||||
|
|
||||||
|
explicit Context(const vk::CommandBuffer cmd)
|
||||||
|
: m_Cmd{cmd}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Begin();
|
||||||
|
void End();
|
||||||
|
};
|
||||||
|
|
||||||
|
class GraphicsContext : public Context
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
friend Device;
|
||||||
|
friend Frame;
|
||||||
|
|
||||||
|
explicit GraphicsContext(const vk::CommandBuffer cmd)
|
||||||
|
: Context{cmd}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void BindPipeline(vk::Pipeline pipeline);
|
||||||
|
void DrawIndexed(u32 indexCount);
|
||||||
|
|
||||||
|
[[deprecated]]
|
||||||
|
void Dependency(const vk::DependencyInfo& dependencyInfo);
|
||||||
|
|
||||||
|
[[deprecated]]
|
||||||
|
void BeginRendering(const vk::RenderingInfo& renderingInfo);
|
||||||
|
void EndRendering();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Frame
|
||||||
|
{
|
||||||
|
// Persistent
|
||||||
|
::Device *m_Device;
|
||||||
|
// TODO: ThreadSafe
|
||||||
|
vk::CommandPool m_Pool;
|
||||||
|
vk::Fence m_FrameAvailableFence;
|
||||||
|
vk::Semaphore m_ImageAcquireSem;
|
||||||
|
vk::Semaphore m_RenderFinishSem;
|
||||||
|
u32 m_FrameIdx;
|
||||||
|
|
||||||
|
// Transient
|
||||||
|
u32 m_ImageIdx;
|
||||||
|
vk::Image m_SwapchainImage;
|
||||||
|
vk::ImageView m_SwapchainImageView;
|
||||||
|
|
||||||
|
GraphicsContext CreateGraphicsContext();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Device final
|
||||||
|
{
|
||||||
|
public: // TODO: Temp
|
||||||
|
std::reference_wrapper<Window> m_Window;
|
||||||
|
Instance m_Instance;
|
||||||
|
Surface m_Surface;
|
||||||
|
::Device m_Device;
|
||||||
|
Swapchain m_Swapchain;
|
||||||
|
|
||||||
|
// TODO: This is single-threaded.
|
||||||
|
vk::Queue m_GraphicsQueue;
|
||||||
|
u32 m_GraphicsQueueFamily;
|
||||||
|
|
||||||
|
std::array<Frame, MAX_FRAMES_IN_FLIGHT> m_Frames;
|
||||||
|
u32 m_CurrentFrameIdx = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// ====================================================================================================
|
||||||
|
// Resource Management
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
//
|
||||||
|
// Buffer Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[[nodiscard]] Ref<StorageBuffer> CreateStorageBuffer(usize size, cstr name = nullptr);
|
||||||
|
[[nodiscard]] Ref<UniformBuffer> CreateUniformBuffer(usize size, cstr name = nullptr);
|
||||||
|
[[nodiscard]] Ref<StagingBuffer> CreateStagingBuffer(usize size, cstr name = nullptr);
|
||||||
|
[[nodiscard]] Ref<VertexBuffer> CreateVertexBuffer(usize size, cstr name = nullptr);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Image Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::ImageInto<Texture> T>
|
||||||
|
[[nodiscard]] Ref<T>
|
||||||
|
CreateTexture2D(const Texture2DCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
return CastImage<T>(CreateTexture2D(createInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <concepts::ImageInto<TextureCube> T>
|
||||||
|
[[nodiscard]] Ref<T>
|
||||||
|
CreateTextureCube(const TextureCubeCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
return CastImage<T>(CreateTextureCube(createInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[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);
|
||||||
|
|
||||||
|
//
|
||||||
|
// View Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::View TImageView>
|
||||||
|
Ref<TImageView>
|
||||||
|
CreateView(const ViewCreateInfo<typename TImageView::ImageType> &createInfo)
|
||||||
|
{
|
||||||
|
return CastView<TImageView>(CreateView(ViewCreateInfo<Image>(createInfo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Ref<ImageView> CreateView(const ViewCreateInfo<Image> &createInfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Image - View Combined Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::ViewTo<Image> T>
|
||||||
|
[[nodiscard]] Ref<T>
|
||||||
|
CreateTexture2DWithView(const Texture2DCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
auto handle = CreateTexture2D(createInfo);
|
||||||
|
return CastView<T>(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <concepts::ViewTo<ImageCube> T>
|
||||||
|
[[nodiscard]] Ref<T>
|
||||||
|
CreateTextureCubeWithView(const TextureCubeCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
auto handle = CreateTextureCube(createInfo);
|
||||||
|
return CastView<T>(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Ref<TextureView> CreateTexture2DWithView(const Texture2DCreateInfo &createInfo);
|
||||||
|
[[nodiscard]] Ref<ImageCubeView> CreateTextureCubeWithView(const TextureCubeCreateInfo &createInfo);
|
||||||
|
[[nodiscard]] Ref<ImageView> CreateAttachmentWithView(const AttachmentCreateInfo &createInfo);
|
||||||
|
[[nodiscard]] Ref<ImageView> CreateDepthStencilImageWithView(const DepthStencilImageCreateInfo &createInfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sampler Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Handle = Ref<Sampler>;
|
||||||
|
using WeakHandle = WeakRef<Sampler>;
|
||||||
|
eastl::hash_map<vk::SamplerCreateInfo, WeakHandle> m_HashToSamplerIdx;
|
||||||
|
|
||||||
|
Ref<Sampler> CreateSampler(const SamplerCreateInfo &createInfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pipeline
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Pipeline CreatePipeline()
|
||||||
|
{
|
||||||
|
// Pipeline Setup
|
||||||
|
auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE);
|
||||||
|
auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE);
|
||||||
|
|
||||||
|
eastl::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages = {{
|
||||||
|
{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||||
|
.module = vertexShaderModule,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.module = fragmentShaderModule,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
|
||||||
|
.setLayoutCount = 0,
|
||||||
|
.pSetLayouts = nullptr,
|
||||||
|
.pushConstantRangeCount = 0,
|
||||||
|
.pPushConstantRanges = nullptr,
|
||||||
|
};
|
||||||
|
vk::PipelineLayout pipelineLayout;
|
||||||
|
vk::Result result = m_Device.m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout);
|
||||||
|
ERROR_IF(Failed(result), "Could not create a pipeline layout. Cause: {}", result) THEN_ABORT(result);
|
||||||
|
m_Device.SetName(pipelineLayout, "Triangle Layout");
|
||||||
|
|
||||||
|
vk::VertexInputBindingDescription inputBindingDescription = Vertex::GetBinding(0);
|
||||||
|
auto inputAttributeDescription = Vertex::GetAttributes(0);
|
||||||
|
|
||||||
|
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {
|
||||||
|
.vertexBindingDescriptionCount = 1,
|
||||||
|
.pVertexBindingDescriptions = &inputBindingDescription,
|
||||||
|
.vertexAttributeDescriptionCount = Cast<u32>(inputAttributeDescription.size()),
|
||||||
|
.pVertexAttributeDescriptions = inputAttributeDescription.data(),
|
||||||
|
};
|
||||||
|
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
|
||||||
|
.topology = vk::PrimitiveTopology::eTriangleList,
|
||||||
|
.primitiveRestartEnable = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = {
|
||||||
|
.viewportCount = 1,
|
||||||
|
.scissorCount = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = {
|
||||||
|
.depthClampEnable = false,
|
||||||
|
.rasterizerDiscardEnable = false,
|
||||||
|
.polygonMode = vk::PolygonMode::eFill,
|
||||||
|
.cullMode = vk::CullModeFlagBits::eNone,
|
||||||
|
.frontFace = vk::FrontFace::eCounterClockwise,
|
||||||
|
.depthBiasEnable = false,
|
||||||
|
.lineWidth = 1.0,
|
||||||
|
};
|
||||||
|
vk::PipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {
|
||||||
|
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
||||||
|
.sampleShadingEnable = false,
|
||||||
|
};
|
||||||
|
vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {
|
||||||
|
.depthTestEnable = false,
|
||||||
|
.depthWriteEnable = false,
|
||||||
|
};
|
||||||
|
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = {
|
||||||
|
.blendEnable = false,
|
||||||
|
.srcColorBlendFactor = vk::BlendFactor::eSrcColor,
|
||||||
|
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor,
|
||||||
|
.colorBlendOp = vk::BlendOp::eAdd,
|
||||||
|
.srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha,
|
||||||
|
.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
|
||||||
|
.alphaBlendOp = vk::BlendOp::eAdd,
|
||||||
|
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||||
|
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
||||||
|
};
|
||||||
|
vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = {
|
||||||
|
.logicOpEnable = false,
|
||||||
|
.attachmentCount = 1,
|
||||||
|
.pAttachments = &colorBlendAttachmentState,
|
||||||
|
};
|
||||||
|
|
||||||
|
eastl::array dynamicStates = {
|
||||||
|
vk::DynamicState::eScissor,
|
||||||
|
vk::DynamicState::eViewport,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
|
||||||
|
.dynamicStateCount = Cast<u32>(dynamicStates.size()),
|
||||||
|
.pDynamicStates = dynamicStates.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::PipelineRenderingCreateInfo renderingCreateInfo = {
|
||||||
|
.viewMask = 0,
|
||||||
|
.colorAttachmentCount = 1,
|
||||||
|
.pColorAttachmentFormats = &swapchain->m_Format,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = {
|
||||||
|
.pNext = &renderingCreateInfo,
|
||||||
|
.stageCount = Cast<u32>(shaderStages.size()),
|
||||||
|
.pStages = shaderStages.data(),
|
||||||
|
.pVertexInputState = &vertexInputStateCreateInfo,
|
||||||
|
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
|
||||||
|
.pViewportState = &viewportStateCreateInfo,
|
||||||
|
.pRasterizationState = &rasterizationStateCreateInfo,
|
||||||
|
.pMultisampleState = &multisampleStateCreateInfo,
|
||||||
|
.pDepthStencilState = &depthStencilStateCreateInfo,
|
||||||
|
.pColorBlendState = &colorBlendStateCreateInfo,
|
||||||
|
.pDynamicState = &dynamicStateCreateInfo,
|
||||||
|
.layout = pipelineLayout,
|
||||||
|
};
|
||||||
|
vk::Pipeline pipeline;
|
||||||
|
result = m_Device.m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline);
|
||||||
|
ERROR_IF(Failed(result), "Could not create a graphics pipeline. Cause: {}", result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
m_Device.SetName(pipeline, "Triangle Pipeline");
|
||||||
|
|
||||||
|
m_Device.m_Device.destroy(vertexShaderModule, nullptr);
|
||||||
|
m_Device.m_Device.destroy(fragmentShaderModule, nullptr);
|
||||||
|
|
||||||
|
return {&m_Device, pipelineLayout, pipeline, {}};
|
||||||
|
}
|
||||||
|
//*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// Frames
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private:
|
||||||
|
Frame CreateFrame(u32 frameIndex);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Frame &GetNextFrame();
|
||||||
|
Size2D GetSwapchainSize() const
|
||||||
|
{
|
||||||
|
return {m_Swapchain.m_Extent.width, m_Swapchain.m_Extent.height};
|
||||||
|
}
|
||||||
|
|
||||||
|
using Receipt = u32;
|
||||||
|
Receipt Submit(Frame& frame, GraphicsContext& graphicsContext);
|
||||||
|
|
||||||
|
void Present(Frame &frame);
|
||||||
|
|
||||||
|
friend Context;
|
||||||
|
friend GraphicsContext;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Device Methods
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::VkHandle T>
|
||||||
|
void
|
||||||
|
SetName(const T &object, cstr name) const
|
||||||
|
{
|
||||||
|
m_Device.SetName(object, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] vk::Queue
|
||||||
|
GetQueue(u32 familyIndex, u32 queueIndex) const
|
||||||
|
{
|
||||||
|
return m_Device.GetQueue(familyIndex, queueIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] eastl::vector<u8>
|
||||||
|
DumpPipelineCache() const
|
||||||
|
{
|
||||||
|
return m_Device.DumpPipelineCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WaitIdle() const
|
||||||
|
{
|
||||||
|
m_Device.WaitIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inner
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[[nodiscard]] CoreDevice &
|
||||||
|
GetInner()
|
||||||
|
{
|
||||||
|
return m_Device;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] vk::Device &
|
||||||
|
GetHandle()
|
||||||
|
{
|
||||||
|
return m_Device.m_Device;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctor/Dtor
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
explicit Device(const DeviceCreateInfo &createInfo);
|
||||||
|
|
||||||
|
~Device();
|
||||||
|
|
||||||
|
PIN_MEMORY(Device);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace systems
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: image_manager.h
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
#include "aster/core/image.h"
|
|
||||||
|
|
||||||
namespace systems
|
|
||||||
{
|
|
||||||
|
|
||||||
template <std::derived_from<Image> TTo, std::derived_from<Image> TFrom>
|
|
||||||
static Ref<TTo>
|
|
||||||
CastImage(const Ref<TFrom> &from)
|
|
||||||
{
|
|
||||||
if constexpr (not concepts::ImageInto<TFrom, TTo>)
|
|
||||||
assert(TTo::FLAGS & from->m_Flags_);
|
|
||||||
return std::reinterpret_pointer_cast<TTo>(from);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Texture2DCreateInfo
|
|
||||||
{
|
|
||||||
vk::Format m_Format = vk::Format::eUndefined;
|
|
||||||
vk::Extent2D m_Extent = {};
|
|
||||||
cstr m_Name = nullptr;
|
|
||||||
bool m_IsSampled = true;
|
|
||||||
bool m_IsMipMapped = false;
|
|
||||||
bool m_IsStorage = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TextureCubeCreateInfo
|
|
||||||
{
|
|
||||||
vk::Format m_Format = vk::Format::eUndefined;
|
|
||||||
u32 m_Side = 0;
|
|
||||||
cstr m_Name = nullptr;
|
|
||||||
bool m_IsSampled = true;
|
|
||||||
bool m_IsMipMapped = false;
|
|
||||||
bool m_IsStorage = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AttachmentCreateInfo
|
|
||||||
{
|
|
||||||
vk::Format m_Format = vk::Format::eUndefined;
|
|
||||||
vk::Extent2D m_Extent = {};
|
|
||||||
cstr m_Name = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DepthStencilImageCreateInfo
|
|
||||||
{
|
|
||||||
vk::Extent2D m_Extent = {};
|
|
||||||
cstr m_Name = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ImageManager final
|
|
||||||
{
|
|
||||||
const Device *m_Device;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ImageManager(const Device *device);
|
|
||||||
|
|
||||||
template <concepts::ImageInto<Texture> T>
|
|
||||||
[[nodiscard]] Ref<T>
|
|
||||||
CreateTexture2D(const Texture2DCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto handle = CreateTexture2D(createInfo);
|
|
||||||
return CastImage<T>(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <concepts::ImageInto<TextureCube> 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
|
|
||||||
|
|
@ -5,11 +5,63 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "aster/core/buffer.h"
|
||||||
|
#include "aster/core/image.h"
|
||||||
|
#include "aster/core/image_view.h"
|
||||||
|
|
||||||
#include <EASTL/intrusive_ptr.h>
|
#include <EASTL/intrusive_ptr.h>
|
||||||
|
|
||||||
namespace systems
|
namespace systems
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Util Methods
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
#pragma region Buffer
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <std::derived_from<Buffer> TTo, std::derived_from<Buffer> TFrom>
|
||||||
|
static Ref<TTo>
|
||||||
|
CastBuffer(const Ref<TFrom> &from)
|
||||||
|
{
|
||||||
|
if constexpr (not concepts::BufferInto<TFrom, TTo>)
|
||||||
|
assert(TTo::FLAGS & from->m_Flags);
|
||||||
|
return std::reinterpret_pointer_cast<TTo>(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region Image
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <std::derived_from<Image> TTo, std::derived_from<Image> TFrom>
|
||||||
|
static Ref<TTo>
|
||||||
|
CastImage(const Ref<TFrom> &from)
|
||||||
|
{
|
||||||
|
if constexpr (not concepts::ImageInto<TFrom, TTo>)
|
||||||
|
assert(TTo::FLAGS & from->m_Flags_);
|
||||||
|
return std::reinterpret_pointer_cast<TTo>(from);
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region View
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::View TTo, std::derived_from<Image> TFrom>
|
||||||
|
static Ref<TTo>
|
||||||
|
CastView(const Ref<View<TFrom>> &from)
|
||||||
|
{
|
||||||
|
if constexpr (not concepts::ImageInto<TFrom, typename TTo::ImageType>)
|
||||||
|
assert(TTo::ImageType::FLAGS & from->m_Image->m_Flags_);
|
||||||
|
return std::reinterpret_pointer_cast<TTo>(from);
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResId manages the lifetime of the committed resource.
|
* ResId manages the lifetime of the committed resource.
|
||||||
* @tparam T Type of the committed resource.
|
* @tparam T Type of the committed resource.
|
||||||
|
|
|
||||||
|
|
@ -1,142 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: resource_manager.h
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
|
|
||||||
#include "buffer_manager.h"
|
|
||||||
#include "image_manager.h"
|
|
||||||
#include "sampler_manager.h"
|
|
||||||
#include "view_manager.h"
|
|
||||||
|
|
||||||
namespace systems
|
|
||||||
{
|
|
||||||
class ResourceManager
|
|
||||||
{
|
|
||||||
|
|
||||||
struct CombinedImageViewManager
|
|
||||||
{
|
|
||||||
ImageManager *m_ImageManager;
|
|
||||||
ViewManager *m_ViewManager;
|
|
||||||
|
|
||||||
CombinedImageViewManager(ImageManager *imageManager, ViewManager *viewManager)
|
|
||||||
: m_ImageManager{imageManager}
|
|
||||||
, m_ViewManager{viewManager}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <concepts::ViewTo<Image> T>
|
|
||||||
[[nodiscard]] Ref<T>
|
|
||||||
CreateTexture2D(const Texture2DCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto handle = CreateTexture2D(createInfo);
|
|
||||||
return CastView<T>(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <concepts::ViewTo<ImageCube> T>
|
|
||||||
[[nodiscard]] Ref<T>
|
|
||||||
CreateTextureCube(const TextureCubeCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto handle = CreateTextureCube(createInfo);
|
|
||||||
return CastView<T>(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Ref<TextureView>
|
|
||||||
CreateTexture2D(const Texture2DCreateInfo &createInfo) const
|
|
||||||
{
|
|
||||||
return m_ViewManager->CreateView<TextureView>({
|
|
||||||
.m_Image = CastImage<Texture>(m_ImageManager->CreateTexture2D(createInfo)),
|
|
||||||
.m_Name = createInfo.m_Name,
|
|
||||||
.m_ViewType = vk::ImageViewType::e2D,
|
|
||||||
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Ref<ImageCubeView>
|
|
||||||
CreateTextureCube(const TextureCubeCreateInfo &createInfo) const
|
|
||||||
{
|
|
||||||
return m_ViewManager->CreateView<ImageCubeView>({
|
|
||||||
.m_Image = m_ImageManager->CreateTextureCube(createInfo),
|
|
||||||
.m_Name = createInfo.m_Name,
|
|
||||||
.m_ViewType = vk::ImageViewType::eCube,
|
|
||||||
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Ref<ImageView>
|
|
||||||
CreateAttachment(const AttachmentCreateInfo &createInfo) const
|
|
||||||
{
|
|
||||||
return m_ViewManager->CreateView({
|
|
||||||
.m_Image = m_ImageManager->CreateAttachment(createInfo),
|
|
||||||
.m_Name = createInfo.m_Name,
|
|
||||||
.m_ViewType = vk::ImageViewType::e2D,
|
|
||||||
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Ref<ImageView>
|
|
||||||
CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo) const
|
|
||||||
{
|
|
||||||
return m_ViewManager->CreateView({
|
|
||||||
.m_Image = m_ImageManager->CreateDepthStencilImage(createInfo),
|
|
||||||
.m_Name = createInfo.m_Name,
|
|
||||||
.m_ViewType = vk::ImageViewType::e2D,
|
|
||||||
.m_AspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BufferManager m_Buffers;
|
|
||||||
ImageManager m_Images;
|
|
||||||
SamplerManager m_Samplers;
|
|
||||||
ViewManager m_Views;
|
|
||||||
CombinedImageViewManager m_CombinedImageViews;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ResourceManager(const Device *device)
|
|
||||||
: m_Buffers{device}
|
|
||||||
, m_Images{device}
|
|
||||||
, m_Samplers{device}
|
|
||||||
, m_Views{device}
|
|
||||||
, m_CombinedImageViews{&m_Images, &m_Views}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferManager &
|
|
||||||
Buffers()
|
|
||||||
{
|
|
||||||
return m_Buffers;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageManager &
|
|
||||||
Images()
|
|
||||||
{
|
|
||||||
return m_Images;
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewManager &
|
|
||||||
Views()
|
|
||||||
{
|
|
||||||
return m_Views;
|
|
||||||
}
|
|
||||||
|
|
||||||
SamplerManager &
|
|
||||||
Samplers()
|
|
||||||
{
|
|
||||||
return m_Samplers;
|
|
||||||
}
|
|
||||||
|
|
||||||
CombinedImageViewManager &
|
|
||||||
CombinedImageViews()
|
|
||||||
{
|
|
||||||
return m_CombinedImageViews;
|
|
||||||
}
|
|
||||||
|
|
||||||
~ResourceManager() = default;
|
|
||||||
|
|
||||||
PIN_MEMORY(ResourceManager);
|
|
||||||
};
|
|
||||||
} // namespace systems
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: sampler_manager.h
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "EASTL/hash_map.h"
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
#include "aster/core/sampler.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
|
|
||||||
{
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class SamplerManager sampler_manager.h
|
|
||||||
*
|
|
||||||
* Manages (and caches) objects of sampler. Currently Samplers are never deleted.
|
|
||||||
*/
|
|
||||||
class SamplerManager final
|
|
||||||
{
|
|
||||||
using Handle = Ref<Sampler>;
|
|
||||||
using WeakHandle = WeakRef<Sampler>;
|
|
||||||
eastl::hash_map<vk::SamplerCreateInfo, WeakHandle> m_HashToSamplerIdx;
|
|
||||||
|
|
||||||
const Device *m_Device;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SamplerManager(const Device *device);
|
|
||||||
~SamplerManager();
|
|
||||||
|
|
||||||
Ref<Sampler> CreateSampler(const SamplerCreateInfo &createInfo);
|
|
||||||
};
|
|
||||||
} // namespace systems
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: view_manager.h
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
#include "aster/core/image_view.h"
|
|
||||||
|
|
||||||
#include <EASTL/optional.h>
|
|
||||||
|
|
||||||
namespace systems
|
|
||||||
{
|
|
||||||
|
|
||||||
template <concepts::View TTo, std::derived_from<Image> TFrom>
|
|
||||||
static Ref<TTo>
|
|
||||||
CastView(const Ref<View<TFrom>> &from)
|
|
||||||
{
|
|
||||||
if constexpr (not concepts::ImageInto<TFrom, typename TTo::ImageType>)
|
|
||||||
assert(TTo::ImageType::FLAGS & from->m_Image->m_Flags_);
|
|
||||||
return std::reinterpret_pointer_cast<TTo>(from);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <concepts::AnyImage TImage>
|
|
||||||
struct ViewCreateInfo
|
|
||||||
{
|
|
||||||
using ImageType = TImage;
|
|
||||||
|
|
||||||
Ref<ImageType> m_Image;
|
|
||||||
cstr m_Name;
|
|
||||||
vk::ImageViewType m_ViewType = vk::ImageViewType::e2D;
|
|
||||||
vk::ComponentMapping m_Components = {};
|
|
||||||
vk::ImageAspectFlags m_AspectMask = {};
|
|
||||||
eastl::optional<u8> m_MipLevelCount = eastl::nullopt;
|
|
||||||
eastl::optional<u8> m_LayerCount = eastl::nullopt;
|
|
||||||
u8 m_BaseMipLevel = 0;
|
|
||||||
u8 m_BaseLayer = 0;
|
|
||||||
|
|
||||||
[[nodiscard]] u8
|
|
||||||
GetMipLevelCount() const
|
|
||||||
{
|
|
||||||
return m_MipLevelCount.value_or(m_Image->m_MipLevels - m_BaseMipLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u8
|
|
||||||
GetLayerCount() const
|
|
||||||
{
|
|
||||||
return m_LayerCount.value_or(m_Image->m_LayerCount - m_BaseLayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit
|
|
||||||
operator vk::ImageViewCreateInfo() const
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
.image = m_Image->m_Image,
|
|
||||||
.viewType = m_ViewType,
|
|
||||||
.format = m_Image->m_Format,
|
|
||||||
.components = m_Components,
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = m_AspectMask,
|
|
||||||
.baseMipLevel = m_BaseMipLevel,
|
|
||||||
.levelCount = GetMipLevelCount(),
|
|
||||||
.baseArrayLayer = m_BaseLayer,
|
|
||||||
.layerCount = GetLayerCount(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit
|
|
||||||
operator ViewCreateInfo<Image>() const
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
.m_Image = CastImage<Image>(m_Image),
|
|
||||||
.m_Name = m_Name,
|
|
||||||
.m_ViewType = m_ViewType,
|
|
||||||
.m_Components = m_Components,
|
|
||||||
.m_AspectMask = m_AspectMask,
|
|
||||||
.m_MipLevelCount = m_MipLevelCount,
|
|
||||||
.m_LayerCount = m_LayerCount,
|
|
||||||
.m_BaseMipLevel = m_BaseMipLevel,
|
|
||||||
.m_BaseLayer = m_BaseLayer,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ViewManager final
|
|
||||||
{
|
|
||||||
const Device *m_Device;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ViewManager(const Device *device);
|
|
||||||
|
|
||||||
template <concepts::View TImageView>
|
|
||||||
Ref<TImageView>
|
|
||||||
CreateView(const ViewCreateInfo<typename TImageView::ImageType> &createInfo)
|
|
||||||
{
|
|
||||||
return CastView<TImageView>(CreateView(ViewCreateInfo<Image>(createInfo)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Ref<ImageView> CreateView(const ViewCreateInfo<Image> &createInfo) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace systems
|
|
||||||
|
|
@ -17,18 +17,12 @@ constexpr eastl::array DEVICE_EXTENSIONS = {
|
||||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
Device::Device(const Instance &context, PhysicalDevice &physicalDevice, Features &enabledFeatures,
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name)
|
const eastl::span<QueueAllocation> &queueAllocations, const eastl::span<u8> &pipelineCacheData,
|
||||||
: Device(context, physicalDevice, enabledFeatures, queueAllocations, {}, std::move(name))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, eastl::span<u8> &&pipelineCacheData,
|
|
||||||
NameString &&name)
|
NameString &&name)
|
||||||
: m_Name(std::move(name))
|
: m_Name(std::move(name))
|
||||||
, m_PhysicalDevice(physicalDevice->m_PhysicalDevice)
|
, m_PhysicalDevice(physicalDevice.m_PhysicalDevice)
|
||||||
, m_ValidationEnabled(context->m_DebugMessenger != nullptr)
|
, m_ValidationEnabled(context.m_DebugMessenger != nullptr)
|
||||||
{
|
{
|
||||||
// Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap
|
// Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap
|
||||||
eastl::fixed_vector<vk::DeviceQueueCreateInfo, 4> deviceQueueCreateInfos;
|
eastl::fixed_vector<vk::DeviceQueueCreateInfo, 4> deviceQueueCreateInfos;
|
||||||
|
|
@ -51,10 +45,10 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::PhysicalDeviceFeatures *deviceFeatures = &enabledFeatures->m_Vulkan10Features;
|
vk::PhysicalDeviceFeatures *deviceFeatures = &enabledFeatures.m_Vulkan10Features;
|
||||||
vk::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures->m_Vulkan11Features;
|
vk::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures.m_Vulkan11Features;
|
||||||
vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures->m_Vulkan12Features;
|
vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures.m_Vulkan12Features;
|
||||||
vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures->m_Vulkan13Features;
|
vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures.m_Vulkan13Features;
|
||||||
|
|
||||||
vulkan11Features->pNext = vulkan12Features;
|
vulkan11Features->pNext = vulkan12Features;
|
||||||
vulkan12Features->pNext = vulkan13Features;
|
vulkan12Features->pNext = vulkan13Features;
|
||||||
|
|
@ -71,7 +65,7 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
|
||||||
vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device);
|
vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device);
|
||||||
ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", result)
|
ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", result)
|
||||||
THEN_ABORT(result)
|
THEN_ABORT(result)
|
||||||
ELSE_DEBUG("{} ({}) Initialized.", m_Name, physicalDevice->m_DeviceProperties.deviceName.data());
|
ELSE_DEBUG("{} ({}) Initialized.", m_Name, physicalDevice.m_DeviceProperties.deviceName.data());
|
||||||
|
|
||||||
SetName(m_Device, m_Name.data());
|
SetName(m_Device, m_Name.data());
|
||||||
|
|
||||||
|
|
@ -85,7 +79,7 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
|
||||||
.physicalDevice = m_PhysicalDevice,
|
.physicalDevice = m_PhysicalDevice,
|
||||||
.device = m_Device,
|
.device = m_Device,
|
||||||
.pVulkanFunctions = &vmaVulkanFunctions,
|
.pVulkanFunctions = &vmaVulkanFunctions,
|
||||||
.instance = context->m_Instance,
|
.instance = context.m_Instance,
|
||||||
.vulkanApiVersion = ASTER_API_VERSION,
|
.vulkanApiVersion = ASTER_API_VERSION,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -110,6 +104,9 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
|
||||||
|
|
||||||
Device::~Device()
|
Device::~Device()
|
||||||
{
|
{
|
||||||
|
if (!m_Device)
|
||||||
|
return;
|
||||||
|
|
||||||
m_Device.destroy(m_PipelineCache, nullptr);
|
m_Device.destroy(m_PipelineCache, nullptr);
|
||||||
if (m_Allocator)
|
if (m_Allocator)
|
||||||
{
|
{
|
||||||
|
|
@ -156,6 +153,7 @@ Device::Device(Device &&other) noexcept
|
||||||
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
|
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
|
||||||
, m_Device(Take(other.m_Device))
|
, m_Device(Take(other.m_Device))
|
||||||
, m_Allocator(Take(other.m_Allocator))
|
, m_Allocator(Take(other.m_Allocator))
|
||||||
|
, m_PipelineCache(Take(other.m_PipelineCache))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,5 +166,6 @@ Device::operator=(Device &&other) noexcept
|
||||||
m_PhysicalDevice = Take(other.m_PhysicalDevice);
|
m_PhysicalDevice = Take(other.m_PhysicalDevice);
|
||||||
m_Device = Take(other.m_Device);
|
m_Device = Take(other.m_Device);
|
||||||
m_Allocator = Take(other.m_Allocator);
|
m_Allocator = Take(other.m_Allocator);
|
||||||
|
m_PipelineCache = Take(other.m_PipelineCache);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include "core/instance.h"
|
#include "core/instance.h"
|
||||||
|
|
||||||
|
#include "core/window.h"
|
||||||
|
|
||||||
#include <EASTL/array.h>
|
#include <EASTL/array.h>
|
||||||
#include <EASTL/fixed_vector.h>
|
#include <EASTL/fixed_vector.h>
|
||||||
|
|
||||||
|
|
@ -61,9 +63,9 @@ Instance::Instance(const cstr appName, const Version version, bool enableValidat
|
||||||
.pUserData = nullptr,
|
.pUserData = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 glfwExtensionCount = 0;
|
u32 windowExtensionCount = 0;
|
||||||
cstr *glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
cstr *windowExtensions = Window::GetInstanceExtensions(&windowExtensionCount);
|
||||||
eastl::fixed_vector<cstr, 3> instanceExtensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
|
eastl::fixed_vector<cstr, 3> instanceExtensions(windowExtensions, windowExtensions + windowExtensionCount);
|
||||||
if (enableValidation)
|
if (enableValidation)
|
||||||
{
|
{
|
||||||
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
|
@ -99,6 +101,9 @@ Instance::Instance(const cstr appName, const Version version, bool enableValidat
|
||||||
|
|
||||||
Instance::~Instance()
|
Instance::~Instance()
|
||||||
{
|
{
|
||||||
|
if (!m_Instance)
|
||||||
|
return;
|
||||||
|
|
||||||
if (m_DebugMessenger)
|
if (m_DebugMessenger)
|
||||||
{
|
{
|
||||||
m_Instance.destroy(m_DebugMessenger, nullptr);
|
m_Instance.destroy(m_DebugMessenger, nullptr);
|
||||||
|
|
|
||||||
|
|
@ -154,11 +154,10 @@ EnumeratePhysicalDevices(const vk::Instance instance)
|
||||||
return physicalDevices;
|
return physicalDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicalDevices::PhysicalDevices(const Surface *surface, const Instance *context)
|
PhysicalDevices::PhysicalDevices(const Surface &surface, const Instance &context)
|
||||||
{
|
{
|
||||||
auto physicalDevices = EnumeratePhysicalDevices(context->m_Instance);
|
for (auto physicalDevices = EnumeratePhysicalDevices(context.m_Instance); auto physicalDevice : physicalDevices)
|
||||||
for (auto physicalDevice : physicalDevices)
|
|
||||||
{
|
{
|
||||||
this->emplace_back(surface->m_Surface, physicalDevice);
|
this->emplace_back(surface.m_Surface, physicalDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,13 +8,12 @@
|
||||||
#include "core/instance.h"
|
#include "core/instance.h"
|
||||||
#include "core/window.h"
|
#include "core/window.h"
|
||||||
|
|
||||||
Surface::Surface(Instance *context, const Window *window, cstr name)
|
Surface::Surface(Instance &context, const Window &window)
|
||||||
: m_Context(context)
|
: m_Context(&context)
|
||||||
, m_Name(name)
|
|
||||||
{
|
{
|
||||||
VkSurfaceKHR surface;
|
VkSurfaceKHR surface;
|
||||||
auto result = Cast<vk::Result>(
|
auto result = Cast<vk::Result>(
|
||||||
glfwCreateWindowSurface(Cast<VkInstance>(m_Context->m_Instance), window->m_Window, nullptr, &surface));
|
glfwCreateWindowSurface(Cast<VkInstance>(m_Context->m_Instance), window.m_Window, nullptr, &surface));
|
||||||
ERROR_IF(Failed(result), "Failed to create Surface with {}", result)
|
ERROR_IF(Failed(result), "Failed to create Surface with {}", result)
|
||||||
THEN_ABORT(result)
|
THEN_ABORT(result)
|
||||||
ELSE_DEBUG("Surface {} Created", m_Name);
|
ELSE_DEBUG("Surface {} Created", m_Name);
|
||||||
|
|
@ -23,14 +22,14 @@ Surface::Surface(Instance *context, const Window *window, cstr name)
|
||||||
|
|
||||||
Surface::~Surface()
|
Surface::~Surface()
|
||||||
{
|
{
|
||||||
if (m_Context && m_Surface)
|
if (!m_Context || !m_Context->m_Instance || !m_Surface)
|
||||||
{
|
return;
|
||||||
m_Context->m_Instance.destroy(m_Surface, nullptr);
|
|
||||||
DEBUG("Surface Destroyed");
|
|
||||||
|
|
||||||
m_Surface = nullptr;
|
m_Context->m_Instance.destroy(m_Surface, nullptr);
|
||||||
m_Context = nullptr;
|
DEBUG("Surface Destroyed");
|
||||||
}
|
|
||||||
|
m_Surface = nullptr;
|
||||||
|
m_Context = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Surface::Surface(Surface &&other) noexcept
|
Surface::Surface(Surface &&other) noexcept
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,8 @@
|
||||||
|
|
||||||
[[nodiscard]] vk::Extent2D GetExtent(Size2D size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities);
|
[[nodiscard]] vk::Extent2D GetExtent(Size2D size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities);
|
||||||
|
|
||||||
Swapchain::Swapchain(const Surface *surface, const Device *device, Size2D size, NameString &&name)
|
Swapchain::Swapchain(const Surface &surface, const Device &device, Size2D size)
|
||||||
: m_Device(device)
|
: m_Device(&device)
|
||||||
, m_Name(std::move(name))
|
|
||||||
, m_Format(vk::Format::eUndefined)
|
, m_Format(vk::Format::eUndefined)
|
||||||
{
|
{
|
||||||
this->Create(surface, size);
|
this->Create(surface, size);
|
||||||
|
|
@ -27,11 +26,11 @@ Swapchain::~Swapchain()
|
||||||
Swapchain::Swapchain(Swapchain &&other) noexcept
|
Swapchain::Swapchain(Swapchain &&other) noexcept
|
||||||
: m_Device(other.m_Device)
|
: m_Device(other.m_Device)
|
||||||
, m_Swapchain(Take(other.m_Swapchain))
|
, m_Swapchain(Take(other.m_Swapchain))
|
||||||
, m_Name(std::move(other.m_Name))
|
|
||||||
, m_Extent(other.m_Extent)
|
, m_Extent(other.m_Extent)
|
||||||
, m_Format(other.m_Format)
|
, m_Format(other.m_Format)
|
||||||
, m_Images(std::move(other.m_Images))
|
, m_Images(std::move(other.m_Images))
|
||||||
, m_ImageViews(std::move(other.m_ImageViews))
|
, m_ImageViews(std::move(other.m_ImageViews))
|
||||||
|
, m_ResizeCallbacks(std::move(other.m_ResizeCallbacks))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,29 +41,29 @@ Swapchain::operator=(Swapchain &&other) noexcept
|
||||||
return *this;
|
return *this;
|
||||||
m_Device = other.m_Device;
|
m_Device = other.m_Device;
|
||||||
m_Swapchain = Take(other.m_Swapchain);
|
m_Swapchain = Take(other.m_Swapchain);
|
||||||
m_Name = std::move(other.m_Name);
|
|
||||||
m_Extent = other.m_Extent;
|
m_Extent = other.m_Extent;
|
||||||
m_Format = other.m_Format;
|
m_Format = other.m_Format;
|
||||||
m_Images = std::move(other.m_Images);
|
m_Images = std::move(other.m_Images);
|
||||||
m_ImageViews = std::move(other.m_ImageViews);
|
m_ImageViews = std::move(other.m_ImageViews);
|
||||||
|
m_ResizeCallbacks = std::move(other.m_ResizeCallbacks);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Swapchain::Create(const Surface *surface, Size2D size)
|
Swapchain::Create(const Surface &surface, Size2D size)
|
||||||
{
|
{
|
||||||
auto surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface->m_Surface);
|
auto surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface.m_Surface);
|
||||||
m_Extent = GetExtent(size, &surfaceCapabilities);
|
m_Extent = GetExtent(size, &surfaceCapabilities);
|
||||||
|
|
||||||
while (m_Extent.width == 0 || m_Extent.height == 0)
|
while (m_Extent.width == 0 || m_Extent.height == 0)
|
||||||
{
|
{
|
||||||
glfwWaitEvents();
|
glfwWaitEvents();
|
||||||
surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface->m_Surface);
|
surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface.m_Surface);
|
||||||
m_Extent = GetExtent(size, &surfaceCapabilities);
|
m_Extent = GetExtent(size, &surfaceCapabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto surfaceFormats = GetSurfaceFormats(m_Device->m_PhysicalDevice, surface->m_Surface);
|
auto surfaceFormats = GetSurfaceFormats(m_Device->m_PhysicalDevice, surface.m_Surface);
|
||||||
auto presentModes = GetSurfacePresentModes(m_Device->m_PhysicalDevice, surface->m_Surface);
|
auto presentModes = GetSurfacePresentModes(m_Device->m_PhysicalDevice, surface.m_Surface);
|
||||||
|
|
||||||
m_Format = vk::Format::eUndefined;
|
m_Format = vk::Format::eUndefined;
|
||||||
auto swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
|
auto swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
|
||||||
|
|
@ -95,16 +94,14 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 swapchainImageCount = 3;
|
u32 swapchainImageCount = 3;
|
||||||
if (surfaceCapabilities.maxImageCount > 0)
|
u32 maxImageCount =
|
||||||
{
|
glm::max(swapchainImageCount, glm::max(surfaceCapabilities.maxImageCount, surfaceCapabilities.minImageCount));
|
||||||
swapchainImageCount =
|
swapchainImageCount = glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, maxImageCount);
|
||||||
glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Note that different queues might need the images to be shared.
|
// TODO: Note that different queues might need the images to be shared.
|
||||||
|
|
||||||
const vk::SwapchainCreateInfoKHR swapchainCreateInfo = {
|
const vk::SwapchainCreateInfoKHR swapchainCreateInfo = {
|
||||||
.surface = surface->m_Surface,
|
.surface = surface.m_Surface,
|
||||||
.minImageCount = swapchainImageCount,
|
.minImageCount = swapchainImageCount,
|
||||||
.imageFormat = m_Format,
|
.imageFormat = m_Format,
|
||||||
.imageColorSpace = swapchainColorSpace,
|
.imageColorSpace = swapchainColorSpace,
|
||||||
|
|
@ -120,28 +117,30 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::Device device = m_Device->m_Device;
|
vk::Device device = m_Device->m_Device;
|
||||||
|
NameString name = "Swapchain of ";
|
||||||
|
name += m_Device->m_Name;
|
||||||
|
|
||||||
vk::SwapchainKHR swapchain;
|
vk::SwapchainKHR swapchain;
|
||||||
vk::Result result = device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
|
vk::Result result = device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
|
||||||
ERROR_IF(Failed(result), "Swapchain {} creation failed. Cause {}", m_Name, result)
|
ERROR_IF(Failed(result), "'{}' creation failed. Cause {}", name, result)
|
||||||
THEN_ABORT(result)
|
THEN_ABORT(result)
|
||||||
ELSE_DEBUG("Created Swapchain '{}'", m_Name);
|
ELSE_DEBUG("Created '{}'", name);
|
||||||
|
|
||||||
// Irrelevant on the first run. Required for re-creation.
|
// Irrelevant on the first run. Required for re-creation.
|
||||||
Cleanup();
|
Cleanup();
|
||||||
|
|
||||||
m_Swapchain = swapchain;
|
m_Swapchain = swapchain;
|
||||||
|
|
||||||
m_Device->SetName(m_Swapchain, m_Name.data());
|
m_Device->SetName(m_Swapchain, m_Device->m_Name.data());
|
||||||
|
|
||||||
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr);
|
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr);
|
||||||
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
|
ERROR_IF(Failed(result), "Failed getting {}'s images. Cause {}", name, result)
|
||||||
THEN_ABORT(result);
|
THEN_ABORT(result);
|
||||||
|
|
||||||
// Managed by the Swapchain.
|
// Managed by the Swapchain.
|
||||||
m_Images.resize(swapchainImageCount);
|
m_Images.resize(swapchainImageCount, nullptr);
|
||||||
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, m_Images.data());
|
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, m_Images.data());
|
||||||
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
|
ERROR_IF(Failed(result), "Failed getting {}'s images. Cause {}", name, result)
|
||||||
THEN_ABORT(result);
|
THEN_ABORT(result);
|
||||||
|
|
||||||
vk::ImageViewCreateInfo viewCreateInfo = {
|
vk::ImageViewCreateInfo viewCreateInfo = {
|
||||||
|
|
@ -165,7 +164,7 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
|
|
||||||
vk::ImageView imageView;
|
vk::ImageView imageView;
|
||||||
result = device.createImageView(&viewCreateInfo, nullptr, &imageView);
|
result = device.createImageView(&viewCreateInfo, nullptr, &imageView);
|
||||||
ERROR_IF(Failed(result), "Failed creating swapchain {}'s image view [{}]. Cause {}", m_Name, index, result)
|
ERROR_IF(Failed(result), "Failed creating {}'s image view [{}]. Cause {}", name, index, result)
|
||||||
THEN_ABORT(result);
|
THEN_ABORT(result);
|
||||||
|
|
||||||
m_ImageViews.push_back(imageView);
|
m_ImageViews.push_back(imageView);
|
||||||
|
|
@ -173,7 +172,7 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG("Swapchain {} Image Views created.", m_Name);
|
DEBUG("{} Image Views created.", name);
|
||||||
|
|
||||||
for (auto &callback : m_ResizeCallbacks)
|
for (auto &callback : m_ResizeCallbacks)
|
||||||
{
|
{
|
||||||
|
|
@ -184,24 +183,31 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
void
|
void
|
||||||
Swapchain::RegisterResizeCallback(FnResizeCallback &&callback)
|
Swapchain::RegisterResizeCallback(FnResizeCallback &&callback)
|
||||||
{
|
{
|
||||||
m_ResizeCallbacks.emplace_back(callback);
|
m_ResizeCallbacks.emplace_back(std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Swapchain::Cleanup()
|
Swapchain::Cleanup()
|
||||||
{
|
{
|
||||||
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
|
if (!m_Swapchain)
|
||||||
DEBUG("Swapchain {} Image Views destroyed.", m_Name);
|
return;
|
||||||
|
|
||||||
|
NameString name = "Swapchain of ";
|
||||||
|
name += m_Device->m_Name;
|
||||||
|
|
||||||
for (const auto imageView : m_ImageViews)
|
for (const auto imageView : m_ImageViews)
|
||||||
{
|
{
|
||||||
m_Device->m_Device.destroy(imageView, nullptr);
|
m_Device->m_Device.destroy(imageView, nullptr);
|
||||||
}
|
}
|
||||||
|
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
|
||||||
|
DEBUG("Swapchain {} Image Views destroyed.", name);
|
||||||
m_ImageViews.clear();
|
m_ImageViews.clear();
|
||||||
|
m_Images.clear();
|
||||||
if (m_Swapchain)
|
if (m_Swapchain)
|
||||||
{
|
{
|
||||||
m_Device->m_Device.destroy(m_Swapchain, nullptr);
|
m_Device->m_Device.destroy(m_Swapchain, nullptr);
|
||||||
m_Swapchain = nullptr;
|
m_Swapchain = nullptr;
|
||||||
DEBUG("Swapchain '{}' destroyed.", m_Name);
|
DEBUG("Swapchain '{}' destroyed.", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,30 @@
|
||||||
std::atomic_uint64_t Window::m_WindowCount = 0;
|
std::atomic_uint64_t Window::m_WindowCount = 0;
|
||||||
std::atomic_bool Window::m_IsGlfwInit = false;
|
std::atomic_bool Window::m_IsGlfwInit = false;
|
||||||
|
|
||||||
|
void
|
||||||
|
Window::SetupLibrary()
|
||||||
|
{
|
||||||
|
if (!m_IsGlfwInit)
|
||||||
|
{
|
||||||
|
if (!glfwInit())
|
||||||
|
{
|
||||||
|
const char *error = nullptr;
|
||||||
|
const auto code = glfwGetError(&error);
|
||||||
|
ERROR("GLFW Init failed. Cause: ({}) {}", code, error)
|
||||||
|
THEN_ABORT(code);
|
||||||
|
}
|
||||||
|
m_WindowCount = 0;
|
||||||
|
m_IsGlfwInit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cstr*
|
||||||
|
Window::GetInstanceExtensions(u32 *extensionCount)
|
||||||
|
{
|
||||||
|
SetupLibrary();
|
||||||
|
return glfwGetRequiredInstanceExtensions(extensionCount);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Window::RequestExit() const noexcept
|
Window::RequestExit() const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -42,18 +66,7 @@ Window::Window(const cstr title, Size2D extent, const b8 isFullScreen)
|
||||||
{
|
{
|
||||||
m_Name = title;
|
m_Name = title;
|
||||||
|
|
||||||
if (!m_IsGlfwInit)
|
SetupLibrary();
|
||||||
{
|
|
||||||
if (!glfwInit())
|
|
||||||
{
|
|
||||||
const char *error = nullptr;
|
|
||||||
const auto code = glfwGetError(&error);
|
|
||||||
ERROR("GLFW Init failed. Cause: ({}) {}", code, error)
|
|
||||||
THEN_ABORT(code);
|
|
||||||
}
|
|
||||||
m_WindowCount = 0;
|
|
||||||
m_IsGlfwInit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
||||||
ERROR_IF(!monitor, "No monitor found");
|
ERROR_IF(!monitor, "No monitor found");
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,5 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
target_sources(aster_core
|
target_sources(aster_core
|
||||||
PRIVATE
|
PRIVATE
|
||||||
"buffer_manager.cpp"
|
"device.cpp"
|
||||||
"image_manager.cpp"
|
|
||||||
"view_manager.cpp"
|
|
||||||
"sampler_manager.cpp"
|
|
||||||
"commit_manager.cpp")
|
"commit_manager.cpp")
|
||||||
|
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: buffer_manager.cpp
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "systems/buffer_manager.h"
|
|
||||||
|
|
||||||
using namespace systems;
|
|
||||||
|
|
||||||
Ref<StorageBuffer>
|
|
||||||
BufferManager::CreateStorageBuffer(const usize size, const cstr name) const
|
|
||||||
{
|
|
||||||
// TODO: Storage and Index buffer are set.
|
|
||||||
// This is hacky and should be improved.
|
|
||||||
constexpr vk::BufferUsageFlags usage =
|
|
||||||
vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer |
|
|
||||||
vk::BufferUsageFlagBits::eShaderDeviceAddress | vk::BufferUsageFlagBits::eTransferDst;
|
|
||||||
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
||||||
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
|
||||||
return std::make_shared<StorageBuffer>(Buffer{m_Device, size, usage, createFlags, memoryUsage, name});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<UniformBuffer>
|
|
||||||
BufferManager::CreateUniformBuffer(const usize size, const cstr name) const
|
|
||||||
{
|
|
||||||
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eUniformBuffer;
|
|
||||||
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
||||||
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
|
||||||
return std::make_shared<UniformBuffer>(Buffer{m_Device, size, usage, createFlags, memoryUsage, name});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<StagingBuffer>
|
|
||||||
BufferManager::CreateStagingBuffer(const usize size, const cstr name) const
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
return std::make_shared<StagingBuffer>(Buffer{m_Device, size, usage, createFlags, memoryUsage, name});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<VertexBuffer>
|
|
||||||
BufferManager::CreateVertexBuffer(const usize size, const cstr name) const
|
|
||||||
{
|
|
||||||
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eVertexBuffer;
|
|
||||||
constexpr VmaAllocationCreateFlags createFlags =
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
||||||
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
|
||||||
return std::make_shared<VertexBuffer>(Buffer{m_Device, size, usage, createFlags, memoryUsage, name});
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// void
|
|
||||||
// UniformBuffer::Init(const Device *device, const usize size, const cstr name)
|
|
||||||
//{
|
|
||||||
// Allocate(device, size, vk::BufferUsageFlagBits::eUniformBuffer,
|
|
||||||
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
// VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
// VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
// void
|
|
||||||
// StorageBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name)
|
|
||||||
//{
|
|
||||||
// Init(device, size, hostVisible, false, name);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
// void
|
|
||||||
// StorageBuffer::Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name)
|
|
||||||
//{
|
|
||||||
// vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer;
|
|
||||||
// if (deviceAddress)
|
|
||||||
// {
|
|
||||||
// usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress;
|
|
||||||
// }
|
|
||||||
// if (hostVisible)
|
|
||||||
// {
|
|
||||||
// Allocate(device, size, usage,
|
|
||||||
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
// VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
// VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// usage |= vk::BufferUsageFlagBits::eTransferDst;
|
|
||||||
// Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
// void
|
|
||||||
// StorageIndexBuffer::Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name)
|
|
||||||
//{
|
|
||||||
// vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer;
|
|
||||||
// if (deviceAddress)
|
|
||||||
// {
|
|
||||||
// usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress;
|
|
||||||
// }
|
|
||||||
// if (hostVisible)
|
|
||||||
// {
|
|
||||||
// Allocate(device, size, usage,
|
|
||||||
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
// VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
// VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// usage |= vk::BufferUsageFlagBits::eTransferDst;
|
|
||||||
// Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
// void
|
|
||||||
// IndirectBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name)
|
|
||||||
//{
|
|
||||||
// vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndirectBuffer |
|
|
||||||
// vk::BufferUsageFlagBits::eShaderDeviceAddress;
|
|
||||||
// if (hostVisible)
|
|
||||||
// {
|
|
||||||
// Allocate(device, size, usage,
|
|
||||||
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
// VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
// VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// usage |= vk::BufferUsageFlagBits::eTransferDst;
|
|
||||||
// Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
// void
|
|
||||||
// VertexBuffer::Init(const Device *device, usize size, cstr name)
|
|
||||||
//{
|
|
||||||
// Allocate(device, size, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst, 0,
|
|
||||||
// VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
// void
|
|
||||||
// IndexBuffer::Init(const Device *device, usize size, cstr name)
|
|
||||||
//{
|
|
||||||
// Allocate(device, size, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst, 0,
|
|
||||||
// VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
// void
|
|
||||||
// StagingBuffer::Init(const Device *device, usize size, cstr name)
|
|
||||||
//{
|
|
||||||
// Allocate(device, size, vk::BufferUsageFlagBits::eTransferSrc,
|
|
||||||
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
// VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
//}
|
|
||||||
|
|
||||||
BufferManager::BufferManager(const Device *device)
|
|
||||||
: m_Device{device}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,694 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: device.cpp
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "systems/device.h"
|
||||||
|
|
||||||
|
#include "core/queue_allocation.h"
|
||||||
|
#include "core/window.h"
|
||||||
|
#include "systems/resource.h"
|
||||||
|
|
||||||
|
static constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT =
|
||||||
|
QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::eCompute |
|
||||||
|
QueueSupportFlagBits::ePresent | QueueSupportFlagBits::eTransfer;
|
||||||
|
|
||||||
|
PhysicalDevice
|
||||||
|
systems::DefaultPhysicalDeviceSelector(const PhysicalDevices &physicalDevices)
|
||||||
|
{
|
||||||
|
for (auto &physicalDevice : physicalDevices)
|
||||||
|
{
|
||||||
|
const bool hasAllRequiredQueues =
|
||||||
|
std::ranges::any_of(physicalDevice.m_QueueFamilies, [](const auto &queueFamilyProp) {
|
||||||
|
return (queueFamilyProp.m_Support & REQUIRED_QUEUE_SUPPORT) == REQUIRED_QUEUE_SUPPORT;
|
||||||
|
});
|
||||||
|
|
||||||
|
const bool isNotCpu = physicalDevice.m_DeviceProperties.deviceType != vk::PhysicalDeviceType::eCpu;
|
||||||
|
|
||||||
|
const bool hasPresentMode = !physicalDevice.m_PresentModes.empty();
|
||||||
|
|
||||||
|
const bool hasSurfaceFormat = !physicalDevice.m_SurfaceFormats.empty();
|
||||||
|
|
||||||
|
if (hasSurfaceFormat && hasPresentMode && isNotCpu && hasAllRequiredQueues)
|
||||||
|
{
|
||||||
|
return physicalDevice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ERROR("No suitable GPU available on the system.")
|
||||||
|
THEN_ABORT(vk::Result::eErrorUnknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Buffer Management
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
Ref<StorageBuffer>
|
||||||
|
systems::Device::CreateStorageBuffer(const usize size, const cstr name)
|
||||||
|
{
|
||||||
|
// TODO: Storage and Index buffer are set.
|
||||||
|
// This is hacky and should be improved.
|
||||||
|
constexpr vk::BufferUsageFlags usage =
|
||||||
|
vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer |
|
||||||
|
vk::BufferUsageFlagBits::eShaderDeviceAddress | vk::BufferUsageFlagBits::eTransferDst;
|
||||||
|
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
||||||
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
|
||||||
|
VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||||
|
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
||||||
|
return std::make_shared<StorageBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<UniformBuffer>
|
||||||
|
systems::Device::CreateUniformBuffer(const usize size, const cstr name)
|
||||||
|
{
|
||||||
|
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eUniformBuffer;
|
||||||
|
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
||||||
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
|
||||||
|
VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||||
|
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
||||||
|
return std::make_shared<UniformBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<StagingBuffer>
|
||||||
|
systems::Device::CreateStagingBuffer(const usize size, const cstr name)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
return std::make_shared<StagingBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<VertexBuffer>
|
||||||
|
systems::Device::CreateVertexBuffer(const usize size, const cstr name)
|
||||||
|
{
|
||||||
|
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eVertexBuffer;
|
||||||
|
constexpr VmaAllocationCreateFlags createFlags =
|
||||||
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||||
|
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
||||||
|
return std::make_shared<VertexBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Image Management
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
vk::ImageCreateInfo ToImageCreateInfo(const systems::Texture2DCreateInfo &createInfo);
|
||||||
|
vk::ImageCreateInfo ToImageCreateInfo(const systems::TextureCubeCreateInfo &createInfo);
|
||||||
|
vk::ImageCreateInfo ToImageCreateInfo(const systems::AttachmentCreateInfo &createInfo);
|
||||||
|
vk::ImageCreateInfo ToImageCreateInfo(const systems::DepthStencilImageCreateInfo &createInfo);
|
||||||
|
|
||||||
|
namespace usage_flags
|
||||||
|
{
|
||||||
|
constexpr vk::ImageUsageFlags MIPMAP = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
||||||
|
constexpr vk::ImageUsageFlags SAMPLE = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
|
||||||
|
constexpr vk::ImageUsageFlags STORAGE =
|
||||||
|
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc;
|
||||||
|
constexpr vk::ImageUsageFlags COLOR_ATTACHMENT =
|
||||||
|
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc;
|
||||||
|
constexpr vk::ImageUsageFlags DEPTH_STENCIL_ATTACHMENT = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||||
|
} // namespace usage_flags
|
||||||
|
|
||||||
|
Ref<Image>
|
||||||
|
systems::Device::CreateTexture2D(const Texture2DCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||||
|
.flags = {},
|
||||||
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkImage rawImage;
|
||||||
|
VmaAllocation allocation;
|
||||||
|
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
||||||
|
auto result = Cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
||||||
|
&allocationCreateInfo, &rawImage, &allocation, nullptr));
|
||||||
|
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
||||||
|
|
||||||
|
vk::Image image = rawImage;
|
||||||
|
|
||||||
|
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
||||||
|
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
||||||
|
Image::Flags flags = {};
|
||||||
|
if (createInfo.m_IsSampled)
|
||||||
|
flags |= Image::FlagBits::eSampled;
|
||||||
|
if (createInfo.m_IsStorage)
|
||||||
|
flags |= Image::FlagBits::eStorage;
|
||||||
|
|
||||||
|
m_Device.SetName(image, createInfo.m_Name);
|
||||||
|
|
||||||
|
return std::make_shared<Image>(&m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format, flags,
|
||||||
|
layerCount, mipLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ImageCube>
|
||||||
|
systems::Device::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||||
|
.flags = {},
|
||||||
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkImage rawImage;
|
||||||
|
VmaAllocation allocation;
|
||||||
|
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
||||||
|
auto result = Cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
||||||
|
&allocationCreateInfo, &rawImage, &allocation, nullptr));
|
||||||
|
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
||||||
|
|
||||||
|
vk::Image image = rawImage;
|
||||||
|
|
||||||
|
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
||||||
|
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
||||||
|
Image::Flags flags = Image::FlagBits::eCube;
|
||||||
|
if (createInfo.m_IsSampled)
|
||||||
|
flags |= Image::FlagBits::eSampled;
|
||||||
|
if (createInfo.m_IsStorage)
|
||||||
|
flags |= Image::FlagBits::eStorage;
|
||||||
|
|
||||||
|
m_Device.SetName(image, createInfo.m_Name);
|
||||||
|
|
||||||
|
return CastImage<ImageCube>(std::make_shared<Image>(&m_Device, image, allocation, imageCreateInfo.extent,
|
||||||
|
imageCreateInfo.format, flags, layerCount, mipLevels));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<Image>
|
||||||
|
systems::Device::CreateAttachment(const AttachmentCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||||
|
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
||||||
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkImage rawImage;
|
||||||
|
VmaAllocation allocation;
|
||||||
|
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
||||||
|
auto result = Cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
||||||
|
&allocationCreateInfo, &rawImage, &allocation, nullptr));
|
||||||
|
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
||||||
|
|
||||||
|
vk::Image image = rawImage;
|
||||||
|
|
||||||
|
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
||||||
|
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
||||||
|
|
||||||
|
m_Device.SetName(image, createInfo.m_Name);
|
||||||
|
|
||||||
|
return std::make_shared<Image>(&m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format,
|
||||||
|
Image::Flags{}, layerCount, mipLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<Image>
|
||||||
|
systems::Device::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||||
|
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
||||||
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkImage rawImage;
|
||||||
|
VmaAllocation allocation;
|
||||||
|
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
||||||
|
auto result = Cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
||||||
|
&allocationCreateInfo, &rawImage, &allocation, nullptr));
|
||||||
|
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
||||||
|
|
||||||
|
vk::Image image = rawImage;
|
||||||
|
|
||||||
|
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
||||||
|
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
||||||
|
|
||||||
|
m_Device.SetName(image, createInfo.m_Name);
|
||||||
|
|
||||||
|
return std::make_shared<Image>(&m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format,
|
||||||
|
Image::Flags{}, layerCount, mipLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ImageCreateInfo
|
||||||
|
ToImageCreateInfo(const systems::Texture2DCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
auto &[format, extent, name, isSampled, isMipMapped, isStorage] = createInfo;
|
||||||
|
|
||||||
|
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::ImageUsageFlags{};
|
||||||
|
if (isSampled)
|
||||||
|
usage |= usage_flags::SAMPLE;
|
||||||
|
if (isMipMapped)
|
||||||
|
usage |= usage_flags::MIPMAP;
|
||||||
|
if (isStorage)
|
||||||
|
usage |= usage_flags::STORAGE;
|
||||||
|
|
||||||
|
return {
|
||||||
|
.imageType = vk::ImageType::e2D,
|
||||||
|
.format = format,
|
||||||
|
.extent = ToExtent3D(extent, 1),
|
||||||
|
.mipLevels = mipLevels,
|
||||||
|
.arrayLayers = 1,
|
||||||
|
.usage = usage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ImageCreateInfo
|
||||||
|
ToImageCreateInfo(const systems::TextureCubeCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
auto &[format, side, name, isSampled, isMipMapped, isStorage] = createInfo;
|
||||||
|
|
||||||
|
WARN_IF(!IsPowerOfTwo(side), "ImageCube {1} is {0}x{0} (Non Power of Two)", side, name ? name : "<unnamed>");
|
||||||
|
|
||||||
|
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(side))) : 1;
|
||||||
|
|
||||||
|
auto usage = vk::ImageUsageFlags{};
|
||||||
|
if (isSampled)
|
||||||
|
usage |= usage_flags::SAMPLE;
|
||||||
|
if (isMipMapped)
|
||||||
|
usage |= usage_flags::MIPMAP;
|
||||||
|
if (isStorage)
|
||||||
|
usage |= usage_flags::STORAGE;
|
||||||
|
|
||||||
|
return {
|
||||||
|
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
|
||||||
|
.imageType = vk::ImageType::e2D,
|
||||||
|
.format = format,
|
||||||
|
.extent = {side, side, 1},
|
||||||
|
.mipLevels = mipLevels,
|
||||||
|
.arrayLayers = 6,
|
||||||
|
.usage = usage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ImageCreateInfo
|
||||||
|
ToImageCreateInfo(const systems::AttachmentCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
auto &[format, extent, name] = createInfo;
|
||||||
|
|
||||||
|
constexpr auto usage = usage_flags::COLOR_ATTACHMENT;
|
||||||
|
|
||||||
|
return {
|
||||||
|
.imageType = vk::ImageType::e2D,
|
||||||
|
.format = format,
|
||||||
|
.extent = ToExtent3D(extent, 1),
|
||||||
|
.mipLevels = 1,
|
||||||
|
.arrayLayers = 1,
|
||||||
|
.usage = usage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ImageCreateInfo
|
||||||
|
ToImageCreateInfo(const systems::DepthStencilImageCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
auto &[extent, name] = createInfo;
|
||||||
|
|
||||||
|
constexpr auto format = vk::Format::eD24UnormS8Uint;
|
||||||
|
constexpr auto usage = usage_flags::DEPTH_STENCIL_ATTACHMENT;
|
||||||
|
|
||||||
|
return {
|
||||||
|
.imageType = vk::ImageType::e2D,
|
||||||
|
.format = format,
|
||||||
|
.extent = ToExtent3D(extent, 1),
|
||||||
|
.mipLevels = 1,
|
||||||
|
.arrayLayers = 1,
|
||||||
|
.usage = usage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region View Management
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
Ref<ImageView>
|
||||||
|
systems::Device::CreateView(const ViewCreateInfo<Image> &createInfo)
|
||||||
|
{
|
||||||
|
const auto layerCount = createInfo.GetLayerCount();
|
||||||
|
const auto mipCount = createInfo.GetMipLevelCount();
|
||||||
|
ERROR_IF((createInfo.m_BaseLayer + layerCount) > createInfo.m_Image->m_LayerCount, "Invalid Layer Access")
|
||||||
|
THEN_ABORT(-1);
|
||||||
|
ERROR_IF((createInfo.m_BaseMipLevel + mipCount) > createInfo.m_Image->m_MipLevels, "Invalid Mip Level Access")
|
||||||
|
THEN_ABORT(-1);
|
||||||
|
|
||||||
|
vk::ImageView view;
|
||||||
|
const auto imageViewCreateInfo = Cast<vk::ImageViewCreateInfo>(createInfo);
|
||||||
|
auto result = m_Device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
||||||
|
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
m_Device.SetName(view, createInfo.m_Name);
|
||||||
|
|
||||||
|
return std::make_shared<ImageView>(createInfo.m_Image, view, createInfo.m_Image->m_Extent, createInfo.m_BaseLayer,
|
||||||
|
layerCount, createInfo.m_BaseMipLevel, mipCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Image - View Combined Management
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
Ref<TextureView>
|
||||||
|
systems::Device::CreateTexture2DWithView(const Texture2DCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
return CreateView<TextureView>({
|
||||||
|
.m_Image = CastImage<Texture>(CreateTexture2D(createInfo)),
|
||||||
|
.m_Name = createInfo.m_Name,
|
||||||
|
.m_ViewType = vk::ImageViewType::e2D,
|
||||||
|
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ImageCubeView>
|
||||||
|
systems::Device::CreateTextureCubeWithView(const TextureCubeCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
return CreateView<ImageCubeView>({
|
||||||
|
.m_Image = CreateTextureCube(createInfo),
|
||||||
|
.m_Name = createInfo.m_Name,
|
||||||
|
.m_ViewType = vk::ImageViewType::eCube,
|
||||||
|
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ImageView>
|
||||||
|
systems::Device::CreateAttachmentWithView(const AttachmentCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
return CreateView({
|
||||||
|
.m_Image = CreateAttachment(createInfo),
|
||||||
|
.m_Name = createInfo.m_Name,
|
||||||
|
.m_ViewType = vk::ImageViewType::e2D,
|
||||||
|
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ImageView>
|
||||||
|
systems::Device::CreateDepthStencilImageWithView(const DepthStencilImageCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
return CreateView({
|
||||||
|
.m_Image = CreateDepthStencilImage(createInfo),
|
||||||
|
.m_Name = createInfo.m_Name,
|
||||||
|
.m_ViewType = vk::ImageViewType::e2D,
|
||||||
|
.m_AspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Sampler
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
Ref<Sampler>
|
||||||
|
systems::Device::CreateSampler(const SamplerCreateInfo &createInfo)
|
||||||
|
{
|
||||||
|
auto vkCreateInfo = Cast<vk::SamplerCreateInfo>(createInfo);
|
||||||
|
|
||||||
|
if (const auto iter = m_HashToSamplerIdx.find(vkCreateInfo);
|
||||||
|
iter != m_HashToSamplerIdx.end() && !iter->second.expired())
|
||||||
|
{
|
||||||
|
return iter->second.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto object = std::make_shared<Sampler>(&m_Device, vkCreateInfo, createInfo.m_Name ? createInfo.m_Name : nullptr);
|
||||||
|
m_HashToSamplerIdx.emplace(vkCreateInfo, object);
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
QueueAllocation
|
||||||
|
FindAppropriateQueueAllocation(const PhysicalDevice &physicalDevice)
|
||||||
|
{
|
||||||
|
for (auto &queueFamilyInfo : physicalDevice.m_QueueFamilies)
|
||||||
|
{
|
||||||
|
if ((queueFamilyInfo.m_Support & REQUIRED_QUEUE_SUPPORT) == REQUIRED_QUEUE_SUPPORT)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.m_Family = queueFamilyInfo.m_Index,
|
||||||
|
.m_Count = queueFamilyInfo.m_Count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ERROR("No suitable queue family on the GPU.")
|
||||||
|
THEN_ABORT(vk::Result::eErrorUnknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::Device::Device(const DeviceCreateInfo &createInfo)
|
||||||
|
: m_Window{createInfo.m_Window}
|
||||||
|
{
|
||||||
|
assert(createInfo.m_AppName);
|
||||||
|
assert(createInfo.m_PhysicalDeviceSelector);
|
||||||
|
|
||||||
|
m_Instance = Instance{createInfo.m_AppName, createInfo.m_AppVersion};
|
||||||
|
m_Surface = Surface{m_Instance, createInfo.m_Window};
|
||||||
|
|
||||||
|
const auto physicalDevices = PhysicalDevices{m_Surface, m_Instance};
|
||||||
|
auto physicalDevice = createInfo.m_PhysicalDeviceSelector(physicalDevices);
|
||||||
|
|
||||||
|
Features features = createInfo.m_Features;
|
||||||
|
|
||||||
|
// TODO: Add the other queues
|
||||||
|
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(physicalDevice);
|
||||||
|
|
||||||
|
m_Device = ::Device{
|
||||||
|
m_Instance, physicalDevice, features, {&queueAllocation, 1}, createInfo.m_PipelineCacheData, createInfo.m_Name};
|
||||||
|
|
||||||
|
m_GraphicsQueueFamily = queueAllocation.m_Family;
|
||||||
|
m_GraphicsQueue = m_Device.GetQueue(m_GraphicsQueueFamily, 0);
|
||||||
|
|
||||||
|
m_Swapchain = Swapchain{m_Surface, m_Device, m_Window.get().GetSize()};
|
||||||
|
|
||||||
|
u32 index = 0;
|
||||||
|
for (auto &frame : m_Frames)
|
||||||
|
{
|
||||||
|
frame = CreateFrame(index++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::Device::~Device()
|
||||||
|
{
|
||||||
|
for (auto &frame : m_Frames)
|
||||||
|
{
|
||||||
|
m_Device.m_Device.destroy(Take(frame.m_FrameAvailableFence), nullptr);
|
||||||
|
m_Device.m_Device.destroy(Take(frame.m_ImageAcquireSem), nullptr);
|
||||||
|
m_Device.m_Device.destroy(Take(frame.m_RenderFinishSem), nullptr);
|
||||||
|
m_Device.m_Device.destroy(Take(frame.m_Pool), nullptr);
|
||||||
|
}
|
||||||
|
m_HashToSamplerIdx.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Frames
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
systems::Frame
|
||||||
|
systems::Device::CreateFrame(u32 frameIndex)
|
||||||
|
{
|
||||||
|
vk::CommandPool pool;
|
||||||
|
vk::Fence fence;
|
||||||
|
vk::Semaphore acquire;
|
||||||
|
vk::Semaphore finished;
|
||||||
|
|
||||||
|
NameString name = "Frame ";
|
||||||
|
name += Cast<char>(frameIndex + '0');
|
||||||
|
|
||||||
|
const vk::CommandPoolCreateInfo commandPoolCreateInfo = {
|
||||||
|
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
||||||
|
.queueFamilyIndex = m_GraphicsQueueFamily,
|
||||||
|
};
|
||||||
|
AbortIfFailedMV(m_Device.m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &pool),
|
||||||
|
"Could not command pool for frame {}", frameIndex);
|
||||||
|
|
||||||
|
constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled};
|
||||||
|
AbortIfFailedMV(m_Device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence),
|
||||||
|
"Could not create a fence for frame {}", frameIndex);
|
||||||
|
|
||||||
|
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {};
|
||||||
|
AbortIfFailedMV(m_Device.m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &acquire),
|
||||||
|
"Could not create IA semaphore for frame {}.", frameIndex);
|
||||||
|
AbortIfFailedMV(m_Device.m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &finished),
|
||||||
|
"Could not create RF semaphore for frame {}.", frameIndex);
|
||||||
|
|
||||||
|
m_Device.SetName(pool, name.c_str());
|
||||||
|
m_Device.SetName(fence, name.c_str());
|
||||||
|
m_Device.SetName(acquire, name.c_str());
|
||||||
|
m_Device.SetName(finished, name.c_str());
|
||||||
|
|
||||||
|
DEBUG("Frame {} created successfully.", frameIndex);
|
||||||
|
|
||||||
|
return Frame{
|
||||||
|
.m_Device = &m_Device,
|
||||||
|
.m_Pool = pool,
|
||||||
|
.m_FrameAvailableFence = fence,
|
||||||
|
.m_ImageAcquireSem = acquire,
|
||||||
|
.m_RenderFinishSem = finished,
|
||||||
|
.m_FrameIdx = frameIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::Frame &
|
||||||
|
systems::Device::GetNextFrame()
|
||||||
|
{
|
||||||
|
Frame ¤tFrame = m_Frames[m_CurrentFrameIdx];
|
||||||
|
u32 frameIndex = m_CurrentFrameIdx;
|
||||||
|
|
||||||
|
m_CurrentFrameIdx = (m_CurrentFrameIdx + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||||
|
|
||||||
|
AbortIfFailedMV(m_Device.m_Device.waitForFences(1, ¤tFrame.m_FrameAvailableFence, true, MaxValue<u64>),
|
||||||
|
"Waiting for fence {} failed.", frameIndex);
|
||||||
|
|
||||||
|
u32 imageIndex = 0;
|
||||||
|
bool imageAcquired = false;
|
||||||
|
while (!imageAcquired)
|
||||||
|
{
|
||||||
|
switch (auto result = m_Device.m_Device.acquireNextImageKHR(
|
||||||
|
m_Swapchain.m_Swapchain, MaxValue<u64>, currentFrame.m_ImageAcquireSem, nullptr, &imageIndex))
|
||||||
|
{
|
||||||
|
case vk::Result::eSuccess:
|
||||||
|
case vk::Result::eSuboptimalKHR: // Suboptimal can still render. Better to let this go for semaphores etc.
|
||||||
|
imageAcquired = true;
|
||||||
|
break; // Image acquired. Break out of loop.
|
||||||
|
case vk::Result::eErrorOutOfDateKHR:
|
||||||
|
DEBUG("Recreating Swapchain. Cause: {}", result);
|
||||||
|
m_Swapchain.Create(m_Surface, m_Window.get().GetSize());
|
||||||
|
break; // Image acquire has failed. We move to the next frame.
|
||||||
|
default:
|
||||||
|
AbortIfFailedMV(result, "Waiting for swapchain image {} failed.", frameIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset fences here. In case swapchain was out of date, we leave the fences signalled.
|
||||||
|
AbortIfFailedMV(m_Device.m_Device.resetFences(1, ¤tFrame.m_FrameAvailableFence), "Fence {} reset failed.",
|
||||||
|
frameIndex);
|
||||||
|
|
||||||
|
AbortIfFailedMV(m_Device.m_Device.resetCommandPool(currentFrame.m_Pool, {}), "Command pool {} reset failed.",
|
||||||
|
frameIndex);
|
||||||
|
|
||||||
|
currentFrame.m_ImageIdx = imageIndex;
|
||||||
|
|
||||||
|
currentFrame.m_SwapchainImage = m_Swapchain.m_Images[imageIndex];
|
||||||
|
currentFrame.m_SwapchainImageView = m_Swapchain.m_ImageViews[imageIndex];
|
||||||
|
|
||||||
|
return currentFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::Device::Receipt
|
||||||
|
systems::Device::Submit(Frame &frame, GraphicsContext &graphicsContext)
|
||||||
|
{
|
||||||
|
// TODO Consider a semaphore pool that you can 'pick' a semaphore from and use for submits.
|
||||||
|
// This can be used as a wait-semaphore for the next submit if you say 'depends on'.
|
||||||
|
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||||
|
vk::SubmitInfo submitInfo = {
|
||||||
|
.waitSemaphoreCount = 1,
|
||||||
|
.pWaitSemaphores = &frame.m_ImageAcquireSem,
|
||||||
|
.pWaitDstStageMask = &waitDstStage,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
.pCommandBuffers = &graphicsContext.m_Cmd,
|
||||||
|
.signalSemaphoreCount = 1,
|
||||||
|
.pSignalSemaphores = &frame.m_RenderFinishSem,
|
||||||
|
};
|
||||||
|
auto result = m_GraphicsQueue.submit(1, &submitInfo, frame.m_FrameAvailableFence);
|
||||||
|
ERROR_IF(Failed(result), "Command queue submit failed. Cause: {}", result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Device::Present(Frame &frame)
|
||||||
|
{
|
||||||
|
vk::PresentInfoKHR presentInfo = {
|
||||||
|
.waitSemaphoreCount = 1,
|
||||||
|
.pWaitSemaphores = &frame.m_RenderFinishSem,
|
||||||
|
.swapchainCount = 1,
|
||||||
|
.pSwapchains = &m_Swapchain.m_Swapchain,
|
||||||
|
.pImageIndices = &frame.m_ImageIdx,
|
||||||
|
.pResults = nullptr,
|
||||||
|
};
|
||||||
|
switch (auto result = m_GraphicsQueue.presentKHR(&presentInfo))
|
||||||
|
{
|
||||||
|
case vk::Result::eSuccess:
|
||||||
|
break;
|
||||||
|
case vk::Result::eErrorOutOfDateKHR:
|
||||||
|
case vk::Result::eSuboptimalKHR:
|
||||||
|
DEBUG("Recreating Swapchain. Cause: {}", result);
|
||||||
|
m_Swapchain.Create(m_Surface, m_Window.get().GetSize());
|
||||||
|
break; // Present failed. We do nothing. Frame is skipped.
|
||||||
|
default:
|
||||||
|
AbortIfFailedM(result, "Swapchain Present failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::GraphicsContext
|
||||||
|
systems::Frame::CreateGraphicsContext()
|
||||||
|
{
|
||||||
|
vk::CommandBuffer cmd;
|
||||||
|
|
||||||
|
const vk::CommandBufferAllocateInfo allocateInfo{
|
||||||
|
.commandPool = m_Pool,
|
||||||
|
.level = vk::CommandBufferLevel::ePrimary,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
};
|
||||||
|
AbortIfFailedMV(m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &cmd), "Command buffer {} alloc failed.",
|
||||||
|
m_FrameIdx);
|
||||||
|
|
||||||
|
return GraphicsContext{cmd};
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Context Impl
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Context::Begin()
|
||||||
|
{
|
||||||
|
vk::CommandBufferBeginInfo commandBufferBeginInfo = {
|
||||||
|
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = m_Cmd.begin(&commandBufferBeginInfo);
|
||||||
|
ERROR_IF(Failed(result), "Could not begin context") THEN_ABORT(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Context::End()
|
||||||
|
{
|
||||||
|
auto result = m_Cmd.end();
|
||||||
|
ERROR_IF(Failed(result), "Could not end context") THEN_ABORT(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::BindPipeline(vk::Pipeline pipeline)
|
||||||
|
{
|
||||||
|
m_Cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::DrawIndexed(u32 indexCount)
|
||||||
|
{
|
||||||
|
m_Cmd.drawIndexed(indexCount, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::Dependency(const vk::DependencyInfo &dependencyInfo)
|
||||||
|
{
|
||||||
|
m_Cmd.pipelineBarrier2(&dependencyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::BeginRendering(const vk::RenderingInfo &renderingInfo)
|
||||||
|
{
|
||||||
|
m_Cmd.beginRendering(&renderingInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::EndRendering()
|
||||||
|
{
|
||||||
|
m_Cmd.endRendering();
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
@ -1,236 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: image_manager.cpp
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "systems/image_manager.h"
|
|
||||||
|
|
||||||
#include "core/device.h"
|
|
||||||
|
|
||||||
using namespace systems;
|
|
||||||
|
|
||||||
vk::ImageCreateInfo ToImageCreateInfo(const Texture2DCreateInfo &createInfo);
|
|
||||||
vk::ImageCreateInfo ToImageCreateInfo(const TextureCubeCreateInfo &createInfo);
|
|
||||||
vk::ImageCreateInfo ToImageCreateInfo(const AttachmentCreateInfo &createInfo);
|
|
||||||
vk::ImageCreateInfo ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo);
|
|
||||||
|
|
||||||
namespace usage_flags
|
|
||||||
{
|
|
||||||
constexpr vk::ImageUsageFlags MIPMAP = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
|
||||||
constexpr vk::ImageUsageFlags SAMPLE = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
|
|
||||||
constexpr vk::ImageUsageFlags STORAGE =
|
|
||||||
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc;
|
|
||||||
constexpr vk::ImageUsageFlags COLOR_ATTACHMENT =
|
|
||||||
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc;
|
|
||||||
constexpr vk::ImageUsageFlags DEPTH_STENCIL_ATTACHMENT = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
|
||||||
} // namespace usage_flags
|
|
||||||
|
|
||||||
ImageManager::ImageManager(const Device *device)
|
|
||||||
: m_Device{device}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<Image>
|
|
||||||
ImageManager::CreateTexture2D(const Texture2DCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = {},
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage rawImage;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
|
||||||
&allocationCreateInfo, &rawImage, &allocation, nullptr));
|
|
||||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::Image image = rawImage;
|
|
||||||
|
|
||||||
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
|
||||||
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
|
||||||
Image::Flags flags = {};
|
|
||||||
if (createInfo.m_IsSampled)
|
|
||||||
flags |= Image::FlagBits::eSampled;
|
|
||||||
if (createInfo.m_IsStorage)
|
|
||||||
flags |= Image::FlagBits::eStorage;
|
|
||||||
|
|
||||||
m_Device->SetName(image, createInfo.m_Name);
|
|
||||||
|
|
||||||
return std::make_shared<Image>(m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format, flags,
|
|
||||||
layerCount, mipLevels);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<ImageCube>
|
|
||||||
ImageManager::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = {},
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage rawImage;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
|
||||||
&allocationCreateInfo, &rawImage, &allocation, nullptr));
|
|
||||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::Image image = rawImage;
|
|
||||||
|
|
||||||
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
|
||||||
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
|
||||||
Image::Flags flags = Image::FlagBits::eCube;
|
|
||||||
if (createInfo.m_IsSampled)
|
|
||||||
flags |= Image::FlagBits::eSampled;
|
|
||||||
if (createInfo.m_IsStorage)
|
|
||||||
flags |= Image::FlagBits::eStorage;
|
|
||||||
|
|
||||||
m_Device->SetName(image, createInfo.m_Name);
|
|
||||||
|
|
||||||
return CastImage<ImageCube>(std::make_shared<Image>(m_Device, image, allocation, imageCreateInfo.extent,
|
|
||||||
imageCreateInfo.format, flags, layerCount, mipLevels));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<Image>
|
|
||||||
ImageManager::CreateAttachment(const AttachmentCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage rawImage;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
|
||||||
&allocationCreateInfo, &rawImage, &allocation, nullptr));
|
|
||||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::Image image = rawImage;
|
|
||||||
|
|
||||||
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
|
||||||
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
|
||||||
|
|
||||||
m_Device->SetName(image, createInfo.m_Name);
|
|
||||||
|
|
||||||
return std::make_shared<Image>(m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format,
|
|
||||||
Image::Flags{}, layerCount, mipLevels);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<Image>
|
|
||||||
ImageManager::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage rawImage;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
|
||||||
&allocationCreateInfo, &rawImage, &allocation, nullptr));
|
|
||||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::Image image = rawImage;
|
|
||||||
|
|
||||||
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
|
||||||
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
|
||||||
|
|
||||||
m_Device->SetName(image, createInfo.m_Name);
|
|
||||||
|
|
||||||
return std::make_shared<Image>(m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format,
|
|
||||||
Image::Flags{}, layerCount, mipLevels);
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo
|
|
||||||
ToImageCreateInfo(const Texture2DCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto &[format, extent, name, isSampled, isMipMapped, isStorage] = createInfo;
|
|
||||||
|
|
||||||
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::ImageUsageFlags{};
|
|
||||||
if (isSampled)
|
|
||||||
usage |= usage_flags::SAMPLE;
|
|
||||||
if (isMipMapped)
|
|
||||||
usage |= usage_flags::MIPMAP;
|
|
||||||
if (isStorage)
|
|
||||||
usage |= usage_flags::STORAGE;
|
|
||||||
|
|
||||||
return {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = mipLevels,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.usage = usage,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo
|
|
||||||
ToImageCreateInfo(const TextureCubeCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto &[format, side, name, isSampled, isMipMapped, isStorage] = createInfo;
|
|
||||||
|
|
||||||
WARN_IF(!IsPowerOfTwo(side), "ImageCube {1} is {0}x{0} (Non Power of Two)", side, name ? name : "<unnamed>");
|
|
||||||
|
|
||||||
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(side))) : 1;
|
|
||||||
|
|
||||||
auto usage = vk::ImageUsageFlags{};
|
|
||||||
if (isSampled)
|
|
||||||
usage |= usage_flags::SAMPLE;
|
|
||||||
if (isMipMapped)
|
|
||||||
usage |= usage_flags::MIPMAP;
|
|
||||||
if (isStorage)
|
|
||||||
usage |= usage_flags::STORAGE;
|
|
||||||
|
|
||||||
return {
|
|
||||||
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = {side, side, 1},
|
|
||||||
.mipLevels = mipLevels,
|
|
||||||
.arrayLayers = 6,
|
|
||||||
.usage = usage,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo
|
|
||||||
ToImageCreateInfo(const AttachmentCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto &[format, extent, name] = createInfo;
|
|
||||||
|
|
||||||
constexpr auto usage = usage_flags::COLOR_ATTACHMENT;
|
|
||||||
|
|
||||||
return {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = 1,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.usage = usage,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo
|
|
||||||
ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto &[extent, name] = createInfo;
|
|
||||||
|
|
||||||
constexpr auto format = vk::Format::eD24UnormS8Uint;
|
|
||||||
constexpr auto usage = usage_flags::DEPTH_STENCIL_ATTACHMENT;
|
|
||||||
|
|
||||||
return {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = 1,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.usage = usage,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: sampler_manager.cpp
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "systems/sampler_manager.h"
|
|
||||||
|
|
||||||
#include "core/device.h"
|
|
||||||
|
|
||||||
using namespace systems;
|
|
||||||
|
|
||||||
SamplerManager::SamplerManager(const Device *device)
|
|
||||||
: m_Device{device}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SamplerManager::~SamplerManager()
|
|
||||||
{
|
|
||||||
m_HashToSamplerIdx.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<Sampler>
|
|
||||||
SamplerManager::CreateSampler(const SamplerCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto vkCreateInfo = Cast<vk::SamplerCreateInfo>(createInfo);
|
|
||||||
|
|
||||||
if (const auto iter = m_HashToSamplerIdx.find(vkCreateInfo); iter != m_HashToSamplerIdx.end() && !iter->second.expired())
|
|
||||||
{
|
|
||||||
return iter->second.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto object = std::make_shared<Sampler>(m_Device, vkCreateInfo, createInfo.m_Name ? createInfo.m_Name : nullptr);
|
|
||||||
m_HashToSamplerIdx.emplace(vkCreateInfo, object);
|
|
||||||
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: view_manager.cpp
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "systems/view_manager.h"
|
|
||||||
|
|
||||||
#include "core/device.h"
|
|
||||||
|
|
||||||
using namespace systems;
|
|
||||||
|
|
||||||
ViewManager::ViewManager(const Device *device)
|
|
||||||
: m_Device{device}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<ImageView>
|
|
||||||
ViewManager::CreateView(const ViewCreateInfo<Image> &createInfo) const
|
|
||||||
{
|
|
||||||
const auto layerCount = createInfo.GetLayerCount();
|
|
||||||
const auto mipCount = createInfo.GetMipLevelCount();
|
|
||||||
ERROR_IF((createInfo.m_BaseLayer + layerCount) > createInfo.m_Image->m_LayerCount, "Invalid Layer Access")
|
|
||||||
THEN_ABORT(-1);
|
|
||||||
ERROR_IF((createInfo.m_BaseMipLevel + mipCount) > createInfo.m_Image->m_MipLevels, "Invalid Mip Level Access")
|
|
||||||
THEN_ABORT(-1);
|
|
||||||
|
|
||||||
vk::ImageView view;
|
|
||||||
const auto imageViewCreateInfo = Cast<vk::ImageViewCreateInfo>(createInfo);
|
|
||||||
auto result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
m_Device->SetName(view, createInfo.m_Name);
|
|
||||||
|
|
||||||
return std::make_shared<ImageView>(createInfo.m_Image, view, createInfo.m_Image->m_Extent, createInfo.m_BaseLayer,
|
|
||||||
layerCount, createInfo.m_BaseMipLevel, mipCount);
|
|
||||||
}
|
|
||||||
|
|
@ -8,18 +8,16 @@
|
||||||
#include "aster/core/buffer.h"
|
#include "aster/core/buffer.h"
|
||||||
#include "aster/core/constants.h"
|
#include "aster/core/constants.h"
|
||||||
#include "aster/core/instance.h"
|
#include "aster/core/instance.h"
|
||||||
#include "aster/core/device.h"
|
|
||||||
#include "aster/core/physical_device.h"
|
#include "aster/core/physical_device.h"
|
||||||
#include "aster/core/pipeline.h"
|
#include "aster/core/pipeline.h"
|
||||||
#include "aster/core/swapchain.h"
|
#include "aster/core/swapchain.h"
|
||||||
#include "aster/core/window.h"
|
#include "aster/core/window.h"
|
||||||
|
|
||||||
|
#include "aster/systems/device.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "aster/systems/resource_manager.h"
|
|
||||||
|
|
||||||
#include <EASTL/array.h>
|
#include <EASTL/array.h>
|
||||||
|
|
||||||
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
|
|
||||||
constexpr auto VERTEX_SHADER_FILE = "shader/triangle.vert.glsl.spv";
|
constexpr auto VERTEX_SHADER_FILE = "shader/triangle.vert.glsl.spv";
|
||||||
constexpr auto FRAGMENT_SHADER_FILE = "shader/triangle.frag.glsl.spv";
|
constexpr auto FRAGMENT_SHADER_FILE = "shader/triangle.frag.glsl.spv";
|
||||||
|
|
||||||
|
|
@ -57,61 +55,21 @@ struct Vertex
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Frame
|
|
||||||
{
|
|
||||||
const Device *m_Device;
|
|
||||||
vk::CommandPool m_Pool;
|
|
||||||
vk::CommandBuffer m_CommandBuffer;
|
|
||||||
vk::Fence m_FrameAvailableFence;
|
|
||||||
vk::Semaphore m_ImageAcquireSem;
|
|
||||||
vk::Semaphore m_RenderFinishSem;
|
|
||||||
|
|
||||||
Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount);
|
|
||||||
~Frame();
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int, char **)
|
main(int, char **)
|
||||||
{
|
{
|
||||||
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
||||||
|
|
||||||
Window window = {"Triangle (Aster)", {640, 480}};
|
Window window = {"Triangle (Aster)", {640, 480}};
|
||||||
Instance context = {"Triangle", VERSION};
|
systems::Device device{{
|
||||||
Surface surface = {&context, &window, "Primary"};
|
.m_AppName = "Triangle",
|
||||||
|
.m_Window = window,
|
||||||
|
.m_Name = "Primary",
|
||||||
|
.m_Features = {.m_Vulkan12Features = {.bufferDeviceAddress = true},
|
||||||
|
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true}},
|
||||||
|
}};
|
||||||
|
|
||||||
PhysicalDevices physicalDevices = {&surface, &context};
|
Pipeline pipeline = CreatePipeline(&device.m_Device, &device.m_Swapchain);
|
||||||
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
|
|
||||||
|
|
||||||
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
|
|
||||||
|
|
||||||
Features enabledDeviceFeatures = {
|
|
||||||
.m_Vulkan12Features = {.bufferDeviceAddress = true},
|
|
||||||
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
|
|
||||||
};
|
|
||||||
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
|
|
||||||
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
|
|
||||||
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
|
|
||||||
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
|
|
||||||
Pipeline pipeline = CreatePipeline(&device, &swapchain);
|
|
||||||
systems::ResourceManager resourceManager{&device};
|
|
||||||
|
|
||||||
vk::CommandPool copyPool;
|
|
||||||
vk::CommandBuffer copyBuffer;
|
|
||||||
{
|
|
||||||
vk::CommandPoolCreateInfo poolCreateInfo = {
|
|
||||||
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
|
||||||
.queueFamilyIndex = queueAllocation.m_Family,
|
|
||||||
};
|
|
||||||
auto result = device.m_Device.createCommandPool(&poolCreateInfo, nullptr, ©Pool);
|
|
||||||
ERROR_IF(Failed(result), "Copy command pool creation failed. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
vk::CommandBufferAllocateInfo bufferAllocateInfo = {
|
|
||||||
.commandPool = copyPool,
|
|
||||||
.level = vk::CommandBufferLevel::ePrimary,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
};
|
|
||||||
result = device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, ©Buffer);
|
|
||||||
ERROR_IF(Failed(result), "Copy command buffer allocation failed. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eastl::array<Vertex, 3> vertices{};
|
// eastl::array<Vertex, 3> vertices{};
|
||||||
eastl::array vertices = {
|
eastl::array vertices = {
|
||||||
|
|
@ -119,22 +77,24 @@ main(int, char **)
|
||||||
Vertex{.m_Position = {0.5f, -0.5f, 0.0f}, .m_Color = {0.0f, 1.0f, 0.0f}},
|
Vertex{.m_Position = {0.5f, -0.5f, 0.0f}, .m_Color = {0.0f, 1.0f, 0.0f}},
|
||||||
Vertex{.m_Position = {0.0f, 0.5f, 0.0f}, .m_Color = {0.0f, 0.0f, 1.0f}},
|
Vertex{.m_Position = {0.0f, 0.5f, 0.0f}, .m_Color = {0.0f, 0.0f, 1.0f}},
|
||||||
};
|
};
|
||||||
auto vbo = resourceManager.Buffers().CreateVertexBuffer(vertices.size() * sizeof vertices[0], "VBO");
|
auto vbo = device.CreateVertexBuffer(vertices.size() * sizeof vertices[0], "VBO");
|
||||||
vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data());
|
vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data());
|
||||||
|
|
||||||
|
Size2D swapchainSize = device.GetSwapchainSize();
|
||||||
|
|
||||||
// Persistent variables
|
// Persistent variables
|
||||||
vk::Viewport viewport = {
|
vk::Viewport viewport = {
|
||||||
.x = 0,
|
.x = 0,
|
||||||
.y = Cast<f32>(swapchain.m_Extent.height),
|
.y = Cast<f32>(swapchainSize.m_Height),
|
||||||
.width = Cast<f32>(swapchain.m_Extent.width),
|
.width = Cast<f32>(swapchainSize.m_Width),
|
||||||
.height = -Cast<f32>(swapchain.m_Extent.height),
|
.height = -Cast<f32>(swapchainSize.m_Height),
|
||||||
.minDepth = 0.0,
|
.minDepth = 0.0,
|
||||||
.maxDepth = 1.0,
|
.maxDepth = 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::Rect2D scissor = {
|
vk::Rect2D scissor = {
|
||||||
.offset = {0, 0},
|
.offset = {0, 0},
|
||||||
.extent = swapchain.m_Extent,
|
.extent = Cast<vk::Extent2D>(swapchainSize),
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::ImageSubresourceRange subresourceRange = {
|
vk::ImageSubresourceRange subresourceRange = {
|
||||||
|
|
@ -151,8 +111,8 @@ main(int, char **)
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||||
.oldLayout = vk::ImageLayout::eUndefined,
|
.oldLayout = vk::ImageLayout::eUndefined,
|
||||||
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.subresourceRange = subresourceRange,
|
.subresourceRange = subresourceRange,
|
||||||
};
|
};
|
||||||
vk::DependencyInfo topOfThePipeDependency = {
|
vk::DependencyInfo topOfThePipeDependency = {
|
||||||
|
|
@ -166,8 +126,8 @@ main(int, char **)
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eNone,
|
.dstAccessMask = vk::AccessFlagBits2::eNone,
|
||||||
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
.newLayout = vk::ImageLayout::ePresentSrcKHR,
|
.newLayout = vk::ImageLayout::ePresentSrcKHR,
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.subresourceRange = subresourceRange,
|
.subresourceRange = subresourceRange,
|
||||||
};
|
};
|
||||||
vk::DependencyInfo renderToPresentDependency = {
|
vk::DependencyInfo renderToPresentDependency = {
|
||||||
|
|
@ -175,70 +135,34 @@ main(int, char **)
|
||||||
.pImageMemoryBarriers = &renderToPresentBarrier,
|
.pImageMemoryBarriers = &renderToPresentBarrier,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Frames
|
|
||||||
eastl::fixed_vector<Frame, MAX_FRAMES_IN_FLIGHT> frames;
|
|
||||||
for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
|
|
||||||
{
|
|
||||||
frames.emplace_back(&device, queueAllocation.m_Family, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO("Starting loop");
|
INFO("Starting loop");
|
||||||
u32 frameIndex = 0;
|
u32 frameIndex = 0;
|
||||||
while (window.Poll())
|
while (window.Poll())
|
||||||
{
|
{
|
||||||
Frame *currentFrame = &frames[frameIndex];
|
systems::Frame ¤tFrame = device.GetNextFrame();
|
||||||
|
|
||||||
auto result = device.m_Device.waitForFences(1, ¤tFrame->m_FrameAvailableFence, true, MaxValue<u64>);
|
vk::CommandBuffer cmd;
|
||||||
ERROR_IF(Failed(result), "Waiting for fence {} failed. Cause: {}", frameIndex, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
u32 imageIndex;
|
const vk::CommandBufferAllocateInfo allocateInfo{
|
||||||
result = device.m_Device.acquireNextImageKHR(swapchain.m_Swapchain, MaxValue<u64>,
|
.commandPool = currentFrame.m_Pool,
|
||||||
currentFrame->m_ImageAcquireSem, nullptr, &imageIndex);
|
.level = vk::CommandBufferLevel::ePrimary,
|
||||||
if (Failed(result))
|
.commandBufferCount = 1,
|
||||||
{
|
};
|
||||||
switch (result)
|
AbortIfFailedMV(device.m_Device.m_Device.allocateCommandBuffers(&allocateInfo, &cmd),
|
||||||
{
|
"Command buffer {} alloc failed.", currentFrame.m_FrameIdx);
|
||||||
case vk::Result::eErrorOutOfDateKHR:
|
|
||||||
case vk::Result::eSuboptimalKHR:
|
|
||||||
INFO("Recreating Swapchain. Cause: {}", result);
|
|
||||||
swapchain.Create(&surface, window.GetSize());
|
|
||||||
viewport.y = Cast<f32>(swapchain.m_Extent.height);
|
|
||||||
viewport.width = Cast<f32>(swapchain.m_Extent.width);
|
|
||||||
viewport.height = -Cast<f32>(swapchain.m_Extent.height);
|
|
||||||
scissor.extent = swapchain.m_Extent;
|
|
||||||
continue; // Image acquire has failed. We move to the next frame.
|
|
||||||
default:
|
|
||||||
ERROR("Waiting for swapchain image {} failed. Cause: {}", frameIndex, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Reset fences here. In case swapchain was out of date, we leave the fences signalled.
|
|
||||||
result = device.m_Device.resetFences(1, ¤tFrame->m_FrameAvailableFence);
|
|
||||||
ERROR_IF(Failed(result), "Fence {} reset failed. Cause: {}", frameIndex, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
result = device.m_Device.resetCommandPool(currentFrame->m_Pool, {});
|
auto context = currentFrame.CreateGraphicsContext();
|
||||||
ERROR_IF(Failed(result), "Command pool {} reset failed. Cause: {}", frameIndex, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
|
topOfThePipeBarrier.image = currentFrame.m_SwapchainImage;
|
||||||
vk::Image currentImage = swapchain.m_Images[imageIndex];
|
renderToPresentBarrier.image = currentFrame.m_SwapchainImage;
|
||||||
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
|
|
||||||
|
|
||||||
topOfThePipeBarrier.image = currentImage;
|
context.Begin();
|
||||||
renderToPresentBarrier.image = currentImage;
|
|
||||||
|
|
||||||
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
context.Dependency(topOfThePipeDependency);
|
||||||
result = cmd.begin(&beginInfo);
|
|
||||||
ERROR_IF(Failed(result), "Command buffer begin failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
cmd.pipelineBarrier2(&topOfThePipeDependency);
|
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
vk::RenderingAttachmentInfo attachmentInfo = {
|
vk::RenderingAttachmentInfo attachmentInfo = {
|
||||||
.imageView = currentImageView,
|
.imageView = currentFrame.m_SwapchainImageView,
|
||||||
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
.resolveMode = vk::ResolveModeFlagBits::eNone,
|
.resolveMode = vk::ResolveModeFlagBits::eNone,
|
||||||
.loadOp = vk::AttachmentLoadOp::eClear,
|
.loadOp = vk::AttachmentLoadOp::eClear,
|
||||||
|
|
@ -247,115 +171,37 @@ main(int, char **)
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::RenderingInfo renderingInfo = {
|
vk::RenderingInfo renderingInfo = {
|
||||||
.renderArea = {.extent = swapchain.m_Extent},
|
.renderArea = {.extent = scissor.extent},
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
.colorAttachmentCount = 1,
|
.colorAttachmentCount = 1,
|
||||||
.pColorAttachments = &attachmentInfo,
|
.pColorAttachments = &attachmentInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
cmd.beginRendering(&renderingInfo);
|
context.BeginRendering(renderingInfo);
|
||||||
|
|
||||||
cmd.setViewport(0, 1, &viewport);
|
// cmd.setViewport(0, 1, &viewport);
|
||||||
cmd.setScissor(0, 1, &scissor);
|
// cmd.setScissor(0, 1, &scissor);
|
||||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
|
// cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
|
||||||
usize offsets = 0;
|
// usize offsets = 0;
|
||||||
cmd.bindVertexBuffers(0, 1, &vbo->m_Buffer, &offsets);
|
// cmd.bindVertexBuffers(0, 1, &vbo->m_Buffer, &offsets);
|
||||||
cmd.draw(3, 1, 0, 0);
|
// cmd.draw(3, 1, 0, 0);
|
||||||
|
|
||||||
cmd.endRendering();
|
context.EndRendering();
|
||||||
|
|
||||||
cmd.pipelineBarrier2(&renderToPresentDependency);
|
context.Dependency(renderToPresentDependency);
|
||||||
|
|
||||||
result = cmd.end();
|
context.End();
|
||||||
ERROR_IF(Failed(result), "Command buffer end failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
device.Submit(currentFrame, context);
|
||||||
vk::SubmitInfo submitInfo = {
|
|
||||||
.waitSemaphoreCount = 1,
|
|
||||||
.pWaitSemaphores = ¤tFrame->m_ImageAcquireSem,
|
|
||||||
.pWaitDstStageMask = &waitDstStage,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &cmd,
|
|
||||||
.signalSemaphoreCount = 1,
|
|
||||||
.pSignalSemaphores = ¤tFrame->m_RenderFinishSem,
|
|
||||||
};
|
|
||||||
result = commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence);
|
|
||||||
ERROR_IF(Failed(result), "Command queue submit failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::PresentInfoKHR presentInfo = {
|
device.Present(currentFrame);
|
||||||
.waitSemaphoreCount = 1,
|
|
||||||
.pWaitSemaphores = ¤tFrame->m_RenderFinishSem,
|
|
||||||
.swapchainCount = 1,
|
|
||||||
.pSwapchains = &swapchain.m_Swapchain,
|
|
||||||
.pImageIndices = &imageIndex,
|
|
||||||
.pResults = nullptr,
|
|
||||||
};
|
|
||||||
result = commandQueue.presentKHR(&presentInfo);
|
|
||||||
if (Failed(result))
|
|
||||||
{
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case vk::Result::eErrorOutOfDateKHR:
|
|
||||||
case vk::Result::eSuboptimalKHR:
|
|
||||||
INFO("Recreating Swapchain. Cause: {}", result);
|
|
||||||
swapchain.Create(&surface, window.GetSize());
|
|
||||||
viewport.y = Cast<f32>(swapchain.m_Extent.height);
|
|
||||||
viewport.width = Cast<f32>(swapchain.m_Extent.width);
|
|
||||||
viewport.height = -Cast<f32>(swapchain.m_Extent.height);
|
|
||||||
scissor.extent = swapchain.m_Extent;
|
|
||||||
break; // Present failed. We redo the frame.
|
|
||||||
default:
|
|
||||||
ERROR("Command queue present failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
device.WaitIdle();
|
device.WaitIdle();
|
||||||
|
|
||||||
device.m_Device.destroy(copyPool, nullptr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Frame::Frame(const Device *device, const u32 queueFamilyIndex, const u32 frameCount)
|
|
||||||
{
|
|
||||||
m_Device = device;
|
|
||||||
|
|
||||||
const vk::CommandPoolCreateInfo commandPoolCreateInfo = {
|
|
||||||
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
|
||||||
.queueFamilyIndex = queueFamilyIndex,
|
|
||||||
};
|
|
||||||
vk::Result result = device->m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &m_Pool);
|
|
||||||
ERROR_IF(Failed(result), "Could not command pool for frame {}. Cause: {}", frameCount, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled};
|
|
||||||
result = device->m_Device.createFence(&fenceCreateInfo, nullptr, &m_FrameAvailableFence);
|
|
||||||
ERROR_IF(Failed(result), "Could not create a fence for frame {}. Cause: {}", frameCount, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {};
|
|
||||||
result = device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_ImageAcquireSem);
|
|
||||||
ERROR_IF(Failed(result), "Could not create IA semaphore for frame {}. Cause: {}", frameCount, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
result = device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_RenderFinishSem);
|
|
||||||
ERROR_IF(Failed(result), "Could not create RF semaphore for frame {}. Cause: {}", frameCount, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
const vk::CommandBufferAllocateInfo allocateInfo = {
|
|
||||||
.commandPool = m_Pool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1};
|
|
||||||
|
|
||||||
result = m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &m_CommandBuffer);
|
|
||||||
ERROR_IF(Failed(result), "Command buffer allocation failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
DEBUG("Frame {} created successfully.", frameCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pipeline
|
Pipeline
|
||||||
CreatePipeline(const Device *device, const Swapchain *swapchain)
|
CreatePipeline(const Device *device, const Swapchain *swapchain)
|
||||||
{
|
{
|
||||||
|
|
@ -498,13 +344,3 @@ CreateShader(const Device *device, cstr shaderFile)
|
||||||
THEN_ABORT(result);
|
THEN_ABORT(result);
|
||||||
return shaderModule;
|
return shaderModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
Frame::~Frame()
|
|
||||||
{
|
|
||||||
m_Device->m_Device.destroy(m_RenderFinishSem, nullptr);
|
|
||||||
m_Device->m_Device.destroy(m_ImageAcquireSem, nullptr);
|
|
||||||
m_Device->m_Device.destroy(m_FrameAvailableFence, nullptr);
|
|
||||||
m_Device->m_Device.destroy(m_Pool, nullptr);
|
|
||||||
|
|
||||||
DEBUG("Destoryed Frame");
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue