/* * Copyright 2022 Frederico de Oliveira Linhares * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "graphics_pipeline.hpp" #include #include #include "../core.hpp" #include "core.hpp" #include "image.hpp" #include "uniform_buffer.hpp" #include "vertex.hpp" namespace { void load_descriptor_set_layout_model_instance(void *obj) { auto self = static_cast(obj); std::array layout_bindings{}; layout_bindings[0].binding = 0; layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; layout_bindings[0].descriptorCount = 1; layout_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; layout_bindings[0].pImmutableSamplers = nullptr; layout_bindings[1].binding = 1; layout_bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; layout_bindings[1].descriptorCount = 1; layout_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; layout_bindings[1].pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo layout_info{}; layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_info.pNext = nullptr; layout_info.flags = 0; layout_info.bindingCount = static_cast(layout_bindings.size()); layout_info.pBindings = layout_bindings.data(); if(vkCreateDescriptorSetLayout( cg_core.vk_device_with_swapchain->device, &layout_info, nullptr, &self->descriptor_set_layout_model_instance) != VK_SUCCESS) throw CommandError{ "Failed to create Vulkan descriptor set layout for model instance."}; } void unload_descriptor_set_layout_model_instance(void *obj) { auto self = static_cast(obj); vkDestroyDescriptorSetLayout( cg_core.vk_device_with_swapchain->device, self->descriptor_set_layout_model_instance, nullptr); } void load_descriptor_set_layout_world_view(void *obj) { auto self = static_cast(obj); std::array set_layouts{}; set_layouts[0].binding = 0; set_layouts[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; set_layouts[0].descriptorCount = 1; set_layouts[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; set_layouts[0].pImmutableSamplers = nullptr; set_layouts[1].binding = 1; set_layouts[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; set_layouts[1].descriptorCount = 1; set_layouts[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; set_layouts[1].pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo layout_info{}; layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_info.pNext = nullptr; layout_info.flags = 0; layout_info.bindingCount = set_layouts.size(); layout_info.pBindings = set_layouts.data(); if(vkCreateDescriptorSetLayout( cg_core.vk_device_with_swapchain->device, &layout_info, nullptr, &self->descriptor_set_layout_world_view) != VK_SUCCESS) throw CommandError{ "Failed to create Vulkan descriptor set layout for world view."}; } void unload_descriptor_set_layout_world_view(void *obj) { auto self = static_cast(obj); vkDestroyDescriptorSetLayout( cg_core.vk_device_with_swapchain->device, self->descriptor_set_layout_world_view, nullptr); } void load_pipeline_layout(void *obj) { auto self = static_cast(obj); std::array set_layouts{ self->descriptor_set_layout_world_view, self->descriptor_set_layout_model_instance}; VkPipelineLayoutCreateInfo pipeline_layout_info{}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_info.setLayoutCount = set_layouts.size(); pipeline_layout_info.pSetLayouts = set_layouts.data(); pipeline_layout_info.pushConstantRangeCount = 0; pipeline_layout_info.pPushConstantRanges = nullptr; if(vkCreatePipelineLayout( cg_core.vk_device_with_swapchain->device, &pipeline_layout_info, nullptr, &self->pipeline_layout) != VK_SUCCESS) throw CommandError{"Failed to create Vulkan pipeline layout."}; } void unload_pipeline_layout(void *obj) { auto self = static_cast(obj); vkDestroyPipelineLayout( cg_core.vk_device_with_swapchain->device, self->pipeline_layout, nullptr); } void load_view_projection_uniform_buffer(void *obj) { auto self = static_cast(obj); try { self->ub_view_projection.reserve(cg_core.vk_swapchain->images_count); for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) self->ub_view_projection.emplace_back( cg_core.vk_device_with_swapchain, sizeof(VK::UBOViewProjection)); } catch(const std::exception& e) { throw CommandError{e.what()}; } } void unload_view_projection_uniform_buffer(void *obj) { auto self = static_cast(obj); self->ub_view_projection.clear(); } void load_directional_light_uniform_buffer(void *obj) { auto self = static_cast(obj); try { self->ub_view_projection.reserve(cg_core.vk_swapchain->images_count); for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) self->ub_directional_light.emplace_back( cg_core.vk_device_with_swapchain, sizeof(VK::UBODirectionalLight)); } catch(const std::exception& e) { throw CommandError{e.what()}; } } void unload_directional_light_uniform_buffer(void *obj) { auto self = static_cast(obj); self->ub_directional_light.clear(); } void load_descriptor_pool(void *obj) { auto self = static_cast(obj); VkDescriptorPoolSize descriptor_pool_size{}; descriptor_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptor_pool_size.descriptorCount = self->ub_view_projection.size() + self->ub_directional_light.size(); VkDescriptorPoolCreateInfo pool_info{}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_info.pNext = nullptr; pool_info.flags = 0; pool_info.maxSets = self->ub_view_projection.size() + self->ub_directional_light.size(); pool_info.poolSizeCount = 1; pool_info.pPoolSizes = &descriptor_pool_size; if(vkCreateDescriptorPool( cg_core.vk_device_with_swapchain->device, &pool_info, nullptr, &self->descriptor_pool) != VK_SUCCESS) throw CommandError{"Failed to create a Vulkan descriptor pool."}; } void unload_descriptor_pool(void *obj) { auto self = static_cast(obj); vkDestroyDescriptorPool( cg_core.vk_device_with_swapchain->device, self->descriptor_pool, nullptr); } void load_world_view_descriptor_sets(void *obj) { auto self = static_cast(obj); std::vector layouts( cg_core.vk_swapchain->images_count, self->descriptor_set_layout_world_view); VkDescriptorSetAllocateInfo alloc_info{}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = self->descriptor_pool; alloc_info.descriptorSetCount = self->ub_view_projection.size(); alloc_info.pSetLayouts = layouts.data(); self->world_view_descriptor_sets.resize( cg_core.vk_swapchain->images_count); if(vkAllocateDescriptorSets( cg_core.vk_device_with_swapchain->device, &alloc_info, self->world_view_descriptor_sets.data()) != VK_SUCCESS) throw CommandError{"Failed to create Vulkan world view descriptor set."}; } void load_resources_to_descriptor_sets(void *obj) { auto self = static_cast(obj); for(auto i{0}; i < self->ub_view_projection.size(); i++) { VkDescriptorBufferInfo view_projection_info{}; view_projection_info.buffer = self->ub_view_projection[i].buffer; view_projection_info.offset = 0; view_projection_info.range = sizeof(VK::UBOViewProjection); VkDescriptorBufferInfo directional_light_info{}; directional_light_info.buffer = self->ub_directional_light[i].buffer; directional_light_info.offset = 0; directional_light_info.range = sizeof(VK::UBODirectionalLight); std::array write_descriptors{}; write_descriptors[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_descriptors[0].dstSet = self->world_view_descriptor_sets[i]; write_descriptors[0].dstBinding = 0; write_descriptors[0].dstArrayElement = 0; write_descriptors[0].descriptorCount = 1; write_descriptors[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; write_descriptors[0].pBufferInfo = &view_projection_info; write_descriptors[0].pImageInfo = nullptr; write_descriptors[0].pTexelBufferView = nullptr; write_descriptors[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_descriptors[1].dstSet = self->world_view_descriptor_sets[i]; write_descriptors[1].dstBinding = 1; write_descriptors[1].dstArrayElement = 0; write_descriptors[1].descriptorCount = 1; write_descriptors[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; write_descriptors[1].pBufferInfo = &directional_light_info; write_descriptors[1].pImageInfo = nullptr; write_descriptors[1].pTexelBufferView = nullptr; vkUpdateDescriptorSets( cg_core.vk_device_with_swapchain->device, write_descriptors.size(), write_descriptors.data(), 0, nullptr); } } void load_depth_image(void *obj) { auto self = static_cast(obj); VkExtent3D extent3d{}; extent3d.width = cg_core.screen_width; extent3d.height = cg_core.screen_height; extent3d.depth = 1; try { VK::Image::create( cg_core.vk_device_with_swapchain, &self->depth_image, &self->depth_image_memory, VK_FORMAT_D32_SFLOAT, extent3d, 1, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT ); } catch(VK::Image::Error error) { std::string error_message{"Failed to create depth image → "}; error_message += error.what(); throw CommandError{error_message}; } } void unload_depth_image(void *obj) { auto self = static_cast(obj); vkDestroyImage( cg_core.vk_device_with_swapchain->device, self->depth_image, nullptr); vkFreeMemory( cg_core.vk_device_with_swapchain->device, self->depth_image_memory, nullptr); } void load_depth_image_view(void *obj) { auto self = static_cast(obj); try { VK::Image::create_view( cg_core.vk_device_with_swapchain, &self->depth_image_view, self->depth_image, VK_FORMAT_D32_SFLOAT, VK_IMAGE_ASPECT_DEPTH_BIT); } catch(VK::Image::Error error) { std::string error_message{"Failed to create depth image view → "}; error_message += error.what(); throw CommandError{error_message}; } } void unload_depth_image_view(void *obj) { auto self = static_cast(obj); vkDestroyImageView( cg_core.vk_device_with_swapchain->device, self->depth_image_view, nullptr); } void load_render_pass(void *obj) { auto self = static_cast(obj); std::array attachments{}; // Color attachment. attachments[0].flags = 0; attachments[0].format = cg_core.vk_swapchain->image_format; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // Depth attachment. attachments[1].flags = 0; attachments[1].format = VK_FORMAT_D32_SFLOAT; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference color_attachment_ref{}; color_attachment_ref.attachment = 0; color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference depth_attachment_ref{}; depth_attachment_ref.attachment = 1; depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass = {}; subpass.flags = 0; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.inputAttachmentCount = 0; subpass.pInputAttachments = nullptr; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_attachment_ref; subpass.pResolveAttachments = nullptr; subpass.pDepthStencilAttachment = &depth_attachment_ref; subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = nullptr; VkSubpassDependency dependency = {}; 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_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; VkRenderPassCreateInfo render_pass_info{}; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; render_pass_info.pNext = nullptr; render_pass_info.flags = 0; render_pass_info.attachmentCount = attachments.size(); render_pass_info.pAttachments = attachments.data(); render_pass_info.subpassCount = 1; render_pass_info.pSubpasses = &subpass; render_pass_info.dependencyCount = 1; render_pass_info.pDependencies = &dependency; if(vkCreateRenderPass( cg_core.vk_device_with_swapchain->device, &render_pass_info, nullptr, &self->render_pass) != VK_SUCCESS) throw CommandError{"Failed to create Vulkan Render Pass."}; } void unload_render_pass(void *obj) { auto self = static_cast(obj); vkDestroyRenderPass( cg_core.vk_device_with_swapchain->device, self->render_pass, nullptr); } void load_framebuffer(void *obj) { auto self = static_cast(obj); self->swapchain_framebuffers.resize(cg_core.vk_swapchain->images_count); for (auto i{0}; i < cg_core.vk_swapchain->images_count; i++) { std::array attachments = { cg_core.vk_swapchain->image_views[i], self->depth_image_view }; VkFramebufferCreateInfo framebuffer_info{}; framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebuffer_info.renderPass = self->render_pass; framebuffer_info.attachmentCount = attachments.size(); framebuffer_info.pAttachments = attachments.data(); framebuffer_info.width = cg_core.screen_width; framebuffer_info.height = cg_core.screen_height; framebuffer_info.layers = 1; if(vkCreateFramebuffer( cg_core.vk_device_with_swapchain->device, &framebuffer_info, nullptr, &self->swapchain_framebuffers[i]) != VK_SUCCESS) throw CommandError{"Failed to create Vulkan Framebuffer."}; } } void unload_framebuffer(void *obj) { auto self = static_cast(obj); for(auto framebuffer: self->swapchain_framebuffers) vkDestroyFramebuffer( cg_core.vk_device_with_swapchain->device, framebuffer, nullptr); } void load_pipeline(void *obj) { auto self = static_cast(obj); VkPipelineShaderStageCreateInfo vert_shader_stage_info = {}; vert_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vert_shader_stage_info.pNext = nullptr; vert_shader_stage_info.flags = 0; vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; vert_shader_stage_info.module = cg_core.vk_device_with_swapchain->vert_shader_module; vert_shader_stage_info.pName = "main"; vert_shader_stage_info.pSpecializationInfo = nullptr; VkPipelineShaderStageCreateInfo frag_shader_stage_info = {}; frag_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; frag_shader_stage_info.pNext = nullptr; frag_shader_stage_info.flags = 0; frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; frag_shader_stage_info.module = cg_core.vk_device_with_swapchain->frag_shader_module; frag_shader_stage_info.pName = "main"; frag_shader_stage_info.pSpecializationInfo = nullptr; VkPipelineShaderStageCreateInfo shader_stages[] = { vert_shader_stage_info, frag_shader_stage_info }; VkVertexInputBindingDescription vertex_input_binding{}; vertex_input_binding.binding = 0; vertex_input_binding.stride = sizeof(VK::Vertex); vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; std::array vertex_attribute{}; // Position. vertex_attribute[0].location = 0; vertex_attribute[0].binding = 0; vertex_attribute[0].format = VK_FORMAT_R32G32B32_SFLOAT; vertex_attribute[0].offset = offsetof(VK::Vertex, position); // Normal. vertex_attribute[1].location = 1; vertex_attribute[1].binding = 0; vertex_attribute[1].format = VK_FORMAT_R32G32B32_SFLOAT; vertex_attribute[1].offset = offsetof(VK::Vertex, normal); // Texture coordinate. vertex_attribute[2].location = 2; vertex_attribute[2].binding = 0; vertex_attribute[2].format = VK_FORMAT_R32G32_SFLOAT; vertex_attribute[2].offset = offsetof(VK::Vertex, texture_coord); VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input_info.pNext = nullptr; vertex_input_info.flags = 0; vertex_input_info.vertexBindingDescriptionCount = 1; vertex_input_info.pVertexBindingDescriptions = &vertex_input_binding; vertex_input_info.vertexAttributeDescriptionCount = static_cast(vertex_attribute.size()); vertex_input_info.pVertexAttributeDescriptions = vertex_attribute.data(); VkPipelineInputAssemblyStateCreateInfo input_assembly = {}; input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; input_assembly.pNext = nullptr; input_assembly.flags = 0; input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; input_assembly.primitiveRestartEnable = VK_FALSE; VkViewport viewport = {}; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = static_cast(cg_core.screen_width); viewport.height = static_cast(cg_core.screen_height); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkRect2D scissor = {}; scissor.offset = {0, 0}; scissor.extent = {cg_core.screen_width, cg_core.screen_height}; VkPipelineViewportStateCreateInfo viewport_state = {}; viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_state.pNext = nullptr; viewport_state.flags = 0; viewport_state.viewportCount = 1; viewport_state.pViewports = &viewport; viewport_state.scissorCount = 1; viewport_state.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterizer = {}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.pNext = nullptr; rasterizer.flags = 0; rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.cullMode = VK_CULL_MODE_NONE; 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; VkPipelineMultisampleStateCreateInfo multisampling = {}; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; multisampling.sampleShadingEnable = VK_FALSE; multisampling.minSampleShading = 1.0f; multisampling.pSampleMask = nullptr; multisampling.alphaToCoverageEnable = VK_FALSE; multisampling.alphaToOneEnable = VK_FALSE; VkPipelineDepthStencilStateCreateInfo depth_stencil = {}; 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; depth_stencil.maxDepthBounds = 1.0f; depth_stencil.stencilTestEnable = VK_FALSE; depth_stencil.front = {}; depth_stencil.back = {}; VkPipelineColorBlendAttachmentState color_blend_attachment = {}; 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; VkPipelineColorBlendStateCreateInfo color_blending = {}; color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; color_blending.pNext = nullptr; color_blending.flags = 0; color_blending.logicOpEnable = VK_FALSE; color_blending.logicOp = VK_LOGIC_OP_COPY; color_blending.attachmentCount = 1; 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; VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_LINE_WIDTH }; VkPipelineDynamicStateCreateInfo dynamic_state_info = {}; dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state_info.dynamicStateCount = 2; dynamic_state_info.pDynamicStates = dynamic_states; VkGraphicsPipelineCreateInfo pipeline_info{}; pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_info.pNext = nullptr; pipeline_info.flags = 0; pipeline_info.stageCount = 2; pipeline_info.pStages = shader_stages; pipeline_info.pVertexInputState = &vertex_input_info; pipeline_info.pInputAssemblyState = &input_assembly; pipeline_info.pTessellationState = nullptr; 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_info; pipeline_info.layout = self->pipeline_layout; pipeline_info.renderPass = self->render_pass; pipeline_info.subpass = 0; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_info.basePipelineIndex = -1; if(vkCreateGraphicsPipelines( cg_core.vk_device_with_swapchain->device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &self->graphic_pipeline) != VK_SUCCESS) throw CommandError{"Failed to create graphics pipeline."}; } void unload_pipeline(void *obj) { auto self = static_cast(obj); vkDestroyPipeline( cg_core.vk_device_with_swapchain->device, self->graphic_pipeline, nullptr); } void load_frame_sync(void *obj) { auto self = static_cast(obj); self->image_available_semaphores.resize(self->max_frames_in_flight); self->render_finished_semaphores.resize(self->max_frames_in_flight); self->in_flight_fences.resize(self->max_frames_in_flight); VkSemaphoreCreateInfo semaphore_info = {}; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphore_info.pNext = nullptr; semaphore_info.flags = 0; VkFenceCreateInfo fence_info = {}; fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fence_info.pNext = nullptr; fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; // FIXME: if this loop fails, it will not destroy the semaphores. for(auto i{0}; i < self->max_frames_in_flight; i++) { if(vkCreateSemaphore( cg_core.vk_device_with_swapchain->device, &semaphore_info, nullptr, &self->image_available_semaphores[i]) != VK_SUCCESS || vkCreateSemaphore( cg_core.vk_device_with_swapchain->device, &semaphore_info, nullptr, &self->render_finished_semaphores[i]) != VK_SUCCESS || vkCreateFence(cg_core.vk_device_with_swapchain->device, &fence_info, nullptr, &self->in_flight_fences[i]) != VK_SUCCESS) throw CommandError{"Failed to create semaphores."}; } } void unload_frame_sync(void *obj) { auto self = static_cast(obj); vkDeviceWaitIdle(cg_core.vk_device_with_swapchain->device); for(auto i{0}; i < self->max_frames_in_flight; i++) { vkDestroySemaphore(cg_core.vk_device_with_swapchain->device, self->render_finished_semaphores[i], nullptr); vkDestroySemaphore(cg_core.vk_device_with_swapchain->device, self->image_available_semaphores[i], nullptr); vkDestroyFence(cg_core.vk_device_with_swapchain->device, self->in_flight_fences[i], nullptr); } } void load_queue_family(void *obj) { auto self = static_cast(obj); self->queue_family = cg_core.vk_device_with_swapchain->get_queue_family_with_presentation(); } void load_command_pool(void *obj) { auto self = static_cast(obj); VkCommandPoolCreateInfo create_info{}; create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; create_info.pNext = nullptr; create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; create_info.queueFamilyIndex = self->queue_family->family_index; vkCreateCommandPool( self->queue_family->device->device, &create_info, nullptr, &self->command_pool); } void unload_command_pool(void *obj) { auto self = static_cast(obj); vkWaitForFences(cg_core.vk_device_with_swapchain->device, 2, self->in_flight_fences.data(), VK_TRUE, std::numeric_limits::max()); vkDestroyCommandPool( self->queue_family->device->device, self->command_pool, nullptr); } void load_draw_command_buffer(void *obj) { auto self = static_cast(obj); // FIXME: 3 is a magical number, triple buffering. self->draw_command_buffers.resize(3); VkCommandBufferAllocateInfo allocate_info{}; allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocate_info.pNext = nullptr; allocate_info.commandPool = self->command_pool; allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocate_info.commandBufferCount = self->draw_command_buffers.size(); if(vkAllocateCommandBuffers( self->queue_family->device->device, &allocate_info, self->draw_command_buffers.data()) != VK_SUCCESS) throw CommandError{"Vulkan draw command buffers could not be allocated."}; } const CommandChain loader{ {&load_descriptor_set_layout_model_instance, &unload_descriptor_set_layout_model_instance}, {&load_descriptor_set_layout_world_view, &unload_descriptor_set_layout_world_view}, {&load_pipeline_layout, &unload_pipeline_layout}, {&load_view_projection_uniform_buffer, &unload_view_projection_uniform_buffer}, {&load_directional_light_uniform_buffer, &unload_directional_light_uniform_buffer}, {&load_descriptor_pool, &unload_descriptor_pool}, // By destroying the pool the sets are also destroyed. {&load_world_view_descriptor_sets, nullptr}, {&load_resources_to_descriptor_sets, nullptr}, {&load_depth_image, &unload_depth_image}, {&load_depth_image_view, &unload_depth_image_view}, {&load_render_pass, &unload_render_pass}, {&load_framebuffer, &unload_framebuffer}, {&load_pipeline, &unload_pipeline}, {&load_frame_sync, &unload_frame_sync}, {&load_queue_family, nullptr}, {&load_command_pool, &unload_command_pool}, {&load_draw_command_buffer, nullptr} }; } namespace VK { GraphicsPipeline::GraphicsPipeline(): current_frame{0}, camera_position{std::make_shared(0.0f, 0.0f, 0.0f)}, camera_rotation{std::make_shared(0.0f, 0.0f, 0.0f)}, models_to_draw{cg_core.vk_swapchain->images_count} { loader.execute(this); } GraphicsPipeline::~GraphicsPipeline() { loader.revert(this); } void GraphicsPipeline::draw() { vkWaitForFences(cg_core.vk_device_with_swapchain->device, 1, &this->in_flight_fences[this->current_frame], VK_TRUE, std::numeric_limits::max()); vkResetFences(cg_core.vk_device_with_swapchain->device, 1, &this->in_flight_fences[this->current_frame]); uint32_t image_index; vkAcquireNextImageKHR( cg_core.vk_device_with_swapchain->device, cg_core.vk_swapchain->swapchain, std::numeric_limits::max(), this->image_available_semaphores[this->current_frame], VK_NULL_HANDLE, &image_index); auto &draw_command_buffer = this->draw_command_buffers[this->current_frame]; vkResetCommandBuffer(draw_command_buffer, 0); // Load command. { VkCommandBufferBeginInfo begin_info{}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags = 0; begin_info.pInheritanceInfo = nullptr; if (vkBeginCommandBuffer(draw_command_buffer, &begin_info) != VK_SUCCESS) { throw std::runtime_error{"Failed to beggin draw command buffer."}; } // Dark gray blue. std::array clear_values{}; clear_values[0].color = {0.12f, 0.12f, 0.18f, 1.0f}; clear_values[1].depthStencil = {1.0f, 0}; VkRenderPassBeginInfo render_pass_begin{}; render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_pass_begin.pNext = nullptr; render_pass_begin.renderPass = this->render_pass; render_pass_begin.framebuffer = this->swapchain_framebuffers[image_index]; render_pass_begin.renderArea.offset = {0, 0}; render_pass_begin.renderArea.extent = { cg_core.screen_width, cg_core.screen_height}; render_pass_begin.clearValueCount = clear_values.size(); render_pass_begin.pClearValues = clear_values.data(); vkCmdBeginRenderPass( draw_command_buffer, &render_pass_begin,VK_SUBPASS_CONTENTS_INLINE); VkViewport vk_viewport{}; vk_viewport.width = static_cast(cg_core.screen_width); vk_viewport.height = static_cast(cg_core.screen_height); vk_viewport.minDepth = 0.0f; vk_viewport.maxDepth = 1.0f; vkCmdSetViewport(draw_command_buffer, 0, 1, &vk_viewport); VkRect2D vk_scissor{}; vk_scissor.extent.width = cg_core.screen_width; vk_scissor.extent.height = cg_core.screen_height; vk_scissor.offset.x = 0; vk_scissor.offset.y = 0; vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor); // Draw models for(auto& [model, instances] : this->models_to_draw[this->current_frame]) { // Commands { std::array vk_descriptor_sets{ this->world_view_descriptor_sets[image_index], model->descriptor_sets[image_index]}; VkBuffer vertex_buffers[]{model->vertex_buffer->buffer}; VkDeviceSize offsets[]{0}; vkCmdBindDescriptorSets( draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, this->pipeline_layout, 0, vk_descriptor_sets.size(), vk_descriptor_sets.data(), 0, nullptr); vkCmdBindPipeline( draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, this->graphic_pipeline); vkCmdBindVertexBuffers( draw_command_buffer, 0, 1, vertex_buffers, offsets); vkCmdBindIndexBuffer( draw_command_buffer, model->index_buffer->buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdDrawIndexed( draw_command_buffer, model->index_count, instances.size(), 0, 0, 0); } VK::UBOModelInstance ubo_model_instance; for(int i{0}; i < instances.size(); i++) { // Object matrix. glm::mat4 instance_matrix{1.0f}; instance_matrix = glm::translate( instance_matrix, instances[i].position); instance_matrix = glm::rotate( instance_matrix, instances[i].rotation.x, glm::vec3{1.0, 0.0, 0.0}); instance_matrix = glm::rotate( instance_matrix, instances[i].rotation.y, glm::vec3{0.0, 1.0, 0.0}); instance_matrix = glm::rotate( instance_matrix, instances[i].rotation.z, glm::vec3{0.0, 0.0, 1.0}); ubo_model_instance.model[i] = instance_matrix; } model->ub_model_instance[image_index].copy_data(&ubo_model_instance); } vkCmdEndRenderPass(draw_command_buffer); if(vkEndCommandBuffer(draw_command_buffer) != VK_SUCCESS) throw std::runtime_error{"Failed to end draw command buffer."}; } // Update view projection uniform buffers { VK::UBOViewProjection ubo_view_projection{}; // View matrix. ubo_view_projection.view = glm::mat4{1.0f}; ubo_view_projection.view = glm::translate( ubo_view_projection.view, *this->camera_position); ubo_view_projection.view = glm::rotate( ubo_view_projection.view, this->camera_rotation->y, glm::vec3{0.0, 1.0, 0.0}); ubo_view_projection.view = glm::rotate( ubo_view_projection.view, this->camera_rotation->x, glm::vec3{1.0, 0.0, 0.0}); ubo_view_projection.view = glm::rotate( ubo_view_projection.view, this->camera_rotation->z, glm::vec3{0.0, 0.0, 1.0}); ubo_view_projection.view = glm::inverse(ubo_view_projection.view); // Projection matrix. ubo_view_projection.proj = glm::perspective( glm::radians(45.0f), cg_core.screen_width / static_cast(cg_core.screen_height), 0.1f, 100.0f); ubo_view_projection.proj[1][1] *= -1; ubo_view_projection.ambient_color = glm::vec4{0.25, 0.25, 0.25, 1.0}; this->ub_view_projection[image_index].copy_data(&ubo_view_projection); } // Update directional light uniform buffers { UBODirectionalLight ubo_directional_light{}; ubo_directional_light.direction = glm::vec3{-0.57735, -0.57735, -0.57735}; ubo_directional_light.color = glm::vec4{0.8, 0.8, 0.8, 1.0}; this->ub_directional_light[image_index].copy_data(&ubo_directional_light); } // Submit drawing command. { auto queue{this->queue_family->get_queue()}; VkSemaphore wait_semaphores[]{ this->image_available_semaphores[this->current_frame]}; VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkSemaphore signal_semaphores[]{ this->render_finished_semaphores[this->current_frame]}; VkSubmitInfo submit_info{}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = wait_semaphores; submit_info.pWaitDstStageMask = wait_stages; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &draw_command_buffer; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = signal_semaphores; if(vkQueueSubmit( queue.queue, 1, &submit_info, this->in_flight_fences[this->current_frame]) != VK_SUCCESS) throw std::runtime_error{"Failed to submit draw command buffer."}; VkSwapchainKHR swap_chains[]{cg_core.vk_swapchain->swapchain}; VkPresentInfoKHR present_info{}; present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present_info.pNext = nullptr; present_info.waitSemaphoreCount = 1; present_info.pWaitSemaphores = signal_semaphores; present_info.swapchainCount = 1; present_info.pSwapchains = swap_chains; present_info.pImageIndices = &image_index; present_info.pResults = nullptr; vkQueuePresentKHR(queue.queue, &present_info); // Prepare for the next frame. this->current_frame = (this->current_frame + 1) % this->max_frames_in_flight; this->models_to_draw[this->current_frame].clear(); } } }