2024-01-07 16:34:09 -07:00
|
|
|
#define VK_USE_PLATFORM_MACOS_MVK
|
2024-01-07 03:04:12 -07:00
|
|
|
#include "vulkan/vulkan_core.h"
|
2024-01-07 01:01:57 -07:00
|
|
|
#define GLFW_INCLUDE_VULKAN
|
|
|
|
#include <GLFW/glfw3.h>
|
2024-01-07 16:34:09 -07:00
|
|
|
#define GLFW_EXPOSE_NATIVE_COCOA
|
|
|
|
#include <GLFW/glfw3native.h>
|
2024-01-07 01:01:57 -07:00
|
|
|
|
2024-01-07 03:04:12 -07:00
|
|
|
#define GLM_FORCE_RADIANS
|
|
|
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
2024-01-08 12:42:59 -07:00
|
|
|
#include <cglm/types.h>
|
|
|
|
#include <cglm/mat4.h>
|
2024-01-07 03:04:12 -07:00
|
|
|
#include <stdio.h>
|
2024-01-07 16:34:09 -07:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
typedef struct QueueIndicesStruct {
|
|
|
|
uint32_t graphics_family;
|
|
|
|
uint32_t graphics_index;
|
|
|
|
|
|
|
|
uint32_t present_family;
|
|
|
|
uint32_t present_index;
|
2024-01-08 11:59:58 -07:00
|
|
|
|
|
|
|
uint32_t transfer_family;
|
|
|
|
uint32_t transfer_index;
|
2024-01-07 16:34:09 -07:00
|
|
|
} QueueIndices;
|
|
|
|
|
|
|
|
typedef struct QueuesStruct {
|
|
|
|
VkQueue graphics;
|
|
|
|
VkQueue present;
|
2024-01-08 11:59:58 -07:00
|
|
|
VkQueue transfer;
|
2024-01-07 16:34:09 -07:00
|
|
|
} Queues;
|
|
|
|
|
|
|
|
typedef struct SwapchainDetailsStruct {
|
|
|
|
VkSurfaceCapabilitiesKHR capabilities;
|
|
|
|
|
|
|
|
VkSurfaceFormatKHR* formats;
|
|
|
|
uint32_t formats_count;
|
|
|
|
|
|
|
|
VkPresentModeKHR* present_modes;
|
|
|
|
uint32_t present_modes_count;
|
|
|
|
} SwapchainDetails;
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
typedef struct SwapchainImagesStruct {
|
|
|
|
VkImage* images;
|
|
|
|
uint32_t count;
|
|
|
|
} SwapchainImages;
|
|
|
|
|
2024-01-08 09:44:47 -07:00
|
|
|
typedef struct AllocatedBufferStruct {
|
|
|
|
VkBuffer buffer;
|
|
|
|
VkDeviceMemory memory;
|
|
|
|
} AllocatedBuffer;
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
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;
|
2024-01-07 22:27:53 -07:00
|
|
|
|
|
|
|
VkSurfaceFormatKHR swapchain_format;
|
|
|
|
VkPresentModeKHR swapchain_present_mode;
|
|
|
|
VkExtent2D swapchain_extent;
|
|
|
|
|
|
|
|
uint32_t swapchain_image_count;
|
2024-01-07 22:55:36 -07:00
|
|
|
// Per image objects
|
2024-01-07 22:27:53 -07:00
|
|
|
VkImage* swapchain_images;
|
|
|
|
VkImageView* swapchain_image_views;
|
|
|
|
VkFramebuffer* swapchain_framebuffers;
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
uint32_t max_frames_in_flight;
|
|
|
|
// Per frame objects
|
|
|
|
VkCommandBuffer* swapchain_command_buffers;
|
|
|
|
VkSemaphore* image_available_semaphores;
|
|
|
|
VkSemaphore* render_finished_semaphores;
|
|
|
|
VkFence* in_flight_fences;
|
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
AllocatedBuffer* uniform_buffers;
|
|
|
|
void** uniform_buffer_ptrs;
|
|
|
|
VkDescriptorPool descriptor_pool;
|
|
|
|
VkDescriptorSet* descriptor_sets;
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
VkRenderPass render_pass;
|
|
|
|
|
|
|
|
VkCommandPool graphics_command_pool;
|
2024-01-08 11:59:58 -07:00
|
|
|
VkCommandPool transfer_command_pool;
|
2024-01-07 22:27:53 -07:00
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
VkDescriptorSetLayout triangle_descriptor_set;
|
2024-01-07 22:27:53 -07:00
|
|
|
VkPipelineLayout triangle_pipeline_layout;
|
|
|
|
VkPipeline triangle_pipeline;
|
2024-01-08 11:59:58 -07:00
|
|
|
AllocatedBuffer triangle_vertex_buffer;
|
|
|
|
AllocatedBuffer triangle_index_buffer;
|
2024-01-07 22:27:53 -07:00
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
uint32_t current_frame;
|
2024-01-07 16:34:09 -07:00
|
|
|
} VulkanContext;
|
|
|
|
|
2024-01-08 09:44:47 -07:00
|
|
|
struct Vertex{
|
|
|
|
vec2 pos;
|
|
|
|
vec3 color;
|
|
|
|
};
|
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
struct ShaderUBO {
|
|
|
|
mat4 model;
|
|
|
|
mat4 view;
|
|
|
|
mat4 proj;
|
|
|
|
};
|
|
|
|
|
2024-01-08 09:44:47 -07:00
|
|
|
const struct Vertex vertices[] = {
|
2024-01-08 11:59:58 -07:00
|
|
|
{.pos = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}},
|
|
|
|
{.pos = { 0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}},
|
|
|
|
{.pos = { 0.5f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}},
|
|
|
|
{.pos = {-0.5f, 0.5f}, .color = {1.0f, 1.0f, 1.0f}},
|
|
|
|
};
|
|
|
|
|
|
|
|
const uint16_t indices[] = {
|
|
|
|
0, 1, 2, 2, 3, 0,
|
2024-01-08 09:44:47 -07:00
|
|
|
};
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
const char * validation_layers[] = {
|
|
|
|
"VK_LAYER_KHRONOS_validation",
|
2024-01-08 11:59:58 -07:00
|
|
|
//"VK_LAYER_LUNARG_api_dump",
|
|
|
|
//"VK_LAYER_KHRONOS_profiles",
|
|
|
|
//"VK_LAYER_KHRONOS_synchronization2",
|
|
|
|
"VK_LAYER_KHRONOS_shader_object",
|
2024-01-07 16:34:09 -07:00
|
|
|
};
|
|
|
|
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 *);
|
2024-01-07 03:04:12 -07:00
|
|
|
|
|
|
|
void glfw_error(int error, const char* description) {
|
|
|
|
fprintf(stderr, "GLFW_ERR: 0x%02x - %s\n", error, description);
|
|
|
|
}
|
|
|
|
|
2024-01-08 09:44:47 -07:00
|
|
|
VkVertexInputBindingDescription vertex_bindings[1];
|
|
|
|
VkVertexInputBindingDescription* vertex_binding_descriptions() {
|
|
|
|
vertex_bindings[0].binding = 0;
|
|
|
|
vertex_bindings[0].stride = sizeof(struct Vertex);
|
|
|
|
vertex_bindings[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
|
|
|
|
|
|
|
return vertex_bindings;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkVertexInputAttributeDescription vertex_attributes[2];
|
|
|
|
VkVertexInputAttributeDescription* vertex_attribute_descriptions() {
|
|
|
|
vertex_attributes[0].binding = 0;
|
|
|
|
vertex_attributes[0].location = 0;
|
|
|
|
vertex_attributes[0].format = VK_FORMAT_R32G32_SFLOAT;
|
|
|
|
vertex_attributes[0].offset = offsetof(struct Vertex, pos);
|
|
|
|
|
|
|
|
vertex_attributes[1].binding = 0;
|
|
|
|
vertex_attributes[1].location = 1;
|
|
|
|
vertex_attributes[1].format = VK_FORMAT_R32G32B32_SFLOAT;
|
|
|
|
vertex_attributes[1].offset = offsetof(struct Vertex, color);
|
|
|
|
|
|
|
|
return vertex_attributes;
|
|
|
|
}
|
|
|
|
|
2024-01-07 03:04:12 -07:00
|
|
|
GLFWwindow* init_window(int width, int height) {
|
|
|
|
glfwInit();
|
|
|
|
glfwSetErrorCallback(glfw_error);
|
|
|
|
|
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
|
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
|
|
|
GLFWwindow* window = glfwCreateWindow(width, height, "Vulkan window", 0, 0);
|
|
|
|
|
|
|
|
|
|
|
|
return window;
|
|
|
|
}
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
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;
|
2024-01-08 12:42:59 -07:00
|
|
|
}
|
2024-01-07 16:34:09 -07:00
|
|
|
|
|
|
|
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;
|
2024-01-08 12:42:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
VkDescriptorSet* create_descriptor_sets(VkDevice device, VkDescriptorSetLayout layout, VkDescriptorPool pool, AllocatedBuffer* uniform_buffers, uint32_t count) {
|
|
|
|
VkDescriptorSetLayout* layouts = malloc(sizeof(VkDescriptorSetLayout)*count);
|
|
|
|
if(layouts == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkDescriptorSet* sets = malloc(sizeof(VkDescriptorSet)*count);
|
|
|
|
if(sets == 0) {
|
|
|
|
free(layouts);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < count; i++) {
|
|
|
|
layouts[i] = layout;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkDescriptorSetAllocateInfo alloc_info = {};
|
|
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
|
|
alloc_info.descriptorPool = pool;
|
|
|
|
alloc_info.descriptorSetCount = count;
|
|
|
|
alloc_info.pSetLayouts = layouts;
|
|
|
|
|
|
|
|
VkResult result = vkAllocateDescriptorSets(device, &alloc_info, sets);
|
|
|
|
free(layouts);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
free(sets);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < count; i++) {
|
|
|
|
VkDescriptorBufferInfo buffer_info = {};
|
|
|
|
buffer_info.buffer = uniform_buffers[i].buffer;
|
|
|
|
buffer_info.offset = 0;
|
|
|
|
buffer_info.range = sizeof(struct ShaderUBO);
|
|
|
|
|
|
|
|
VkWriteDescriptorSet descriptor_write = {};
|
|
|
|
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
|
|
descriptor_write.dstSet = sets[i];
|
|
|
|
descriptor_write.dstBinding = 0;
|
|
|
|
descriptor_write.dstArrayElement = 0;
|
|
|
|
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
|
|
descriptor_write.descriptorCount = 1;
|
|
|
|
descriptor_write.pBufferInfo = &buffer_info;
|
|
|
|
descriptor_write.pImageInfo = 0;
|
|
|
|
descriptor_write.pTexelBufferView = 0;
|
|
|
|
|
|
|
|
vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sets;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkDescriptorSetLayout create_descriptor_set_layout(VkDevice device) {
|
|
|
|
VkDescriptorSetLayoutBinding layout_binding = {};
|
|
|
|
layout_binding.binding = 0;
|
|
|
|
layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
|
|
layout_binding.descriptorCount = 1;
|
|
|
|
layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
|
|
|
layout_binding.pImmutableSamplers = 0;
|
|
|
|
|
|
|
|
VkDescriptorSetLayoutCreateInfo layout_info = {};
|
|
|
|
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
|
|
layout_info.bindingCount = 1;
|
|
|
|
layout_info.pBindings = &layout_binding;
|
|
|
|
|
|
|
|
VkDescriptorSetLayout layout;
|
|
|
|
VkResult result = vkCreateDescriptorSetLayout(device, &layout_info, 0, &layout);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return layout;
|
|
|
|
}
|
2024-01-07 16:34:09 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
bool check_queue_indices(QueueIndices indices) {
|
|
|
|
return ((indices.graphics_family != 0xFFFFFFFF)
|
|
|
|
&& (indices.present_family != 0xFFFFFFFF)
|
|
|
|
&& (indices.transfer_family != 0xFFFFFFFF));
|
|
|
|
}
|
2024-01-07 16:34:09 -07:00
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
QueueIndices get_queue_indices(VkPhysicalDevice physical_device, VkSurfaceKHR surface) {
|
|
|
|
QueueIndices indices = {};
|
|
|
|
indices.graphics_family = 0xFFFFFFFF;
|
|
|
|
indices.graphics_index = 0xFFFFFFFF;
|
|
|
|
indices.present_family = 0xFFFFFFFF;
|
|
|
|
indices.present_index = 0xFFFFFFFF;
|
|
|
|
indices.transfer_family = 0xFFFFFFFF;
|
|
|
|
indices.transfer_index = 0xFFFFFFFF;
|
2024-01-07 16:34:09 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
for(uint32_t family_idx = 0; family_idx < queue_family_count; family_idx++) {
|
|
|
|
VkBool32 present_support;
|
|
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, family_idx, surface, &present_support);
|
|
|
|
for(uint32_t queue_idx = 0; queue_idx < queue_families[family_idx].queueCount; queue_idx++) {
|
|
|
|
if(((indices.graphics_family == 0xFFFFFFFF)
|
|
|
|
|| (indices.present_family == 0xFFFFFFFF)
|
|
|
|
|| (indices.present_family != indices.graphics_family))
|
|
|
|
&& (queue_families[family_idx].queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
|
|
|
&& (present_support == VK_TRUE)) {
|
|
|
|
fprintf(stderr, "Selected %d:%d for graphics and present queues\n", family_idx, queue_idx);
|
|
|
|
indices.graphics_family = family_idx;
|
|
|
|
indices.graphics_index = queue_idx;
|
|
|
|
|
|
|
|
indices.present_family = family_idx;
|
|
|
|
indices.present_index = queue_idx;
|
|
|
|
} else if((indices.graphics_family == 0xFFFFFFFF)
|
|
|
|
&& (queue_families[family_idx].queueFlags & VK_QUEUE_GRAPHICS_BIT)) {
|
|
|
|
fprintf(stderr, "Selected %d:%d for graphics queue\n", family_idx, queue_idx);
|
|
|
|
indices.graphics_family = family_idx;
|
|
|
|
indices.graphics_index = queue_idx;
|
|
|
|
} else if((indices.present_family == 0xFFFFFFFF)
|
|
|
|
&& (present_support == VK_TRUE)) {
|
|
|
|
fprintf(stderr, "Selected %d:%d for present queue\n", family_idx, queue_idx);
|
|
|
|
indices.present_family = family_idx;
|
|
|
|
indices.present_index = queue_idx;
|
|
|
|
} else if((indices.transfer_family == 0xFFFFFFFF)
|
|
|
|
&& (queue_families[family_idx].queueFlags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT))) {
|
|
|
|
fprintf(stderr, "Selected %d:%d for transfer queue\n", family_idx, queue_idx);
|
|
|
|
indices.transfer_family = family_idx;
|
|
|
|
indices.transfer_index = queue_idx;
|
|
|
|
}
|
2024-01-07 16:34:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(queue_families);
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
return indices;
|
2024-01-07 16:34:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2024-01-08 11:59:58 -07:00
|
|
|
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 | VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT;
|
2024-01-07 16:34:09 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
VkDescriptorPool create_descriptor_pool(VkDevice device, uint32_t size) {
|
|
|
|
VkDescriptorPoolSize pool_size = {};
|
|
|
|
pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
|
|
pool_size.descriptorCount = size;
|
|
|
|
|
|
|
|
VkDescriptorPoolCreateInfo pool_info = {};
|
|
|
|
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
|
|
pool_info.poolSizeCount = 1;
|
|
|
|
pool_info.pPoolSizes = &pool_size;
|
|
|
|
pool_info.maxSets = size;
|
|
|
|
|
|
|
|
VkDescriptorPool pool;
|
|
|
|
VkResult result = vkCreateDescriptorPool(device, &pool_info, 0, &pool);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pool;
|
|
|
|
}
|
|
|
|
|
2024-01-07 03:04:12 -07:00
|
|
|
VkInstance create_instance() {
|
|
|
|
VkInstance instance;
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
if(check_validation_layers(validation_layers, validation_layer_count) == false) {
|
|
|
|
fprintf(stderr, "requested validation layers not supported");
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkApplicationInfo app_info = {};
|
2024-01-07 03:04:12 -07:00
|
|
|
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
|
|
app_info.pApplicationName = "spacegame";
|
|
|
|
app_info.applicationVersion = VK_MAKE_VERSION(0, 0, 1);
|
|
|
|
app_info.pEngineName = "spacegame";
|
|
|
|
app_info.engineVersion = VK_MAKE_VERSION(0, 0, 1);
|
|
|
|
app_info.apiVersion = VK_API_VERSION_1_3;
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
VkInstanceCreateInfo instance_info = {};
|
2024-01-07 03:04:12 -07:00
|
|
|
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
|
|
instance_info.pApplicationInfo = &app_info;
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
instance_info.enabledLayerCount = validation_layer_count;
|
|
|
|
instance_info.ppEnabledLayerNames = validation_layers;
|
|
|
|
|
2024-01-07 03:04:12 -07:00
|
|
|
uint32_t glfwExtensionCount = 0;
|
|
|
|
const char** glfwExtensions;
|
|
|
|
|
|
|
|
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
const char** requested_extensions = malloc(sizeof(char*)*(glfwExtensionCount + instance_extension_count));
|
2024-01-07 03:13:49 -07:00
|
|
|
for (uint32_t i = 0; i < glfwExtensionCount; i++) {
|
|
|
|
requested_extensions[i] = glfwExtensions[i];
|
2024-01-07 03:04:12 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
for (uint32_t i = 0; i < instance_extension_count; i++) {
|
|
|
|
requested_extensions[glfwExtensionCount + i] = instance_extensions[i];
|
|
|
|
}
|
2024-01-07 03:13:49 -07:00
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
instance_info.enabledExtensionCount = glfwExtensionCount + instance_extension_count;
|
2024-01-07 03:13:49 -07:00
|
|
|
instance_info.ppEnabledExtensionNames = requested_extensions;
|
2024-01-07 03:04:12 -07:00
|
|
|
|
2024-01-07 03:13:49 -07:00
|
|
|
instance_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
|
|
|
|
2024-01-07 03:04:12 -07:00
|
|
|
VkResult result = vkCreateInstance(&instance_info, 0, &instance);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
fprintf(stderr, "vkCreateInstance: 0x%02x\n", result);
|
2024-01-07 16:34:09 -07:00
|
|
|
return VK_NULL_HANDLE;
|
2024-01-07 03:04:12 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 03:13:49 -07:00
|
|
|
free(requested_extensions);
|
|
|
|
|
2024-01-07 03:04:12 -07:00
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
VkDevice create_logical_device(VkPhysicalDevice physical_device, QueueIndices queue_indices) {
|
|
|
|
VkDevice device;
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
uint32_t unique_families[3] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
|
|
|
|
uint32_t unique_family_queues[3] = {0, 0, 0};
|
|
|
|
uint32_t unique_family_count = 0;
|
|
|
|
|
|
|
|
uint32_t queue_family[] = {queue_indices.transfer_family, queue_indices.graphics_family, queue_indices.present_family};
|
|
|
|
uint32_t unique_queue_count = 3;
|
|
|
|
if((queue_indices.graphics_family == queue_indices.present_family)
|
|
|
|
&& (queue_indices.graphics_index == queue_indices.present_index)) {
|
|
|
|
unique_queue_count = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(uint32_t queue_idx = 0; queue_idx < unique_queue_count; queue_idx++) {
|
|
|
|
uint32_t idx = 0xFFFFFFFF;
|
|
|
|
for(uint32_t check_idx = 0; check_idx < unique_family_count; check_idx++) {
|
|
|
|
if(queue_family[queue_idx] == unique_families[check_idx]) {
|
|
|
|
idx = check_idx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-01-07 16:34:09 -07:00
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
if(idx == 0xFFFFFFFF) {
|
|
|
|
unique_families[unique_family_count] = queue_family[queue_idx];
|
|
|
|
unique_family_queues[unique_family_count] += 1;
|
|
|
|
unique_family_count += 1;
|
|
|
|
} else {
|
|
|
|
unique_family_queues[idx] += 1;
|
|
|
|
}
|
2024-01-07 16:34:09 -07:00
|
|
|
}
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
VkDeviceQueueCreateInfo queue_create_info[3] = {};
|
|
|
|
float default_queue_priority = 1.0f;
|
|
|
|
for(uint32_t i = 0; i < unique_family_count; i++) {
|
|
|
|
queue_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
|
|
queue_create_info[i].queueFamilyIndex = unique_families[i];
|
|
|
|
queue_create_info[i].queueCount = unique_family_queues[i];
|
|
|
|
queue_create_info[i].pQueuePriorities = &default_queue_priority;
|
|
|
|
}
|
2024-01-07 16:34:09 -07:00
|
|
|
|
|
|
|
VkPhysicalDeviceFeatures device_features = {};
|
|
|
|
|
|
|
|
VkDeviceCreateInfo device_create_info = {};
|
|
|
|
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
|
|
device_create_info.pQueueCreateInfos = queue_create_info;
|
2024-01-08 11:59:58 -07:00
|
|
|
device_create_info.queueCreateInfoCount = unique_family_count;
|
2024-01-07 16:34:09 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
VkSwapchainKHR create_swapchain(VkDevice device, VkSurfaceFormatKHR format, VkPresentModeKHR present_mode, VkExtent2D extent, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR capabilities, QueueIndices indices, VkSwapchainKHR old_swapchain) {
|
|
|
|
uint32_t image_count = capabilities.minImageCount + 1;
|
|
|
|
uint32_t max_images = capabilities.maxImageCount;
|
2024-01-07 16:34:09 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
swapchain_info.preTransform = capabilities.currentTransform;
|
2024-01-07 16:34:09 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
SwapchainImages get_swapchain_images(VkDevice device, VkSwapchainKHR swapchain) {
|
|
|
|
SwapchainImages images;
|
|
|
|
images.images = 0;
|
|
|
|
images.count = 0;
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, 0);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
images.count = 0;
|
|
|
|
return images;
|
|
|
|
}
|
|
|
|
|
|
|
|
images.images = malloc(sizeof(VkImage)*images.count);
|
|
|
|
if(images.images == 0) {
|
|
|
|
images.count = 0;
|
|
|
|
return images;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, images.images);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
images.count = 0;
|
|
|
|
return images;
|
|
|
|
}
|
|
|
|
|
|
|
|
return images;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkImageView* create_image_views(VkDevice device, uint32_t image_count, VkImage* images, VkSurfaceFormatKHR format) {
|
|
|
|
VkImageView* image_views = malloc(sizeof(VkImageView)*image_count);
|
|
|
|
if(image_views == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < image_count; i++) {
|
|
|
|
VkImageViewCreateInfo view_info = {};
|
|
|
|
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
|
|
view_info.image = images[i];
|
|
|
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
view_info.format = format.format;
|
|
|
|
|
|
|
|
view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
|
|
|
|
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
view_info.subresourceRange.baseMipLevel = 0;
|
|
|
|
view_info.subresourceRange.levelCount = 1;
|
|
|
|
view_info.subresourceRange.baseArrayLayer = 0;
|
|
|
|
view_info.subresourceRange.layerCount = 1;
|
|
|
|
|
|
|
|
VkResult result = vkCreateImageView(device, &view_info, 0, &image_views[i]);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
free(image_views);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return image_views;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkFramebuffer* create_swapchain_framebuffers(VkDevice device, uint32_t image_count, VkImageView* image_views, VkRenderPass render_pass, VkExtent2D extent) {
|
|
|
|
VkFramebuffer* framebuffers = malloc(sizeof(VkFramebuffer)*image_count);
|
|
|
|
if(framebuffers == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < image_count; i++) {
|
|
|
|
VkImageView attachments[] = {
|
|
|
|
image_views[i],
|
|
|
|
};
|
|
|
|
|
|
|
|
VkFramebufferCreateInfo framebuffer_info = {};
|
|
|
|
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
|
|
framebuffer_info.renderPass = render_pass;
|
|
|
|
framebuffer_info.attachmentCount = 1;
|
|
|
|
framebuffer_info.pAttachments = attachments;
|
|
|
|
framebuffer_info.width = extent.width;
|
|
|
|
framebuffer_info.height = extent.height;
|
|
|
|
framebuffer_info.layers = 1;
|
|
|
|
|
|
|
|
VkResult result = vkCreateFramebuffer(device, &framebuffer_info, 0, &framebuffers[i]);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
free(framebuffers);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return framebuffers;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkShaderModule create_shader_module(VkDevice device, const char * code, uint32_t code_size) {
|
|
|
|
VkShaderModuleCreateInfo shader_info = {};
|
|
|
|
shader_info.sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
|
|
|
|
shader_info.codeSize = code_size;
|
|
|
|
shader_info.pCode = (uint32_t*)code;
|
|
|
|
|
|
|
|
VkShaderModule shader;
|
|
|
|
VkResult result;
|
|
|
|
result = vkCreateShaderModule(device, &shader_info, 0, &shader);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return shader;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkShaderModule load_shader_file(uint32_t buffer_size, const char* path, VkDevice device) {
|
|
|
|
FILE* file;
|
|
|
|
file = fopen(path, "r");
|
|
|
|
if(file == 0) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
char * buffer = malloc(buffer_size);
|
|
|
|
if(buffer == 0) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t read = fread(buffer, 1, buffer_size, file);
|
|
|
|
|
|
|
|
VkShaderModule shader = create_shader_module(device, buffer, read);
|
|
|
|
free(buffer);
|
|
|
|
return shader;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkRenderPass create_render_pass(VkDevice device, VkSurfaceFormatKHR format) {
|
|
|
|
VkAttachmentDescription color_attachment;
|
|
|
|
color_attachment.format = format.format;
|
|
|
|
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
|
|
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
|
|
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
|
|
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
|
|
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
|
|
|
|
|
|
VkAttachmentReference color_attachment_ref = {};
|
|
|
|
color_attachment_ref.attachment = 0;
|
|
|
|
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
|
|
|
|
VkSubpassDescription subpass = {};
|
|
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
|
|
subpass.colorAttachmentCount = 1;
|
|
|
|
subpass.pColorAttachments = &color_attachment_ref;
|
|
|
|
|
|
|
|
VkSubpassDependency dependency = {};
|
|
|
|
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
|
|
|
dependency.dstSubpass = 0;
|
|
|
|
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
dependency.srcAccessMask = 0;
|
|
|
|
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
|
|
|
|
|
|
VkRenderPassCreateInfo render_info = {};
|
|
|
|
render_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
|
|
render_info.attachmentCount = 1;
|
|
|
|
render_info.pAttachments = &color_attachment;
|
|
|
|
render_info.subpassCount = 1;
|
|
|
|
render_info.pSubpasses = &subpass;
|
|
|
|
render_info.dependencyCount = 1;
|
|
|
|
render_info.pDependencies = &dependency;
|
|
|
|
|
|
|
|
VkRenderPass render_pass;
|
|
|
|
VkResult result = vkCreateRenderPass(device, &render_info, 0, &render_pass);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return render_pass;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkPipelineLayout create_pipeline_layout(VkDevice device, uint32_t set_count, VkDescriptorSetLayout* sets, uint32_t pcr_count, VkPushConstantRange* pcrs) {
|
|
|
|
VkPipelineLayout layout;
|
|
|
|
VkPipelineLayoutCreateInfo layout_info = {};
|
|
|
|
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
|
|
layout_info.setLayoutCount = set_count;
|
|
|
|
layout_info.pSetLayouts = sets;
|
|
|
|
layout_info.pushConstantRangeCount = pcr_count;
|
|
|
|
layout_info.pPushConstantRanges = pcrs;
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
result = vkCreatePipelineLayout(device, &layout_info, 0, &layout);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return layout;
|
|
|
|
}
|
|
|
|
|
2024-01-08 09:44:47 -07:00
|
|
|
uint32_t find_memory_type(VkPhysicalDevice physical_device, uint32_t type_filter, VkMemoryPropertyFlags properties) {
|
|
|
|
VkPhysicalDeviceMemoryProperties memory_properties;
|
|
|
|
vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) {
|
|
|
|
if ((type_filter & (1 << i)) && (memory_properties.memoryTypes[i].propertyFlags & properties) == properties) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0xFFFFFFFF;
|
|
|
|
}
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
AllocatedBuffer allocate_buffer(VkPhysicalDevice physical_device, VkDevice device, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties) {
|
|
|
|
AllocatedBuffer ret = {};
|
|
|
|
ret.memory = VK_NULL_HANDLE;
|
|
|
|
ret.buffer = VK_NULL_HANDLE;
|
|
|
|
|
2024-01-08 09:44:47 -07:00
|
|
|
|
|
|
|
VkBufferCreateInfo buffer_info = {};
|
|
|
|
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
|
|
buffer_info.size = size;
|
2024-01-08 11:59:58 -07:00
|
|
|
buffer_info.usage = usage;
|
2024-01-08 09:44:47 -07:00
|
|
|
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
VkResult result = vkCreateBuffer(device, &buffer_info, 0, &ret.buffer);
|
2024-01-08 09:44:47 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
2024-01-08 11:59:58 -07:00
|
|
|
ret.buffer = VK_NULL_HANDLE;
|
|
|
|
ret.memory = VK_NULL_HANDLE;
|
|
|
|
return ret;
|
2024-01-08 09:44:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
VkMemoryRequirements memory_requirements;
|
2024-01-08 11:59:58 -07:00
|
|
|
vkGetBufferMemoryRequirements(device, ret.buffer, &memory_requirements);
|
2024-01-08 09:44:47 -07:00
|
|
|
|
|
|
|
VkMemoryAllocateInfo alloc_info = {};
|
|
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
|
|
alloc_info.allocationSize = memory_requirements.size;
|
2024-01-08 11:59:58 -07:00
|
|
|
alloc_info.memoryTypeIndex = find_memory_type(physical_device, memory_requirements.memoryTypeBits, properties);
|
|
|
|
|
|
|
|
result = vkAllocateMemory(device, &alloc_info, 0, &ret.memory);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
vkDestroyBuffer(device, ret.buffer, 0);
|
|
|
|
ret.buffer = VK_NULL_HANDLE;
|
|
|
|
ret.memory = VK_NULL_HANDLE;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = vkBindBufferMemory(device, ret.buffer, ret.memory, 0);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
vkDestroyBuffer(device, ret.buffer, 0);
|
|
|
|
ret.buffer = VK_NULL_HANDLE;
|
|
|
|
ret.memory = VK_NULL_HANDLE;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void deallocate_buffer(VkDevice device, AllocatedBuffer buffer) {
|
|
|
|
vkDestroyBuffer(device, buffer.buffer, 0);
|
|
|
|
vkFreeMemory(device, buffer.memory, 0);
|
|
|
|
};
|
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
AllocatedBuffer* allocate_buffers(VkPhysicalDevice physical_device, VkDevice device, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, uint32_t count) {
|
|
|
|
AllocatedBuffer* buffers = malloc(sizeof(AllocatedBuffer)*count);
|
|
|
|
if(buffers == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < count; i++) {
|
|
|
|
buffers[i] = allocate_buffer(physical_device, device, size, usage, properties);
|
|
|
|
if(buffers[i].memory == VK_NULL_HANDLE) {
|
|
|
|
for(uint32_t j = 0; j < i; j++) {
|
|
|
|
deallocate_buffer(device, buffers[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(buffers);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffers;
|
|
|
|
}
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
VkResult command_copy_buffers(VkDevice device, VkCommandPool transfer_pool, VkQueue transfer_queue, VkBuffer source, VkBuffer dest, VkDeviceSize size) {
|
|
|
|
VkCommandBufferAllocateInfo command_info = {};
|
|
|
|
command_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
|
|
command_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
|
|
command_info.commandPool = transfer_pool;
|
|
|
|
command_info.commandBufferCount = 1;
|
|
|
|
|
|
|
|
VkCommandBuffer command_buffer;
|
|
|
|
VkResult result = vkAllocateCommandBuffers(device, &command_info, &command_buffer);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkCommandBufferBeginInfo begin_info = {};
|
|
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
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 result;
|
|
|
|
}
|
2024-01-08 09:44:47 -07:00
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
VkBufferCopy copy_region = {};
|
|
|
|
copy_region.srcOffset = 0;
|
|
|
|
copy_region.dstOffset = 0;
|
|
|
|
copy_region.size = size;
|
|
|
|
|
|
|
|
vkCmdCopyBuffer(command_buffer, source, dest, 1, ©_region);
|
|
|
|
result = vkEndCommandBuffer(command_buffer);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkSubmitInfo submit_info = {};
|
|
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
|
|
submit_info.commandBufferCount = 1;
|
|
|
|
submit_info.pCommandBuffers = &command_buffer;
|
|
|
|
|
|
|
|
result = vkQueueSubmit(transfer_queue, 1, &submit_info, 0);
|
2024-01-08 09:44:47 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
2024-01-08 11:59:58 -07:00
|
|
|
vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer);
|
|
|
|
return result;
|
2024-01-08 09:44:47 -07:00
|
|
|
}
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
result = vkQueueWaitIdle(transfer_queue);
|
2024-01-08 09:44:47 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
2024-01-08 11:59:58 -07:00
|
|
|
vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer);
|
|
|
|
|
|
|
|
return VK_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
AllocatedBuffer create_populated_buffer(VkPhysicalDevice physical_device, VkDevice device, void* data, VkDeviceSize size, VkCommandPool transfer_pool, VkQueue transfer_queue, VkBufferUsageFlags usage) {
|
|
|
|
AllocatedBuffer staging_buffer = {};
|
|
|
|
AllocatedBuffer vertex_buffer = {};
|
|
|
|
staging_buffer = allocate_buffer(physical_device, device, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
|
|
|
if(staging_buffer.memory == VK_NULL_HANDLE) {
|
|
|
|
return vertex_buffer;
|
2024-01-08 09:44:47 -07:00
|
|
|
}
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
|
2024-01-08 09:44:47 -07:00
|
|
|
void* buffer_data;
|
2024-01-08 11:59:58 -07:00
|
|
|
VkResult result = vkMapMemory(device, staging_buffer.memory, 0, size, 0, &buffer_data);
|
2024-01-08 09:44:47 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
2024-01-08 11:59:58 -07:00
|
|
|
deallocate_buffer(device, staging_buffer);
|
|
|
|
return vertex_buffer;
|
2024-01-08 09:44:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(buffer_data, data, size);
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
vkUnmapMemory(device, staging_buffer.memory);
|
|
|
|
|
|
|
|
vertex_buffer = allocate_buffer(physical_device, device, size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | usage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
|
|
|
if(vertex_buffer.memory == VK_NULL_HANDLE) {
|
|
|
|
deallocate_buffer(device, staging_buffer);
|
|
|
|
return vertex_buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = command_copy_buffers(device, transfer_pool, transfer_queue, staging_buffer.buffer, vertex_buffer.buffer, size);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
deallocate_buffer(device, staging_buffer);
|
|
|
|
deallocate_buffer(device, vertex_buffer);
|
|
|
|
vertex_buffer.buffer = VK_NULL_HANDLE;
|
|
|
|
vertex_buffer.memory = VK_NULL_HANDLE;
|
|
|
|
return vertex_buffer;
|
|
|
|
}
|
2024-01-08 09:44:47 -07:00
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
return vertex_buffer;
|
2024-01-08 09:44:47 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
VkPipeline create_graphics_pipeline(VkDevice device, VkExtent2D extent, VkPipelineLayout layout, VkRenderPass render_pass) {
|
2024-01-08 09:44:47 -07:00
|
|
|
VkShaderModule vert_shader = load_shader_file(2048, "shader_src/basic.vert.spv", device);
|
|
|
|
VkShaderModule frag_shader = load_shader_file(2048, "shader_src/basic.frag.spv", device);
|
2024-01-07 22:27:53 -07:00
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo shader_stages[2] = {};
|
|
|
|
shader_stages[0].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
|
|
|
|
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
|
|
shader_stages[0].module = vert_shader;
|
|
|
|
shader_stages[0].pName = "main";
|
|
|
|
|
|
|
|
shader_stages[1].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
|
|
|
|
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
|
|
shader_stages[1].module = frag_shader;
|
|
|
|
shader_stages[1].pName = "main";
|
|
|
|
|
|
|
|
VkDynamicState dynamic_states[] = {
|
|
|
|
VK_DYNAMIC_STATE_VIEWPORT,
|
|
|
|
VK_DYNAMIC_STATE_SCISSOR,
|
|
|
|
};
|
|
|
|
uint32_t dynamic_state_count = sizeof(dynamic_states)/sizeof(VkDynamicState);
|
|
|
|
|
|
|
|
VkPipelineDynamicStateCreateInfo dynamic_info = {};
|
|
|
|
dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
|
|
|
dynamic_info.dynamicStateCount = dynamic_state_count;
|
|
|
|
dynamic_info.pDynamicStates = dynamic_states;
|
|
|
|
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
|
|
|
|
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
2024-01-08 09:44:47 -07:00
|
|
|
vertex_input_info.vertexBindingDescriptionCount = 1;
|
|
|
|
vertex_input_info.pVertexBindingDescriptions = vertex_binding_descriptions();
|
|
|
|
vertex_input_info.vertexAttributeDescriptionCount = 2;
|
|
|
|
vertex_input_info.pVertexAttributeDescriptions = vertex_attribute_descriptions();
|
2024-01-07 22:27:53 -07:00
|
|
|
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo input_assemvly_info = {};
|
|
|
|
input_assemvly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
|
|
|
input_assemvly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
|
|
input_assemvly_info.primitiveRestartEnable = VK_FALSE;
|
|
|
|
|
|
|
|
VkViewport viewport = {};
|
|
|
|
viewport.x = 0.0f;
|
|
|
|
viewport.y = 0.0f;
|
|
|
|
viewport.width = (float)(extent.width);
|
|
|
|
viewport.height = (float)(extent.height);
|
|
|
|
viewport.minDepth = 0.0f;
|
|
|
|
viewport.maxDepth = 1.0f;
|
|
|
|
|
|
|
|
VkRect2D scissor = {};
|
|
|
|
VkOffset2D scissor_offset = {.x = 0, .y = 0};
|
|
|
|
scissor.offset = scissor_offset;
|
|
|
|
scissor.extent = extent;
|
|
|
|
|
|
|
|
VkPipelineViewportStateCreateInfo viewport_state = {};
|
|
|
|
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
|
|
|
viewport_state.viewportCount = 1;
|
|
|
|
viewport_state.pViewports = &viewport;
|
|
|
|
viewport_state.scissorCount = 1;
|
|
|
|
viewport_state.pScissors = &scissor;
|
|
|
|
|
|
|
|
VkPipelineRasterizationStateCreateInfo raster_info = {};
|
|
|
|
raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
|
|
|
raster_info.depthClampEnable = VK_FALSE;
|
|
|
|
raster_info.rasterizerDiscardEnable = VK_FALSE;
|
|
|
|
raster_info.polygonMode = VK_POLYGON_MODE_FILL;
|
|
|
|
raster_info.lineWidth = 1.0f;
|
|
|
|
raster_info.cullMode = VK_CULL_MODE_BACK_BIT;
|
|
|
|
raster_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
|
|
|
raster_info.depthBiasEnable = VK_FALSE;
|
|
|
|
raster_info.depthBiasConstantFactor = 0.0f;
|
|
|
|
raster_info.depthBiasClamp = 0.0f;
|
|
|
|
raster_info.depthBiasSlopeFactor = 0.0f;
|
|
|
|
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisample_info = {};
|
|
|
|
multisample_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
|
|
|
multisample_info.sampleShadingEnable = VK_FALSE;
|
|
|
|
multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
multisample_info.minSampleShading = 1.0f;
|
|
|
|
multisample_info.pSampleMask = 0;
|
|
|
|
multisample_info.alphaToCoverageEnable = VK_FALSE;
|
|
|
|
multisample_info.alphaToOneEnable = VK_FALSE;
|
|
|
|
|
|
|
|
VkPipelineColorBlendAttachmentState color_blend_attachment = {};
|
|
|
|
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
|
|
|
color_blend_attachment.blendEnable = VK_TRUE;
|
|
|
|
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
|
|
|
color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
|
|
color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
|
|
|
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
|
|
color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
|
|
|
color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
|
|
|
|
|
|
|
VkPipelineColorBlendStateCreateInfo color_blend_info = {};
|
|
|
|
color_blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
|
|
|
color_blend_info.logicOpEnable = VK_FALSE;
|
|
|
|
color_blend_info.logicOp = VK_LOGIC_OP_COPY;
|
|
|
|
color_blend_info.attachmentCount = 1;
|
|
|
|
color_blend_info.pAttachments = &color_blend_attachment;
|
|
|
|
color_blend_info.blendConstants[0] = 0.0f;
|
|
|
|
color_blend_info.blendConstants[1] = 0.0f;
|
|
|
|
color_blend_info.blendConstants[2] = 0.0f;
|
|
|
|
color_blend_info.blendConstants[3] = 0.0f;
|
|
|
|
|
|
|
|
VkGraphicsPipelineCreateInfo pipeline_info = {};
|
|
|
|
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
|
|
|
pipeline_info.stageCount = 2;
|
|
|
|
pipeline_info.pStages = shader_stages;
|
|
|
|
pipeline_info.pVertexInputState = &vertex_input_info;
|
|
|
|
pipeline_info.pInputAssemblyState = &input_assemvly_info;
|
|
|
|
pipeline_info.pViewportState = &viewport_state;
|
|
|
|
pipeline_info.pRasterizationState = &raster_info;
|
|
|
|
pipeline_info.pDepthStencilState = 0;
|
|
|
|
pipeline_info.pColorBlendState = &color_blend_info;
|
|
|
|
pipeline_info.pDynamicState = &dynamic_info;
|
|
|
|
pipeline_info.layout = layout;
|
|
|
|
pipeline_info.renderPass = render_pass;
|
|
|
|
pipeline_info.subpass = 0;
|
|
|
|
|
|
|
|
pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
|
|
|
|
pipeline_info.basePipelineIndex = -1;
|
|
|
|
|
|
|
|
VkPipeline pipeline;
|
|
|
|
VkResult result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_info, 0, &pipeline);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pipeline;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkCommandPool create_command_pool(VkDevice device, uint32_t queue_family) {
|
|
|
|
VkCommandPoolCreateInfo pool_info = {};
|
|
|
|
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
|
|
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
|
|
pool_info.queueFamilyIndex = queue_family;
|
|
|
|
|
|
|
|
VkCommandPool command_pool;
|
|
|
|
VkResult result = vkCreateCommandPool(device, &pool_info, 0, &command_pool);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return command_pool;
|
|
|
|
}
|
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
VkResult record_command_buffer_triangle(VkCommandBuffer command_buffer, uint32_t image_index, VkRenderPass render_pass, VkFramebuffer* framebuffers, VkExtent2D extent, VkPipeline graphics_pipeline, VkPipelineLayout pipeline_layout, VkDescriptorSet descriptor_set, VkBuffer vertex_buffer, VkBuffer index_buffer, uint32_t num_vertices) {
|
2024-01-07 22:27:53 -07:00
|
|
|
VkCommandBufferBeginInfo begin_info = {};
|
|
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
begin_info.flags = 0;
|
|
|
|
begin_info.pInheritanceInfo = 0;
|
|
|
|
|
|
|
|
VkResult result = vkBeginCommandBuffer(command_buffer, &begin_info);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkRenderPassBeginInfo render_pass_info = {};
|
|
|
|
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
|
|
render_pass_info.renderPass = render_pass;
|
|
|
|
render_pass_info.framebuffer = framebuffers[image_index];
|
|
|
|
VkOffset2D render_offset = {.x = 0, .y = 0};
|
|
|
|
render_pass_info.renderArea.offset = render_offset;
|
|
|
|
render_pass_info.renderArea.extent = extent;
|
|
|
|
VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
|
|
|
render_pass_info.clearValueCount = 1;
|
|
|
|
render_pass_info.pClearValues = &clear_color;
|
|
|
|
|
|
|
|
vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
|
|
|
|
|
2024-01-08 09:44:47 -07:00
|
|
|
VkBuffer vertex_buffers[] = {vertex_buffer};
|
|
|
|
VkDeviceSize offsets[] = {0};
|
|
|
|
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, offsets);
|
2024-01-08 11:59:58 -07:00
|
|
|
vkCmdBindIndexBuffer(command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16);
|
2024-01-08 09:44:47 -07:00
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
VkViewport viewport = {};
|
|
|
|
viewport.x = 0.0f;
|
|
|
|
viewport.y = 0.0f;
|
|
|
|
viewport.width = (float)(extent.width);
|
|
|
|
viewport.height = (float)(extent.height);
|
|
|
|
viewport.minDepth = 0.0f;
|
|
|
|
viewport.maxDepth = 1.0f;
|
|
|
|
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
|
|
|
|
|
|
|
|
VkRect2D scissor = {};
|
|
|
|
VkOffset2D scissor_offset = {.x = 0.0f, .y = 0.0f};
|
|
|
|
scissor.offset = scissor_offset;
|
|
|
|
scissor.extent = extent;
|
|
|
|
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, 0);
|
2024-01-08 11:59:58 -07:00
|
|
|
vkCmdDrawIndexed(command_buffer, num_vertices, 1, 0, 0, 0);
|
2024-01-07 22:27:53 -07:00
|
|
|
vkCmdEndRenderPass(command_buffer);
|
|
|
|
|
|
|
|
return vkEndCommandBuffer(command_buffer);
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
VkCommandBuffer* create_command_buffers(VkDevice device, VkCommandPool command_pool, uint32_t image_count) {
|
2024-01-07 22:27:53 -07:00
|
|
|
VkCommandBufferAllocateInfo alloc_info = {};
|
|
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
|
|
alloc_info.commandPool = command_pool;
|
|
|
|
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
2024-01-07 22:55:36 -07:00
|
|
|
alloc_info.commandBufferCount = image_count;
|
|
|
|
|
|
|
|
VkCommandBuffer* command_buffers = malloc(sizeof(VkCommandBuffer)*image_count);
|
|
|
|
if(command_buffers == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
2024-01-07 22:27:53 -07:00
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
VkResult result = vkAllocateCommandBuffers(device, &alloc_info, command_buffers);
|
2024-01-07 22:27:53 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
return command_buffers;
|
2024-01-07 22:27:53 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
VkSemaphore* create_semaphores(VkDevice device, VkSemaphoreCreateFlags flags, uint32_t count) {
|
|
|
|
VkSemaphore* semaphores = malloc(sizeof(VkSemaphore)*count);
|
|
|
|
if(semaphores == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
VkSemaphoreCreateInfo semaphore_info = {};
|
|
|
|
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
|
|
semaphore_info.flags = flags;
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
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;
|
|
|
|
}
|
2024-01-07 22:27:53 -07:00
|
|
|
}
|
2024-01-07 22:55:36 -07:00
|
|
|
return semaphores;
|
2024-01-07 22:27:53 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
VkFence* create_fences(VkDevice device, VkFenceCreateFlags flags, uint32_t count) {
|
|
|
|
VkFence* fences = malloc(sizeof(VkFence)*count);
|
|
|
|
if(fences == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
VkFenceCreateInfo fence_info = {};
|
|
|
|
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
|
|
fence_info.flags = flags;
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
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;
|
|
|
|
}
|
2024-01-07 22:27:53 -07:00
|
|
|
}
|
2024-01-07 22:55:36 -07:00
|
|
|
return fences;
|
2024-01-07 22:27:53 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
VulkanContext* init_vulkan(GLFWwindow* window, uint32_t max_frames_in_flight) {
|
2024-01-07 16:34:09 -07:00
|
|
|
VulkanContext* context = (VulkanContext*)malloc(sizeof(VulkanContext));
|
|
|
|
|
2024-01-07 03:04:12 -07:00
|
|
|
VkInstance instance = create_instance();
|
2024-01-07 16:34:09 -07:00
|
|
|
if(instance == VK_NULL_HANDLE) {
|
|
|
|
fprintf(stderr, "failed to initialize vulkan instance\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->instance = instance;
|
2024-01-07 03:04:12 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
QueueIndices queue_indices = get_queue_indices(context->physical_device, context->surface);
|
|
|
|
if(check_queue_indices(queue_indices) == false) {
|
2024-01-07 16:34:09 -07:00
|
|
|
fprintf(stderr, "failed to get vulkan queue indices\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
2024-01-08 11:59:58 -07:00
|
|
|
context->queue_indices = queue_indices;
|
2024-01-07 16:34:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
} 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);
|
2024-01-08 11:59:58 -07:00
|
|
|
vkGetDeviceQueue(device, context->queue_indices.transfer_family, context->queue_indices.transfer_index, &context->queues.transfer);
|
2024-01-07 16:34:09 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
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);
|
|
|
|
|
|
|
|
VkSwapchainKHR swapchain = create_swapchain(context->device, context->swapchain_format, context->swapchain_present_mode, context->swapchain_extent, context->surface, context->swapchain_details.capabilities, context->queue_indices, VK_NULL_HANDLE);
|
2024-01-07 16:34:09 -07:00
|
|
|
if(swapchain == VK_NULL_HANDLE) {
|
2024-01-07 22:27:53 -07:00
|
|
|
fprintf(stderr, "failed to create vulkan swapchain\n");
|
2024-01-07 16:34:09 -07:00
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->swapchain = swapchain;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
SwapchainImages swapchain_images = get_swapchain_images(context->device, context->swapchain);
|
|
|
|
if(swapchain_images.count == 0) {
|
|
|
|
fprintf(stderr, "failed to get vulkan swapchain images\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->swapchain_image_count = swapchain_images.count;
|
|
|
|
context->swapchain_images = swapchain_images.images;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkImageView* image_views = create_image_views(context->device, context->swapchain_image_count, context->swapchain_images, context->swapchain_format);
|
|
|
|
if(image_views == 0) {
|
|
|
|
fprintf(stderr, "failed to create vulkan image views\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->swapchain_image_views = image_views;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkRenderPass render_pass = create_render_pass(context->device, context->swapchain_format);
|
|
|
|
if(render_pass == VK_NULL_HANDLE) {
|
|
|
|
fprintf(stderr, "failed to create vulkan render pass\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->render_pass = render_pass;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkFramebuffer* framebuffers = create_swapchain_framebuffers(context->device, context->swapchain_image_count, context->swapchain_image_views, context->render_pass, context->swapchain_extent);
|
|
|
|
if(framebuffers == 0) {
|
|
|
|
fprintf(stderr, "failed to create vulkan framebuffers\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->swapchain_framebuffers = framebuffers;
|
|
|
|
}
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
VkCommandPool graphics_command_pool = create_command_pool(context->device, context->queue_indices.graphics_family);
|
|
|
|
if(graphics_command_pool == VK_NULL_HANDLE) {
|
2024-01-07 22:55:36 -07:00
|
|
|
fprintf(stderr, "failed to create vulkan graphics command pool");
|
2024-01-07 22:27:53 -07:00
|
|
|
return 0;
|
|
|
|
} else {
|
2024-01-08 11:59:58 -07:00
|
|
|
context->graphics_command_pool = graphics_command_pool;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkCommandPool transfer_command_pool = create_command_pool(context->device, context->queue_indices.transfer_family);
|
|
|
|
if(transfer_command_pool == VK_NULL_HANDLE) {
|
|
|
|
fprintf(stderr, "failed to create vulkan transfer command pool");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->transfer_command_pool = transfer_command_pool;
|
2024-01-07 22:27:53 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
context->max_frames_in_flight = max_frames_in_flight;
|
|
|
|
|
|
|
|
VkCommandBuffer* swapchain_command_buffers = create_command_buffers(context->device, context->graphics_command_pool, max_frames_in_flight);
|
|
|
|
if(swapchain_command_buffers == VK_NULL_HANDLE) {
|
|
|
|
fprintf(stderr, "failed to create vulkan swapchain command buffer\n");
|
2024-01-07 22:27:53 -07:00
|
|
|
return 0;
|
|
|
|
} else {
|
2024-01-07 22:55:36 -07:00
|
|
|
context->swapchain_command_buffers = swapchain_command_buffers;
|
2024-01-07 22:27:53 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
VkSemaphore* ia_semaphores = create_semaphores(context->device, 0, max_frames_in_flight);
|
|
|
|
if(ia_semaphores == 0) {
|
|
|
|
fprintf(stderr, "failed to create vulkan image available semaphores\n");
|
2024-01-07 22:27:53 -07:00
|
|
|
return 0;
|
|
|
|
} else {
|
2024-01-07 22:55:36 -07:00
|
|
|
context->image_available_semaphores = ia_semaphores;
|
2024-01-07 22:27:53 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
VkSemaphore* rf_semaphores = create_semaphores(context->device, 0, max_frames_in_flight);
|
|
|
|
if(rf_semaphores == 0) {
|
|
|
|
fprintf(stderr, "failed to create vulkan render finished semaphores\n");
|
2024-01-07 22:27:53 -07:00
|
|
|
return 0;
|
|
|
|
} else {
|
2024-01-07 22:55:36 -07:00
|
|
|
context->render_finished_semaphores = rf_semaphores;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkFence* if_fences = create_fences(context->device, VK_FENCE_CREATE_SIGNALED_BIT, max_frames_in_flight);
|
|
|
|
if(if_fences == 0) {
|
|
|
|
fprintf(stderr, "failed to create vulkan in flight fence\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->in_flight_fences = if_fences;
|
2024-01-07 22:27:53 -07:00
|
|
|
}
|
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
AllocatedBuffer* uniform_buffers = allocate_buffers(context->physical_device, context->device, sizeof(struct ShaderUBO), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, max_frames_in_flight);
|
|
|
|
if(uniform_buffers == 0) {
|
|
|
|
fprintf(stderr, "failed to create vulkan uniform buffers\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->uniform_buffers = uniform_buffers;
|
|
|
|
context->uniform_buffer_ptrs = malloc(sizeof(void*)*max_frames_in_flight);
|
|
|
|
if(context->uniform_buffer_ptrs == 0) {
|
|
|
|
fprintf(stderr, "failed to allocate cpu pointers for uniform buffers\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
for(uint32_t i = 0; i < max_frames_in_flight; i++) {
|
|
|
|
VkResult result = vkMapMemory(context->device, context->uniform_buffers[i].memory, 0, sizeof(struct ShaderUBO), 0, &context->uniform_buffer_ptrs[i]);
|
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
fprintf(stderr, "failed to map cpu pointer for uniform buffer\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VkDescriptorPool descriptor_pool = create_descriptor_pool(context->device, max_frames_in_flight);
|
|
|
|
if(descriptor_pool == VK_NULL_HANDLE) {
|
|
|
|
fprintf(stderr, "failed to create vulkan descriptor pool\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->descriptor_pool = descriptor_pool;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkDescriptorSetLayout triangle_descriptor_set = create_descriptor_set_layout(device);
|
|
|
|
if(triangle_descriptor_set == VK_NULL_HANDLE) {
|
|
|
|
fprintf(stderr, "failed to create vulkan descriptor set layout\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->triangle_descriptor_set = triangle_descriptor_set;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkDescriptorSet* descriptor_sets = create_descriptor_sets(context->device, context->triangle_descriptor_set, context->descriptor_pool, context->uniform_buffers, max_frames_in_flight);
|
|
|
|
if(descriptor_sets == 0) {
|
|
|
|
fprintf(stderr, "failed to create vulkan descriptor sets\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->descriptor_sets = descriptor_sets;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkPipelineLayout triangle_pipeline_layout = create_pipeline_layout(device, 1, &context->triangle_descriptor_set, 0, 0);
|
2024-01-07 22:27:53 -07:00
|
|
|
if(triangle_pipeline_layout == VK_NULL_HANDLE) {
|
|
|
|
fprintf(stderr, "failed to create vulkan pipeline layout\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->triangle_pipeline_layout = triangle_pipeline_layout;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkPipeline triangle_pipeline = create_graphics_pipeline(context->device, context->swapchain_extent, context->triangle_pipeline_layout, context->render_pass);
|
|
|
|
if(triangle_pipeline == VK_NULL_HANDLE) {
|
|
|
|
fprintf(stderr, "failed to create vulkan graphics pipeline\n");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
context->triangle_pipeline = triangle_pipeline;
|
|
|
|
}
|
|
|
|
|
2024-01-08 11:59:58 -07:00
|
|
|
AllocatedBuffer triangle_vertex_buffer = create_populated_buffer(context->physical_device, context->device, (void*)vertices, sizeof(vertices), context->transfer_command_pool, context->queues.transfer, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
|
|
|
|
if(triangle_vertex_buffer.memory == VK_NULL_HANDLE) {
|
|
|
|
fprintf(stderr, "failed to allocate vulkan buffer for triangle buffer\n");
|
|
|
|
} else {
|
|
|
|
context->triangle_vertex_buffer = triangle_vertex_buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
AllocatedBuffer triangle_index_buffer = create_populated_buffer(context->physical_device, context->device, (void*)indices, sizeof(indices), context->transfer_command_pool, context->queues.transfer, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
|
|
|
|
if(triangle_index_buffer.memory == VK_NULL_HANDLE) {
|
2024-01-08 09:44:47 -07:00
|
|
|
fprintf(stderr, "failed to allocate vulkan buffer for triangle buffer\n");
|
|
|
|
} else {
|
2024-01-08 11:59:58 -07:00
|
|
|
context->triangle_index_buffer = triangle_index_buffer;
|
2024-01-08 09:44:47 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
return context;
|
2024-01-07 03:04:12 -07:00
|
|
|
}
|
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
VkResult update_ubo(void** buffers, uint32_t frame_index) {
|
|
|
|
struct ShaderUBO ubo = {};
|
|
|
|
glm_mat4_identity(ubo.proj);
|
|
|
|
glm_mat4_identity(ubo.view);
|
|
|
|
glm_mat4_identity(ubo.model);
|
|
|
|
|
|
|
|
memcpy(buffers[frame_index], (void*)&ubo, sizeof(ubo));
|
|
|
|
|
|
|
|
return VK_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
VkResult draw_frame(VulkanContext* context) {
|
2024-01-08 12:42:59 -07:00
|
|
|
update_ubo(context->uniform_buffer_ptrs, context->current_frame);
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
VkResult result;
|
2024-01-07 22:55:36 -07:00
|
|
|
result = vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
|
2024-01-07 22:27:53 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
result = vkResetFences(context->device, 1, &context->in_flight_fences[context->current_frame]);
|
2024-01-07 22:27:53 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t image_index;
|
2024-01-07 22:55:36 -07:00
|
|
|
result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphores[context->current_frame], VK_NULL_HANDLE, &image_index);
|
2024-01-07 22:27:53 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
result = vkResetCommandBuffer(context->swapchain_command_buffers[context->current_frame], 0);
|
2024-01-07 22:27:53 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-01-08 12:42:59 -07:00
|
|
|
result = record_command_buffer_triangle(context->swapchain_command_buffers[context->current_frame], image_index, context->render_pass, context->swapchain_framebuffers, context->swapchain_extent, context->triangle_pipeline, context->triangle_pipeline_layout, context->descriptor_sets[context->current_frame], context->triangle_vertex_buffer.buffer, context->triangle_index_buffer.buffer, 6);
|
2024-01-07 22:27:53 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkSubmitInfo submit_info = {};
|
|
|
|
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
|
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
|
|
submit_info.waitSemaphoreCount = 1;
|
2024-01-07 22:55:36 -07:00
|
|
|
submit_info.pWaitSemaphores = &context->image_available_semaphores[context->current_frame];
|
2024-01-07 22:27:53 -07:00
|
|
|
submit_info.pWaitDstStageMask = wait_stages;
|
|
|
|
submit_info.commandBufferCount = 1;
|
2024-01-07 22:55:36 -07:00
|
|
|
submit_info.pCommandBuffers = &context->swapchain_command_buffers[context->current_frame];
|
2024-01-07 22:27:53 -07:00
|
|
|
submit_info.signalSemaphoreCount = 1;
|
2024-01-07 22:55:36 -07:00
|
|
|
submit_info.pSignalSemaphores = &context->render_finished_semaphores[context->current_frame];
|
2024-01-07 22:27:53 -07:00
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
result = vkQueueSubmit(context->queues.graphics, 1, &submit_info, context->in_flight_fences[context->current_frame]);
|
2024-01-07 22:27:53 -07:00
|
|
|
if(result != VK_SUCCESS) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkPresentInfoKHR present_info = {};
|
|
|
|
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
|
|
|
present_info.waitSemaphoreCount = 1;
|
2024-01-07 22:55:36 -07:00
|
|
|
present_info.pWaitSemaphores = &context->render_finished_semaphores[context->current_frame];
|
2024-01-07 22:27:53 -07:00
|
|
|
present_info.swapchainCount = 1;
|
|
|
|
present_info.pSwapchains = &context->swapchain;
|
|
|
|
present_info.pImageIndices = &image_index;
|
|
|
|
present_info.pResults = 0;
|
|
|
|
|
|
|
|
return vkQueuePresentKHR(context->queues.present, &present_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
void main_loop(GLFWwindow* window, VulkanContext* context) {
|
2024-01-07 22:55:36 -07:00
|
|
|
context->current_frame = 0;
|
2024-01-07 03:04:12 -07:00
|
|
|
while(!glfwWindowShouldClose(window)) {
|
|
|
|
glfwPollEvents();
|
2024-01-07 22:27:53 -07:00
|
|
|
draw_frame(context);
|
2024-01-07 22:55:36 -07:00
|
|
|
context->current_frame += 1;
|
|
|
|
if(context->current_frame >= context->max_frames_in_flight) {
|
|
|
|
context->current_frame = 0;
|
|
|
|
}
|
2024-01-07 03:04:12 -07:00
|
|
|
}
|
2024-01-07 22:55:36 -07:00
|
|
|
|
|
|
|
vkDeviceWaitIdle(context->device);
|
2024-01-07 03:04:12 -07:00
|
|
|
}
|
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
void cleanup(GLFWwindow* window, VulkanContext* context) {
|
|
|
|
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);
|
|
|
|
}
|
2024-01-07 03:04:12 -07:00
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
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();
|
|
|
|
}
|
2024-01-07 03:04:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
GLFWwindow* window = init_window(800, 600);
|
|
|
|
if(window == 0) {
|
|
|
|
fprintf(stderr, "failed to initialize glfw window\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:55:36 -07:00
|
|
|
VulkanContext* context = init_vulkan(window, 2);
|
2024-01-07 16:34:09 -07:00
|
|
|
if (context == 0) {
|
|
|
|
fprintf(stderr, "failed to initialize vulkan context\n");
|
2024-01-07 03:04:12 -07:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2024-01-07 22:27:53 -07:00
|
|
|
main_loop(window, context);
|
2024-01-07 03:04:12 -07:00
|
|
|
|
2024-01-07 16:34:09 -07:00
|
|
|
cleanup(window, context);
|
2024-01-07 01:01:57 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|