diff options
Diffstat (limited to 'src/blu_cat/gra/renderer.cpp')
-rw-r--r-- | src/blu_cat/gra/renderer.cpp | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/src/blu_cat/gra/renderer.cpp b/src/blu_cat/gra/renderer.cpp new file mode 100644 index 0000000..ad66003 --- /dev/null +++ b/src/blu_cat/gra/renderer.cpp @@ -0,0 +1,425 @@ +/* + * Copyright 2022-2024 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 <array> + +#include "../int/core.hpp" +#include "uniform_data_object.hpp" + +namespace +{ + +void +load_descriptor_pool(void *obj) +{ + auto self = static_cast<BluCat::GRA::Renderer*>(obj); + + uint32_t uniform_buffer_count = 0; + for(auto &view : self->views_3d) + uniform_buffer_count += (view->ub_3d.size() + view->ub_2d.size()); + for(auto &view : self->views_2d) + uniform_buffer_count += (view->ub_2d.size()); + + VkDescriptorPoolSize descriptor_pool_size{}; + descriptor_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_pool_size.descriptorCount = uniform_buffer_count; + + VkDescriptorPoolCreateInfo pool_info{}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = nullptr; + pool_info.flags = 0; + pool_info.maxSets = uniform_buffer_count; + pool_info.poolSizeCount = 1; + pool_info.pPoolSizes = &descriptor_pool_size; + + if(vkCreateDescriptorPool( + BluCat::INT::core.vk_device_with_swapchain->device, &pool_info, nullptr, + &self->descriptor_pool) != VK_SUCCESS) + throw CommandError{"Failed to create a Vulkan descriptor pool."}; + + for(auto &view : self->views_3d) + view->load_descriptor_sets(self->descriptor_pool); + for(auto &view : self->views_2d) + view->load_descriptor_sets(self->descriptor_pool); +} + +void +unload_descriptor_pool(void *obj) +{ + auto self = static_cast<BluCat::GRA::Renderer*>(obj); + + for(auto &view : self->views_3d) view->unload_descriptor_sets(); + for(auto &view : self->views_2d) view->unload_descriptor_sets(); + + vkDestroyDescriptorPool( + BluCat::INT::core.vk_device_with_swapchain->device, self->descriptor_pool, + nullptr); +} + +void +load_queue_family(void *obj) +{ + auto self = static_cast<BluCat::GRA::Renderer*>(obj); + + self->queue_family = + BluCat::INT::core.vk_device_with_swapchain->get_queue_family_with_presentation(); +} + +void +load_command_pool(void *obj) +{ + auto self = static_cast<BluCat::GRA::Renderer*>(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<BluCat::GRA::Renderer*>(obj); + + self->wait_frame(); + vkDestroyCommandPool( + self->queue_family->device->device, self->command_pool, nullptr); +} + +void +load_draw_command_buffer(void *obj) +{ + auto self = static_cast<BluCat::GRA::Renderer*>(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_pool, &unload_descriptor_pool}, + {&load_queue_family, nullptr}, + {&load_command_pool, &unload_command_pool}, + {&load_draw_command_buffer, nullptr} +}; + +} + +namespace BluCat::GRA +{ + +Renderer::Renderer(std::vector<std::shared_ptr<View2D>> views_2d, + std::vector<std::shared_ptr<View3D>> views_3d): + skeletal_models_to_draw{BluCat::INT::core.vk_swapchain->images_count}, + static_models_to_draw{BluCat::INT::core.vk_swapchain->images_count}, + sprites_3d_to_draw{BluCat::INT::core.vk_swapchain->images_count}, + views_2d{views_2d}, + views_3d{views_3d} +{ + loader.execute(this); +} + +Renderer::Renderer(std::initializer_list<std::shared_ptr<View2D>> views_2d, + std::initializer_list<std::shared_ptr<View3D>> views_3d): + Renderer(std::vector(views_2d), std::vector(views_3d)) +{ +} + +Renderer::~Renderer() +{ + loader.revert(this); +} + +// FIXME: this is a workaround to prevent a code to free some resource while +// it still being rendered. +void +Renderer::wait_frame() +{ + vkWaitForFences(BluCat::INT::core.vk_device_with_swapchain->device, + BluCat::GRA::Swapchain::max_frames_in_flight, + BluCat::INT::core.vk_swapchain->in_flight_fences.data(), VK_TRUE, + std::numeric_limits<uint64_t>::max()); +} + +void +Renderer::draw() +{ + auto fence_status = vkGetFenceStatus( + BluCat::INT::core.vk_device_with_swapchain->device, + BluCat::INT::core.vk_swapchain->in_flight_fences[ + BluCat::INT::core.vk_swapchain->current_frame]); + + if(fence_status == VK_SUCCESS) + { + auto next_frame = BluCat::INT::core.vk_swapchain->current_frame + 1; + if(next_frame == Swapchain::max_frames_in_flight) next_frame = 0; + + vkResetFences(INT::core.vk_device_with_swapchain->device, 1, + &BluCat::INT::core.vk_swapchain->in_flight_fences[ + BluCat::INT::core.vk_swapchain->current_frame]); + + uint32_t image_index; + vkAcquireNextImageKHR( + BluCat::INT::core.vk_device_with_swapchain->device, + BluCat::INT::core.vk_swapchain->swapchain, + std::numeric_limits<uint64_t>::max(), + BluCat::INT::core.vk_swapchain->image_available_semaphores[ + BluCat::INT::core.vk_swapchain->current_frame], VK_NULL_HANDLE, &image_index); + + VkCommandBuffer draw_command_buffer = + this->draw_command_buffers[BluCat::INT::core.vk_swapchain->current_frame]; + vkResetCommandBuffer(draw_command_buffer, 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."}; + } + + // 3D drawing. + { + // Dark gray blue. + std::array<VkClearValue, 2> clear_values{}; + clear_values[0].color = {0.12f, 0.12f, 0.18f, 1.0f}; + clear_values[1].depthStencil = {1.0f, 0}; + + { // Update world uniform buffer + UDOWorld3D_Vert ubo_world_3d_vert{}; + ubo_world_3d_vert.ambient_light_color = + glm::vec4{0.25, 0.25, 0.25, 1.0}; + BluCat::INT::core.vk_light->ub_world_vert[image_index].copy_data( + &ubo_world_3d_vert); + + UDOWorld3D_Frag ubo_world_3d_frag{}; + ubo_world_3d_frag.directional_light_direction = + glm::vec3{-0.57735, 0.57735, -0.57735}; + ubo_world_3d_frag.directional_light_color = + glm::vec4{0.8, 0.8, 0.8, 1.0}; + BluCat::INT::core.vk_light->ub_world_frag[image_index].copy_data( + &ubo_world_3d_frag); + } + + VkRenderPassBeginInfo render_pass_begin{}; + render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_begin.pNext = nullptr; + render_pass_begin.renderPass = BluCat::INT::core.vk_render_pass->pipeline_3d; + render_pass_begin.framebuffer = + BluCat::INT::core.vk_framebuffer->pipeline_3d[image_index]; + render_pass_begin.renderArea.offset = {0, 0}; + render_pass_begin.renderArea.extent = { + static_cast<uint32_t>(BluCat::INT::core.display_width), + static_cast<uint32_t>(BluCat::INT::core.display_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); + } + + for(auto &view: this->views_3d) + { + { // Set viewport + VkViewport vk_viewport{}; + vk_viewport.x = view->region.x; + vk_viewport.y = view->region.y; + vk_viewport.width = view->region.z; + vk_viewport.height = view->region.w; + vk_viewport.minDepth = 0.0f; + vk_viewport.maxDepth = 1.0f; + vkCmdSetViewport(draw_command_buffer, 0, 1, &vk_viewport); + + VkRect2D vk_scissor{}; + vk_scissor.offset.x = static_cast<int32_t>(view->region.x); + vk_scissor.offset.y = static_cast<int32_t>(view->region.y); + vk_scissor.extent.width = static_cast<uint32_t>(view->region.z); + vk_scissor.extent.height = static_cast<uint32_t>(view->region.w); + vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor); + } + + BluCat::INT::core.vk_graphics_pipeline_3d->draw( + view, draw_command_buffer, BluCat::INT::core.vk_swapchain->current_frame, + image_index); + + BluCat::INT::core.vk_graphics_pipeline_sprite_3d->draw( + view, draw_command_buffer, BluCat::INT::core.vk_swapchain->current_frame, + image_index); + + BluCat::INT::core.vk_graphics_pipeline_3d_skeletal->draw( + view, draw_command_buffer, BluCat::INT::core.vk_swapchain->current_frame, + image_index); + + { // Update view uniform buffers + BluCat::GRA::UDOView3D ubo_view_3d{}; + + // View matrix. + glm::mat4 translation_matrix{1.0f}; + translation_matrix = glm::translate( + translation_matrix, *view->camera_position); + glm::mat4 rotation_matrix{glm::toMat4(*view->camera_orientation)}; + ubo_view_3d.view = glm::inverse(translation_matrix * rotation_matrix); + + // Projection matrix. + ubo_view_3d.proj = glm::perspective( + glm::radians(view->field_of_view), + view->region.z / view->region.w, + 0.1f, 100.0f); + + view->ub_3d[image_index].copy_data(&ubo_view_3d); + } + } + + vkCmdEndRenderPass(draw_command_buffer); + + { // 2D render pass + VkRenderPassBeginInfo render_pass_begin{}; + render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_begin.pNext = nullptr; + render_pass_begin.renderPass = BluCat::INT::core.vk_render_pass->pipeline_2d; + render_pass_begin.framebuffer = + BluCat::INT::core.vk_framebuffer->pipeline_2d[image_index]; + render_pass_begin.renderArea.offset = {0, 0}; + render_pass_begin.renderArea.extent = { + static_cast<uint32_t>(BluCat::INT::core.display_width), + static_cast<uint32_t>(BluCat::INT::core.display_height)}; + render_pass_begin.clearValueCount = 0; + render_pass_begin.pClearValues = nullptr; + + vkCmdBeginRenderPass( + draw_command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); + + } + + { // 2D solid drawing + for(auto &view: this->views_2d) + BluCat::INT::core.vk_graphics_pipeline_2d_solid->draw( + view, draw_command_buffer, BluCat::INT::core.vk_swapchain->current_frame, + next_frame, image_index); + + for(auto &view: this->views_3d) + BluCat::INT::core.vk_graphics_pipeline_2d_solid->draw( + view, draw_command_buffer, BluCat::INT::core.vk_swapchain->current_frame, + next_frame, image_index); + } + + { // 2D wired drawing + for(auto &view: this->views_2d) + BluCat::INT::core.vk_graphics_pipeline_2d_wired->draw( + view, draw_command_buffer, BluCat::INT::core.vk_swapchain->current_frame, + next_frame, image_index); + + for(auto &view: this->views_3d) + BluCat::INT::core.vk_graphics_pipeline_2d_wired->draw( + view, draw_command_buffer, BluCat::INT::core.vk_swapchain->current_frame, + next_frame, image_index); + } + + vkCmdEndRenderPass(draw_command_buffer); + + // 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[]{ + BluCat::INT::core.vk_swapchain->image_available_semaphores[ + BluCat::INT::core.vk_swapchain->current_frame]}; + VkPipelineStageFlags wait_stages[] = + {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + VkSemaphore signal_semaphores[]{ + BluCat::INT::core.vk_swapchain->render_finished_semaphores[ + BluCat::INT::core.vk_swapchain->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, BluCat::INT::core.vk_swapchain->in_flight_fences[ + BluCat::INT::core.vk_swapchain->current_frame]) != VK_SUCCESS) + throw std::runtime_error{"Failed to submit draw command buffer."}; + + VkSwapchainKHR swap_chains[]{BluCat::INT::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->skeletal_models_to_draw[next_frame].clear(); + this->static_models_to_draw[next_frame].clear(); + this->sprites_3d_to_draw[next_frame].clear(); + BluCat::INT::core.vk_swapchain->current_frame = next_frame; + } + } + else + { + // Clear images for the current frame because we are skipping this frame. + this->skeletal_models_to_draw[ + BluCat::INT::core.vk_swapchain->current_frame].clear(); + this->static_models_to_draw[ + BluCat::INT::core.vk_swapchain->current_frame].clear(); + this->sprites_3d_to_draw[ + BluCat::INT::core.vk_swapchain->current_frame].clear(); + for(auto &view: this->views_2d) + view->sprites_to_draw[BluCat::INT::core.vk_swapchain->current_frame].clear(); + for(auto &view: this->views_3d) + view->sprites_to_draw[BluCat::INT::core.vk_swapchain->current_frame].clear(); + } +} + +} |