From 079b55f0d0410416a6d4423bbf77f506c9fda28a Mon Sep 17 00:00:00 2001 From: GeorgH93 Date: Sat, 26 Sep 2020 21:13:14 +0200 Subject: [PATCH] Refactor code --- openVulkanoCpp/Base/PlatformEnums.hpp | 8 + openVulkanoCpp/Base/UI/BaseWindow.hpp | 12 ++ openVulkanoCpp/Base/UI/IWindow.hpp | 8 + openVulkanoCpp/Host/GraphicsAppManager.cpp | 176 ++++++++++++++++ openVulkanoCpp/Host/GraphicsAppManager.hpp | 217 ++++--------------- openVulkanoCpp/Host/PlatformProducer.cpp | 36 ++++ openVulkanoCpp/Host/PlatformProducer.hpp | 37 ++-- openVulkanoCpp/Vulkan/Device.cpp | 225 ++++++++++++++++++++ openVulkanoCpp/Vulkan/Device.hpp | 232 +++------------------ 9 files changed, 548 insertions(+), 403 deletions(-) create mode 100644 openVulkanoCpp/Host/GraphicsAppManager.cpp create mode 100644 openVulkanoCpp/Host/PlatformProducer.cpp create mode 100644 openVulkanoCpp/Vulkan/Device.cpp diff --git a/openVulkanoCpp/Base/PlatformEnums.hpp b/openVulkanoCpp/Base/PlatformEnums.hpp index e74b3b7..4a2015e 100644 --- a/openVulkanoCpp/Base/PlatformEnums.hpp +++ b/openVulkanoCpp/Base/PlatformEnums.hpp @@ -1,5 +1,13 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + #pragma once +#include + namespace openVulkanoCpp { namespace RenderAPI diff --git a/openVulkanoCpp/Base/UI/BaseWindow.hpp b/openVulkanoCpp/Base/UI/BaseWindow.hpp index 8274058..512b772 100644 --- a/openVulkanoCpp/Base/UI/BaseWindow.hpp +++ b/openVulkanoCpp/Base/UI/BaseWindow.hpp @@ -1,4 +1,11 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + #pragma once + #include "IWindow.hpp" namespace openVulkanoCpp @@ -80,6 +87,11 @@ namespace openVulkanoCpp return position; } + bool HasTitle() override + { + return WindowMode::WINDOWED == GetWindowMode(); + } + const std::string& GetTitle() override { return windowConfig.title; diff --git a/openVulkanoCpp/Base/UI/IWindow.hpp b/openVulkanoCpp/Base/UI/IWindow.hpp index c21e5df..e03739b 100644 --- a/openVulkanoCpp/Base/UI/IWindow.hpp +++ b/openVulkanoCpp/Base/UI/IWindow.hpp @@ -1,4 +1,11 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + #pragma once + #include #include #include @@ -35,6 +42,7 @@ namespace openVulkanoCpp virtual void Init(RenderAPI::RenderApi renderApi) = 0; + virtual bool HasTitle() = 0; virtual const std::string& GetTitle() = 0; virtual void SetTitle(const std::string& title) = 0; diff --git a/openVulkanoCpp/Host/GraphicsAppManager.cpp b/openVulkanoCpp/Host/GraphicsAppManager.cpp new file mode 100644 index 0000000..0797cc6 --- /dev/null +++ b/openVulkanoCpp/Host/GraphicsAppManager.cpp @@ -0,0 +1,176 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "GraphicsAppManager.hpp" +#include "../Base/IPlatform.hpp" +#include "../Base/Logger.hpp" +#include "PlatformProducer.hpp" +#include +#include +#include + +namespace openVulkanoCpp +{ + GraphicsAppManager::GraphicsAppManager(openVulkanoCpp::IGraphicsApp* app, RenderAPI::RenderApi renderApi) + : app(app), renderApi(renderApi) + { + if (renderApi >= RenderAPI::MAX_VALUE) throw std::runtime_error("Invalid RenderAPI"); + Logger::SetupLogger(); + if (!app) + { + constexpr auto msg = "The app must not be null!"; + Logger::MANAGER->error(msg); + throw std::runtime_error(msg); + } + platform = std::unique_ptr(PlatformProducer::CreatePlatform(renderApi)); + window = platform->MakeWindow(); + renderer = std::unique_ptr(PlatformProducer::CreateRenderManager(renderApi)); + app->SetGraphicsAppManager(this); + window->SetWindowHandler(this); + } + + GraphicsAppManager::~GraphicsAppManager() noexcept = default; + + void GraphicsAppManager::Stop() + { + running = false; + Logger::MANAGER->info("Graphics application stopped"); + } + + void GraphicsAppManager::Pause() + { + paused = true; + frameTimer.Stop(); + Logger::MANAGER->info("Graphics application paused"); + } + + void GraphicsAppManager::Resume() + { + paused = false; + frameTimer.Start(); + Logger::MANAGER->info("Graphics application resumed"); + } + + void GraphicsAppManager::Run() + { + running = true; + StartUp(); + frameTimer.Reset(); + Loop(); // Runs the rendering loop + ShutDown(); + } + + void GraphicsAppManager::StartUp() + { + try + { + Logger::MANAGER->info("Initializing ..."); + app->Init(); + platform->Init(); + window->Init(renderApi); + //TODO restore window settings if there are any set + renderer->Init(static_cast(this), window); + windowTitleFormat = app->GetAppName() + " " + app->GetAppVersion() + " - " + renderer->GetMainRenderDeviceName() + " - {:.1f} fps ({:.1f} ms)"; + Logger::MANAGER->info("Initialized"); + } + catch (std::exception& e) + { + Logger::MANAGER->error("Failed to initiate: {0}", e.what()); + running = false; +#ifdef DEBUG + throw e; +#endif + } + } + + void GraphicsAppManager::Loop() + { + while (running) + { + LoopTick(); + } + } + + void GraphicsAppManager::LoopTick() + { + platform->Tick(); + if (paused) + { // The rendering is paused + // No need to burn cpu time if the app is paused + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + else + { + app->Tick(); + renderer->Tick(); + frameTimer.Tick(); + UpdateFps(); + } + } + + void GraphicsAppManager::ShutDown() const + { + Logger::MANAGER->info("Shutting down ..."); + renderer->Close(); + window->Close(); + platform->Close(); + app->Close(); + Logger::MANAGER->info("Shutdown complete"); + } + + void GraphicsAppManager::UpdateFps() + { + frameCount++; + fpsTimer += frameTimer.GetTickSeconds(); + + if(fpsTimer > 1.0f) + { + avgFps = static_cast(frameCount - lastFrameCount) / fpsTimer; + avgFrameTime = (1 / avgFps) * 1000.0f; + lastFrameCount = frameCount; + fpsTimer = 0; + if (window->HasTitle()) + { + window->SetTitle(fmt::format(windowTitleFormat, avgFps, avgFrameTime)); + } + } + } + + void GraphicsAppManager::OnWindowMinimize(openVulkanoCpp::IWindow* window) + { + if (window != this->window) return; + Pause(); + } + + void GraphicsAppManager::OnWindowRestore(openVulkanoCpp::IWindow* window) + { + if (window != this->window) return; + Resume(); + } + + void GraphicsAppManager::OnWindowFocusLost(IWindow* window) + {} + + void GraphicsAppManager::OnWindowFocusGained(IWindow* window) + {} + + void GraphicsAppManager::OnWindowMove(IWindow* window, int posX, int posY) + { + //TODO save window pos + } + + void GraphicsAppManager::OnWindowResize(openVulkanoCpp::IWindow* window, const uint32_t newWidth, const uint32_t newHeight) + { + if(window != this->window) return; + renderer->Resize(newWidth, newHeight); + } + + void GraphicsAppManager::OnWindowClose(openVulkanoCpp::IWindow* window) + { + if (window != this->window) return; + Stop(); + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/GraphicsAppManager.hpp b/openVulkanoCpp/Host/GraphicsAppManager.hpp index c3ea2c1..61e68f3 100644 --- a/openVulkanoCpp/Host/GraphicsAppManager.hpp +++ b/openVulkanoCpp/Host/GraphicsAppManager.hpp @@ -1,29 +1,34 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + #pragma once -#include -#include -#include -#include + #include "../Base/IGraphicsAppManager.hpp" -#include "../Base/UI/IWindow.hpp" #include "../Base/IGraphicsApp.hpp" -#include "../Base/PlatformEnums.hpp" -#include "../Base/Logger.hpp" -#include "../Base/Timer.hpp" #include "../Base/Render/IRenderer.hpp" -#include "PlatformProducer.hpp" +#include "../Base/UI/IWindow.hpp" +#include "../Base/PlatformEnums.hpp" +#include "../Base/Timer.hpp" +#include +#include namespace openVulkanoCpp { + class IPlatform; + /** * \brief A simple GraphicsAppManager. It can only handle one window. */ class GraphicsAppManager final : virtual public IGraphicsAppManager, virtual public IWindowHandler { private: - IPlatform* platform; + std::unique_ptr platform; + std::unique_ptr renderer; IWindow* window; IGraphicsApp* app; - IRenderer* renderer; RenderAPI::RenderApi renderApi; bool paused = false, running = false; float fpsTimer = 0, avgFps = 0, avgFrameTime = 0; @@ -32,198 +37,60 @@ namespace openVulkanoCpp std::string windowTitleFormat; public: - explicit GraphicsAppManager(IGraphicsApp* app, RenderAPI::RenderApi renderApi = RenderAPI::VULKAN) : app(app), renderApi(renderApi) - { - if (renderApi >= RenderAPI::MAX_VALUE) throw std::runtime_error("Invalid RenderAPI"); - Logger::SetupLogger(); - if (!app) - { - constexpr auto msg = "The app must not be null!"; - Logger::MANAGER->error(msg); - throw std::runtime_error(msg); - } - platform = PlatformProducer::CreatePlatform(renderApi); - window = platform->MakeWindow(); - renderer = PlatformProducer::CreateRenderManager(renderApi); - app->SetGraphicsAppManager(this); - window->SetWindowHandler(this); - } + explicit GraphicsAppManager(IGraphicsApp* app, RenderAPI::RenderApi renderApi = RenderAPI::VULKAN); - ~GraphicsAppManager() override - { - delete renderer; - delete platform; - } + ~GraphicsAppManager() noexcept override; public: // Getter - RenderAPI::RenderApi GetRenderApi() const override - { - return renderApi; - } + [[nodiscard]] RenderAPI::RenderApi GetRenderApi() const override { return renderApi; } - IGraphicsApp* GetGraphicsApp() const override - { - return app; - } + [[nodiscard]] IGraphicsApp* GetGraphicsApp() const override { return app; } - IRenderer* GetRenderer() const override - { - return renderer; - } + [[nodiscard]] IRenderer* GetRenderer() const override { return renderer.get(); } - bool IsRunning() const override - { - return running; - } + [[nodiscard]] bool IsRunning() const override { return running; } - bool IsPaused() const override - { - return paused; - } + [[nodiscard]] bool IsPaused() const override { return paused; } public: // Setter - void Stop() override - { - running = false; - Logger::MANAGER->info("Graphics application stopped"); - } + void Stop() override; - void Pause() override - { - paused = true; - frameTimer.Stop(); - Logger::MANAGER->info("Graphics application paused"); - } + void Pause() override; - void Resume() override - { - paused = false; - frameTimer.Start(); - Logger::MANAGER->info("Graphics application resumed"); - } + void Resume() override; public: - void Run() override - { - running = true; - StartUp(); - frameTimer.Reset(); - Loop(); // Runs the rendering loop - ShutDown(); - } + void Run() override; private: - void StartUp() - { - try - { - Logger::MANAGER->info("Initializing ..."); - app->Init(); - platform->Init(); - window->Init(renderApi); - //TODO restore window settings if there are any set - renderer->Init(static_cast(this), window); - windowTitleFormat = app->GetAppName() + " " + app->GetAppVersion() + " - " + renderer->GetMainRenderDeviceName() + " - {:.1f} fps ({:.1f} ms)"; - Logger::MANAGER->info("Initialized"); - } - catch (std::exception& e) - { - Logger::MANAGER->error("Failed to initiate: {0}", e.what()); - running = false; -#ifdef DEBUG - throw e; -#endif - } - } + void StartUp(); - void Loop() - { - while (running) - { - platform->Tick(); - if (paused) - { // The rendering is paused - // No need to burn cpu time if the app is paused - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - else - { - app->Tick(); - renderer->Tick(); - frameTimer.Tick(); - UpdateFps(); - } - } - } + void Loop(); - void ShutDown() const - { - Logger::MANAGER->info("Shutting down ..."); - renderer->Close(); - window->Close(); - platform->Close(); - app->Close(); - Logger::MANAGER->info("Shutdown complete"); - } + void LoopTick(); - void UpdateFps() - { - frameCount++; - fpsTimer += frameTimer.GetTickSeconds(); + void ShutDown() const; - if(fpsTimer > 1.0f) - { - avgFps = static_cast(frameCount - lastFrameCount) / fpsTimer; - avgFrameTime = (1 / avgFps) * 1000.0f; - lastFrameCount = frameCount; - fpsTimer = 0; - window->SetTitle(fmt::format(windowTitleFormat, avgFps, avgFrameTime)); - } - } + void UpdateFps(); public: //FPS stuff - uint64_t GetFrameCount() const override - { - return frameCount; - } + [[nodiscard]] uint64_t GetFrameCount() const override { return frameCount; } - float GetAvgFrameTime() const override - { - return avgFrameTime; - } + [[nodiscard]] float GetAvgFrameTime() const override { return avgFrameTime; } - float GetAvgFps() const override - { - return avgFps; - } + [[nodiscard]] float GetAvgFps() const override { return avgFps; } public: // Window Manager - void OnWindowMinimize(IWindow* window) override - { - if (window != this->window) return; - Pause(); - } + void OnWindowMinimize(IWindow* window) override; - void OnWindowRestore(IWindow* window) override - { - if (window != this->window) return; - Resume(); - } + void OnWindowRestore(IWindow* window) override; - void OnWindowFocusLost(IWindow* window) override {} - void OnWindowFocusGained(IWindow* window) override {} - void OnWindowMove(IWindow* window, int posX, int posY) override {} //TODO save window pos + void OnWindowFocusLost(IWindow* window) override; + void OnWindowFocusGained(IWindow* window) override; + void OnWindowMove(IWindow* window, int posX, int posY) override; - void OnWindowResize(IWindow* window, const uint32_t newWidth, const uint32_t newHeight) override - { - if(window != this->window) return; - renderer->Resize(newWidth, newHeight); - } + void OnWindowResize(IWindow* window, uint32_t newWidth, uint32_t newHeight) override; - void OnWindowClose(IWindow* window) override - { - if (window != this->window) return; - Stop(); - } + void OnWindowClose(IWindow* window) override; }; } diff --git a/openVulkanoCpp/Host/PlatformProducer.cpp b/openVulkanoCpp/Host/PlatformProducer.cpp new file mode 100644 index 0000000..e7771a1 --- /dev/null +++ b/openVulkanoCpp/Host/PlatformProducer.cpp @@ -0,0 +1,36 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "PlatformProducer.hpp" +#include +#include "../Base/Logger.hpp" +#include "../Vulkan/Renderer.hpp" +#include "GLFW/PlatformGLFW.hpp" + +namespace openVulkanoCpp +{ + IRenderer* PlatformProducer::CreateRenderManager(RenderAPI::RenderApi renderApi) + { + switch (renderApi) + { + case RenderAPI::VULKAN: return new Vulkan::Renderer(); + default: + Logger::RENDER->error("Unsupported render api requested! Requested %d", static_cast(renderApi)); + throw std::runtime_error("Unsupported render api requested!"); + } + } + + IPlatform* PlatformProducer::CreatePlatform(RenderAPI::RenderApi renderApi) + { + switch (renderApi) + { + case RenderAPI::VULKAN: return new GLFW::PlatformGLFW(); + default: + Logger::MANAGER->error("Unsupported render api requested! Requested %d", static_cast(renderApi)); + throw std::runtime_error("Unsupported render api requested!"); + } + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/PlatformProducer.hpp b/openVulkanoCpp/Host/PlatformProducer.hpp index 382d549..272f179 100644 --- a/openVulkanoCpp/Host/PlatformProducer.hpp +++ b/openVulkanoCpp/Host/PlatformProducer.hpp @@ -1,13 +1,18 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + #pragma once -#include -#include "../Base/Logger.hpp" + #include "../Base/PlatformEnums.hpp" -#include "../Base/IPlatform.hpp" -#include "../Vulkan/Renderer.hpp" -#include "GLFW/PlatformGLFW.hpp" namespace openVulkanoCpp { + class IPlatform; + class IRenderer; + /** * \brief Helper class the produces all the platform depending classes */ @@ -21,16 +26,7 @@ namespace openVulkanoCpp * \return The created Renderer. * \throws std::runtime_error if the render api is not supported */ - static IRenderer* CreateRenderManager(RenderAPI::RenderApi renderApi) - { - switch (renderApi) - { - case RenderAPI::VULKAN: return new Vulkan::Renderer(); - default: - Logger::RENDER->error("Unsupported render api requested! Requested %d", static_cast(renderApi)); - throw std::runtime_error("Unsupported render api requested!"); - } - } + static IRenderer* CreateRenderManager(RenderAPI::RenderApi renderApi); /** * \brief Creates a platform that fits best for the current environment @@ -38,15 +34,6 @@ namespace openVulkanoCpp * \return The created platform * \throws std::runtime_error if the render api is not supported */ - static IPlatform* CreatePlatform(RenderAPI::RenderApi renderApi) - { - switch (renderApi) - { - case RenderAPI::VULKAN: return new GLFW::PlatformGLFW(); - default: - Logger::MANAGER->error("Unsupported render api requested! Requested %d", static_cast(renderApi)); - throw std::runtime_error("Unsupported render api requested!"); - } - } + static IPlatform* CreatePlatform(RenderAPI::RenderApi renderApi); }; } diff --git a/openVulkanoCpp/Vulkan/Device.cpp b/openVulkanoCpp/Vulkan/Device.cpp new file mode 100644 index 0000000..424778c --- /dev/null +++ b/openVulkanoCpp/Vulkan/Device.cpp @@ -0,0 +1,225 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "Device.hpp" + +namespace openVulkanoCpp::Vulkan +{ + namespace + { + class DeviceQueueCreateInfoBuilder + { + std::vector createInfos; + std::vector> prioritiesVector; + public: + DeviceQueueCreateInfoBuilder() = default; + ~DeviceQueueCreateInfoBuilder() = default; + + void AddQueueFamily(const uint32_t queueFamilyIndex, const std::vector& priorities) + { + prioritiesVector.push_back(priorities); + createInfos.emplace_back(vk::DeviceQueueCreateFlags(), queueFamilyIndex, priorities.size(), prioritiesVector[prioritiesVector.size()-1].data()); + } + + void AddQueueFamily(uint32_t queueFamilyIndex, uint32_t count = 1) + { + std::vector priorities; + priorities.resize(count); + std::fill(priorities.begin(), priorities.end(), 0.0f); + AddQueueFamily(queueFamilyIndex, priorities); + } + + std::vector& GetDeviceQueueCreateInfos() + { + return createInfos; + } + }; + } + + Device::Device(vk::PhysicalDevice& physicalDevice) + { + this->physicalDevice = physicalDevice; + useDebugMarkers = false; + QueryDevice(); + } + + void Device::PrepareDevice(const vk::ArrayProxy& requestedExtensions, const vk::SurfaceKHR& surface) + { + queueIndices.graphics = FindBestQueue(vk::QueueFlagBits::eGraphics, surface); // Make sure that the graphics queue supports the surface + BuildDevice(requestedExtensions); + //TODO setup debug marker + pipelineCache = device.createPipelineCache(vk::PipelineCacheCreateInfo()); + + graphicsQueue = device.getQueue(queueIndices.graphics, 0); + graphicsCommandPool = device.createCommandPool({ vk::CommandPoolCreateFlagBits::eResetCommandBuffer, queueIndices.graphics, }); + } + + void Device::WaitIdle() const + { //TODO wait all queues idle + graphicsQueue.waitIdle(); + device.waitIdle(); + } + + vk::CommandBuffer Device::CreateCommandBuffer(vk::CommandBufferLevel level) const + { + const vk::CommandBufferAllocateInfo cmdBufferAllocInfo(graphicsCommandPool, level, 1); + return device.allocateCommandBuffers(cmdBufferAllocInfo)[0]; + } + + void Device::FlushCommandBuffer(vk::CommandBuffer& cmdBuffer) const + { + graphicsQueue.submit(vk::SubmitInfo{ 0, nullptr, nullptr, 1, &cmdBuffer }, vk::Fence()); + WaitIdle(); + } + + void Device::ExecuteNow(const std::function& function) const + { + vk::CommandBuffer commandBuffer = CreateCommandBuffer(); + commandBuffer.begin(vk::CommandBufferBeginInfo{ vk::CommandBufferUsageFlagBits::eOneTimeSubmit }); + function(commandBuffer); + commandBuffer.end(); + FlushCommandBuffer(commandBuffer); + device.freeCommandBuffers(graphicsCommandPool, commandBuffer); + } + + vk::ShaderModule Device::CreateShaderModule(const std::string& filename) + { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + if (!file.is_open()) throw std::runtime_error("Failed to open shader file!"); + const size_t fileSize = static_cast(file.tellg()); + std::vector buffer(fileSize); + file.seekg(0); + file.read(buffer.data(), fileSize); + file.close(); + vk::ShaderModuleCreateInfo smci = { {}, buffer.size(), reinterpret_cast(buffer.data()) }; + return CreateShaderModule(smci); + } + + vk::ShaderModule Device::CreateShaderModule(vk::ShaderModuleCreateInfo& createInfo) const + { + return device.createShaderModule(createInfo); + } + + void Device::QueryDevice() + { + // Query device features + queueFamilyProperties = physicalDevice.getQueueFamilyProperties(); + properties = physicalDevice.getProperties(); + features = physicalDevice.getFeatures(); + for (auto& ext : physicalDevice.enumerateDeviceExtensionProperties()) { supportedExtensions.insert(ext.extensionName); } + + // Query device memory properties + memoryProperties = physicalDevice.getMemoryProperties(); + queueIndices.graphics = FindBestQueue(vk::QueueFlagBits::eGraphics); + queueIndices.compute = FindBestQueue(vk::QueueFlagBits::eCompute); + queueIndices.transfer = FindBestQueue(vk::QueueFlagBits::eTransfer); + } + + void Device::BuildDevice(const vk::ArrayProxy& requestedExtensions) + { + vk::DeviceCreateInfo deviceCreateInfo; + deviceCreateInfo.pEnabledFeatures = &features; //TODO add option to disable not needed features + + + DeviceQueueCreateInfoBuilder deviceQueueCreateInfoBuilder; + deviceQueueCreateInfoBuilder.AddQueueFamily(queueIndices.GetGraphics(), queueFamilyProperties[queueIndices.GetGraphics()].queueCount); + if (queueIndices.GetCompute() != VK_QUEUE_FAMILY_IGNORED) + deviceQueueCreateInfoBuilder.AddQueueFamily(queueIndices.GetCompute(), queueFamilyProperties[queueIndices.GetCompute()].queueCount); + if (queueIndices.GetTransfer() != VK_QUEUE_FAMILY_IGNORED) + deviceQueueCreateInfoBuilder.AddQueueFamily(queueIndices.GetTransfer(), queueFamilyProperties[queueIndices.GetTransfer()].queueCount); + const std::vector deviceQueueCreateInfos = deviceQueueCreateInfoBuilder.GetDeviceQueueCreateInfos(); + + deviceCreateInfo.queueCreateInfoCount = static_cast(deviceQueueCreateInfos.size()); + deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos.data(); + + std::vector enabledExtensions; + for (const auto& extension : requestedExtensions) + { + enabledExtensions.push_back(extension.c_str()); + } +#ifdef DEBUG + if (IsExtensionAvailable({ VK_EXT_DEBUG_MARKER_EXTENSION_NAME })) + { // Enable debug marker extension if available + enabledExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); + useDebugMarkers = true; + } +#endif + deviceCreateInfo.enabledExtensionCount = static_cast(enabledExtensions.size()); + deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); + + device = physicalDevice.createDevice(deviceCreateInfo); + } + + uint32_t Device::FindBestQueue(const vk::QueueFlags& desiredFlags, const vk::SurfaceKHR& surface) const + { + uint32_t best = VK_QUEUE_FAMILY_IGNORED; + VkQueueFlags bestExtraFlagsCount = VK_QUEUE_FLAG_BITS_MAX_ENUM; + for (size_t i = 0; i < queueFamilyProperties.size(); i++) + { + vk::QueueFlags flags = queueFamilyProperties[i].queueFlags; + if (!(flags & desiredFlags)) continue; // Skip queue without desired flags + if (surface && VK_FALSE == physicalDevice.getSurfaceSupportKHR(i, surface)) continue; + const VkQueueFlags currentExtraFlags = (flags & ~desiredFlags).operator VkQueueFlags(); + + if (0 == currentExtraFlags) return i; // return exact match + + if (best == VK_QUEUE_FAMILY_IGNORED || currentExtraFlags < bestExtraFlagsCount) + { + best = i; + bestExtraFlagsCount = currentExtraFlags; + } + } + return best; + } + + vk::Format Device::GetSupportedDepthFormat(const std::vector& depthFormats) const + { + for (auto& format : depthFormats) + { + vk::FormatProperties formatProps; + formatProps = physicalDevice.getFormatProperties(format); + if (formatProps.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment) + { + return format; + } + } + + throw std::runtime_error("No supported depth format"); + } + + bool Device::GetMemoryType(uint32_t typeBits, const vk::MemoryPropertyFlags& properties, uint32_t* typeIndex) const + { + for (uint32_t i = 0; i < 32; i++) + { + if ((typeBits & 1) == 1) + { + if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + *typeIndex = i; + return true; + } + } + typeBits >>= 1; + } + return false; + } + + uint32_t Device::GetMemoryType(uint32_t typeBits, const vk::MemoryPropertyFlags& properties) const + { + uint32_t result = 0; + if (!GetMemoryType(typeBits, properties, &result)) + { + throw std::runtime_error("Unable to find memory type " + to_string(properties)); + } + return result; + } + + void Device::Close() + { + device.destroyCommandPool(graphicsCommandPool); + //TODO fill + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Vulkan/Device.hpp b/openVulkanoCpp/Vulkan/Device.hpp index e1d6d01..4bb8011 100644 --- a/openVulkanoCpp/Vulkan/Device.hpp +++ b/openVulkanoCpp/Vulkan/Device.hpp @@ -1,41 +1,21 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + #pragma once + #include #include #include #include +#include "../Base/ICloseable.hpp" namespace openVulkanoCpp { namespace Vulkan { - class DeviceQueueCreateInfoBuilder - { - std::vector createInfos; - std::vector> prioritiesVector; - public: - DeviceQueueCreateInfoBuilder() = default; - ~DeviceQueueCreateInfoBuilder() = default; - - void AddQueueFamily(const uint32_t queueFamilyIndex, const std::vector& priorities) - { - prioritiesVector.push_back(priorities); - createInfos.emplace_back(vk::DeviceQueueCreateFlags(), queueFamilyIndex, priorities.size(), prioritiesVector[prioritiesVector.size()-1].data()); - } - - void AddQueueFamily(uint32_t queueFamilyIndex, uint32_t count = 1) - { - std::vector priorities; - priorities.resize(count); - std::fill(priorities.begin(), priorities.end(), 0.0f); - AddQueueFamily(queueFamilyIndex, priorities); - } - - std::vector& GetDeviceQueueCreateInfos() - { - return createInfos; - } - }; - class Device : virtual public ICloseable { public: @@ -63,35 +43,21 @@ namespace openVulkanoCpp bool useDebugMarkers; public: - Device(vk::PhysicalDevice& physicalDevice) - { - this->physicalDevice = physicalDevice; - useDebugMarkers = false; - QueryDevice(); - } + Device(vk::PhysicalDevice& physicalDevice); - std::string GetDeviceName() const + [[nodiscard]] std::string GetDeviceName() const { return properties.deviceName; } - void PrepareDevice(const vk::ArrayProxy& requestedExtensions, const vk::SurfaceKHR& surface) - { - queueIndices.graphics = FindBestQueue(vk::QueueFlagBits::eGraphics, surface); // Make sure that the graphics queue supports the surface - BuildDevice(requestedExtensions); - //TODO setup debug marker - pipelineCache = device.createPipelineCache(vk::PipelineCacheCreateInfo()); + void PrepareDevice(const vk::ArrayProxy& requestedExtensions, const vk::SurfaceKHR& surface); - graphicsQueue = device.getQueue(queueIndices.graphics, 0); - graphicsCommandPool = device.createCommandPool({ vk::CommandPoolCreateFlagBits::eResetCommandBuffer, queueIndices.graphics, }); - } - - std::set GetExtensions() const + [[nodiscard]] const std::set& GetExtensions() const { return supportedExtensions; } - bool IsExtensionAvailable(const vk::ArrayProxy& extensions) const + [[nodiscard]] bool IsExtensionAvailable(const vk::ArrayProxy& extensions) const { for(const auto& extension : extensions) { @@ -100,185 +66,45 @@ namespace openVulkanoCpp return true; } - void WaitIdle() const - { //TODO wait all queues idle - graphicsQueue.waitIdle(); - device.waitIdle(); - } + void WaitIdle() const; - vk::CommandBuffer CreateCommandBuffer(vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary) const - { - const vk::CommandBufferAllocateInfo cmdBufferAllocInfo(graphicsCommandPool, level, 1); - return device.allocateCommandBuffers(cmdBufferAllocInfo)[0]; - } + [[nodiscard]] vk::CommandBuffer CreateCommandBuffer(vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary) const; - void FlushCommandBuffer(vk::CommandBuffer& cmdBuffer) const - { - graphicsQueue.submit(vk::SubmitInfo{ 0, nullptr, nullptr, 1, &cmdBuffer }, vk::Fence()); - WaitIdle(); - } + void FlushCommandBuffer(vk::CommandBuffer& cmdBuffer) const; - void ExecuteNow(const std::function& function) const - { - vk::CommandBuffer commandBuffer = CreateCommandBuffer(); - commandBuffer.begin(vk::CommandBufferBeginInfo{ vk::CommandBufferUsageFlagBits::eOneTimeSubmit }); - function(commandBuffer); - commandBuffer.end(); - FlushCommandBuffer(commandBuffer); - device.freeCommandBuffers(graphicsCommandPool, commandBuffer); - } + void ExecuteNow(const std::function& function) const; - vk::ShaderModule CreateShaderModule(const std::string filename) - { - std::ifstream file(filename, std::ios::ate | std::ios::binary); - if (!file.is_open()) throw std::runtime_error("Failed to open shader file!"); - const size_t fileSize = static_cast(file.tellg()); - std::vector buffer(fileSize); - file.seekg(0); - file.read(buffer.data(), fileSize); - file.close(); - vk::ShaderModuleCreateInfo smci = { {}, buffer.size(), reinterpret_cast(buffer.data()) }; - return CreateShaderModule(smci); - } + vk::ShaderModule CreateShaderModule(const std::string& filename); + + vk::ShaderModule CreateShaderModule(vk::ShaderModuleCreateInfo& createInfo) const; - vk::ShaderModule CreateShaderModule(vk::ShaderModuleCreateInfo& createInfo) const - { - return device.createShaderModule(createInfo); - } private: - void QueryDevice() - { - // Query device features - queueFamilyProperties = physicalDevice.getQueueFamilyProperties(); - properties = physicalDevice.getProperties(); - features = physicalDevice.getFeatures(); - for (auto& ext : physicalDevice.enumerateDeviceExtensionProperties()) { supportedExtensions.insert(ext.extensionName); } + void QueryDevice(); - // Query device memory properties - memoryProperties = physicalDevice.getMemoryProperties(); - queueIndices.graphics = FindBestQueue(vk::QueueFlagBits::eGraphics); - queueIndices.compute = FindBestQueue(vk::QueueFlagBits::eCompute); - queueIndices.transfer = FindBestQueue(vk::QueueFlagBits::eTransfer); - } + void BuildDevice(const vk::ArrayProxy& requestedExtensions); - void BuildDevice(const vk::ArrayProxy& requestedExtensions) - { - vk::DeviceCreateInfo deviceCreateInfo; - deviceCreateInfo.pEnabledFeatures = &features; //TODO add option to disable not needed features - - - DeviceQueueCreateInfoBuilder deviceQueueCreateInfoBuilder; - deviceQueueCreateInfoBuilder.AddQueueFamily(queueIndices.GetGraphics(), queueFamilyProperties[queueIndices.GetGraphics()].queueCount); - if (queueIndices.GetCompute() != VK_QUEUE_FAMILY_IGNORED) - deviceQueueCreateInfoBuilder.AddQueueFamily(queueIndices.GetCompute(), queueFamilyProperties[queueIndices.GetCompute()].queueCount); - if (queueIndices.GetTransfer() != VK_QUEUE_FAMILY_IGNORED) - deviceQueueCreateInfoBuilder.AddQueueFamily(queueIndices.GetTransfer(), queueFamilyProperties[queueIndices.GetTransfer()].queueCount); - const std::vector deviceQueueCreateInfos = deviceQueueCreateInfoBuilder.GetDeviceQueueCreateInfos(); - - deviceCreateInfo.queueCreateInfoCount = static_cast(deviceQueueCreateInfos.size()); - deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos.data(); - - std::vector enabledExtensions; - for (const auto& extension : requestedExtensions) - { - enabledExtensions.push_back(extension.c_str()); - } -#ifdef DEBUG - if (IsExtensionAvailable({ VK_EXT_DEBUG_MARKER_EXTENSION_NAME })) - { // Enable debug marker extension if available - enabledExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); - useDebugMarkers = true; - } -#endif - deviceCreateInfo.enabledExtensionCount = static_cast(enabledExtensions.size()); - deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); - - device = physicalDevice.createDevice(deviceCreateInfo); - } - - uint32_t FindBestQueue(const vk::QueueFlags& desiredFlags, const vk::SurfaceKHR& surface = nullptr) const - { - uint32_t best = VK_QUEUE_FAMILY_IGNORED; - VkQueueFlags bestExtraFlagsCount = VK_QUEUE_FLAG_BITS_MAX_ENUM; - for (size_t i = 0; i < queueFamilyProperties.size(); i++) - { - vk::QueueFlags flags = queueFamilyProperties[i].queueFlags; - if (!(flags & desiredFlags)) continue; // Skip queue without desired flags - if (surface && VK_FALSE == physicalDevice.getSurfaceSupportKHR(i, surface)) continue; - const VkQueueFlags currentExtraFlags = (flags & ~desiredFlags).operator VkQueueFlags(); - - if (0 == currentExtraFlags) return i; // return exact match - - if (best == VK_QUEUE_FAMILY_IGNORED || currentExtraFlags < bestExtraFlagsCount) - { - best = i; - bestExtraFlagsCount = currentExtraFlags; - } - } - return best; - } + [[nodiscard]] uint32_t FindBestQueue(const vk::QueueFlags& desiredFlags, const vk::SurfaceKHR& surface = nullptr) const; public: /** - * \brief Vulkan does not require does not define a depth buffer format that must be supported. This method checks for the first supported format from an given array of formats. + * \brief Vulkan does not define a depth buffer format that must be supported. This method checks for the first supported format from an given array of formats. * \param depthFormats Array of depth formats * \return The first format supported as a depth buffer * \throws If no depth buffer format is supported */ - vk::Format GetSupportedDepthFormat(const std::vector& depthFormats = { vk::Format::eD32SfloatS8Uint, vk::Format::eD32Sfloat, vk::Format::eD24UnormS8Uint, vk::Format::eD16UnormS8Uint, vk::Format::eD16Unorm }) const - { - for (auto& format : depthFormats) - { - vk::FormatProperties formatProps; - formatProps = physicalDevice.getFormatProperties(format); - if (formatProps.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment) - { - return format; - } - } + [[nodiscard]] vk::Format GetSupportedDepthFormat(const std::vector& depthFormats = { vk::Format::eD32SfloatS8Uint, vk::Format::eD32Sfloat, vk::Format::eD24UnormS8Uint, vk::Format::eD16UnormS8Uint, vk::Format::eD16Unorm }) const; - throw std::runtime_error("No supported depth format"); - } + bool GetMemoryType(uint32_t typeBits, const vk::MemoryPropertyFlags& properties, uint32_t* typeIndex) const; - vk::Bool32 GetMemoryType(uint32_t typeBits, const vk::MemoryPropertyFlags& properties, uint32_t* typeIndex) const - { - for (uint32_t i = 0; i < 32; i++) - { - if ((typeBits & 1) == 1) - { - if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) - { - *typeIndex = i; - return VK_TRUE; - } - } - typeBits >>= 1; - } - return VK_FALSE; - } + [[nodiscard]] uint32_t GetMemoryType(uint32_t typeBits, const vk::MemoryPropertyFlags& properties) const; - uint32_t GetMemoryType(uint32_t typeBits, const vk::MemoryPropertyFlags& properties) const - { - uint32_t result = 0; - if (VK_FALSE == GetMemoryType(typeBits, properties, &result)) - { - throw std::runtime_error("Unable to find memory type " + to_string(properties)); - } - return result; - } - - vk::MemoryPropertyFlags GetMemoryFlags(uint32_t memoryType) const + [[nodiscard]] vk::MemoryPropertyFlags GetMemoryFlags(uint32_t memoryType) const { return memoryProperties.memoryTypes[memoryType].propertyFlags; } - - void Close() override - { - device.destroyCommandPool(graphicsCommandPool); - //TODO fill - } + void Close() override; }; } }