VulkanGraphics/main.cpp
2024-11-08 15:13:13 +03:00

714 lines
No EOL
22 KiB
C++

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <stdexcept>
#include <cstdlib>
#include <vector>
#include <cstring>
#include <fstream>
const std::vector<const char*> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
const std::vector<const char*> validationLayers = {
#ifdef NDEBUG
#else
"VK_LAYER_KHRONOS_validation"
#endif
};
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities {};
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
class Application {
public:
void run() {
initWindow();
initVulkan();
mainLoop();
cleanup();
}
private:
void initWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
mWindow = glfwCreateWindow(800, 600, "App", nullptr, nullptr);
}
void initVulkan() {
createInstance();
createWindowSurface();
pickPhysicalDevice();
findPhysicalDeviceQueueFamilies();
createLogicalDevice();
getQueues();
createSwapChain();
createSwapChainImageViews();
createRenderPass();
createGraphicsPipeline();
createSwapChainFramebuffers();
}
void mainLoop() {
while(!glfwWindowShouldClose(mWindow)) {
glfwPollEvents();
}
}
void createInstance() {
if (!checkValidationLayerSupport()) {
throw std::runtime_error("no required validation layers present");
}
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
VkApplicationInfo appInfo {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "App",
.applicationVersion = VK_MAKE_VERSION(0, 0, 0),
.pEngineName = "no",
.engineVersion = VK_MAKE_VERSION(0, 0, 0),
.apiVersion = VK_API_VERSION_1_0,
};
VkInstanceCreateInfo createInfo {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo,
.enabledLayerCount = (uint32_t) validationLayers.size(),
.ppEnabledLayerNames = validationLayers.data(),
.enabledExtensionCount = glfwExtensionCount,
.ppEnabledExtensionNames = glfwExtensions,
};
if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!");
}
}
static bool checkValidationLayerSupport() {
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
for (const auto& requestedLayer : validationLayers) {
bool presents = false;
for (auto &layer: availableLayers) {
if (strcmp(requestedLayer, layer.layerName) == 0) {
presents = true;
break;
}
}
if (!presents) return false;
}
return true;
}
void pickPhysicalDevice() {
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr);
if (deviceCount == 0) {
throw std::runtime_error("no gpu with vulkan support");
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
for (const auto& device : devices) {
if (isDeviceSuitable(device)) {
mPhysicalDevice = device;
break;
}
}
if (!mPhysicalDevice) throw std::runtime_error("no suitable gpu");
}
bool isDeviceSuitable(VkPhysicalDevice device) {
VkPhysicalDeviceProperties properties;
VkPhysicalDeviceFeatures features;
vkGetPhysicalDeviceProperties(device, &properties);
vkGetPhysicalDeviceFeatures(device, &features);
// if (properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) return false;
if (!features.geometryShader) return false;
if (!checkDeviceExtensions(device)) return false;
if (!checkDeviceSwapChain(device)) return false;
return true;
}
bool checkDeviceSwapChain(VkPhysicalDevice device) {
SwapChainSupportDetails details = querySwapChainSupportDetails(device);
return !(details.presentModes.empty() || details.formats.empty());
}
static bool checkDeviceExtensions(VkPhysicalDevice device) {
uint32_t extensionsCount = 0;
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, nullptr);
std::vector<VkExtensionProperties> extensions(extensionsCount);
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, extensions.data());
for (const auto& requiredExtension : deviceExtensions) {
bool found = false;
for (const auto& extension : extensions) {
if (strcmp(requiredExtension, extension.extensionName) == 0) {
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
void findPhysicalDeviceQueueFamilies() {
uint32_t queuesCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilyProperties(queuesCount);
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, queueFamilyProperties.data());
int index = 0;
for (const auto& familyProperty : queueFamilyProperties) {
if (familyProperty.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
mGraphicsQueueFamilyIndex = index;
}
VkBool32 presentationQueueSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, index, mSurface, &presentationQueueSupport);
if (presentationQueueSupport) {
mPresentationQueueFamilyIndex = index;
}
index++;
}
if (mPresentationQueueFamilyIndex == -1 || mGraphicsQueueFamilyIndex == -1) {
throw std::runtime_error("nu require queue families found");
}
}
SwapChainSupportDetails querySwapChainSupportDetails(VkPhysicalDevice device) {
SwapChainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &details.capabilities);
uint32_t formatCount = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr);
if (formatCount != 0) {
details.formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, details.formats.data());
}
uint32_t modeCount = 0;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &modeCount, nullptr);
if (modeCount != 0) {
details.presentModes.resize(modeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &modeCount, details.presentModes.data());
}
return details;
}
void createLogicalDevice() {
float queuePriority = 1.f;
VkDeviceQueueCreateInfo graphicsQueueCreateInfos {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = mGraphicsQueueFamilyIndex,
.queueCount = 1,
.pQueuePriorities = &queuePriority,
};
VkDeviceQueueCreateInfo presentationQueueCreateInfos {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = mPresentationQueueFamilyIndex,
.queueCount = 1,
.pQueuePriorities = &queuePriority,
};
std::vector<VkDeviceQueueCreateInfo> queues = { graphicsQueueCreateInfos };
if (mPresentationQueueFamilyIndex != mGraphicsQueueFamilyIndex) {
queues.push_back(presentationQueueCreateInfos);
}
VkPhysicalDeviceFeatures features {};
VkDeviceCreateInfo deviceCreateInfo {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.queueCreateInfoCount = (uint32_t) queues.size(),
.pQueueCreateInfos = queues.data(),
.enabledExtensionCount = (uint32_t) deviceExtensions.size(),
.ppEnabledExtensionNames = deviceExtensions.data(),
.pEnabledFeatures = &features,
};
vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice);
if (mDevice == VK_NULL_HANDLE) throw std::runtime_error("failed to create vulkan logical device");
}
void getQueues() {
vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue);
vkGetDeviceQueue(mDevice, mPresentationQueueFamilyIndex, 0, &mPresentQueue);
}
void createWindowSurface() {
if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) {
throw std::runtime_error("failed to create vulkan window surface");
}
}
void createSwapChain() {
SwapChainSupportDetails details = querySwapChainSupportDetails(mPhysicalDevice);
VkSurfaceFormatKHR surfaceFormat = pickSwapChainSurfaceFormat(details);
VkPresentModeKHR presentMode = pickSwapChainPresentMode(details);
VkExtent2D extent2D = pickSwapChainExtent(details.capabilities);
// +1 so will not have to wait for device to finish frame to query an image to render to
uint32_t imageCount = details.capabilities.minImageCount + 1;
if (details.capabilities.maxImageCount > 0 && imageCount > details.capabilities.maxImageCount) {
imageCount = details.capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfo {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = mSurface,
.minImageCount = imageCount,
.imageFormat = surfaceFormat.format,
.imageExtent = extent2D,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.preTransform = details.capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = presentMode,
.clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE,
};
uint32_t queueIndices[] = { mGraphicsQueueFamilyIndex, mPresentationQueueFamilyIndex };
if (mGraphicsQueueFamilyIndex != mPresentationQueueFamilyIndex) {
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueIndices;
} else {
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
}
if (vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) {
throw std::runtime_error("failed to create swap chain");
}
mSwapChainExtent = extent2D;
mSwapChainFormat = surfaceFormat.format;
uint32_t createdImageCount = 0;
vkGetSwapchainImagesKHR(mDevice, mSwapChain, &createdImageCount, nullptr);
mSwapChainImages.resize(createdImageCount);
vkGetSwapchainImagesKHR(mDevice, mSwapChain, &createdImageCount, mSwapChainImages.data());
}
static VkSurfaceFormatKHR pickSwapChainSurfaceFormat(const SwapChainSupportDetails& details) {
for (const auto& format : details.formats) {
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) {
return format;
}
}
return details.formats.front();
}
static VkPresentModeKHR pickSwapChainPresentMode(const SwapChainSupportDetails& details) {
for (const auto& mode : details.presentModes) {
if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
return mode;
}
}
return VK_PRESENT_MODE_FIFO_KHR; // guaranteed exists
}
VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
// if set by vulkan just keep it
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
return capabilities.currentExtent;
}
int width, height;
glfwGetFramebufferSize(mWindow, &width, &height);
VkExtent2D out = { (uint32_t) width, (uint32_t) height };
out.width = std::clamp(out.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
out.height = std::clamp(out.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
return out;
}
void createSwapChainImageViews() {
mSwapChainImageViews.resize(mSwapChainImages.size());
int i = 0;
for (const auto& image : mSwapChainImages) {
VkImageViewCreateInfo createInfo {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = mSwapChainFormat,
.components = {
.r = VK_COMPONENT_SWIZZLE_R,
.g = VK_COMPONENT_SWIZZLE_G,
.b = VK_COMPONENT_SWIZZLE_B,
.a = VK_COMPONENT_SWIZZLE_A,
},
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
}
};
if (vkCreateImageView(mDevice, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) {
throw std::runtime_error("cannot create image view for the swap chain");
}
i++;
}
}
void createSwapChainFramebuffers() {
mSwapChainFrameBuffers.resize(mSwapChainImageViews.size());
for (size_t i = 0; i < mSwapChainImageViews.size(); i++) {
VkImageView attachments[] = {
mSwapChainImageViews[i],
};
VkFramebufferCreateInfo framebufferCreateInfo {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = mGraphicsRenderPass,
.attachmentCount = 1,
.pAttachments = attachments,
.width = mSwapChainExtent.width,
.height = mSwapChainExtent.height,
.layers = 1,
};
if (vkCreateFramebuffer(mDevice, &framebufferCreateInfo, nullptr, &mSwapChainFrameBuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create swapchain framebuffers");
}
}
}
void destroySwapChainFramebuffers() {
for (const auto& buffer : mSwapChainFrameBuffers ) {
vkDestroyFramebuffer(mDevice, buffer, nullptr);
}
}
void destroySwapChainImageViews() {
for (const auto& imageView : mSwapChainImageViews) {
vkDestroyImageView(mDevice, imageView, nullptr);
}
}
void createGraphicsPipeline() {
std::vector<char> vertShaderByteCode = readFile("bin/vert.spv");
mShaderModuleVert = createShaderModule(vertShaderByteCode);
VkPipelineShaderStageCreateInfo vertShaderStageCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = mShaderModuleVert,
.pName = "main",
};
std::vector<char> fragShaderByteCode = readFile("bin/frag.spv");
mShaderModuleFrag = createShaderModule(fragShaderByteCode);
VkPipelineShaderStageCreateInfo fragShaderStageCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = mShaderModuleFrag,
.pName = "main",
};
VkPipelineShaderStageCreateInfo shaderStageCreateInfos[] = {
vertShaderStageCreateInfo,
fragShaderStageCreateInfo
};
std::vector<VkDynamicState> dynamicStates = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = (uint32_t) dynamicStates.size(),
.pDynamicStates = dynamicStates.data(),
};
VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 0,
.vertexAttributeDescriptionCount = 0,
};
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
VkViewport viewport {
.x = 0,
.y = 0,
.width = (float) mSwapChainExtent.width,
.height = (float) mSwapChainExtent.height,
.minDepth = 0.f,
.maxDepth = 1.f,
};
VkRect2D scissor {
.offset = { 0, 0 },
.extent = mSwapChainExtent,
};
VkPipelineViewportStateCreateInfo viewportState {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.pViewports = &viewport,
.scissorCount = 1,
.pScissors = &scissor,
};
VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.f,
.depthBiasClamp = 0.f,
.depthBiasSlopeFactor = 0.f,
.lineWidth = 1,
};
VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.sampleShadingEnable = VK_FALSE,
};
VkPipelineColorBlendAttachmentState colorBlendAttachmentState {
.blendEnable = VK_FALSE,
.colorWriteMask = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT),
};
VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.attachmentCount = 1,
.pAttachments = &colorBlendAttachmentState,
};
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
};
if (vkCreatePipelineLayout(mDevice, &pipelineLayoutCreateInfo, nullptr, &mGraphicsPipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("failed to create pipeline layout");
}
VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 1,
.pStages = shaderStageCreateInfos,
.pVertexInputState = &vertexInputStateCreateInfo,
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizationStateCreateInfo,
.pMultisampleState = &multisampleStateCreateInfo,
.pColorBlendState = &colorBlendStateCreateInfo,
.pDynamicState = &dynamicStateCreateInfo,
.layout = mGraphicsPipelineLayout,
.renderPass = mGraphicsRenderPass,
.subpass = 0,
};
if (vkCreateGraphicsPipelines(mDevice, nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &mGraphicsPipeline) != VK_SUCCESS) {
throw std::runtime_error("failed to create graphics pipeline");
}
}
void createRenderPass() {
VkAttachmentDescription colorAttachment {
.format = mSwapChainFormat,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
};
VkAttachmentReference colorAttachmentReference {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
VkSubpassDescription subpassDescription {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1,
.pColorAttachments = &colorAttachmentReference,
};
VkRenderPassCreateInfo renderPassCreateInfo {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &colorAttachment,
.subpassCount = 1,
.pSubpasses = &subpassDescription,
};
if (vkCreateRenderPass(mDevice, &renderPassCreateInfo, nullptr, &mGraphicsRenderPass) != VK_SUCCESS) {
throw std::runtime_error("failed to create render pass");
}
}
void destroyGraphicsPipeline() {
vkDestroyShaderModule(mDevice, mShaderModuleVert, nullptr);
vkDestroyShaderModule(mDevice, mShaderModuleFrag, nullptr);
vkDestroyPipelineLayout(mDevice, mGraphicsPipelineLayout, nullptr);
vkDestroyPipeline(mDevice, mGraphicsPipeline, nullptr);
}
VkShaderModule createShaderModule(const std::vector<char>& bytecode) {
VkShaderModuleCreateInfo createInfo {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = (uint32_t) bytecode.size(),
.pCode = (uint32_t*) bytecode.data(),
};
VkShaderModule shaderModule;
if (vkCreateShaderModule(mDevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
throw std::runtime_error("cannot create shader module");
}
return shaderModule;
}
static std::vector<char> readFile(const std::string& fileName) {
std::ifstream file(fileName, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("cannot open file");
}
size_t fileSize = (size_t) file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), (std::streamsize) fileSize);
file.close();
return buffer;
}
void cleanup() {
destroySwapChainFramebuffers();
vkDestroyRenderPass(mDevice, mGraphicsRenderPass, nullptr);
destroyGraphicsPipeline();
destroySwapChainImageViews();
vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr);
vkDestroyDevice(mDevice, nullptr);
vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
vkDestroyInstance(mInstance, nullptr);
glfwDestroyWindow(mWindow);
glfwTerminate();
}
private:
GLFWwindow* mWindow = nullptr;
uint32_t mGraphicsQueueFamilyIndex = -1;
uint32_t mPresentationQueueFamilyIndex = -1;
VkInstance mInstance = VK_NULL_HANDLE;
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
VkDevice mDevice = VK_NULL_HANDLE;
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
VkQueue mPresentQueue = VK_NULL_HANDLE;
VkSurfaceKHR mSurface = VK_NULL_HANDLE;
VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
VkFormat mSwapChainFormat {};
VkExtent2D mSwapChainExtent {};
std::vector<VkImage> mSwapChainImages;
std::vector<VkImageView> mSwapChainImageViews;
std::vector<VkFramebuffer> mSwapChainFrameBuffers;
VkPipeline mGraphicsPipeline = VK_NULL_HANDLE;
VkShaderModule mShaderModuleVert = VK_NULL_HANDLE;
VkShaderModule mShaderModuleFrag = VK_NULL_HANDLE;
VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE;
VkPipelineLayout mGraphicsPipelineLayout {}; // no uniforms used in the shader
};
int main() {
Application app;
try {
app.run();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}