#include "vulkan_pipeline.h" #include "locale_vulkan.h" #include "utilities/file_utils.h" #include "utilities/logger.h" #include "vulkan_errors.h" #include "mesh_controller.h" #include "rse_vulkan_image.h" #include "vulkan/vulkan.h" #include #include #include #define SHADER_MODULES_COUNT 2U enum shader_stage_t { VERTEX, FRAGMENTS }; #define IMAGE_FORMAT VK_FORMAT_B8G8R8A8_SRGB; uint32_t g_swapchain_images_count = 0U; VkImage* g_swapchain_images = NULL; VkShaderModule g_shader_modules[SHADER_MODULES_COUNT]; const char* g_shader_paths[] = { /* TODO: When changing to release and install, move those files*/ "../graphics/shaders/shader.vert.num", "../graphics/shaders/shader.frag.num", }; VkShaderModule create_shader_module(const char* binary_data, size_t data_size); static uint8_t create_swapchain(); static uint8_t create_swapchain_image_views(); static uint8_t create_render_pass(); static uint8_t create_descriptor_set_layout(); static uint8_t create_pipeline_layout(); static uint8_t create_graphics_pipeline(); static uint8_t create_framebuffers(); static uint8_t fill_shader_stages(VkPipelineShaderStageCreateInfo* shader_stages); static void fill_dynamic_pipeline_state_info(VkPipelineDynamicStateCreateInfo* dynamic_state); static void free_dynamic_pipeline_state_info(VkPipelineDynamicStateCreateInfo* dynamic_state); static void fill_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_info); static void free_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_info); static void fill_input_assembly_info(VkPipelineInputAssemblyStateCreateInfo* input_assembly); static void fill_viewport_state_info(VkPipelineViewportStateCreateInfo* viewport_state); static void free_viewport_state_info(VkPipelineViewportStateCreateInfo* viewport_state); static void fill_rasterization_info(VkPipelineRasterizationStateCreateInfo* rasterizer); static void fill_multisampling_state_info(VkPipelineMultisampleStateCreateInfo* multisampling); static void fill_color_blend_state_info(VkPipelineColorBlendStateCreateInfo* color_blending); static void free_color_blend_info(VkPipelineColorBlendStateCreateInfo* color_blending); static VkVertexInputBindingDescription get_binding_description() { VkVertexInputBindingDescription binding_description; binding_description.binding = 0; binding_description.stride = sizeof(struct rse_vertex_t); binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; return binding_description; } static VkVertexInputAttributeDescription* get_attribute_descriptions() { static VkVertexInputAttributeDescription attribute_descriptions[3]; attribute_descriptions[0].binding = 0; attribute_descriptions[0].location = 0; /* inPosition in shader.vert */ attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; attribute_descriptions[0].offset = offsetof(struct rse_vertex_t, pos); attribute_descriptions[1].binding = 0; attribute_descriptions[1].location = 1; /* inColor in shader.vert */ attribute_descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; attribute_descriptions[1].offset = offsetof(struct rse_vertex_t, color); attribute_descriptions[2].binding = 0; attribute_descriptions[2].location = 2; /* inTexCoords in shader.vert */ attribute_descriptions[2].format = VK_FORMAT_R32G32_SFLOAT; attribute_descriptions[2].offset = offsetof(struct rse_vertex_t, tex_coords); return attribute_descriptions; } /** * @brief Creates shader module * * @param binary_data Shader compiled code * @return std::optional Handle to compiled shader module or nullopt */ VkShaderModule create_shader_module(const char* binary_data, size_t data_size) { VkShaderModule shader_module; VkShaderModuleCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; create_info.pNext = NULL; create_info.flags = 0U; create_info.codeSize = data_size; create_info.pCode = (const uint32_t*)(binary_data); if (VK_SUCCESS != vkCreateShaderModule(g_vulkan_state.device, &create_info, NULL, &shader_module)) { LOGF(vulkan_error_messages[VULKAN_SHADER_UNABLE_TO_CREATE_SHADER]); // TODO: Add error handling return shader_module; } return shader_module; } /** * @brief Create a Swapchain * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED */ uint8_t create_swapchain() { VkSwapchainCreateInfoKHR create_info; VkSurfaceCapabilitiesKHR physical_device_surface_capabilities; VkBool32 surfaceSupported; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_vulkan_state.physical_device, g_vulkan_state.surface, &physical_device_surface_capabilities); /* Store extent in global variable, for later use */ g_vulkan_state.swapchain_extent = physical_device_surface_capabilities.currentExtent; /* Check if device supports surface for presentation */ vkGetPhysicalDeviceSurfaceSupportKHR(g_vulkan_state.physical_device, g_vulkan_state.queue_family_indices[0], g_vulkan_state.surface, &surfaceSupported); create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; create_info.pNext = NULL; create_info.flags = 0U; create_info.surface = g_vulkan_state.surface; create_info.minImageCount = SWAP_BUFFER_COUNT + 1; create_info.imageFormat = IMAGE_FORMAT; /* TODO: Check for supported formats */ create_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; /* TODO: Check for supported color space */ create_info.imageExtent = g_vulkan_state.swapchain_extent; create_info.imageArrayLayers = 1U; /* For non-stereoscopic-3D applications, this value is 1. */ create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; /* TODO: Probably change later? Dunno */ create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; /* Using only one queue family, so this is ok */ create_info.queueFamilyIndexCount = 1U; create_info.pQueueFamilyIndices = g_vulkan_state.queue_family_indices; create_info.preTransform = physical_device_surface_capabilities.currentTransform; create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; /* TODO: Choose present mode from VkPresentModeKHR */ create_info.clipped = VK_TRUE; create_info.oldSwapchain = VK_NULL_HANDLE; if (VK_SUCCESS != vkCreateSwapchainKHR(g_vulkan_state.device, &create_info, NULL, &g_vulkan_state.swapchain)) { LOGF(vulkan_error_messages[VULKAN_SWAPCHAIN_CREATION_FAILED]); return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED; } /* Obtain swapchain images */ vkGetSwapchainImagesKHR(g_vulkan_state.device, g_vulkan_state.swapchain, &g_swapchain_images_count, NULL); g_swapchain_images = (VkImage*)malloc(sizeof(VkImage) * g_swapchain_images_count); vkGetSwapchainImagesKHR(g_vulkan_state.device, g_vulkan_state.swapchain, &g_swapchain_images_count, g_swapchain_images); return VULKAN_ERROR_NO_ERROR; } /** * @brief Create a Swapchain Image Views * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: */ uint8_t create_swapchain_image_views() { g_vulkan_state.swapchain_image_views = malloc(sizeof(VkImageView) * g_swapchain_images_count); for (size_t i = 0; i < g_swapchain_images_count; i++) { VkImageViewCreateInfo create_info; create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; create_info.pNext = NULL; create_info.flags = 0U; create_info.image = g_swapchain_images[i]; create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; /* 2D Texture */ create_info.format = VK_FORMAT_B8G8R8A8_SRGB; create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; 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.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; create_info.subresourceRange.baseMipLevel = 0U; create_info.subresourceRange.levelCount = 1U; create_info.subresourceRange.baseArrayLayer = 0U; create_info.subresourceRange.layerCount = 1U; if (VK_SUCCESS != vkCreateImageView(g_vulkan_state.device, &create_info, NULL, &g_vulkan_state.swapchain_image_views[i])) { LOGF(vulkan_error_messages[VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED]); return VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED; } } return VULKAN_ERROR_NO_ERROR; } /** * @brief Create a Render Pass * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_RENDERPASS_CREATION_FAILED */ uint8_t create_render_pass() { VkAttachmentDescription attachments[2] = {}; VkAttachmentDescription color_attachment = {}; VkAttachmentReference color_attachment_ref = {}; VkSubpassDescription subpass = {}; VkRenderPassCreateInfo renderpass_info = {}; VkAttachmentDescription depth_attachment = {}; VkAttachmentReference depth_attachment_ref = {}; VkSubpassDependency dependency = {}; depth_attachment.format = find_depth_format(); depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT; depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; depth_attachment_ref.attachment = 1; depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; color_attachment.flags = 0U; color_attachment.format = IMAGE_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; color_attachment_ref.attachment = 0; color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_attachment_ref; subpass.pDepthStencilAttachment = &depth_attachment_ref; attachments[0] = color_attachment; attachments[1] = depth_attachment; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.srcAccessMask = 0; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; renderpass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderpass_info.attachmentCount = 2; renderpass_info.pAttachments = attachments; renderpass_info.subpassCount = 1; renderpass_info.pSubpasses = &subpass; renderpass_info.dependencyCount = 1; renderpass_info.pDependencies = &dependency; if (VK_SUCCESS != vkCreateRenderPass(g_vulkan_state.device, &renderpass_info, NULL, &g_vulkan_state.render_pass)) { LOGF(vulkan_error_messages[VULKAN_RENDERPASS_CREATION_FAILED]); return VULKAN_ERROR_RENDERPASS_CREATION_FAILED; } return VULKAN_ERROR_NO_ERROR; } /** * @brief Create a Descriptor Set Layout * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: */ uint8_t create_descriptor_set_layout() { VkDescriptorSetLayoutCreateInfo layout_info = {}; VkDescriptorSetLayoutBinding bindings[2]; VkDescriptorSetLayoutBinding ubo_layout_binding = {}; VkDescriptorSetLayoutBinding sampler_layout_binding = {}; // VkDescriptorSetLayoutBinding textures_layout_binding = {}; ubo_layout_binding.binding = 0; ubo_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; ubo_layout_binding.descriptorCount = 1; ubo_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; ubo_layout_binding.pImmutableSamplers = NULL; sampler_layout_binding.binding = 1; sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; sampler_layout_binding.descriptorCount = get_textures_count(); sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; sampler_layout_binding.pImmutableSamplers = NULL; bindings[0] = ubo_layout_binding; bindings[1] = sampler_layout_binding; layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_info.pNext = NULL; layout_info.flags = 0U; layout_info.bindingCount = 2U; layout_info.pBindings = bindings; if (vkCreateDescriptorSetLayout(g_vulkan_state.device, &layout_info, NULL, &g_vulkan_state.descriptor_set_layout) != VK_SUCCESS) { LOGF(vulkan_error_messages[VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED]); return VULKAN_ERROR_DESCRIPTORSETLAYOUT_CREATION_FAILED; } return VULKAN_ERROR_NO_ERROR; } /** * @brief Create a Pipeline Layout * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED */ uint8_t create_pipeline_layout() { VkPipelineLayoutCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; create_info.pNext = NULL; create_info.flags = 0U; create_info.setLayoutCount = 1U; create_info.pSetLayouts = &g_vulkan_state.descriptor_set_layout; create_info.pushConstantRangeCount = 0U; create_info.pPushConstantRanges = NULL; if (VK_SUCCESS != vkCreatePipelineLayout(g_vulkan_state.device, &create_info, NULL, &g_vulkan_state.pipeline_layout)) { LOGF(vulkan_error_messages[VULKAN_PIPELAYOUT_CREATION_FAILED]); return VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED; } return VULKAN_ERROR_NO_ERROR; } /** * @brief Create Graphics Pipeline * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED */ uint8_t create_graphics_pipeline() { uint8_t status = VULKAN_ERROR_NO_ERROR; VkPipelineShaderStageCreateInfo shader_stages[SHADER_MODULES_COUNT]; VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; VkPipelineInputAssemblyStateCreateInfo input_assembly = {}; VkPipelineViewportStateCreateInfo viewport_state = {}; VkPipelineRasterizationStateCreateInfo rasterizer = {}; VkPipelineMultisampleStateCreateInfo multisampling = {}; VkPipelineColorBlendStateCreateInfo color_blending = {}; VkPipelineDynamicStateCreateInfo dynamic_state = {}; VkPipelineDepthStencilStateCreateInfo depth_stencil = {}; STATUS_CHECK(fill_shader_stages(shader_stages)); fill_vertex_input_info(&vertex_input_info); fill_input_assembly_info(&input_assembly); fill_viewport_state_info(&viewport_state); fill_rasterization_info(&rasterizer); fill_multisampling_state_info(&multisampling); fill_color_blend_state_info(&color_blending); fill_dynamic_pipeline_state_info(&dynamic_state); //TODO: Move to another function depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; depth_stencil.depthTestEnable = VK_TRUE; depth_stencil.depthWriteEnable = VK_TRUE; depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; depth_stencil.depthBoundsTestEnable = VK_FALSE; depth_stencil.minDepthBounds = 0.0f; // Optional depth_stencil.maxDepthBounds = 1.0f; // Optional depth_stencil.stencilTestEnable = VK_FALSE; // depth_stencil.front = {0}; // Optional // depth_stencil.back = {0}; // Optional VkGraphicsPipelineCreateInfo pipeline_info = {}; pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_info.pNext = NULL; pipeline_info.flags = 0U; pipeline_info.stageCount = SHADER_MODULES_COUNT; pipeline_info.pStages = shader_stages; pipeline_info.pVertexInputState = &vertex_input_info; pipeline_info.pInputAssemblyState = &input_assembly; pipeline_info.pTessellationState = NULL; pipeline_info.pViewportState = &viewport_state; pipeline_info.pRasterizationState = &rasterizer; pipeline_info.pMultisampleState = &multisampling; pipeline_info.pDepthStencilState = &depth_stencil; pipeline_info.pColorBlendState = &color_blending; pipeline_info.pDynamicState = &dynamic_state; pipeline_info.layout = g_vulkan_state.pipeline_layout; pipeline_info.renderPass = g_vulkan_state.render_pass; pipeline_info.subpass = 0; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_info.basePipelineIndex = -1; if (VK_SUCCESS != vkCreateGraphicsPipelines(g_vulkan_state.device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &g_vulkan_state.graphics_pipeline)) { LOGF(vulkan_error_messages[VULKAN_GRAPHICSPIPELINE_CREATION_FAILED]); status = VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED; } free_dynamic_pipeline_state_info(&dynamic_state); free_color_blend_info(&color_blending); free_viewport_state_info(&viewport_state); free_vertex_input_info(&vertex_input_info); return status; } /** * @brief Create Frambuffers * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: */ uint8_t create_framebuffers() { g_vulkan_state.swapchain_framebuffers = malloc(sizeof(VkFramebuffer) * g_swapchain_images_count); for (size_t i = 0; i < g_swapchain_images_count; i++) { VkImageView attachments[] = {g_vulkan_state.swapchain_image_views[i], rse_get_depth_image_view()}; VkFramebufferCreateInfo framebufferInfo = {}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = g_vulkan_state.render_pass; framebufferInfo.attachmentCount = 2; framebufferInfo.pAttachments = attachments; framebufferInfo.width = g_vulkan_state.swapchain_extent.width; framebufferInfo.height = g_vulkan_state.swapchain_extent.height; framebufferInfo.layers = 1; if (vkCreateFramebuffer(g_vulkan_state.device, &framebufferInfo, NULL, &g_vulkan_state.swapchain_framebuffers[i]) != VK_SUCCESS) { LOGF(vulkan_error_messages[VULKAN_FRAMEBUFFERS_CREATION_FAILED]); return VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED; } } return VULKAN_ERROR_NO_ERROR; } /** * @brief Recreate swapchain * */ void recreate_swapchain() { cleanup_swapchain(); create_swapchain(); create_swapchain_image_views(); create_framebuffers(); } /** * @brief Cleanup swapchain * */ void cleanup_swapchain() { size_t i; vkDeviceWaitIdle(g_vulkan_state.device); for (i = 0; i < g_swapchain_images_count; i++) { vkDestroyFramebuffer(g_vulkan_state.device, g_vulkan_state.swapchain_framebuffers[i], NULL); } for (i = 0; i < g_swapchain_images_count; i++) { vkDestroyImageView(g_vulkan_state.device, g_vulkan_state.swapchain_image_views[i], NULL); } vkDestroySwapchainKHR(g_vulkan_state.device, g_vulkan_state.swapchain, NULL); } /** * @brief Creates shader stages from hardcoded shader paths. Changes input * * @param shader_stages Empty vector, that will be filled with data * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_SHADER_FILE_OPEN_FAILED * VULKAN_ERROR_SHADER_CREATION_FAILED */ static uint8_t fill_shader_stages(VkPipelineShaderStageCreateInfo* shader_stages) { char* buffer; long buffer_size; VkShaderModule vertex_shader_module; VkShaderModule fragments_shader_module; VkPipelineShaderStageCreateInfo vert_shader_stage_info = {}; VkPipelineShaderStageCreateInfo frag_shader_stage_info = {}; rse_read_file(g_shader_paths[VERTEX], &buffer_size, NULL); buffer = malloc(buffer_size); rse_read_file(g_shader_paths[VERTEX], &buffer_size, buffer); vertex_shader_module = create_shader_module(buffer, buffer_size); free(buffer); rse_read_file(g_shader_paths[FRAGMENTS], &buffer_size, NULL); buffer = malloc(buffer_size); rse_read_file(g_shader_paths[FRAGMENTS], &buffer_size, buffer); fragments_shader_module = create_shader_module(buffer, buffer_size); free(buffer); //TODO: Shader creation might have failed. Add error handling /* Vertex Shader Pipeline Stage */ vert_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vert_shader_stage_info.pNext = NULL; vert_shader_stage_info.flags = 0U; vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; vert_shader_stage_info.module = vertex_shader_module; vert_shader_stage_info.pName = "main"; /* Entry point function */ vert_shader_stage_info.pSpecializationInfo = NULL; /* Specialization in a SPIR-V module */ /* Fragments Shader Pipeline Stage */ frag_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; frag_shader_stage_info.pNext = NULL; frag_shader_stage_info.flags = 0U; frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; frag_shader_stage_info.module = fragments_shader_module; frag_shader_stage_info.pName = "main"; /* Entry point function */ frag_shader_stage_info.pSpecializationInfo = NULL; /* Specialization in a SPIR-V module */ shader_stages[VERTEX] = vert_shader_stage_info; shader_stages[FRAGMENTS] = frag_shader_stage_info; memcpy(&g_shader_modules[0], &vertex_shader_module, sizeof(VkShaderModule)); memcpy(&g_shader_modules[1], &fragments_shader_module, sizeof(VkShaderModule)); return VULKAN_ERROR_NO_ERROR; } /** * @brief Fill dynamic pipeline state info * * @param dynamic_state structure to be filled */ void fill_dynamic_pipeline_state_info(VkPipelineDynamicStateCreateInfo* dynamic_state) { VkDynamicState* dynamic_states; /* * Select pipeline stages that will be set dynamically. Those that are selected are ignored in * pipeline create structures. I'll list ignored members of structs, that will be ignored due to the * settings and also functions, that will need to be called to set specific state. */ dynamic_states = malloc(sizeof(VkDynamicState) * 2); dynamic_states[0] = VK_DYNAMIC_STATE_VIEWPORT; /* VkPipelineViewportStateCreateInfo:pViewport; vkCmdSetViewport() */ dynamic_states[1] = VK_DYNAMIC_STATE_SCISSOR; /* VkPipelineViewportStateCreateInfo:pScissors; vkCmdSetScissors() */ dynamic_state->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state->pNext = NULL; dynamic_state->flags = 0U; dynamic_state->dynamicStateCount = 2U; /* dynamic_state is constructed localy. To avoid creation of VkPipelineDynamicStateCreateInfo structure in * main pipeline construction function, we have to do a little memory trickery here and allocate memory for * dynamic state pointer manually. We must also free this memory later */ dynamic_state->pDynamicStates = dynamic_states; } /** * @brief Release allocated dynamic pipeline state info * * @param dynamic_state structure to be freed */ void free_dynamic_pipeline_state_info(VkPipelineDynamicStateCreateInfo* dynamic_state) { free((VkDynamicState*)dynamic_state->pDynamicStates); } /** * @brief Fill vertex input info * * @param vertex_input_info structure to be filled */ void fill_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_info) { const size_t binding_descriptions_count = 2; const size_t attribute_descriptions_count = 7; /* This is clusterfuck with some magic numbers here and there and unneeded malloc and memcpy stuff. Change, * so you won't feel ashamed. */ VkVertexInputBindingDescription vertex_binding_description = get_binding_description(); VkVertexInputAttributeDescription* vertex_attribute_descriptions = get_attribute_descriptions(); VkVertexInputBindingDescription* binding_descriptions = malloc(sizeof(VkVertexInputBindingDescription) * binding_descriptions_count); VkVertexInputAttributeDescription* attribute_descriptions = malloc(sizeof(VkVertexInputAttributeDescription) * attribute_descriptions_count); binding_descriptions[0] = vertex_binding_description; binding_descriptions[1].binding = 1; binding_descriptions[1].stride = sizeof(struct rse_instance_data_t); //TODO: change binding_descriptions[1].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; memcpy(attribute_descriptions, vertex_attribute_descriptions, sizeof(VkVertexInputAttributeDescription) * 3); attribute_descriptions[3].binding = 1; attribute_descriptions[3].location = 3; attribute_descriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT; attribute_descriptions[3].offset = offsetof(struct rse_instance_data_t, pos); attribute_descriptions[4].binding = 1; attribute_descriptions[4].location = 4; attribute_descriptions[4].format = VK_FORMAT_R32G32B32_SFLOAT; attribute_descriptions[4].offset = offsetof(struct rse_instance_data_t, rot); attribute_descriptions[5].binding = 1; attribute_descriptions[5].location = 5; attribute_descriptions[5].format = VK_FORMAT_R32_SFLOAT; attribute_descriptions[5].offset = offsetof(struct rse_instance_data_t, scale); attribute_descriptions[6].binding = 1; attribute_descriptions[6].location = 6; attribute_descriptions[6].format = VK_FORMAT_R8_UINT; attribute_descriptions[6].offset = offsetof(struct rse_instance_data_t, texture_id); // ************************ vertex_input_info->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input_info->pNext = NULL; vertex_input_info->flags = 0U; vertex_input_info->vertexBindingDescriptionCount = binding_descriptions_count; vertex_input_info->pVertexBindingDescriptions = binding_descriptions; vertex_input_info->vertexAttributeDescriptionCount = attribute_descriptions_count; vertex_input_info->pVertexAttributeDescriptions = attribute_descriptions; } /** * @brief Free allocated vertex input info * * @param vertex_input_info structure to be freed */ void free_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_info) { free((VkVertexInputBindingDescription*)vertex_input_info->pVertexBindingDescriptions); free((VkVertexInputAttributeDescription*)vertex_input_info->pVertexAttributeDescriptions); } /** * @brief Fill input assembly info * * @param input_assembly structure to be filled */ void fill_input_assembly_info(VkPipelineInputAssemblyStateCreateInfo* input_assembly) { input_assembly->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; input_assembly->pNext = NULL; input_assembly->flags = 0U; input_assembly->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; // TODO: Change to _STRIP input_assembly->primitiveRestartEnable = VK_FALSE; /* If I want to break lines during _STRIP, * I must set this to VK_TRUE */ } /** * @brief Fill viewport state info * * @param viewport_state structure to be filled */ void fill_viewport_state_info(VkPipelineViewportStateCreateInfo* viewport_state) { VkViewport* viewport = malloc(sizeof(VkViewport)); VkRect2D* scissor = malloc(sizeof(VkRect2D)); viewport->x = 0.0f; viewport->y = 0.0f; viewport->width = (float)(g_vulkan_state.swapchain_extent.width); viewport->height = (float)(g_vulkan_state.swapchain_extent.height); viewport->minDepth = 0.0f; viewport->maxDepth = 1.0f; /* Setting Scissors to fill entire window*/ scissor->offset.x = 0; scissor->offset.y = 0; scissor->extent = g_vulkan_state.swapchain_extent; viewport_state->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_state->viewportCount = 1; viewport_state->pViewports = viewport; viewport_state->scissorCount = 1; viewport_state->pScissors = scissor; } /** * @brief Free allocated viewport state input info * * @param viewport_state structure to be freed */ void free_viewport_state_info(VkPipelineViewportStateCreateInfo* viewport_state) { free((VkViewport*)viewport_state->pViewports); free((VkRect2D*)viewport_state->pScissors); } /** * @brief Fill rasteriation info * * @param rasterizer */ void fill_rasterization_info(VkPipelineRasterizationStateCreateInfo* rasterizer) { rasterizer->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer->pNext = NULL; rasterizer->flags = 0U; rasterizer->depthClampEnable = VK_FALSE; /* VK_TRUE to play with shadow maps */ rasterizer->rasterizerDiscardEnable = VK_FALSE; /* Disable/Enable output to framebuffer*/ rasterizer->polygonMode = VK_POLYGON_MODE_FILL; rasterizer->cullMode = VK_CULL_MODE_BACK_BIT; rasterizer->frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rasterizer->depthBiasEnable = VK_FALSE; rasterizer->depthBiasConstantFactor = 0.0f; rasterizer->depthBiasClamp = 0.0f; rasterizer->depthBiasSlopeFactor = 0.0f; rasterizer->lineWidth = 1.0f; } /** * @brief Fill multisampling state info * * @param multisampling structure to be filled */ void fill_multisampling_state_info(VkPipelineMultisampleStateCreateInfo* multisampling) { multisampling->sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling->pNext = NULL; multisampling->flags = 0U; multisampling->rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; multisampling->sampleShadingEnable = VK_FALSE; multisampling->minSampleShading = 1.0f; multisampling->pSampleMask = NULL; multisampling->alphaToCoverageEnable = VK_FALSE; multisampling->alphaToOneEnable = VK_FALSE; } /** * @brief Fill color blend state info * * @param color_blending structure to be filled */ void fill_color_blend_state_info(VkPipelineColorBlendStateCreateInfo* color_blending) { VkPipelineColorBlendAttachmentState* color_blend_attachment = malloc(sizeof(VkPipelineColorBlendAttachmentState)); color_blend_attachment->blendEnable = VK_FALSE; color_blend_attachment->srcColorBlendFactor = VK_BLEND_FACTOR_ONE; color_blend_attachment->dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; color_blend_attachment->colorBlendOp = VK_BLEND_OP_ADD; color_blend_attachment->srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; color_blend_attachment->dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; color_blend_attachment->alphaBlendOp = VK_BLEND_OP_ADD; 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_blending->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; color_blending->pNext = NULL; color_blending->flags = 0U; color_blending->logicOpEnable = VK_FALSE; color_blending->logicOp = VK_LOGIC_OP_COPY; color_blending->attachmentCount = 1U; color_blending->pAttachments = color_blend_attachment; color_blending->blendConstants[0] = 0.0f; color_blending->blendConstants[1] = 0.0f; color_blending->blendConstants[2] = 0.0f; color_blending->blendConstants[3] = 0.0f; } /** * @brief Free allocated color blend state info * * @param color_blending Structure to be freed */ void free_color_blend_info(VkPipelineColorBlendStateCreateInfo* color_blending) { free((VkPipelineColorBlendAttachmentState*)color_blending->pAttachments); } uint8_t create_pipeline() { uint8_t status = VULKAN_ERROR_NO_ERROR; STATUS_CHECK(create_swapchain()); STATUS_CHECK(create_swapchain_image_views()); STATUS_CHECK(create_render_pass()); STATUS_CHECK(create_descriptor_set_layout()); STATUS_CHECK(create_pipeline_layout()); STATUS_CHECK(create_graphics_pipeline()); STATUS_CHECK(create_framebuffers()); return status; } void destroy_pipeline() { vkDestroyPipeline(g_vulkan_state.device, g_vulkan_state.graphics_pipeline, NULL); vkDestroyPipelineLayout(g_vulkan_state.device, g_vulkan_state.pipeline_layout, NULL); vkDestroyRenderPass(g_vulkan_state.device, g_vulkan_state.render_pass, NULL); for (size_t i = 0; i < SHADER_MODULES_COUNT; i++) { vkDestroyShaderModule(g_vulkan_state.device, g_shader_modules[i], NULL); } }