#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