Refactored upto a swapchain.

This commit is contained in:
Anish Bhobe 2025-01-15 13:36:25 +01:00
parent 14f4ac39be
commit 31809faa75
21 changed files with 417 additions and 231 deletions

View File

@ -21,4 +21,5 @@ include(add_shader.cmake)
include(add_resource_dir.cmake) include(add_resource_dir.cmake)
add_subdirectory("aster") add_subdirectory("aster")
add_subdirectory("samples") add_subdirectory("scratch")
# add_subdirectory("samples")

View File

@ -22,7 +22,10 @@ set(HEADER_FILES
swapchain.h swapchain.h
pipeline.h pipeline.h
queue_allocation.h queue_allocation.h
buffer.h) buffer.h
size.h
image.h
surface.h)
set(SOURCE_FILES set(SOURCE_FILES
logger.cpp logger.cpp
@ -35,7 +38,7 @@ set(SOURCE_FILES
pipeline.cpp pipeline.cpp
buffer.cpp buffer.cpp
image.cpp image.cpp
image.h) surface.cpp)
add_library(aster_core STATIC ${SOURCE_FILES} ${HEADER_FILES}) add_library(aster_core STATIC ${SOURCE_FILES} ${HEADER_FILES})
set_property(TARGET aster_core PROPERTY CXX_STANDARD 20) set_property(TARGET aster_core PROPERTY CXX_STANDARD 20)

View File

@ -49,13 +49,13 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs
m_Buffer = buffer; m_Buffer = buffer;
m_Size_ = size | VALID_BUFFER_BIT | OWNED_BIT; m_Size_ = size | VALID_BUFFER_BIT | OWNED_BIT;
m_Allocation = allocation; m_Allocation = allocation;
m_Mapped = Cast<u8 *>(allocationInfo.pMappedData); m_Mapped = hostAccessible ? Cast<u8 *>(allocationInfo.pMappedData) : nullptr;
device->SetName(m_Buffer, name); device->SetName(m_Buffer, name);
} }
uptr uptr
Buffer::GetDeviceAddress(const Device *device) Buffer::GetDeviceAddress(const Device *device) const
{ {
vk::BufferDeviceAddressInfo addressInfo = {.buffer = m_Buffer}; vk::BufferDeviceAddressInfo addressInfo = {.buffer = m_Buffer};
return device->m_Device.getBufferAddress(&addressInfo); return device->m_Device.getBufferAddress(&addressInfo);

View File

@ -32,7 +32,7 @@ struct Buffer
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name); VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name);
uptr uptr
GetDeviceAddress(const Device *device); GetDeviceAddress(const Device *device) const;
// Buffer.size is used for bookkeeping // Buffer.size is used for bookkeeping
// If the buffer is Invalid, the remaining data in Buffer is used intrusively by `GpuResourceManager`. // If the buffer is Invalid, the remaining data in Buffer is used intrusively by `GpuResourceManager`.

View File

@ -38,18 +38,11 @@ DebugCallback(const VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
return false; return false;
} }
Context::Context(const cstr appName, const Version version, bool enableValidation) void
Context::Init(const cstr appName, const Version version, bool enableValidation)
{ {
INFO_IF(enableValidation, "Validation Layers enabled"); INFO_IF(enableValidation, "Validation Layers enabled");
if (!glfwInit())
{
const char *error = nullptr;
const auto code = glfwGetError(&error);
ERROR("GLFW Init failed. Cause: ({}) {}", code, error)
THEN_ABORT(code);
}
// TODO Get/Check API Version // TODO Get/Check API Version
// Creating Instance // Creating Instance
@ -110,8 +103,12 @@ Context::Context(const cstr appName, const Version version, bool enableValidatio
} }
} }
Context::~Context() void
Context::Destroy()
{ {
if (!m_Instance)
return;
if (m_DebugMessenger) if (m_DebugMessenger)
{ {
m_Instance.destroy(m_DebugMessenger, nullptr); m_Instance.destroy(m_DebugMessenger, nullptr);
@ -120,21 +117,12 @@ Context::~Context()
m_Instance.destroy(nullptr); m_Instance.destroy(nullptr);
DEBUG("Instance destroyed"); DEBUG("Instance destroyed");
glfwTerminate(); m_DebugMessenger = nullptr;
m_Instance = nullptr;
} }
Context::Context(Context &&other) noexcept bool
: m_Instance(Take(other.m_Instance)) Context::IsInDebugMode() const
, m_DebugMessenger(Take(other.m_DebugMessenger))
{ {
} return m_DebugMessenger != nullptr;
Context &
Context::operator=(Context &&other) noexcept
{
if (this == &other)
return *this;
m_Instance = Take(other.m_Instance);
m_DebugMessenger = Take(other.m_DebugMessenger);
return *this;
} }

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include "device.h"
#include "global.h" #include "global.h"
/** /**
@ -17,22 +18,21 @@
struct Context final struct Context final
{ {
// Members // Members
vk::Instance m_Instance = nullptr; vk::Instance m_Instance;
vk::DebugUtilsMessengerEXT m_DebugMessenger = nullptr; vk::DebugUtilsMessengerEXT m_DebugMessenger;
// Ctor/Dtor // Ctor/Dtor
Context(cstr appName, Version version, bool enableValidation = ENABLE_VALIDATION_DEFAULT_VALUE); void Init(cstr appName, Version version, bool enableValidation = ENABLE_VALIDATION_DEFAULT_VALUE);
~Context(); void Destroy();
// Move [[nodiscard]] bool IsInDebugMode() const;
Context(Context &&other) noexcept;
Context &operator=(Context &&other) noexcept;
#if !defined(ASTER_NDEBUG) #if !defined(ASTER_NDEBUG)
constexpr static bool ENABLE_VALIDATION_DEFAULT_VALUE = true; constexpr static bool ENABLE_VALIDATION_DEFAULT_VALUE = true;
#else #else
constexpr static bool ENABLE_VALIDATION_DEFAULT_VALUE = false; constexpr static bool ENABLE_VALIDATION_DEFAULT_VALUE = false;
#endif #endif
DISALLOW_COPY_AND_ASSIGN(Context);
}; };
static_assert(std::is_trivially_copyable_v<Context>);
static_assert(std::is_trivially_copy_assignable_v<Context>);

View File

@ -17,19 +17,22 @@ constexpr eastl::array DEVICE_EXTENSIONS = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
}; };
Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures, void
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name) Device::Init(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
: Device(context, physicalDevice, enabledFeatures, queueAllocations, {}, std::move(name)) const eastl::vector<QueueAllocation> &queueAllocations, cstr name)
{ {
this->Init(context, physicalDevice, enabledFeatures, queueAllocations, {}, name);
} }
Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures, void
const eastl::vector<QueueAllocation> &queueAllocations, eastl::span<u8> &&pipelineCacheData, Device::Init(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
NameString &&name) const eastl::vector<QueueAllocation> &queueAllocations, const eastl::span<u8> &pipelineCacheData,
: m_Name(std::move(name)) cstr name)
, m_PhysicalDevice(physicalDevice->m_PhysicalDevice)
, m_ValidationEnabled(context->m_DebugMessenger != nullptr)
{ {
assert(!m_Device);
m_PhysicalDevice = physicalDevice->m_PhysicalDevice;
m_ValidationEnabled = context->IsInDebugMode();
// Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap // Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap
eastl::fixed_vector<vk::DeviceQueueCreateInfo, 4> deviceQueueCreateInfos; eastl::fixed_vector<vk::DeviceQueueCreateInfo, 4> deviceQueueCreateInfos;
deviceQueueCreateInfos.reserve(queueAllocations.size()); deviceQueueCreateInfos.reserve(queueAllocations.size());
@ -71,9 +74,9 @@ Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features
vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device); vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device);
ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", result) ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", result)
THEN_ABORT(result) THEN_ABORT(result)
ELSE_DEBUG("{} ({}) Initialized.", m_Name, physicalDevice->m_DeviceProperties.deviceName.data()); ELSE_DEBUG("{} ({}) Initialized.", name, physicalDevice->m_DeviceProperties.deviceName.data());
SetName(m_Device, m_Name.data()); SetName(m_Device, name);
VmaVulkanFunctions vmaVulkanFunctions = { VmaVulkanFunctions vmaVulkanFunctions = {
.vkGetInstanceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetInstanceProcAddr, .vkGetInstanceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetInstanceProcAddr,
@ -105,11 +108,15 @@ Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features
THEN_ABORT(result) THEN_ABORT(result)
ELSE_VERBOSE("Pipeline Cache created."); ELSE_VERBOSE("Pipeline Cache created.");
DEBUG("Created Device '{}' Successfully", m_Name); DEBUG("Created Device '{}' Successfully", name);
} }
Device::~Device() void
Device::Destroy()
{ {
if (!m_Device)
return;
m_Device.destroy(m_PipelineCache, nullptr); m_Device.destroy(m_PipelineCache, nullptr);
if (m_Allocator) if (m_Allocator)
{ {
@ -118,8 +125,11 @@ Device::~Device()
DEBUG("Memory Allocator Destroyed"); DEBUG("Memory Allocator Destroyed");
} }
m_Device.destroy(nullptr); m_Device.destroy(nullptr);
DEBUG("Device '{}' Destroyed", m_Name); DEBUG("Device Destroyed");
m_Allocator = nullptr;
m_PhysicalDevice = nullptr; m_PhysicalDevice = nullptr;
m_Device = nullptr;
} }
vk::Queue vk::Queue
@ -150,23 +160,3 @@ Device::WaitIdle() const
ERROR_IF(Failed(deviceIdleResult), "Device Idling Failed. Cause: {}", deviceIdleResult) ERROR_IF(Failed(deviceIdleResult), "Device Idling Failed. Cause: {}", deviceIdleResult)
THEN_ABORT(deviceIdleResult); THEN_ABORT(deviceIdleResult);
} }
Device::Device(Device &&other) noexcept
: m_Name(std::move(other.m_Name))
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
, m_Device(Take(other.m_Device))
, m_Allocator(Take(other.m_Allocator))
{
}
Device &
Device::operator=(Device &&other) noexcept
{
if (this == &other)
return *this;
m_Name = std::move(other.m_Name);
m_PhysicalDevice = Take(other.m_PhysicalDevice);
m_Device = Take(other.m_Device);
m_Allocator = Take(other.m_Allocator);
return *this;
}

View File

@ -24,7 +24,6 @@ struct Features
struct Device final struct Device final
{ {
NameString m_Name;
vk::PhysicalDevice m_PhysicalDevice = nullptr; vk::PhysicalDevice m_PhysicalDevice = nullptr;
vk::Device m_Device = nullptr; vk::Device m_Device = nullptr;
VmaAllocator m_Allocator = nullptr; VmaAllocator m_Allocator = nullptr;
@ -40,17 +39,11 @@ struct Device final
void WaitIdle() const; void WaitIdle() const;
// Ctor/Dtor // Ctor/Dtor
Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures, void Init(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name); const eastl::vector<QueueAllocation> &queueAllocations, cstr name);
Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures, void Init(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
const eastl::vector<QueueAllocation> &queueAllocations, eastl::span<u8> &&pipelineCacheData, NameString &&name); const eastl::vector<QueueAllocation> &queueAllocations, const eastl::span<u8> &pipelineCacheData, cstr name);
~Device(); void Destroy();
// Move
Device(Device &&other) noexcept;
Device &operator=(Device &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(Device);
}; };
template <typename T> template <typename T>

View File

@ -6,7 +6,8 @@
#include "physical_device.h" #include "physical_device.h"
#include "context.h" #include "context.h"
#include "window.h" #include "queue_allocation.h"
#include "surface.h"
[[nodiscard]] vk::SurfaceCapabilitiesKHR [[nodiscard]] vk::SurfaceCapabilitiesKHR
GetSurfaceCapabilities(const vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface) GetSurfaceCapabilities(const vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface)
@ -138,6 +139,24 @@ PhysicalDevice::PhysicalDevice(const vk::SurfaceKHR surface, const vk::PhysicalD
m_PhysicalDevice = physicalDevice; m_PhysicalDevice = physicalDevice;
} }
QueueAllocation
PhysicalDevice::FindAppropriateQueueAllocation()
{
for (auto &queueFamilyInfo : this->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);
}
eastl::fixed_vector<vk::PhysicalDevice, 8> eastl::fixed_vector<vk::PhysicalDevice, 8>
EnumeratePhysicalDevices(const vk::Instance instance) EnumeratePhysicalDevices(const vk::Instance instance)
{ {
@ -154,12 +173,44 @@ EnumeratePhysicalDevices(const vk::Instance instance)
return physicalDevices; return physicalDevices;
} }
PhysicalDevices::PhysicalDevices(const Window *window, const Context *context) PhysicalDevices::PhysicalDevices(const Surface *surface, const Context *context)
{ {
auto surface = window->m_Surface; auto surfaceImpl = surface->m_Surface;
auto physicalDevices = EnumeratePhysicalDevices(context->m_Instance); auto physicalDevices = EnumeratePhysicalDevices(context->m_Instance);
for (auto physicalDevice : physicalDevices) for (auto physicalDevice : physicalDevices)
{ {
this->emplace_back(surface, physicalDevice); this->emplace_back(surfaceImpl, physicalDevice);
} }
} }
bool
IsSuitableDevice(const PhysicalDevice *physicalDevice)
{
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();
return hasSurfaceFormat && hasPresentMode && isNotCpu && hasAllRequiredQueues;
}
PhysicalDevice
PhysicalDevices::FindSuitableDevice()
{
for (auto &physicalDevice : *this)
{
if (IsSuitableDevice(&physicalDevice))
{
return physicalDevice;
}
}
ERROR("No suitable GPU available on the system.")
THEN_ABORT(vk::Result::eErrorUnknown);
}

View File

@ -8,6 +8,8 @@
#include "global.h" #include "global.h"
#include <EASTL/fixed_vector.h> #include <EASTL/fixed_vector.h>
struct QueueAllocation;
struct Surface;
struct Window; struct Window;
struct Context; struct Context;
@ -47,10 +49,22 @@ struct PhysicalDevice final
eastl::vector<QueueFamilyInfo> m_QueueFamilies; eastl::vector<QueueFamilyInfo> m_QueueFamilies;
PhysicalDevice(vk::SurfaceKHR surface, vk::PhysicalDevice physicalDevice); PhysicalDevice(vk::SurfaceKHR surface, vk::PhysicalDevice physicalDevice);
QueueAllocation FindAppropriateQueueAllocation();
}; };
class PhysicalDevices : public eastl::fixed_vector<PhysicalDevice, 4> class PhysicalDevices : public eastl::vector<PhysicalDevice>
{ {
public: public:
PhysicalDevices(const Window *window, const Context *context); PhysicalDevices(const Surface *surface, const Context *context);
}; PhysicalDevice FindSuitableDevice();
};
constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics |
QueueSupportFlagBits::eCompute | QueueSupportFlagBits::ePresent |
QueueSupportFlagBits::eTransfer;
bool
IsSuitableDevice(const vk::PhysicalDevice physicalDevice);
PhysicalDevice
FindSuitableDevice(const PhysicalDevices &physicalDevices);

25
aster/size.h Normal file
View File

@ -0,0 +1,25 @@
// =============================================
// Aster: size.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
struct Size
{
u32 m_Width;
u32 m_Height;
operator vk::Extent2D() const
{
return {m_Width, m_Height};
}
glm::vec<2, u32>
ToVec()
{
return {m_Width, m_Height};
}
};

33
aster/surface.cpp Normal file
View File

@ -0,0 +1,33 @@
// =============================================
// Aster: surface.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "surface.h"
#include "context.h"
#include "window.h"
void
Surface::Init(Context *context, const Window *window)
{
assert(!m_Surface);
VkSurfaceKHR surface;
auto result = Cast<vk::Result>(
glfwCreateWindowSurface(Cast<VkInstance>(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_Surface = vk::SurfaceKHR(surface);
}
void
Surface::Destroy(const Context *context)
{
if (!m_Surface)
return;
context->m_Instance.destroy(Take(m_Surface), nullptr);
DEBUG("Surface Destroyed");
}

19
aster/surface.h Normal file
View File

@ -0,0 +1,19 @@
// =============================================
// Aster: surface.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
struct Context;
struct Window;
struct Surface
{
vk::SurfaceKHR m_Surface;
void Init(Context *context, const Window *window);
void Destroy(const Context *context);
};

View File

@ -7,64 +7,32 @@
#include "device.h" #include "device.h"
#include "physical_device.h" #include "physical_device.h"
#include "window.h" #include "surface.h"
[[nodiscard]] vk::Extent2D GetExtent(const Window *window, vk::SurfaceCapabilitiesKHR *surfaceCapabilities); [[nodiscard]] vk::Extent2D GetExtent(Size size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities);
Swapchain::Swapchain(const Window *window, const Device *device, NameString &&name) void
: m_Device(device) Swapchain::Init(const Surface *surface, const Device *device, Size size, cstr name)
, m_Name(std::move(name))
, m_Format(vk::Format::eUndefined)
{ {
this->Create(window); assert(!m_Swapchain);
} this->Create(surface, device, size, name);
Swapchain::~Swapchain()
{
this->Cleanup();
}
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))
{
}
Swapchain &
Swapchain::operator=(Swapchain &&other) noexcept
{
if (this == &other)
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);
return *this;
} }
void void
Swapchain::Create(const Window *window) Swapchain::Create(const Surface *surface, const Device *device, Size size, cstr name)
{ {
auto surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, window->m_Surface); auto surfaceCapabilities = GetSurfaceCapabilities(device->m_PhysicalDevice, surface->m_Surface);
m_Extent = GetExtent(window, &surfaceCapabilities); m_Extent = GetExtent(size, &surfaceCapabilities);
while (m_Extent.width == 0 || m_Extent.height == 0) while (m_Extent.width == 0 || m_Extent.height == 0)
{ {
glfwWaitEvents(); glfwWaitEvents();
surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, window->m_Surface); surfaceCapabilities = GetSurfaceCapabilities(device->m_PhysicalDevice, surface->m_Surface);
m_Extent = GetExtent(window, &surfaceCapabilities); m_Extent = GetExtent(size, &surfaceCapabilities);
} }
auto surfaceFormats = GetSurfaceFormats(m_Device->m_PhysicalDevice, window->m_Surface); auto surfaceFormats = GetSurfaceFormats(device->m_PhysicalDevice, surface->m_Surface);
auto presentModes = GetSurfacePresentModes(m_Device->m_PhysicalDevice, window->m_Surface); auto presentModes = GetSurfacePresentModes(device->m_PhysicalDevice, surface->m_Surface);
m_Format = vk::Format::eUndefined; m_Format = vk::Format::eUndefined;
vk::ColorSpaceKHR swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear; vk::ColorSpaceKHR swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
@ -104,7 +72,7 @@ Swapchain::Create(const Window *window)
// TODO: Note that different queues might need the images to be shared. // TODO: Note that different queues might need the images to be shared.
const vk::SwapchainCreateInfoKHR swapchainCreateInfo = { const vk::SwapchainCreateInfoKHR swapchainCreateInfo = {
.surface = window->m_Surface, .surface = surface->m_Surface,
.minImageCount = swapchainImageCount, .minImageCount = swapchainImageCount,
.imageFormat = m_Format, .imageFormat = m_Format,
.imageColorSpace = swapchainColorSpace, .imageColorSpace = swapchainColorSpace,
@ -119,29 +87,29 @@ Swapchain::Create(const Window *window)
.oldSwapchain = m_Swapchain, .oldSwapchain = m_Swapchain,
}; };
vk::Device device = m_Device->m_Device; vk::Device vkDevice = device->m_Device;
vk::SwapchainKHR swapchain; vk::SwapchainKHR swapchain;
vk::Result result = device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain); vk::Result result = vkDevice.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
ERROR_IF(Failed(result), "Swapchain {} creation failed. Cause {}", m_Name, result) ERROR_IF(Failed(result), "Swapchain {} creation failed. Cause {}", name, result)
THEN_ABORT(result) THEN_ABORT(result)
ELSE_DEBUG("Created Swapchain '{}'", m_Name); ELSE_DEBUG("Created Swapchain '{}'", name);
// Irrelevant on the first run. Required for re-creation. // Irrelevant on the first run. Required for re-creation.
Cleanup(); Destroy(device);
m_Swapchain = swapchain; m_Swapchain = swapchain;
m_Device->SetName(m_Swapchain, m_Name.data()); device->SetName(m_Swapchain, name);
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr); result = vkDevice.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr);
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result) ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", name, result)
THEN_ABORT(result); THEN_ABORT(result);
// Managed by the Swapchain. // Managed by the Swapchain.
m_Images.resize(swapchainImageCount); m_Images.resize(swapchainImageCount);
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, m_Images.data()); result = vkDevice.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 swapchain {}'s images. Cause {}", name, result)
THEN_ABORT(result); THEN_ABORT(result);
vk::ImageViewCreateInfo viewCreateInfo = { vk::ImageViewCreateInfo viewCreateInfo = {
@ -164,8 +132,8 @@ Swapchain::Create(const Window *window)
viewCreateInfo.image = image; viewCreateInfo.image = image;
vk::ImageView imageView; vk::ImageView imageView;
result = device.createImageView(&viewCreateInfo, nullptr, &imageView); result = vkDevice.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 swapchain {}'s image view [{}]. Cause {}", name, index, result)
THEN_ABORT(result); THEN_ABORT(result);
m_ImageViews.push_back(imageView); m_ImageViews.push_back(imageView);
@ -173,7 +141,7 @@ Swapchain::Create(const Window *window)
++index; ++index;
} }
DEBUG("Swapchain {} Image Views created.", m_Name); DEBUG("Swapchain {} Image Views created.", name);
for (auto &callback : m_ResizeCallbacks) for (auto &callback : m_ResizeCallbacks)
{ {
@ -188,32 +156,32 @@ Swapchain::RegisterResizeCallback(FnResizeCallback &&callback)
} }
void void
Swapchain::Cleanup() Swapchain::Destroy(const Device *device)
{ {
if (!m_ImageViews.empty()) // Don't want the condition in the logs. if (!m_ImageViews.empty()) // Don't want the condition in the logs.
DEBUG("Swapchain {} Image Views destroyed.", m_Name); DEBUG("Swapchain Image Views destroyed.");
for (const auto imageView : m_ImageViews) for (const auto imageView : m_ImageViews)
{ {
m_Device->m_Device.destroy(imageView, nullptr); device->m_Device.destroy(imageView, nullptr);
} }
m_ImageViews.clear(); m_ImageViews.clear();
if (m_Swapchain) if (m_Swapchain)
{ {
m_Device->m_Device.destroy(m_Swapchain, nullptr); device->m_Device.destroy(m_Swapchain, nullptr);
m_Swapchain = nullptr; m_Swapchain = nullptr;
DEBUG("Swapchain '{}' destroyed.", m_Name); DEBUG("Swapchain destroyed.");
} }
} }
vk::Extent2D vk::Extent2D
GetExtent(const Window *window, vk::SurfaceCapabilitiesKHR *surfaceCapabilities) GetExtent(Size size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities)
{ {
if (surfaceCapabilities->currentExtent.width != MaxValue<u32>) if (surfaceCapabilities->currentExtent.width != MaxValue<u32>)
{ {
return surfaceCapabilities->currentExtent; return surfaceCapabilities->currentExtent;
} }
auto [width, height] = window->GetSize(); auto [width, height] = size;
auto [minWidth, minHeight] = surfaceCapabilities->minImageExtent; auto [minWidth, minHeight] = surfaceCapabilities->minImageExtent;
auto [maxWidth, maxHeight] = surfaceCapabilities->maxImageExtent; auto [maxWidth, maxHeight] = surfaceCapabilities->maxImageExtent;

View File

@ -7,8 +7,11 @@
#include "global.h" #include "global.h"
#include "size.h"
#include <EASTL/fixed_vector.h> #include <EASTL/fixed_vector.h>
struct Surface;
struct PhysicalDevice; struct PhysicalDevice;
struct Window; struct Window;
struct Device; struct Device;
@ -17,9 +20,7 @@ struct Swapchain final
{ {
using FnResizeCallback = eastl::function<void(vk::Extent2D)>; using FnResizeCallback = eastl::function<void(vk::Extent2D)>;
const Device *m_Device;
vk::SwapchainKHR m_Swapchain; vk::SwapchainKHR m_Swapchain;
NameString m_Name;
vk::Extent2D m_Extent; vk::Extent2D m_Extent;
vk::Format m_Format; vk::Format m_Format;
eastl::fixed_vector<vk::Image, 4> m_Images; eastl::fixed_vector<vk::Image, 4> m_Images;
@ -27,19 +28,10 @@ struct Swapchain final
eastl::vector<FnResizeCallback> m_ResizeCallbacks; eastl::vector<FnResizeCallback> m_ResizeCallbacks;
void Create(const Window *window); void Create(const Surface *surface, const Device *device, Size size, cstr name);
void RegisterResizeCallback(FnResizeCallback &&callback); void RegisterResizeCallback(FnResizeCallback &&callback);
// Ctor/Dtor // Ctor/Dtor
Swapchain(const Window *window, const Device *device, NameString &&name); void Init(const Surface *surface, const Device *device, Size size, cstr name);
~Swapchain(); void Destroy(const Device *device);
// Move
Swapchain(Swapchain &&other) noexcept;
Swapchain &operator=(Swapchain &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(Swapchain);
private:
void Cleanup();
}; };

View File

@ -35,10 +35,16 @@ Window::GetSize() const
return {Cast<u32>(width), Cast<u32>(height)}; return {Cast<u32>(width), Cast<u32>(height)};
} }
Window::Window(const cstr title, Context *context, vk::Extent2D extent, const b8 isFullScreen) void
Window::Init(cstr title, vk::Extent2D extent, b8 isFullScreen)
{ {
m_Context = context; if (!glfwInit())
m_Name = title; {
const char *error = nullptr;
const auto code = glfwGetError(&error);
ERROR("GLFW Init failed. Cause: ({}) {}", code, error)
THEN_ABORT(code);
}
GLFWmonitor *monitor = glfwGetPrimaryMonitor(); GLFWmonitor *monitor = glfwGetPrimaryMonitor();
ERROR_IF(!monitor, "No monitor found"); ERROR_IF(!monitor, "No monitor found");
@ -49,10 +55,10 @@ Window::Window(const cstr title, Context *context, vk::Extent2D extent, const b8
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE); glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE);
m_Window = glfwCreateWindow(Cast<i32>(extent.width), Cast<i32>(extent.height), m_Name.c_str(), m_Window = glfwCreateWindow(Cast<i32>(extent.width), Cast<i32>(extent.height), title,
isFullScreen ? monitor : nullptr, nullptr); isFullScreen ? monitor : nullptr, nullptr);
ERROR_IF(m_Window == nullptr, "Window creation failed") ERROR_IF(m_Window == nullptr, "Window creation failed")
ELSE_DEBUG("Window '{}' created with resolution '{}x{}'", m_Name, extent.width, extent.height); ELSE_DEBUG("Window '{}' created with resolution '{}x{}'", title, extent.width, extent.height);
if (m_Window == nullptr) if (m_Window == nullptr)
{ {
const char *error = nullptr; const char *error = nullptr;
@ -67,49 +73,21 @@ Window::Window(const cstr title, Context *context, vk::Extent2D extent, const b8
Cast<i32>(windowHeight - extent.height) / 2); Cast<i32>(windowHeight - extent.height) / 2);
} }
glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
VkSurfaceKHR surface;
auto result =
Cast<vk::Result>(glfwCreateWindowSurface(Cast<VkInstance>(m_Context->m_Instance), m_Window, nullptr, &surface));
ERROR_IF(Failed(result), "Failed to create Surface with {}", result)
THEN_ABORT(result)
ELSE_DEBUG("Surface {} Created", m_Name);
m_Surface = vk::SurfaceKHR(surface);
} }
Window::~Window() void
Window::Destroy()
{ {
if (m_Context && m_Surface) if (!m_Window)
{ return;
m_Context->m_Instance.destroy(m_Surface, nullptr);
DEBUG("Surface Destroyed");
}
if (m_Window) const char *title = glfwGetWindowTitle(m_Window);
{
glfwDestroyWindow(m_Window);
m_Window = nullptr;
}
DEBUG("Window '{}' Destroyed", m_Name); DEBUG("Window {} Destroyed", title);
}
glfwDestroyWindow(m_Window);
Window::Window(Window &&other) noexcept m_Window = nullptr;
: m_Context(other.m_Context)
, m_Window(Take(other.m_Window)) // Currently only one window is supported.
, m_Surface(Take(other.m_Surface)) glfwTerminate();
, m_Name(Take(other.m_Name))
{
}
Window &
Window::operator=(Window &&other) noexcept
{
if (this == &other)
return *this;
m_Context = other.m_Context;
m_Window = Take(other.m_Window);
m_Surface = Take(other.m_Surface);
m_Name = Take(other.m_Name);
return *this;
} }

View File

@ -6,18 +6,12 @@
#pragma once #pragma once
#include "global.h" #include "global.h"
#include <EASTL/fixed_string.h>
struct Context; struct Context;
struct Window final struct Window final
{ {
// fields
Context *m_Context{};
GLFWwindow *m_Window = nullptr; GLFWwindow *m_Window = nullptr;
vk::SurfaceKHR m_Surface;
eastl::fixed_string<char, 32> m_Name;
// Methods // Methods
[[nodiscard]] bool [[nodiscard]] bool
@ -34,12 +28,6 @@ struct Window final
[[nodiscard]] vk::Extent2D GetSize() const; [[nodiscard]] vk::Extent2D GetSize() const;
// Ctor/Dtor // Ctor/Dtor
Window(cstr title, Context *context, vk::Extent2D extent, b8 isFullScreen = false); void Init(cstr title, vk::Extent2D extent, b8 isFullScreen = false);
~Window(); void Destroy();
// Move
Window(Window &&other) noexcept;
Window &operator=(Window &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(Window);
}; };

15
scratch/CMakeLists.txt Normal file
View File

@ -0,0 +1,15 @@
# CMakeList.txt ; CMake project for Refactoring scratchwork
cmake_minimum_required(VERSION 3.13)
set(SOURCE_FILES "graphics.cpp")
set(HEADER_FILES "graphics.h")
add_library(scratch STATIC ${SOURCE_FILES} ${HEADER_FILES})
add_executable(scratch_exe "scratch_run.cpp")
target_link_libraries(scratch_exe PRIVATE scratch)
target_link_libraries(scratch PRIVATE aster_core)
target_link_libraries(scratch_exe PRIVATE aster_core)

25
scratch/graphics.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "graphics.h"
#include "physical_device.h"
#include "queue_allocation.h"
void
Graphics::Init(const Window *window, const cstr appName, const Version version)
{
m_Context.Init(appName, version);
m_Surface.Init(&m_Context, window);
auto deviceToUse = PhysicalDevices(&m_Surface, &m_Context).FindSuitableDevice();
Features features = {};
QueueAllocation qa = deviceToUse.FindAppropriateQueueAllocation();
m_Device.Init(&m_Context, &deviceToUse, &features, {qa}, "Primary");
}
void
Graphics::Destroy()
{
m_Device.Destroy();
m_Surface.Destroy(&m_Context);
m_Context.Destroy();
}

69
scratch/graphics.h Normal file
View File

@ -0,0 +1,69 @@
// =============================================
// Scratch: graphics.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
#include "context.h"
#include "device.h"
#include "surface.h"
struct Window;
struct Memory
{
u8 *m_Data;
u8 *m_TransientMemory;
usize m_TransientMemorySize;
u8 *m_PermanentMemory;
usize m_PermanentMemoryRemaining;
void Init(usize transientSize, usize permanentSize)
{
m_Data = new u8[permanentSize + transientSize];
m_TransientMemory = m_Data + permanentSize;
m_TransientMemorySize = transientSize;
m_PermanentMemory = m_Data;
m_PermanentMemoryRemaining = permanentSize;
}
void
Destroy()
{
delete[] Take(m_Data);
}
template <typename T>
void MakePermanent(T* &val) requires std::is_default_constructible_v<T>
{
const usize alignment = alignof(T);
const usize size = sizeof(T);
const usize offset = (alignment - Recast<uptr>(val) % alignment) % alignment;
const usize totalSize = size + offset;
assert(totalSize <= m_PermanentMemoryRemaining);
u8 *block = m_PermanentMemory;
block += offset;
m_PermanentMemory += totalSize;
m_PermanentMemoryRemaining -= totalSize;
val = Recast<T *>(block);
}
};
struct Graphics
{
Context m_Context;
Surface m_Surface;
Device m_Device;
void Init(const Window* window, const cstr appName, const Version version);
void Destroy();
};

34
scratch/scratch_run.cpp Normal file
View File

@ -0,0 +1,34 @@
// =============================================
// Scratch: scratch_run.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "global.h"
#include "graphics.h"
#include "window.h"
int
main(int argc, const char *argv[])
{
Memory memory;
memory.Init(Gigabyte(4), Megabyte(128u));
Window window;
Graphics graphics;
window.Init("Scratch", {640, 480});
graphics.Init(&window, "Scratch", VERSION);
while (window.Poll())
{
}
graphics.Destroy();
window.Destroy();
memory.Destroy();
return 0;
}