pawengine/vulkan.c

956 lines
35 KiB
C
Raw Permalink Normal View History

2025-02-09 18:48:25 +01:00
#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, &copy_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, &region);
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;
}