feat: initial commit
This commit is contained in:
64
.gitignore
vendored
Normal file
64
.gitignore
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/c++,clion
|
||||||
|
# Edit at https://www.gitignore.io/?templates=c++,clion
|
||||||
|
|
||||||
|
### C++ ###
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
### CLion ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/c++,clion
|
||||||
|
|
||||||
|
*.spv
|
||||||
15
CMakeLists.txt
Normal file
15
CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(VulkanTest)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
find_package(glfw3 REQUIRED)
|
||||||
|
find_package(Vulkan REQUIRED)
|
||||||
|
|
||||||
|
add_compile_definitions(WITH_VALIDATION_LAYERS)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
|
||||||
|
FILE(GLOB_RECURSE CXX_SOURCES src/*.cpp)
|
||||||
|
|
||||||
|
add_executable(VulkanTest ${CXX_SOURCES})
|
||||||
|
target_link_libraries(VulkanTest glfw vulkan)
|
||||||
10
shaders/shader.frag
Normal file
10
shaders/shader.frag
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#version 450
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 fragColor;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
outColor = vec4(fragColor, 1.0);
|
||||||
|
}
|
||||||
21
shaders/shader.vert
Normal file
21
shaders/shader.vert
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#version 450
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
661
src/HelloTriangleApplication.cpp
Normal file
661
src/HelloTriangleApplication.cpp
Normal file
@@ -0,0 +1,661 @@
|
|||||||
|
//
|
||||||
|
// Created by rick on 08-05-20.
|
||||||
|
//
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#include "HelloTriangleApplication.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#define GLFW_INCLUDE_VULKAN
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include "debugLayers.h"
|
||||||
|
#include "queues.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
const std::vector<const char *> deviceExtensions = {
|
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkShaderModule HelloTriangleApplication::createShaderModule(const std::vector<char> &code) {
|
||||||
|
VkShaderModuleCreateInfo 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 (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create shader module");
|
||||||
|
}
|
||||||
|
return shaderModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSuitableDevice(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
|
VkPhysicalDeviceProperties deviceProperties;
|
||||||
|
VkPhysicalDeviceFeatures features;
|
||||||
|
QueueFamilyIndices indices;
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceProperties(device, &deviceProperties);
|
||||||
|
vkGetPhysicalDeviceFeatures(device, &features);
|
||||||
|
indices = findQueueFamilies(device, surface);
|
||||||
|
|
||||||
|
bool extensionSupport = checkDeviceExtensionSupport(device, deviceExtensions);
|
||||||
|
bool swapChainAdequate = false;
|
||||||
|
if (extensionSupport) {
|
||||||
|
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device, surface);
|
||||||
|
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
|
||||||
|
features.geometryShader &&
|
||||||
|
indices.isComplete() &&
|
||||||
|
extensionSupport &&
|
||||||
|
swapChainAdequate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::run() {
|
||||||
|
initWindow();
|
||||||
|
initVulkan();
|
||||||
|
mainLoop();
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::initWindow() {
|
||||||
|
glfwInit();
|
||||||
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||||
|
|
||||||
|
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createInstance() {
|
||||||
|
#ifdef WITH_VALIDATION_LAYERS
|
||||||
|
if (!checkValidationLayerSupport()) {
|
||||||
|
throw std::runtime_error("validation layer support required");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
VkApplicationInfo appInfo{};
|
||||||
|
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
|
appInfo.pApplicationName = "Hello Triangle";
|
||||||
|
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||||
|
appInfo.pEngineName = "No Engine";
|
||||||
|
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||||
|
appInfo.apiVersion = VK_API_VERSION_1_0;
|
||||||
|
|
||||||
|
VkInstanceCreateInfo createInfo{};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||||
|
createInfo.pApplicationInfo = &appInfo;
|
||||||
|
|
||||||
|
auto extensions = getRequiredExtensions();
|
||||||
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||||||
|
createInfo.ppEnabledExtensionNames = extensions.data();
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef WITH_VALIDATION_LAYERS
|
||||||
|
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
|
||||||
|
createInfo.ppEnabledLayerNames = validationLayers.data();
|
||||||
|
#else
|
||||||
|
createInfo.enabledLayerCount = 0;
|
||||||
|
createInfo.ppEnabledLayerNames = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create instance!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createSurface() {
|
||||||
|
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create surface");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::pickPhysicalDevice() {
|
||||||
|
uint32_t deviceCount = 0;
|
||||||
|
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
|
||||||
|
if (deviceCount == 0) {
|
||||||
|
throw std::runtime_error("No devices available");
|
||||||
|
}
|
||||||
|
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||||||
|
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
|
||||||
|
for (const auto &device: devices) {
|
||||||
|
if (isSuitableDevice(device, surface)) {
|
||||||
|
physicalDevice = device;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (physicalDevice == VK_NULL_HANDLE) {
|
||||||
|
throw std::runtime_error("No suitable device found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createLogicalDevice() {
|
||||||
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface);
|
||||||
|
|
||||||
|
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
||||||
|
std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()};
|
||||||
|
float queuePriority = 1.0f;
|
||||||
|
for (uint32_t queueFamily : uniqueQueueFamilies) {
|
||||||
|
VkDeviceQueueCreateInfo queueCreateInfo{};
|
||||||
|
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
queueCreateInfo.queueFamilyIndex = queueFamily;
|
||||||
|
queueCreateInfo.queueCount = 1;
|
||||||
|
queueCreateInfo.pQueuePriorities = &queuePriority;
|
||||||
|
queueCreateInfos.push_back(queueCreateInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures deviceFeatures{};
|
||||||
|
|
||||||
|
VkDeviceCreateInfo createInfo{};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
|
createInfo.pQueueCreateInfos = queueCreateInfos.data();
|
||||||
|
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
|
||||||
|
createInfo.pEnabledFeatures = &deviceFeatures;
|
||||||
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
|
||||||
|
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
|
||||||
|
createInfo.enabledLayerCount = 0;
|
||||||
|
|
||||||
|
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create logical device");
|
||||||
|
}
|
||||||
|
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
|
||||||
|
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createSwapChain() {
|
||||||
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface);
|
||||||
|
uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};
|
||||||
|
SwapChainSupportDetails swapChainSupportDetails = querySwapChainSupport(physicalDevice, surface);
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupportDetails.formats);
|
||||||
|
VkPresentModeKHR presentMode = chooseSwapChainPresentMode(swapChainSupportDetails.presentModes);
|
||||||
|
VkExtent2D extent = chooseSwapExtent(swapChainSupportDetails.capabilities, WIDTH, HEIGHT);
|
||||||
|
uint32_t imageCount = swapChainSupportDetails.capabilities.minImageCount + 1;
|
||||||
|
if (swapChainSupportDetails.capabilities.maxImageCount > 0 &&
|
||||||
|
imageCount > swapChainSupportDetails.capabilities.maxImageCount) {
|
||||||
|
imageCount = swapChainSupportDetails.capabilities.maxImageCount;
|
||||||
|
}
|
||||||
|
VkSwapchainCreateInfoKHR 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;
|
||||||
|
if (indices.graphicsFamily != indices.presentFamily) {
|
||||||
|
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||||
|
createInfo.queueFamilyIndexCount = 2;
|
||||||
|
createInfo.pQueueFamilyIndices = queueFamilyIndices;
|
||||||
|
} else {
|
||||||
|
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
createInfo.queueFamilyIndexCount = 0;
|
||||||
|
createInfo.pQueueFamilyIndices = nullptr;
|
||||||
|
}
|
||||||
|
createInfo.preTransform = swapChainSupportDetails.capabilities.currentTransform;
|
||||||
|
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
createInfo.presentMode = presentMode;
|
||||||
|
createInfo.clipped = VK_TRUE;
|
||||||
|
createInfo.oldSwapchain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create swapChain");
|
||||||
|
}
|
||||||
|
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
|
||||||
|
swapChainImages.resize(imageCount);
|
||||||
|
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
|
||||||
|
swapChainImageFormat = surfaceFormat.format;
|
||||||
|
swapChainExtent = extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createImageViews() {
|
||||||
|
swapChainImageViews.resize(swapChainImages.size());
|
||||||
|
for (int i = 0; i < swapChainImages.size(); i++) {
|
||||||
|
VkImageViewCreateInfo 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 (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create image view");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createRenderPass() {
|
||||||
|
VkAttachmentDescription colorAttachment{
|
||||||
|
.format = swapChainImageFormat,
|
||||||
|
.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 colorAttachmentRef{
|
||||||
|
.attachment = 0,
|
||||||
|
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
|
};
|
||||||
|
VkSubpassDescription subpass{
|
||||||
|
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
|
.colorAttachmentCount = 1,
|
||||||
|
.pColorAttachments = &colorAttachmentRef,
|
||||||
|
};
|
||||||
|
VkSubpassDependency dependency{
|
||||||
|
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||||
|
.dstSubpass = 0,
|
||||||
|
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
.srcAccessMask = 0,
|
||||||
|
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||||
|
};
|
||||||
|
VkRenderPassCreateInfo renderPassCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||||
|
.attachmentCount = 1,
|
||||||
|
.pAttachments = &colorAttachment,
|
||||||
|
.subpassCount = 1,
|
||||||
|
.pSubpasses = &subpass,
|
||||||
|
.dependencyCount = 1,
|
||||||
|
.pDependencies = &dependency,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (vkCreateRenderPass(device, &renderPassCreateInfo, nullptr, &renderPass) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create render pass");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createGraphicsPipeline() {
|
||||||
|
std::vector<char> vertShaderCode;
|
||||||
|
std::vector<char> fragShaderCode;
|
||||||
|
|
||||||
|
readFile("shaders/vert.spv", &vertShaderCode);
|
||||||
|
readFile("shaders/frag.spv", &fragShaderCode);
|
||||||
|
|
||||||
|
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
|
||||||
|
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo vertShaderStageInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||||
|
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||||
|
.module = vertShaderModule,
|
||||||
|
.pName = "main",
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo fragShaderStageInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||||
|
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
|
.module = fragShaderModule,
|
||||||
|
.pName = "main",
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
|
||||||
|
|
||||||
|
VkPipelineVertexInputStateCreateInfo vertexInputInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||||
|
.vertexBindingDescriptionCount = 0,
|
||||||
|
.pVertexBindingDescriptions = nullptr,
|
||||||
|
.vertexAttributeDescriptionCount = 0,
|
||||||
|
.pVertexAttributeDescriptions = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineInputAssemblyStateCreateInfo inputAssembly{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||||
|
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||||
|
.primitiveRestartEnable = VK_FALSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkViewport viewport{
|
||||||
|
.x = 0.0f,
|
||||||
|
.y = 0.0f,
|
||||||
|
.width = (float) swapChainExtent.width,
|
||||||
|
.height = (float) swapChainExtent.height,
|
||||||
|
.minDepth = 0.0f,
|
||||||
|
.maxDepth = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkRect2D scissor{
|
||||||
|
.offset = {0, 0},
|
||||||
|
.extent = swapChainExtent,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineViewportStateCreateInfo viewportState{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||||
|
.viewportCount = 1,
|
||||||
|
.pViewports = &viewport,
|
||||||
|
.scissorCount = 1,
|
||||||
|
.pScissors = &scissor,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineRasterizationStateCreateInfo rasterizer{
|
||||||
|
.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.0f,
|
||||||
|
.depthBiasClamp = 0.0f,
|
||||||
|
.depthBiasSlopeFactor = 0.0f,
|
||||||
|
.lineWidth = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineMultisampleStateCreateInfo multisampling{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||||
|
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||||
|
.sampleShadingEnable = VK_FALSE,
|
||||||
|
.minSampleShading = 1.0f,
|
||||||
|
.pSampleMask = nullptr,
|
||||||
|
.alphaToCoverageEnable = VK_FALSE,
|
||||||
|
.alphaToOneEnable = VK_FALSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineColorBlendAttachmentState colorBlendAttachmentState{
|
||||||
|
.blendEnable = VK_TRUE,
|
||||||
|
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
|
||||||
|
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||||
|
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||||
|
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||||
|
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
|
||||||
|
VK_COLOR_COMPONENT_A_BIT,
|
||||||
|
};
|
||||||
|
// colorBlendAttachmentState.blendEnable = VK_FALSE;
|
||||||
|
// colorBlendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||||
|
// colorBlendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||||
|
// colorBlendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
|
||||||
|
// colorBlendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||||
|
// colorBlendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||||
|
// colorBlendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||||
|
VkPipelineColorBlendStateCreateInfo colorBlending{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||||
|
.logicOpEnable = VK_FALSE,
|
||||||
|
.logicOp = VK_LOGIC_OP_COPY,
|
||||||
|
.attachmentCount = 1,
|
||||||
|
.pAttachments = &colorBlendAttachmentState,
|
||||||
|
};
|
||||||
|
colorBlending.blendConstants[0] = 0.0f;
|
||||||
|
colorBlending.blendConstants[1] = 0.0f;
|
||||||
|
colorBlending.blendConstants[2] = 0.0f;
|
||||||
|
colorBlending.blendConstants[3] = 0.0f;
|
||||||
|
|
||||||
|
VkDynamicState dynamicStates[] = {
|
||||||
|
VK_DYNAMIC_STATE_VIEWPORT,
|
||||||
|
VK_DYNAMIC_STATE_LINE_WIDTH,
|
||||||
|
};
|
||||||
|
VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||||
|
.dynamicStateCount = 2,
|
||||||
|
.pDynamicStates = dynamicStates,
|
||||||
|
};
|
||||||
|
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||||
|
.setLayoutCount = 0,
|
||||||
|
.pSetLayouts = nullptr,
|
||||||
|
.pushConstantRangeCount = 0,
|
||||||
|
.pPushConstantRanges = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("failed to create pipeline layout");
|
||||||
|
}
|
||||||
|
|
||||||
|
VkGraphicsPipelineCreateInfo pipelineInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||||
|
.stageCount = 2,
|
||||||
|
.pStages = shaderStages,
|
||||||
|
.pVertexInputState = &vertexInputInfo,
|
||||||
|
.pInputAssemblyState = &inputAssembly,
|
||||||
|
.pViewportState = &viewportState,
|
||||||
|
.pRasterizationState = &rasterizer,
|
||||||
|
.pMultisampleState = &multisampling,
|
||||||
|
.pDepthStencilState = nullptr,
|
||||||
|
.pColorBlendState = &colorBlending,
|
||||||
|
.pDynamicState = nullptr,
|
||||||
|
.layout = pipelineLayout,
|
||||||
|
.renderPass = renderPass,
|
||||||
|
.subpass = 0,
|
||||||
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
|
.basePipelineIndex = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create graphics pipeline");
|
||||||
|
}
|
||||||
|
|
||||||
|
vkDestroyShaderModule(device, fragShaderModule, nullptr);
|
||||||
|
vkDestroyShaderModule(device, vertShaderModule, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createFrameBuffers() {
|
||||||
|
swapChainFrameBuffers.resize(swapChainImageViews.size());
|
||||||
|
for (size_t i = 0; i < swapChainImageViews.size(); i++) {
|
||||||
|
VkImageView attachments[] = {
|
||||||
|
swapChainImageViews[i]
|
||||||
|
};
|
||||||
|
VkFramebufferCreateInfo framebufferCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||||
|
.renderPass = renderPass,
|
||||||
|
.attachmentCount = 1,
|
||||||
|
.pAttachments = attachments,
|
||||||
|
.width = swapChainExtent.width,
|
||||||
|
.height = swapChainExtent.height,
|
||||||
|
.layers = 1
|
||||||
|
};
|
||||||
|
if (vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &swapChainFrameBuffers[i]) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create frame buffer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createCommandPool() {
|
||||||
|
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice, surface);
|
||||||
|
|
||||||
|
VkCommandPoolCreateInfo poolInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||||
|
.flags = 0,
|
||||||
|
.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create command pool");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createCommandBuffers() {
|
||||||
|
commandBuffers.resize(swapChainFrameBuffers.size());
|
||||||
|
|
||||||
|
VkCommandBufferAllocateInfo allocateInfo {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||||
|
.commandPool = commandPool,
|
||||||
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||||
|
.commandBufferCount = (uint32_t) commandBuffers.size(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (vkAllocateCommandBuffers(device, &allocateInfo, commandBuffers.data()) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to allocate command buffers");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < commandBuffers.size(); i++) {
|
||||||
|
VkCommandBufferBeginInfo beginInfo {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||||
|
.flags = 0,
|
||||||
|
.pInheritanceInfo = nullptr,
|
||||||
|
};
|
||||||
|
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to begin recording command buffer");
|
||||||
|
}
|
||||||
|
VkRenderPassBeginInfo renderPassInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||||
|
.renderPass = renderPass,
|
||||||
|
.framebuffer = swapChainFrameBuffers[i],
|
||||||
|
};
|
||||||
|
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(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
|
||||||
|
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
|
||||||
|
vkCmdEndRenderPass(commandBuffers[i]);
|
||||||
|
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error{"Failed to record command buffer"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::createSyncObjects() {
|
||||||
|
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
imagesInFlight.resize(MAX_FRAMES_IN_FLIGHT, VK_NULL_HANDLE);
|
||||||
|
VkSemaphoreCreateInfo semaphoreCreateInfo {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||||
|
};
|
||||||
|
VkFenceCreateInfo fenceInfo {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||||
|
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
|
||||||
|
if (vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
|
||||||
|
vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
|
||||||
|
vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i])) {
|
||||||
|
throw std::runtime_error("failed to create semaphore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::initVulkan() {
|
||||||
|
createInstance();
|
||||||
|
#ifdef WITH_VALIDATION_LAYERS
|
||||||
|
setupDebugMessenger(instance, &debugMessenger);
|
||||||
|
#endif
|
||||||
|
createSurface();
|
||||||
|
pickPhysicalDevice();
|
||||||
|
createLogicalDevice();
|
||||||
|
createSwapChain();
|
||||||
|
createImageViews();
|
||||||
|
createRenderPass();
|
||||||
|
createGraphicsPipeline();
|
||||||
|
createFrameBuffers();
|
||||||
|
createCommandPool();
|
||||||
|
createCommandBuffers();
|
||||||
|
createSyncObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::mainLoop() {
|
||||||
|
while (!glfwWindowShouldClose(window)) {
|
||||||
|
glfwPollEvents();
|
||||||
|
drawFrame();
|
||||||
|
}
|
||||||
|
vkDeviceWaitIdle(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::drawFrame() {
|
||||||
|
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
|
||||||
|
uint32_t imageIndex;
|
||||||
|
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
|
||||||
|
|
||||||
|
if (imagesInFlight[currentFrame] != VK_NULL_HANDLE) {
|
||||||
|
vkWaitForFences(device, 1, &imagesInFlight[currentFrame], VK_TRUE, UINT64_MAX);
|
||||||
|
}
|
||||||
|
imagesInFlight[currentFrame] = inFlightFences[currentFrame];
|
||||||
|
|
||||||
|
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
|
||||||
|
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
|
||||||
|
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
||||||
|
VkSubmitInfo submitInfo {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
|
.waitSemaphoreCount = 1,
|
||||||
|
.pWaitSemaphores = waitSemaphores,
|
||||||
|
.pWaitDstStageMask = waitStages,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
.pCommandBuffers = &commandBuffers[imageIndex],
|
||||||
|
.signalSemaphoreCount = 1,
|
||||||
|
.pSignalSemaphores = signalSemaphores,
|
||||||
|
};
|
||||||
|
|
||||||
|
vkResetFences(device, 1, &inFlightFences[currentFrame]);
|
||||||
|
|
||||||
|
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to submit draw command");
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSwapchainKHR swapchains[] = {swapChain};
|
||||||
|
VkPresentInfoKHR presentInfo {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||||
|
.waitSemaphoreCount = 1,
|
||||||
|
.pWaitSemaphores = signalSemaphores,
|
||||||
|
.swapchainCount = 1,
|
||||||
|
.pSwapchains = swapchains,
|
||||||
|
.pImageIndices = &imageIndex,
|
||||||
|
.pResults = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
vkQueuePresentKHR(presentQueue, &presentInfo);
|
||||||
|
|
||||||
|
// vkQueueWaitIdle(presentQueue);
|
||||||
|
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloTriangleApplication::cleanup() {
|
||||||
|
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
|
||||||
|
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
|
||||||
|
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
|
||||||
|
vkDestroyFence(device, inFlightFences[i], 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);
|
||||||
|
#ifdef WITH_VALIDATION_LAYERS
|
||||||
|
DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
|
||||||
|
#endif
|
||||||
|
vkDestroyInstance(instance, nullptr);
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
glfwTerminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const char *> HelloTriangleApplication::getRequiredExtensions() {
|
||||||
|
uint32_t glfwExtensionCount;
|
||||||
|
const char **glfwExtensions;
|
||||||
|
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
||||||
|
|
||||||
|
std::vector<const char *> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
|
||||||
|
|
||||||
|
#ifdef WITH_VALIDATION_LAYERS
|
||||||
|
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
82
src/HelloTriangleApplication.h
Normal file
82
src/HelloTriangleApplication.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
//
|
||||||
|
// Created by rick on 08-05-20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef VULKANTEST_HELLOTRIANGLEAPPLICATION_H
|
||||||
|
#define VULKANTEST_HELLOTRIANGLEAPPLICATION_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define GLFW_INCLUDE_VULKAN
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
const int MAX_FRAMES_IN_FLIGHT = 2;
|
||||||
|
|
||||||
|
const int WIDTH = 800;
|
||||||
|
const int HEIGHT = 600;
|
||||||
|
|
||||||
|
class HelloTriangleApplication {
|
||||||
|
public:
|
||||||
|
void run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLFWwindow* window;
|
||||||
|
VkInstance instance;
|
||||||
|
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||||
|
VkDevice device;
|
||||||
|
VkQueue graphicsQueue;
|
||||||
|
VkQueue presentQueue;
|
||||||
|
VkSurfaceKHR surface;
|
||||||
|
VkDebugUtilsMessengerEXT debugMessenger;
|
||||||
|
VkSwapchainKHR swapChain;
|
||||||
|
std::vector<VkImage> swapChainImages;
|
||||||
|
std::vector<VkImageView> swapChainImageViews;
|
||||||
|
VkFormat swapChainImageFormat;
|
||||||
|
VkExtent2D swapChainExtent;
|
||||||
|
VkRenderPass renderPass;
|
||||||
|
VkPipelineLayout pipelineLayout;
|
||||||
|
VkPipeline graphicsPipeline;
|
||||||
|
std::vector<VkFramebuffer> swapChainFrameBuffers;
|
||||||
|
VkCommandPool commandPool;
|
||||||
|
std::vector<VkCommandBuffer> commandBuffers;
|
||||||
|
|
||||||
|
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||||
|
std::vector<VkSemaphore> renderFinishedSemaphores;
|
||||||
|
std::vector<VkFence> inFlightFences;
|
||||||
|
std::vector<VkFence> imagesInFlight;
|
||||||
|
|
||||||
|
int currentFrame = 0;
|
||||||
|
|
||||||
|
// window creation
|
||||||
|
void initWindow();
|
||||||
|
|
||||||
|
// Vulkan initialisation
|
||||||
|
void createInstance();
|
||||||
|
void pickPhysicalDevice();
|
||||||
|
void createLogicalDevice();
|
||||||
|
void createSurface();
|
||||||
|
void createSwapChain();
|
||||||
|
void createImageViews();
|
||||||
|
void createRenderPass();
|
||||||
|
void createGraphicsPipeline();
|
||||||
|
void createFrameBuffers();
|
||||||
|
void createCommandPool();
|
||||||
|
void createCommandBuffers();
|
||||||
|
void createSyncObjects();
|
||||||
|
void initVulkan();
|
||||||
|
|
||||||
|
// main loop
|
||||||
|
void mainLoop();
|
||||||
|
void drawFrame();
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
// utility functions
|
||||||
|
std::vector<const char*> getRequiredExtensions();
|
||||||
|
VkShaderModule createShaderModule(const std::vector<char> &code);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //VULKANTEST_HELLOTRIANGLEAPPLICATION_H
|
||||||
71
src/debugLayers.cpp
Normal file
71
src/debugLayers.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
//
|
||||||
|
// Created by rick on 24-05-20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "debugLayers.h"
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#ifdef WITH_VALIDATION_LAYERS
|
||||||
|
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
||||||
|
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||||
|
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||||
|
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||||
|
void* pUserData) {
|
||||||
|
std::cerr << "Validation layer: " << pCallbackData->pMessage << std::endl;
|
||||||
|
|
||||||
|
return VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
|
||||||
|
auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
|
||||||
|
if (func != nullptr) {
|
||||||
|
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
|
||||||
|
} else {
|
||||||
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
|
||||||
|
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
|
||||||
|
if (func != nullptr) {
|
||||||
|
func(instance, debugMessenger, pAllocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkValidationLayerSupport() {
|
||||||
|
uint32_t layerCount;
|
||||||
|
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkLayerProperties> availableLayers(layerCount);
|
||||||
|
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
|
||||||
|
|
||||||
|
bool layerFound = false;
|
||||||
|
for (const auto& layerProperties : availableLayers) {
|
||||||
|
for (const char* validationLayer : validationLayers) {
|
||||||
|
if (strcmp(validationLayer, layerProperties.layerName) == 0) {
|
||||||
|
layerFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (layerFound) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return layerFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupDebugMessenger(VkInstance instance, VkDebugUtilsMessengerEXT* debugMessenger) {
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT createInfo{};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||||
|
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||||
|
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||||
|
createInfo.pfnUserCallback = debugCallback;
|
||||||
|
createInfo.pUserData = nullptr;
|
||||||
|
|
||||||
|
if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, debugMessenger) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to setup debug messenger");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
26
src/debugLayers.h
Normal file
26
src/debugLayers.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Created by rick on 24-05-20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef VULKANTEST_DEBUGLAYERS_H
|
||||||
|
#define VULKANTEST_DEBUGLAYERS_H
|
||||||
|
#ifdef WITH_VALIDATION_LAYERS
|
||||||
|
#include <vector>
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
const std::vector<const char*> validationLayers = {
|
||||||
|
"VK_LAYER_KHRONOS_validation",
|
||||||
|
};
|
||||||
|
|
||||||
|
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
||||||
|
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||||
|
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||||
|
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||||
|
void* pUserData);
|
||||||
|
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger);
|
||||||
|
void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator);
|
||||||
|
bool checkValidationLayerSupport();
|
||||||
|
void setupDebugMessenger(VkInstance instance, VkDebugUtilsMessengerEXT* debugMessenger);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif //VULKANTEST_DEBUGLAYERS_H
|
||||||
17
src/main.cpp
Normal file
17
src/main.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "HelloTriangleApplication.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
HelloTriangleApplication app;
|
||||||
|
try {
|
||||||
|
app.run();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
97
src/queues.cpp
Normal file
97
src/queues.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
//
|
||||||
|
// Created by rick on 24-05-20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "queues.h"
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
bool checkDeviceExtensionSupport(VkPhysicalDevice device, std::vector<const char*> deviceExtensions) {
|
||||||
|
uint32_t extensionCount;
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
|
||||||
|
|
||||||
|
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
|
||||||
|
for (const auto& extension : availableExtensions) {
|
||||||
|
requiredExtensions.erase(extension.extensionName);
|
||||||
|
}
|
||||||
|
return requiredExtensions.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
|
QueueFamilyIndices indices;
|
||||||
|
uint32_t queueFamilyCount = 0;
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyCount);
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilyProperties.data());
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& queueFamily : queueFamilyProperties) {
|
||||||
|
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||||
|
indices.graphicsFamily = i;
|
||||||
|
}
|
||||||
|
VkBool32 presentSupport = false;
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
|
||||||
|
if (presentSupport == VK_TRUE) {
|
||||||
|
indices.presentFamily = i;
|
||||||
|
}
|
||||||
|
if (indices.isComplete()) {
|
||||||
|
break; // have all queues
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// Assign index to queue families that could be found
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
|
SwapChainSupportDetails details;
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
|
||||||
|
|
||||||
|
uint32_t formatCount;
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
|
||||||
|
if (formatCount > 0) {
|
||||||
|
details.formats.resize(formatCount);
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t presentModeCount;
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
|
||||||
|
if (presentModeCount > 0) {
|
||||||
|
details.presentModes.resize(presentModeCount);
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
|
||||||
|
for (const auto& format : availableFormats) {
|
||||||
|
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return availableFormats[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPresentModeKHR chooseSwapChainPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
|
||||||
|
// for (const auto& mode : availablePresentModes) {
|
||||||
|
// if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||||
|
// return mode;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, uint32_t width, uint32_t height) {
|
||||||
|
if (capabilities.currentExtent.width != UINT32_MAX) {
|
||||||
|
return capabilities.currentExtent;
|
||||||
|
}
|
||||||
|
VkExtent2D actualExtend = {width, height};
|
||||||
|
actualExtend.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtend.width));
|
||||||
|
actualExtend.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtend.height));
|
||||||
|
return actualExtend;
|
||||||
|
}
|
||||||
33
src/queues.h
Normal file
33
src/queues.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// Created by rick on 24-05-20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef VULKANTEST_QUEUES_H
|
||||||
|
#define VULKANTEST_QUEUES_H
|
||||||
|
#include <optional>
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct QueueFamilyIndices {
|
||||||
|
std::optional<uint32_t> graphicsFamily;
|
||||||
|
std::optional<uint32_t> presentFamily;
|
||||||
|
|
||||||
|
bool isComplete() {
|
||||||
|
return graphicsFamily.has_value() && presentFamily.has_value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SwapChainSupportDetails {
|
||||||
|
VkSurfaceCapabilitiesKHR capabilities{};
|
||||||
|
std::vector<VkSurfaceFormatKHR> formats;
|
||||||
|
std::vector<VkPresentModeKHR> presentModes;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool checkDeviceExtensionSupport(VkPhysicalDevice device, std::vector<const char*> deviceExtensions);
|
||||||
|
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||||
|
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||||
|
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
|
||||||
|
VkPresentModeKHR chooseSwapChainPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
|
||||||
|
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
#endif //VULKANTEST_QUEUES_H
|
||||||
23
src/util.cpp
Normal file
23
src/util.cpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// Created by rick on 24-05-20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
void readFile(const std::string& filename, std::vector<char>* target) {
|
||||||
|
auto actualPath = std::filesystem::path("..") / filename;
|
||||||
|
|
||||||
|
std::ifstream file(actualPath.string(), std::ios::ate | std::ios::binary);
|
||||||
|
|
||||||
|
if (!file.is_open()) {
|
||||||
|
throw std::runtime_error("failed to open file");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t fileSize = (size_t) file.tellg();
|
||||||
|
target->resize(fileSize);
|
||||||
|
file.seekg(0);
|
||||||
|
file.read(target->data(), fileSize);
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
12
src/util.h
Normal file
12
src/util.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// Created by rick on 24-05-20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef VULKANTEST_UTIL_H
|
||||||
|
#define VULKANTEST_UTIL_H
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
void readFile(const std::string& filename, std::vector<char>* target);
|
||||||
|
|
||||||
|
#endif //VULKANTEST_UTIL_H
|
||||||
Reference in New Issue
Block a user