Created
November 14, 2022 22:30
-
-
Save TheVice/0ae55199763301db7d8e5b8a1716d91b to your computer and use it in GitHub Desktop.
This sample contain code original described at the video "Vulkan is HARD" 03.09.2022 https://www.youtube.com/watch?v=XGmVrjbFVm0 by @lolzdev. Any not visible parts took from next source file - https://github.com/Overv/VulkanTutorial/raw/87803541171579165caa354120157a0cc6c8192f/code/15_hello_triangle.cpp. Shaders, that was not described in the vi…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 450 | |
layout(location = 0) in vec3 fragColor; | |
layout(location = 0) out vec4 outColor; | |
void main() { | |
outColor = vec4(fragColor, 1.0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 450 | |
layout(location = 0) out vec3 fragColor; | |
vec2 positions[3] = vec2[]( | |
vec2(0.0, -0.5), | |
vec2(0.5, 0.5), | |
vec2(-0.5, 0.5) | |
); | |
vec3 colors[3] = vec3[]( | |
vec3(1.0, 0.0, 0.0), | |
vec3(0.0, 1.0, 0.0), | |
vec3(0.0, 0.0, 1.0) | |
); | |
void main() { | |
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); | |
fragColor = colors[gl_VertexIndex]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "App.hpp" | |
#include <set> | |
#include <limits> | |
#include <string> | |
#include <cstring> | |
#include <fstream> | |
#include <iostream> | |
#include <algorithm> | |
/*#pragma comment(lib, "glfw3.lib") | |
#pragma comment(lib, "vulkan-1.lib")*/ | |
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("failed to open file!"); | |
} | |
size_t fileSize = static_cast<size_t>(file.tellg()); | |
std::vector<char> buffer(fileSize); | |
// | |
file.seekg(0); | |
file.read(buffer.data(), fileSize); | |
// | |
file.close(); | |
// | |
return buffer; | |
} | |
void App::run() | |
{ | |
initWindow(); | |
initVulkan(); | |
mainLoop(); | |
cleanup(); | |
} | |
void App::initVulkan() | |
{ | |
createInstance(); | |
createSurface(); | |
choosePhysicalDevice(); | |
createDevice(); | |
createSwapChain(); | |
createImageViews(); | |
createRenderPass(); | |
createGraphicsPipeline(); | |
createFramebuffers(); | |
createCommandPool(); | |
createCommandBuffer(); | |
createSyncObjects(); | |
} | |
void App::createInstance() | |
{ | |
VkResult vkResult = VkResult::VK_SUCCESS; | |
VkApplicationInfo appInfo; | |
memset(&appInfo, 0, sizeof(appInfo)); | |
// | |
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | |
appInfo.pApplicationName = "Vulkan Test"; | |
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); | |
appInfo.pEngineName = "Vulkan Test"; | |
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); | |
appInfo.apiVersion = VK_API_VERSION_1_0;// VK_API_VERSION_1_3; | |
// | |
VkInstanceCreateInfo createInfo; | |
memset(&createInfo, 0, sizeof(createInfo)); | |
// | |
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | |
createInfo.pApplicationInfo = &appInfo; | |
createInfo.flags = 0; | |
#if 0 | |
if (!checkValidationLayers()) | |
{ | |
throw std::runtime_error("Cannot find requested validation layers"); | |
} | |
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); | |
createInfo.ppEnabledLayerNames = validationLayers.data(); | |
#else | |
std::vector<const char*> vkInstanceLayerNames = | |
{ | |
/*#ifndef NDEBUG | |
"VK_LAYER_LUNARG_core_validation" | |
#endif*/ | |
}; | |
createInfo.enabledLayerCount = static_cast<uint32_t>(vkInstanceLayerNames.size()); | |
createInfo.ppEnabledLayerNames = vkInstanceLayerNames.data(); | |
#endif | |
// | |
std::vector<const char*> extensions = getRequiredExtensions(); | |
// | |
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); | |
createInfo.ppEnabledExtensionNames = extensions.data(); | |
if (VK_SUCCESS != (vkResult = vkCreateInstance(&createInfo, nullptr, &instance))) | |
{ | |
throw std::runtime_error("Cannot create Vulkan instance"); | |
} | |
} | |
void App::cleanup() | |
{ | |
vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); | |
vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); | |
vkDestroyFence(device, inFlightFence, nullptr); | |
vkDestroyCommandPool(device, commandPool, nullptr); | |
for (auto framebuffer : swapChainFramebuffers) | |
{ | |
vkDestroyFramebuffer(device, framebuffer, nullptr); | |
} | |
vkDestroyPipeline(device, graphicsPipeline, nullptr); | |
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); | |
vkDestroyRenderPass(device, renderPass, nullptr); | |
for (auto imageView : swapChainImageViews) | |
{ | |
vkDestroyImageView(device, imageView, nullptr); | |
} | |
vkDestroySwapchainKHR(device, swapChain, nullptr); | |
vkDestroyDevice(device, nullptr); | |
vkDestroySurfaceKHR(instance, surface, nullptr); | |
vkDestroyInstance(instance, nullptr); | |
// | |
glfwDestroyWindow(window); | |
// | |
glfwTerminate(); | |
} | |
void App::mainLoop() | |
{ | |
while (!glfwWindowShouldClose(window)) | |
{ | |
glfwPollEvents(); | |
drawFrame(); | |
if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_Q)) | |
{ | |
glfwSetWindowShouldClose(window, true); | |
} | |
} | |
vkDeviceWaitIdle(device); | |
} | |
void App::initWindow() | |
{ | |
glfwInit(); | |
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); | |
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); | |
window = glfwCreateWindow(800, 600, "Vulkan", nullptr, nullptr); | |
} | |
void App::choosePhysicalDevice() | |
{ | |
uint32_t deviceCount = 0; | |
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); | |
if (0 == deviceCount) | |
{ | |
throw std::runtime_error("Failed to find any GPUs that support Vulkan API."); | |
} | |
std::vector<VkPhysicalDevice> devices(deviceCount); | |
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); | |
for (const auto& device_ : devices) | |
{ | |
if (isPhysicalDeviceSuitable(device_)) | |
{ | |
physicalDevice = device_; | |
break; | |
} | |
} | |
if (VK_NULL_HANDLE == physicalDevice) | |
{ | |
throw std::runtime_error("GPU not founded."); | |
} | |
} | |
void App::createDevice() | |
{ | |
QueueIndices queueIndices = getQueueIndices(physicalDevice); | |
std::vector<VkDeviceQueueCreateInfo> deviceQueues; | |
std::set<uint32_t> queueFamilies = { queueIndices.graphicsIndex.value(), queueIndices.presentIndex.value() }; | |
const float priority = 1.0f; | |
for (uint32_t family : queueFamilies) | |
{ | |
VkDeviceQueueCreateInfo queueCreateInfo; | |
memset(&queueCreateInfo, 0, sizeof(queueCreateInfo)); | |
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; | |
queueCreateInfo.flags = 0; | |
queueCreateInfo.queueFamilyIndex = family; | |
queueCreateInfo.queueCount = 1; | |
queueCreateInfo.pQueuePriorities = &priority; | |
deviceQueues.push_back(queueCreateInfo); | |
} | |
VkDeviceCreateInfo createInfo; | |
memset(&createInfo, 0, sizeof(createInfo)); | |
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; | |
createInfo.flags = 0; | |
createInfo.queueCreateInfoCount = static_cast<uint32_t>(deviceQueues.size()); | |
createInfo.pQueueCreateInfos = deviceQueues.data(); | |
createInfo.enabledLayerCount = 0; | |
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size()); | |
createInfo.ppEnabledExtensionNames = deviceExtensions.data(); | |
createInfo.pEnabledFeatures = nullptr; | |
if (VK_SUCCESS != vkCreateDevice(physicalDevice, &createInfo, nullptr, &device)) | |
{ | |
throw std::runtime_error("Cannot create device"); | |
} | |
vkGetDeviceQueue(device, queueIndices.graphicsIndex.value(), 0, &graphicsQueue); | |
vkGetDeviceQueue(device, queueIndices.presentIndex.value(), 0, &presentQueue); | |
} | |
void App::createSurface() | |
{ | |
if (VK_SUCCESS != glfwCreateWindowSurface(instance, window, nullptr, &surface)) | |
{ | |
throw std::runtime_error("Cannot create surface"); | |
} | |
} | |
void App::createSwapChain() | |
{ | |
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); | |
// | |
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); | |
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); | |
VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); | |
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; | |
if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) | |
{ | |
imageCount = swapChainSupport.capabilities.maxImageCount; | |
} | |
VkSwapchainCreateInfoKHR createInfo; | |
memset(&createInfo, 0, sizeof(createInfo)); | |
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; | |
createInfo.surface = surface; | |
createInfo.minImageCount = imageCount; | |
createInfo.imageFormat = surfaceFormat.format; | |
createInfo.imageColorSpace = surfaceFormat.colorSpace; | |
createInfo.imageExtent = extent; | |
createInfo.imageArrayLayers = 1; | |
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | |
// | |
QueueIndices indices = getQueueIndices(physicalDevice); | |
uint32_t queueFamilyIndicies[] = { indices.graphicsIndex.value(), indices.presentIndex.value() }; | |
if (indices.graphicsIndex != indices.presentIndex) | |
{ | |
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; | |
createInfo.queueFamilyIndexCount = 2; | |
createInfo.pQueueFamilyIndices = queueFamilyIndicies; | |
} | |
else | |
{ | |
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
createInfo.queueFamilyIndexCount = 0; | |
createInfo.pQueueFamilyIndices = nullptr; | |
} | |
createInfo.preTransform = swapChainSupport.capabilities.currentTransform; | |
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
createInfo.presentMode = presentMode; | |
createInfo.clipped = VK_TRUE; | |
createInfo.oldSwapchain = VK_NULL_HANDLE; | |
if (VK_SUCCESS != vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain)) | |
{ | |
throw std::runtime_error("Cannot create swap chain"); | |
} | |
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); | |
swapChainImages.resize(imageCount); | |
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); | |
// | |
swapChainImageFormat = surfaceFormat.format; | |
swapChainExtent = extent; | |
} | |
void App::createImageViews() | |
{ | |
swapChainImageViews.resize(swapChainImages.size()); | |
for (size_t i = 0; i < swapChainImages.size(); ++i) | |
{ | |
VkImageViewCreateInfo createInfo; | |
memset(&createInfo, 0, sizeof(createInfo)); | |
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
createInfo.image = swapChainImages[i]; | |
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
createInfo.format = swapChainImageFormat; | |
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
createInfo.subresourceRange.baseMipLevel = 0; | |
createInfo.subresourceRange.levelCount = 1; | |
createInfo.subresourceRange.baseArrayLayer = 0; | |
createInfo.subresourceRange.layerCount = 1; | |
if (VK_SUCCESS != vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i])) | |
{ | |
throw std::runtime_error("failed to create image views"); | |
} | |
} | |
} | |
void App::createGraphicsPipeline() | |
{ | |
auto vertShaderCode = readFile("../shaders/vert.spv"); | |
auto fragShaderCode = readFile("../shaders/frag.spv"); | |
// | |
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); | |
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); | |
// | |
VkPipelineShaderStageCreateInfo vertShaderStageInfo; | |
memset(&vertShaderStageInfo, 0, sizeof(vertShaderStageInfo)); | |
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; | |
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; | |
vertShaderStageInfo.module = vertShaderModule; | |
vertShaderStageInfo.pName = "main"; | |
// | |
VkPipelineShaderStageCreateInfo fragShaderStageInfo; | |
memset(&fragShaderStageInfo, 0, sizeof(fragShaderStageInfo)); | |
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; | |
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; | |
fragShaderStageInfo.module = fragShaderModule; | |
fragShaderStageInfo.pName = "main"; | |
// | |
VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; | |
// | |
VkPipelineVertexInputStateCreateInfo vertexInputInfo; | |
memset(&vertexInputInfo, 0, sizeof(vertexInputInfo)); | |
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; | |
vertexInputInfo.vertexBindingDescriptionCount = 0; | |
vertexInputInfo.pVertexBindingDescriptions = nullptr; | |
vertexInputInfo.vertexAttributeDescriptionCount = 0; | |
vertexInputInfo.pVertexAttributeDescriptions = nullptr; | |
// | |
VkPipelineInputAssemblyStateCreateInfo inputAssembly; | |
memset(&inputAssembly, 0, sizeof(inputAssembly)); | |
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; | |
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; | |
inputAssembly.primitiveRestartEnable = VK_FALSE; | |
// | |
VkViewport viewport; | |
memset(&viewport, 0, sizeof(viewport)); | |
viewport.x = 0.0f; | |
viewport.y = 0.0f; | |
viewport.width = static_cast<float>(swapChainExtent.width); | |
viewport.height = static_cast<float>(swapChainExtent.height); | |
viewport.minDepth = 0.0f; | |
viewport.maxDepth = 1.0f; | |
// | |
VkRect2D scissor; | |
memset(&scissor, 0, sizeof(scissor)); | |
scissor.offset = { 0, 0 }; | |
scissor.extent = swapChainExtent; | |
// | |
VkPipelineViewportStateCreateInfo viewportState; | |
memset(&viewportState, 0, sizeof(viewportState)); | |
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; | |
viewportState.viewportCount = 1; | |
viewportState.pViewports = &viewport; | |
viewportState.scissorCount = 1; | |
viewportState.pScissors = &scissor; | |
// | |
VkPipelineRasterizationStateCreateInfo rasterizer; | |
memset(&rasterizer, 0, sizeof(rasterizer)); | |
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | |
rasterizer.depthClampEnable = VK_FALSE; | |
rasterizer.rasterizerDiscardEnable = VK_FALSE; | |
rasterizer.polygonMode = VK_POLYGON_MODE_FILL; | |
rasterizer.lineWidth = 1.0f; | |
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; | |
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; | |
rasterizer.depthBiasEnable = VK_FALSE; | |
rasterizer.depthBiasConstantFactor = 0.0f; | |
rasterizer.depthBiasClamp = 0.0f; | |
rasterizer.depthBiasSlopeFactor = 0.0f; | |
// | |
VkPipelineMultisampleStateCreateInfo multisampling; | |
memset(&multisampling, 0, sizeof(multisampling)); | |
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; | |
multisampling.sampleShadingEnable = VK_FALSE; | |
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; | |
multisampling.minSampleShading = 1.0f; | |
multisampling.pSampleMask = nullptr; | |
multisampling.alphaToCoverageEnable = VK_FALSE; | |
multisampling.alphaToOneEnable = VK_FALSE; | |
// | |
VkPipelineColorBlendAttachmentState colorBlendAttachment; | |
memset(&colorBlendAttachment, 0, sizeof(colorBlendAttachment)); | |
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; | |
colorBlendAttachment.blendEnable = VK_FALSE; | |
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; | |
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; | |
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; | |
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; | |
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; | |
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; | |
// | |
VkPipelineColorBlendStateCreateInfo colorBlending; | |
memset(&colorBlending, 0, sizeof(colorBlending)); | |
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; | |
colorBlending.logicOpEnable = VK_FALSE; | |
colorBlending.logicOp = VK_LOGIC_OP_COPY; | |
colorBlending.attachmentCount = 1; | |
colorBlending.pAttachments = &colorBlendAttachment; | |
colorBlending.blendConstants[0] = 0.0f; | |
colorBlending.blendConstants[1] = 0.0f; | |
colorBlending.blendConstants[2] = 0.0f; | |
colorBlending.blendConstants[3] = 0.0f; | |
// | |
std::vector<VkDynamicState> dynamicStates = | |
{ | |
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR | |
}; | |
// | |
VkPipelineDynamicStateCreateInfo dynamicState; | |
memset(&dynamicState, 0, sizeof(dynamicState)); | |
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; | |
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size()); | |
dynamicState.pDynamicStates = dynamicStates.data(); | |
// | |
VkPipelineLayoutCreateInfo pipelineLayoutInfo; | |
memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo)); | |
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; | |
pipelineLayoutInfo.setLayoutCount = 0; | |
pipelineLayoutInfo.pSetLayouts = nullptr; | |
pipelineLayoutInfo.pushConstantRangeCount = 0; | |
pipelineLayoutInfo.pPushConstantRanges = nullptr; | |
if (VK_SUCCESS != vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout)) | |
{ | |
throw std::runtime_error("failed to create pipeline layout"); | |
} | |
VkGraphicsPipelineCreateInfo pipelineInfo; | |
memset(&pipelineInfo, 0, sizeof(pipelineInfo)); | |
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; | |
pipelineInfo.stageCount = 2; | |
pipelineInfo.pStages = shaderStages; | |
// | |
pipelineInfo.pVertexInputState = &vertexInputInfo; | |
pipelineInfo.pInputAssemblyState = &inputAssembly; | |
pipelineInfo.pViewportState = &viewportState; | |
pipelineInfo.pRasterizationState = &rasterizer; | |
pipelineInfo.pMultisampleState = &multisampling; | |
pipelineInfo.pDepthStencilState = nullptr; | |
pipelineInfo.pColorBlendState = &colorBlending; | |
pipelineInfo.pDynamicState = &dynamicState; | |
pipelineInfo.layout = pipelineLayout; | |
pipelineInfo.renderPass = renderPass; | |
pipelineInfo.subpass = 0; | |
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; | |
pipelineInfo.basePipelineIndex = -1; | |
if (VK_SUCCESS != vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline)) | |
{ | |
throw std::runtime_error("failed to create graphics pipeline"); | |
} | |
vkDestroyShaderModule(device, fragShaderModule, nullptr); | |
vkDestroyShaderModule(device, vertShaderModule, nullptr); | |
} | |
VkShaderModule App::createShaderModule(const std::vector<char>& code) | |
{ | |
VkShaderModuleCreateInfo createInfo; | |
memset(&createInfo, 0, sizeof(createInfo)); | |
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; | |
createInfo.codeSize = code.size(); | |
createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data()); | |
// | |
VkShaderModule shaderModule; | |
if (VK_SUCCESS != vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule)) | |
{ | |
throw std::runtime_error("failed to create shader module"); | |
} | |
return shaderModule; | |
} | |
void App::createRenderPass() | |
{ | |
VkAttachmentDescription colorAttachment; | |
memset(&colorAttachment, 0, sizeof(colorAttachment)); | |
colorAttachment.format = swapChainImageFormat; | |
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; | |
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; | |
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; | |
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; | |
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | |
// | |
VkAttachmentReference colorAttachmentRef; | |
memset(&colorAttachmentRef, 0, sizeof(colorAttachmentRef)); | |
colorAttachmentRef.attachment = 0; | |
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
// | |
VkSubpassDescription subpass; | |
memset(&subpass, 0, sizeof(subpass)); | |
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; | |
subpass.colorAttachmentCount = 1; | |
subpass.pColorAttachments = &colorAttachmentRef; | |
// | |
VkSubpassDependency dependency; | |
memset(&dependency, 0, sizeof(dependency)); | |
dependency.srcSubpass = VK_SUBPASS_EXTERNAL; | |
dependency.dstSubpass = 0; | |
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; | |
dependency.srcAccessMask = 0; | |
dependency.dstStageMask = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
// | |
VkRenderPassCreateInfo renderPassInfo; | |
memset(&renderPassInfo, 0, sizeof(renderPassInfo)); | |
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; | |
renderPassInfo.attachmentCount = 1; | |
renderPassInfo.pAttachments = &colorAttachment; | |
renderPassInfo.subpassCount = 1; | |
renderPassInfo.pSubpasses = &subpass; | |
renderPassInfo.dependencyCount = 1; | |
renderPassInfo.pDependencies = &dependency; | |
if (VK_SUCCESS != vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass)) | |
{ | |
throw std::runtime_error("Cannot create render pass"); | |
} | |
} | |
void App::createFramebuffers() | |
{ | |
swapChainFramebuffers.resize(swapChainImageViews.size()); | |
for (size_t i = 0; i < swapChainImageViews.size(); ++i) | |
{ | |
VkImageView attachments[] = | |
{ | |
swapChainImageViews[i] | |
}; | |
// | |
VkFramebufferCreateInfo framebufferInfo; | |
memset(&framebufferInfo, 0, sizeof(framebufferInfo)); | |
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; | |
framebufferInfo.renderPass = renderPass; | |
framebufferInfo.attachmentCount = 1; | |
framebufferInfo.pAttachments = attachments; | |
framebufferInfo.width = swapChainExtent.width; | |
framebufferInfo.height = swapChainExtent.height; | |
framebufferInfo.layers = 1; | |
if (VK_SUCCESS != vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i])) | |
{ | |
throw std::runtime_error("Cannot create framebuffer"); | |
} | |
} | |
} | |
std::vector<const char*> App::getRequiredExtensions() | |
{ | |
uint32_t glfwExtensionCount = 0; | |
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); | |
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); | |
return extensions; | |
} | |
bool App::checkValidationLayers() | |
{ | |
uint32_t layerCount = 0; | |
vkEnumerateInstanceLayerProperties(&layerCount, nullptr); | |
std::vector<VkLayerProperties> availableLayers(layerCount); | |
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); | |
for (const auto& layer : validationLayers) | |
{ | |
bool layerFound = false; | |
for (const auto& availableLayer : availableLayers) | |
{ | |
if (0 == strcmp(availableLayer.layerName, layer)) | |
{ | |
layerFound = true; | |
break; | |
} | |
} | |
if (!layerFound) | |
{ | |
return false; | |
} | |
} | |
return true; | |
} | |
bool App::isPhysicalDeviceSuitable(VkPhysicalDevice device_) | |
{ | |
bool extensionsSupported = checkDeviceExtensionSupport(device_); | |
bool swapChainAdequate = false; | |
if (extensionsSupported) | |
{ | |
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device_); | |
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); | |
} | |
QueueIndices indices = getQueueIndices(device_); | |
return indices.complete() && extensionsSupported && swapChainAdequate; | |
} | |
QueueIndices App::getQueueIndices(VkPhysicalDevice device_) | |
{ | |
QueueIndices indices; | |
uint32_t queueCount = 0; | |
vkGetPhysicalDeviceQueueFamilyProperties(device_, &queueCount, nullptr); | |
std::vector<VkQueueFamilyProperties> queueFamies(queueCount); | |
vkGetPhysicalDeviceQueueFamilyProperties(device_, &queueCount, queueFamies.data()); | |
uint32_t index = 0; | |
for (const auto& family : queueFamies) | |
{ | |
if (indices.complete()) | |
{ | |
break; | |
} | |
VkBool32 presentSupport = false; | |
vkGetPhysicalDeviceSurfaceSupportKHR(device_, index, surface, &presentSupport); | |
if (presentSupport) | |
{ | |
indices.presentIndex = index; | |
} | |
if (VK_QUEUE_GRAPHICS_BIT & family.queueFlags) | |
{ | |
indices.graphicsIndex = index; | |
} | |
++index; | |
} | |
if (!indices.complete()) | |
{ | |
throw std::runtime_error("Cannot find all the needed queues"); | |
} | |
return indices; | |
} | |
bool App::checkDeviceExtensionSupport(VkPhysicalDevice device_) | |
{ | |
uint32_t extensionCount = 0; | |
vkEnumerateDeviceExtensionProperties(device_, nullptr, &extensionCount, nullptr); | |
std::vector<VkExtensionProperties> availableExtensions(extensionCount); | |
vkEnumerateDeviceExtensionProperties(device_, nullptr, &extensionCount, availableExtensions.data()); | |
std::set<std::string> requiredExtensions(deviceExtensions.cbegin(), deviceExtensions.cend()); | |
for (const auto& extension : availableExtensions) | |
{ | |
requiredExtensions.erase(extension.extensionName); | |
} | |
return requiredExtensions.empty(); | |
} | |
SwapChainSupportDetails App::querySwapChainSupport(VkPhysicalDevice device_) | |
{ | |
SwapChainSupportDetails details; | |
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device_, surface, &details.capabilities); | |
uint32_t formatCount = 0; | |
vkGetPhysicalDeviceSurfaceFormatsKHR(device_, surface, &formatCount, nullptr); | |
if (0 != formatCount) | |
{ | |
details.formats.resize(formatCount); | |
vkGetPhysicalDeviceSurfaceFormatsKHR(device_, surface, &formatCount, details.formats.data()); | |
} | |
uint32_t presentModeCount = 0; | |
vkGetPhysicalDeviceSurfacePresentModesKHR(device_, surface, &presentModeCount, nullptr); | |
if (0 != presentModeCount) | |
{ | |
details.presentModes.resize(presentModeCount); | |
vkGetPhysicalDeviceSurfacePresentModesKHR(device_, surface, &presentModeCount, details.presentModes.data()); | |
} | |
return details; | |
} | |
VkSurfaceFormatKHR App::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) | |
{ | |
for (const auto& availableFormat : availableFormats) | |
{ | |
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) | |
{ | |
return availableFormat; | |
} | |
} | |
return availableFormats[0]; | |
} | |
VkPresentModeKHR App::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) | |
{ | |
/*TODO:*/(void)availablePresentModes; | |
return VK_PRESENT_MODE_FIFO_KHR; | |
} | |
VkExtent2D App::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) | |
{ | |
if (std::numeric_limits<uint32_t>::max() != capabilities.currentExtent.width) | |
{ | |
return capabilities.currentExtent; | |
} | |
else | |
{ | |
int width, height; | |
glfwGetFramebufferSize(window, &width, &height); | |
// | |
VkExtent2D actualExtent = | |
{ | |
static_cast<uint32_t>(width), | |
static_cast<uint32_t>(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); | |
// | |
return actualExtent; | |
} | |
} | |
void App::createCommandBuffer() | |
{ | |
VkCommandBufferAllocateInfo allocInfo; | |
memset(&allocInfo, 0, sizeof(allocInfo)); | |
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | |
allocInfo.commandPool = commandPool; | |
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; | |
allocInfo.commandBufferCount = 1; | |
if (VK_SUCCESS != vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer)) | |
{ | |
throw std::runtime_error("Cannot allocate command buffers"); | |
} | |
} | |
void App::createCommandPool() | |
{ | |
QueueIndices queueIndices = getQueueIndices(physicalDevice); | |
// | |
VkCommandPoolCreateInfo poolInfo; | |
memset(&poolInfo, 0, sizeof(poolInfo)); | |
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; | |
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; | |
poolInfo.queueFamilyIndex = queueIndices.graphicsIndex.value(); | |
if (VK_SUCCESS != vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool)) | |
{ | |
throw std::runtime_error("failed to create command pool!"); | |
} | |
} | |
void App::recordCommandBuffer(VkCommandBuffer commandBuffer_, uint32_t imageIndex) | |
{ | |
VkCommandBufferBeginInfo beginInfo; | |
memset(&beginInfo, 0, sizeof(beginInfo)); | |
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | |
beginInfo.flags = 0; | |
beginInfo.pInheritanceInfo = nullptr; | |
if (VK_SUCCESS != vkBeginCommandBuffer(commandBuffer_, &beginInfo)) | |
{ | |
throw std::runtime_error("failed to begin recording command buffer"); | |
} | |
VkRenderPassBeginInfo renderPassInfo; | |
memset(&renderPassInfo, 0, sizeof(renderPassInfo)); | |
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; | |
renderPassInfo.renderPass = renderPass; | |
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex]; | |
renderPassInfo.renderArea.offset = { 0, 0 }; | |
renderPassInfo.renderArea.extent = swapChainExtent; | |
// | |
VkClearValue clearColor = {{{ 0.0f, 0.0f, 0.0f, 1.0f }}}; | |
renderPassInfo.clearValueCount = 1; | |
renderPassInfo.pClearValues = &clearColor; | |
// | |
vkCmdBeginRenderPass(commandBuffer_, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); | |
vkCmdBindPipeline(commandBuffer_, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); | |
// | |
VkViewport viewport; | |
memset(&viewport, 0, sizeof(viewport)); | |
viewport.x = 0.0f; | |
viewport.y = 0.0f; | |
viewport.width = static_cast<float>(swapChainExtent.width); | |
viewport.height = static_cast<float>(swapChainExtent.height); | |
viewport.minDepth = 0.0f; | |
viewport.maxDepth = 1.0f; | |
vkCmdSetViewport(commandBuffer_, 0, 1, &viewport); | |
// | |
VkRect2D scissor; | |
memset(&scissor, 0, sizeof(scissor)); | |
scissor.offset = { 0, 0 }; | |
scissor.extent = swapChainExtent; | |
vkCmdSetScissor(commandBuffer_, 0, 1, &scissor); | |
// | |
vkCmdDraw(commandBuffer_, 3, 1, 0, 0); | |
vkCmdEndRenderPass(commandBuffer_); | |
if (VK_SUCCESS != vkEndCommandBuffer(commandBuffer_)) | |
{ | |
throw std::runtime_error("Cannot record command buffer"); | |
} | |
} | |
void App::drawFrame() | |
{ | |
vkWaitForFences(device, 1, &inFlightFence, VK_TRUE, UINT64_MAX); | |
vkResetFences(device, 1, &inFlightFence); | |
uint32_t imageIndex; | |
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); | |
vkResetCommandBuffer(commandBuffer, 0); | |
recordCommandBuffer(commandBuffer, imageIndex); | |
// | |
VkSubmitInfo submitInfo; | |
memset(&submitInfo, 0, sizeof(submitInfo)); | |
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; | |
// | |
VkSemaphore waitSemaphores[] = { imageAvailableSemaphore }; | |
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; | |
submitInfo.waitSemaphoreCount = 1; | |
submitInfo.pWaitSemaphores = waitSemaphores; | |
submitInfo.pWaitDstStageMask = waitStages; | |
submitInfo.commandBufferCount = 1; | |
submitInfo.pCommandBuffers = &commandBuffer; | |
// | |
VkSemaphore signalSemaphores[] = { renderFinishedSemaphore }; | |
submitInfo.signalSemaphoreCount = 1; | |
submitInfo.pSignalSemaphores = signalSemaphores; | |
if (VK_SUCCESS != vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFence)) | |
{ | |
throw std::runtime_error("Cannot submit draw command buffer"); | |
} | |
VkPresentInfoKHR presentInfo; | |
memset(&presentInfo, 0, sizeof(presentInfo)); | |
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; | |
// | |
presentInfo.waitSemaphoreCount = 1; | |
presentInfo.pWaitSemaphores = signalSemaphores; | |
// | |
VkSwapchainKHR swapChains[] = { swapChain }; | |
presentInfo.swapchainCount = 1; | |
presentInfo.pSwapchains = swapChains; | |
presentInfo.pImageIndices = &imageIndex; | |
presentInfo.pResults = nullptr; | |
vkQueuePresentKHR(presentQueue, &presentInfo); | |
} | |
void App::createSyncObjects() | |
{ | |
VkSemaphoreCreateInfo semaphoreInfo; | |
memset(&semaphoreInfo, 0, sizeof(semaphoreInfo)); | |
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | |
// | |
VkFenceCreateInfo fenceInfo; | |
memset(&fenceInfo, 0, sizeof(fenceInfo)); | |
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; | |
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; | |
if (VK_SUCCESS != vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) || | |
VK_SUCCESS != vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) || | |
VK_SUCCESS != vkCreateFence(device, &fenceInfo, nullptr, &inFlightFence)) | |
{ | |
throw std::runtime_error("Cannot create semaphores"); | |
} | |
} | |
int main() | |
{ | |
App app; | |
// | |
try | |
{ | |
app.run(); | |
} | |
catch (const std::exception& e) | |
{ | |
std::cerr << e.what() << std::endl; | |
return EXIT_FAILURE; | |
} | |
return EXIT_SUCCESS; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __APP_HPP__ | |
#define __APP_HPP__ | |
#ifdef _WIN32 | |
#define NOMINMAX | |
#endif | |
#include <vector> | |
#include <optional> | |
#ifdef _WIN32 | |
#define VK_USE_PLATFORM_WIN32_KHR | |
#endif | |
#define GLFW_INCLUDE_VULKAN | |
#include <GLFW/glfw3.h> | |
const std::vector<const char*> validationLayers = | |
{ | |
"VK_LAYER_KHRONOS_validation" | |
}; | |
const std::vector<const char*> deviceExtensions = | |
{ | |
VK_KHR_SWAPCHAIN_EXTENSION_NAME | |
}; | |
struct QueueIndices | |
{ | |
std::optional<uint32_t> graphicsIndex; | |
std::optional<uint32_t> presentIndex; | |
bool complete() | |
{ | |
return graphicsIndex.has_value() && presentIndex.has_value(); | |
} | |
}; | |
struct SwapChainSupportDetails | |
{ | |
VkSurfaceCapabilitiesKHR capabilities; | |
std::vector<VkSurfaceFormatKHR> formats; | |
std::vector<VkPresentModeKHR> presentModes; | |
}; | |
class App { | |
public: | |
void run(); | |
private: | |
void initWindow(); | |
GLFWwindow* window; | |
void mainLoop(); | |
void initVulkan(); | |
void cleanup(); | |
void createInstance(); | |
VkInstance instance; | |
void choosePhysicalDevice(); | |
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; | |
void createSurface(); | |
VkSurfaceKHR surface; | |
VkQueue graphicsQueue; | |
VkQueue presentQueue; | |
void createSwapChain(); | |
void createImageViews(); | |
VkSwapchainKHR swapChain; | |
std::vector<VkImage> swapChainImages; | |
VkFormat swapChainImageFormat; | |
VkExtent2D swapChainExtent; | |
std::vector<VkImageView> swapChainImageViews; | |
void createGraphicsPipeline(); | |
VkShaderModule createShaderModule(const std::vector<char>& code); | |
VkPipelineLayout pipelineLayout; | |
VkPipeline graphicsPipeline; | |
void createRenderPass(); | |
VkRenderPass renderPass; | |
void createFramebuffers(); | |
std::vector<VkFramebuffer> swapChainFramebuffers; | |
void createCommandPool(); | |
VkCommandPool commandPool; | |
void createCommandBuffer(); | |
VkCommandBuffer commandBuffer; | |
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex); | |
void drawFrame(); | |
void createSyncObjects(); | |
VkSemaphore imageAvailableSemaphore; | |
VkSemaphore renderFinishedSemaphore; | |
VkFence inFlightFence; | |
void createDevice(); | |
VkDevice device; | |
std::vector<const char*> getRequiredExtensions(); | |
bool checkValidationLayers(); | |
bool isPhysicalDeviceSuitable(VkPhysicalDevice device); | |
QueueIndices getQueueIndices(VkPhysicalDevice device); | |
bool checkDeviceExtensionSupport(VkPhysicalDevice device); | |
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); | |
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats); | |
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes); | |
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); | |
}; | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cmake_minimum_required(VERSION 3.15) | |
project(vulkan_test) | |
if(MINGW) | |
message(WARNING "MinGW environment not tested.") | |
endif() | |
list(APPEND SOURCES "${CMAKE_SOURCE_DIR}/App.cpp") | |
list(APPEND HEADERS "${CMAKE_SOURCE_DIR}/App.hpp") | |
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS}) | |
if(CMAKE_VERSION VERSION_LESS 3.1 OR ";${CMAKE_CXX_COMPILE_FEATURES};" MATCHES ";cxx_std_17;") | |
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) | |
else() | |
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) | |
endif() | |
target_compile_options(${PROJECT_NAME} PRIVATE | |
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Werror> | |
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Werror> | |
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX> | |
) | |
# glfw | |
set(glfw3_FOUND 0) | |
find_package(glfw3) | |
if(NOT glfw3_FOUND) | |
message(WARNING "glfw3 not FOUND, will search via GLFW_PATH variable.") | |
if(DEFINED ENV{GLFW_PATH}) | |
string(REPLACE "\\" "/" glfw_Path $ENV{GLFW_PATH}) | |
elseif(DEFINED GLFW_PATH) | |
string(REPLACE "\\" "/" glfw_Path ${GLFW_PATH}) | |
else() | |
message(FATAL_ERROR "Could not find glfw library, please check it at the https://github.com/glfw/glfw/releases") | |
endif() | |
if(EXISTS "${glfw_Path}/CMakeLists.txt") | |
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) | |
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) | |
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) | |
add_subdirectory(${glfw_Path} ${CMAKE_BINARY_DIR}/glfw) | |
else() | |
if(MINGW) | |
set(glfw_HINT_PATH ${glfw_Path}/lib-mingw-w64) | |
elseif(MSVC) | |
if(NOT (MSVC_VERSION LESS 1930)) | |
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2022) | |
elseif(NOT (MSVC_VERSION LESS 1920)) | |
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2019) | |
elseif(NOT (MSVC_VERSION LESS 1910)) | |
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2017) | |
elseif(NOT (MSVC_VERSION LESS 1900)) | |
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2015) | |
elseif(NOT (MSVC_VERSION LESS 1800)) | |
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2013) | |
elseif(NOT (MSVC_VERSION LESS 1700)) | |
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2012) | |
endif() | |
endif() | |
find_library( | |
glfw_full_path | |
NAMES glfw3 | |
PATHS "${glfw_HINT_PATH}") | |
add_library(glfw INTERFACE) | |
target_link_libraries(glfw INTERFACE ${glfw_full_path}) | |
target_include_directories(glfw INTERFACE SYSTEM ${glfw_Path}/include) | |
endif() | |
endif() | |
if(UNIX) | |
add_compile_definitions(GLFW_EXPOSE_NATIVE_X11) | |
add_compile_definitions(GLFW_EXPOSE_NATIVE_GLX) | |
else() | |
add_compile_definitions(GLFW_EXPOSE_NATIVE_WIN32) | |
add_compile_definitions(GLFW_EXPOSE_NATIVE_WGL) | |
endif() | |
target_link_libraries(${PROJECT_NAME} glfw) | |
# Vulkan | |
set(Vulkan_FOUND 0) | |
find_package(Vulkan) | |
if(NOT Vulkan_FOUND) | |
message(WARNING "Vulkan not FOUND, will search via VULKAN_SDK variable.") | |
if(DEFINED ENV{VULKAN_SDK}) | |
string(REPLACE "\\" "/" VULKAN_SDK $ENV{VULKAN_SDK}) | |
elseif(DEFINED VULKAN_SDK) | |
string(REPLACE "\\" "/" VULKAN_SDK ${VULKAN_SDK}) | |
else() | |
message(FATAL_ERROR "Could not find VULKAN SDK, please check it at the https://vulkan.lunarg.com/sdk/home") | |
endif() | |
find_library( | |
vulkan_full_path | |
NAMES vulkan-1 | |
PATHS "${VULKAN_SDK}/Lib" "${VULKAN_SDK}/Bin") | |
add_library(vulkan INTERFACE) | |
target_link_libraries(vulkan INTERFACE ${vulkan_full_path}) | |
target_include_directories(vulkan INTERFACE SYSTEM ${VULKAN_SDK}/include) | |
endif() | |
target_link_libraries(${PROJECT_NAME} vulkan) | |
# ${PROJECT_NAME}_shader | |
add_executable(glslang::validator IMPORTED) | |
find_program(GLSLANG_VALIDATOR "glslangValidator" HINTS $ENV{VULKAN_SDK}/bin ${VULKAN_SDK}/bin REQUIRED) | |
set_property(TARGET glslang::validator PROPERTY IMPORTED_LOCATION "${GLSLANG_VALIDATOR}") | |
function(add_shaders_target TARGET) | |
set(SHADERS_DIR ${CMAKE_BINARY_DIR}/shaders) | |
add_custom_command( | |
OUTPUT ${SHADERS_DIR} | |
COMMAND ${CMAKE_COMMAND} -E make_directory ${SHADERS_DIR}) | |
add_custom_command( | |
OUTPUT ${SHADERS_DIR}/frag.spv | |
COMMAND glslang::validator | |
ARGS ${CMAKE_SOURCE_DIR}/09_shader_base.frag -V -o ${SHADERS_DIR}/frag.spv | |
WORKING_DIRECTORY ${SHADERS_DIR} | |
DEPENDS ${SHADERS_DIR} ${CMAKE_SOURCE_DIR}/09_shader_base.frag | |
COMMENT "Compiling fragment Shader" | |
VERBATIM) | |
add_custom_command( | |
OUTPUT ${SHADERS_DIR}/vert.spv | |
COMMAND glslang::validator | |
ARGS ${CMAKE_SOURCE_DIR}/09_shader_base.vert -V -o ${SHADERS_DIR}/vert.spv | |
WORKING_DIRECTORY ${SHADERS_DIR} | |
DEPENDS ${SHADERS_DIR} ${CMAKE_SOURCE_DIR}/09_shader_base.vert | |
COMMENT "Compiling vertical Shader" | |
VERBATIM) | |
add_custom_target(${TARGET} DEPENDS ${SHADERS_DIR}/frag.spv ${SHADERS_DIR}/vert.spv) | |
endfunction() | |
add_shaders_target(${PROJECT_NAME}_shader) | |
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_shader) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment