1059 lines
35 KiB
C
1059 lines
35 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#define VK_USE_PLATFORM_WAYLAND_KHR
|
|
#define GLFW_INCLUDE_VULKAN
|
|
#include <GLFW/glfw3.h>
|
|
#define GLFW_EXPOSE_NATIVE_WAYLAND
|
|
#include <GLFW/glfw3native.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
const uint32_t WIDTH = 800;
|
|
const uint32_t HEIGHT = 600;
|
|
const int MAX_FRAMES_IN_FLIGHT = 2;
|
|
|
|
const char* VALIDATION_LAYERS[] = { "VK_LAYER_KHRONOS_validation" };
|
|
#define VALIDATION_LAYER_COUNT 1
|
|
|
|
const char* DEVICE_EXTENSIONS[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
|
|
#define DEVICE_EXTENSION_COUNT 1
|
|
|
|
#ifdef NDEBUG
|
|
const uint32_t enableValidationLayers = 0;
|
|
#else
|
|
const uint32_t enableValidationLayers = 1;
|
|
#endif
|
|
|
|
struct VulkanData {
|
|
VkInstance instance;
|
|
VkDebugUtilsMessengerEXT debugMessenger;
|
|
VkPhysicalDevice physicalDevice;
|
|
VkDevice device;
|
|
VkQueue graphicsQueue;
|
|
VkQueue presentQueue;
|
|
VkSurfaceKHR surface;
|
|
VkSwapchainKHR swapChain;
|
|
VkImage swapChainImages[4];
|
|
VkFormat swapChainImageFormat;
|
|
VkExtent2D swapChainExtent;
|
|
VkImageView swapChainImageViews[4];
|
|
int imageCount;
|
|
VkPipelineLayout pipelineLayout;
|
|
VkRenderPass renderPass;
|
|
VkPipeline graphicsPipeline;
|
|
VkFramebuffer swapChainFramebuffers[4];
|
|
VkCommandPool commandPool;
|
|
VkCommandBuffer commandBuffer;
|
|
VkSemaphore imageAvailableSemaphore;
|
|
VkSemaphore renderFinishedSemaphore;
|
|
VkFence inFlightFence;
|
|
};
|
|
|
|
struct SwapChainSupportDetails {
|
|
VkSurfaceCapabilitiesKHR capabilities;
|
|
VkPresentModeKHR presentModes[5];
|
|
VkSurfaceFormatKHR formats[8];
|
|
};
|
|
|
|
struct Optional {
|
|
uint32_t v;
|
|
uint32_t is_some;
|
|
};
|
|
|
|
struct QueueFamilyIndices {
|
|
struct Optional graphicsFamily;
|
|
struct Optional presentFamily;
|
|
};
|
|
|
|
GLFWwindow* initWindow() {
|
|
if (!glfwInit()) {
|
|
fprintf(stderr, "ERROR: Failed to initialize window\n");
|
|
exit(1);
|
|
}
|
|
|
|
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", NULL, NULL);
|
|
if (!window) {
|
|
glfwTerminate();
|
|
fprintf(stderr, "ERROR: Failed to create window\n");
|
|
exit(1);
|
|
}
|
|
|
|
glfwMakeContextCurrent(window);
|
|
return window;
|
|
}
|
|
|
|
// INDIRECTION: main->run->initVulkan->createInstance
|
|
uint32_t checkValidationLayerSupport() {
|
|
uint32_t layerCount;
|
|
vkEnumerateInstanceLayerProperties(&layerCount, NULL);
|
|
|
|
VkLayerProperties availableLayers[layerCount];
|
|
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
|
|
|
|
for (int i = 0; i < VALIDATION_LAYER_COUNT; i++) {
|
|
const char* layerName = VALIDATION_LAYERS[i];
|
|
uint32_t layerFound = 0;
|
|
|
|
for (uint32_t j = 0; j < layerCount; j++) {
|
|
VkLayerProperties layerProperties = availableLayers[j];
|
|
if (strcmp(layerName, layerProperties.layerName) == 0) {
|
|
layerFound = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!layerFound) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// INDIRECTION: main->run->initVulkan->createInstance
|
|
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
|
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
|
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
|
void* pUserData) {
|
|
|
|
if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT && messageType >= VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
|
|
printf("validation layer: %s, with data: %p\n", pCallbackData->pMessage, pUserData);
|
|
}
|
|
|
|
return VK_FALSE;
|
|
}
|
|
|
|
// INDIRECTION: main->run->initVulkan
|
|
void createInstance(struct VulkanData* data) {
|
|
if (enableValidationLayers && !checkValidationLayerSupport()) {
|
|
printf("ERROR: Validation layers requested, but not available\n");
|
|
exit(1);
|
|
}
|
|
VkApplicationInfo appInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
.pApplicationName = "Hello Triangle",
|
|
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
|
.pEngineName = "No Engine",
|
|
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
|
.apiVersion = VK_API_VERSION_1_3,
|
|
};
|
|
|
|
uint32_t count = 0;
|
|
vkEnumerateInstanceExtensionProperties(NULL, &count, NULL);
|
|
VkExtensionProperties availableExtensions[count];
|
|
vkEnumerateInstanceExtensionProperties(NULL, &count, availableExtensions);
|
|
printf("available extensions:\n");
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
VkExtensionProperties extension = availableExtensions[i];
|
|
printf("\t%s\n", extension.extensionName);
|
|
}
|
|
|
|
uint32_t glfwExtensionCount = 0;
|
|
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
|
const char* extensions[glfwExtensionCount + 1];
|
|
|
|
for (uint32_t i = 0; i < glfwExtensionCount; i++) {
|
|
extensions[i] = glfwExtensions[i];
|
|
}
|
|
|
|
if (enableValidationLayers) {
|
|
extensions[glfwExtensionCount] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
|
}
|
|
uint32_t extensionCount = sizeof(extensions) / sizeof(extensions[0]);
|
|
|
|
printf("required extensions:\n");
|
|
for (uint32_t i = 0; i < extensionCount; i++) {
|
|
printf("\t%s\n", extensions[i]);
|
|
}
|
|
|
|
VkInstanceCreateInfo createInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
|
.pApplicationInfo = &appInfo,
|
|
.enabledExtensionCount = extensionCount,
|
|
.ppEnabledExtensionNames = extensions,
|
|
.enabledLayerCount = 0,
|
|
};
|
|
|
|
if (enableValidationLayers) {
|
|
createInfo.enabledLayerCount = VALIDATION_LAYER_COUNT;
|
|
createInfo.ppEnabledLayerNames = VALIDATION_LAYERS;
|
|
|
|
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
|
.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,
|
|
.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,
|
|
.pfnUserCallback = debugCallback,
|
|
};
|
|
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
|
|
} else {
|
|
createInfo.enabledLayerCount = 0;
|
|
createInfo.pNext = NULL;
|
|
}
|
|
|
|
VkInstance instance;
|
|
if (vkCreateInstance(&createInfo, NULL, &instance) != VK_SUCCESS) {
|
|
printf("ERROR: Failed to create instance\n");
|
|
exit(1);
|
|
}
|
|
data->instance = instance;
|
|
}
|
|
|
|
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
|
|
|
|
PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
|
|
|
|
if (func == NULL) {
|
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
|
}
|
|
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
|
|
}
|
|
|
|
|
|
void setupDebugMessenger(struct VulkanData* data) {
|
|
if (!enableValidationLayers) return;
|
|
|
|
VkDebugUtilsMessengerCreateInfoEXT createInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
|
.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,
|
|
.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,
|
|
.pfnUserCallback = debugCallback,
|
|
};
|
|
|
|
if (CreateDebugUtilsMessengerEXT(data->instance, &createInfo, NULL, &data->debugMessenger) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to set up debug messenger\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void createSurface(struct VulkanData* data, GLFWwindow* window) {
|
|
VkWaylandSurfaceCreateInfoKHR createInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
|
|
.display = glfwGetWaylandDisplay(),
|
|
.surface = glfwGetWaylandWindow(window),
|
|
};
|
|
// if (glfwCreateWindowSurface(data->instance, window, NULL, &surface) != VK_SUCCESS) {
|
|
// fprintf(stderr, "ERROR: Failed to create window surface\n");
|
|
// exit(1);
|
|
// }
|
|
if (vkCreateWaylandSurfaceKHR(data->instance, &createInfo, NULL, &data->surface) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to create window surface\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
|
|
PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
|
|
if (func != NULL) {
|
|
func(instance, debugMessenger, pAllocator);
|
|
}
|
|
}
|
|
|
|
uint32_t clamp(uint32_t value, uint32_t min, uint32_t max) {
|
|
if (value > max) return max;
|
|
if (value < min) return min;
|
|
return value;
|
|
}
|
|
|
|
|
|
int checkDeviceExtensionSupport(VkPhysicalDevice device) {
|
|
uint32_t extensionCount;
|
|
vkEnumerateDeviceExtensionProperties(device, NULL, &extensionCount, NULL);
|
|
|
|
VkExtensionProperties availableExtensions[extensionCount];
|
|
vkEnumerateDeviceExtensionProperties(device, NULL, &extensionCount, availableExtensions);
|
|
|
|
int requiredExtensionsFound = 0;
|
|
for (uint32_t i = 0; i < extensionCount; i++) {
|
|
VkExtensionProperties extension = availableExtensions[i];
|
|
for (int j = 0; j < DEVICE_EXTENSION_COUNT; j++) {
|
|
if (strcmp(extension.extensionName, DEVICE_EXTENSIONS[j]) == 0) {
|
|
requiredExtensionsFound++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return requiredExtensionsFound == DEVICE_EXTENSION_COUNT;
|
|
}
|
|
|
|
struct SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
|
struct SwapChainSupportDetails details;
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
|
|
uint32_t formatCount;
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, NULL);
|
|
if (formatCount != 0) {
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats);
|
|
}
|
|
|
|
uint32_t presentModeCount;
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, NULL);
|
|
|
|
if (presentModeCount != 0) {
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes);
|
|
}
|
|
|
|
return details;
|
|
}
|
|
|
|
struct QueueFamilyIndices findQueueFamilies(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) {
|
|
struct QueueFamilyIndices indices = {
|
|
.graphicsFamily.v = 0,
|
|
.graphicsFamily.is_some = 0,
|
|
|
|
.presentFamily.v = 0,
|
|
.presentFamily.is_some = 0,
|
|
};
|
|
|
|
uint32_t queueFamilyCount = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, NULL);
|
|
VkQueueFamilyProperties queueFamilies[queueFamilyCount];
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies);
|
|
|
|
for (uint32_t i = 0; i < queueFamilyCount; i++) {
|
|
VkQueueFamilyProperties queueFamily = queueFamilies[i];
|
|
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
|
indices.graphicsFamily.v = i;
|
|
indices.graphicsFamily.is_some = 1;
|
|
}
|
|
|
|
VkBool32 presentSupport = 0;
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &presentSupport);
|
|
|
|
if (presentSupport) {
|
|
indices.presentFamily.v = i;
|
|
indices.presentFamily.is_some = 1;
|
|
}
|
|
|
|
if (indices.presentFamily.is_some && indices.graphicsFamily.is_some) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return indices;
|
|
}
|
|
|
|
int isDeviceSuitable(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) { // One could pick the device based on a score.
|
|
struct QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface);
|
|
|
|
VkPhysicalDeviceProperties deviceProperties;
|
|
vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
|
|
printf("GPU: %s\n", deviceProperties.deviceName);
|
|
VkPhysicalDeviceFeatures deviceFeatures;
|
|
int featureCount = sizeof(deviceFeatures) / 4;
|
|
VkBool32* p = &deviceFeatures.robustBufferAccess;
|
|
for (int i = 0; i < featureCount; i++) {
|
|
*p = VK_FALSE;
|
|
p++;
|
|
}
|
|
vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures);
|
|
|
|
int extensionsSupported = checkDeviceExtensionSupport(physicalDevice);
|
|
|
|
int swapChainAdequate = 0;
|
|
if (extensionsSupported) {
|
|
struct SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice, surface);
|
|
swapChainAdequate = swapChainSupport.formats[0].format != 0;
|
|
}
|
|
|
|
return
|
|
deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
|
|
deviceFeatures.geometryShader &&
|
|
swapChainAdequate &&
|
|
indices.graphicsFamily.is_some && indices.presentFamily.is_some;
|
|
}
|
|
|
|
void pickPhysicalDevice(struct VulkanData* data) {
|
|
data->physicalDevice = VK_NULL_HANDLE;
|
|
uint32_t deviceCount = 0;
|
|
vkEnumeratePhysicalDevices(data->instance, &deviceCount, NULL);
|
|
if (deviceCount == 0) {
|
|
printf("ERROR: Failed to find GPUs with Vulkan support\n");
|
|
exit(1);
|
|
}
|
|
VkPhysicalDevice devices[deviceCount];
|
|
vkEnumeratePhysicalDevices(data->instance, &deviceCount, devices);
|
|
|
|
for (uint32_t i = 0; i < deviceCount; i++) {
|
|
VkPhysicalDevice device = devices[i];
|
|
|
|
if (isDeviceSuitable(device, data->surface)) {
|
|
data->physicalDevice = device;
|
|
break;
|
|
}
|
|
}
|
|
if (data->physicalDevice == VK_NULL_HANDLE) {
|
|
printf("ERROR: Failed to find a suitable GPU\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void createLogicalDevice(struct VulkanData* data) {
|
|
struct QueueFamilyIndices indices = findQueueFamilies(data->physicalDevice, data->surface);
|
|
float queuePriority = 1.0f;
|
|
|
|
VkDeviceQueueCreateInfo queueCreateInfos[2];
|
|
uint32_t uniqueQueueFamilies[] = {indices.graphicsFamily.v, indices.presentFamily.v};
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
uint32_t queueFamily = uniqueQueueFamilies[i];
|
|
VkDeviceQueueCreateInfo queueCreateInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
|
.queueFamilyIndex = queueFamily,
|
|
.queueCount = 1,
|
|
.pQueuePriorities = &queuePriority,
|
|
};
|
|
queueCreateInfos[i] = queueCreateInfo;
|
|
}
|
|
|
|
int queueCreateInfoCount = 2;
|
|
if (indices.graphicsFamily.v == indices.presentFamily.v) {
|
|
queueCreateInfoCount = 1;
|
|
}
|
|
|
|
for (int i = 0; i < queueCreateInfoCount; i++) {
|
|
uint32_t queueFamily = uniqueQueueFamilies[i];
|
|
VkDeviceQueueCreateInfo queueCreateInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
|
.queueFamilyIndex = queueFamily,
|
|
.queueCount = 1,
|
|
.pQueuePriorities = &queuePriority,
|
|
};
|
|
queueCreateInfos[i] = queueCreateInfo;
|
|
}
|
|
|
|
VkPhysicalDeviceFeatures deviceFeatures;
|
|
int featureCount = sizeof(deviceFeatures) / 4;
|
|
VkBool32* p = &deviceFeatures.robustBufferAccess;
|
|
for (int i = 0; i < featureCount; i++) {
|
|
*p = VK_FALSE;
|
|
p++;
|
|
}
|
|
|
|
VkDeviceCreateInfo createInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
|
.pEnabledFeatures = &deviceFeatures,
|
|
.enabledExtensionCount = DEVICE_EXTENSION_COUNT,
|
|
.queueCreateInfoCount = queueCreateInfoCount,
|
|
.pQueueCreateInfos = queueCreateInfos,
|
|
.ppEnabledExtensionNames = DEVICE_EXTENSIONS,
|
|
};
|
|
|
|
if (enableValidationLayers) {
|
|
createInfo.enabledLayerCount = VALIDATION_LAYER_COUNT;
|
|
createInfo.ppEnabledLayerNames = VALIDATION_LAYERS;
|
|
} else {
|
|
createInfo.enabledLayerCount = 0;
|
|
}
|
|
|
|
if (vkCreateDevice(data->physicalDevice, &createInfo, NULL, &data->device) != VK_SUCCESS) {
|
|
printf("ERROR: Failed to create logical device\n");
|
|
exit(1);
|
|
}
|
|
if (!indices.graphicsFamily.is_some) {
|
|
printf("no graphics\n");
|
|
}
|
|
if (!indices.presentFamily.is_some) {
|
|
printf("no present\n");
|
|
}
|
|
vkGetDeviceQueue(data->device, indices.graphicsFamily.v, 0, &data->graphicsQueue);
|
|
vkGetDeviceQueue(data->device, indices.presentFamily.v, 0, &data->presentQueue);
|
|
}
|
|
|
|
VkExtent2D chooseSwapExtent(VkSurfaceCapabilitiesKHR* capabilities, GLFWwindow* window) {
|
|
if (capabilities->currentExtent.width != UINT_MAX) {
|
|
return capabilities->currentExtent;
|
|
} else {
|
|
int width, height;
|
|
glfwGetFramebufferSize(window, &width, &height);
|
|
|
|
VkExtent2D actualExtent = {
|
|
(uint32_t)(width),
|
|
(uint32_t)(height)
|
|
};
|
|
|
|
actualExtent.width = clamp(actualExtent.width, capabilities->minImageExtent.width, capabilities->maxImageExtent.width);
|
|
actualExtent.height = clamp(actualExtent.height, capabilities->minImageExtent.height, capabilities->maxImageExtent.height);
|
|
|
|
return actualExtent;
|
|
}
|
|
}
|
|
|
|
VkSurfaceFormatKHR chooseSwapSurfaceFormat(VkSurfaceFormatKHR availableFormats[]) {
|
|
for(int i = 0; 1; i++) {
|
|
VkSurfaceFormatKHR availableFormat = availableFormats[i];
|
|
if (availableFormat.format == VK_FORMAT_UNDEFINED) {
|
|
break;
|
|
}
|
|
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
|
return availableFormat;
|
|
}
|
|
}
|
|
return availableFormats[0];
|
|
}
|
|
|
|
VkPresentModeKHR chooseSwapPresentMode(VkPresentModeKHR availablePresentModes[]) {
|
|
for(int i = 0; 1; i++) {
|
|
if (availablePresentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
|
|
return availablePresentModes[i];
|
|
}
|
|
}
|
|
return VK_PRESENT_MODE_FIFO_KHR;
|
|
}
|
|
|
|
void createSwapChain(struct VulkanData* data, GLFWwindow* window) {
|
|
struct SwapChainSupportDetails swapChainSupport = querySwapChainSupport(data->physicalDevice, data->surface);
|
|
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
|
|
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
|
|
VkExtent2D extent = chooseSwapExtent(&swapChainSupport.capabilities, window);
|
|
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
|
|
data->imageCount = imageCount;
|
|
if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
|
|
imageCount = swapChainSupport.capabilities.maxImageCount;
|
|
}
|
|
|
|
VkSwapchainCreateInfoKHR createInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
|
.surface = data->surface,
|
|
.minImageCount = imageCount,
|
|
.imageFormat = surfaceFormat.format,
|
|
.imageColorSpace = surfaceFormat.colorSpace,
|
|
.imageExtent = extent,
|
|
.imageArrayLayers = 1,
|
|
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
};
|
|
|
|
struct QueueFamilyIndices indices = findQueueFamilies(data->physicalDevice, data->surface);
|
|
uint32_t queueFamilyIndices[] = {indices.graphicsFamily.v, indices.presentFamily.v};
|
|
|
|
if (indices.graphicsFamily.v != indices.presentFamily.v) {
|
|
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
|
createInfo.queueFamilyIndexCount = 2;
|
|
createInfo.pQueueFamilyIndices = queueFamilyIndices;
|
|
} else {
|
|
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
createInfo.queueFamilyIndexCount = 0; // Optional
|
|
createInfo.pQueueFamilyIndices = NULL; // Optional
|
|
}
|
|
|
|
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(data->device, &createInfo, NULL, &data->swapChain) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to create swap chain");
|
|
}
|
|
|
|
data->swapChainImageFormat = surfaceFormat.format;
|
|
data->swapChainExtent = extent;
|
|
|
|
vkGetSwapchainImagesKHR(data->device, data->swapChain, &imageCount, NULL);
|
|
vkGetSwapchainImagesKHR(data->device, data->swapChain, &imageCount, data->swapChainImages);
|
|
}
|
|
|
|
void createImageViews(struct VulkanData* data) {
|
|
for (int i = 0; i < data->imageCount; i++) {
|
|
VkImageViewCreateInfo createInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
.image = data->swapChainImages[i],
|
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
|
.format = data->swapChainImageFormat,
|
|
.components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
.components.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
.components.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
.components.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.subresourceRange.baseMipLevel = 0,
|
|
.subresourceRange.levelCount = 1,
|
|
.subresourceRange.baseArrayLayer = 0,
|
|
.subresourceRange.layerCount = 1,
|
|
};
|
|
if (vkCreateImageView(data->device, &createInfo, NULL, &data->swapChainImageViews[i]) != VK_SUCCESS) {
|
|
fprintf(stderr, "failed to create image views!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void readFile(const char* fileName, char buffer[], int len) {
|
|
FILE* file = fopen(fileName, "r");
|
|
if (!file) {
|
|
printf("ERROR: File \"%s\" can't be opened \n", fileName);
|
|
exit(1);
|
|
}
|
|
fread(buffer, sizeof(buffer[0]), len, file);
|
|
fclose(file);
|
|
}
|
|
|
|
uint32_t fileSize(const char* fileName) {
|
|
FILE* file = fopen(fileName, "r");
|
|
if (!file) {
|
|
printf("ERROR: File \"%s\" can't be opened \n", fileName);
|
|
exit(1);
|
|
}
|
|
|
|
fseek(file, 0L, SEEK_END);
|
|
uint32_t size = ftell(file);
|
|
printf("INFO: File \"%s\" has size %d\n", fileName, size);
|
|
rewind(file);
|
|
return size;
|
|
}
|
|
|
|
VkShaderModule createShaderModule(const char* code, uint32_t size, VkDevice device) {
|
|
VkShaderModuleCreateInfo createInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
|
.codeSize = size,
|
|
.pCode = (uint32_t*)code,
|
|
};
|
|
VkShaderModule shaderModule;
|
|
if (vkCreateShaderModule(device, &createInfo, NULL, &shaderModule) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to create shader module\n");
|
|
}
|
|
return shaderModule;
|
|
}
|
|
|
|
void createGraphicsPipeline(struct VulkanData* data) {
|
|
const char* vertFileName = "shaders/vert.spv";
|
|
uint32_t vertLen = fileSize(vertFileName);
|
|
char vertShaderCode[vertLen];
|
|
readFile(vertFileName, vertShaderCode, vertLen);
|
|
|
|
const char* fragFileName = "shaders/frag.spv";
|
|
uint32_t fragLen = fileSize(fragFileName);
|
|
char fragShaderCode[fragLen];
|
|
readFile(fragFileName, fragShaderCode, fragLen);
|
|
|
|
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode, vertLen, data->device);
|
|
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode, fragLen, data->device);
|
|
|
|
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",
|
|
.pSpecializationInfo = NULL,
|
|
};
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
|
|
|
|
uint32_t dynamicStateCount = 2;
|
|
VkDynamicState dynamicStates[] = {
|
|
VK_DYNAMIC_STATE_VIEWPORT,
|
|
VK_DYNAMIC_STATE_SCISSOR
|
|
};
|
|
|
|
VkPipelineDynamicStateCreateInfo dynamicState = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
.dynamicStateCount = dynamicStateCount,
|
|
.pDynamicStates = dynamicStates,
|
|
};
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
.vertexBindingDescriptionCount = 0,
|
|
.pVertexBindingDescriptions = NULL, // Optional
|
|
.vertexAttributeDescriptionCount = 0,
|
|
.pVertexAttributeDescriptions = NULL, // Optional
|
|
};
|
|
|
|
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) data->swapChainExtent.width,
|
|
.height = (float) data->swapChainExtent.height,
|
|
.minDepth = 0.0f,
|
|
.maxDepth = 1.0f,
|
|
};
|
|
|
|
VkRect2D scissor = {
|
|
.offset = {0, 0},
|
|
.extent = data->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,
|
|
.lineWidth = 1.0f,
|
|
.cullMode = VK_CULL_MODE_BACK_BIT,
|
|
.frontFace = VK_FRONT_FACE_CLOCKWISE,
|
|
.depthBiasEnable = VK_FALSE,
|
|
.depthBiasConstantFactor = 0.0f, // Optional
|
|
.depthBiasClamp = 0.0f, // Optional
|
|
.depthBiasSlopeFactor = 0.0f, // Optional
|
|
};
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisampling = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
.sampleShadingEnable = VK_FALSE,
|
|
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
|
.minSampleShading = 1.0f, // Optional
|
|
.pSampleMask = NULL, // Optional
|
|
.alphaToCoverageEnable = VK_FALSE, // Optional
|
|
.alphaToOneEnable = VK_FALSE, // Optional
|
|
};
|
|
|
|
VkPipelineColorBlendAttachmentState colorBlendAttachment = {
|
|
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
|
.blendEnable = VK_FALSE,
|
|
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE, // Optional
|
|
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, // Optional
|
|
.colorBlendOp = VK_BLEND_OP_ADD, // Optional
|
|
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, // Optional
|
|
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, // Optional
|
|
.alphaBlendOp = VK_BLEND_OP_ADD, // Optional
|
|
};
|
|
|
|
VkPipelineColorBlendStateCreateInfo colorBlending = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
|
.logicOpEnable = VK_FALSE,
|
|
.logicOp = VK_LOGIC_OP_COPY, // Optional
|
|
.attachmentCount = 1,
|
|
.pAttachments = &colorBlendAttachment,
|
|
.blendConstants[0] = 0.0f, // Optional
|
|
.blendConstants[1] = 0.0f, // Optional
|
|
.blendConstants[2] = 0.0f, // Optional
|
|
.blendConstants[3] = 0.0f, // Optional
|
|
};
|
|
|
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
.setLayoutCount = 0, // Optional
|
|
.pSetLayouts = NULL, // Optional
|
|
.pushConstantRangeCount = 0, // Optional
|
|
.pPushConstantRanges = NULL, // Optional
|
|
};
|
|
|
|
if (vkCreatePipelineLayout(data->device, &pipelineLayoutInfo, NULL, &data->pipelineLayout) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to create pipeline layout\n");
|
|
exit(1);
|
|
}
|
|
|
|
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 = NULL, // Optional
|
|
.pColorBlendState = &colorBlending,
|
|
.pDynamicState = &dynamicState,
|
|
.layout = data->pipelineLayout,
|
|
.renderPass = data->renderPass,
|
|
.subpass = 0,
|
|
.basePipelineHandle = VK_NULL_HANDLE, // Optional
|
|
.basePipelineIndex = -1, // Optional
|
|
};
|
|
|
|
if (vkCreateGraphicsPipelines(data->device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &data->graphicsPipeline) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to create graphics pipeline\n");
|
|
exit(1);
|
|
}
|
|
|
|
vkDestroyShaderModule(data->device, fragShaderModule, NULL);
|
|
vkDestroyShaderModule(data->device, vertShaderModule, NULL);
|
|
}
|
|
|
|
void createRenderPass(struct VulkanData* data) {
|
|
VkAttachmentDescription colorAttachment = {
|
|
.format = data->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,
|
|
.srcAccessMask = 0,
|
|
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
};
|
|
|
|
VkRenderPassCreateInfo renderPassInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
.attachmentCount = 1,
|
|
.pAttachments = &colorAttachment,
|
|
.subpassCount = 1,
|
|
.pSubpasses = &subpass,
|
|
.dependencyCount = 1,
|
|
.pDependencies = &dependency,
|
|
};
|
|
|
|
if (vkCreateRenderPass(data->device, &renderPassInfo, NULL, &data->renderPass) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to create render pass!\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void createFramebuffers(struct VulkanData* data) {
|
|
for (int i = 0; i < data->imageCount; i++) {
|
|
VkImageView attachments[] = {
|
|
data->swapChainImageViews[i]
|
|
};
|
|
|
|
VkFramebufferCreateInfo framebufferInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
|
.renderPass = data->renderPass,
|
|
.attachmentCount = 1,
|
|
.pAttachments = attachments,
|
|
.width = data->swapChainExtent.width,
|
|
.height = data->swapChainExtent.height,
|
|
.layers = 1,
|
|
};
|
|
|
|
if (vkCreateFramebuffer(data->device, &framebufferInfo, NULL, &data->swapChainFramebuffers[i]) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to create framebuffer\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void createCommandPool(struct VulkanData* data) {
|
|
struct QueueFamilyIndices queueFamilyIndices = findQueueFamilies(data->physicalDevice, data->surface);
|
|
|
|
VkCommandPoolCreateInfo poolInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
|
.queueFamilyIndex = queueFamilyIndices.graphicsFamily.v,
|
|
};
|
|
|
|
if (vkCreateCommandPool(data->device, &poolInfo, NULL, &data->commandPool) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: failed to create command pool\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void createCommandBuffer(struct VulkanData* data) {
|
|
VkCommandBufferAllocateInfo allocInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
.commandPool = data->commandPool,
|
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
.commandBufferCount = 1,
|
|
};
|
|
|
|
if (vkAllocateCommandBuffers(data->device, &allocInfo, &data->commandBuffer) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to allocate command buffers\n");
|
|
exit(1);
|
|
}
|
|
|
|
}
|
|
|
|
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex, struct VulkanData* data) {
|
|
VkCommandBufferBeginInfo beginInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
.flags = 0,
|
|
.pInheritanceInfo = NULL,
|
|
};
|
|
|
|
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to begin recording command buffer\n");
|
|
exit(1);
|
|
}
|
|
|
|
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
|
|
|
VkRenderPassBeginInfo renderPassInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
|
.renderPass = data->renderPass,
|
|
.framebuffer = data->swapChainFramebuffers[imageIndex],
|
|
.renderArea.offset = {0, 0},
|
|
.renderArea.extent = data->swapChainExtent,
|
|
.clearValueCount = 1,
|
|
.pClearValues = &clearColor,
|
|
};
|
|
|
|
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, data->graphicsPipeline);
|
|
|
|
VkViewport viewport = {
|
|
.x = 0.0f,
|
|
.y = 0.0f,
|
|
.width = (float) data->swapChainExtent.width,
|
|
.height = (float) data->swapChainExtent.height,
|
|
.minDepth = 0.0f,
|
|
.maxDepth = 1.0f,
|
|
};
|
|
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
|
|
|
VkRect2D scissor = {
|
|
.offset = {0, 0},
|
|
.extent = data->swapChainExtent,
|
|
};
|
|
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
|
|
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
|
|
vkCmdEndRenderPass(commandBuffer);
|
|
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to record command buffer\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void drawFrame(struct VulkanData* data) {
|
|
vkWaitForFences(data->device, 1, &data->inFlightFence, VK_TRUE, UINT64_MAX);
|
|
vkResetFences(data->device, 1, &data->inFlightFence);
|
|
|
|
uint32_t imageIndex;
|
|
vkAcquireNextImageKHR(data->device, data->swapChain, UINT64_MAX, data->imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
|
|
vkResetCommandBuffer(data->commandBuffer, 0);
|
|
recordCommandBuffer(data->commandBuffer, imageIndex, data);
|
|
|
|
VkSemaphore waitSemaphores[] = {data->imageAvailableSemaphore};
|
|
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
|
VkSemaphore signalSemaphores[] = {data->renderFinishedSemaphore};
|
|
VkSubmitInfo submitInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
.waitSemaphoreCount = 1,
|
|
.pWaitSemaphores = waitSemaphores,
|
|
.pWaitDstStageMask = waitStages,
|
|
.commandBufferCount = 1,
|
|
.pCommandBuffers = &data->commandBuffer,
|
|
.signalSemaphoreCount = 1,
|
|
.pSignalSemaphores = signalSemaphores,
|
|
};
|
|
if (vkQueueSubmit(data->graphicsQueue, 1, &submitInfo, data->inFlightFence) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to submit draw command buffer\n");
|
|
exit(1);
|
|
}
|
|
|
|
VkSwapchainKHR swapChains[] = {data->swapChain};
|
|
VkPresentInfoKHR presentInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
|
.waitSemaphoreCount = 1,
|
|
.pWaitSemaphores = signalSemaphores,
|
|
.swapchainCount = 1,
|
|
.pSwapchains = swapChains,
|
|
.pImageIndices = &imageIndex,
|
|
.pResults = NULL,
|
|
};
|
|
|
|
vkQueuePresentKHR(data->presentQueue, &presentInfo);
|
|
}
|
|
|
|
void createSyncObjects(struct VulkanData* data) {
|
|
VkSemaphoreCreateInfo semaphoreInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
|
};
|
|
VkFenceCreateInfo fenceInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
|
};
|
|
if (vkCreateSemaphore(data->device, &semaphoreInfo, NULL, &data->imageAvailableSemaphore) != VK_SUCCESS ||
|
|
vkCreateSemaphore(data->device, &semaphoreInfo, NULL, &data->renderFinishedSemaphore) != VK_SUCCESS ||
|
|
vkCreateFence(data->device, &fenceInfo, NULL, &data->inFlightFence) != VK_SUCCESS) {
|
|
fprintf(stderr, "ERROR: Failed to create semaphores\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
struct VulkanData initVulkan(GLFWwindow* window) {
|
|
struct VulkanData data;
|
|
createInstance(&data);
|
|
setupDebugMessenger(&data);
|
|
createSurface(&data, window);
|
|
pickPhysicalDevice(&data);
|
|
createLogicalDevice(&data);
|
|
createSwapChain(&data, window);
|
|
createImageViews(&data);
|
|
createRenderPass(&data);
|
|
createGraphicsPipeline(&data);
|
|
createFramebuffers(&data);
|
|
createCommandPool(&data);
|
|
createCommandBuffer(&data);
|
|
createSyncObjects(&data);
|
|
return data;
|
|
}
|
|
|
|
void mainLoop(GLFWwindow* window, struct VulkanData* data) {
|
|
while (!glfwWindowShouldClose(window)) {
|
|
glfwSwapBuffers(window);
|
|
glfwPollEvents();
|
|
drawFrame(data);
|
|
int state = glfwGetKey(window, GLFW_KEY_Q);
|
|
if (state == GLFW_PRESS) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
vkDeviceWaitIdle(data->device);
|
|
}
|
|
|
|
void cleanup(GLFWwindow* window, struct VulkanData* data) {
|
|
vkDestroySemaphore(data->device, data->imageAvailableSemaphore, NULL);
|
|
vkDestroySemaphore(data->device, data->renderFinishedSemaphore, NULL);
|
|
vkDestroyFence(data->device, data->inFlightFence, NULL);
|
|
vkDestroyCommandPool(data->device, data->commandPool, NULL);
|
|
for (int i = 0; i < data->imageCount; i++) {
|
|
vkDestroyFramebuffer(data->device, data->swapChainFramebuffers[i], NULL);
|
|
}
|
|
vkDestroyPipeline(data->device, data->graphicsPipeline, NULL);
|
|
vkDestroyPipelineLayout(data->device, data->pipelineLayout, NULL);
|
|
vkDestroyRenderPass(data->device, data->renderPass, NULL);
|
|
for (int i = 0; i < data->imageCount; i++) {
|
|
vkDestroyImageView(data->device, data->swapChainImageViews[i], NULL);
|
|
}
|
|
vkDestroySwapchainKHR(data->device, data->swapChain, NULL);
|
|
vkDestroyDevice(data->device, NULL);
|
|
if (enableValidationLayers) {
|
|
DestroyDebugUtilsMessengerEXT(data->instance, data->debugMessenger, NULL);
|
|
}
|
|
vkDestroySurfaceKHR(data->instance, data->surface, NULL);
|
|
vkDestroyInstance(data->instance, NULL);
|
|
glfwDestroyWindow(window);
|
|
glfwTerminate();
|
|
}
|
|
|
|
void run() {
|
|
GLFWwindow* window = initWindow();
|
|
struct VulkanData data = initVulkan(window);
|
|
mainLoop(window, &data);
|
|
cleanup(window, &data);
|
|
}
|
|
|
|
int main() {
|
|
run();
|
|
}
|