diff options
author | kartofen <mladenovnasko0@gmail.com> | 2023-10-22 21:18:13 +0300 |
---|---|---|
committer | kartofen <mladenovnasko0@gmail.com> | 2023-10-22 21:18:13 +0300 |
commit | e9bd321c8bd6bdb6aa7305c6da8b33e8cac462f7 (patch) | |
tree | 14d1e8993b68520dcfedfd6939b84c1af1330376 /src/graphics.c | |
parent | 7e555eeadd1f295db7700b8bde884fcb92bb2b8d (diff) |
renamed device.c/.h to graphics
Diffstat (limited to 'src/graphics.c')
-rw-r--r-- | src/graphics.c | 1146 |
1 files changed, 1146 insertions, 0 deletions
diff --git a/src/graphics.c b/src/graphics.c new file mode 100644 index 0000000..8da9c43 --- /dev/null +++ b/src/graphics.c @@ -0,0 +1,1146 @@ +#include <string.h> +#include <errno.h> +#include <vulkan/vulkan.h> + +#include "graphics.h" +#include "common.h" + +// TODO: add more error checking +// TODO: add log output +// TODO: check for memory leaks + +#define SWAP_CHAIN_IMAGES 3 + +#define CLAMP(val, min, max) (((val) < (min)) ? (min) : ((val) > (max) ? (max) : (val))) + +#define ECHECK(f, ...) if(f(__VA_ARGS__) != 0) { err(#f ": failed"); goto exit; } +#define FCHECK(f, ...) if(f(__VA_ARGS__) != 0) { err(#f ": failed"); goto fail; } + +#define VCHECK(f, ...) \ + do { \ + VkResult res; \ + if((res = f(__VA_ARGS__)) != VK_SUCCESS) { \ + err(#f ": %s", str_VkResult(res)); \ + goto exit; } \ + } while(0) + +#ifdef DEBUG +char *validation_layers[] = { + "VK_LAYER_KHRONOS_validation" +}; +#endif + +char *device_extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME +}; + +struct queue_family_idx { + u32 graphics_family; + bool has_graphics_family; + + u32 present_family; + bool has_present_family; +}; + +struct swap_chain_support_details { + VkSurfaceCapabilitiesKHR capabilities; + + VkSurfaceFormatKHR *formats; + u32 nformats; + + VkPresentModeKHR *present_modes; + u32 npresent_modes; +}; + +// most function that return an int follow this scheme: +// 0 - success; 1 - error; (sometimes -1 - fatal error) + +// -- Major Functions --- +static int create_instance(graphics_t graphics, struct graphics_info *info); +static int create_surface(graphics_t graphics, struct graphics_info *info); +static int create_physical_device(graphics_t graphics, struct graphics_info *info); +static int create_logical_device(graphics_t graphics, struct graphics_info *info); +static int create_pipeline(graphics_t graphics, struct graphics_info *info); +static int create_swap_chain(graphics_t graphics, struct graphics_info *info); +static int create_command_pool(graphics_t graphics, struct graphics_info *info); +static int create_sync_objects(graphics_t graphics, struct graphics_info *info); + +static void destroy_swap_chain(graphics_t graphics); +static void destroy_pipeline(graphics_t graphics); +// static void destroy_command_pool(graphics_t graphics); +static void destroy_sync_objects(graphics_t graphics); + +// --- Helper Functions --- +static char *str_VkResult(VkResult result); + +static bool device_is_suitable(VkPhysicalDevice phy_device, VkSurfaceKHR surface); +static bool device_has_extension_support(VkPhysicalDevice phy_device); +static int device_queue_families(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct queue_family_idx *queue_family); + +#define PPLN graphics->pipeline +static int pipeline_load_shader_module(VkDevice device, char *path, VkShaderModule *module); + +#define SWCH graphics->swap_chain +static int swap_chain_support(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct swap_chain_support_details *details); +static void swap_chain_free_support_details(struct swap_chain_support_details *details); +static int swap_chain_choose_format(VkSurfaceFormatKHR* formats, u32 nformats, VkSurfaceFormatKHR *format); +static int swap_chain_choose_present_mode(VkPresentModeKHR *modes, u32 nmodes, VkPresentModeKHR *mode); +static int swap_chain_get_extent(VkSurfaceCapabilitiesKHR capabilities, VkExtent2D *extent); + +#define CMND graphics->command +static int command_buffer_record(graphics_t graphics, u32 image_index); + +#define SYNC graphics->sync + +F_LOAD_FILE_ALIGNED(u32) // from config.h + +// --- Debug Functions --- +#ifdef DEBUG +static bool instance_has_validation_layers(const char * const *layers, u32 nlayers); + +static int create_debug_messenger(graphics_t graphics, struct graphics_info* info); +static void debug_messenger_populate_info(VkDebugUtilsMessengerCreateInfoEXT *info); + +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_messenger_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); + +// Vulkan Wrappers +VkResult CreateDebugUtilsMessengerEXT( + VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pDebugMessenger); +void DestroyDebugUtilsMessengerEXT( + VkInstance instance, + VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks* pAllocator); +#endif + +#define CCHECK(f) FCHECK(f, graphics, info) + +graphics_t graphics_create(struct graphics_info *info) +{ + graphics_t graphics = xmalloc(sizeof(struct graphics)); + graphics->instance = VK_NULL_HANDLE; + graphics->logical_device = VK_NULL_HANDLE; + + + PPLN.pipeline = VK_NULL_HANDLE; + PPLN.layout = VK_NULL_HANDLE; + PPLN.render_pass = VK_NULL_HANDLE; + + SWCH.swap_chain = VK_NULL_HANDLE; + SWCH.nimages = 0; + + CMND.pool = VK_NULL_HANDLE; + + SYNC.semph_image_available = VK_NULL_HANDLE; + SYNC.semph_render_finished = VK_NULL_HANDLE; + SYNC.fence_inflight = VK_NULL_HANDLE; + + CCHECK(create_instance); + CCHECK(create_surface); +#ifdef DEBUG + CCHECK(create_debug_messenger); +#endif + CCHECK(create_physical_device); + CCHECK(create_logical_device); + CCHECK(create_pipeline); + CCHECK(create_swap_chain); + CCHECK(create_command_pool); + CCHECK(create_sync_objects); + + return graphics; +fail: + graphics_destroy(graphics); + return NULL; +} + +void graphics_destroy(graphics_t graphics) +{ + if(!graphics) return; + + vkDeviceWaitIdle(graphics->logical_device); + + destroy_sync_objects(graphics); + + vkDestroyCommandPool(graphics->logical_device, CMND.pool, NULL); + + destroy_swap_chain(graphics); + destroy_pipeline(graphics); + + vkDestroyDevice(graphics->logical_device, NULL); + + #ifdef DEBUG + DestroyDebugUtilsMessengerEXT(graphics->instance, graphics->debug_messenger, NULL); + #endif + + vkDestroySurfaceKHR(graphics->instance, graphics->surface, NULL); + vkDestroyInstance(graphics->instance, NULL); + + free(graphics); +} + +int graphics_draw_frame(graphics_t graphics) +{ + int ret = 1; + + // wait for the previous frame to finish + vkWaitForFences(graphics->logical_device, 1, &SYNC.fence_inflight, VK_TRUE, UINT64_MAX); + + // aquire the next image the form the swap chain + u32 image_index; + VkResult res = vkAcquireNextImageKHR(graphics->logical_device, SWCH.swap_chain, UINT64_MAX, SYNC.semph_image_available, VK_NULL_HANDLE, &image_index); + + // recreate the swap chain on resize + if(res == VK_ERROR_OUT_OF_DATE_KHR) { + destroy_swap_chain(graphics); + create_swap_chain(graphics, NULL); + + ret = 0; goto exit; + } + + vkResetFences(graphics->logical_device, 1, &SYNC.fence_inflight); + + // reset the command buffer + vkResetCommandBuffer(CMND.buffer, 0); + command_buffer_record(graphics, image_index); + + // prepare the queue submit info + VkSubmitInfo submit_info = {0}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore wait_semaphs[] = { SYNC.semph_image_available }; + VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submit_info.waitSemaphoreCount = ARR_SIZE(wait_semaphs); + submit_info.pWaitSemaphores = wait_semaphs; + submit_info.pWaitDstStageMask = wait_stages; + + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &CMND.buffer; + + VkSemaphore signal_semphs[] = { SYNC.semph_image_available }; + submit_info.signalSemaphoreCount = ARR_SIZE(signal_semphs); + submit_info.pSignalSemaphores = signal_semphs; + + // submit the graphics work + VCHECK(vkQueueSubmit, graphics->graphics_queue, 1, &submit_info, SYNC.fence_inflight); + + // prepare the graphics info + VkPresentInfoKHR present_info = {0}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + present_info.waitSemaphoreCount = ARR_SIZE(signal_semphs); + present_info.pWaitSemaphores = signal_semphs; + + VkSwapchainKHR swap_chains[] = { SWCH.swap_chain }; + present_info.swapchainCount = ARR_SIZE(swap_chains); + present_info.pSwapchains = swap_chains; + present_info.pImageIndices = &image_index; + present_info.pResults = NULL; // Optional + + + vkQueuePresentKHR(graphics->present_queue, &present_info); + + + ret = 0; +exit: + return ret; +} + +static int create_instance(graphics_t graphics, struct graphics_info *info) +{ + int ret = 1; + + VkApplicationInfo app_info = {0}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pEngineName = "Engine"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_API_VERSION_1_0; + + app_info.pApplicationName = info->name; + app_info.applicationVersion = info->version; + + VkInstanceCreateInfo create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + + create_info.enabledLayerCount = 0; + +#ifdef DEBUG + // add debug utils extensions for debug callback + char **extensions = xcalloc(info->ext_count+1, sizeof(*extensions)); + extensions[info->ext_count] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + + for(u32 i = 0; i < info->ext_count; i++) { + extensions[i] = (char *)info->extensions[i]; + } + + create_info.enabledExtensionCount = info->ext_count+1; + create_info.ppEnabledExtensionNames = (const char * const *)extensions; + + // validation layer support + u32 nlayers = ARR_SIZE(validation_layers); + + if(!instance_has_validation_layers((const char * const *)validation_layers, nlayers)) { + err("validation_layer_support: failed"); + goto exit; + } + + create_info.ppEnabledLayerNames = (const char * const *)validation_layers; + create_info.enabledLayerCount = nlayers; + + // add the debug messenger for instance creation and destruction + VkDebugUtilsMessengerCreateInfoEXT msg_info = {0}; + debug_messenger_populate_info(&msg_info); + + create_info.pNext = &msg_info; +#else + create_info.enabledExtensionCount = info->ext_count; + create_info.ppEnabledExtensionNames = (const char * const *)info->extensions; + + create_info.enabledLayerCount = 0; +#endif + + VCHECK(vkCreateInstance, &create_info, NULL, &graphics->instance); + + ret = 0; +exit: +#ifdef DEBUG + free(extensions); +#endif + return ret; +} + +static int create_surface(graphics_t graphics, struct graphics_info *info) +{ + if(info->surface_func(graphics->instance, &graphics->surface)) { + err("Couldn't create a VkSurfaceKHR"); + return 1; + } + + return 0; +} + +static int create_physical_device(graphics_t graphics, struct graphics_info *info) +{ + (void)info; + int ret = 1; + + graphics->physical_device = VK_NULL_HANDLE; + + u32 dev_count = 0; + vkEnumeratePhysicalDevices(graphics->instance, &dev_count, NULL); + + if(dev_count == 0) { + err("No physical devices could be found!"); + goto exit; + } + + VkPhysicalDevice *devices = xcalloc(dev_count, sizeof(*devices)); + vkEnumeratePhysicalDevices(graphics->instance, &dev_count, devices); + + for(u32 i = 0; i < dev_count; i++) { + if(device_is_suitable(devices[i], graphics->surface)) { + graphics->physical_device = devices[i]; + break; + } + } + + free(devices); + + if(graphics->physical_device == VK_NULL_HANDLE) { + err("No suitable physical device could be found"); + goto exit; + } + + ret = 0; +exit: + return ret; +} + +static int create_logical_device(graphics_t graphics, struct graphics_info *info) +{ + (void)info; + int ret = 1; + + // queue family data + struct queue_family_idx indices = {0}; + device_queue_families(graphics->physical_device, graphics->surface, &indices); + + // queue infos + u32 unique_queue_family_count = 0; + u32 queue_indices[] = { + indices.graphics_family, indices.present_family, + }; + + VkDeviceQueueCreateInfo queue_infos[ARR_SIZE(queue_indices)] = {0}; + + float queue_priority = 1.0f; + // basically get only the unique queue families + for(size_t i = 0; i < ARR_SIZE(queue_indices); i++) + { + bool unique = true; + for(size_t j = 0; j < i; j++) { + if(queue_indices[i] == queue_indices[j]) { + unique = false; + break; + } + } + + if(!unique) continue; + + queue_infos[unique_queue_family_count].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_infos[unique_queue_family_count].queueFamilyIndex = queue_indices[i]; + queue_infos[unique_queue_family_count].queueCount = 1; + queue_infos[unique_queue_family_count].pQueuePriorities = &queue_priority; + + unique_queue_family_count++; + } + + // device features + VkPhysicalDeviceFeatures device_features = {0}; + + // logical device create info + VkDeviceCreateInfo create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + + create_info.pQueueCreateInfos = queue_infos; + create_info.queueCreateInfoCount = unique_queue_family_count; + + create_info.pEnabledFeatures = &device_features; + + create_info.ppEnabledExtensionNames = (const char * const *)device_extensions; + create_info.enabledExtensionCount = ARR_SIZE(device_extensions); + + // validation layers can be set, but + // newer implementations will ignore them + // so i wont bother adding them + create_info.enabledLayerCount = 0; + + VCHECK(vkCreateDevice, graphics->physical_device, &create_info, NULL, &graphics->logical_device); + + vkGetDeviceQueue(graphics->logical_device, indices.graphics_family, 0, &graphics->graphics_queue); + vkGetDeviceQueue(graphics->logical_device, indices.present_family, 0, &graphics->present_queue); + + ret = 0; +exit: + return ret; +} + +static int create_swap_chain(graphics_t graphics, struct graphics_info *info) +{ + (void)info; + int ret = 1; + + struct swap_chain_support_details details; + swap_chain_support(graphics->physical_device, graphics->surface, &details); + + VkSurfaceFormatKHR surface_format; + VkPresentModeKHR present_mode; + VkExtent2D extent; + u32 image_count = SWAP_CHAIN_IMAGES; + + swap_chain_choose_format(details.formats, details.nformats, &surface_format); + swap_chain_choose_present_mode(details.present_modes, details.npresent_modes, &present_mode); + swap_chain_get_extent(details.capabilities, &extent); + + if(details.capabilities.maxImageCount > 0) { + image_count = CLAMP(image_count, details.capabilities.minImageCount, details.capabilities.maxImageCount); + } + + // start filling in the create info + VkSwapchainCreateInfoKHR create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + + create_info.surface = graphics->surface; + create_info.minImageCount = image_count; + create_info.imageFormat = surface_format.format; + create_info.imageColorSpace = surface_format.colorSpace; + create_info.imageExtent = extent; + create_info.imageArrayLayers = 1; + create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + struct queue_family_idx indices = {0}; + device_queue_families(graphics->physical_device, graphics->surface, &indices); + + u32 queue_indices[] = { indices.graphics_family, indices.present_family }; + + // set the sharing mode of the images + if(indices.graphics_family != indices.present_family) { + create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + create_info.queueFamilyIndexCount = ARR_SIZE(queue_indices); + create_info.pQueueFamilyIndices = queue_indices; + } else { + create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.queueFamilyIndexCount = 0; + create_info.pQueueFamilyIndices = NULL; + } + + create_info.preTransform = details.capabilities.currentTransform; + create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + + create_info.presentMode = present_mode; + create_info.clipped = VK_TRUE; + + create_info.oldSwapchain = NULL; + + VCHECK(vkCreateSwapchainKHR, graphics->logical_device, &create_info, NULL, &SWCH.swap_chain); + + // Get the Images + SWCH.image_format = surface_format.format; + SWCH.extent = extent; + + vkGetSwapchainImagesKHR(graphics->logical_device, SWCH.swap_chain, &SWCH.nimages, NULL); + + SWCH.images = xcalloc(SWCH.nimages, sizeof(*SWCH.images)); + SWCH.image_views = xcalloc(SWCH.nimages, sizeof(SWCH.image_views)); + SWCH.framebuffers = xcalloc(SWCH.nimages, sizeof(*SWCH.framebuffers)); + + vkGetSwapchainImagesKHR(graphics->logical_device, SWCH.swap_chain, &SWCH.nimages, SWCH.images); + + for(u32 i = 0; i < SWCH.nimages; i++) + { + VkImageViewCreateInfo create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + create_info.image = SWCH.images[i]; + + create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + create_info.format = surface_format.format; + + create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + create_info.subresourceRange.baseMipLevel = 0; + create_info.subresourceRange.levelCount = 1; + create_info.subresourceRange.baseArrayLayer = 0; + create_info.subresourceRange.layerCount = 1; + + VCHECK(vkCreateImageView, graphics->logical_device, &create_info, NULL, &SWCH.image_views[i]); + } + + for(u32 i = 0; i < SWCH.nimages; i++) + { + VkImageView attachments[] = { + SWCH.image_views[i] + }; + + VkFramebufferCreateInfo framebuffer_info = {0}; + framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_info.renderPass = PPLN.render_pass; + framebuffer_info.attachmentCount = 1; + framebuffer_info.pAttachments = attachments; + framebuffer_info.width = SWCH.extent.width; + framebuffer_info.height = SWCH.extent.height; + framebuffer_info.layers = 1; + + VCHECK(vkCreateFramebuffer, graphics->logical_device, &framebuffer_info, NULL, &SWCH.framebuffers[i]); + } + + ret = 0; +exit: + swap_chain_free_support_details(&details); + return ret; +} + +static void destroy_swap_chain(graphics_t graphics) +{ + for(u32 i = 0; i < SWCH.nimages; i++) { + vkDestroyImageView(graphics->logical_device, SWCH.image_views[i], NULL); + vkDestroyFramebuffer(graphics->logical_device, SWCH.framebuffers[i], NULL); + } + + if(SWCH.nimages != 0) { + free(SWCH.images); + free(SWCH.image_views); + free(SWCH.framebuffers); + } + + vkDestroySwapchainKHR(graphics->logical_device, SWCH.swap_chain, NULL); +} + +static int create_pipeline(graphics_t graphics, struct graphics_info *info) +{ + (void)info; + int ret = 1; + + // Shader Things + VkShaderModule vert_shader = VK_NULL_HANDLE; + VkShaderModule frag_shader = VK_NULL_HANDLE; + + pipeline_load_shader_module(graphics->logical_device, "shaders/shader1.vert.spv", &vert_shader); + pipeline_load_shader_module(graphics->logical_device, "shaders/shader1.frag.spv", &frag_shader); + + VkPipelineShaderStageCreateInfo vert_stage = {0}; + vert_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vert_stage.stage = VK_SHADER_STAGE_VERTEX_BIT; + vert_stage.module = vert_shader; + vert_stage.pName = "main"; + + VkPipelineShaderStageCreateInfo frag_stage = {0}; + frag_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + frag_stage.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + frag_stage.module = frag_shader; + frag_stage.pName = "main"; + + VkPipelineShaderStageCreateInfo shader_stages[] = { vert_stage, frag_stage }; + + VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + + // A Bunch of other things for the pipeline + VkPipelineDynamicStateCreateInfo dynamic_state = {0}; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = ARR_SIZE(dynamic_states); + dynamic_state.pDynamicStates = dynamic_states; + + // vertex things - hard coded (for now) + VkPipelineVertexInputStateCreateInfo vertex_input = {0}; + vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input.vertexBindingDescriptionCount = 0; + vertex_input.pVertexBindingDescriptions = NULL; + vertex_input.vertexAttributeDescriptionCount = 0; + vertex_input.pVertexAttributeDescriptions = NULL; + + VkPipelineInputAssemblyStateCreateInfo input_assembly = {0}; + input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + VkPipelineViewportStateCreateInfo viewport_state = {0}; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo rasterizer = {0}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; // Optional + rasterizer.depthBiasClamp = 0.0f; // Optional + rasterizer.depthBiasSlopeFactor = 0.0f; // Optional + + VkPipelineMultisampleStateCreateInfo multisampling = {0}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; // Optional + multisampling.pSampleMask = NULL; // Optional + multisampling.alphaToCoverageEnable = VK_FALSE; // Optional + multisampling.alphaToOneEnable = VK_FALSE; // Optional + + VkPipelineColorBlendAttachmentState color_blend_attachment = {0}; + color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + color_blend_attachment.blendEnable = VK_FALSE; + color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional + color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional + + VkPipelineColorBlendStateCreateInfo color_blend = {0}; + color_blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blend.logicOpEnable = VK_FALSE; + color_blend.logicOp = VK_LOGIC_OP_COPY; // Optional + color_blend.attachmentCount = 1; + color_blend.pAttachments = &color_blend_attachment; + color_blend.blendConstants[0] = 0.0f; // Optional + color_blend.blendConstants[1] = 0.0f; // Optional + color_blend.blendConstants[2] = 0.0f; // Optional + color_blend.blendConstants[3] = 0.0f; // Optional + + // Create Pipeline Layout + VkPipelineLayoutCreateInfo pipeline_layout_info = {0}; + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = 0; // Optional + pipeline_layout_info.pSetLayouts = NULL; // Optional + pipeline_layout_info.pushConstantRangeCount = 0; // Optional + pipeline_layout_info.pPushConstantRanges = NULL; // Optional + + VCHECK(vkCreatePipelineLayout, graphics->logical_device, &pipeline_layout_info, NULL, &PPLN.layout); + + // get the required format + // currently the only to do that + // TOOD: fix this + VkSurfaceFormatKHR surface_format; + struct swap_chain_support_details details; + swap_chain_support(graphics->physical_device, graphics->surface, &details); + swap_chain_choose_format(details.formats, details.nformats, &surface_format); + swap_chain_free_support_details(&details); + + // Create Render Pass + VkAttachmentDescription color_attachment = {0}; + color_attachment.format = surface_format.format; + color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_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.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + 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; + + VCHECK(vkCreateRenderPass, graphics->logical_device, &render_pass_info, NULL, &PPLN.render_pass); + + // Finally Create the Pipeline + VkGraphicsPipelineCreateInfo pipeline_info = {0}; + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.stageCount = 2; + pipeline_info.pStages = shader_stages; + + pipeline_info.pVertexInputState = &vertex_input; + pipeline_info.pInputAssemblyState = &input_assembly; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterizer; + pipeline_info.pMultisampleState = &multisampling; + pipeline_info.pDepthStencilState = NULL; + pipeline_info.pColorBlendState = &color_blend; + pipeline_info.pDynamicState = &dynamic_state; + + pipeline_info.layout = PPLN.layout; + pipeline_info.renderPass = PPLN.render_pass; + pipeline_info.subpass = 0; + + pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // Optional + pipeline_info.basePipelineIndex = -1; // Optional + + VCHECK(vkCreateGraphicsPipelines, graphics->logical_device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &PPLN.pipeline); + + ret = 0; +exit: + vkDestroyShaderModule(graphics->logical_device, frag_shader, NULL); + vkDestroyShaderModule(graphics->logical_device, vert_shader, NULL); + return ret; +} + +static void destroy_pipeline(graphics_t graphics) +{ + vkDestroyPipeline(graphics->logical_device, PPLN.pipeline, NULL); + vkDestroyPipelineLayout(graphics->logical_device, PPLN.layout, NULL); + vkDestroyRenderPass(graphics->logical_device, PPLN.render_pass, NULL); +} + +static int create_command_pool(graphics_t graphics, struct graphics_info *info) +{ + (void)info; + int ret = 1; + + struct queue_family_idx indices; + device_queue_families(graphics->physical_device, graphics->surface, &indices); + + VkCommandPoolCreateInfo pool_info = {0}; + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pool_info.queueFamilyIndex = indices.graphics_family; + + VCHECK(vkCreateCommandPool, graphics->logical_device, &pool_info, NULL, &CMND.pool); + + // allocate the command buffer(s) + VkCommandBufferAllocateInfo allocate_info = {0}; + allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocate_info.commandPool = CMND.pool; + allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocate_info.commandBufferCount = 1; + + VCHECK(vkAllocateCommandBuffers, graphics->logical_device, &allocate_info, &CMND.buffer); + + ret = 0; +exit: + return ret; +} + +static int create_sync_objects(graphics_t graphics, struct graphics_info *info) +{ + (void)info; + int ret = 1; + + VkSemaphoreCreateInfo semph_info = {0}; + semph_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; + + VCHECK(vkCreateSemaphore, graphics->logical_device, &semph_info, NULL, &SYNC.semph_image_available); + VCHECK(vkCreateSemaphore, graphics->logical_device, &semph_info, NULL, &SYNC.semph_render_finished); + VCHECK(vkCreateFence, graphics->logical_device, &fence_info, NULL, &SYNC.fence_inflight); + + ret = 0; +exit: + return ret; +} + +static void destroy_sync_objects(graphics_t graphics) +{ + vkDestroySemaphore(graphics->logical_device, SYNC.semph_image_available, NULL); + vkDestroySemaphore(graphics->logical_device, SYNC.semph_render_finished, NULL); + vkDestroyFence(graphics->logical_device, SYNC.fence_inflight, NULL); +} + +static int device_queue_families(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct queue_family_idx *queue_family) +{ + u32 count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(phy_device, &count, NULL); + + VkQueueFamilyProperties *properties = xcalloc(count, sizeof(*properties)); + vkGetPhysicalDeviceQueueFamilyProperties(phy_device, &count, properties); + + for(u32 i = 0; i < count; i++) { + if(properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) { + queue_family->graphics_family = i; + queue_family->has_graphics_family = true; + } + + VkBool32 present_support = false; + vkGetPhysicalDeviceSurfaceSupportKHR(phy_device, i, surface, &present_support); + + if(present_support) { + queue_family->present_family = i; + queue_family->has_present_family = true; + } + } + + free(properties); + return 0; +} + +static bool device_is_suitable(VkPhysicalDevice phy_device, VkSurfaceKHR surface) +{ + struct queue_family_idx indices; + struct swap_chain_support_details details; + + return + (device_queue_families(phy_device, surface, &indices), + indices.has_graphics_family && indices.has_present_family) + && + (device_has_extension_support(phy_device)) + && + (swap_chain_support(phy_device, surface, &details), + swap_chain_free_support_details(&details), + (details.nformats > 0) && (details.npresent_modes > 0)); +} + +static bool device_has_extension_support(VkPhysicalDevice phy_device) +{ + bool ret = false; + + u32 count = 0; + vkEnumerateDeviceExtensionProperties(phy_device, NULL, &count, NULL); + + VkExtensionProperties *properties = xcalloc(count, sizeof(*properties)); + vkEnumerateDeviceExtensionProperties(phy_device, NULL, &count, properties); + + for(size_t i = 0; i < ARR_SIZE(device_extensions); i++) + { + bool present = false; + for(u32 j = 0; j < count; j++) + if(strncmp(device_extensions[i], properties[j].extensionName, 256) == 0) { + present = true; + break; + } + + if(!present) { + goto exit; + } + } + + ret = true; +exit: + free(properties); + return ret; +} + +static int swap_chain_support(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct swap_chain_support_details *details) +{ + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phy_device, surface, &details->capabilities); + + vkGetPhysicalDeviceSurfaceFormatsKHR(phy_device, surface, &details->nformats, NULL); + if(details->nformats > 0) { + details->formats = xcalloc(details->nformats, sizeof(*details->formats)); + vkGetPhysicalDeviceSurfaceFormatsKHR(phy_device, surface, &details->nformats, details->formats); + } + + vkGetPhysicalDeviceSurfacePresentModesKHR(phy_device, surface, &details->npresent_modes, NULL); + if(details->npresent_modes > 0) { + details->present_modes = xcalloc(details->npresent_modes, sizeof(*details->present_modes)); + vkGetPhysicalDeviceSurfacePresentModesKHR(phy_device, surface, &details->npresent_modes, details->present_modes); + } + + return 0; +} + +static void swap_chain_free_support_details(struct swap_chain_support_details *details) +{ + if(details->nformats > 0) free(details->formats); + if(details->npresent_modes > 0) free(details->present_modes); +} + +static int swap_chain_choose_format(VkSurfaceFormatKHR* formats, u32 nformats, VkSurfaceFormatKHR *format) +{ + for(u32 i = 0; i < nformats; i++) + if(formats[i].format == VK_FORMAT_B8G8R8A8_SRGB && + formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + *format = formats[i]; + return 0; + } + + *format = formats[0]; + return 0; +} + +static int swap_chain_choose_present_mode(VkPresentModeKHR *modes, u32 nmodes, VkPresentModeKHR *mode) +{ + for(u32 i = 0; i < nmodes; i++) + if(modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { + *mode = modes[i]; + return 0; + } + + *mode = VK_PRESENT_MODE_FIFO_KHR; + return 0; +} + +static int swap_chain_get_extent(VkSurfaceCapabilitiesKHR capabilities, VkExtent2D *extent) +{ + if(capabilities.currentExtent.width != UINT32_MAX) { + *extent = capabilities.currentExtent; + return 0; + } + + // TODO implement + err("Not Implemented"); + return 1; +} + +static int pipeline_load_shader_module(VkDevice device, char *path, VkShaderModule *module) +{ + int ret = 1; + + size_t size = 0; + u32 *buf = NULL; + + ECHECK(load_file_u32_aligned, path, &size, NULL); + buf = xmalloc(size); + ECHECK(load_file_u32_aligned, path, &size, buf); + + VkShaderModuleCreateInfo create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + create_info.codeSize = size; + create_info.pCode = buf; + + VCHECK(vkCreateShaderModule, device, &create_info, NULL, module); + + ret = 0; +exit: + if(buf) free(buf); + return ret; +} + +static int command_buffer_record(graphics_t graphics, u32 image_index) +{ + int ret = 1; + + // Begin the buffer + VkCommandBufferBeginInfo begin_info = {0}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = 0; // Optional + begin_info.pInheritanceInfo = NULL; // Optional + + VCHECK(vkBeginCommandBuffer, CMND.buffer, &begin_info); + + // Begin the render pass + VkRenderPassBeginInfo render_pass_info = {0}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_info.renderPass = PPLN.render_pass; + render_pass_info.framebuffer = SWCH.framebuffers[image_index]; + + render_pass_info.renderArea.offset = (VkOffset2D){0, 0}; + render_pass_info.renderArea.extent = SWCH.extent; + + VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + render_pass_info.clearValueCount = 1; + render_pass_info.pClearValues = &clear_color; + + vkCmdBeginRenderPass(CMND.buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); + + // Begin the drawing commands + vkCmdBindPipeline(CMND.buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, PPLN.pipeline); + + VkViewport viewport = {0}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)SWCH.extent.width; + viewport.height = (float)SWCH.extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + vkCmdSetViewport(CMND.buffer, 0, 1, &viewport); + + VkRect2D scissor = {0}; + scissor.offset = (VkOffset2D){0, 0}; + scissor.extent = SWCH.extent; + vkCmdSetScissor(CMND.buffer, 0, 1, &scissor); + + // 3 vertices, 1 instance (for instanced rendering) + // 0 is the first vertex, 0 is the fist instance + vkCmdDraw(CMND.buffer, 3, 1, 0, 0); + + // Cleaning up + vkCmdEndRenderPass(CMND.buffer); + + VCHECK(vkEndCommandBuffer, CMND.buffer); + + ret = 0; +exit: + return ret; +} + +#define X_VK_RESULT_TABLE(X) \ + X(VK_SUCCESS) \ + X(VK_NOT_READY) \ + X(VK_TIMEOUT) \ + X(VK_EVENT_SET) \ + X(VK_EVENT_RESET) \ + X(VK_INCOMPLETE) \ + X(VK_ERROR_OUT_OF_HOST_MEMORY) \ + X(VK_ERROR_OUT_OF_DEVICE_MEMORY) \ + X(VK_ERROR_INITIALIZATION_FAILED) \ + X(VK_ERROR_DEVICE_LOST) \ + X(VK_ERROR_MEMORY_MAP_FAILED) \ + X(VK_ERROR_LAYER_NOT_PRESENT) \ + X(VK_ERROR_EXTENSION_NOT_PRESENT) \ + X(VK_ERROR_FEATURE_NOT_PRESENT) \ + X(VK_ERROR_INCOMPATIBLE_DRIVER) \ + X(VK_ERROR_TOO_MANY_OBJECTS) \ + X(VK_ERROR_FORMAT_NOT_SUPPORTED) \ + X(VK_ERROR_FRAGMENTED_POOL) \ + X(VK_ERROR_UNKNOWN) \ + X(VK_ERROR_OUT_OF_POOL_MEMORY) \ + X(VK_ERROR_INVALID_EXTERNAL_HANDLE) \ + X(VK_ERROR_FRAGMENTATION) \ + X(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS) \ + +#define X_VK_RESULT_CASE(error) \ + case error: return #error; + +static char *str_VkResult(VkResult result) +{ + switch(result) { + X_VK_RESULT_TABLE(X_VK_RESULT_CASE) + default: + return "VK_ERROR_OTHER"; + } +} + +#ifdef DEBUG + +static bool instance_has_validation_layers(const char * const *layers, u32 nlayers) +{ + (void)layers; + (void)nlayers; + + // u32 navaliable = 0; + // vkEnumerateInstanceLayerProperties(&navaliable, NULL); + + // VkLayerProperties *available_layers = xcalloc(navaliable, sizeof(VkLayerProperties)); + // vkEnumerateInstanceLayerProperties(&navaliable, available_layers); + return true; +} + +static int create_debug_messenger(graphics_t graphics, struct graphics_info *info) +{ + (void)info; + int ret = 1; + + VkDebugUtilsMessengerCreateInfoEXT cinfo = {0}; + debug_messenger_populate_info(&cinfo); + + VCHECK(CreateDebugUtilsMessengerEXT, graphics->instance, &cinfo, NULL, &graphics->debug_messenger); + + ret = 0; +exit: + return ret; +} + +static void debug_messenger_populate_info(VkDebugUtilsMessengerCreateInfoEXT *info) +{ + info->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + info->messageSeverity = + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + 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; + info->pfnUserCallback = debug_messenger_callback; +} + +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_messenger_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) +{ + (void)messageSeverity; + (void)messageType; + (void)pUserData; + + warn("Validation Layer: %s", pCallbackData->pMessage); + return VK_FALSE; +} + +VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { + PFN_vkCreateDebugUtilsMessengerEXT f = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + if (f != NULL) { + return f(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { + PFN_vkDestroyDebugUtilsMessengerEXT f = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + if (f != NULL) { + f(instance, debugMessenger, pAllocator); + } +} + +#endif |