|
|
@ -0,0 +1,780 @@ |
|
|
|
|
|
|
|
#define GLFW_INCLUDE_VULKAN |
|
|
|
|
|
|
|
#include <GLFW/glfw3.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define GLM_FORCE_RADIANS |
|
|
|
|
|
|
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE |
|
|
|
|
|
|
|
#include <glm/vec4.hpp> |
|
|
|
|
|
|
|
#include <glm/mat4x4.hpp> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <iostream> |
|
|
|
|
|
|
|
#include <vector> |
|
|
|
|
|
|
|
#include <cstring> |
|
|
|
|
|
|
|
#include <optional> |
|
|
|
|
|
|
|
#include <set> |
|
|
|
|
|
|
|
#include <fstream> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const std::vector<const char*> deviceExtensions = { |
|
|
|
|
|
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef NDEBUG |
|
|
|
|
|
|
|
#define ENABLE_VALIDATION_LAYERS |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef ENABLE_VALIDATION_LAYERS |
|
|
|
|
|
|
|
const std::vector<const char*> validationLayers = { |
|
|
|
|
|
|
|
"VK_LAYER_KHRONOS_validation" |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
bool checkValidationLayerSupport(){ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t layerCount; |
|
|
|
|
|
|
|
vkEnumerateInstanceLayerProperties(&layerCount, nullptr); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<VkLayerProperties> availableLayers(layerCount); |
|
|
|
|
|
|
|
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const char* layerName : validationLayers){ |
|
|
|
|
|
|
|
bool layerFound = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& layerProperties : availableLayers){ |
|
|
|
|
|
|
|
if (strcmp(layerName, layerProperties.layerName) == 0){ |
|
|
|
|
|
|
|
layerFound = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!layerFound){ |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 = file.tellg(); |
|
|
|
|
|
|
|
std::vector<char> buffer(fileSize); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
file.seekg(0); |
|
|
|
|
|
|
|
file.read(buffer.data(), static_cast<std::streamsize>(fileSize)); |
|
|
|
|
|
|
|
file.close(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return buffer; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MyApp { |
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
void run(){ |
|
|
|
|
|
|
|
initWindow(); |
|
|
|
|
|
|
|
initVulkan(); |
|
|
|
|
|
|
|
mainLoop(); |
|
|
|
|
|
|
|
cleanup(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
|
GLFWwindow *window = nullptr; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uint32_t WIDTH = 800; |
|
|
|
|
|
|
|
const uint32_t HEIGHT = 600; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void initWindow(){ |
|
|
|
|
|
|
|
glfwInit(); |
|
|
|
|
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); |
|
|
|
|
|
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); |
|
|
|
|
|
|
|
window = glfwCreateWindow((int)WIDTH, (int)HEIGHT, "Vulkan Simulation", nullptr, nullptr); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void initVulkan(){ |
|
|
|
|
|
|
|
createInstance(); |
|
|
|
|
|
|
|
createSurface(); |
|
|
|
|
|
|
|
pickPhysicalDevice(); |
|
|
|
|
|
|
|
createLogicalDevice(); |
|
|
|
|
|
|
|
createSwapchain(); |
|
|
|
|
|
|
|
createImageViews(); |
|
|
|
|
|
|
|
createRenderPass(); |
|
|
|
|
|
|
|
createGraphicsPipeline(); |
|
|
|
|
|
|
|
createFramebuffers(); |
|
|
|
|
|
|
|
createCommandPool(); |
|
|
|
|
|
|
|
createCommandBuffer(); |
|
|
|
|
|
|
|
createSyncObjects(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkInstance instance = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
void createInstance(){ |
|
|
|
|
|
|
|
#ifdef ENABLE_VALIDATION_LAYERS |
|
|
|
|
|
|
|
if (!checkValidationLayerSupport()){ |
|
|
|
|
|
|
|
throw std::runtime_error("validation layers requested, but not available!"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkApplicationInfo applicationInfo {}; |
|
|
|
|
|
|
|
applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
|
|
|
|
|
|
|
applicationInfo.pApplicationName = "Coole Simulation"; |
|
|
|
|
|
|
|
applicationInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); |
|
|
|
|
|
|
|
applicationInfo.pEngineName = "No Engine"; |
|
|
|
|
|
|
|
applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); |
|
|
|
|
|
|
|
applicationInfo.apiVersion = VK_API_VERSION_1_0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t glfwExtensionCount = 0; |
|
|
|
|
|
|
|
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkInstanceCreateInfo instanceCreateInfo {}; |
|
|
|
|
|
|
|
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
|
|
|
|
|
|
|
instanceCreateInfo.pApplicationInfo = &applicationInfo; |
|
|
|
|
|
|
|
instanceCreateInfo.enabledExtensionCount = glfwExtensionCount; |
|
|
|
|
|
|
|
instanceCreateInfo.ppEnabledExtensionNames = glfwExtensions; |
|
|
|
|
|
|
|
#ifdef ENABLE_VALIDATION_LAYERS |
|
|
|
|
|
|
|
instanceCreateInfo.enabledLayerCount = validationLayers.size(); |
|
|
|
|
|
|
|
instanceCreateInfo.ppEnabledLayerNames = validationLayers.data(); |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
instanceCreateInfo.enabledLayerCount = 0; |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (vkCreateInstance(&instanceCreateInfo, nullptr, &instance) != VK_SUCCESS){ |
|
|
|
|
|
|
|
throw std::runtime_error("failed to create instance!"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSurfaceKHR surface = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
void createSurface(){ |
|
|
|
|
|
|
|
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS){ |
|
|
|
|
|
|
|
throw std::runtime_error("failed to create window surface!"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
void pickPhysicalDevice(){ |
|
|
|
|
|
|
|
uint32_t deviceCount = 0; |
|
|
|
|
|
|
|
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); |
|
|
|
|
|
|
|
if (deviceCount == 0){ |
|
|
|
|
|
|
|
throw std::runtime_error("failed to find GPUs with Vulkan support!"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
std::vector<VkPhysicalDevice> devices(deviceCount); |
|
|
|
|
|
|
|
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const VkPhysicalDevice &device : devices){ |
|
|
|
|
|
|
|
if (isDeviceSuitable(device)){ |
|
|
|
|
|
|
|
physicalDevice = device; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (physicalDevice == VK_NULL_HANDLE){ |
|
|
|
|
|
|
|
throw std::runtime_error("failed to find a suitable GPU!"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDeviceProperties properties; |
|
|
|
|
|
|
|
vkGetPhysicalDeviceProperties(physicalDevice, &properties); |
|
|
|
|
|
|
|
printf("Picked physical device: %s\n", properties.deviceName); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct QueueFamilyIndices { |
|
|
|
|
|
|
|
std::optional<uint32_t> graphicsFamily; |
|
|
|
|
|
|
|
std::optional<uint32_t> computeFamily; |
|
|
|
|
|
|
|
std::optional<uint32_t> presentFamily; |
|
|
|
|
|
|
|
bool isComplete() const { |
|
|
|
|
|
|
|
return graphicsFamily.has_value() && computeFamily.has_value() && presentFamily.has_value(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
std::set<uint32_t> uniqueQueueFamilies(){ |
|
|
|
|
|
|
|
return {graphicsFamily.value(), presentFamily.value(), computeFamily.value()}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device){ |
|
|
|
|
|
|
|
uint32_t queueFamilyCount = 0; |
|
|
|
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); |
|
|
|
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QueueFamilyIndices indices {}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t i = 0; |
|
|
|
|
|
|
|
for (const VkQueueFamilyProperties& queueFamilyProperties : queueFamilies){ |
|
|
|
|
|
|
|
if (queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT){ |
|
|
|
|
|
|
|
indices.graphicsFamily = i; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (queueFamilyProperties.queueFlags & VK_QUEUE_COMPUTE_BIT){ |
|
|
|
|
|
|
|
indices.computeFamily = i; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
VkBool32 presentSupport = false; |
|
|
|
|
|
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); |
|
|
|
|
|
|
|
if (presentSupport){ |
|
|
|
|
|
|
|
indices.presentFamily = i; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (indices.isComplete()){ |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
i++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return indices; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool isDeviceSuitable(VkPhysicalDevice device){ |
|
|
|
|
|
|
|
VkPhysicalDeviceProperties deviceProperties; |
|
|
|
|
|
|
|
vkGetPhysicalDeviceProperties(device, &deviceProperties); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDeviceFeatures deviceFeatures; |
|
|
|
|
|
|
|
vkGetPhysicalDeviceFeatures(device, &deviceFeatures); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QueueFamilyIndices indices = findQueueFamilies(device); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool extensionsSupported = checkDeviceExtensionSupport(device); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool swapChainAdequate = false; |
|
|
|
|
|
|
|
if (extensionsSupported){ |
|
|
|
|
|
|
|
SwapchainSupportDetails details = querySwapchainSupport(device); |
|
|
|
|
|
|
|
swapChainAdequate = !details.formats.empty() && !details.presentModes.empty(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return indices.isComplete() && extensionsSupported && swapChainAdequate; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool checkDeviceExtensionSupport(VkPhysicalDevice device){ |
|
|
|
|
|
|
|
std::set<std::string> required(deviceExtensions.begin(), deviceExtensions.end()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t extensionCount; |
|
|
|
|
|
|
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<VkExtensionProperties> availableExtensions(extensionCount); |
|
|
|
|
|
|
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const VkExtensionProperties& extension : availableExtensions){ |
|
|
|
|
|
|
|
required.erase(extension.extensionName); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return required.empty(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct SwapchainSupportDetails { |
|
|
|
|
|
|
|
VkSurfaceCapabilitiesKHR capabilities {}; |
|
|
|
|
|
|
|
std::vector<VkSurfaceFormatKHR> formats; |
|
|
|
|
|
|
|
std::vector<VkPresentModeKHR> presentModes; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
SwapchainSupportDetails querySwapchainSupport(VkPhysicalDevice device){ |
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkDevice device = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
VkQueue graphicsQueue = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
VkQueue presentQueue = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
void createLogicalDevice(){ |
|
|
|
|
|
|
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float queuePriority = 1.0f; |
|
|
|
|
|
|
|
for (uint32_t queueFamily : indices.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 = queueCreateInfos.size(); |
|
|
|
|
|
|
|
createInfo.pEnabledFeatures = &deviceFeatures; |
|
|
|
|
|
|
|
createInfo.enabledExtensionCount = deviceExtensions.size(); |
|
|
|
|
|
|
|
createInfo.ppEnabledExtensionNames = deviceExtensions.data(); |
|
|
|
|
|
|
|
#ifdef ENABLE_VALIDATION_LAYERS |
|
|
|
|
|
|
|
createInfo.enabledLayerCount = validationLayers.size(); |
|
|
|
|
|
|
|
createInfo.ppEnabledLayerNames = validationLayers.data(); |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
createInfo.enabledLayerCount = 0; |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static VkSurfaceFormatKHR 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]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes){ |
|
|
|
|
|
|
|
return VK_PRESENT_MODE_FIFO_KHR; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities){ |
|
|
|
|
|
|
|
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()){ |
|
|
|
|
|
|
|
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 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 {}; |
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
|
|
|
|
|
|
uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.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 (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; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSwapchainKHR swapchain = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
std::vector<VkImage> swapchainImages; |
|
|
|
|
|
|
|
VkFormat swapchainImageFormat {}; |
|
|
|
|
|
|
|
VkExtent2D swapchainExtent {}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<VkImageView> swapchainImageViews; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void createImageViews(){ |
|
|
|
|
|
|
|
swapchainImageViews.resize(swapchainImages.size()); |
|
|
|
|
|
|
|
for (size_t i = 0; i < swapchainImages.size(); i++){ |
|
|
|
|
|
|
|
VkImageViewCreateInfo createInfo {}; |
|
|
|
|
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; |
|
|
|
|
|
|
|
createInfo.format = swapchainImageFormat; |
|
|
|
|
|
|
|
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
|
|
|
|
|
|
|
createInfo.image = swapchainImages[i]; |
|
|
|
|
|
|
|
createInfo.components = { |
|
|
|
|
|
|
|
VK_COMPONENT_SWIZZLE_IDENTITY, |
|
|
|
|
|
|
|
VK_COMPONENT_SWIZZLE_IDENTITY, |
|
|
|
|
|
|
|
VK_COMPONENT_SWIZZLE_IDENTITY, |
|
|
|
|
|
|
|
VK_COMPONENT_SWIZZLE_IDENTITY, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
createInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; |
|
|
|
|
|
|
|
if (vkCreateImageView(device, &createInfo, nullptr, &swapchainImageViews[i]) != VK_SUCCESS){ |
|
|
|
|
|
|
|
throw std::runtime_error("failed to create image views!"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkShaderModule 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; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void createRenderPass(){ |
|
|
|
|
|
|
|
VkAttachmentDescription 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 {}; |
|
|
|
|
|
|
|
colorAttachmentRef.attachment = 0; |
|
|
|
|
|
|
|
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSubpassDescription subpass {}; |
|
|
|
|
|
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
|
|
|
|
|
|
|
subpass.colorAttachmentCount = 1; |
|
|
|
|
|
|
|
subpass.pColorAttachments = &colorAttachmentRef; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSubpassDependency dependency {}; |
|
|
|
|
|
|
|
dependency.srcSubpass = VK_SUBPASS_EXTERNAL; |
|
|
|
|
|
|
|
dependency.dstSubpass = 0; |
|
|
|
|
|
|
|
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
|
|
|
|
|
|
|
dependency.srcAccessMask = 0; |
|
|
|
|
|
|
|
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
|
|
|
|
|
|
|
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkRenderPassCreateInfo 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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkRenderPass renderPass = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void createGraphicsPipeline(){ |
|
|
|
|
|
|
|
auto vertShaderCode = readFile("shaders/vert.spv"); |
|
|
|
|
|
|
|
auto fragShaderCode = readFile("shaders/frag.spv"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); |
|
|
|
|
|
|
|
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo 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 {}; |
|
|
|
|
|
|
|
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 {}; |
|
|
|
|
|
|
|
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
|
|
|
|
|
|
|
vertexInputInfo.vertexBindingDescriptionCount = 0; |
|
|
|
|
|
|
|
vertexInputInfo.vertexAttributeDescriptionCount = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo inputAssembly {}; |
|
|
|
|
|
|
|
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
|
|
|
|
|
|
|
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; |
|
|
|
|
|
|
|
inputAssembly.primitiveRestartEnable = VK_FALSE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<VkDynamicState> dynamicStates = { |
|
|
|
|
|
|
|
VK_DYNAMIC_STATE_VIEWPORT, |
|
|
|
|
|
|
|
VK_DYNAMIC_STATE_SCISSOR |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineDynamicStateCreateInfo dynamicState {}; |
|
|
|
|
|
|
|
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
|
|
|
|
|
|
|
dynamicState.dynamicStateCount = dynamicStates.size(); |
|
|
|
|
|
|
|
dynamicState.pDynamicStates = dynamicStates.data(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineViewportStateCreateInfo viewportState {}; |
|
|
|
|
|
|
|
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
|
|
|
|
|
|
|
viewportState.viewportCount = 1; |
|
|
|
|
|
|
|
viewportState.scissorCount = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineRasterizationStateCreateInfo 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; |
|
|
|
|
|
|
|
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; |
|
|
|
|
|
|
|
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; |
|
|
|
|
|
|
|
rasterizer.depthBiasClamp = VK_FALSE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisample {}; |
|
|
|
|
|
|
|
multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
|
|
|
|
|
|
|
multisample.sampleShadingEnable = VK_FALSE; |
|
|
|
|
|
|
|
multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineColorBlendAttachmentState 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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineColorBlendStateCreateInfo colorBlending {}; |
|
|
|
|
|
|
|
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
|
|
|
|
|
|
|
colorBlending.logicOpEnable = VK_FALSE; |
|
|
|
|
|
|
|
colorBlending.attachmentCount = 1; |
|
|
|
|
|
|
|
colorBlending.pAttachments = &colorBlendAttachment; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo {}; |
|
|
|
|
|
|
|
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkGraphicsPipelineCreateInfo 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 = &multisample; |
|
|
|
|
|
|
|
pipelineInfo.pColorBlendState = &colorBlending; |
|
|
|
|
|
|
|
pipelineInfo.pDynamicState = &dynamicState; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pipelineInfo.layout = pipelineLayout; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pipelineInfo.renderPass = renderPass; |
|
|
|
|
|
|
|
pipelineInfo.subpass = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkDestroyShaderModule(device, vertShaderModule, nullptr); |
|
|
|
|
|
|
|
vkDestroyShaderModule(device, fragShaderModule, nullptr); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPipeline graphicsPipeline = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<VkFramebuffer> swapchainFramebuffers; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void createFramebuffers(){ |
|
|
|
|
|
|
|
swapchainFramebuffers.resize(swapchainImageViews.size()); |
|
|
|
|
|
|
|
for (size_t i = 0; i < swapchainImageViews.size(); i++){ |
|
|
|
|
|
|
|
VkImageView attachments[] = {swapchainImageViews[i]}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkFramebufferCreateInfo 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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapchainFramebuffers[i]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkCommandPool commandPool = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
VkCommandBuffer commandBuffer = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void createCommandPool(){ |
|
|
|
|
|
|
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkCommandPoolCreateInfo poolInfo {}; |
|
|
|
|
|
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
|
|
|
|
|
|
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
|
|
|
|
|
|
|
poolInfo.queueFamilyIndex = indices.graphicsFamily.value(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void createCommandBuffer(){ |
|
|
|
|
|
|
|
VkCommandBufferAllocateInfo allocateInfo {}; |
|
|
|
|
|
|
|
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
|
|
|
|
|
|
|
allocateInfo.commandPool = commandPool; |
|
|
|
|
|
|
|
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
|
|
|
|
|
|
|
allocateInfo.commandBufferCount = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkAllocateCommandBuffers(device, &allocateInfo, &commandBuffer); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex){ |
|
|
|
|
|
|
|
VkCommandBufferBeginInfo beginInfo {}; |
|
|
|
|
|
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkBeginCommandBuffer(commandBuffer, &beginInfo); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkRenderPassBeginInfo 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, 0, 0, 1}}}; |
|
|
|
|
|
|
|
renderPassInfo.clearValueCount = 1; |
|
|
|
|
|
|
|
renderPassInfo.pClearValues = &clearColor; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); |
|
|
|
|
|
|
|
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkViewport viewport {}; |
|
|
|
|
|
|
|
viewport.x = 0; |
|
|
|
|
|
|
|
viewport.y = 0; |
|
|
|
|
|
|
|
viewport.width = static_cast<float>(swapchainExtent.width); |
|
|
|
|
|
|
|
viewport.height = static_cast<float>(swapchainExtent.height); |
|
|
|
|
|
|
|
viewport.minDepth = 0; |
|
|
|
|
|
|
|
viewport.maxDepth = 1; |
|
|
|
|
|
|
|
vkCmdSetViewport(commandBuffer, 0, 1, &viewport); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkRect2D scissor {}; |
|
|
|
|
|
|
|
scissor.offset = {0, 0}; |
|
|
|
|
|
|
|
scissor.extent = swapchainExtent; |
|
|
|
|
|
|
|
vkCmdSetScissor(commandBuffer, 0, 1, &scissor); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkCmdDraw(commandBuffer, 3, 1, 0, 0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkCmdEndRenderPass(commandBuffer); |
|
|
|
|
|
|
|
vkEndCommandBuffer(commandBuffer); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSemaphore imageAvailable = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
VkSemaphore renderFinished = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
VkFence inFlight = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void createSyncObjects(){ |
|
|
|
|
|
|
|
VkSemaphoreCreateInfo semaphoreInfo {}; |
|
|
|
|
|
|
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkFenceCreateInfo fenceInfo {}; |
|
|
|
|
|
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
|
|
|
|
|
|
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailable); |
|
|
|
|
|
|
|
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinished); |
|
|
|
|
|
|
|
vkCreateFence(device, &fenceInfo, nullptr, &inFlight); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void drawFrame(){ |
|
|
|
|
|
|
|
vkWaitForFences(device, 1, &inFlight, VK_TRUE, UINT64_MAX); |
|
|
|
|
|
|
|
vkResetFences(device, 1, &inFlight); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t imageIndex; |
|
|
|
|
|
|
|
vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imageAvailable, VK_NULL_HANDLE, &imageIndex); |
|
|
|
|
|
|
|
vkResetCommandBuffer(commandBuffer, 0); |
|
|
|
|
|
|
|
recordCommandBuffer(commandBuffer, imageIndex); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSubmitInfo submitInfo {}; |
|
|
|
|
|
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSemaphore waitSemaphores[] = {imageAvailable}; |
|
|
|
|
|
|
|
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[] = {renderFinished}; |
|
|
|
|
|
|
|
submitInfo.signalSemaphoreCount = 1; |
|
|
|
|
|
|
|
submitInfo.pSignalSemaphores = signalSemaphores; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlight); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPresentInfoKHR 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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkQueuePresentKHR(presentQueue, &presentInfo); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void mainLoop(){ |
|
|
|
|
|
|
|
while (!glfwWindowShouldClose(window)){ |
|
|
|
|
|
|
|
glfwPollEvents(); |
|
|
|
|
|
|
|
drawFrame(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkDeviceWaitIdle(device); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void cleanup(){ |
|
|
|
|
|
|
|
vkDestroySemaphore(device, imageAvailable, nullptr); |
|
|
|
|
|
|
|
vkDestroySemaphore(device, renderFinished, nullptr); |
|
|
|
|
|
|
|
vkDestroyFence(device, inFlight, 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(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int main() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MyApp app; |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
app.run(); |
|
|
|
|
|
|
|
} catch (const std::exception& e){ |
|
|
|
|
|
|
|
std::cerr << e.what() << "\n"; |
|
|
|
|
|
|
|
return EXIT_FAILURE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS; |
|
|
|
|
|
|
|
} |