Refactor code

This commit is contained in:
2020-09-26 21:13:14 +02:00
parent 4f2122a2be
commit 079b55f0d0
9 changed files with 548 additions and 403 deletions

View File

@@ -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 <string>
namespace openVulkanoCpp
{
namespace RenderAPI

View File

@@ -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;

View File

@@ -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 <string>
#include <stdexcept>
#include <glm/glm.hpp>
@@ -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;

View File

@@ -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 <chrono>
#include <thread>
#include <stdexcept>
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<IPlatform>(PlatformProducer::CreatePlatform(renderApi));
window = platform->MakeWindow();
renderer = std::unique_ptr<IRenderer>(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<IGraphicsAppManager*>(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<float>(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();
}
}

View File

@@ -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 <chrono>
#include <thread>
#include <string>
#include <stdexcept>
#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 <string>
#include <memory>
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<IPlatform> platform;
std::unique_ptr<IRenderer> 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<IGraphicsAppManager*>(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<float>(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;
};
}

View File

@@ -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 <stdexcept>
#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<int>(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<int>(renderApi));
throw std::runtime_error("Unsupported render api requested!");
}
}
}

View File

@@ -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 <stdexcept>
#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<int>(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<int>(renderApi));
throw std::runtime_error("Unsupported render api requested!");
}
}
static IPlatform* CreatePlatform(RenderAPI::RenderApi renderApi);
};
}

View File

@@ -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<vk::DeviceQueueCreateInfo> createInfos;
std::vector<std::vector<float>> prioritiesVector;
public:
DeviceQueueCreateInfoBuilder() = default;
~DeviceQueueCreateInfoBuilder() = default;
void AddQueueFamily(const uint32_t queueFamilyIndex, const std::vector<float>& 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<float> priorities;
priorities.resize(count);
std::fill(priorities.begin(), priorities.end(), 0.0f);
AddQueueFamily(queueFamilyIndex, priorities);
}
std::vector<vk::DeviceQueueCreateInfo>& GetDeviceQueueCreateInfos()
{
return createInfos;
}
};
}
Device::Device(vk::PhysicalDevice& physicalDevice)
{
this->physicalDevice = physicalDevice;
useDebugMarkers = false;
QueryDevice();
}
void Device::PrepareDevice(const vk::ArrayProxy<const std::string>& 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<void(const vk::CommandBuffer&)>& 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<size_t>(file.tellg());
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
vk::ShaderModuleCreateInfo smci = { {}, buffer.size(), reinterpret_cast<const uint32_t*>(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<const std::string>& 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<vk::DeviceQueueCreateInfo> deviceQueueCreateInfos = deviceQueueCreateInfoBuilder.GetDeviceQueueCreateInfos();
deviceCreateInfo.queueCreateInfoCount = static_cast<uint32_t>(deviceQueueCreateInfos.size());
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos.data();
std::vector<const char*> 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<uint32_t>(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<vk::Format>& 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
}
}

View File

@@ -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 <vulkan/vulkan.hpp>
#include <set>
#include <functional>
#include <fstream>
#include "../Base/ICloseable.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
class DeviceQueueCreateInfoBuilder
{
std::vector<vk::DeviceQueueCreateInfo> createInfos;
std::vector<std::vector<float>> prioritiesVector;
public:
DeviceQueueCreateInfoBuilder() = default;
~DeviceQueueCreateInfoBuilder() = default;
void AddQueueFamily(const uint32_t queueFamilyIndex, const std::vector<float>& 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<float> priorities;
priorities.resize(count);
std::fill(priorities.begin(), priorities.end(), 0.0f);
AddQueueFamily(queueFamilyIndex, priorities);
}
std::vector<vk::DeviceQueueCreateInfo>& 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<const std::string>& 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<const std::string>& requestedExtensions, const vk::SurfaceKHR& surface);
graphicsQueue = device.getQueue(queueIndices.graphics, 0);
graphicsCommandPool = device.createCommandPool({ vk::CommandPoolCreateFlagBits::eResetCommandBuffer, queueIndices.graphics, });
}
std::set<std::string> GetExtensions() const
[[nodiscard]] const std::set<std::string>& GetExtensions() const
{
return supportedExtensions;
}
bool IsExtensionAvailable(const vk::ArrayProxy<const std::string>& extensions) const
[[nodiscard]] bool IsExtensionAvailable(const vk::ArrayProxy<const std::string>& 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<void(const vk::CommandBuffer& commandBuffer)>& 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<void(const vk::CommandBuffer& commandBuffer)>& 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<size_t>(file.tellg());
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
vk::ShaderModuleCreateInfo smci = { {}, buffer.size(), reinterpret_cast<const uint32_t*>(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<const std::string>& requestedExtensions);
void BuildDevice(const vk::ArrayProxy<const std::string>& 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<vk::DeviceQueueCreateInfo> deviceQueueCreateInfos = deviceQueueCreateInfoBuilder.GetDeviceQueueCreateInfos();
deviceCreateInfo.queueCreateInfoCount = static_cast<uint32_t>(deviceQueueCreateInfos.size());
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos.data();
std::vector<const char*> 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<uint32_t>(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<vk::Format>& 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<vk::Format>& 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;
};
}
}