diff --git a/.gitignore b/.gitignore index 8803005..7d8fa21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ .cache/ build/ +.vs/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f3354bf..67ccac6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,12 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) 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("triangle") diff --git a/CMakePresets.json b/CMakePresets.json index c0ce12b..703d9d8 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -12,6 +12,16 @@ "CMAKE_CXX_COMPILER": "/usr/bin/clang++", "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" + } } ] } diff --git a/aster/device.cpp b/aster/device.cpp index 0dc1336..6ad11f9 100644 --- a/aster/device.cpp +++ b/aster/device.cpp @@ -11,10 +11,12 @@ #include #include -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, - const vk::PhysicalDeviceFeatures *enabledFeatures, +Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures, const eastl::vector &queueAllocations, NameString &&name) : m_Name(std::move(name)) , 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 = { + .pNext = vulkan11Features, .queueCreateInfoCount = Cast(deviceQueueCreateInfos.size()), .pQueueCreateInfos = deviceQueueCreateInfos.data(), .enabledExtensionCount = Cast(DEVICE_EXTENSIONS.size()), .ppEnabledExtensionNames = DEVICE_EXTENSIONS.data(), - .pEnabledFeatures = enabledFeatures, + .pEnabledFeatures = deviceFeatures, }; vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device); @@ -86,4 +97,12 @@ Device::~Device() m_Device.destroy(nullptr); DEBUG("Device '{}' Destroyed", m_Name); 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; } \ No newline at end of file diff --git a/aster/device.h b/aster/device.h index dbc2c09..32b7353 100644 --- a/aster/device.h +++ b/aster/device.h @@ -18,6 +18,14 @@ struct QueueAllocation u32 m_Count; }; +struct Features +{ + vk::PhysicalDeviceFeatures m_Vulkan10Features; + vk::PhysicalDeviceVulkan11Features m_Vulkan11Features; + vk::PhysicalDeviceVulkan12Features m_Vulkan12Features; + vk::PhysicalDeviceVulkan13Features m_Vulkan13Features; +}; + struct Device final { NameString m_Name; @@ -25,7 +33,9 @@ struct Device final vk::Device m_Device = 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 &queueAllocations, NameString &&name); ~Device(); + + [[nodiscard]] vk::Queue GetQueue(u32 familyIndex, u32 queueIndex) const; }; diff --git a/aster/global.cpp b/aster/global.cpp index 7f635df..ec8c699 100644 --- a/aster/global.cpp +++ b/aster/global.cpp @@ -15,14 +15,15 @@ VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE 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]; } void * -operator new[](size_t size, size_t alignment, size_t alignmentOffset, const char *pName, int flags, unsigned debugFlags, - const char *file, int line) +operator new[](size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, const char * /*pName*/, int /*flags*/, + unsigned /*debugFlags*/, const char * /*file*/, int /*line*/) { return new u8[size]; } \ No newline at end of file diff --git a/aster/logger.h b/aster/logger.h index 663c6ea..7869b6d 100644 --- a/aster/logger.h +++ b/aster/logger.h @@ -42,7 +42,6 @@ struct Logger return "[DEBUG]:"; if constexpr (TLogLevel == LogType::eVerbose) return "[VERB]: "; - return ""; } template @@ -59,7 +58,6 @@ struct Logger return ansi_color::White; if constexpr (TLogLevel == LogType::eVerbose) return ansi_color::Blue; - return ansi_color::White; } template diff --git a/aster/swapchain.cpp b/aster/swapchain.cpp index d82c54c..350c92b 100644 --- a/aster/swapchain.cpp +++ b/aster/swapchain.cpp @@ -18,12 +18,7 @@ Swapchain::Swapchain(const Window *window, const Device *device, NameString &&na Swapchain::~Swapchain() { - if (m_Swapchain) - { - m_Device->m_Device.destroy(m_Swapchain, nullptr); - m_Swapchain = nullptr; - DEBUG("Swapchain '{}' destroyed.", m_Name); - } + Cleanup(); } void @@ -61,10 +56,9 @@ Swapchain::Create(const Window *window) } } - vk::Extent2D swapchainExtent; if (surfaceCapabilities.currentExtent.width != MaxValue) { - swapchainExtent = surfaceCapabilities.currentExtent; + m_Extent = surfaceCapabilities.currentExtent; } else { @@ -72,8 +66,8 @@ Swapchain::Create(const Window *window) auto [minWidth, minHeight] = surfaceCapabilities.minImageExtent; auto [maxWidth, maxHeight] = surfaceCapabilities.maxImageExtent; - swapchainExtent.width = glm::clamp(width, minWidth, maxWidth); - swapchainExtent.height = glm::clamp(height, minHeight, maxHeight); + m_Extent.width = glm::clamp(width, minWidth, maxWidth); + m_Extent.height = glm::clamp(height, minHeight, maxHeight); } u32 swapchainImageCount = 3; @@ -90,7 +84,7 @@ Swapchain::Create(const Window *window) .minImageCount = swapchainImageCount, .imageFormat = swapchainFormat, .imageColorSpace = swapchainColorSpace, - .imageExtent = swapchainExtent, + .imageExtent = m_Extent, .imageArrayLayers = 1, .imageUsage = vk::ImageUsageFlagBits::eColorAttachment, .imageSharingMode = vk::SharingMode::eExclusive, @@ -101,11 +95,75 @@ Swapchain::Create(const Window *window) .oldSwapchain = m_Swapchain, }; + vk::Device device = m_Device->m_Device; + 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) THEN_ABORT(result) ELSE_DEBUG("Created Swapchain '{}'", m_Name); + // Irrelevant on the first run. Required for re-creation. + Cleanup(); + 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); + } } \ No newline at end of file diff --git a/aster/swapchain.h b/aster/swapchain.h index faf9c20..904bfb6 100644 --- a/aster/swapchain.h +++ b/aster/swapchain.h @@ -7,6 +7,8 @@ #include "global.h" +#include + struct PhysicalDevice; struct Window; struct Device; @@ -16,8 +18,14 @@ struct Swapchain final const Device *m_Device; vk::SwapchainKHR m_Swapchain; NameString m_Name; + vk::Extent2D m_Extent; + eastl::fixed_vector m_Images; + eastl::fixed_vector m_ImageViews; Swapchain(const Window *window, const Device *device, NameString &&name); ~Swapchain(); void Create(const Window *window); + + private: + void Cleanup(); }; diff --git a/triangle/CMakeLists.txt b/triangle/CMakeLists.txt index 449ca9c..10b097e 100644 --- a/triangle/CMakeLists.txt +++ b/triangle/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) -add_executable(aster_exe "aster.cpp") -add_dependencies(aster_exe aster_core) +add_executable(triangle "triangle.cpp") +add_dependencies(triangle aster_core) -target_link_libraries(aster_exe PRIVATE aster_core) +target_link_libraries(triangle PRIVATE aster_core) diff --git a/triangle/aster.cpp b/triangle/aster.cpp deleted file mode 100644 index 84ae0a3..0000000 --- a/triangle/aster.cpp +++ /dev/null @@ -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; -} diff --git a/triangle/triangle.cpp b/triangle/triangle.cpp new file mode 100644 index 0000000..3e97565 --- /dev/null +++ b/triangle/triangle.cpp @@ -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 + +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 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); + ERROR_IF(Failed(result), "Waiting for fence {} failed. Cause: {}", frameIndex, result) + THEN_ABORT(result); + + u32 imageIndex; + result = device.acquireNextImageKHR(swapchain->m_Swapchain, MaxValue, 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; +} \ No newline at end of file