Clear Screen.
This commit is contained in:
parent
76a7927643
commit
c6ff2f4f76
|
|
@ -1,3 +1,4 @@
|
||||||
.idea/
|
.idea/
|
||||||
.cache/
|
.cache/
|
||||||
build/
|
build/
|
||||||
|
.vs/
|
||||||
|
|
@ -8,7 +8,12 @@ set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "-Wall -fno-rtti -fno-exceptions")
|
if (MSVC)
|
||||||
|
set(CMAKE_CXX_FLAGS "/W4 /GR- /Zi")
|
||||||
|
add_compile_definitions(_NO_EXCEPTIONS=1)
|
||||||
|
else ()
|
||||||
|
set(CMAKE_CXX_FLAGS "-Wall -fno-rtti -fno-exceptions")
|
||||||
|
endif ()
|
||||||
|
|
||||||
add_subdirectory("aster")
|
add_subdirectory("aster")
|
||||||
add_subdirectory("triangle")
|
add_subdirectory("triangle")
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,16 @@
|
||||||
"CMAKE_CXX_COMPILER": "/usr/bin/clang++",
|
"CMAKE_CXX_COMPILER": "/usr/bin/clang++",
|
||||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/build",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
|
||||||
|
"CMAKE_MAKE_PROGRAM": "ninja",
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,12 @@
|
||||||
#include <EASTL/array.h>
|
#include <EASTL/array.h>
|
||||||
#include <EASTL/fixed_vector.h>
|
#include <EASTL/fixed_vector.h>
|
||||||
|
|
||||||
constexpr eastl::array DEVICE_EXTENSIONS = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
// TODO: This will need to be flexible for devices that don't support some of the extensions.
|
||||||
|
constexpr eastl::array DEVICE_EXTENSIONS = {
|
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
Device::Device(const Context *context, PhysicalDevice *physicalDevice,
|
Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
||||||
const vk::PhysicalDeviceFeatures *enabledFeatures,
|
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name)
|
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name)
|
||||||
: m_Name(std::move(name))
|
: m_Name(std::move(name))
|
||||||
, m_PhysicalDevice(physicalDevice->m_PhysicalDevice)
|
, m_PhysicalDevice(physicalDevice->m_PhysicalDevice)
|
||||||
|
|
@ -40,12 +42,21 @@ Device::Device(const Context *context, PhysicalDevice *physicalDevice,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
vk::DeviceCreateInfo deviceCreateInfo = {
|
vk::DeviceCreateInfo deviceCreateInfo = {
|
||||||
|
.pNext = vulkan11Features,
|
||||||
.queueCreateInfoCount = Cast<u32>(deviceQueueCreateInfos.size()),
|
.queueCreateInfoCount = Cast<u32>(deviceQueueCreateInfos.size()),
|
||||||
.pQueueCreateInfos = deviceQueueCreateInfos.data(),
|
.pQueueCreateInfos = deviceQueueCreateInfos.data(),
|
||||||
.enabledExtensionCount = Cast<u32>(DEVICE_EXTENSIONS.size()),
|
.enabledExtensionCount = Cast<u32>(DEVICE_EXTENSIONS.size()),
|
||||||
.ppEnabledExtensionNames = DEVICE_EXTENSIONS.data(),
|
.ppEnabledExtensionNames = DEVICE_EXTENSIONS.data(),
|
||||||
.pEnabledFeatures = enabledFeatures,
|
.pEnabledFeatures = deviceFeatures,
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device);
|
vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device);
|
||||||
|
|
@ -86,4 +97,12 @@ Device::~Device()
|
||||||
m_Device.destroy(nullptr);
|
m_Device.destroy(nullptr);
|
||||||
DEBUG("Device '{}' Destroyed", m_Name);
|
DEBUG("Device '{}' Destroyed", m_Name);
|
||||||
m_PhysicalDevice = nullptr;
|
m_PhysicalDevice = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Queue
|
||||||
|
Device::GetQueue(const u32 familyIndex, const u32 queueIndex) const
|
||||||
|
{
|
||||||
|
vk::Queue queue;
|
||||||
|
m_Device.getQueue(familyIndex, queueIndex, &queue);
|
||||||
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
@ -18,6 +18,14 @@ struct QueueAllocation
|
||||||
u32 m_Count;
|
u32 m_Count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Features
|
||||||
|
{
|
||||||
|
vk::PhysicalDeviceFeatures m_Vulkan10Features;
|
||||||
|
vk::PhysicalDeviceVulkan11Features m_Vulkan11Features;
|
||||||
|
vk::PhysicalDeviceVulkan12Features m_Vulkan12Features;
|
||||||
|
vk::PhysicalDeviceVulkan13Features m_Vulkan13Features;
|
||||||
|
};
|
||||||
|
|
||||||
struct Device final
|
struct Device final
|
||||||
{
|
{
|
||||||
NameString m_Name;
|
NameString m_Name;
|
||||||
|
|
@ -25,7 +33,9 @@ struct Device final
|
||||||
vk::Device m_Device = nullptr;
|
vk::Device m_Device = nullptr;
|
||||||
VmaAllocator m_Allocator = nullptr;
|
VmaAllocator m_Allocator = nullptr;
|
||||||
|
|
||||||
Device(const Context *context, PhysicalDevice *physicalDevice, const vk::PhysicalDeviceFeatures *enabledFeatures,
|
Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
|
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
|
||||||
~Device();
|
~Device();
|
||||||
|
|
||||||
|
[[nodiscard]] vk::Queue GetQueue(u32 familyIndex, u32 queueIndex) const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,15 @@
|
||||||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||||
|
|
||||||
void *
|
void *
|
||||||
operator new[](size_t size, const char *pName, int flags, unsigned debugFlags, const char *file, int line)
|
operator new[](size_t size, const char * /*pName*/, int /*flags*/, unsigned /*debugFlags*/, const char * /*file*/,
|
||||||
|
int /*line*/)
|
||||||
{
|
{
|
||||||
return new u8[size];
|
return new u8[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
operator new[](size_t size, size_t alignment, size_t alignmentOffset, const char *pName, int flags, unsigned debugFlags,
|
operator new[](size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, const char * /*pName*/, int /*flags*/,
|
||||||
const char *file, int line)
|
unsigned /*debugFlags*/, const char * /*file*/, int /*line*/)
|
||||||
{
|
{
|
||||||
return new u8[size];
|
return new u8[size];
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,6 @@ struct Logger
|
||||||
return "[DEBUG]:";
|
return "[DEBUG]:";
|
||||||
if constexpr (TLogLevel == LogType::eVerbose)
|
if constexpr (TLogLevel == LogType::eVerbose)
|
||||||
return "[VERB]: ";
|
return "[VERB]: ";
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
template <LogType TLogLevel>
|
||||||
|
|
@ -59,7 +58,6 @@ struct Logger
|
||||||
return ansi_color::White;
|
return ansi_color::White;
|
||||||
if constexpr (TLogLevel == LogType::eVerbose)
|
if constexpr (TLogLevel == LogType::eVerbose)
|
||||||
return ansi_color::Blue;
|
return ansi_color::Blue;
|
||||||
return ansi_color::White;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
template <LogType TLogLevel>
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,7 @@ Swapchain::Swapchain(const Window *window, const Device *device, NameString &&na
|
||||||
|
|
||||||
Swapchain::~Swapchain()
|
Swapchain::~Swapchain()
|
||||||
{
|
{
|
||||||
if (m_Swapchain)
|
Cleanup();
|
||||||
{
|
|
||||||
m_Device->m_Device.destroy(m_Swapchain, nullptr);
|
|
||||||
m_Swapchain = nullptr;
|
|
||||||
DEBUG("Swapchain '{}' destroyed.", m_Name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -61,10 +56,9 @@ Swapchain::Create(const Window *window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::Extent2D swapchainExtent;
|
|
||||||
if (surfaceCapabilities.currentExtent.width != MaxValue<u32>)
|
if (surfaceCapabilities.currentExtent.width != MaxValue<u32>)
|
||||||
{
|
{
|
||||||
swapchainExtent = surfaceCapabilities.currentExtent;
|
m_Extent = surfaceCapabilities.currentExtent;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -72,8 +66,8 @@ Swapchain::Create(const Window *window)
|
||||||
auto [minWidth, minHeight] = surfaceCapabilities.minImageExtent;
|
auto [minWidth, minHeight] = surfaceCapabilities.minImageExtent;
|
||||||
auto [maxWidth, maxHeight] = surfaceCapabilities.maxImageExtent;
|
auto [maxWidth, maxHeight] = surfaceCapabilities.maxImageExtent;
|
||||||
|
|
||||||
swapchainExtent.width = glm::clamp(width, minWidth, maxWidth);
|
m_Extent.width = glm::clamp(width, minWidth, maxWidth);
|
||||||
swapchainExtent.height = glm::clamp(height, minHeight, maxHeight);
|
m_Extent.height = glm::clamp(height, minHeight, maxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 swapchainImageCount = 3;
|
u32 swapchainImageCount = 3;
|
||||||
|
|
@ -90,7 +84,7 @@ Swapchain::Create(const Window *window)
|
||||||
.minImageCount = swapchainImageCount,
|
.minImageCount = swapchainImageCount,
|
||||||
.imageFormat = swapchainFormat,
|
.imageFormat = swapchainFormat,
|
||||||
.imageColorSpace = swapchainColorSpace,
|
.imageColorSpace = swapchainColorSpace,
|
||||||
.imageExtent = swapchainExtent,
|
.imageExtent = m_Extent,
|
||||||
.imageArrayLayers = 1,
|
.imageArrayLayers = 1,
|
||||||
.imageUsage = vk::ImageUsageFlagBits::eColorAttachment,
|
.imageUsage = vk::ImageUsageFlagBits::eColorAttachment,
|
||||||
.imageSharingMode = vk::SharingMode::eExclusive,
|
.imageSharingMode = vk::SharingMode::eExclusive,
|
||||||
|
|
@ -101,11 +95,75 @@ Swapchain::Create(const Window *window)
|
||||||
.oldSwapchain = m_Swapchain,
|
.oldSwapchain = m_Swapchain,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vk::Device device = m_Device->m_Device;
|
||||||
|
|
||||||
vk::SwapchainKHR swapchain;
|
vk::SwapchainKHR swapchain;
|
||||||
vk::Result result = m_Device->m_Device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
|
vk::Result result = device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
|
||||||
ERROR_IF(Failed(result), "Swapchain {} creation failed. Cause {}", m_Name, result)
|
ERROR_IF(Failed(result), "Swapchain {} creation failed. Cause {}", m_Name, result)
|
||||||
THEN_ABORT(result)
|
THEN_ABORT(result)
|
||||||
ELSE_DEBUG("Created Swapchain '{}'", m_Name);
|
ELSE_DEBUG("Created Swapchain '{}'", m_Name);
|
||||||
|
|
||||||
|
// Irrelevant on the first run. Required for re-creation.
|
||||||
|
Cleanup();
|
||||||
|
|
||||||
m_Swapchain = swapchain;
|
m_Swapchain = swapchain;
|
||||||
|
|
||||||
|
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr);
|
||||||
|
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
// Managed by the Swapchain.
|
||||||
|
m_Images.resize(swapchainImageCount);
|
||||||
|
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, m_Images.data());
|
||||||
|
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
vk::ImageViewCreateInfo viewCreateInfo = {
|
||||||
|
.viewType = vk::ImageViewType::e2D,
|
||||||
|
.format = swapchainFormat,
|
||||||
|
.components = {.r = vk::ComponentSwizzle::eIdentity,
|
||||||
|
.g = vk::ComponentSwizzle::eIdentity,
|
||||||
|
.b = vk::ComponentSwizzle::eIdentity,
|
||||||
|
.a = vk::ComponentSwizzle::eIdentity},
|
||||||
|
.subresourceRange = {.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 index = 0;
|
||||||
|
for (auto image : m_Images)
|
||||||
|
{
|
||||||
|
viewCreateInfo.image = image;
|
||||||
|
|
||||||
|
vk::ImageView imageView;
|
||||||
|
result = device.createImageView(&viewCreateInfo, nullptr, &imageView);
|
||||||
|
ERROR_IF(Failed(result), "Failed creating swapchain {}'s image view [{}]. Cause {}", m_Name, index, result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
m_ImageViews.push_back(imageView);
|
||||||
|
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("Swapchain {} Image Views created.", m_Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Swapchain::Cleanup()
|
||||||
|
{
|
||||||
|
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
|
||||||
|
DEBUG("Swapchain {} Image Views destroyed.", m_Name);
|
||||||
|
for (const auto imageView : m_ImageViews)
|
||||||
|
{
|
||||||
|
m_Device->m_Device.destroy(imageView, nullptr);
|
||||||
|
}
|
||||||
|
m_ImageViews.clear();
|
||||||
|
if (m_Swapchain)
|
||||||
|
{
|
||||||
|
m_Device->m_Device.destroy(m_Swapchain, nullptr);
|
||||||
|
m_Swapchain = nullptr;
|
||||||
|
DEBUG("Swapchain '{}' destroyed.", m_Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
|
#include <EASTL/fixed_vector.h>
|
||||||
|
|
||||||
struct PhysicalDevice;
|
struct PhysicalDevice;
|
||||||
struct Window;
|
struct Window;
|
||||||
struct Device;
|
struct Device;
|
||||||
|
|
@ -16,8 +18,14 @@ struct Swapchain final
|
||||||
const Device *m_Device;
|
const Device *m_Device;
|
||||||
vk::SwapchainKHR m_Swapchain;
|
vk::SwapchainKHR m_Swapchain;
|
||||||
NameString m_Name;
|
NameString m_Name;
|
||||||
|
vk::Extent2D m_Extent;
|
||||||
|
eastl::fixed_vector<vk::Image, 4> m_Images;
|
||||||
|
eastl::fixed_vector<vk::ImageView, 4> m_ImageViews;
|
||||||
|
|
||||||
Swapchain(const Window *window, const Device *device, NameString &&name);
|
Swapchain(const Window *window, const Device *device, NameString &&name);
|
||||||
~Swapchain();
|
~Swapchain();
|
||||||
void Create(const Window *window);
|
void Create(const Window *window);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Cleanup();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
add_executable(aster_exe "aster.cpp")
|
add_executable(triangle "triangle.cpp")
|
||||||
add_dependencies(aster_exe aster_core)
|
add_dependencies(triangle aster_core)
|
||||||
|
|
||||||
target_link_libraries(aster_exe PRIVATE aster_core)
|
target_link_libraries(triangle PRIVATE aster_core)
|
||||||
|
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
#include "aster/constants.h"
|
|
||||||
#include "aster/context.h"
|
|
||||||
#include "aster/device.h"
|
|
||||||
#include "aster/glfw_context.h"
|
|
||||||
#include "aster/physical_device.h"
|
|
||||||
#include "aster/window.h"
|
|
||||||
|
|
||||||
#include "aster/global.h"
|
|
||||||
#include "aster/swapchain.h"
|
|
||||||
|
|
||||||
constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics |
|
|
||||||
QueueSupportFlagBits::eCompute | QueueSupportFlagBits::ePresent |
|
|
||||||
QueueSupportFlagBits::eTransfer;
|
|
||||||
|
|
||||||
[[nodiscard]] 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
|
|
||||||
FindSuitableDevice(const PhysicalDevices &physicalDevices)
|
|
||||||
{
|
|
||||||
for (auto &physicalDevice : physicalDevices)
|
|
||||||
{
|
|
||||||
if (IsSuitableDevice(&physicalDevice))
|
|
||||||
{
|
|
||||||
return physicalDevice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ERROR("No suitable GPU available on the system.")
|
|
||||||
THEN_ABORT(vk::Result::eErrorUnknown);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int, char **)
|
|
||||||
{
|
|
||||||
MIN_LOG_LEVEL(Logger::LogType::eDebug);
|
|
||||||
|
|
||||||
GlfwContext glfwContext = {};
|
|
||||||
Context context = {"Aster", VERSION};
|
|
||||||
Window window = {"Aster1", &context, {640, 480}};
|
|
||||||
|
|
||||||
PhysicalDevices physicalDevices = {&window, &context};
|
|
||||||
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
|
|
||||||
|
|
||||||
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
|
|
||||||
|
|
||||||
vk::PhysicalDeviceFeatures features = {};
|
|
||||||
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
|
|
||||||
|
|
||||||
Device device = {&context, &deviceToUse, &features, {queueAllocation}, "Primary Device"};
|
|
||||||
|
|
||||||
Swapchain swapchain = {&window, &device, "Primary Chain"};
|
|
||||||
|
|
||||||
while (window.Poll())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,314 @@
|
||||||
|
#include "aster/constants.h"
|
||||||
|
#include "aster/context.h"
|
||||||
|
#include "aster/device.h"
|
||||||
|
#include "aster/glfw_context.h"
|
||||||
|
#include "aster/physical_device.h"
|
||||||
|
#include "aster/window.h"
|
||||||
|
|
||||||
|
#include "aster/global.h"
|
||||||
|
#include "aster/swapchain.h"
|
||||||
|
|
||||||
|
#include <EASTL/array.h>
|
||||||
|
|
||||||
|
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
|
||||||
|
|
||||||
|
bool IsSuitableDevice(const PhysicalDevice *physicalDevice);
|
||||||
|
|
||||||
|
PhysicalDevice FindSuitableDevice(const PhysicalDevices &physicalDevices);
|
||||||
|
|
||||||
|
QueueAllocation FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice);
|
||||||
|
|
||||||
|
struct Frame
|
||||||
|
{
|
||||||
|
const Device *m_Device;
|
||||||
|
vk::CommandPool m_Pool;
|
||||||
|
vk::Fence m_FrameAvailableFence;
|
||||||
|
vk::Semaphore m_ImageAcquireSem;
|
||||||
|
vk::Semaphore m_RenderFinishSem;
|
||||||
|
|
||||||
|
Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount);
|
||||||
|
~Frame();
|
||||||
|
|
||||||
|
u32 BeginFrame(const Swapchain *swapchain, u32 frameIndex) const;
|
||||||
|
[[nodiscard]] vk::CommandBuffer AllocateCommandBuffer() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int, char **)
|
||||||
|
{
|
||||||
|
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
||||||
|
|
||||||
|
GlfwContext glfwContext = {};
|
||||||
|
Context context = {"Aster", VERSION};
|
||||||
|
Window window = {"Aster1", &context, {640, 480}};
|
||||||
|
|
||||||
|
PhysicalDevices physicalDevices = {&window, &context};
|
||||||
|
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
|
||||||
|
|
||||||
|
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
|
||||||
|
|
||||||
|
Features enabledDeviceFeatures = {.m_Vulkan13Features = {.dynamicRendering = vk::True}};
|
||||||
|
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
|
||||||
|
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
|
||||||
|
|
||||||
|
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 1);
|
||||||
|
|
||||||
|
Swapchain swapchain = {&window, &device, "Primary Chain"};
|
||||||
|
|
||||||
|
eastl::fixed_vector<Frame, MAX_FRAMES_IN_FLIGHT> frames;
|
||||||
|
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
|
||||||
|
{
|
||||||
|
frames.emplace_back(&device, queueAllocation.m_Family, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 frameIndex = 0;
|
||||||
|
while (window.Poll())
|
||||||
|
{
|
||||||
|
Frame *currentFrame = &frames[frameIndex];
|
||||||
|
u32 imageIndex = currentFrame->BeginFrame(&swapchain, frameIndex);
|
||||||
|
|
||||||
|
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
|
||||||
|
vk::Image currentImage = swapchain.m_Images[imageIndex];
|
||||||
|
vk::CommandBuffer cmd = currentFrame->AllocateCommandBuffer();
|
||||||
|
|
||||||
|
vk::ImageMemoryBarrier topOfThePipeBarrier = {
|
||||||
|
.oldLayout = vk::ImageLayout::eUndefined,
|
||||||
|
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
|
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
||||||
|
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
||||||
|
.image = currentImage,
|
||||||
|
.subresourceRange =
|
||||||
|
{
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
vk::ImageMemoryBarrier renderToPresentBarrier = {
|
||||||
|
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
|
.newLayout = vk::ImageLayout::ePresentSrcKHR,
|
||||||
|
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
||||||
|
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
||||||
|
.image = currentImage,
|
||||||
|
.subresourceRange =
|
||||||
|
{
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
||||||
|
vk::Result result = cmd.begin(&beginInfo);
|
||||||
|
ERROR_IF(Failed(result), "Command buffer begin failed. Cause: {}", result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||||
|
{}, 0, nullptr, 0, nullptr, 1, &topOfThePipeBarrier);
|
||||||
|
|
||||||
|
// Render
|
||||||
|
vk::RenderingAttachmentInfo attachmentInfo = {
|
||||||
|
.imageView = currentImageView,
|
||||||
|
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
|
.resolveMode = vk::ResolveModeFlagBits::eNone,
|
||||||
|
.loadOp = vk::AttachmentLoadOp::eLoad,
|
||||||
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||||
|
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f},
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::RenderingInfo renderingInfo = {
|
||||||
|
.renderArea = {.offset = {0, 0}, .extent = swapchain.m_Extent},
|
||||||
|
.layerCount = 1,
|
||||||
|
.colorAttachmentCount = 1,
|
||||||
|
.pColorAttachments = &attachmentInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd.beginRendering(&renderingInfo);
|
||||||
|
|
||||||
|
cmd.endRendering();
|
||||||
|
|
||||||
|
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eBottomOfPipe,
|
||||||
|
{}, 0, nullptr, 0, nullptr, 1, &renderToPresentBarrier);
|
||||||
|
|
||||||
|
result = cmd.end();
|
||||||
|
ERROR_IF(Failed(result), "Command buffer end failed. Cause: {}", result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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(&window);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ERROR("Command queue present failed. Cause: {}", result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics |
|
||||||
|
QueueSupportFlagBits::eCompute | QueueSupportFlagBits::ePresent |
|
||||||
|
QueueSupportFlagBits::eTransfer;
|
||||||
|
|
||||||
|
PhysicalDevice
|
||||||
|
FindSuitableDevice(const PhysicalDevices &physicalDevices)
|
||||||
|
{
|
||||||
|
for (auto &physicalDevice : physicalDevices)
|
||||||
|
{
|
||||||
|
if (IsSuitableDevice(&physicalDevice))
|
||||||
|
{
|
||||||
|
return physicalDevice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ERROR("No suitable GPU available on the system.")
|
||||||
|
THEN_ABORT(vk::Result::eErrorUnknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
DEBUG("Frame {} created successfully.", frameCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32
|
||||||
|
Frame::BeginFrame(const Swapchain *swapchain, u32 frameIndex) const
|
||||||
|
{
|
||||||
|
const auto device = m_Device->m_Device;
|
||||||
|
|
||||||
|
vk::Result result = device.waitForFences(1, &m_FrameAvailableFence, VK_TRUE, MaxValue<u64>);
|
||||||
|
ERROR_IF(Failed(result), "Waiting for fence {} failed. Cause: {}", frameIndex, result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
u32 imageIndex;
|
||||||
|
result = device.acquireNextImageKHR(swapchain->m_Swapchain, MaxValue<u64>, m_ImageAcquireSem, nullptr, &imageIndex);
|
||||||
|
ERROR_IF(Failed(result), "Waiting for swapchain image {} failed. Cause: {}", frameIndex, result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
// TODO: Deliberate failure on recreation. Will recreate later.
|
||||||
|
|
||||||
|
result = device.resetFences(1, &m_FrameAvailableFence);
|
||||||
|
ERROR_IF(Failed(result), "Fence {} reset failed. Cause: {}", frameIndex, result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
result = device.resetCommandPool(m_Pool, {});
|
||||||
|
ERROR_IF(Failed(result), "Command pool {} reset failed. Cause: {}", frameIndex, result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
return imageIndex;
|
||||||
|
}
|
||||||
|
vk::CommandBuffer
|
||||||
|
Frame::AllocateCommandBuffer() const
|
||||||
|
{
|
||||||
|
const vk::CommandBufferAllocateInfo allocateInfo = {
|
||||||
|
.commandPool = m_Pool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1};
|
||||||
|
|
||||||
|
vk::CommandBuffer commandBuffer;
|
||||||
|
vk::Result result = m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &commandBuffer);
|
||||||
|
ERROR_IF(Failed(result), "Command buffer allocation failed. Cause: {}", result)
|
||||||
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
return commandBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame::~Frame()
|
||||||
|
{
|
||||||
|
auto result = m_Device->m_Device.waitIdle();
|
||||||
|
ERROR_IF(Failed(result), "Wait idle failed. Cause: {}", result);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue