|
|
@ -1,12 +1,72 @@
|
|
|
|
|
|
|
|
#define VK_USE_PLATFORM_MACOS_MVK
|
|
|
|
#include "vulkan/vulkan_core.h"
|
|
|
|
#include "vulkan/vulkan_core.h"
|
|
|
|
#define GLFW_INCLUDE_VULKAN
|
|
|
|
#define GLFW_INCLUDE_VULKAN
|
|
|
|
#include <GLFW/glfw3.h>
|
|
|
|
#include <GLFW/glfw3.h>
|
|
|
|
|
|
|
|
#define GLFW_EXPOSE_NATIVE_COCOA
|
|
|
|
|
|
|
|
#include <GLFW/glfw3native.h>
|
|
|
|
|
|
|
|
|
|
|
|
#define GLM_FORCE_RADIANS
|
|
|
|
#define GLM_FORCE_RADIANS
|
|
|
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
|
|
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
|
|
|
#include <cglm/mat4.h>
|
|
|
|
#include <cglm/mat4.h>
|
|
|
|
#include <cglm/vec4.h>
|
|
|
|
#include <cglm/vec4.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct QueueIndicesStruct {
|
|
|
|
|
|
|
|
uint32_t graphics_family;
|
|
|
|
|
|
|
|
uint32_t graphics_index;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t present_family;
|
|
|
|
|
|
|
|
uint32_t present_index;
|
|
|
|
|
|
|
|
} QueueIndices;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct QueuesStruct {
|
|
|
|
|
|
|
|
VkQueue graphics;
|
|
|
|
|
|
|
|
VkQueue present;
|
|
|
|
|
|
|
|
} Queues;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct SwapchainDetailsStruct {
|
|
|
|
|
|
|
|
VkSurfaceCapabilitiesKHR capabilities;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSurfaceFormatKHR* formats;
|
|
|
|
|
|
|
|
uint32_t formats_count;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPresentModeKHR* present_modes;
|
|
|
|
|
|
|
|
uint32_t present_modes_count;
|
|
|
|
|
|
|
|
} SwapchainDetails;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct VulkanContextStruct {
|
|
|
|
|
|
|
|
VkInstance instance;
|
|
|
|
|
|
|
|
VkDebugUtilsMessengerEXT debug_messenger;
|
|
|
|
|
|
|
|
VkPhysicalDevice physical_device;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QueueIndices queue_indices;
|
|
|
|
|
|
|
|
VkDevice device;
|
|
|
|
|
|
|
|
Queues queues;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSurfaceKHR surface;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SwapchainDetails swapchain_details;
|
|
|
|
|
|
|
|
VkSwapchainKHR swapchain;
|
|
|
|
|
|
|
|
} VulkanContext;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char * validation_layers[] = {
|
|
|
|
|
|
|
|
"VK_LAYER_KHRONOS_validation",
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32_t validation_layer_count = sizeof(validation_layers) / sizeof(const char *);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char * instance_extensions[] = {
|
|
|
|
|
|
|
|
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
|
|
|
|
|
|
|
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
|
|
|
|
|
|
|
VK_MVK_MACOS_SURFACE_EXTENSION_NAME,
|
|
|
|
|
|
|
|
VK_KHR_SURFACE_EXTENSION_NAME,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32_t instance_extension_count = sizeof(instance_extensions) / sizeof(const char *);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char * device_extensions[] = {
|
|
|
|
|
|
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32_t device_extension_count = sizeof(device_extensions) / sizeof(const char *);
|
|
|
|
|
|
|
|
|
|
|
|
void glfw_error(int error, const char* description) {
|
|
|
|
void glfw_error(int error, const char* description) {
|
|
|
|
fprintf(stderr, "GLFW_ERR: 0x%02x - %s\n", error, description);
|
|
|
|
fprintf(stderr, "GLFW_ERR: 0x%02x - %s\n", error, description);
|
|
|
@ -24,10 +84,193 @@ GLFWwindow* init_window(int width, int height) {
|
|
|
|
return window;
|
|
|
|
return window;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool check_validation_layers(const char ** layers, uint32_t num_layers) {
|
|
|
|
|
|
|
|
uint32_t layer_count;
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result = vkEnumerateInstanceLayerProperties(&layer_count, 0);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkLayerProperties* available_layers = malloc(sizeof(VkLayerProperties)*layer_count);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result = vkEnumerateInstanceLayerProperties(&layer_count, available_layers);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < num_layers; i++) {
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
for(uint32_t j = 0; j < layer_count; j++) {
|
|
|
|
|
|
|
|
if(strcmp(layers[i], available_layers[j].layerName) == 0) {
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(found == false) {
|
|
|
|
|
|
|
|
free(available_layers);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
free(available_layers);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(
|
|
|
|
|
|
|
|
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
|
|
|
|
|
|
|
VkDebugUtilsMessageTypeFlagsEXT type,
|
|
|
|
|
|
|
|
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
|
|
|
|
|
|
|
|
void* user_data) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(void)severity;
|
|
|
|
|
|
|
|
(void)type;
|
|
|
|
|
|
|
|
(void)user_data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "Validation layer: %s\n", callback_data->pMessage);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return VK_FALSE;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSurfaceKHR create_surface_khr(VkInstance instance, GLFWwindow* window) {
|
|
|
|
|
|
|
|
VkSurfaceKHR surface;
|
|
|
|
|
|
|
|
VkResult result = glfwCreateWindowSurface(instance, window, 0, &surface);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return surface;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDevice get_best_physical_device(VkInstance instance) {
|
|
|
|
|
|
|
|
VkPhysicalDevice device = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t device_count = 0;
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
result = vkEnumeratePhysicalDevices(instance, &device_count, 0);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDevice* devices = malloc(sizeof(VkPhysicalDevice)*device_count);
|
|
|
|
|
|
|
|
result = vkEnumeratePhysicalDevices(instance, &device_count, devices);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
free(devices);
|
|
|
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int top_score = -1;
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < device_count; i++) {
|
|
|
|
|
|
|
|
int score = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDeviceProperties properties;
|
|
|
|
|
|
|
|
vkGetPhysicalDeviceProperties(devices[i], &properties);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDeviceFeatures features;
|
|
|
|
|
|
|
|
vkGetPhysicalDeviceFeatures(devices[i], &features);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch(properties.deviceType) {
|
|
|
|
|
|
|
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
|
|
|
|
|
|
|
score += 100;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
|
|
|
|
|
|
|
score += 50;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
|
|
|
|
|
|
|
score += 25;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VK_PHYSICAL_DEVICE_TYPE_CPU:
|
|
|
|
|
|
|
|
score += 0;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(score > top_score) {
|
|
|
|
|
|
|
|
top_score = score;
|
|
|
|
|
|
|
|
device = devices[i];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
free(devices);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return device;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct MaybeQueueIndices {
|
|
|
|
|
|
|
|
bool valid;
|
|
|
|
|
|
|
|
QueueIndices indices;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct MaybeQueueIndices get_queue_indices(VkPhysicalDevice physical_device, VkSurfaceKHR surface) {
|
|
|
|
|
|
|
|
struct MaybeQueueIndices ret = {};
|
|
|
|
|
|
|
|
ret.indices.graphics_family = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
ret.indices.graphics_index = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
ret.indices.present_family = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
ret.indices.present_index = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t queue_family_count;
|
|
|
|
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkQueueFamilyProperties* queue_families = malloc(sizeof(VkQueueFamilyProperties)*queue_family_count);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0;
|
|
|
|
|
|
|
|
(i < queue_family_count)
|
|
|
|
|
|
|
|
&& ((ret.indices.graphics_family == 0xFFFFFFFF) || (ret.indices.present_family == 0xFFFFFFFF));
|
|
|
|
|
|
|
|
i++) {
|
|
|
|
|
|
|
|
if(queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
|
|
|
|
|
|
|
ret.indices.graphics_family = i;
|
|
|
|
|
|
|
|
ret.indices.graphics_index = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkBool32 present_support = false;
|
|
|
|
|
|
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface, &present_support);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(present_support == VK_TRUE) {
|
|
|
|
|
|
|
|
ret.indices.present_family = i;
|
|
|
|
|
|
|
|
ret.indices.present_index = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if((ret.indices.graphics_family != 0xFFFFFFFF) && (ret.indices.present_family != 0xFFFFFFFF)) {
|
|
|
|
|
|
|
|
ret.valid = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
free(queue_families);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkDebugUtilsMessengerEXT create_debug_messenger(VkInstance instance) {
|
|
|
|
|
|
|
|
VkDebugUtilsMessengerEXT debug_messenger;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkDebugUtilsMessengerCreateInfoEXT messenger_info = {};
|
|
|
|
|
|
|
|
messenger_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
|
|
|
|
|
|
|
messenger_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
|
|
|
|
|
|
|
|
messenger_info.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;
|
|
|
|
|
|
|
|
messenger_info.pfnUserCallback = debug_callback;
|
|
|
|
|
|
|
|
messenger_info.pUserData = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
result = func(instance, &messenger_info, 0, &debug_messenger);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
fprintf(stderr, "failed to create debug messenger\n");
|
|
|
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return debug_messenger;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VkInstance create_instance() {
|
|
|
|
VkInstance create_instance() {
|
|
|
|
VkInstance instance;
|
|
|
|
VkInstance instance;
|
|
|
|
|
|
|
|
|
|
|
|
VkApplicationInfo app_info;
|
|
|
|
if(check_validation_layers(validation_layers, validation_layer_count) == false) {
|
|
|
|
|
|
|
|
fprintf(stderr, "requested validation layers not supported");
|
|
|
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkApplicationInfo app_info = {};
|
|
|
|
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
|
|
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
|
|
app_info.pApplicationName = "spacegame";
|
|
|
|
app_info.pApplicationName = "spacegame";
|
|
|
|
app_info.applicationVersion = VK_MAKE_VERSION(0, 0, 1);
|
|
|
|
app_info.applicationVersion = VK_MAKE_VERSION(0, 0, 1);
|
|
|
@ -35,33 +278,36 @@ VkInstance create_instance() {
|
|
|
|
app_info.engineVersion = VK_MAKE_VERSION(0, 0, 1);
|
|
|
|
app_info.engineVersion = VK_MAKE_VERSION(0, 0, 1);
|
|
|
|
app_info.apiVersion = VK_API_VERSION_1_3;
|
|
|
|
app_info.apiVersion = VK_API_VERSION_1_3;
|
|
|
|
|
|
|
|
|
|
|
|
VkInstanceCreateInfo instance_info;
|
|
|
|
VkInstanceCreateInfo instance_info = {};
|
|
|
|
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
|
|
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
|
|
instance_info.pApplicationInfo = &app_info;
|
|
|
|
instance_info.pApplicationInfo = &app_info;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
instance_info.enabledLayerCount = validation_layer_count;
|
|
|
|
|
|
|
|
instance_info.ppEnabledLayerNames = validation_layers;
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t glfwExtensionCount = 0;
|
|
|
|
uint32_t glfwExtensionCount = 0;
|
|
|
|
const char** glfwExtensions;
|
|
|
|
const char** glfwExtensions;
|
|
|
|
|
|
|
|
|
|
|
|
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
|
|
|
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
|
|
|
|
|
|
|
|
|
|
|
const char** requested_extensions = malloc(sizeof(char*)*(glfwExtensionCount+1));
|
|
|
|
const char** requested_extensions = malloc(sizeof(char*)*(glfwExtensionCount + instance_extension_count));
|
|
|
|
for (uint32_t i = 0; i < glfwExtensionCount; i++) {
|
|
|
|
for (uint32_t i = 0; i < glfwExtensionCount; i++) {
|
|
|
|
requested_extensions[i] = glfwExtensions[i];
|
|
|
|
requested_extensions[i] = glfwExtensions[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
requested_extensions[glfwExtensionCount] = VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME;
|
|
|
|
for (uint32_t i = 0; i < instance_extension_count; i++) {
|
|
|
|
|
|
|
|
requested_extensions[glfwExtensionCount + i] = instance_extensions[i];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
instance_info.enabledExtensionCount = glfwExtensionCount + 1;
|
|
|
|
instance_info.enabledExtensionCount = glfwExtensionCount + instance_extension_count;
|
|
|
|
instance_info.ppEnabledExtensionNames = requested_extensions;
|
|
|
|
instance_info.ppEnabledExtensionNames = requested_extensions;
|
|
|
|
|
|
|
|
|
|
|
|
instance_info.enabledLayerCount = 0;
|
|
|
|
|
|
|
|
instance_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
|
|
|
instance_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkResult result = vkCreateInstance(&instance_info, 0, &instance);
|
|
|
|
VkResult result = vkCreateInstance(&instance_info, 0, &instance);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
fprintf(stderr, "vkCreateInstance: 0x%02x\n", result);
|
|
|
|
fprintf(stderr, "vkCreateInstance: 0x%02x\n", result);
|
|
|
|
return 0;
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free(requested_extensions);
|
|
|
|
free(requested_extensions);
|
|
|
@ -69,14 +315,242 @@ VkInstance create_instance() {
|
|
|
|
return instance;
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int init_vulkan() {
|
|
|
|
VkDevice create_logical_device(VkPhysicalDevice physical_device, QueueIndices queue_indices) {
|
|
|
|
|
|
|
|
VkDevice device;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkDeviceQueueCreateInfo queue_create_info[2] = {};
|
|
|
|
|
|
|
|
uint32_t queue_create_count;
|
|
|
|
|
|
|
|
float default_queue_priority = 1.0f;
|
|
|
|
|
|
|
|
if(queue_indices.graphics_family == queue_indices.present_family) {
|
|
|
|
|
|
|
|
queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
|
|
|
|
|
|
queue_create_info[0].queueFamilyIndex = queue_indices.graphics_family;
|
|
|
|
|
|
|
|
queue_create_info[0].queueCount = 1;
|
|
|
|
|
|
|
|
queue_create_info[0].pQueuePriorities = &default_queue_priority;
|
|
|
|
|
|
|
|
queue_create_count = 1;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
|
|
|
|
|
|
queue_create_info[0].queueFamilyIndex = queue_indices.graphics_family;
|
|
|
|
|
|
|
|
queue_create_info[0].queueCount = 1;
|
|
|
|
|
|
|
|
queue_create_info[0].pQueuePriorities = &default_queue_priority;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
|
|
|
|
|
|
queue_create_info[1].queueFamilyIndex = queue_indices.present_family;
|
|
|
|
|
|
|
|
queue_create_info[1].queueCount = 1;
|
|
|
|
|
|
|
|
queue_create_info[1].pQueuePriorities = &default_queue_priority;
|
|
|
|
|
|
|
|
queue_create_count = 2;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDeviceFeatures device_features = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkDeviceCreateInfo device_create_info = {};
|
|
|
|
|
|
|
|
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
|
|
|
|
|
|
device_create_info.pQueueCreateInfos = queue_create_info;
|
|
|
|
|
|
|
|
device_create_info.queueCreateInfoCount = queue_create_count;
|
|
|
|
|
|
|
|
device_create_info.pEnabledFeatures = &device_features;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
device_create_info.enabledExtensionCount = device_extension_count;
|
|
|
|
|
|
|
|
device_create_info.ppEnabledExtensionNames = device_extensions;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
device_create_info.enabledLayerCount = validation_layer_count;
|
|
|
|
|
|
|
|
device_create_info.ppEnabledLayerNames = validation_layers;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkResult result = vkCreateDevice(physical_device, &device_create_info, 0, &device);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return device;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct MaybeSwapchainDetails {
|
|
|
|
|
|
|
|
bool valid;
|
|
|
|
|
|
|
|
SwapchainDetails details;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct MaybeSwapchainDetails get_swapchain_details(VkPhysicalDevice physical_device, VkSurfaceKHR surface) {
|
|
|
|
|
|
|
|
struct MaybeSwapchainDetails ret = {};
|
|
|
|
|
|
|
|
ret.valid = false;
|
|
|
|
|
|
|
|
ret.details.formats = 0;
|
|
|
|
|
|
|
|
ret.details.present_modes = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &ret.details.capabilities);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &ret.details.formats_count, 0);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.details.formats = malloc(sizeof(VkSurfaceFormatKHR)*ret.details.formats_count);
|
|
|
|
|
|
|
|
result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &ret.details.formats_count, ret.details.formats);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
free(ret.details.formats);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &ret.details.present_modes_count, 0);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
free(ret.details.formats);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.details.present_modes = malloc(sizeof(VkPresentModeKHR)*ret.details.present_modes_count);
|
|
|
|
|
|
|
|
result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &ret.details.present_modes_count, ret.details.present_modes);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
free(ret.details.formats);
|
|
|
|
|
|
|
|
free(ret.details.present_modes);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ret.valid = true;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSurfaceFormatKHR choose_swapchain_format(SwapchainDetails swapchain_details) {
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < swapchain_details.formats_count; i++) {
|
|
|
|
|
|
|
|
VkSurfaceFormatKHR format = swapchain_details.formats[i];
|
|
|
|
|
|
|
|
if(format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
|
|
|
|
|
|
|
return format;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return swapchain_details.formats[0];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPresentModeKHR choose_present_mode(SwapchainDetails swapchain_details) {
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < swapchain_details.present_modes_count; i++) {
|
|
|
|
|
|
|
|
if(swapchain_details.present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
|
|
|
|
|
|
|
|
return VK_PRESENT_MODE_MAILBOX_KHR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return VK_PRESENT_MODE_FIFO_KHR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkExtent2D choose_swapchain_extent(SwapchainDetails swapchain_details) {
|
|
|
|
|
|
|
|
return swapchain_details.capabilities.currentExtent;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_details, VkSurfaceKHR surface, QueueIndices indices, VkSwapchainKHR old_swapchain) {
|
|
|
|
|
|
|
|
VkSurfaceFormatKHR format = choose_swapchain_format(swapchain_details);
|
|
|
|
|
|
|
|
VkPresentModeKHR present_mode = choose_present_mode(swapchain_details);
|
|
|
|
|
|
|
|
VkExtent2D extent = choose_swapchain_extent(swapchain_details);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t image_count = swapchain_details.capabilities.minImageCount + 1;
|
|
|
|
|
|
|
|
uint32_t max_images = swapchain_details.capabilities.maxImageCount;
|
|
|
|
|
|
|
|
if((max_images > 0) && (image_count > max_images)) {
|
|
|
|
|
|
|
|
image_count = max_images;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSwapchainCreateInfoKHR swapchain_info = {};
|
|
|
|
|
|
|
|
swapchain_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
|
|
|
|
|
|
swapchain_info.surface = surface;
|
|
|
|
|
|
|
|
swapchain_info.minImageCount = image_count;
|
|
|
|
|
|
|
|
swapchain_info.imageFormat = format.format;
|
|
|
|
|
|
|
|
swapchain_info.imageColorSpace = format.colorSpace;
|
|
|
|
|
|
|
|
swapchain_info.imageExtent = extent;
|
|
|
|
|
|
|
|
swapchain_info.imageArrayLayers = 1;
|
|
|
|
|
|
|
|
swapchain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t queue_families[2] = {indices.graphics_family, indices.present_index};
|
|
|
|
|
|
|
|
if(indices.graphics_family != indices.present_family) {
|
|
|
|
|
|
|
|
swapchain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
|
|
|
|
|
|
|
swapchain_info.queueFamilyIndexCount = 2;
|
|
|
|
|
|
|
|
swapchain_info.pQueueFamilyIndices = queue_families;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
swapchain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
|
|
|
|
swapchain_info.queueFamilyIndexCount = 0;
|
|
|
|
|
|
|
|
swapchain_info.pQueueFamilyIndices = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
swapchain_info.preTransform = swapchain_details.capabilities.currentTransform;
|
|
|
|
|
|
|
|
swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
|
|
|
|
|
|
|
|
swapchain_info.presentMode = present_mode;
|
|
|
|
|
|
|
|
swapchain_info.clipped = VK_TRUE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
swapchain_info.oldSwapchain = old_swapchain;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSwapchainKHR swapchain;
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
result = vkCreateSwapchainKHR(device, &swapchain_info, 0, &swapchain);
|
|
|
|
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return swapchain;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VulkanContext* init_vulkan(GLFWwindow* window) {
|
|
|
|
|
|
|
|
VulkanContext* context = (VulkanContext*)malloc(sizeof(VulkanContext));
|
|
|
|
|
|
|
|
|
|
|
|
VkInstance instance = create_instance();
|
|
|
|
VkInstance instance = create_instance();
|
|
|
|
if(instance == 0) {
|
|
|
|
if(instance == VK_NULL_HANDLE) {
|
|
|
|
return 1;
|
|
|
|
fprintf(stderr, "failed to initialize vulkan instance\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
context->instance = instance;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkDebugUtilsMessengerEXT debug_messenger = create_debug_messenger(context->instance);
|
|
|
|
|
|
|
|
if(debug_messenger == VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
fprintf(stderr, "failed to initialize vulkan debug messenger\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
context->debug_messenger = debug_messenger;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDevice physical_device = get_best_physical_device(context->instance);
|
|
|
|
|
|
|
|
if(physical_device == VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
fprintf(stderr, "failed to pick vulkan physical device\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
context->physical_device = physical_device;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSurfaceKHR surface = create_surface_khr(context->instance, window);
|
|
|
|
|
|
|
|
if(surface == VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
fprintf(stderr, "failed to create vulkan surface\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
context->surface = surface;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct MaybeQueueIndices maybe_indices = get_queue_indices(context->physical_device, context->surface);
|
|
|
|
|
|
|
|
if(maybe_indices.valid == false) {
|
|
|
|
|
|
|
|
fprintf(stderr, "failed to get vulkan queue indices\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
context->queue_indices = maybe_indices.indices;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
vkDestroyInstance(instance, 0);
|
|
|
|
VkDevice device = create_logical_device(context->physical_device, context->queue_indices);
|
|
|
|
|
|
|
|
if(device == VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
fprintf(stderr, "failed to create vulkan logical device\n");
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
context->device = device;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
vkGetDeviceQueue(device, context->queue_indices.graphics_family, context->queue_indices.graphics_index, &context->queues.graphics);
|
|
|
|
|
|
|
|
vkGetDeviceQueue(device, context->queue_indices.present_family, context->queue_indices.present_index, &context->queues.present);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct MaybeSwapchainDetails maybe_details = get_swapchain_details(context->physical_device, context->surface);
|
|
|
|
|
|
|
|
if(maybe_details.valid == false) {
|
|
|
|
|
|
|
|
fprintf(stderr, "failed to create vulkan logical device\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
context->swapchain_details = maybe_details.details;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkSwapchainKHR swapchain = create_swapchain(context->device, context->swapchain_details, context->surface, context->queue_indices, VK_NULL_HANDLE);
|
|
|
|
|
|
|
|
if(swapchain == VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
context->swapchain = swapchain;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return context;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void main_loop(GLFWwindow* window) {
|
|
|
|
void main_loop(GLFWwindow* window) {
|
|
|
@ -85,11 +559,36 @@ void main_loop(GLFWwindow* window) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cleanup(GLFWwindow* window) {
|
|
|
|
void cleanup(GLFWwindow* window, VulkanContext* context) {
|
|
|
|
glfwDestroyWindow(window);
|
|
|
|
if(context != 0) {
|
|
|
|
|
|
|
|
if(context->instance != VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
if(context->swapchain != VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
vkDestroySwapchainKHR(context->device, context->swapchain, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(context->surface != VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
vkDestroySurfaceKHR(context->instance, context->surface, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(context->device != VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
vkDestroyDevice(context->device, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(context->debug_messenger != VK_NULL_HANDLE) {
|
|
|
|
|
|
|
|
PFN_vkDestroyDebugUtilsMessengerEXT destroy_messenger = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context->instance, "vkDestroyDebugUtilsMessengerEXT");
|
|
|
|
|
|
|
|
destroy_messenger(context->instance, context->debug_messenger, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkDestroyInstance(context->instance, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
free(context);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(window != 0) {
|
|
|
|
|
|
|
|
glfwDestroyWindow(window);
|
|
|
|
glfwTerminate();
|
|
|
|
glfwTerminate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
int main() {
|
|
|
|
GLFWwindow* window = init_window(800, 600);
|
|
|
|
GLFWwindow* window = init_window(800, 600);
|
|
|
@ -98,15 +597,15 @@ int main() {
|
|
|
|
return 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int result = init_vulkan();
|
|
|
|
VulkanContext* context = init_vulkan(window);
|
|
|
|
if (result != 0) {
|
|
|
|
if (context == 0) {
|
|
|
|
fprintf(stderr, "failed to initialize vulkan: err %d\n", result);
|
|
|
|
fprintf(stderr, "failed to initialize vulkan context\n");
|
|
|
|
return 2;
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
main_loop(window);
|
|
|
|
main_loop(window);
|
|
|
|
|
|
|
|
|
|
|
|
cleanup(window);
|
|
|
|
cleanup(window, context);
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|