956 lines
35 KiB
C
956 lines
35 KiB
C
|
#include "vulkan.h"
|
||
|
#include "kitty.h"
|
||
|
#include "log.h"
|
||
|
#include "matrix.h"
|
||
|
#include "types.h"
|
||
|
|
||
|
#include "image.h"
|
||
|
|
||
|
#include <stdbool.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <vulkan/vulkan.h>
|
||
|
#include <vulkan/vulkan_core.h>
|
||
|
|
||
|
#define VERTEX_ATTRIBUTE_COUNT 2
|
||
|
|
||
|
#define QUAD_VERTEX_COUNT 3
|
||
|
struct Vertex quad_vertices[QUAD_VERTEX_COUNT] = {
|
||
|
(struct Vertex){(struct Vec2){.x = 0.0f, .y = -0.5f},
|
||
|
(struct Vec3){1.0f, 0.0f, 1.0f}},
|
||
|
(struct Vertex){(struct Vec2){.x = 0.5f, .y = 0.5f},
|
||
|
(struct Vec3){1.0f, 0.0f, 0.2f}},
|
||
|
(struct Vertex){(struct Vec2){.x = -0.5f, .y = 0.5f},
|
||
|
(struct Vec3){0.2f, 0.0f, 1.0f}},
|
||
|
};
|
||
|
|
||
|
struct UBO {
|
||
|
struct mat3x3 model;
|
||
|
struct mat3x3 view;
|
||
|
struct mat3x3 proj;
|
||
|
};
|
||
|
|
||
|
VkVertexInputBindingDescription get_vertex_binding_description() {
|
||
|
VkVertexInputBindingDescription description = {0};
|
||
|
description.binding = 0;
|
||
|
description.stride = sizeof(struct Vertex);
|
||
|
description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||
|
return description;
|
||
|
}
|
||
|
|
||
|
// return value owned by caller
|
||
|
VkVertexInputAttributeDescription *get_vertex_attribute_description() {
|
||
|
VkVertexInputAttributeDescription *description = malloc(
|
||
|
sizeof(VkVertexInputAttributeDescription) * VERTEX_ATTRIBUTE_COUNT);
|
||
|
description[0].binding = 0;
|
||
|
description[0].location = 0;
|
||
|
description[0].format = VK_FORMAT_R32G32_SFLOAT;
|
||
|
description[0].offset = offsetof(struct Vertex, pos);
|
||
|
|
||
|
description[1].binding = 0;
|
||
|
description[1].location = 1;
|
||
|
description[1].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||
|
description[1].offset = offsetof(struct Vertex, col);
|
||
|
return description;
|
||
|
}
|
||
|
|
||
|
static const char *const extensions[] = {
|
||
|
"VK_EXT_debug_utils", "VK_KHR_surface", "VK_KHR_wayland_surface",
|
||
|
/* "VK_KHR_xcb_surface", */
|
||
|
};
|
||
|
static const char *const layers[] = {"VK_LAYER_KHRONOS_validation"};
|
||
|
static const char *const device_extensions[] = {"VK_KHR_swapchain"};
|
||
|
|
||
|
static VkBool32
|
||
|
handle_vulkan_error(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||
|
VkDebugUtilsMessageTypeFlagsEXT type,
|
||
|
const VkDebugUtilsMessengerCallbackDataEXT *callbackData,
|
||
|
void *userData) {
|
||
|
bool backtrace = false;
|
||
|
printf("Vulkan ");
|
||
|
switch (type) {
|
||
|
case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
|
||
|
printf("general ");
|
||
|
break;
|
||
|
case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT:
|
||
|
printf("validation ");
|
||
|
break;
|
||
|
case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT:
|
||
|
printf("performance ");
|
||
|
break;
|
||
|
}
|
||
|
switch (severity) {
|
||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
|
||
|
printf("(verbose): ");
|
||
|
break;
|
||
|
default:
|
||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
|
||
|
printf("(info): ");
|
||
|
break;
|
||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
|
||
|
printf("(warning): ");
|
||
|
break;
|
||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
|
||
|
printf("(error): ");
|
||
|
backtrace = true;
|
||
|
break;
|
||
|
}
|
||
|
printf("%s\n", callbackData->pMessage);
|
||
|
if (backtrace) {
|
||
|
print_backtrace();
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void create_swapchain(struct Vk *state) {
|
||
|
VkSurfaceCapabilitiesKHR capabilities;
|
||
|
CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
||
|
state->phys_device, state->vulkan_surface, &capabilities));
|
||
|
uint32_t format_count;
|
||
|
CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(
|
||
|
state->phys_device, state->vulkan_surface, &format_count, NULL));
|
||
|
VkSurfaceFormatKHR formats[format_count];
|
||
|
CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(
|
||
|
state->phys_device, state->vulkan_surface, &format_count, formats));
|
||
|
|
||
|
VkSurfaceFormatKHR chosen_format = formats[0];
|
||
|
for (uint32_t i = 0; i < format_count; i++) {
|
||
|
if (formats[i].format == VK_FORMAT_R8G8B8A8_SRGB) {
|
||
|
chosen_format = formats[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
state->format = chosen_format.format;
|
||
|
state->image_count = capabilities.minImageCount + 1;
|
||
|
if (capabilities.maxImageCount > 0 &&
|
||
|
capabilities.maxImageCount < state->image_count) {
|
||
|
state->image_count = capabilities.maxImageCount;
|
||
|
}
|
||
|
|
||
|
VkSwapchainCreateInfoKHR swapchain_info = {0};
|
||
|
swapchain_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||
|
swapchain_info.surface = state->vulkan_surface;
|
||
|
swapchain_info.minImageCount = state->image_count;
|
||
|
swapchain_info.imageFormat = chosen_format.format;
|
||
|
swapchain_info.imageColorSpace = chosen_format.colorSpace;
|
||
|
swapchain_info.imageExtent.width = state->width;
|
||
|
swapchain_info.imageExtent.height = state->heigh;
|
||
|
swapchain_info.imageArrayLayers = 1;
|
||
|
swapchain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||
|
if (state->queue_family_index_present != state->queue_family_index_graphics) {
|
||
|
swapchain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||
|
swapchain_info.queueFamilyIndexCount = 2;
|
||
|
uint32_t indices[] = {state->queue_family_index_graphics,
|
||
|
state->queue_family_index_present};
|
||
|
swapchain_info.pQueueFamilyIndices = indices;
|
||
|
} else {
|
||
|
swapchain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||
|
}
|
||
|
swapchain_info.preTransform = capabilities.currentTransform;
|
||
|
swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||
|
swapchain_info.presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||
|
swapchain_info.clipped = VK_TRUE;
|
||
|
swapchain_info.oldSwapchain = VK_NULL_HANDLE;
|
||
|
CHECK_VK_RESULT(vkCreateSwapchainKHR(state->device, &swapchain_info, NULL,
|
||
|
&state->swapchain));
|
||
|
|
||
|
CHECK_VK_RESULT(vkGetSwapchainImagesKHR(state->device, state->swapchain,
|
||
|
&state->image_count, NULL));
|
||
|
|
||
|
VkImage images[state->image_count];
|
||
|
CHECK_VK_RESULT(vkGetSwapchainImagesKHR(state->device, state->swapchain,
|
||
|
&state->image_count, images));
|
||
|
state->elements = malloc(state->image_count * sizeof(struct SwapchainElm));
|
||
|
|
||
|
for (int i = 0; i < state->image_count; i++) {
|
||
|
state->elements[i].image = images[i];
|
||
|
|
||
|
VkImageViewCreateInfo image_view_info = {0};
|
||
|
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||
|
image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||
|
image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||
|
image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||
|
image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||
|
image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||
|
image_view_info.subresourceRange.baseMipLevel = 0;
|
||
|
image_view_info.subresourceRange.levelCount = 1;
|
||
|
image_view_info.subresourceRange.baseArrayLayer = 0;
|
||
|
image_view_info.subresourceRange.layerCount = 1;
|
||
|
image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
|
image_view_info.image = state->elements[i].image;
|
||
|
image_view_info.format = state->format;
|
||
|
CHECK_VK_RESULT(vkCreateImageView(state->device, &image_view_info, NULL,
|
||
|
&state->elements[i].image_view));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VkCommandBuffer begin_single_time_commands(struct Vk *state) {
|
||
|
VkCommandBufferAllocateInfo alloc_info = {0};
|
||
|
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||
|
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||
|
alloc_info.commandPool = state->command_pool;
|
||
|
alloc_info.commandBufferCount = 1;
|
||
|
|
||
|
VkCommandBuffer command_buffer;
|
||
|
vkAllocateCommandBuffers(state->device, &alloc_info, &command_buffer);
|
||
|
|
||
|
VkCommandBufferBeginInfo begin_info = {0};
|
||
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||
|
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||
|
|
||
|
vkBeginCommandBuffer(command_buffer, &begin_info);
|
||
|
|
||
|
return command_buffer;
|
||
|
}
|
||
|
|
||
|
void end_single_time_commands(struct Vk *state,
|
||
|
VkCommandBuffer command_buffer) {
|
||
|
vkEndCommandBuffer(command_buffer);
|
||
|
|
||
|
VkSubmitInfo submit_info = {0};
|
||
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||
|
submit_info.commandBufferCount = 1;
|
||
|
submit_info.pCommandBuffers = &command_buffer;
|
||
|
|
||
|
vkQueueSubmit(state->queue_graphics, 1, &submit_info, VK_NULL_HANDLE);
|
||
|
vkQueueWaitIdle(state->queue_graphics);
|
||
|
|
||
|
vkFreeCommandBuffers(state->device, state->command_pool, 1, &command_buffer);
|
||
|
}
|
||
|
|
||
|
void setup_framebuffers(struct Vk *state) {
|
||
|
for (int i = 0; i < state->image_count; i++) {
|
||
|
VkFramebufferCreateInfo framebuffer_info = {0};
|
||
|
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||
|
framebuffer_info.renderPass = state->render_pass;
|
||
|
framebuffer_info.attachmentCount = 1;
|
||
|
framebuffer_info.pAttachments = &state->elements[i].image_view;
|
||
|
framebuffer_info.width = state->width;
|
||
|
framebuffer_info.height = state->heigh;
|
||
|
framebuffer_info.layers = 1;
|
||
|
CHECK_VK_RESULT(vkCreateFramebuffer(state->device, &framebuffer_info, NULL,
|
||
|
&state->elements[i].framebuffer));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void destroy_swapchain(struct Vk *state) {
|
||
|
for (int i = 0; i < state->image_count; i++) {
|
||
|
vkDestroyFramebuffer(state->device, state->elements[i].framebuffer, NULL);
|
||
|
vkDestroyImageView(state->device, state->elements[i].image_view, NULL);
|
||
|
}
|
||
|
free(state->elements);
|
||
|
vkDestroySwapchainKHR(state->device, state->swapchain, NULL);
|
||
|
}
|
||
|
|
||
|
void recreate_swapchain(struct Vk *state) {
|
||
|
destroy_swapchain(state);
|
||
|
create_swapchain(state);
|
||
|
setup_framebuffers(state);
|
||
|
}
|
||
|
|
||
|
VkShaderModule create_shader_module(struct Vk *state, uint32_t *data,
|
||
|
int size) {
|
||
|
VkShaderModuleCreateInfo shader_module_info = {0};
|
||
|
shader_module_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||
|
shader_module_info.codeSize = size;
|
||
|
shader_module_info.pCode = data;
|
||
|
|
||
|
VkShaderModule shader_module;
|
||
|
CHECK_VK_RESULT(vkCreateShaderModule(state->device, &shader_module_info, NULL,
|
||
|
&shader_module));
|
||
|
return shader_module;
|
||
|
}
|
||
|
|
||
|
VkInstance create_instance() {
|
||
|
VkApplicationInfo app_info = {0};
|
||
|
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||
|
app_info.pApplicationName = "meooow";
|
||
|
app_info.applicationVersion = VK_MAKE_VERSION(0, 0, 1);
|
||
|
app_info.pEngineName = "Cat Engine";
|
||
|
app_info.engineVersion = VK_MAKE_VERSION(0, 0, 1);
|
||
|
app_info.apiVersion = VK_API_VERSION_1_3;
|
||
|
|
||
|
VkInstanceCreateInfo instance_info = {0};
|
||
|
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||
|
instance_info.pApplicationInfo = &app_info;
|
||
|
instance_info.enabledExtensionCount =
|
||
|
sizeof(extensions) / sizeof(const char *);
|
||
|
instance_info.ppEnabledExtensionNames = extensions;
|
||
|
|
||
|
size_t found_layers = 0;
|
||
|
|
||
|
uint32_t instance_layer_count;
|
||
|
CHECK_VK_RESULT(
|
||
|
vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL));
|
||
|
|
||
|
VkLayerProperties *instance_layer_properties =
|
||
|
malloc(instance_layer_count * sizeof(VkLayerProperties));
|
||
|
CHECK_VK_RESULT(vkEnumerateInstanceLayerProperties(
|
||
|
&instance_layer_count, instance_layer_properties));
|
||
|
for (uint32_t i = 0; i < instance_layer_count; i++) {
|
||
|
for (int j = 0; j < sizeof(layers) / sizeof(const char *); j++) {
|
||
|
if (strcmp(instance_layer_properties[i].layerName, layers[j]) == 0) {
|
||
|
found_layers++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
free(instance_layer_properties);
|
||
|
if (found_layers >= sizeof(layers) / sizeof(const char *)) {
|
||
|
instance_info.enabledLayerCount = sizeof(layers) / sizeof(const char *);
|
||
|
instance_info.ppEnabledLayerNames = layers;
|
||
|
}
|
||
|
|
||
|
VkInstance instance;
|
||
|
CHECK_VK_RESULT(vkCreateInstance(&instance_info, NULL, &instance));
|
||
|
return instance;
|
||
|
}
|
||
|
|
||
|
void setup_debug_messenger(struct Vk *state) {
|
||
|
VkDebugUtilsMessengerCreateInfoEXT debug_utils_info = {0};
|
||
|
debug_utils_info.sType =
|
||
|
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||
|
debug_utils_info.messageSeverity =
|
||
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||
|
debug_utils_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;
|
||
|
debug_utils_info.pfnUserCallback = handle_vulkan_error;
|
||
|
|
||
|
CHECK_VK_RESULT(
|
||
|
GET_EXTENSION_FUNCTION(state->instance, vkCreateDebugUtilsMessengerEXT)(
|
||
|
state->instance, &debug_utils_info, NULL, &state->debug_messenger));
|
||
|
}
|
||
|
|
||
|
VkPhysicalDevice create_physical_device(struct Vk *state) {
|
||
|
uint32_t phys_device_count;
|
||
|
vkEnumeratePhysicalDevices(state->instance, &phys_device_count, NULL);
|
||
|
VkPhysicalDevice phys_devices[phys_device_count];
|
||
|
vkEnumeratePhysicalDevices(state->instance, &phys_device_count, phys_devices);
|
||
|
|
||
|
VkPhysicalDevice device;
|
||
|
int best_score = 0;
|
||
|
for (int i = 0; i < phys_device_count; i++) {
|
||
|
VkPhysicalDeviceProperties properties;
|
||
|
vkGetPhysicalDeviceProperties(phys_devices[i], &properties);
|
||
|
|
||
|
int score;
|
||
|
switch (properties.deviceType) {
|
||
|
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
|
||
|
score = 1;
|
||
|
break;
|
||
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
||
|
score = 4;
|
||
|
break;
|
||
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
||
|
score = 5;
|
||
|
break;
|
||
|
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
||
|
score = 3;
|
||
|
break;
|
||
|
case VK_PHYSICAL_DEVICE_TYPE_CPU:
|
||
|
score = 2;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
if (score > best_score) {
|
||
|
best_score = score;
|
||
|
device = phys_devices[i];
|
||
|
}
|
||
|
}
|
||
|
return device;
|
||
|
}
|
||
|
|
||
|
void setup_device_and_queues(struct Vk *state) {
|
||
|
uint32_t queue_family_count;
|
||
|
vkGetPhysicalDeviceQueueFamilyProperties(state->phys_device,
|
||
|
&queue_family_count, NULL);
|
||
|
VkQueueFamilyProperties queue_families[queue_family_count];
|
||
|
vkGetPhysicalDeviceQueueFamilyProperties(state->phys_device,
|
||
|
&queue_family_count, queue_families);
|
||
|
for (int i = 0; i < queue_family_count; i++) {
|
||
|
VkBool32 present = 0;
|
||
|
CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceSupportKHR(
|
||
|
state->phys_device, i, state->vulkan_surface, &present));
|
||
|
|
||
|
if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||
|
state->queue_family_index_graphics = i;
|
||
|
}
|
||
|
if (present) {
|
||
|
state->queue_family_index_present = i;
|
||
|
}
|
||
|
}
|
||
|
uint32_t *indices;
|
||
|
int index_count;
|
||
|
if (state->queue_family_index_present != state->queue_family_index_graphics) {
|
||
|
index_count = 2;
|
||
|
indices = alloca(sizeof(uint32_t) * index_count);
|
||
|
indices[0] = state->queue_family_index_graphics;
|
||
|
indices[1] = state->queue_family_index_present;
|
||
|
} else {
|
||
|
index_count = 1;
|
||
|
indices = alloca(sizeof(uint32_t) * index_count);
|
||
|
indices[0] = state->queue_family_index_graphics;
|
||
|
}
|
||
|
VkDeviceQueueCreateInfo queue_infos[index_count];
|
||
|
memset(queue_infos, 0, sizeof(VkDeviceQueueCreateInfo) * index_count);
|
||
|
|
||
|
state->queue_priority = 1.0f;
|
||
|
for (int i = 0; i < index_count; i++) {
|
||
|
queue_infos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||
|
queue_infos[i].queueFamilyIndex = indices[i];
|
||
|
queue_infos[i].queueCount = 1;
|
||
|
queue_infos[i].pQueuePriorities = &state->queue_priority;
|
||
|
}
|
||
|
|
||
|
VkPhysicalDeviceFeatures device_features = {0};
|
||
|
|
||
|
VkDeviceCreateInfo device_info = {0};
|
||
|
device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||
|
device_info.queueCreateInfoCount = index_count;
|
||
|
device_info.pQueueCreateInfos = queue_infos;
|
||
|
device_info.enabledExtensionCount =
|
||
|
sizeof(device_extensions) / sizeof(const char *);
|
||
|
device_info.ppEnabledExtensionNames = device_extensions;
|
||
|
device_info.pEnabledFeatures = &device_features;
|
||
|
|
||
|
CHECK_VK_RESULT(
|
||
|
vkCreateDevice(state->phys_device, &device_info, NULL, &state->device));
|
||
|
vkGetDeviceQueue(state->device, state->queue_family_index_present, 0,
|
||
|
&state->queue_present);
|
||
|
vkGetDeviceQueue(state->device, state->queue_family_index_graphics, 0,
|
||
|
&state->queue_graphics);
|
||
|
}
|
||
|
|
||
|
VkRenderPass create_render_pass(struct Vk *state) {
|
||
|
VkAttachmentDescription color_attachment = {0};
|
||
|
color_attachment.format = state->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_DONT_CARE;
|
||
|
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||
|
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||
|
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||
|
|
||
|
VkAttachmentReference color_attachment_ref = {0};
|
||
|
color_attachment_ref.attachment = 0;
|
||
|
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||
|
|
||
|
VkSubpassDescription subpass = {0};
|
||
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||
|
subpass.colorAttachmentCount = 1;
|
||
|
subpass.pColorAttachments = &color_attachment_ref;
|
||
|
|
||
|
VkSubpassDependency dependency = {0};
|
||
|
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_pass_info = {0};
|
||
|
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||
|
render_pass_info.attachmentCount = 1;
|
||
|
render_pass_info.pAttachments = &color_attachment;
|
||
|
render_pass_info.subpassCount = 1;
|
||
|
render_pass_info.pSubpasses = &subpass;
|
||
|
render_pass_info.dependencyCount = 1;
|
||
|
render_pass_info.pDependencies = &dependency;
|
||
|
|
||
|
VkRenderPass render_pass;
|
||
|
CHECK_VK_RESULT(
|
||
|
vkCreateRenderPass(state->device, &render_pass_info, NULL, &render_pass));
|
||
|
return render_pass;
|
||
|
}
|
||
|
|
||
|
void setup_command_pool(struct Vk *state) {
|
||
|
VkCommandPoolCreateInfo command_pool_info = {0};
|
||
|
command_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||
|
command_pool_info.queueFamilyIndex = state->queue_family_index_graphics;
|
||
|
command_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||
|
CHECK_VK_RESULT(vkCreateCommandPool(state->device, &command_pool_info, NULL,
|
||
|
&state->command_pool));
|
||
|
}
|
||
|
|
||
|
void setup_command_buffers(struct Vk *state) {
|
||
|
VkCommandBufferAllocateInfo command_buffer_alloc_info = {0};
|
||
|
command_buffer_alloc_info.sType =
|
||
|
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||
|
command_buffer_alloc_info.commandPool = state->command_pool;
|
||
|
command_buffer_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||
|
command_buffer_alloc_info.commandBufferCount = 1;
|
||
|
command_buffer_alloc_info.commandBufferCount = MAX_FRAMES_IN_FLIGHT;
|
||
|
VkCommandBuffer buffers[MAX_FRAMES_IN_FLIGHT];
|
||
|
CHECK_VK_RESULT(vkAllocateCommandBuffers(
|
||
|
state->device, &command_buffer_alloc_info, buffers));
|
||
|
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||
|
state->flights[i].command_buffer = buffers[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void setup_sync_objects(struct Vk *state) {
|
||
|
VkSemaphoreCreateInfo semaphore_info = {0};
|
||
|
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||
|
|
||
|
VkFenceCreateInfo fence_info = {0};
|
||
|
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||
|
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||
|
|
||
|
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||
|
VkSemaphore ias, rfs;
|
||
|
VkFence iff;
|
||
|
CHECK_VK_RESULT(
|
||
|
vkCreateSemaphore(state->device, &semaphore_info, NULL, &ias));
|
||
|
CHECK_VK_RESULT(
|
||
|
vkCreateSemaphore(state->device, &semaphore_info, NULL, &rfs));
|
||
|
CHECK_VK_RESULT(vkCreateFence(state->device, &fence_info, NULL, &iff));
|
||
|
state->flights[i].image_available_semaphore = ias;
|
||
|
state->flights[i].render_finished_semaphore = rfs;
|
||
|
state->flights[i].in_flight_fence = iff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint32_t find_memory_type(struct Vk *state, uint32_t memory_type_bits,
|
||
|
uint32_t properties) {
|
||
|
VkPhysicalDeviceMemoryProperties phys_mem_props;
|
||
|
vkGetPhysicalDeviceMemoryProperties(state->phys_device, &phys_mem_props);
|
||
|
|
||
|
uint32_t memory_type_index = -1;
|
||
|
for (uint32_t i = 0; i < phys_mem_props.memoryTypeCount; i++) {
|
||
|
if (memory_type_bits & (1 << i) &&
|
||
|
(phys_mem_props.memoryTypes[i].propertyFlags & properties) ==
|
||
|
properties) {
|
||
|
memory_type_index = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (memory_type_index == -1) {
|
||
|
meow("failed to find memory type");
|
||
|
}
|
||
|
return memory_type_index;
|
||
|
}
|
||
|
|
||
|
void create_buffer(struct Vk *state, VkDeviceSize size,
|
||
|
VkBufferUsageFlags usage, VkMemoryPropertyFlags properties,
|
||
|
VkBuffer *buffer, VkDeviceMemory *buffer_memory) {
|
||
|
|
||
|
VkBufferCreateInfo buffer_info = {0};
|
||
|
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||
|
buffer_info.size = size;
|
||
|
buffer_info.usage = usage;
|
||
|
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||
|
|
||
|
CHECK_VK_RESULT(vkCreateBuffer(state->device, &buffer_info, NULL, buffer));
|
||
|
|
||
|
VkMemoryRequirements mem_reqs;
|
||
|
vkGetBufferMemoryRequirements(state->device, *buffer, &mem_reqs);
|
||
|
|
||
|
VkMemoryAllocateInfo alloc_info = {0};
|
||
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||
|
alloc_info.allocationSize = mem_reqs.size;
|
||
|
alloc_info.memoryTypeIndex =
|
||
|
find_memory_type(state, mem_reqs.memoryTypeBits, properties);
|
||
|
|
||
|
CHECK_VK_RESULT(
|
||
|
vkAllocateMemory(state->device, &alloc_info, NULL, buffer_memory));
|
||
|
vkBindBufferMemory(state->device, *buffer, *buffer_memory, 0);
|
||
|
}
|
||
|
|
||
|
void copy_buffer(struct Vk *state, VkBuffer src_buffer, VkBuffer dst_buffer,
|
||
|
VkDeviceSize size) {
|
||
|
VkCommandBuffer command_buffer = begin_single_time_commands(state);
|
||
|
|
||
|
VkBufferCopy copy_region = {
|
||
|
.srcOffset = 0,
|
||
|
.dstOffset = 0,
|
||
|
.size = size,
|
||
|
};
|
||
|
vkCmdCopyBuffer(command_buffer, src_buffer, dst_buffer, 1, ©_region);
|
||
|
|
||
|
end_single_time_commands(state, command_buffer);
|
||
|
}
|
||
|
|
||
|
void transition_image_layout(struct Vk *state, VkImage image, VkFormat format,
|
||
|
VkImageLayout old_layout,
|
||
|
VkImageLayout new_layout) {
|
||
|
VkCommandBuffer command_buffer = begin_single_time_commands(state);
|
||
|
|
||
|
VkImageMemoryBarrier barrier = {0};
|
||
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||
|
barrier.oldLayout = old_layout;
|
||
|
barrier.newLayout = new_layout;
|
||
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||
|
barrier.image = image;
|
||
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
|
barrier.subresourceRange.baseMipLevel = 0;
|
||
|
barrier.subresourceRange.levelCount = 1;
|
||
|
barrier.subresourceRange.baseArrayLayer = 0;
|
||
|
barrier.subresourceRange.layerCount = 1;
|
||
|
|
||
|
VkPipelineStageFlags source_stage;
|
||
|
VkPipelineStageFlags dest_stage;
|
||
|
|
||
|
if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED &&
|
||
|
new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
||
|
barrier.srcAccessMask = 0;
|
||
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||
|
|
||
|
source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||
|
dest_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||
|
} else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
|
||
|
new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
|
||
|
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||
|
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||
|
|
||
|
source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||
|
dest_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||
|
} else {
|
||
|
meow("invalid transition OwO rudeeee");
|
||
|
}
|
||
|
vkCmdPipelineBarrier(command_buffer, source_stage, dest_stage, 0, 0, NULL, 0,
|
||
|
NULL, 1, &barrier);
|
||
|
|
||
|
end_single_time_commands(state, command_buffer);
|
||
|
}
|
||
|
|
||
|
void copy_buffer_to_image(struct Vk *state, VkBuffer buffer, VkImage image,
|
||
|
struct IVec2 size) {
|
||
|
VkCommandBuffer command_buffer = begin_single_time_commands(state);
|
||
|
|
||
|
VkBufferImageCopy region = {0};
|
||
|
region.bufferOffset = 0;
|
||
|
region.bufferRowLength = 0;
|
||
|
region.bufferImageHeight = 0;
|
||
|
|
||
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
|
region.imageSubresource.mipLevel = 0;
|
||
|
region.imageSubresource.baseArrayLayer = 0;
|
||
|
region.imageSubresource.layerCount = 1;
|
||
|
|
||
|
region.imageOffset = (VkOffset3D){0, 0, 0};
|
||
|
region.imageExtent = (VkExtent3D){size.x, size.y, 1};
|
||
|
|
||
|
vkCmdCopyBufferToImage(command_buffer, buffer, image,
|
||
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||
|
|
||
|
end_single_time_commands(state, command_buffer);
|
||
|
}
|
||
|
|
||
|
struct ImageBuffer {
|
||
|
VkImage image;
|
||
|
VkDeviceMemory memory;
|
||
|
struct IVec2 dims;
|
||
|
VkDeviceSize size;
|
||
|
VkImageView view;
|
||
|
VkSampler sampler;
|
||
|
};
|
||
|
|
||
|
void create_image(struct Vk *state, struct IVec2 dims, VkFormat format,
|
||
|
VkImageTiling tiling, VkImageUsageFlags usage,
|
||
|
VkMemoryPropertyFlags properties, VkImage *image,
|
||
|
VkDeviceMemory *memory) {
|
||
|
VkImageCreateInfo image_info = {0};
|
||
|
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||
|
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||
|
image_info.extent.width = dims.x;
|
||
|
image_info.extent.height = dims.y;
|
||
|
image_info.extent.depth = 1;
|
||
|
image_info.mipLevels = 1;
|
||
|
image_info.arrayLayers = 1;
|
||
|
image_info.format = format;
|
||
|
image_info.tiling = tiling;
|
||
|
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||
|
image_info.usage = usage;
|
||
|
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||
|
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||
|
|
||
|
CHECK_VK_RESULT(vkCreateImage(state->device, &image_info, NULL, image));
|
||
|
|
||
|
VkMemoryRequirements mem_reqs;
|
||
|
vkGetImageMemoryRequirements(state->device, *image, &mem_reqs);
|
||
|
|
||
|
VkMemoryAllocateInfo alloc_info = {0};
|
||
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||
|
alloc_info.allocationSize = mem_reqs.size;
|
||
|
alloc_info.memoryTypeIndex =
|
||
|
find_memory_type(state, mem_reqs.memoryTypeBits, properties);
|
||
|
|
||
|
CHECK_VK_RESULT(vkAllocateMemory(state->device, &alloc_info, NULL, memory));
|
||
|
|
||
|
vkBindImageMemory(state->device, *image, *memory, 0);
|
||
|
}
|
||
|
|
||
|
struct ImageBuffer create_image_buffer(struct Vk *state) {
|
||
|
struct ImageBuffer buffer = {0};
|
||
|
|
||
|
uchar *image_data = load_image("image.png", &buffer.dims);
|
||
|
|
||
|
buffer.size = buffer.dims.x * buffer.dims.y * 4;
|
||
|
|
||
|
VkBuffer staging_buffer;
|
||
|
VkDeviceMemory staging_buffer_memory;
|
||
|
create_buffer(state, buffer.size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||
|
&staging_buffer, &staging_buffer_memory);
|
||
|
|
||
|
void *data;
|
||
|
vkMapMemory(state->device, staging_buffer_memory, 0, buffer.size, 0, &data);
|
||
|
memcpy(data, image_data, buffer.size);
|
||
|
vkUnmapMemory(state->device, staging_buffer_memory);
|
||
|
|
||
|
free(image_data);
|
||
|
|
||
|
create_image(
|
||
|
state, buffer.dims, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL,
|
||
|
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &buffer.image, &buffer.memory);
|
||
|
|
||
|
transition_image_layout(state, buffer.image, VK_FORMAT_R8G8B8A8_SRGB,
|
||
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
||
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||
|
copy_buffer_to_image(state, staging_buffer, buffer.image, buffer.dims);
|
||
|
transition_image_layout(state, buffer.image, VK_FORMAT_R8G8B8A8_SRGB,
|
||
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||
|
|
||
|
vkDestroyBuffer(state->device, staging_buffer, NULL);
|
||
|
vkFreeMemory(state->device, staging_buffer_memory, NULL);
|
||
|
|
||
|
VkImageViewCreateInfo view_info = {0};
|
||
|
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||
|
view_info.image = buffer.image;
|
||
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||
|
view_info.format = VK_FORMAT_R8G8B8A8_SRGB;
|
||
|
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;
|
||
|
CHECK_VK_RESULT(
|
||
|
vkCreateImageView(state->device, &view_info, NULL, &buffer.view));
|
||
|
|
||
|
VkSamplerCreateInfo sampler_info = {0};
|
||
|
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||
|
sampler_info.magFilter = VK_FILTER_NEAREST;
|
||
|
sampler_info.minFilter = VK_FILTER_NEAREST;
|
||
|
sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||
|
sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||
|
sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||
|
sampler_info.anisotropyEnable = VK_FALSE;
|
||
|
sampler_info.maxAnisotropy = 1.0f;
|
||
|
sampler_info.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
|
||
|
sampler_info.unnormalizedCoordinates = VK_FALSE;
|
||
|
sampler_info.compareEnable = VK_FALSE;
|
||
|
sampler_info.compareOp = VK_COMPARE_OP_ALWAYS;
|
||
|
sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||
|
sampler_info.mipLodBias = 0.0f;
|
||
|
sampler_info.minLod = 0.0;
|
||
|
sampler_info.maxLod = 0.0;
|
||
|
CHECK_VK_RESULT(
|
||
|
vkCreateSampler(state->device, &sampler_info, NULL, &buffer.sampler));
|
||
|
|
||
|
return buffer;
|
||
|
}
|
||
|
|
||
|
struct VertexBuffer create_vertex_buffer(struct Vk *state, int count,
|
||
|
struct Vertex *vertex_data) {
|
||
|
VkDeviceSize size = count * sizeof(struct Vertex);
|
||
|
struct VertexBuffer buffer = {0};
|
||
|
buffer.count = count;
|
||
|
|
||
|
VkBuffer staging_buffer;
|
||
|
VkDeviceMemory staging_buffer_memory;
|
||
|
|
||
|
create_buffer(state, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||
|
&staging_buffer, &staging_buffer_memory);
|
||
|
|
||
|
void *data;
|
||
|
vkMapMemory(state->device, staging_buffer_memory, 0, size, 0, &data);
|
||
|
memcpy(data, vertex_data, size);
|
||
|
vkUnmapMemory(state->device, staging_buffer_memory);
|
||
|
|
||
|
create_buffer(
|
||
|
state, size,
|
||
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &buffer.buffer, &buffer.memory);
|
||
|
|
||
|
copy_buffer(state, staging_buffer, buffer.buffer, size);
|
||
|
|
||
|
vkDestroyBuffer(state->device, staging_buffer, NULL);
|
||
|
vkFreeMemory(state->device, staging_buffer_memory, NULL);
|
||
|
|
||
|
return buffer;
|
||
|
}
|
||
|
|
||
|
struct Vk *init_vk(void *data, int width, int heigh,
|
||
|
VkSurfaceKHR (*surface)(void *, VkInstance)) {
|
||
|
struct Vk *state = malloc(sizeof(struct Vk));
|
||
|
memset(state, 0, sizeof(struct Vk));
|
||
|
memset(state->flights, 0, sizeof(state->flights));
|
||
|
state->width = width;
|
||
|
state->heigh = heigh;
|
||
|
|
||
|
state->instance = create_instance();
|
||
|
|
||
|
setup_debug_messenger(state);
|
||
|
|
||
|
state->vulkan_surface = surface(data, state->instance);
|
||
|
meow("got a surface at %p", state->vulkan_surface);
|
||
|
|
||
|
state->phys_device = create_physical_device(state);
|
||
|
setup_device_and_queues(state);
|
||
|
create_swapchain(state);
|
||
|
state->render_pass = create_render_pass(state);
|
||
|
setup_command_pool(state);
|
||
|
setup_framebuffers(state);
|
||
|
setup_command_buffers(state);
|
||
|
setup_sync_objects(state);
|
||
|
|
||
|
dyn_array_create_inplace(&state->kitties);
|
||
|
|
||
|
return state;
|
||
|
}
|
||
|
|
||
|
void uninit_vk(struct Vk *state) {
|
||
|
CHECK_VK_RESULT(vkDeviceWaitIdle(state->device));
|
||
|
|
||
|
dyn_array_destroy(&state->kitties);
|
||
|
|
||
|
destroy_swapchain(state);
|
||
|
|
||
|
vkDestroyRenderPass(state->device, state->render_pass, NULL);
|
||
|
|
||
|
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||
|
vkDestroySemaphore(state->device,
|
||
|
state->flights[i].render_finished_semaphore, NULL);
|
||
|
vkDestroySemaphore(state->device,
|
||
|
state->flights[i].image_available_semaphore, NULL);
|
||
|
vkDestroyFence(state->device, state->flights[i].in_flight_fence, NULL);
|
||
|
}
|
||
|
|
||
|
vkDestroyCommandPool(state->device, state->command_pool, NULL);
|
||
|
|
||
|
vkDestroyDevice(state->device, NULL);
|
||
|
|
||
|
GET_EXTENSION_FUNCTION(state->instance, vkDestroyDebugUtilsMessengerEXT)(
|
||
|
state->instance, state->debug_messenger, NULL);
|
||
|
|
||
|
vkDestroySurfaceKHR(state->instance, state->vulkan_surface, NULL);
|
||
|
vkDestroyInstance(state->instance, NULL);
|
||
|
|
||
|
memset(state, 0, sizeof(struct Vk));
|
||
|
free(state);
|
||
|
meow("unmade a vk, nini :3");
|
||
|
}
|
||
|
|
||
|
void vk_draw(struct Vk *state) {
|
||
|
CHECK_VK_RESULT(vkWaitForFences(
|
||
|
state->device, 1, &state->flights[state->current_frame].in_flight_fence,
|
||
|
VK_TRUE, UINT64_MAX));
|
||
|
CHECK_VK_RESULT(vkResetFences(
|
||
|
state->device, 1, &state->flights[state->current_frame].in_flight_fence));
|
||
|
|
||
|
uint32_t image_index;
|
||
|
VkResult result = vkAcquireNextImageKHR(
|
||
|
state->device, state->swapchain, UINT64_MAX,
|
||
|
state->flights[state->current_frame].image_available_semaphore, NULL,
|
||
|
&image_index);
|
||
|
|
||
|
struct SwapchainElm *element = &state->elements[image_index];
|
||
|
struct InFlightObjects flight = state->flights[state->current_frame];
|
||
|
|
||
|
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||
|
meow("recreating swapchain");
|
||
|
CHECK_VK_RESULT(vkDeviceWaitIdle(state->device));
|
||
|
recreate_swapchain(state);
|
||
|
return;
|
||
|
} else if (result < 0) {
|
||
|
CHECK_VK_RESULT(result);
|
||
|
}
|
||
|
|
||
|
vkResetCommandBuffer(flight.command_buffer, 0);
|
||
|
|
||
|
VkCommandBufferBeginInfo command_buffer_begin_info = {0};
|
||
|
command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||
|
CHECK_VK_RESULT(
|
||
|
vkBeginCommandBuffer(flight.command_buffer, &command_buffer_begin_info));
|
||
|
|
||
|
VkClearValue clear_value = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||
|
|
||
|
VkRenderPassBeginInfo begin_info = {0};
|
||
|
begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||
|
begin_info.renderPass = state->render_pass;
|
||
|
begin_info.framebuffer = element->framebuffer;
|
||
|
begin_info.renderArea.offset = (VkOffset2D){0, 0};
|
||
|
begin_info.renderArea.extent = (VkExtent2D){state->width, state->heigh};
|
||
|
begin_info.clearValueCount = 1;
|
||
|
begin_info.pClearValues = &clear_value;
|
||
|
|
||
|
vkCmdBeginRenderPass(flight.command_buffer, &begin_info,
|
||
|
VK_SUBPASS_CONTENTS_INLINE);
|
||
|
|
||
|
for (int i = 0; i < state->kitties.count; i++) {
|
||
|
kitty_draw(state, image_index, state->kitties.items[i]);
|
||
|
}
|
||
|
|
||
|
vkCmdEndRenderPass(flight.command_buffer);
|
||
|
|
||
|
CHECK_VK_RESULT(vkEndCommandBuffer(flight.command_buffer));
|
||
|
|
||
|
VkPipelineStageFlags wait_stage[] = {
|
||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
||
|
VkSemaphore wait_semaphore[] = {flight.image_available_semaphore};
|
||
|
VkSemaphore signal_semaphore[] = {flight.render_finished_semaphore};
|
||
|
VkSubmitInfo submit_info = {0};
|
||
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||
|
submit_info.waitSemaphoreCount = 1;
|
||
|
submit_info.pWaitSemaphores = wait_semaphore;
|
||
|
submit_info.pWaitDstStageMask = wait_stage;
|
||
|
submit_info.commandBufferCount = 1;
|
||
|
submit_info.pCommandBuffers = &flight.command_buffer;
|
||
|
submit_info.signalSemaphoreCount = 1;
|
||
|
submit_info.pSignalSemaphores = signal_semaphore;
|
||
|
CHECK_VK_RESULT(vkQueueSubmit(state->queue_graphics, 1, &submit_info,
|
||
|
flight.in_flight_fence));
|
||
|
|
||
|
VkSwapchainKHR swapchain[] = {state->swapchain};
|
||
|
VkPresentInfoKHR present_info = {0};
|
||
|
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||
|
present_info.waitSemaphoreCount = 1;
|
||
|
present_info.pWaitSemaphores = signal_semaphore;
|
||
|
present_info.swapchainCount = 1;
|
||
|
present_info.pSwapchains = swapchain;
|
||
|
present_info.pImageIndices = &image_index;
|
||
|
result = vkQueuePresentKHR(state->queue_present, &present_info);
|
||
|
|
||
|
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||
|
CHECK_VK_RESULT(vkDeviceWaitIdle(state->device));
|
||
|
recreate_swapchain(state);
|
||
|
meow("recreated swapchain");
|
||
|
} else if (result < 0) {
|
||
|
CHECK_VK_RESULT(result);
|
||
|
}
|
||
|
|
||
|
state->current_frame = (state->current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||
|
}
|
||
|
|
||
|
void vk_resize(struct Vk *state, int width, int heigh) {
|
||
|
state->width = width;
|
||
|
state->heigh = heigh;
|
||
|
|
||
|
CHECK_VK_RESULT(vkDeviceWaitIdle(state->device));
|
||
|
|
||
|
recreate_swapchain(state);
|
||
|
|
||
|
state->current_frame = 0;
|
||
|
}
|