#include <stdafx.h> #include "Context.h" #include "IO.h" #include "VulkanWindow.h" #include "SwapChain.h" #include <algorithm> #include <numeric> namespace fpr { Version::Version(uint32_t variant, uint32_t major, uint32_t minor, uint32_t patch): m_variant(variant), m_major(major), m_minor(minor), m_patch(patch) { } uint32_t Version::MakeVersion() const FPR_NOEXCEPT { return VK_MAKE_API_VERSION(m_variant, m_major, m_minor, m_patch); } VulkanWindow* Context::s_window; std::string Context::s_app_name; fpr::Version Context::s_app_version; std::string Context::s_engine_name; fpr::Version Context::s_engine_version; fpr::Version Context::s_vulkan_version; vk::ApplicationInfo Context::ApplicationInfo( const char* application_name, const Version& app_version, const char* engine_name, const Version& engine_version, const Version& api_version) FPR_NOEXCEPT { vk::ApplicationInfo app_info{}; app_info.pApplicationName = application_name; app_info.applicationVersion = app_version.MakeVersion(); app_info.pEngineName = engine_name; app_info.engineVersion = engine_version.MakeVersion(); app_info.apiVersion = api_version.MakeVersion(); return app_info; } std::vector<const char*> Context::GetRequiredExtensions(const std::vector<const char*>& validation_layers) FPR_NOEXCEPT { auto [enum_result, available_layers] = vk::enumerateInstanceLayerProperties(); // TODO: assert enum result if(validation_layers_enabled) { [[maybe_unused]] bool validation_supported = IsValidationLayersValid(validation_layers, available_layers); assert(("Validation layers not supported but requested", validation_supported)); } uint32_t extension_count = 0; const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&extension_count); std::vector<const char*> result(glfw_extensions, glfw_extensions + extension_count); if(validation_layers_enabled) { result.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } return result; } inline bool Context::IsValidationLayersValid( const std::vector<const char*>& validation_layers, const std::vector<vk::LayerProperties>& available_layers) FPR_NOEXCEPT { for(auto& validation_layer : validation_layers) { auto layer_name_matches = [&](const auto& layer) { return strcmp(layer.layerName, validation_layer) == 0; }; if(std::none_of(available_layers.begin(), available_layers.end(), layer_name_matches)) return false; } return true; } bool Context::IsVulkanSupported() const FPR_NOEXCEPT { auto [enum_result, ext_props] = vk::enumerateInstanceExtensionProperties(); // TODO: assert std::vector<vk::ExtensionProperties> extensions = ext_props; for(const auto extensionToCheck : m_extensions) { bool hasExtensions = false; for(const auto& ext : extensions) { if(strcmp(extensionToCheck, ext.extensionName) == 0) { hasExtensions = true; break; } } if(!hasExtensions) return false; } return true; } const vk::Instance& fpr::Context::GetVulkanInstance() const FPR_NOEXCEPT { return m_instance.get(); } std::function<Context::PipelineCreationData(void)> Context::DEFAULT_PIPELINE_CREATION_CALLBACK = []() { fpr::Context::PipelineCreationData pipelines; pipelines.emplace_back( EPipelineType::EPT_Graphics, "Graphics", fpr::Pipeline::GetDefaultGraphicsPipeline(), tl::nullopt); pipelines.emplace_back(EPipelineType::EPT_Graphics, "Depth", fpr::Pipeline::GetDepthPassPipeline(), "Graphics"); pipelines.emplace_back( EPipelineType::EPT_Compute, "LightCulling", fpr::Pipeline::GetLightCullingPipeline(), tl::nullopt); return pipelines; }; void Context::RecreateSwapchain() { m_old_swapchain = std::move(m_swapchain->GetUniqueHandle()); m_swapchain.reset(); m_swapchain = std::make_unique<fpr::SwapChain>(m_device.get(), *m_surface, std::make_optional(std::move(m_old_swapchain))); m_swapchain->CreateCommandBuffers(); } void Context::CreatePipelines(std::function<PipelineCreationData(void)> fn) { auto pipeline_configs = std::invoke(std::bind(fn)); for(auto& pipeline_config : pipeline_configs) { auto& [pipeline_type, name, settings, parent_name] = pipeline_config; switch(pipeline_type) { case EPipelineType::EPT_Graphics: { fpr::Pipeline* parent_pipeline = nullptr; if(parent_name != tl::nullopt) parent_pipeline = m_render_graph->GraphicsPipelines.Get(*parent_name); auto pipeline = std::make_unique<fpr::Pipeline>(settings, parent_pipeline); m_render_graph->GraphicsPipelines.Add(std::string(name), pipeline); break; } case EPipelineType::EPT_Compute: { fpr::Pipeline* parent_pipeline = nullptr; if(parent_name != tl::nullopt) parent_pipeline = m_render_graph->ComputePipelines.Get(*parent_name); auto pipeline = std::make_unique<fpr::Pipeline>(settings, parent_pipeline, EPipelineType::EPT_Compute); m_render_graph->ComputePipelines.Add(std::string(name), pipeline); break; } } } } void Context::CreateSingleTimeCommandBuffer() { vk::CommandBufferAllocateInfo cmd_buffer_alloc_info{}; cmd_buffer_alloc_info.level = vk::CommandBufferLevel::ePrimary; cmd_buffer_alloc_info.commandPool = GetGraphicsCmdPool(); cmd_buffer_alloc_info.commandBufferCount = 1; auto [cmd_buff_alloc_result, cmd_buff] = m_device->GetLogicalDeviceHandle().allocateCommandBuffersUnique(cmd_buffer_alloc_info); // todo: assert m_single_time_cmd_buffer = std::move(cmd_buff[0]); } Context& Context::GetImpl( VulkanWindow* Window, const std::string& app_name, fpr::Version app_version, const std::string& engine_name, fpr::Version engine_version, fpr::Version vulkan_version) { static Context context = fpr::Context(Window, app_name, app_version, engine_name, engine_version, vulkan_version); return context; } void fpr::Context::CreateModelDescriptorSet() { auto& device = GetDevice()->GetLogicalDeviceHandle(); for(size_t i = 0; i < GetSwapChain()->GetMaxFrameIdx(); ++i) { auto layout = m_render_graph->DescriptorSetLayouts.Get("Model"); vk::DescriptorSetAllocateInfo desc_set_alloc_info; desc_set_alloc_info.descriptorPool = GetDescriptorPool(); desc_set_alloc_info.setSetLayouts(layout); auto [result_signal, descriptor_sets] = device.allocateDescriptorSetsUnique(desc_set_alloc_info); assert(("Failed to allocate descriptor sets!", result_signal == vk::Result::eSuccess)); m_render_graph->DescriptorSets.Add("Model" + std::to_string(i), descriptor_sets[0]); vk::DescriptorBufferInfo uniform_buffer_info; uniform_buffer_info.buffer = m_render_graph->Buffers.Get("Model" + std::to_string(i))->GetBuffer(); uniform_buffer_info.offset = 0; uniform_buffer_info.range = GetRequiredDynamicAlignment(); std::vector<vk::WriteDescriptorSet> desc_writes = {}; vk::WriteDescriptorSet model_write_desc_set; model_write_desc_set.dstSet = m_render_graph->DescriptorSets.Get("Model" + std::to_string(i)); model_write_desc_set.dstBinding = 0; model_write_desc_set.dstArrayElement = 0; model_write_desc_set.descriptorCount = 1; model_write_desc_set.descriptorType = vk::DescriptorType::eUniformBufferDynamic; model_write_desc_set.pBufferInfo = &uniform_buffer_info; desc_writes.emplace_back(model_write_desc_set); device.updateDescriptorSets(desc_writes, nullptr); } } void fpr::Context::CreateCameraDescriptorSet() { auto& device = GetDevice()->GetLogicalDeviceHandle(); for(size_t i = 0; i < GetSwapChain()->GetMaxFrameIdx(); ++i) { auto layout = m_render_graph->DescriptorSetLayouts.Get("Camera"); vk::DescriptorSetAllocateInfo desc_set_alloc_info; desc_set_alloc_info.descriptorPool = GetDescriptorPool(); desc_set_alloc_info.setSetLayouts(layout); auto [result_signal, descriptor_sets] = device.allocateDescriptorSetsUnique(desc_set_alloc_info); assert(("Failed to allocate descriptor sets!", result_signal == vk::Result::eSuccess)); m_render_graph->DescriptorSets.Add("Camera" + std::to_string(i), descriptor_sets[0]); vk::DescriptorBufferInfo uniform_buffer_info; uniform_buffer_info.buffer = m_render_graph->Buffers.Get("Camera" + std::to_string(i))->GetBuffer(); uniform_buffer_info.offset = 0; uniform_buffer_info.range = sizeof(UBOPerFrame); std::vector<vk::WriteDescriptorSet> desc_writes = {}; vk::WriteDescriptorSet camera_write_desc_set; camera_write_desc_set.dstSet = m_render_graph->DescriptorSets.Get("Camera" + std::to_string(i)); camera_write_desc_set.dstBinding = 0; camera_write_desc_set.dstArrayElement = 0; camera_write_desc_set.descriptorCount = 1; camera_write_desc_set.descriptorType = vk::DescriptorType::eUniformBuffer; camera_write_desc_set.pBufferInfo = &uniform_buffer_info; desc_writes.emplace_back(camera_write_desc_set); device.updateDescriptorSets(desc_writes, nullptr); } } void fpr::Context::CreateCameraDescriptorSetLayout() { auto& device = GetDevice()->GetLogicalDeviceHandle(); const vk::ShaderStageFlags camera_stage_flags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eCompute; vk::DescriptorSetLayoutBinding camera_desc_set_layout_bind; camera_desc_set_layout_bind.binding = 0; camera_desc_set_layout_bind.descriptorType = vk::DescriptorType::eUniformBuffer; camera_desc_set_layout_bind.descriptorCount = 1; camera_desc_set_layout_bind.stageFlags = camera_stage_flags; vk::DescriptorSetLayoutCreateInfo create_info; create_info.setBindings(camera_desc_set_layout_bind); auto [layout_result, layout] = device.createDescriptorSetLayoutUnique(create_info); assert(layout_result == vk::Result::eSuccess); m_render_graph->DescriptorSetLayouts.Add("Camera", std::move(layout)); } void fpr::Context::CreateModelDescriptorSetLayout() { auto& device = GetDevice()->GetLogicalDeviceHandle(); const vk::ShaderStageFlags model_stage_flags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eCompute; vk::DescriptorSetLayoutBinding model_desc_set_layout_bind; model_desc_set_layout_bind.binding = 0; model_desc_set_layout_bind.descriptorType = vk::DescriptorType::eUniformBufferDynamic; model_desc_set_layout_bind.descriptorCount = 1; model_desc_set_layout_bind.stageFlags = model_stage_flags; vk::DescriptorSetLayoutCreateInfo create_info; create_info.setBindings(model_desc_set_layout_bind); auto [layout_result, layout] = device.createDescriptorSetLayoutUnique(create_info); assert(layout_result == vk::Result::eSuccess); m_render_graph->DescriptorSetLayouts.Add("Model", std::move(layout)); } void fpr::Context::CreateDepthSamplerLayout() { auto& device = GetDevice()->GetLogicalDeviceHandle(); const vk::ShaderStageFlags depth_sampler_flags = vk::ShaderStageFlagBits::eCompute; vk::DescriptorSetLayoutBinding model_desc_set_layout_bind; model_desc_set_layout_bind.binding = 0; model_desc_set_layout_bind.descriptorType = vk::DescriptorType::eCombinedImageSampler; model_desc_set_layout_bind.descriptorCount = 1; model_desc_set_layout_bind.stageFlags = depth_sampler_flags; vk::DescriptorSetLayoutCreateInfo create_info; create_info.setBindings(model_desc_set_layout_bind); auto [layout_result, layout] = device.createDescriptorSetLayoutUnique(create_info); assert(layout_result == vk::Result::eSuccess); m_render_graph->DescriptorSetLayouts.Add("DepthSampler", std::move(layout)); } void fpr::Context::CreateDepthSamplerDescriptorSet() { auto& device = GetDevice()->GetLogicalDeviceHandle(); auto layout = m_render_graph->DescriptorSetLayouts.Get("DepthSampler"); vk::DescriptorSetAllocateInfo desc_set_alloc_info; desc_set_alloc_info.descriptorPool = GetDescriptorPool(); desc_set_alloc_info.setSetLayouts(layout); auto [result_signal, descriptor_sets] = device.allocateDescriptorSetsUnique(desc_set_alloc_info); assert(("Failed to allocate descriptor sets!", result_signal == vk::Result::eSuccess)); m_render_graph->DescriptorSets.Add("DepthSampler", descriptor_sets[0]); vk::DescriptorImageInfo depth_sampler_info; depth_sampler_info.setSampler(m_render_graph->Samplers.Get("Sampler")); depth_sampler_info.setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal); depth_sampler_info.setImageView(GetSwapChain()->GetDepthBuffer()->GetDepthBufferView()); std::vector<vk::WriteDescriptorSet> desc_writes = {}; vk::WriteDescriptorSet depth_sampler_write; depth_sampler_write.dstSet = m_render_graph->DescriptorSets.Get("Sampler"); depth_sampler_write.dstBinding = 0; depth_sampler_write.dstArrayElement = 0; depth_sampler_write.descriptorCount = 1; depth_sampler_write.descriptorType = vk::DescriptorType::eCombinedImageSampler; depth_sampler_write.setImageInfo(depth_sampler_info); desc_writes.emplace_back(depth_sampler_write); device.updateDescriptorSets(desc_writes, nullptr); } void fpr::Context::CreateDescriptorSets() { CreateModelDescriptorSetLayout(); CreateCameraDescriptorSetLayout(); CreateModelDescriptorSet(); CreateCameraDescriptorSet(); } Context& Context::Get() { return GetImpl(s_window, s_app_name, s_app_version, s_engine_name, s_engine_version, s_vulkan_version); } void Context::CreateSceneBuffers() { vk::DeviceSize min_alignment = m_device->GetPhysicalDeviceHandle().getProperties().limits.minUniformBufferOffsetAlignment; uint32_t dynamic_alignment = (uint32_t)(m_dynamic_alignment + min_alignment - 1) & ~(min_alignment - 1); m_dynamic_alignment = dynamic_alignment; size_t buffer_size = dynamic_alignment * MAX_MESHES; for(size_t i = 0; i < m_swapchain->GetMaxFrameIdx(); ++i) { PER_MODEL_UBO.model_matrix = (glm::mat4*)_aligned_malloc(buffer_size, dynamic_alignment); m_render_graph->Buffers.Add( "Model" + std::to_string(i), std::make_unique<fpr::Buffer>( buffer_size, vk::BufferUsageFlagBits::eUniformBuffer, vk::MemoryPropertyFlagBits::eHostVisible)); m_render_graph->Buffers.Add( "Camera" + std::to_string(i), std::make_unique<fpr::Buffer>( sizeof(UBOPerFrame), vk::BufferUsageFlagBits::eUniformBuffer, vk::MemoryPropertyFlagBits::eHostVisible)); } } void Context::Init( VulkanWindow* Window, const std::string& app_name, fpr::Version app_version, const std::string& engine_name, fpr::Version engine_version, fpr::Version vulkan_version) { s_window = Window; s_app_name = app_name; s_app_version = app_version; s_engine_name = engine_name; s_engine_version = engine_version; s_vulkan_version = vulkan_version; auto& context = GetImpl(Window, app_name, app_version, engine_name, engine_version, vulkan_version); context.CreateGraphicsCmdPool(); context.CreateComputeCmdPool(); context.CreateSwapChain(); context.GetSwapChain()->CreateCommandBuffers(); context.CreateDescriptorPools(); context.CreateTextureSampler(); context.CreateSceneBuffers(); context.CreateDescriptorSets(); fpr::PointLight::CreateDescriptorSet(); context.CreatePipelines(DEFAULT_PIPELINE_CREATION_CALLBACK); } VulkanWindow* Context::GetWindow() { return m_window; } SwapChain* Context::GetSwapChain() { return m_swapchain.get(); } void Context::CreateGraphicsCmdPool() { vk::CommandPoolCreateInfo command_pool_create_info; command_pool_create_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; command_pool_create_info.queueFamilyIndex = m_device->GetGraphicsQueueIndex(); auto [cmd_pool_result, cmd_pool] = m_device->GetLogicalDeviceHandle().createCommandPoolUnique(command_pool_create_info); assert(("Failed to create command pool!", cmd_pool_result == vk::Result::eSuccess)); m_command_pool = std::move(cmd_pool); CreateSingleTimeCommandBuffer(); } void Context::CreateSwapChain() { m_swapchain = std::make_unique<SwapChain>(m_device.get(), *m_surface); } void Context::CreateComputeCmdPool() { vk::CommandPoolCreateInfo command_pool_create_info; command_pool_create_info.queueFamilyIndex = m_device->GetGraphicsQueueIndex(); command_pool_create_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; auto [cmd_pool_result, cmd_pool] = m_device->GetLogicalDeviceHandle().createCommandPoolUnique(command_pool_create_info); assert(("Failed to create command pool!", cmd_pool_result == vk::Result::eSuccess)); m_compute_comand_pool = std::move(cmd_pool); } void Context::CreateDescriptorPools() { // TODO: FIXME std::array<vk::DescriptorPoolSize, 4> pool_sizes; pool_sizes[0].type = vk::DescriptorType::eUniformBuffer; pool_sizes[0].descriptorCount = 2; pool_sizes[1].type = vk::DescriptorType::eUniformBufferDynamic; pool_sizes[1].descriptorCount = 2; pool_sizes[2].type = vk::DescriptorType::eCombinedImageSampler; pool_sizes[2].descriptorCount = MAX_TEXTURES + 1; pool_sizes[3].type = vk::DescriptorType::eStorageBuffer; pool_sizes[3].descriptorCount = 4; vk::DescriptorPoolCreateInfo pool_info = {}; pool_info.poolSizeCount = (uint32_t)pool_sizes.size(); pool_info.pPoolSizes = pool_sizes.data(); pool_info.maxSets = std::accumulate( pool_sizes.begin(), pool_sizes.end(), 0, [&](uint32_t i, const vk::DescriptorPoolSize& dps) -> uint32_t { return dps.descriptorCount + i; }); pool_info.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet; auto [pool_result, pool] = m_device->GetLogicalDeviceHandle().createDescriptorPoolUnique(pool_info); assert(pool_result == vk::Result::eSuccess); m_descriptor_pool = std::move(pool); } void Context::CreateTextureSampler() { vk::SamplerCreateInfo create_info; create_info.setMagFilter(vk::Filter::eLinear); create_info.setMinFilter(vk::Filter::eLinear); create_info.setUnnormalizedCoordinates(false); create_info.setMipmapMode(vk::SamplerMipmapMode::eLinear); create_info.setMaxLod(VK_LOD_CLAMP_NONE); create_info.borderColor = vk::BorderColor::eFloatTransparentBlack; create_info.setAnisotropyEnable(true); create_info.setMinLod(0.0f); create_info.setMaxAnisotropy(16.0f); create_info.compareOp = vk::CompareOp::eAlways; auto [sampler_result, sampler] = m_device->GetLogicalDeviceHandle().createSamplerUnique(create_info); assert(sampler_result == vk::Result::eSuccess); m_render_graph->Samplers.Add("Sampler", std::move(sampler)); vk::DescriptorSetLayoutBinding sampler_binding; sampler_binding.setBinding(0); sampler_binding.setDescriptorType(vk::DescriptorType::eCombinedImageSampler); sampler_binding.setDescriptorCount(1); sampler_binding.setStageFlags(vk::ShaderStageFlagBits::eFragment); vk::DescriptorSetLayoutCreateInfo sampler_layout_info; sampler_layout_info.setBindings(sampler_binding); auto [layout_result, sampler_set_layout] = m_device->GetLogicalDeviceHandle().createDescriptorSetLayoutUnique(sampler_layout_info); assert(layout_result == vk::Result::eSuccess); m_render_graph->DescriptorSetLayouts.Add("Sampler", std::move(sampler_set_layout)); } vk::CommandPool& Context::GetGraphicsCmdPool() FPR_NOEXCEPT { return *m_command_pool; } vk::DescriptorPool& Context::GetDescriptorPool() { return m_descriptor_pool.get(); } vk::SurfaceKHR& Context::GetSurface() FPR_NOEXCEPT { return m_surface.get(); } Device* Context::GetDevice() const FPR_NOEXCEPT { return m_device.get(); } uint32_t Context::GetRequiredDynamicAlignment() const { return m_dynamic_alignment; } void Context::SetDynamicAlignment(uint32_t alignment) { m_dynamic_alignment = alignment; } vk::Instance& Context::GetInstance() { return m_instance.get(); } vk::CommandBuffer& Context::BeginSingleTimeCommands() { vk::CommandBufferBeginInfo cmd_buffer_begin_info{}; cmd_buffer_begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit; [[maybe_unused]] auto result = m_single_time_cmd_buffer->begin(cmd_buffer_begin_info); assert(("Failed to begin single time submission command buffer.", result == vk::Result::eSuccess)); return *m_single_time_cmd_buffer; } void Context::EndSingleTimeCommands() { [[maybe_unused]] auto result = m_single_time_cmd_buffer->end(); assert(("Failed to end single time submission command buffer.", result == vk::Result::eSuccess)); vk::SubmitInfo submit_info{}; submit_info.pNext = nullptr; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &m_single_time_cmd_buffer.get(); submit_info.pSignalSemaphores = nullptr; submit_info.signalSemaphoreCount = 0; submit_info.pWaitSemaphores = nullptr; submit_info.waitSemaphoreCount = 0; vk::FenceCreateInfo submit_fence_info; auto [fence_result, update_done_fence] = m_device->GetLogicalDeviceHandle().createFenceUnique(submit_fence_info); result = m_device->GetGraphicsQueue().submit(submit_info, *update_done_fence); [[maybe_unused]] vk::Result wait_result = m_device->GetLogicalDeviceHandle().waitForFences(*update_done_fence, true, std::numeric_limits<uint32_t>::max()); assert(wait_result == vk::Result::eSuccess); } InputHandler& Context::GetCameraInputHandler() { return m_camera_input_handler; } Context::Context( VulkanWindow* Window, const std::string& app_name, fpr::Version app_version, const std::string& engine_name, fpr::Version engine_version, fpr::Version vulkan_version): m_camera_input_handler(Window->GetWindowHandle()) { m_window = Window; m_extensions = GetRequiredExtensions(m_validation_layers); assert(IsVulkanSupported()); vk::ApplicationInfo app_info = ApplicationInfo(app_name.data(), app_version, engine_name.data(), engine_version, vulkan_version); vk::InstanceCreateInfo instance_info{}; instance_info.pApplicationInfo = &app_info; instance_info.enabledExtensionCount = (uint32_t)m_extensions.size(); instance_info.ppEnabledExtensionNames = m_extensions.data(); if(validation_layers_enabled) { instance_info.enabledLayerCount = (uint32_t)m_validation_layers.size(); instance_info.ppEnabledLayerNames = m_validation_layers.data(); } else { instance_info.enabledLayerCount = 0; } instance_info.flags = {}; auto [result_signal, instance] = vk::createInstanceUnique(instance_info); assert(("Failed to create instance!", result_signal == vk::Result::eSuccess)); m_instance = std::move(instance); VkSurfaceKHR tmp_surface; [[maybe_unused]] VkResult result = glfwCreateWindowSurface(*m_instance, Window->GetWindowHandle(), nullptr, &tmp_surface); assert(("Surface creation failed!", result == VK_SUCCESS)); m_surface = vk::UniqueSurfaceKHR(vk::SurfaceKHR(tmp_surface), *m_instance); m_device = std::make_unique<Device>(m_surface.get(), *m_instance); m_render_graph = std::make_unique<RenderGraph>(); } } // namespace fpr