vulkan-test/src/main.cpp

1017 lines
35 KiB
C++
Raw Normal View History

2024-09-30 00:57:13 -04:00
#include <fmt/format.h>
2024-10-01 18:30:31 -04:00
#include <fstream>
#include <glm/glm.hpp>
2024-09-28 18:13:24 -04:00
#include <iostream>
2024-09-30 00:57:13 -04:00
#include <set>
2024-09-25 23:03:56 -04:00
2024-10-06 12:26:44 -04:00
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
2024-09-30 21:21:38 -04:00
#define VK_ENABLE_BETA_EXTENSIONS
2024-09-29 23:02:04 -04:00
#define VULKAN_HPP_NO_CONSTRUCTORS
#include <vulkan/vulkan.hpp>
2024-10-06 12:26:44 -04:00
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
2024-09-28 19:38:13 -04:00
#include "util/types.h"
2024-10-05 23:08:12 -04:00
#define VKFW_NO_STD_FUNCTION_CALLBACKS
#include "vkfw.hpp"
2024-09-25 23:03:56 -04:00
constexpr i32 WIDTH = 800;
constexpr i32 HEIGHT = 600;
2024-09-25 23:03:56 -04:00
2024-10-05 23:08:12 -04:00
constexpr i32 MAX_FRAMES_IN_FLIGHT = 2;
2024-10-10 18:51:20 -04:00
struct Vertex {
glm::vec2 pos;
glm::vec3 color;
static fn getBindingDescription() -> vk::VertexInputBindingDescription {
vk::VertexInputBindingDescription bindingDescription { .binding = 0,
.stride = sizeof(Vertex),
.inputRate = vk::VertexInputRate::eVertex };
return bindingDescription;
}
static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 2> {
std::array<vk::VertexInputAttributeDescription, 2> attributeDescriptions {};
attributeDescriptions[0] = {
.location = 0, .binding = 0, .format = vk::Format::eR32G32Sfloat, .offset = offsetof(Vertex, pos)
};
attributeDescriptions[1] = {
.location = 1, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, .offset = offsetof(Vertex, color)
};
return attributeDescriptions;
}
};
constexpr std::array<Vertex, 4> vertices = {
{ { { -0.5F, -0.5F }, { 1.0F, 0.0F, 0.0F } },
{ { 0.5F, -0.5F }, { 0.0F, 1.0F, 0.0F } },
{ { 0.5F, 0.5F }, { 0.0F, 0.0F, 1.0F } },
{ { -0.5F, 0.5F }, { 1.0F, 1.0F, 1.0F } } }
};
constexpr std::array<u16, 6> indices = { 0, 1, 2, 2, 3, 0 };
2024-09-29 23:02:04 -04:00
constexpr std::array<const char*, 1> validationLayers = { "VK_LAYER_KHRONOS_validation" };
2024-09-25 23:03:56 -04:00
#ifdef __APPLE__
constexpr std::array<const char*, 2> deviceExtensions = { vk::KHRSwapchainExtensionName,
vk::KHRPortabilitySubsetExtensionName };
#else
constexpr std::array<const char*, 1> deviceExtensions = { vk::KHRSwapchainExtensionName };
#endif
2024-09-26 19:56:19 -04:00
#ifdef NDEBUG
2024-09-29 23:02:04 -04:00
constexpr bool enableValidationLayers = false;
2024-09-25 23:03:56 -04:00
#else
2024-09-29 23:02:04 -04:00
constexpr bool enableValidationLayers = true;
2024-09-25 23:03:56 -04:00
#endif
2024-09-29 23:11:12 -04:00
class VulkanApp {
2024-09-25 23:03:56 -04:00
public:
2024-09-30 00:57:13 -04:00
fn run() -> void {
2024-09-25 23:03:56 -04:00
initWindow();
initVulkan();
mainLoop();
}
private:
2024-09-30 16:46:17 -04:00
vkfw::UniqueInstance mGLFWInstance;
vkfw::UniqueWindow mWindow;
2024-09-28 18:13:24 -04:00
2024-09-29 23:02:04 -04:00
vk::UniqueInstance mInstance;
2024-09-25 23:03:56 -04:00
2024-10-06 12:26:44 -04:00
vk::UniqueDebugUtilsMessengerEXT mDebugMessenger;
vk::UniqueSurfaceKHR mSurface;
2024-09-30 00:31:08 -04:00
vk::PhysicalDevice mPhysicalDevice;
2024-09-30 00:57:13 -04:00
vk::UniqueDevice mDevice;
vk::Queue mGraphicsQueue;
vk::Queue mPresentQueue;
2024-10-01 18:54:41 -04:00
vk::UniqueSwapchainKHR mSwapChain;
std::vector<vk::Image> mSwapChainImages;
vk::Format mSwapChainImageFormat;
vk::Extent2D mSwapChainExtent;
std::vector<vk::UniqueImageView> mSwapChainImageViews;
std::vector<vk::UniqueFramebuffer> mSwapChainFramebuffers;
2024-10-01 16:57:40 -04:00
2024-10-01 18:30:31 -04:00
vk::UniqueRenderPass mRenderPass;
vk::UniquePipelineLayout mPipelineLayout;
vk::UniquePipeline mGraphicsPipeline;
2024-10-05 23:08:12 -04:00
vk::UniqueCommandPool mCommandPool;
std::vector<vk::UniqueCommandBuffer> mCommandBuffers;
std::vector<vk::UniqueSemaphore> mImageAvailableSemaphores;
std::vector<vk::UniqueSemaphore> mRenderFinishedSemaphores;
std::vector<vk::UniqueFence> mInFlightFences;
2024-10-01 18:54:41 -04:00
vk::UniqueBuffer mVertexBuffer;
vk::UniqueDeviceMemory mVertexBufferMemory;
2024-10-10 18:51:20 -04:00
vk::UniqueBuffer mIndexBuffer;
vk::UniqueDeviceMemory mIndexBufferMemory;
2024-10-05 23:08:12 -04:00
bool mFramebufferResized = false;
u32 mCurrentFrame = 0;
2024-10-01 18:33:05 -04:00
2024-09-30 00:31:08 -04:00
struct QueueFamilyIndices {
std::optional<u32> graphics_family;
2024-09-30 00:57:13 -04:00
std::optional<u32> present_family;
2024-09-30 00:31:08 -04:00
2024-09-30 00:57:13 -04:00
fn isComplete() -> bool { return graphics_family.has_value() && present_family.has_value(); }
2024-09-30 00:31:08 -04:00
};
2024-09-28 21:55:26 -04:00
struct SwapChainSupportDetails {
vk::SurfaceCapabilitiesKHR capabilities;
std::vector<vk::SurfaceFormatKHR> formats;
std::vector<vk::PresentModeKHR> present_modes;
};
2024-10-01 18:30:31 -04:00
static fn readFile(const std::string& filename) -> std::vector<char> {
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if (!file.is_open())
throw std::runtime_error("Failed to open file! " + filename);
usize fileSize = static_cast<usize>(file.tellg());
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), static_cast<std::streamsize>(fileSize));
file.close();
return buffer;
}
2024-09-30 00:57:13 -04:00
fn initWindow() -> void {
mGLFWInstance = vkfw::initUnique();
2024-09-25 23:03:56 -04:00
vkfw::WindowHints hints;
2024-09-25 23:03:56 -04:00
hints.clientAPI = vkfw::ClientAPI::eNone;
2024-09-30 16:46:17 -04:00
mWindow = vkfw::createWindowUnique(WIDTH, HEIGHT, "Vulkan", hints);
2024-10-05 23:08:12 -04:00
mWindow->setUserPointer(this);
mWindow->setFramebufferSizeCallback(framebufferResizeCallback);
}
static fn framebufferResizeCallback(GLFWwindow* window, int /*width*/, int /*height*/) -> void {
auto* app = std::bit_cast<VulkanApp*>(glfwGetWindowUserPointer(window));
app->mFramebufferResized = true;
2024-09-25 23:03:56 -04:00
}
2024-09-30 00:57:13 -04:00
fn initVulkan() -> void {
2024-09-28 18:13:24 -04:00
createInstance();
setupDebugMessenger();
2024-09-30 00:57:13 -04:00
createSurface();
2024-09-30 00:31:08 -04:00
pickPhysicalDevice();
2024-09-30 00:57:13 -04:00
createLogicalDevice();
2024-10-01 16:57:40 -04:00
createSwapChain();
2024-10-01 17:06:14 -04:00
createImageViews();
2024-10-01 18:30:31 -04:00
createRenderPass();
createGraphicsPipeline();
2024-10-01 18:33:05 -04:00
createFramebuffers();
2024-10-01 18:54:41 -04:00
createCommandPool();
createVertexBuffer();
2024-10-10 18:51:20 -04:00
createIndexBuffer();
2024-10-05 23:08:12 -04:00
createCommandBuffers();
2024-10-01 18:54:41 -04:00
createSyncObjects();
2024-09-29 23:02:04 -04:00
}
2024-09-28 14:54:39 -04:00
2024-09-30 00:57:13 -04:00
fn mainLoop() -> void {
2024-10-01 18:54:41 -04:00
while (!mWindow->shouldClose()) {
vkfw::waitEvents();
drawFrame();
}
2024-10-06 18:14:15 -04:00
mDevice->waitIdle();
2024-09-28 14:54:39 -04:00
}
2024-10-05 23:08:12 -04:00
fn cleanupSwapChain() -> void {
2024-10-06 12:26:44 -04:00
for (vk::UniqueFramebuffer& mSwapChainFramebuffer : mSwapChainFramebuffers) {
2024-10-05 23:08:12 -04:00
mSwapChainFramebuffer.reset();
}
2024-10-06 12:26:44 -04:00
for (vk::UniqueImageView& mSwapChainImageView : mSwapChainImageViews) { mSwapChainImageView.reset(); }
2024-10-05 23:08:12 -04:00
mSwapChain.reset();
}
fn recreateSwapChain() -> void {
u32 width = 0, height = 0;
std::tie(width, height) = mWindow->getFramebufferSize();
while (width == 0 || height == 0) {
std::tie(width, height) = mWindow->getFramebufferSize();
vkfw::waitEvents();
}
2024-10-06 18:14:15 -04:00
mDevice->waitIdle();
2024-10-05 23:08:12 -04:00
cleanupSwapChain();
createSwapChain();
createImageViews();
createFramebuffers();
}
2024-09-25 23:03:56 -04:00
fn createInstance() -> void {
if (enableValidationLayers && !checkValidationLayerSupport())
2024-09-30 21:21:38 -04:00
throw std::runtime_error("Validation layers requested, but not available!");
2024-09-25 23:03:56 -04:00
2024-09-29 20:12:56 -04:00
vk::ApplicationInfo appInfo { .pApplicationName = "Hello Triangle",
.applicationVersion = 1,
.pEngineName = "No Engine",
.engineVersion = 1,
2024-10-06 12:26:44 -04:00
.apiVersion = vk::ApiVersion12 };
2024-09-25 23:03:56 -04:00
2024-09-29 20:12:56 -04:00
// Retrieve extensions using custom function
2024-09-28 18:13:24 -04:00
std::vector<const char*> extensions = getRequiredExtensions();
2024-09-26 19:56:19 -04:00
#ifdef __APPLE__
2024-09-30 00:31:08 -04:00
// Enable the portability extension and set flags
2024-10-01 14:15:39 -04:00
extensions.emplace_back(vk::KHRPortabilityEnumerationExtensionName);
// Technically deprecated but vulkan complains if I don't include it for macOS
// So instead of using the vk::KHRPortabilitySubsetExtensionName, I just use
// the direct string.
extensions.emplace_back("VK_KHR_get_physical_device_properties2");
2024-09-30 21:21:38 -04:00
#endif
2024-09-30 00:31:08 -04:00
2024-09-29 23:05:15 -04:00
vk::InstanceCreateInfo createInfo {
2024-10-01 16:57:40 -04:00
#ifdef __APPLE__
.flags = vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR,
#endif
2024-09-30 00:31:08 -04:00
.pApplicationInfo = &appInfo,
.enabledLayerCount = enableValidationLayers ? static_cast<u32>(validationLayers.size()) : 0,
2024-09-30 00:31:08 -04:00
.ppEnabledLayerNames = enableValidationLayers ? validationLayers.data() : nullptr,
.enabledExtensionCount = static_cast<u32>(extensions.size()),
2024-09-30 00:31:08 -04:00
.ppEnabledExtensionNames = extensions.data()
2024-09-29 23:05:15 -04:00
};
2024-09-26 17:18:45 -04:00
2024-09-30 00:31:08 -04:00
#ifndef NDEBUG
2024-09-29 23:02:04 -04:00
fmt::println("Available extensions:");
for (const char* extension : extensions) fmt::println("\t{}", extension);
2024-09-30 00:31:08 -04:00
#endif
2024-09-25 23:03:56 -04:00
2024-10-06 18:14:15 -04:00
mInstance = vk::createInstanceUnique(createInfo);
2024-10-06 17:34:25 -04:00
2024-10-06 12:26:44 -04:00
VULKAN_HPP_DEFAULT_DISPATCHER.init(mInstance.get());
2024-09-28 14:54:39 -04:00
}
2024-09-30 00:57:13 -04:00
fn setupDebugMessenger() -> void {
2024-09-30 00:31:08 -04:00
if (!enableValidationLayers)
return;
2024-09-28 18:13:24 -04:00
2024-09-30 00:31:08 -04:00
vk::DebugUtilsMessengerCreateInfoEXT messengerCreateInfo {
.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError,
.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
.pfnUserCallback = debugCallback,
};
2024-09-28 18:13:24 -04:00
2024-10-06 18:14:15 -04:00
mDebugMessenger = mInstance->createDebugUtilsMessengerEXTUnique(messengerCreateInfo, nullptr);
2024-09-30 00:31:08 -04:00
}
2024-09-28 18:13:24 -04:00
2024-09-30 16:46:17 -04:00
fn createSurface() -> void { mSurface = vkfw::createWindowSurfaceUnique(mInstance.get(), mWindow.get()); }
2024-09-30 00:57:13 -04:00
2024-09-30 00:31:08 -04:00
fn pickPhysicalDevice() -> void {
2024-10-06 18:14:15 -04:00
std::vector<vk::PhysicalDevice> devices = mInstance->enumeratePhysicalDevices();
2024-09-30 00:31:08 -04:00
2024-10-06 18:14:15 -04:00
if (devices.empty())
2024-09-30 00:31:08 -04:00
throw std::runtime_error("Failed to find GPUs with Vulkan support!");
#ifndef NDEBUG
fmt::println("Available devices:");
#endif
2024-10-06 18:14:15 -04:00
for (const vk::PhysicalDevice& device : devices) {
2024-09-30 00:31:08 -04:00
#ifndef NDEBUG
vk::PhysicalDeviceProperties properties = device.getProperties();
2024-09-30 19:49:44 -04:00
fmt::println("\t{}", properties.deviceName.data());
2024-09-30 00:31:08 -04:00
#endif
if (isDeviceSuitable(device)) {
mPhysicalDevice = device;
break;
}
2024-09-28 18:13:24 -04:00
}
2024-09-30 00:31:08 -04:00
if (!mPhysicalDevice)
throw std::runtime_error("Failed to find a suitable GPU!");
}
2024-09-30 00:57:13 -04:00
fn createLogicalDevice() -> void {
2024-10-10 18:51:20 -04:00
QueueFamilyIndices qfIndices = findQueueFamilies(mPhysicalDevice);
2024-09-30 00:57:13 -04:00
std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
2024-10-10 18:51:20 -04:00
std::set<u32> uniqueQueueFamilies = { qfIndices.graphics_family.value(),
qfIndices.present_family.value() };
2024-09-30 00:57:13 -04:00
f32 queuePriority = 1.0F;
for (u32 queueFamily : uniqueQueueFamilies) {
vk::DeviceQueueCreateInfo queueCreateInfo { .queueFamilyIndex = queueFamily,
.queueCount = 1,
.pQueuePriorities = &queuePriority };
2024-10-01 14:15:39 -04:00
queueCreateInfos.emplace_back(queueCreateInfo);
2024-09-30 00:57:13 -04:00
}
vk::PhysicalDeviceFeatures deviceFeatures;
vk::DeviceCreateInfo createInfo { .queueCreateInfoCount = static_cast<u32>(queueCreateInfos.size()),
.pQueueCreateInfos = queueCreateInfos.data(),
.enabledExtensionCount = static_cast<u32>(deviceExtensions.size()),
.ppEnabledExtensionNames = deviceExtensions.data(),
2024-09-30 00:57:13 -04:00
.pEnabledFeatures = &deviceFeatures };
2024-10-06 18:14:15 -04:00
mDevice = mPhysicalDevice.createDeviceUnique(createInfo);
2024-10-10 18:51:20 -04:00
mGraphicsQueue = mDevice->getQueue(qfIndices.graphics_family.value(), 0);
mPresentQueue = mDevice->getQueue(qfIndices.present_family.value(), 0);
2024-09-30 00:57:13 -04:00
}
2024-10-01 16:57:40 -04:00
fn createSwapChain() -> void {
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(mPhysicalDevice);
vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
vk::PresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.present_modes);
vk::Extent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
u32 imageCount = swapChainSupport.capabilities.minImageCount + 1;
if (swapChainSupport.capabilities.maxImageCount > 0 &&
imageCount > swapChainSupport.capabilities.maxImageCount)
imageCount = swapChainSupport.capabilities.maxImageCount;
2024-10-10 18:51:20 -04:00
QueueFamilyIndices qfIndices = findQueueFamilies(mPhysicalDevice);
std::array<u32, 2> queueFamilyIndices = { qfIndices.graphics_family.value(),
qfIndices.present_family.value() };
2024-10-01 16:57:40 -04:00
vk::SwapchainCreateInfoKHR createInfo {
.surface = mSurface.get(),
.minImageCount = imageCount,
.imageFormat = surfaceFormat.format,
.imageColorSpace = surfaceFormat.colorSpace,
.imageExtent = extent,
.imageArrayLayers = 1,
.imageUsage = vk::ImageUsageFlagBits::eColorAttachment,
2024-10-10 18:51:20 -04:00
.imageSharingMode = qfIndices.graphics_family != qfIndices.present_family ? vk::SharingMode::eConcurrent
: vk::SharingMode::eExclusive,
.queueFamilyIndexCount =
static_cast<u32>(qfIndices.graphics_family != qfIndices.present_family ? 2 : 0),
2024-10-01 16:57:40 -04:00
.pQueueFamilyIndices =
2024-10-10 18:51:20 -04:00
qfIndices.graphics_family != qfIndices.present_family ? queueFamilyIndices.data() : nullptr,
2024-10-01 16:57:40 -04:00
.preTransform = swapChainSupport.capabilities.currentTransform,
.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque,
.presentMode = presentMode,
2024-10-01 18:30:31 -04:00
.clipped = vk::True,
2024-10-01 16:57:40 -04:00
.oldSwapchain = nullptr,
};
2024-10-06 18:14:15 -04:00
mSwapChain = mDevice->createSwapchainKHRUnique(createInfo);
2024-10-06 17:34:25 -04:00
2024-10-06 18:14:15 -04:00
mSwapChainImages = mDevice->getSwapchainImagesKHR(mSwapChain.get());
2024-10-01 16:57:40 -04:00
mSwapChainImageFormat = surfaceFormat.format;
mSwapChainExtent = extent;
}
2024-10-01 17:06:14 -04:00
fn createImageViews() -> void {
mSwapChainImageViews.resize(mSwapChainImages.size());
for (u32 i = 0; i < mSwapChainImages.size(); i++) {
vk::ImageViewCreateInfo createInfo {
2024-10-06 01:26:41 -04:00
.image = mSwapChainImages[i],
.viewType = vk::ImageViewType::e2D,
.format = mSwapChainImageFormat,
// clang-format off
2024-10-01 17:06:14 -04:00
.components = { .r = vk::ComponentSwizzle::eIdentity,
2024-10-06 01:26:41 -04:00
.g = vk::ComponentSwizzle::eIdentity,
.b = vk::ComponentSwizzle::eIdentity,
.a = vk::ComponentSwizzle::eIdentity },
2024-10-01 17:06:14 -04:00
.subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor,
2024-10-06 01:26:41 -04:00
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1 },
// clang-format on
2024-10-01 17:06:14 -04:00
};
2024-10-06 18:14:15 -04:00
mSwapChainImageViews[i] = mDevice->createImageViewUnique(createInfo);
2024-10-01 17:06:14 -04:00
}
}
2024-10-01 18:30:31 -04:00
fn createRenderPass() -> void {
vk::AttachmentDescription colorAttachment {
.format = mSwapChainImageFormat,
.samples = vk::SampleCountFlagBits::e1,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
.initialLayout = vk::ImageLayout::eUndefined,
.finalLayout = vk::ImageLayout::ePresentSrcKHR,
};
vk::AttachmentReference colorAttachmentRef {
.attachment = 0,
.layout = vk::ImageLayout::eColorAttachmentOptimal,
};
vk::SubpassDescription subpass {
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
.colorAttachmentCount = 1,
.pColorAttachments = &colorAttachmentRef,
};
vk::RenderPassCreateInfo renderPassInfo {
.attachmentCount = 1,
.pAttachments = &colorAttachment,
.subpassCount = 1,
.pSubpasses = &subpass,
};
2024-10-06 18:14:15 -04:00
mRenderPass = mDevice->createRenderPassUnique(renderPassInfo);
2024-10-01 18:30:31 -04:00
}
fn createGraphicsPipeline() -> void {
std::vector<char> vertShaderCode = readFile("src/shaders/vert.spv");
std::vector<char> fragShaderCode = readFile("src/shaders/frag.spv");
vk::UniqueShaderModule vertShaderModule = createShaderModule(vertShaderCode);
vk::UniqueShaderModule fragShaderModule = createShaderModule(fragShaderCode);
vk::PipelineShaderStageCreateInfo vertShaderStageInfo {
.stage = vk::ShaderStageFlagBits::eVertex,
.module = vertShaderModule.get(),
.pName = "main",
};
vk::PipelineShaderStageCreateInfo fragShaderStageInfo {
.stage = vk::ShaderStageFlagBits::eFragment,
.module = fragShaderModule.get(),
.pName = "main",
};
std::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages = { vertShaderStageInfo,
fragShaderStageInfo };
vk::VertexInputBindingDescription bindingDescription = Vertex::getBindingDescription();
std::array<vk::VertexInputAttributeDescription, 2> attributeDescriptions =
Vertex::getAttributeDescriptions();
2024-10-01 18:30:31 -04:00
vk::PipelineVertexInputStateCreateInfo vertexInputInfo {
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &bindingDescription,
.vertexAttributeDescriptionCount = static_cast<u32>(attributeDescriptions.size()),
.pVertexAttributeDescriptions = attributeDescriptions.data(),
2024-10-01 18:30:31 -04:00
};
vk::PipelineInputAssemblyStateCreateInfo inputAssembly {
.topology = vk::PrimitiveTopology::eTriangleList,
.primitiveRestartEnable = vk::False,
};
vk::PipelineViewportStateCreateInfo viewportState {
.viewportCount = 1,
.scissorCount = 1,
};
vk::PipelineRasterizationStateCreateInfo rasterizer {
.depthClampEnable = vk::False,
.rasterizerDiscardEnable = vk::False,
.polygonMode = vk::PolygonMode::eFill,
.cullMode = vk::CullModeFlagBits::eBack,
.frontFace = vk::FrontFace::eClockwise,
.depthBiasEnable = vk::False,
.lineWidth = 1.0F,
};
vk::PipelineMultisampleStateCreateInfo multisampling {
.rasterizationSamples = vk::SampleCountFlagBits::e1,
.sampleShadingEnable = vk::False,
};
vk::PipelineColorBlendAttachmentState colorBlendAttachment {
.blendEnable = vk::False,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
vk::PipelineColorBlendStateCreateInfo colorBlending {
.logicOpEnable = vk::False,
.logicOp = vk::LogicOp::eCopy,
.attachmentCount = 1,
.pAttachments = &colorBlendAttachment,
.blendConstants = std::array<float, 4> { 0.0F, 0.0F, 0.0F, 0.0F },
};
std::vector<vk::DynamicState> dynamicStates = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
vk::PipelineDynamicStateCreateInfo dynamicState {
.dynamicStateCount = static_cast<u32>(dynamicStates.size()),
.pDynamicStates = dynamicStates.data(),
};
vk::PipelineLayoutCreateInfo pipelineLayoutInfo {
.setLayoutCount = 0,
.pushConstantRangeCount = 0,
};
2024-10-06 18:14:15 -04:00
mPipelineLayout = mDevice->createPipelineLayoutUnique(pipelineLayoutInfo);
2024-10-01 18:30:31 -04:00
vk::GraphicsPipelineCreateInfo pipelineInfo {
.stageCount = static_cast<u32>(shaderStages.size()),
.pStages = shaderStages.data(),
.pVertexInputState = &vertexInputInfo,
.pInputAssemblyState = &inputAssembly,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pColorBlendState = &colorBlending,
.pDynamicState = &dynamicState,
.layout = mPipelineLayout.get(),
.renderPass = mRenderPass.get(),
.subpass = 0,
};
2024-10-06 17:34:25 -04:00
vk::Result graphicsPipelineResult = vk::Result::eSuccess;
vk::UniquePipeline graphicsPipelineValue;
std::tie(graphicsPipelineResult, graphicsPipelineValue) =
mDevice->createGraphicsPipelineUnique(nullptr, pipelineInfo).asTuple();
if (graphicsPipelineResult != vk::Result::eSuccess)
throw std::runtime_error("Failed to create graphics pipeline!");
mGraphicsPipeline = std::move(graphicsPipelineValue);
2024-10-01 18:30:31 -04:00
}
2024-10-01 18:33:05 -04:00
fn createFramebuffers() -> void {
mSwapChainFramebuffers.resize(mSwapChainImageViews.size());
for (usize i = 0; i < mSwapChainImageViews.size(); i++) {
vk::FramebufferCreateInfo framebufferInfo {
.renderPass = mRenderPass.get(),
.attachmentCount = 1,
.pAttachments = &mSwapChainImageViews[i].get(),
.width = mSwapChainExtent.width,
.height = mSwapChainExtent.height,
.layers = 1,
};
2024-10-06 18:14:15 -04:00
mSwapChainFramebuffers[i] = mDevice->createFramebufferUnique(framebufferInfo);
2024-10-01 18:33:05 -04:00
}
}
2024-10-01 18:54:41 -04:00
fn createCommandPool() -> void {
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(mPhysicalDevice);
vk::CommandPoolCreateInfo poolInfo {
.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
.queueFamilyIndex = queueFamilyIndices.graphics_family.value(),
};
2024-10-06 18:14:15 -04:00
mCommandPool = mDevice->createCommandPoolUnique(poolInfo);
2024-10-01 18:54:41 -04:00
}
fn createVertexBuffer() -> void {
2024-10-10 18:51:20 -04:00
vk::DeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
2024-10-10 16:04:46 -04:00
vk::UniqueBuffer stagingBuffer;
vk::UniqueDeviceMemory stagingBufferMemory;
createBuffer(
bufferSize,
vk::BufferUsageFlagBits::eVertexBuffer,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
stagingBuffer,
stagingBufferMemory
);
void* data = mDevice->mapMemory(stagingBufferMemory.get(), 0, bufferSize);
2024-10-10 18:51:20 -04:00
memcpy(data, vertices.data(), static_cast<usize>(bufferSize));
2024-10-10 16:04:46 -04:00
mDevice->unmapMemory(stagingBufferMemory.get());
createBuffer(
bufferSize,
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst,
vk::MemoryPropertyFlagBits::eDeviceLocal,
mVertexBuffer,
mVertexBufferMemory
);
copyBuffer(stagingBuffer.get(), mVertexBuffer.get(), bufferSize);
stagingBuffer.reset();
stagingBufferMemory.reset();
}
2024-10-10 18:51:20 -04:00
fn createIndexBuffer() -> void {
vk::DeviceSize bufferSize = sizeof(indices[0]) * indices.size();
vk::UniqueBuffer stagingBuffer;
vk::UniqueDeviceMemory stagingBufferMemory;
createBuffer(
bufferSize,
vk::BufferUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
stagingBuffer,
stagingBufferMemory
);
void* data = mDevice->mapMemory(stagingBufferMemory.get(), 0, bufferSize);
memcpy(data, indices.data(), static_cast<usize>(bufferSize));
mDevice->unmapMemory(stagingBufferMemory.get());
createBuffer(
bufferSize,
vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst,
vk::MemoryPropertyFlagBits::eDeviceLocal,
mIndexBuffer,
mIndexBufferMemory
);
copyBuffer(stagingBuffer.get(), mIndexBuffer.get(), bufferSize);
stagingBuffer.reset();
stagingBufferMemory.reset();
}
2024-10-10 16:04:46 -04:00
fn createBuffer(
vk::DeviceSize deviceSize,
vk::BufferUsageFlags bufferUsageFlags,
vk::MemoryPropertyFlags memoryPropertyFlags,
vk::UniqueBuffer& buffer,
vk::UniqueDeviceMemory& bufferMemory
) -> void {
vk::BufferCreateInfo bufferInfo {
2024-10-10 16:04:46 -04:00
.size = deviceSize,
.usage = bufferUsageFlags,
.sharingMode = vk::SharingMode::eExclusive,
};
2024-10-10 16:04:46 -04:00
buffer = mDevice->createBufferUnique(bufferInfo);
2024-10-10 16:04:46 -04:00
vk::MemoryRequirements memRequirements = mDevice->getBufferMemoryRequirements(buffer.get());
vk::MemoryAllocateInfo allocInfo {
.allocationSize = memRequirements.size,
2024-10-10 16:04:46 -04:00
.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, memoryPropertyFlags),
};
bufferMemory = mDevice->allocateMemoryUnique(allocInfo);
mDevice->bindBufferMemory(buffer.get(), bufferMemory.get(), 0);
}
fn copyBuffer(vk::Buffer srcBuffer, vk::Buffer dstBuffer, vk::DeviceSize deviceSize) -> void {
vk::CommandBufferAllocateInfo allocInfo {
.commandPool = mCommandPool.get(),
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
2024-10-10 16:04:46 -04:00
vk::UniqueCommandBuffer commandBuffer = std::move(mDevice->allocateCommandBuffersUnique(allocInfo)[0]);
vk::CommandBufferBeginInfo beginInfo { .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit };
commandBuffer->begin(beginInfo);
vk::BufferCopy copyRegion { .size = deviceSize };
commandBuffer->copyBuffer(srcBuffer, dstBuffer, 1, &copyRegion);
commandBuffer->end();
2024-10-10 16:04:46 -04:00
vk::SubmitInfo submitInfo { .commandBufferCount = 1, .pCommandBuffers = &commandBuffer.get() };
2024-10-10 16:04:46 -04:00
mGraphicsQueue.submit(submitInfo, nullptr);
mGraphicsQueue.waitIdle();
}
fn findMemoryType(u32 typeFilter, vk::MemoryPropertyFlags properties) -> u32 {
vk::PhysicalDeviceMemoryProperties memProperties = mPhysicalDevice.getMemoryProperties();
for (u32 i = 0; i < memProperties.memoryTypeCount; i++)
if ((typeFilter & (1 << i)) &&
(memProperties.memoryTypes.at(i).propertyFlags & properties) == properties)
return i;
throw std::runtime_error("Failed to find a suitable memory type!");
}
2024-10-05 23:08:12 -04:00
fn createCommandBuffers() -> void {
mCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
vk::CommandBufferAllocateInfo allocInfo { .commandPool = mCommandPool.get(),
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount =
static_cast<u32>(mCommandBuffers.size()) };
2024-10-01 18:54:41 -04:00
2024-10-06 18:14:15 -04:00
mCommandBuffers = mDevice->allocateCommandBuffersUnique(allocInfo);
2024-10-01 18:54:41 -04:00
}
fn recordCommandBuffer(vk::CommandBuffer commandBuffer, u32 imageIndex) -> void {
vk::CommandBufferBeginInfo beginInfo {};
2024-10-06 18:14:15 -04:00
commandBuffer.begin(beginInfo);
2024-10-01 18:54:41 -04:00
2024-10-01 19:00:31 -04:00
vk::ClearValue clearColor { .color = { .float32 = std::array<float, 4> { 0.0F, 0.0F, 0.0F, 1.0F } } };
2024-10-01 18:54:41 -04:00
vk::RenderPassBeginInfo renderPassInfo {
.renderPass = mRenderPass.get(),
.framebuffer = mSwapChainFramebuffers[imageIndex].get(),
2024-10-01 19:00:31 -04:00
.renderArea = { .offset = { .x = 0, .y = 0 }, .extent = mSwapChainExtent },
2024-10-01 18:54:41 -04:00
.clearValueCount = 1,
.pClearValues = &clearColor,
};
commandBuffer.beginRenderPass(renderPassInfo, vk::SubpassContents::eInline);
commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mGraphicsPipeline.get());
vk::Viewport viewport {
.x = 0.0F,
.y = 0.0F,
.width = static_cast<f32>(mSwapChainExtent.width),
.height = static_cast<f32>(mSwapChainExtent.height),
.minDepth = 0.0F,
.maxDepth = 1.0F,
};
2024-10-01 18:54:41 -04:00
vk::Rect2D scissor {
.offset = { 0, 0 },
.extent = mSwapChainExtent,
};
2024-10-01 19:00:31 -04:00
commandBuffer.setViewport(0, viewport);
2024-10-01 18:54:41 -04:00
commandBuffer.setScissor(0, scissor);
commandBuffer.bindVertexBuffers(0, mVertexBuffer.get(), { 0 });
2024-10-10 18:51:20 -04:00
commandBuffer.bindIndexBuffer(mIndexBuffer.get(), 0, vk::IndexType::eUint16);
commandBuffer.drawIndexed(static_cast<u32>(indices.size()), 1, 0, 0, 0);
2024-10-01 18:54:41 -04:00
commandBuffer.endRenderPass();
2024-10-06 18:14:15 -04:00
commandBuffer.end();
2024-10-01 18:54:41 -04:00
}
fn createSyncObjects() -> void {
2024-10-05 23:08:12 -04:00
mImageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
mRenderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
mInFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
2024-10-01 18:54:41 -04:00
vk::SemaphoreCreateInfo semaphoreInfo {};
vk::FenceCreateInfo fenceInfo { .flags = vk::FenceCreateFlagBits::eSignaled };
2024-10-05 23:08:12 -04:00
for (usize idx = 0; idx < MAX_FRAMES_IN_FLIGHT; idx++) {
2024-10-06 18:14:15 -04:00
mImageAvailableSemaphores[idx] = mDevice->createSemaphoreUnique(semaphoreInfo);
mRenderFinishedSemaphores[idx] = mDevice->createSemaphoreUnique(semaphoreInfo);
mInFlightFences[idx] = mDevice->createFenceUnique(fenceInfo);
2024-10-05 23:08:12 -04:00
}
2024-10-01 18:54:41 -04:00
}
fn drawFrame() -> void {
2024-10-06 18:34:42 -04:00
try {
vk::Result result =
mDevice->waitForFences(mInFlightFences[mCurrentFrame].get(), vk::Bool32(vk::True), UINT64_MAX);
2024-10-01 18:54:41 -04:00
2024-10-06 18:34:42 -04:00
if (result != vk::Result::eSuccess)
throw std::runtime_error("Failed to wait for fences!");
2024-10-01 18:54:41 -04:00
2024-10-06 18:34:42 -04:00
vk::Result imageIndexResult = vk::Result::eSuccess;
u32 imageIndexValue = 0;
2024-10-06 17:34:25 -04:00
2024-10-06 18:34:42 -04:00
std::tie(imageIndexResult, imageIndexValue) = mDevice->acquireNextImageKHR(
mSwapChain.get(), UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame].get(), nullptr
);
2024-10-05 23:08:12 -04:00
2024-10-06 18:34:42 -04:00
if (imageIndexResult == vk::Result::eErrorOutOfDateKHR) {
recreateSwapChain();
return;
}
2024-10-05 23:08:12 -04:00
2024-10-06 18:34:42 -04:00
if (imageIndexResult != vk::Result::eSuccess && imageIndexResult != vk::Result::eSuboptimalKHR)
throw std::runtime_error("Failed to acquire swap chain image!");
2024-10-01 18:54:41 -04:00
2024-10-06 18:34:42 -04:00
mDevice->resetFences(mInFlightFences[mCurrentFrame].get());
2024-10-01 18:54:41 -04:00
2024-10-06 18:34:42 -04:00
mCommandBuffers[mCurrentFrame]->reset(vk::CommandBufferResetFlagBits::eReleaseResources);
recordCommandBuffer(mCommandBuffers[mCurrentFrame].get(), imageIndexValue);
2024-10-01 18:54:41 -04:00
2024-10-06 18:34:42 -04:00
std::array<vk::PipelineStageFlags, 1> waitStages = {
vk::PipelineStageFlagBits::eColorAttachmentOutput
};
2024-10-01 18:54:41 -04:00
2024-10-06 18:34:42 -04:00
vk::SubmitInfo submitInfo {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &mImageAvailableSemaphores[mCurrentFrame].get(),
.pWaitDstStageMask = waitStages.data(),
.commandBufferCount = 1,
.pCommandBuffers = &mCommandBuffers[mCurrentFrame].get(),
.signalSemaphoreCount = 1,
.pSignalSemaphores = &mRenderFinishedSemaphores[mCurrentFrame].get(),
};
2024-10-01 18:54:41 -04:00
2024-10-06 18:34:42 -04:00
mGraphicsQueue.submit(submitInfo, mInFlightFences[mCurrentFrame].get());
2024-10-01 18:54:41 -04:00
2024-10-06 18:34:42 -04:00
vk::PresentInfoKHR presentInfo {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &mRenderFinishedSemaphores[mCurrentFrame].get(),
.swapchainCount = 1,
.pSwapchains = &mSwapChain.get(),
.pImageIndices = &imageIndexValue,
};
2024-10-01 18:54:41 -04:00
2024-10-06 18:14:15 -04:00
vk::Result presentResult = mPresentQueue.presentKHR(presentInfo);
fmt::println("Present result: {}", vk::to_string(presentResult));
2024-10-01 19:00:31 -04:00
2024-10-06 18:14:15 -04:00
if (presentResult == vk::Result::eErrorOutOfDateKHR || presentResult == vk::Result::eSuboptimalKHR ||
mFramebufferResized) {
mFramebufferResized = false;
recreateSwapChain();
2024-10-07 16:04:49 -04:00
} else if (presentResult != vk::Result::eSuccess)
2024-10-06 18:14:15 -04:00
throw std::runtime_error("Failed to present swap chain image!");
mCurrentFrame = (mCurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
2024-10-07 16:04:49 -04:00
} catch (vk::OutOfDateKHRError& /*err*/) {
2024-10-05 23:08:12 -04:00
mFramebufferResized = false;
recreateSwapChain();
2024-10-06 18:14:15 -04:00
return;
2024-10-05 23:08:12 -04:00
}
2024-10-01 18:54:41 -04:00
}
2024-10-01 18:30:31 -04:00
fn createShaderModule(const std::vector<char>& code) -> vk::UniqueShaderModule {
vk::ShaderModuleCreateInfo createInfo { .codeSize = code.size(),
.pCode = std::bit_cast<const u32*>(code.data()) };
2024-10-06 18:14:15 -04:00
return mDevice->createShaderModuleUnique(createInfo);
2024-10-01 18:30:31 -04:00
}
2024-10-05 23:08:12 -04:00
static fn chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& availableFormats
) -> vk::SurfaceFormatKHR {
2024-10-01 16:57:40 -04:00
for (const auto& availableFormat : availableFormats)
if (availableFormat.format == vk::Format::eB8G8R8A8Srgb &&
availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
return availableFormat;
return availableFormats[0];
}
2024-10-05 23:08:12 -04:00
static fn chooseSwapPresentMode(const std::vector<vk::PresentModeKHR>& availablePresentModes
) -> vk::PresentModeKHR {
2024-10-01 16:57:40 -04:00
for (const auto& availablePresentMode : availablePresentModes)
if (availablePresentMode == vk::PresentModeKHR::eMailbox)
return availablePresentMode;
return vk::PresentModeKHR::eFifo;
}
fn chooseSwapExtent(const vk::SurfaceCapabilitiesKHR capabilities) -> vk::Extent2D {
if (capabilities.currentExtent.width != UINT32_MAX)
return capabilities.currentExtent;
u32 width = 0, height = 0;
std::tie(width, height) = mWindow->getFramebufferSize();
2024-10-06 16:19:29 -04:00
vk::Extent2D actualExtent = { width, height };
actualExtent.width =
std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
actualExtent.height =
std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
2024-10-01 16:57:40 -04:00
return actualExtent;
}
2024-10-01 17:06:14 -04:00
fn querySwapChainSupport(vk::PhysicalDevice device) -> SwapChainSupportDetails {
SwapChainSupportDetails details;
2024-10-06 18:14:15 -04:00
details.capabilities = device.getSurfaceCapabilitiesKHR(mSurface.get());
details.formats = device.getSurfaceFormatsKHR(mSurface.get());
details.present_modes = device.getSurfacePresentModesKHR(mSurface.get());
2024-10-01 17:06:14 -04:00
return details;
}
fn isDeviceSuitable(vk::PhysicalDevice device) -> bool {
2024-10-10 18:51:20 -04:00
QueueFamilyIndices qfIndices = findQueueFamilies(device);
2024-10-01 17:06:14 -04:00
bool extensionsSupported = checkDeviceExtensionSupport(device);
bool swapChainAdequate = false;
if (extensionsSupported) {
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.present_modes.empty();
}
2024-10-10 18:51:20 -04:00
return qfIndices.isComplete() && extensionsSupported && swapChainAdequate;
2024-10-01 17:06:14 -04:00
}
static fn checkDeviceExtensionSupport(vk::PhysicalDevice device) -> bool {
2024-10-06 18:14:15 -04:00
std::vector<vk::ExtensionProperties> availableExtensions = device.enumerateDeviceExtensionProperties();
2024-10-01 17:06:14 -04:00
std::set<string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
2024-10-06 18:14:15 -04:00
for (const vk::ExtensionProperties& extension : availableExtensions)
2024-10-06 17:34:25 -04:00
requiredExtensions.erase(extension.extensionName);
2024-10-01 17:06:14 -04:00
return requiredExtensions.empty();
}
fn findQueueFamilies(vk::PhysicalDevice device) -> QueueFamilyIndices {
2024-10-10 18:51:20 -04:00
QueueFamilyIndices qfIndices;
2024-10-01 17:06:14 -04:00
std::vector<vk::QueueFamilyProperties> queueFamilies = device.getQueueFamilyProperties();
for (u32 i = 0; i < queueFamilies.size(); i++) {
if (queueFamilies[i].queueFlags & vk::QueueFlagBits::eGraphics)
2024-10-10 18:51:20 -04:00
qfIndices.graphics_family = i;
2024-10-01 17:06:14 -04:00
2024-10-06 18:14:15 -04:00
vk::Bool32 queuePresentSupport = device.getSurfaceSupportKHR(i, mSurface.get());
2024-10-01 17:06:14 -04:00
2024-10-06 18:14:15 -04:00
if (queuePresentSupport)
2024-10-10 18:51:20 -04:00
qfIndices.present_family = i;
2024-10-01 17:06:14 -04:00
2024-10-10 18:51:20 -04:00
if (qfIndices.isComplete())
2024-10-01 17:06:14 -04:00
break;
}
2024-10-10 18:51:20 -04:00
return qfIndices;
2024-10-01 17:06:14 -04:00
}
2024-09-28 18:13:24 -04:00
static fn getRequiredExtensions() -> std::vector<const char*> {
2024-09-30 16:46:17 -04:00
std::span<const char*> extensionsSpan = vkfw::getRequiredInstanceExtensions();
2024-09-28 18:13:24 -04:00
2024-09-30 16:46:17 -04:00
std::vector extensions(extensionsSpan.begin(), extensionsSpan.end());
2024-09-28 18:13:24 -04:00
2024-09-28 14:54:39 -04:00
if (enableValidationLayers)
2024-10-01 14:15:39 -04:00
extensions.emplace_back(vk::EXTDebugUtilsExtensionName);
2024-09-28 14:54:39 -04:00
2024-09-28 18:13:24 -04:00
return extensions;
}
2024-09-30 00:31:08 -04:00
static fn checkValidationLayerSupport() -> bool {
2024-10-06 18:14:15 -04:00
std::vector<vk::LayerProperties> availableLayers = vk::enumerateInstanceLayerProperties();
2024-09-30 00:31:08 -04:00
for (const char* layerName : validationLayers) {
bool layerFound = false;
2024-10-06 18:14:15 -04:00
for (const vk::LayerProperties& layerProperties : availableLayers)
2024-09-30 00:31:08 -04:00
if (strcmp(layerName, layerProperties.layerName) == 0) {
layerFound = true;
break;
}
if (!layerFound)
return false;
}
return true;
}
2024-09-28 18:13:24 -04:00
static VKAPI_ATTR fn VKAPI_CALL debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT /*messageSeverity*/,
VkDebugUtilsMessageTypeFlagsEXT /*messageType*/,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* /*pUserData*/
2024-09-30 00:57:13 -04:00
) -> vk::Bool32 {
2024-09-29 23:02:04 -04:00
fmt::println("Validation layer: {}", pCallbackData->pMessage);
2024-09-28 18:13:24 -04:00
2024-09-30 21:34:25 -04:00
return vk::False;
2024-09-25 23:03:56 -04:00
}
};
2024-09-28 21:55:26 -04:00
fn main() -> i32 {
2024-10-06 12:26:44 -04:00
vk::DynamicLoader dynamicLoader;
auto vkGetInstanceProcAddr =
dynamicLoader.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
2024-09-29 23:11:12 -04:00
VulkanApp app;
2024-09-25 23:03:56 -04:00
try {
app.run();
} catch (const std::exception& e) {
2024-10-05 23:08:12 -04:00
fmt::println("{}", e.what());
2024-09-25 23:03:56 -04:00
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}