#include "vulkan.h" #include "kitty.h" #include "log.h" #include "matrix.h" #include "types.h" #include "image.h" #include #include #include #include #include #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; }