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;
|
||||
|
||||
// Ctor/Dtor
|
||||
Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
|
||||
Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
||||
const eastl::vector<QueueAllocation> &queueAllocations, eastl::span<u8> &&pipelineCacheData,
|
||||
Device() = default;
|
||||
Device(const Instance &context, PhysicalDevice &physicalDevice, Features &enabledFeatures,
|
||||
const eastl::span<QueueAllocation> &queueAllocations, const eastl::span<u8> &pipelineCacheData,
|
||||
NameString &&name);
|
||||
~Device();
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,29 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
|
|||
#define TODO(MSG) 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
|
||||
Failed(const vk::Result result)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ struct Instance final
|
|||
vk::DebugUtilsMessengerEXT m_DebugMessenger = nullptr;
|
||||
|
||||
// Ctor/Dtor
|
||||
Instance() = default;
|
||||
Instance(cstr appName, Version version, bool enableValidation = ENABLE_LAYER_MESSAGES_DEFAULT_VALUE);
|
||||
~Instance();
|
||||
|
||||
|
|
|
|||
|
|
@ -54,5 +54,5 @@ struct PhysicalDevice final
|
|||
class PhysicalDevices : public eastl::fixed_vector<PhysicalDevice, 4>
|
||||
{
|
||||
public:
|
||||
PhysicalDevices(const Surface *surface, const Instance *context);
|
||||
PhysicalDevices(const Surface &surface, const Instance &context);
|
||||
};
|
||||
|
|
@ -17,7 +17,8 @@ struct Surface
|
|||
NameString m_Name;
|
||||
|
||||
// Ctor Dtor
|
||||
Surface(Instance *context, const Window *window, cstr name);
|
||||
Surface() = default;
|
||||
Surface(Instance &context, const Window &window);
|
||||
~Surface();
|
||||
|
||||
// Move
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ struct Swapchain final
|
|||
|
||||
const Device *m_Device;
|
||||
vk::SwapchainKHR m_Swapchain;
|
||||
NameString m_Name;
|
||||
vk::Extent2D m_Extent;
|
||||
vk::Format m_Format;
|
||||
eastl::fixed_vector<vk::Image, 4> m_Images;
|
||||
|
|
@ -29,11 +28,12 @@ struct Swapchain final
|
|||
|
||||
eastl::vector<FnResizeCallback> m_ResizeCallbacks;
|
||||
|
||||
void Create(const Surface *window, Size2D size);
|
||||
void Create(const Surface &surface, Size2D size);
|
||||
void RegisterResizeCallback(FnResizeCallback &&callback);
|
||||
|
||||
// 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();
|
||||
|
||||
// Move
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ struct Window final
|
|||
|
||||
static std::atomic_uint64_t m_WindowCount;
|
||||
static std::atomic_bool m_IsGlfwInit;
|
||||
static void SetupLibrary();
|
||||
static cstr *GetInstanceExtensions(u32 *extensionCount);
|
||||
|
||||
// Methods
|
||||
[[nodiscard]] bool
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ cmake_minimum_required(VERSION 3.13)
|
|||
|
||||
target_sources(aster_core
|
||||
INTERFACE
|
||||
"buffer_manager.h"
|
||||
"image_manager.h"
|
||||
"view_manager.h"
|
||||
"sampler_manager.h"
|
||||
"device.h"
|
||||
"resource.h"
|
||||
"resource_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 "buffer_manager.h"
|
||||
#include "sampler_manager.h"
|
||||
#include "view_manager.h"
|
||||
|
||||
#include "aster/util/freelist.h"
|
||||
|
||||
#include "EASTL/deque.h"
|
||||
#include "EASTL/intrusive_hash_map.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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
#include "aster/core/buffer.h"
|
||||
#include "aster/core/image.h"
|
||||
#include "aster/core/image_view.h"
|
||||
|
||||
#include <EASTL/intrusive_ptr.h>
|
||||
|
||||
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.
|
||||
* @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,
|
||||
};
|
||||
|
||||
Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name)
|
||||
: 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,
|
||||
Device::Device(const Instance &context, PhysicalDevice &physicalDevice, Features &enabledFeatures,
|
||||
const eastl::span<QueueAllocation> &queueAllocations, const eastl::span<u8> &pipelineCacheData,
|
||||
NameString &&name)
|
||||
: m_Name(std::move(name))
|
||||
, m_PhysicalDevice(physicalDevice->m_PhysicalDevice)
|
||||
, m_ValidationEnabled(context->m_DebugMessenger != nullptr)
|
||||
, m_PhysicalDevice(physicalDevice.m_PhysicalDevice)
|
||||
, m_ValidationEnabled(context.m_DebugMessenger != nullptr)
|
||||
{
|
||||
// Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap
|
||||
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::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures->m_Vulkan11Features;
|
||||
vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures->m_Vulkan12Features;
|
||||
vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures->m_Vulkan13Features;
|
||||
vk::PhysicalDeviceFeatures *deviceFeatures = &enabledFeatures.m_Vulkan10Features;
|
||||
vk::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures.m_Vulkan11Features;
|
||||
vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures.m_Vulkan12Features;
|
||||
vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures.m_Vulkan13Features;
|
||||
|
||||
vulkan11Features->pNext = vulkan12Features;
|
||||
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);
|
||||
ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", 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());
|
||||
|
||||
|
|
@ -85,7 +79,7 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
|
|||
.physicalDevice = m_PhysicalDevice,
|
||||
.device = m_Device,
|
||||
.pVulkanFunctions = &vmaVulkanFunctions,
|
||||
.instance = context->m_Instance,
|
||||
.instance = context.m_Instance,
|
||||
.vulkanApiVersion = ASTER_API_VERSION,
|
||||
};
|
||||
|
||||
|
|
@ -110,6 +104,9 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
|
|||
|
||||
Device::~Device()
|
||||
{
|
||||
if (!m_Device)
|
||||
return;
|
||||
|
||||
m_Device.destroy(m_PipelineCache, nullptr);
|
||||
if (m_Allocator)
|
||||
{
|
||||
|
|
@ -156,6 +153,7 @@ Device::Device(Device &&other) noexcept
|
|||
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
|
||||
, m_Device(Take(other.m_Device))
|
||||
, 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_Device = Take(other.m_Device);
|
||||
m_Allocator = Take(other.m_Allocator);
|
||||
m_PipelineCache = Take(other.m_PipelineCache);
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include "core/instance.h"
|
||||
|
||||
#include "core/window.h"
|
||||
|
||||
#include <EASTL/array.h>
|
||||
#include <EASTL/fixed_vector.h>
|
||||
|
||||
|
|
@ -61,9 +63,9 @@ Instance::Instance(const cstr appName, const Version version, bool enableValidat
|
|||
.pUserData = nullptr,
|
||||
};
|
||||
|
||||
u32 glfwExtensionCount = 0;
|
||||
cstr *glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
||||
eastl::fixed_vector<cstr, 3> instanceExtensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
|
||||
u32 windowExtensionCount = 0;
|
||||
cstr *windowExtensions = Window::GetInstanceExtensions(&windowExtensionCount);
|
||||
eastl::fixed_vector<cstr, 3> instanceExtensions(windowExtensions, windowExtensions + windowExtensionCount);
|
||||
if (enableValidation)
|
||||
{
|
||||
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()
|
||||
{
|
||||
if (!m_Instance)
|
||||
return;
|
||||
|
||||
if (m_DebugMessenger)
|
||||
{
|
||||
m_Instance.destroy(m_DebugMessenger, nullptr);
|
||||
|
|
|
|||
|
|
@ -154,11 +154,10 @@ EnumeratePhysicalDevices(const vk::Instance instance)
|
|||
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 physicalDevice : physicalDevices)
|
||||
for (auto physicalDevices = EnumeratePhysicalDevices(context.m_Instance); 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/window.h"
|
||||
|
||||
Surface::Surface(Instance *context, const Window *window, cstr name)
|
||||
: m_Context(context)
|
||||
, m_Name(name)
|
||||
Surface::Surface(Instance &context, const Window &window)
|
||||
: m_Context(&context)
|
||||
{
|
||||
VkSurfaceKHR surface;
|
||||
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)
|
||||
THEN_ABORT(result)
|
||||
ELSE_DEBUG("Surface {} Created", m_Name);
|
||||
|
|
@ -23,14 +22,14 @@ Surface::Surface(Instance *context, const Window *window, cstr name)
|
|||
|
||||
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 = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Surface::Surface(Surface &&other) noexcept
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@
|
|||
|
||||
[[nodiscard]] vk::Extent2D GetExtent(Size2D size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities);
|
||||
|
||||
Swapchain::Swapchain(const Surface *surface, const Device *device, Size2D size, NameString &&name)
|
||||
: m_Device(device)
|
||||
, m_Name(std::move(name))
|
||||
Swapchain::Swapchain(const Surface &surface, const Device &device, Size2D size)
|
||||
: m_Device(&device)
|
||||
, m_Format(vk::Format::eUndefined)
|
||||
{
|
||||
this->Create(surface, size);
|
||||
|
|
@ -27,11 +26,11 @@ Swapchain::~Swapchain()
|
|||
Swapchain::Swapchain(Swapchain &&other) noexcept
|
||||
: m_Device(other.m_Device)
|
||||
, m_Swapchain(Take(other.m_Swapchain))
|
||||
, m_Name(std::move(other.m_Name))
|
||||
, m_Extent(other.m_Extent)
|
||||
, m_Format(other.m_Format)
|
||||
, m_Images(std::move(other.m_Images))
|
||||
, 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;
|
||||
m_Device = other.m_Device;
|
||||
m_Swapchain = Take(other.m_Swapchain);
|
||||
m_Name = std::move(other.m_Name);
|
||||
m_Extent = other.m_Extent;
|
||||
m_Format = other.m_Format;
|
||||
m_Images = std::move(other.m_Images);
|
||||
m_ImageViews = std::move(other.m_ImageViews);
|
||||
m_ResizeCallbacks = std::move(other.m_ResizeCallbacks);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
while (m_Extent.width == 0 || m_Extent.height == 0)
|
||||
{
|
||||
glfwWaitEvents();
|
||||
surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface->m_Surface);
|
||||
surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface.m_Surface);
|
||||
m_Extent = GetExtent(size, &surfaceCapabilities);
|
||||
}
|
||||
|
||||
auto surfaceFormats = GetSurfaceFormats(m_Device->m_PhysicalDevice, surface->m_Surface);
|
||||
auto presentModes = GetSurfacePresentModes(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);
|
||||
|
||||
m_Format = vk::Format::eUndefined;
|
||||
auto swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
|
||||
|
|
@ -95,16 +94,14 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
|||
}
|
||||
|
||||
u32 swapchainImageCount = 3;
|
||||
if (surfaceCapabilities.maxImageCount > 0)
|
||||
{
|
||||
swapchainImageCount =
|
||||
glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount);
|
||||
}
|
||||
u32 maxImageCount =
|
||||
glm::max(swapchainImageCount, glm::max(surfaceCapabilities.maxImageCount, surfaceCapabilities.minImageCount));
|
||||
swapchainImageCount = glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, maxImageCount);
|
||||
|
||||
// TODO: Note that different queues might need the images to be shared.
|
||||
|
||||
const vk::SwapchainCreateInfoKHR swapchainCreateInfo = {
|
||||
.surface = surface->m_Surface,
|
||||
.surface = surface.m_Surface,
|
||||
.minImageCount = swapchainImageCount,
|
||||
.imageFormat = m_Format,
|
||||
.imageColorSpace = swapchainColorSpace,
|
||||
|
|
@ -120,28 +117,30 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
|||
};
|
||||
|
||||
vk::Device device = m_Device->m_Device;
|
||||
NameString name = "Swapchain of ";
|
||||
name += m_Device->m_Name;
|
||||
|
||||
vk::SwapchainKHR 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)
|
||||
ELSE_DEBUG("Created Swapchain '{}'", m_Name);
|
||||
ELSE_DEBUG("Created '{}'", name);
|
||||
|
||||
// Irrelevant on the first run. Required for re-creation.
|
||||
Cleanup();
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
// Managed by the Swapchain.
|
||||
m_Images.resize(swapchainImageCount);
|
||||
m_Images.resize(swapchainImageCount, nullptr);
|
||||
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);
|
||||
|
||||
vk::ImageViewCreateInfo viewCreateInfo = {
|
||||
|
|
@ -165,7 +164,7 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
|||
|
||||
vk::ImageView 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);
|
||||
|
||||
m_ImageViews.push_back(imageView);
|
||||
|
|
@ -173,7 +172,7 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
|||
++index;
|
||||
}
|
||||
|
||||
DEBUG("Swapchain {} Image Views created.", m_Name);
|
||||
DEBUG("{} Image Views created.", name);
|
||||
|
||||
for (auto &callback : m_ResizeCallbacks)
|
||||
{
|
||||
|
|
@ -184,24 +183,31 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
|||
void
|
||||
Swapchain::RegisterResizeCallback(FnResizeCallback &&callback)
|
||||
{
|
||||
m_ResizeCallbacks.emplace_back(callback);
|
||||
m_ResizeCallbacks.emplace_back(std::move(callback));
|
||||
}
|
||||
|
||||
void
|
||||
Swapchain::Cleanup()
|
||||
{
|
||||
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
|
||||
DEBUG("Swapchain {} Image Views destroyed.", m_Name);
|
||||
if (!m_Swapchain)
|
||||
return;
|
||||
|
||||
NameString name = "Swapchain of ";
|
||||
name += m_Device->m_Name;
|
||||
|
||||
for (const auto imageView : m_ImageViews)
|
||||
{
|
||||
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_Images.clear();
|
||||
if (m_Swapchain)
|
||||
{
|
||||
m_Device->m_Device.destroy(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_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
|
||||
Window::RequestExit() const noexcept
|
||||
{
|
||||
|
|
@ -42,18 +66,7 @@ Window::Window(const cstr title, Size2D extent, const b8 isFullScreen)
|
|||
{
|
||||
m_Name = title;
|
||||
|
||||
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;
|
||||
}
|
||||
SetupLibrary();
|
||||
|
||||
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
||||
ERROR_IF(!monitor, "No monitor found");
|
||||
|
|
|
|||
|
|
@ -4,8 +4,5 @@ cmake_minimum_required(VERSION 3.13)
|
|||
|
||||
target_sources(aster_core
|
||||
PRIVATE
|
||||
"buffer_manager.cpp"
|
||||
"image_manager.cpp"
|
||||
"view_manager.cpp"
|
||||
"sampler_manager.cpp"
|
||||
"device.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/constants.h"
|
||||
#include "aster/core/instance.h"
|
||||
#include "aster/core/device.h"
|
||||
#include "aster/core/physical_device.h"
|
||||
#include "aster/core/pipeline.h"
|
||||
#include "aster/core/swapchain.h"
|
||||
#include "aster/core/window.h"
|
||||
|
||||
#include "aster/systems/device.h"
|
||||
#include "helpers.h"
|
||||
#include "aster/systems/resource_manager.h"
|
||||
|
||||
#include <EASTL/array.h>
|
||||
|
||||
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
|
||||
constexpr auto VERTEX_SHADER_FILE = "shader/triangle.vert.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
|
||||
main(int, char **)
|
||||
{
|
||||
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
||||
|
||||
Window window = {"Triangle (Aster)", {640, 480}};
|
||||
Instance context = {"Triangle", VERSION};
|
||||
Surface surface = {&context, &window, "Primary"};
|
||||
systems::Device device{{
|
||||
.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};
|
||||
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);
|
||||
}
|
||||
Pipeline pipeline = CreatePipeline(&device.m_Device, &device.m_Swapchain);
|
||||
|
||||
// eastl::array<Vertex, 3> 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.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());
|
||||
|
||||
Size2D swapchainSize = device.GetSwapchainSize();
|
||||
|
||||
// Persistent variables
|
||||
vk::Viewport viewport = {
|
||||
.x = 0,
|
||||
.y = Cast<f32>(swapchain.m_Extent.height),
|
||||
.width = Cast<f32>(swapchain.m_Extent.width),
|
||||
.height = -Cast<f32>(swapchain.m_Extent.height),
|
||||
.y = Cast<f32>(swapchainSize.m_Height),
|
||||
.width = Cast<f32>(swapchainSize.m_Width),
|
||||
.height = -Cast<f32>(swapchainSize.m_Height),
|
||||
.minDepth = 0.0,
|
||||
.maxDepth = 1.0,
|
||||
};
|
||||
|
||||
vk::Rect2D scissor = {
|
||||
.offset = {0, 0},
|
||||
.extent = swapchain.m_Extent,
|
||||
.extent = Cast<vk::Extent2D>(swapchainSize),
|
||||
};
|
||||
|
||||
vk::ImageSubresourceRange subresourceRange = {
|
||||
|
|
@ -151,8 +111,8 @@ main(int, char **)
|
|||
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||
.oldLayout = vk::ImageLayout::eUndefined,
|
||||
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
||||
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||
.subresourceRange = subresourceRange,
|
||||
};
|
||||
vk::DependencyInfo topOfThePipeDependency = {
|
||||
|
|
@ -166,8 +126,8 @@ main(int, char **)
|
|||
.dstAccessMask = vk::AccessFlagBits2::eNone,
|
||||
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||
.newLayout = vk::ImageLayout::ePresentSrcKHR,
|
||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
||||
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||
.subresourceRange = subresourceRange,
|
||||
};
|
||||
vk::DependencyInfo renderToPresentDependency = {
|
||||
|
|
@ -175,70 +135,34 @@ main(int, char **)
|
|||
.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");
|
||||
u32 frameIndex = 0;
|
||||
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>);
|
||||
ERROR_IF(Failed(result), "Waiting for fence {} failed. Cause: {}", frameIndex, result)
|
||||
THEN_ABORT(result);
|
||||
vk::CommandBuffer cmd;
|
||||
|
||||
u32 imageIndex;
|
||||
result = device.m_Device.acquireNextImageKHR(swapchain.m_Swapchain, MaxValue<u64>,
|
||||
currentFrame->m_ImageAcquireSem, nullptr, &imageIndex);
|
||||
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;
|
||||
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);
|
||||
const vk::CommandBufferAllocateInfo allocateInfo{
|
||||
.commandPool = currentFrame.m_Pool,
|
||||
.level = vk::CommandBufferLevel::ePrimary,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
AbortIfFailedMV(device.m_Device.m_Device.allocateCommandBuffers(&allocateInfo, &cmd),
|
||||
"Command buffer {} alloc failed.", currentFrame.m_FrameIdx);
|
||||
|
||||
result = device.m_Device.resetCommandPool(currentFrame->m_Pool, {});
|
||||
ERROR_IF(Failed(result), "Command pool {} reset failed. Cause: {}", frameIndex, result)
|
||||
THEN_ABORT(result);
|
||||
auto context = currentFrame.CreateGraphicsContext();
|
||||
|
||||
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
|
||||
vk::Image currentImage = swapchain.m_Images[imageIndex];
|
||||
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
|
||||
topOfThePipeBarrier.image = currentFrame.m_SwapchainImage;
|
||||
renderToPresentBarrier.image = currentFrame.m_SwapchainImage;
|
||||
|
||||
topOfThePipeBarrier.image = currentImage;
|
||||
renderToPresentBarrier.image = currentImage;
|
||||
context.Begin();
|
||||
|
||||
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
||||
result = cmd.begin(&beginInfo);
|
||||
ERROR_IF(Failed(result), "Command buffer begin failed. Cause: {}", result)
|
||||
THEN_ABORT(result);
|
||||
|
||||
cmd.pipelineBarrier2(&topOfThePipeDependency);
|
||||
context.Dependency(topOfThePipeDependency);
|
||||
|
||||
// Render
|
||||
vk::RenderingAttachmentInfo attachmentInfo = {
|
||||
.imageView = currentImageView,
|
||||
.imageView = currentFrame.m_SwapchainImageView,
|
||||
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||
.resolveMode = vk::ResolveModeFlagBits::eNone,
|
||||
.loadOp = vk::AttachmentLoadOp::eClear,
|
||||
|
|
@ -247,115 +171,37 @@ main(int, char **)
|
|||
};
|
||||
|
||||
vk::RenderingInfo renderingInfo = {
|
||||
.renderArea = {.extent = swapchain.m_Extent},
|
||||
.renderArea = {.extent = scissor.extent},
|
||||
.layerCount = 1,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &attachmentInfo,
|
||||
};
|
||||
|
||||
cmd.beginRendering(&renderingInfo);
|
||||
context.BeginRendering(renderingInfo);
|
||||
|
||||
cmd.setViewport(0, 1, &viewport);
|
||||
cmd.setScissor(0, 1, &scissor);
|
||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
|
||||
usize offsets = 0;
|
||||
cmd.bindVertexBuffers(0, 1, &vbo->m_Buffer, &offsets);
|
||||
cmd.draw(3, 1, 0, 0);
|
||||
// cmd.setViewport(0, 1, &viewport);
|
||||
// cmd.setScissor(0, 1, &scissor);
|
||||
// cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
|
||||
// usize offsets = 0;
|
||||
// cmd.bindVertexBuffers(0, 1, &vbo->m_Buffer, &offsets);
|
||||
// cmd.draw(3, 1, 0, 0);
|
||||
|
||||
cmd.endRendering();
|
||||
context.EndRendering();
|
||||
|
||||
cmd.pipelineBarrier2(&renderToPresentDependency);
|
||||
context.Dependency(renderToPresentDependency);
|
||||
|
||||
result = cmd.end();
|
||||
ERROR_IF(Failed(result), "Command buffer end failed. Cause: {}", result)
|
||||
THEN_ABORT(result);
|
||||
context.End();
|
||||
|
||||
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||
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);
|
||||
device.Submit(currentFrame, context);
|
||||
|
||||
vk::PresentInfoKHR presentInfo = {
|
||||
.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.Present(currentFrame);
|
||||
}
|
||||
|
||||
device.WaitIdle();
|
||||
|
||||
device.m_Device.destroy(copyPool, nullptr);
|
||||
|
||||
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
|
||||
CreatePipeline(const Device *device, const Swapchain *swapchain)
|
||||
{
|
||||
|
|
@ -498,13 +344,3 @@ CreateShader(const Device *device, cstr shaderFile)
|
|||
THEN_ABORT(result);
|
||||
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