roleplay/client/src/gpu.c

1133 lines
38 KiB
C

#include "gpu.h"
#include "GLFW/glfw3.h"
#include "stdio.h"
#include "string.h"
#include "vk_mem_alloc.h"
#include "vulkan/vulkan_core.h"
const uint32_t MAX_FRAMES_IN_FLIGHT = 2;
const char * validation_layers[] = {
"VK_LAYER_KHRONOS_validation",
//"VK_LAYER_LUNARG_api_dump",
"VK_LAYER_KHRONOS_synchronization2",
"VK_LAYER_KHRONOS_shader_object",
};
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_EXT_DEBUG_REPORT_EXTENSION_NAME,
#ifdef __APPLE__
"VK_EXT_metal_surface",
#endif
VK_KHR_SURFACE_EXTENSION_NAME,
};
uint32_t instance_extension_count = sizeof(instance_extensions) / sizeof(const char *);
const char * device_extensions[] = {
#ifdef __APPLE__
"VK_KHR_portability_subset",
#endif
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
};
uint32_t device_extension_count = sizeof(device_extensions) / sizeof(const char *);
VkFormat depth_formats[] = {
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT
};
uint32_t depth_format_count = sizeof(depth_formats) / sizeof(VkFormat);
void glfw_error(int error, const char* description) {
fprintf(stderr, "GLFW_ERR: 0x%02x - %s\n", error, description);
}
GLFWwindow* init_window() {
glfwInit();
glfwSetErrorCallback(glfw_error);
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
GLFWwindow* window = glfwCreateWindow(800, 500, "roleplay", 0, 0);
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);
fprintf(stderr, "Failed to find layer %s\n", layers[i]);
return false;
}
}
free(available_layers);
return true;
}
VkResult create_instance(VkInstance* instance) {
if(check_validation_layers(validation_layers, validation_layer_count) == false) {
return VK_ERROR_VALIDATION_FAILED_EXT;
}
VkApplicationInfo app_info = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "roleplay",
.applicationVersion = VK_MAKE_VERSION(0, 0, 1),
.pEngineName = "roleplay",
.engineVersion = VK_MAKE_VERSION(0, 0, 1),
.apiVersion = VK_API_VERSION_1_2,
};
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
const char** requested_extensions = malloc(sizeof(char*)*(glfwExtensionCount + instance_extension_count));
for (uint32_t i = 0; i < glfwExtensionCount; i++) {
requested_extensions[i] = glfwExtensions[i];
}
for (uint32_t i = 0; i < instance_extension_count; i++) {
requested_extensions[glfwExtensionCount + i] = instance_extensions[i];
}
VkInstanceCreateInfo instance_info = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &app_info,
.enabledLayerCount = validation_layer_count,
.ppEnabledLayerNames = validation_layers,
.enabledExtensionCount = glfwExtensionCount + instance_extension_count,
.ppEnabledExtensionNames = requested_extensions,
.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR,
};
VkResult result = vkCreateInstance(&instance_info, 0, instance);
if(result != VK_SUCCESS) {
return result;
}
free(requested_extensions);
return VK_SUCCESS;
}
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;
}
VkResult create_debug_messenger(VkInstance instance, VkDebugUtilsMessengerEXT* debug_messenger) {
VkDebugUtilsMessengerCreateInfoEXT messenger_info = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_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 | VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT,
.pfnUserCallback = debug_callback,
.pUserData = 0,
};
PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
VkResult result;
result = func(instance, &messenger_info, 0, debug_messenger);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult get_best_physical_device(VkInstance instance, VkPhysicalDevice* device) {
uint32_t device_count = 0;
VkResult result;
result = vkEnumeratePhysicalDevices(instance, &device_count, 0);
if(result != VK_SUCCESS) {
return result;
}
VkPhysicalDevice* devices = malloc(sizeof(VkPhysicalDevice)*device_count);
result = vkEnumeratePhysicalDevices(instance, &device_count, devices);
if(result != VK_SUCCESS) {
free(devices);
return result;
}
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 VK_SUCCESS;
}
VkResult create_logical_device(VkPhysicalDevice physical_device, VkSurfaceKHR surface, Queue* graphics_queue, Queue* present_queue, Queue* transfer_queue, VkDevice* device) {
if(graphics_queue == NULL || present_queue == NULL || transfer_queue == NULL || device == NULL) {
return VK_ERROR_VALIDATION_FAILED_EXT;
}
uint32_t queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, NULL);
VkQueueFamilyProperties* queue_families = malloc(sizeof(VkQueueFamilyProperties)*queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families);
graphics_queue->family = 0xFFFFFFFF;
present_queue->family = 0xFFFFFFFF;
for(uint32_t idx = 0; idx < queue_family_count; idx++) {
VkBool32 present_support = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, idx, surface, &present_support);
VkBool32 graphics_support = (queue_families[idx].queueFlags & VK_QUEUE_GRAPHICS_BIT);
if(graphics_support && present_support) {
graphics_queue->family = idx;
graphics_queue->index = 0;
present_queue->family = idx;
present_queue->index = 0;
break;
} else if (graphics_support && (graphics_queue->family == 0xFFFFFFFF)) {
graphics_queue->family = idx;
graphics_queue->index = 0;
} else if (present_support && (present_queue->family == 0xFFFFFFFF)) {
graphics_queue->family = idx;
present_queue->index = 0;
}
}
transfer_queue->family = 0xFFFFFFFF;
for(uint32_t idx = 0; idx < queue_family_count; idx++) {
VkBool32 graphics_support = (queue_families[idx].queueFlags & VK_QUEUE_GRAPHICS_BIT);
VkBool32 compute_support = (queue_families[idx].queueFlags & VK_QUEUE_COMPUTE_BIT);
VkBool32 is_graphics_family = (graphics_queue->family == idx);
VkBool32 is_present_family = (present_queue->family == idx);
uint32_t queue_count = queue_families[idx].queueCount;
if(is_graphics_family && (queue_count == 1)) {
continue;
} else if (is_present_family && (queue_count == 1)) {
continue;
}
if(graphics_support && compute_support) {
transfer_queue->family = idx;
if(is_graphics_family || is_present_family) {
transfer_queue->index = 1;
} else {
transfer_queue->index = 0;
}
}
}
if(graphics_queue->family == 0xFFFFFFFF || present_queue->family == 0xFFFFFFFF || transfer_queue->family == 0xFFFFFFFF) {
return VK_ERROR_INITIALIZATION_FAILED;
}
VkDeviceQueueCreateInfo queue_create_info[3] = {};
uint32_t queue_count = 0;
float default_queue_priority = 1.0f;
if(graphics_queue->family == present_queue->family && graphics_queue->family == transfer_queue->family) {
queue_count = 1;
queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info[0].queueFamilyIndex = graphics_queue->family;
queue_create_info[0].queueCount = 2;
queue_create_info[0].pQueuePriorities = &default_queue_priority;
} else if (graphics_queue->family == present_queue->family) {
queue_count = 2;
queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info[0].queueFamilyIndex = graphics_queue->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 = transfer_queue->family;
queue_create_info[1].queueCount = 1;
queue_create_info[1].pQueuePriorities = &default_queue_priority;
} else if (graphics_queue->family == transfer_queue->family) {
queue_count = 2;
queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info[0].queueFamilyIndex = graphics_queue->family;
queue_create_info[0].queueCount = 2;
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 = present_queue->family;
queue_create_info[1].queueCount = 1;
queue_create_info[1].pQueuePriorities = &default_queue_priority;
} else {
queue_count = 3;
queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info[0].queueFamilyIndex = graphics_queue->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 = present_queue->family;
queue_create_info[1].queueCount = 1;
queue_create_info[1].pQueuePriorities = &default_queue_priority;
queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info[1].queueFamilyIndex = transfer_queue->family;
queue_create_info[1].queueCount = 1;
queue_create_info[1].pQueuePriorities = &default_queue_priority;
}
VkPhysicalDeviceVulkan12Features features_12 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.bufferDeviceAddress = VK_TRUE,
.runtimeDescriptorArray = VK_TRUE,
.descriptorIndexing = VK_TRUE,
.descriptorBindingPartiallyBound = VK_TRUE,
.descriptorBindingVariableDescriptorCount = VK_TRUE,
.descriptorBindingUniformBufferUpdateAfterBind = VK_TRUE,
.descriptorBindingStorageBufferUpdateAfterBind = VK_TRUE,
.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE,
};
VkPhysicalDeviceFeatures device_features = {
.samplerAnisotropy = VK_TRUE,
};
VkDeviceCreateInfo device_create_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pQueueCreateInfos = queue_create_info,
.queueCreateInfoCount = queue_count,
.pEnabledFeatures = &device_features,
.enabledExtensionCount = device_extension_count,
.ppEnabledExtensionNames = device_extensions,
.enabledLayerCount = validation_layer_count,
.ppEnabledLayerNames = validation_layers,
.pNext = &features_12,
};
VkResult result = vkCreateDevice(physical_device, &device_create_info, 0, device);
if(result != VK_SUCCESS) {
return result;
}
vkGetDeviceQueue(*device, graphics_queue->family, graphics_queue->index, &graphics_queue->handle);
vkGetDeviceQueue(*device, present_queue->family, present_queue->index, &present_queue->handle);
vkGetDeviceQueue(*device, transfer_queue->family, transfer_queue->index, &transfer_queue->handle);
return VK_SUCCESS;
}
VkResult create_memory_allocator(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, VmaAllocator* allocator) {
VmaAllocatorCreateInfo allocator_create_info = {
.vulkanApiVersion = VK_API_VERSION_1_2,
.instance = instance,
.physicalDevice = physical_device,
.device = device,
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
};
VkResult result = vmaCreateAllocator(&allocator_create_info, allocator);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult get_swapchain_details(VkPhysicalDevice physical_device, VkSurfaceKHR surface, SwapchainDetails* details) {
details->formats = 0;
details->present_modes = 0;
VkResult result;
result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &details->capabilities);
if(result != VK_SUCCESS) {
return result;
}
result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &details->formats_count, 0);
if(result != VK_SUCCESS) {
return result;
}
details->formats = malloc(sizeof(VkSurfaceFormatKHR)*details->formats_count);
result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &details->formats_count, details->formats);
if(result != VK_SUCCESS) {
free(details->formats);
return result;
}
result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &details->present_modes_count, 0);
if(result != VK_SUCCESS) {
free(details->formats);
return result;
}
details->present_modes = malloc(sizeof(VkPresentModeKHR)*details->present_modes_count);
result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &details->present_modes_count, details->present_modes);
if(result != VK_SUCCESS) {
free(details->formats);
free(details->present_modes);
return result;
}
return VK_SUCCESS;
}
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;
}
VkResult create_swapchain(VkDevice device, VkSurfaceFormatKHR format, VkPresentModeKHR present_mode, VkExtent2D extent, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR capabilities, uint32_t graphics_family_index, uint32_t present_family_index, VkSwapchainKHR* swapchain) {
uint32_t image_count = capabilities.minImageCount + 1;
uint32_t max_images = capabilities.maxImageCount;
if((max_images > 0) && (image_count > max_images)) {
image_count = max_images;
}
VkSwapchainCreateInfoKHR swapchain_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = surface,
.minImageCount = image_count,
.imageFormat = format.format,
.imageColorSpace = format.colorSpace,
.imageExtent = extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.preTransform = capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
.presentMode = present_mode,
.clipped = VK_TRUE,
.oldSwapchain = *swapchain,
};
uint32_t queue_families[2] = {graphics_family_index, present_family_index};
if(graphics_family_index != present_family_index) {
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;
}
VkResult result;
result = vkCreateSwapchainKHR(device, &swapchain_info, 0, swapchain);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult get_swapchain_images(VkDevice device, VkSwapchainKHR swapchain, VkImage** images, uint32_t* image_count) {
VkResult result;
result = vkGetSwapchainImagesKHR(device, swapchain, image_count, 0);
if(result != VK_SUCCESS) {
return result;
}
*images = malloc(sizeof(VkImage)*(*image_count));
if(*images == 0) {
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
result = vkGetSwapchainImagesKHR(device, swapchain, image_count, *images);
if(result != VK_SUCCESS) {
free(*images);
return result;
}
return VK_SUCCESS;
}
VkResult create_image_views(VkDevice device, uint32_t image_count, VkImage* images, VkSurfaceFormatKHR format, VkImageView** image_views) {
*image_views = malloc(sizeof(VkImageView)*image_count);
if(*image_views == 0) {
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
for(uint32_t i = 0; i < image_count; i++) {
VkImageViewCreateInfo view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = images[i],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = format.format,
.components = {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
VkResult result = vkCreateImageView(device, &view_info, 0, &(*image_views)[i]);
if(result != VK_SUCCESS) {
free(*image_views);
return result;
}
}
return VK_SUCCESS;
}
VkResult find_depth_format(VkPhysicalDevice physical_device, VkImageTiling tiling, VkFormatFeatureFlags features, VkFormat* format) {
for(uint32_t i = 0; i < depth_format_count; i++) {
VkFormatProperties properties;
vkGetPhysicalDeviceFormatProperties(physical_device, depth_formats[i], &properties);
if(tiling == VK_IMAGE_TILING_LINEAR && (properties.linearTilingFeatures & features) == features) {
*format = depth_formats[i];
return VK_SUCCESS;
} else if (tiling == VK_IMAGE_TILING_OPTIMAL && (properties.optimalTilingFeatures & features) == features) {
*format = depth_formats[i];
return VK_SUCCESS;
}
}
return VK_ERROR_UNKNOWN;
}
VkResult create_render_pass(VkDevice device, VkSurfaceFormatKHR format, VkFormat depth_format, VkRenderPass* render_pass) {
VkAttachmentDescription attachments[] = {
{
.format = format.format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
},
{
.format = depth_format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
},
};
VkAttachmentReference color_attachment_refs[] = {
{
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
},
};
VkAttachmentReference depth_attachment_ref = {
.attachment = 1,
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
};
// Create a subpass with the color and depth attachments
VkSubpassDescription subpasses[] = {
{
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = sizeof(color_attachment_refs)/sizeof(VkAttachmentReference),
.pColorAttachments = color_attachment_refs,
.pDepthStencilAttachment = &depth_attachment_ref,
},
{
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = sizeof(color_attachment_refs)/sizeof(VkAttachmentReference),
.pColorAttachments = color_attachment_refs,
},
};
// This basically says "make sure nothing else is writing to the depth_stencil or the color attachment during the pipeline
VkSubpassDependency dependencies[] = {
{
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
},
{
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
},
{
.srcSubpass = 0,
.dstSubpass = 1,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
},
};
VkRenderPassCreateInfo render_info = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = sizeof(attachments)/sizeof(VkAttachmentDescription),
.pAttachments = attachments,
.subpassCount = sizeof(subpasses)/sizeof(VkSubpassDescription),
.pSubpasses = subpasses,
.dependencyCount = sizeof(dependencies)/sizeof(VkSubpassDependency),
.pDependencies = dependencies,
};
VkResult result = vkCreateRenderPass(device, &render_info, 0, render_pass);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult create_swapchain_framebuffers(VkDevice device, uint32_t image_count, VkImageView* image_views, VkImageView depth_image_view, VkRenderPass render_pass, VkExtent2D extent, VkFramebuffer** framebuffers) {
*framebuffers = malloc(sizeof(VkFramebuffer)*image_count);
if(*framebuffers == 0) {
return 0;
}
VkFramebuffer* framebuffer_ptr = *framebuffers;
for(uint32_t i = 0; i < image_count; i++) {
VkImageView attachments[] = {
image_views[i],
depth_image_view,
};
VkFramebufferCreateInfo framebuffer_info = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = render_pass,
.attachmentCount = 2,
.pAttachments = attachments,
.width = extent.width,
.height = extent.height,
.layers = 1,
};
VkResult result = vkCreateFramebuffer(device, &framebuffer_info, 0, &framebuffer_ptr[i]);
if(result != VK_SUCCESS) {
free(*framebuffers);
return result;
}
}
return VK_SUCCESS;
}
VkSemaphore* create_semaphores(VkDevice device, VkSemaphoreCreateFlags flags, uint32_t count) {
VkSemaphore* semaphores = malloc(sizeof(VkSemaphore)*count);
if(semaphores == 0) {
return 0;
}
VkSemaphoreCreateInfo semaphore_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.flags = flags,
};
for(uint32_t i = 0; i < count; i++) {
VkResult result = vkCreateSemaphore(device, &semaphore_info, 0, &semaphores[i]);
if(result != VK_SUCCESS) {
free(semaphores);
return 0;
}
}
return semaphores;
}
VkFence* create_fences(VkDevice device, VkFenceCreateFlags flags, uint32_t count) {
VkFence* fences = malloc(sizeof(VkFence)*count);
if(fences == 0) {
return 0;
}
VkFenceCreateInfo fence_info = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = flags,
};
for(uint32_t i = 0; i < count; i++) {
VkResult result = vkCreateFence(device, &fence_info, 0, &fences[i]);
if(result != VK_SUCCESS) {
free(fences);
return 0;
}
}
return fences;
}
VkCommandBuffer* create_command_buffers(VkDevice device, VkCommandPool command_pool, uint32_t image_count) {
VkCommandBufferAllocateInfo alloc_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = command_pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = image_count,
};
VkCommandBuffer* command_buffers = malloc(sizeof(VkCommandBuffer)*image_count);
if(command_buffers == 0) {
return 0;
}
VkResult result = vkAllocateCommandBuffers(device, &alloc_info, command_buffers);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
return command_buffers;
}
VkResult create_depth_image(VkDevice device, VkFormat depth_format, VkExtent2D swapchain_extent, VmaAllocator allocator, VkCommandPool extra_graphics_pool, Queue graphics_queue, VkImage* depth_image, VmaAllocation* depth_image_memory, VkImageView* depth_image_view) {
VkExtent3D depth_extent = {
.width = swapchain_extent.width,
.height = swapchain_extent.height,
.depth = 1,
};
VkImageCreateInfo depth_image_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.extent = depth_extent,
.mipLevels = 1,
.arrayLayers = 1,
.format = depth_format,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.samples = VK_SAMPLE_COUNT_1_BIT,
.flags = 0,
};
VmaAllocationCreateInfo allocation_info = {
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkResult result = vmaCreateImage(allocator, &depth_image_info, &allocation_info, depth_image, depth_image_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkImageViewCreateInfo depth_view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = *depth_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = depth_format,
.components = {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
result = vkCreateImageView(device, &depth_view_info, 0, depth_image_view);
if(result != VK_SUCCESS) {
return result;
}
result = command_transition_image_layout(device, extra_graphics_pool, graphics_queue, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, *depth_image, 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, VK_IMAGE_ASPECT_DEPTH_BIT);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult init_vulkan(GLFWwindow* window, RenderContext* context) {
VkResult result;
if(context == NULL) {
return VK_ERROR_VALIDATION_FAILED_EXT;
}
int monitor_count;
GLFWmonitor** monitors = glfwGetMonitors(&monitor_count);
glfwGetMonitorContentScale(monitors[0], &context->window_scale[0], &context->window_scale[1]);
result = create_instance(&context->instance);
if(result != VK_SUCCESS) {
return result;
}
result = create_debug_messenger(context->instance, &context->debug_messenger);
if(result != VK_SUCCESS) {
return result;
}
result = get_best_physical_device(context->instance, &context->physical_device);
if(result != VK_SUCCESS) {
return result;
}
vkGetPhysicalDeviceMemoryProperties(context->physical_device, &context->memories);
result = glfwCreateWindowSurface(context->instance, window, 0, &context->surface);
if(result != VK_SUCCESS) {
return result;
}
result = create_logical_device(context->physical_device, context->surface, &context->graphics_queue, &context->present_queue, &context->transfer_queue, &context->device);
if(result != VK_SUCCESS) {
return result;
}
result = create_memory_allocator(context->instance, context->physical_device, context->device, &context->allocator);
if(result != VK_SUCCESS) {
return result;
}
VkCommandPoolCreateInfo extra_pool_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.queueFamilyIndex = context->graphics_queue.family,
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
};
result = vkCreateCommandPool(context->device, &extra_pool_info, 0, &context->extra_graphics_pool);
if(result != VK_SUCCESS) {
return result;
}
VkCommandPoolCreateInfo graphics_pool_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = context->graphics_queue.family,
};
result = vkCreateCommandPool(context->device, &graphics_pool_info, 0, &context->graphics_pool);
if(result != VK_SUCCESS) {
return result;
}
VkCommandPoolCreateInfo transfer_pool_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
.queueFamilyIndex = context->transfer_queue.family,
};
result = vkCreateCommandPool(context->device, &transfer_pool_info, 0, &context->transfer_pool);
if(result != VK_SUCCESS) {
return result;
}
context->image_available_semaphores = create_semaphores(context->device, 0, MAX_FRAMES_IN_FLIGHT);
if(context->image_available_semaphores == NULL) {
return VK_ERROR_UNKNOWN;
}
context->render_finished_semaphores = create_semaphores(context->device, 0, MAX_FRAMES_IN_FLIGHT);
if(context->render_finished_semaphores == NULL) {
return VK_ERROR_UNKNOWN;
}
context->in_flight_fences = create_fences(context->device, VK_FENCE_CREATE_SIGNALED_BIT, MAX_FRAMES_IN_FLIGHT);
if(context->in_flight_fences == NULL) {
return VK_ERROR_UNKNOWN;
}
result = get_swapchain_details(context->physical_device, context->surface, &context->swapchain_details);
if(result != VK_SUCCESS) {
return result;
}
context->swapchain_format = choose_swapchain_format(context->swapchain_details);
context->swapchain_present_mode = choose_present_mode(context->swapchain_details);
context->swapchain_extent = choose_swapchain_extent(context->swapchain_details);
context->swapchain = VK_NULL_HANDLE;
result = create_swapchain(context->device, context->swapchain_format, context->swapchain_present_mode, context->swapchain_extent, context->surface, context->swapchain_details.capabilities, context->graphics_queue.family, context->present_queue.family, &context->swapchain);
if(result != VK_SUCCESS) {
return result;
}
result = get_swapchain_images(context->device, context->swapchain, &context->swapchain_images, &context->swapchain_image_count);
if(result != VK_SUCCESS) {
return result;
}
context->swapchain_command_buffers = create_command_buffers(context->device, context->graphics_pool, context->swapchain_image_count);
if(context->swapchain_command_buffers == NULL) {
return VK_ERROR_UNKNOWN;
}
result = create_image_views(context->device, context->swapchain_image_count, context->swapchain_images, context->swapchain_format, &context->swapchain_image_views);
if(result != VK_SUCCESS) {
return result;
}
result = find_depth_format(context->physical_device, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, &context->depth_format);
if(result != VK_SUCCESS) {
return result;
}
result = create_render_pass(context->device, context->swapchain_format, context->depth_format, &context->render_pass);
if(result != VK_SUCCESS) {
return result;
}
result = create_depth_image(context->device, context->depth_format, context->swapchain_extent, context->allocator, context->extra_graphics_pool, context->graphics_queue, &context->depth_image, &context->depth_image_memory, &context->depth_image_view);
if(result != VK_SUCCESS) {
return result;
}
result = create_swapchain_framebuffers(context->device, context->swapchain_image_count, context->swapchain_image_views, context->depth_image_view, context->render_pass, context->swapchain_extent, &context->swapchain_framebuffers);
if(result != VK_SUCCESS) {
return result;
}
context->current_frame = 0;
return VK_SUCCESS;
}
VkResult create_transfer_buffer(
VmaAllocator allocator,
VkDeviceSize size,
VkBuffer* buffer,
VmaAllocation* memory,
void** mapped) {
VkBufferCreateInfo buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.size = size,
};
VmaAllocationCreateInfo memory_info = {
.usage = VMA_MEMORY_USAGE_CPU_TO_GPU,
};
VkResult result = vmaCreateBuffer(allocator, &buffer_info, &memory_info, buffer, memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
result = vmaMapMemory(allocator, *memory, mapped);
if(result != VK_SUCCESS) {
vmaDestroyBuffer(allocator, *buffer, *memory);
return result;
}
return VK_SUCCESS;
}
VkResult create_storage_buffer(
VmaAllocator allocator,
VkBufferUsageFlags usage,
VkDeviceSize size,
VkBuffer* buffer,
VmaAllocation* memory) {
VkBufferCreateInfo buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = size,
.usage = usage | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
};
VmaAllocationCreateInfo memory_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
return vmaCreateBuffer(allocator, &buffer_info, &memory_info, buffer, memory, NULL);
};
void destroy_transfer_buffer(
VmaAllocator allocator,
VkBuffer buffer,
VmaAllocation memory) {
vmaUnmapMemory(allocator, memory);
vmaDestroyBuffer(allocator, buffer, memory);
}
VkDeviceAddress buffer_address(VkDevice device, VkBuffer buffer) {
VkBufferDeviceAddressInfo info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = buffer,
};
return vkGetBufferDeviceAddress(device, &info);
}
VkCommandBuffer command_begin_single(VkDevice device, VkCommandPool transfer_pool) {
VkCommandBufferAllocateInfo command_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandPool = transfer_pool,
.commandBufferCount = 1,
};
VkCommandBuffer command_buffer;
VkResult result = vkAllocateCommandBuffers(device, &command_info, &command_buffer);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
VkCommandBufferBeginInfo begin_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
result = vkBeginCommandBuffer(command_buffer, &begin_info);
if(result != VK_SUCCESS) {
vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer);
return VK_NULL_HANDLE;
}
return command_buffer;
}
VkResult command_end_single(VkDevice device, VkCommandBuffer command_buffer, VkCommandPool transfer_pool, Queue transfer_queue) {
VkResult result = vkEndCommandBuffer(command_buffer);
if(result != VK_SUCCESS) {
vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer);
return result;
}
VkSubmitInfo submit_info = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &command_buffer,
};
result = vkQueueSubmit(transfer_queue.handle, 1, &submit_info, 0);
if(result != VK_SUCCESS) {
vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer);
return result;
}
result = vkQueueWaitIdle(transfer_queue.handle);
vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer);
return result;
}
void command_copy_buffer(VkCommandBuffer command_buffer, VkBuffer src, VkBuffer dst, VkDeviceSize src_offset, VkDeviceSize dst_offset, VkDeviceSize size) {
VkBufferCopy copy = {
.srcOffset = src_offset,
.dstOffset = dst_offset,
.size = size,
};
vkCmdCopyBuffer(command_buffer, src, dst, 1, &copy);
}
VkResult command_transition_image_layout(VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue, VkImageLayout old_layout, VkImageLayout new_layout, VkImage image, VkAccessFlags src_mask, VkAccessFlags dst_mask, VkPipelineStageFlags source, VkPipelineStageFlags dest, uint32_t source_family, uint32_t dest_family, VkImageAspectFlags aspect_flags) {
VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool);
VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.oldLayout = old_layout,
.newLayout = new_layout,
.srcQueueFamilyIndex = source_family,
.dstQueueFamilyIndex = dest_family,
.image = image,
.subresourceRange = {
.aspectMask = aspect_flags,
.levelCount = 1,
.layerCount = 1,
.baseMipLevel = 0,
.baseArrayLayer = 0,
},
.srcAccessMask = src_mask,
.dstAccessMask = dst_mask,
};
vkCmdPipelineBarrier(command_buffer, source, dest, 0, 0, 0, 0, 0, 1, &barrier);
return command_end_single(device, command_buffer, transfer_pool, transfer_queue);
}