From e9bd321c8bd6bdb6aa7305c6da8b33e8cac462f7 Mon Sep 17 00:00:00 2001 From: kartofen Date: Sun, 22 Oct 2023 21:18:13 +0300 Subject: renamed device.c/.h to graphics --- src/device.c | 1147 -------------------------------------------------------- src/device.h | 73 ---- src/graphics.c | 1146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/graphics.h | 73 ++++ src/main.c | 27 +- 5 files changed, 1231 insertions(+), 1235 deletions(-) delete mode 100644 src/device.c delete mode 100644 src/device.h create mode 100644 src/graphics.c create mode 100644 src/graphics.h diff --git a/src/device.c b/src/device.c deleted file mode 100644 index 5934270..0000000 --- a/src/device.c +++ /dev/null @@ -1,1147 +0,0 @@ -#include -#include -#include - -#include "device.h" -#include "common.h" - -// TODO: rename device.c/.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(device_t device, struct device_info *info); -static int create_surface(device_t device, struct device_info *info); -static int create_physical_device(device_t device, struct device_info *info); -static int create_logical_device(device_t device, struct device_info *info); -static int create_pipeline(device_t device, struct device_info *info); -static int create_swap_chain(device_t device, struct device_info *info); -static int create_command_pool(device_t device, struct device_info *info); -static int create_sync_objects(device_t device, struct device_info *info); - -static void destroy_swap_chain(device_t device); -static void destroy_pipeline(device_t device); -// static void destroy_command_pool(device_t device); -static void destroy_sync_objects(device_t device); - -// --- 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 device->pipeline -static int pipeline_load_shader_module(VkDevice device, char *path, VkShaderModule *module); - -#define SWCH device->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 device->command -static int command_buffer_record(device_t device, u32 image_index); - -#define SYNC device->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(device_t device, struct device_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, device, info) - -device_t device_create(struct device_info *info) -{ - device_t device = xmalloc(sizeof(struct device)); - device->instance = VK_NULL_HANDLE; - device->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 device; -fail: - device_destroy(device); - return NULL; -} - -void device_destroy(device_t device) -{ - if(!device) return; - - vkDeviceWaitIdle(device->logical_device); - - destroy_sync_objects(device); - - vkDestroyCommandPool(device->logical_device, CMND.pool, NULL); - - destroy_swap_chain(device); - destroy_pipeline(device); - - vkDestroyDevice(device->logical_device, NULL); - - #ifdef DEBUG - DestroyDebugUtilsMessengerEXT(device->instance, device->debug_messenger, NULL); - #endif - - vkDestroySurfaceKHR(device->instance, device->surface, NULL); - vkDestroyInstance(device->instance, NULL); - - free(device); -} - -int device_draw_frame(device_t device) -{ - int ret = 1; - - // wait for the previous frame to finish - vkWaitForFences(device->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(device->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(device); - create_swap_chain(device, NULL); - - ret = 0; goto exit; - } - - vkResetFences(device->logical_device, 1, &SYNC.fence_inflight); - - // reset the command buffer - vkResetCommandBuffer(CMND.buffer, 0); - command_buffer_record(device, 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, device->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(device->present_queue, &present_info); - - - ret = 0; -exit: - return ret; -} - -static int create_instance(device_t device, struct device_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, &device->instance); - - ret = 0; -exit: -#ifdef DEBUG - free(extensions); -#endif - return ret; -} - -static int create_surface(device_t device, struct device_info *info) -{ - if(info->surface_func(device->instance, &device->surface)) { - err("Couldn't create a VkSurfaceKHR"); - return 1; - } - - return 0; -} - -static int create_physical_device(device_t device, struct device_info *info) -{ - (void)info; - int ret = 1; - - device->physical_device = VK_NULL_HANDLE; - - u32 dev_count = 0; - vkEnumeratePhysicalDevices(device->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(device->instance, &dev_count, devices); - - for(u32 i = 0; i < dev_count; i++) { - if(device_is_suitable(devices[i], device->surface)) { - device->physical_device = devices[i]; - break; - } - } - - free(devices); - - if(device->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(device_t device, struct device_info *info) -{ - (void)info; - int ret = 1; - - // queue family data - struct queue_family_idx indices = {0}; - device_queue_families(device->physical_device, device->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, device->physical_device, &create_info, NULL, &device->logical_device); - - vkGetDeviceQueue(device->logical_device, indices.graphics_family, 0, &device->graphics_queue); - vkGetDeviceQueue(device->logical_device, indices.present_family, 0, &device->present_queue); - - ret = 0; -exit: - return ret; -} - -static int create_swap_chain(device_t device, struct device_info *info) -{ - (void)info; - int ret = 1; - - struct swap_chain_support_details details; - swap_chain_support(device->physical_device, device->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 = device->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(device->physical_device, device->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, device->logical_device, &create_info, NULL, &SWCH.swap_chain); - - // Get the Images - SWCH.image_format = surface_format.format; - SWCH.extent = extent; - - vkGetSwapchainImagesKHR(device->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(device->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, device->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, device->logical_device, &framebuffer_info, NULL, &SWCH.framebuffers[i]); - } - - ret = 0; -exit: - swap_chain_free_support_details(&details); - return ret; -} - -static void destroy_swap_chain(device_t device) -{ - for(u32 i = 0; i < SWCH.nimages; i++) { - vkDestroyImageView(device->logical_device, SWCH.image_views[i], NULL); - vkDestroyFramebuffer(device->logical_device, SWCH.framebuffers[i], NULL); - } - - if(SWCH.nimages != 0) { - free(SWCH.images); - free(SWCH.image_views); - free(SWCH.framebuffers); - } - - vkDestroySwapchainKHR(device->logical_device, SWCH.swap_chain, NULL); -} - -static int create_pipeline(device_t device, struct device_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(device->logical_device, "shaders/shader1.vert.spv", &vert_shader); - pipeline_load_shader_module(device->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, device->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(device->physical_device, device->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, device->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, device->logical_device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &PPLN.pipeline); - - ret = 0; -exit: - vkDestroyShaderModule(device->logical_device, frag_shader, NULL); - vkDestroyShaderModule(device->logical_device, vert_shader, NULL); - return ret; -} - -static void destroy_pipeline(device_t device) -{ - vkDestroyPipeline(device->logical_device, PPLN.pipeline, NULL); - vkDestroyPipelineLayout(device->logical_device, PPLN.layout, NULL); - vkDestroyRenderPass(device->logical_device, PPLN.render_pass, NULL); -} - -static int create_command_pool(device_t device, struct device_info *info) -{ - (void)info; - int ret = 1; - - struct queue_family_idx indices; - device_queue_families(device->physical_device, device->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, device->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, device->logical_device, &allocate_info, &CMND.buffer); - - ret = 0; -exit: - return ret; -} - -static int create_sync_objects(device_t device, struct device_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, device->logical_device, &semph_info, NULL, &SYNC.semph_image_available); - VCHECK(vkCreateSemaphore, device->logical_device, &semph_info, NULL, &SYNC.semph_render_finished); - VCHECK(vkCreateFence, device->logical_device, &fence_info, NULL, &SYNC.fence_inflight); - - ret = 0; -exit: - return ret; -} - -static void destroy_sync_objects(device_t device) -{ - vkDestroySemaphore(device->logical_device, SYNC.semph_image_available, NULL); - vkDestroySemaphore(device->logical_device, SYNC.semph_render_finished, NULL); - vkDestroyFence(device->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(device_t device, 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(device_t device, struct device_info *info) -{ - (void)info; - int ret = 1; - - VkDebugUtilsMessengerCreateInfoEXT cinfo = {0}; - debug_messenger_populate_info(&cinfo); - - VCHECK(CreateDebugUtilsMessengerEXT, device->instance, &cinfo, NULL, &device->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 diff --git a/src/device.h b/src/device.h deleted file mode 100644 index b28c806..0000000 --- a/src/device.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef DEVICE_H -#define DEVICE_H - -#include -#include -#include - -#include "common.h" - -// from vulkan source -#define MAKE_VERSION(major, minor, patch) \ - ((((u32)(major)) << 22U) | (((u32)(minor)) << 12U) | ((u32)(patch))) - -typedef uint32_t u32; - -typedef struct device { - VkInstance instance; - VkDebugUtilsMessengerEXT debug_messenger; - - VkPhysicalDevice physical_device; - VkDevice logical_device; - - VkQueue graphics_queue; - VkQueue present_queue; - - VkSurfaceKHR surface; - - struct pipeline { - VkRenderPass render_pass; - VkPipelineLayout layout; - VkPipeline pipeline; - } pipeline; - - struct swap_chain { - VkSwapchainKHR swap_chain; - - VkImage *images; - VkImageView *image_views; - VkFramebuffer *framebuffers; - u32 nimages; - - VkFormat image_format; - VkExtent2D extent; - } swap_chain; - - struct command { - VkCommandPool pool; - VkCommandBuffer buffer; - } command; - - struct sync { - VkSemaphore semph_image_available; - VkSemaphore semph_render_finished; - VkFence fence_inflight; - } sync; - -} * device_t; - -struct device_info { - char *name; - u32 version; - - const char* const* extensions; - u32 ext_count; - - int (*surface_func)(VkInstance instance, VkSurfaceKHR *surface); -}; - -device_t device_create(struct device_info *info); -void device_destroy(device_t device); -int device_draw_frame(device_t device); - -#endif 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 +#include +#include + +#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 diff --git a/src/graphics.h b/src/graphics.h new file mode 100644 index 0000000..52e511e --- /dev/null +++ b/src/graphics.h @@ -0,0 +1,73 @@ +#ifndef GRAPHICS_H +#define GRAPHICS_H + +#include +#include +#include + +#include "common.h" + +// from vulkan source +#define MAKE_VERSION(major, minor, patch) \ + ((((u32)(major)) << 22U) | (((u32)(minor)) << 12U) | ((u32)(patch))) + +typedef uint32_t u32; + +typedef struct graphics { + VkInstance instance; + VkDebugUtilsMessengerEXT debug_messenger; + + VkPhysicalDevice physical_device; + VkDevice logical_device; + + VkQueue graphics_queue; + VkQueue present_queue; + + VkSurfaceKHR surface; + + struct pipeline { + VkRenderPass render_pass; + VkPipelineLayout layout; + VkPipeline pipeline; + } pipeline; + + struct swap_chain { + VkSwapchainKHR swap_chain; + + VkImage *images; + VkImageView *image_views; + VkFramebuffer *framebuffers; + u32 nimages; + + VkFormat image_format; + VkExtent2D extent; + } swap_chain; + + struct command { + VkCommandPool pool; + VkCommandBuffer buffer; + } command; + + struct sync { + VkSemaphore semph_image_available; + VkSemaphore semph_render_finished; + VkFence fence_inflight; + } sync; + +} * graphics_t; + +struct graphics_info { + char *name; + u32 version; + + const char* const* extensions; + u32 ext_count; + + int (*surface_func)(VkInstance instance, VkSurfaceKHR *surface); +}; + +graphics_t graphics_create(struct graphics_info *info); +void graphics_destroy(graphics_t device); +int graphics_draw_frame(graphics_t device); + +#endif diff --git a/src/main.c b/src/main.c index 8fd7345..0f35679 100644 --- a/src/main.c +++ b/src/main.c @@ -1,15 +1,12 @@ #include #include -#include "device.h" +#include "graphics.h" #include "window.h" #include "common.h" -// TODO: rename device.c/.h -// to graphics.c/.h - window_t window; -device_t device; +graphics_t graphics; int _create_surface(VkInstance instance, VkSurfaceKHR *surface); @@ -44,18 +41,18 @@ int main(void) window_extension_info(window, &ext_count, (const char **)extensions); // populate the device info - struct device_info dev_info = {0}; - dev_info.name = "Test App"; - dev_info.version = MAKE_VERSION(1, 0, 0); + struct graphics_info grph_info = {0}; + grph_info.name = "Test App"; + grph_info.version = MAKE_VERSION(1, 0, 0); - dev_info.ext_count = ext_count; - dev_info.extensions = (const char * const *)extensions; + grph_info.ext_count = ext_count; + grph_info.extensions = (const char * const *)extensions; - dev_info.surface_func = _create_surface; + grph_info.surface_func = _create_surface; // create the device - device = device_create(&dev_info); - if(!device) { + graphics = graphics_create(&grph_info); + if(!graphics) { err("device_create: failed"); if(extensions) free(extensions); goto f3; @@ -72,12 +69,12 @@ int main(void) break; } - device_draw_frame(device); + graphics_draw_frame(graphics); } ret = 0; - device_destroy(device); + graphics_destroy(graphics); f3: window_destroy(window); f2: SDL_Quit(); f1: return ret; -- cgit v1.2.3