/* * 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 "renderer.hpp" #include "../core.hpp" namespace { 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_frame_sync, &unload_frame_sync}, {&load_queue_family, nullptr}, {&load_command_pool, &unload_command_pool}, {&load_draw_command_buffer, nullptr} }; } namespace VK { Renderer::Renderer(): current_frame{0} { loader.execute(this); } Renderer::~Renderer() { loader.revert(this); } void Renderer::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); VkCommandBuffer draw_command_buffer = this->draw_command_buffers[this->current_frame]; vkResetCommandBuffer(draw_command_buffer, 0); auto next_frame = this->current_frame + 1; if(next_frame == this->max_frames_in_flight) next_frame = 0; // Begin command buffer. { 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."}; } cg_core.vk_graphics_pipeline_3d->draw( draw_command_buffer, current_frame, next_frame, image_index); cg_core.vk_graphics_pipeline_2d->draw( draw_command_buffer, current_frame, next_frame, image_index); // End command buffer. if(vkEndCommandBuffer(draw_command_buffer) != VK_SUCCESS) throw std::runtime_error{"Failed to end draw command buffer."}; // 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 = next_frame; } }