#include <stdafx.h> #include "Context.h" #include "VulkanBoilerplate.h" #include "VulkanWindow.h" #include "SwapChain.h" namespace fpr { 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( PipelineType::eGraphics, "Graphics", fpr::GraphicsPipeline::GetDefaultGraphicsPipeline(), tl::nullopt); pipelines.emplace_back(PipelineType::eGraphics, "Depth", fpr::GraphicsPipeline::GetDepthPassPipeline(), "Graphics"); 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 PipelineType::eGraphics: { fpr::GraphicsPipeline* parent_pipeline = nullptr; if(parent_name != tl::nullopt) parent_pipeline = m_render_graph->GraphicsPipelines.Get(*parent_name); auto pipeline = std::make_unique<fpr::GraphicsPipeline>(settings, parent_pipeline); m_render_graph->GraphicsPipelines.Add(std::string(name), pipeline); } case PipelineType::eCompute: { // TODO: } } } } 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; } Context& Context::Get() { return GetImpl(s_window, s_app_name, s_app_version, s_engine_name, s_engine_version, s_vulkan_version); } 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; GetImpl(Window, app_name, app_version, engine_name, engine_version, vulkan_version); } 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, 3> pool_sizes; pool_sizes[0].type = vk::DescriptorType::eUniformBuffer; pool_sizes[0].descriptorCount = (uint32_t)m_swapchain->GetImages().size(); pool_sizes[1].type = vk::DescriptorType::eCombinedImageSampler; pool_sizes[1].descriptorCount = MAX_TEXTURES; pool_sizes[2].type = vk::DescriptorType::eStorageBuffer; pool_sizes[2].descriptorCount = 1; vk::DescriptorPoolCreateInfo pool_info = {}; pool_info.poolSizeCount = (uint32_t)pool_sizes.size(); pool_info.pPoolSizes = pool_sizes.data(); pool_info.maxSets = (uint32_t)m_swapchain->GetImages().size() + MAX_TEXTURES + 1; 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(); } 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