first release

This commit is contained in:
2019-10-14 23:02:51 +02:00
commit 542ef348ee
72 changed files with 5990 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
#pragma once
#include <stdint.h>
#include <algorithm>
namespace openVulkanoCpp
{
class EngineConfiguration
{
private:
EngineConfiguration() = default;
~EngineConfiguration() = default;
uint32_t numThreads = 1;
public:
static EngineConfiguration* GetEngineConfiguration()
{
static EngineConfiguration* config = new EngineConfiguration();
return config;
}
void SetNumThreads(uint32_t numThreads)
{
this->numThreads = numThreads;
}
uint32_t GetNumThreads() const
{
return std::max(static_cast<uint32_t>(1), numThreads);
}
};
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <string>
#include <spdlog/fmt/fmt.h> //TODO replace with external fmt
namespace openVulkanoCpp
{
#define MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
const char* ENGINE_NAME = "openVulkanoCpp";
struct EngineVersion
{
int major, minor, patch;
int intVersion;
std::string stringVersion;
EngineVersion(int major, int minor, int patch, int build = 0) : major(major), minor(minor), patch(patch)
{
intVersion = ((major) << 24) | ((minor) << 16) | (patch);
std::string buildConfig = "";
#ifdef _DEBUG
buildConfig += "-MSVC_DEBUG";
#elif DEBUG
buildConfig += "-DEBUG";
#endif
stringVersion = fmt::format("v{0}.{1}.{2}.{3}{4}", major, minor, patch, build, buildConfig);
}
};
const EngineVersion ENGINE_VERSION(0, 0, 1);
}

View File

@@ -0,0 +1,12 @@
#pragma once
namespace openVulkanoCpp
{
class ICloseable
{
public:
virtual ~ICloseable() = default;
virtual void Close() = 0;
};
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <string>
#include "IInitable.hpp"
#include "ITickable.hpp"
#include "ICloseable.hpp"
namespace openVulkanoCpp
{
class IGraphicsAppManager;
class IGraphicsApp : public IInitable, public ITickable, public ICloseable
{
private:
IGraphicsAppManager* manager = nullptr;
public:
virtual ~IGraphicsApp() = default;
IGraphicsAppManager* GetGraphicsAppManager() const { return manager; }
void SetGraphicsAppManager(IGraphicsAppManager* manager) { this->manager = manager; }
virtual std::string GetAppName() = 0;
virtual std::string GetAppVersion() = 0;
virtual int GetAppVersionAsInt() = 0;
};
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
#include "PlatformEnums.hpp"
namespace openVulkanoCpp
{
class IWindow;
class IGraphicsApp;
class IRenderer;
class IGraphicsAppManager
{
public:
virtual ~IGraphicsAppManager() = default;
virtual RenderAPI::RenderApi GetRenderApi() const = 0;
virtual IGraphicsApp* GetGraphicsApp() const = 0;
virtual IRenderer* GetRenderer() const = 0;
virtual bool IsRunning() const = 0;
virtual bool IsPaused() const = 0;
virtual void Stop() = 0;
virtual void Run() = 0;
virtual void Pause() = 0;
virtual void Resume() = 0;
virtual float GetAvgFrameTime() const = 0;
virtual float GetAvgFps() const = 0;
virtual uint64_t GetFrameCount() const = 0;
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
namespace openVulkanoCpp
{
class IInitable
{
public:
virtual ~IInitable() = default;
virtual void Init() = 0;
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
namespace openVulkanoCpp
{
class ITickable
{
public:
virtual ~ITickable() = default;
virtual void Tick() = 0;
};
}

View File

@@ -0,0 +1,13 @@
#include "Logger.hpp"
namespace openVulkanoCpp
{
std::vector<spdlog::sink_ptr> Logger::sinks;
std::shared_ptr<spdlog::logger> Logger::WINDOW = nullptr;
std::shared_ptr<spdlog::logger> Logger::MANAGER = nullptr;
std::shared_ptr<spdlog::logger> Logger::RENDER = nullptr;
std::shared_ptr<spdlog::logger> Logger::PHYSIC = nullptr;
std::shared_ptr<spdlog::logger> Logger::AUDIO = nullptr;
std::shared_ptr<spdlog::logger> Logger::DATA = nullptr;
std::shared_ptr<spdlog::logger> Logger::SCENE = nullptr;
}

View File

@@ -0,0 +1,95 @@
#pragma once
#define SPDLOG_DEBUG_ON
#define SPDLOG_TRACE_ON
#include <iostream>
#include <filesystem>
#include <spdlog/spdlog.h>
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/null_sink.h"
#ifndef NO_CONSOLE_LOG
#include <spdlog/sinks/stdout_color_sinks.h>
#endif
#ifdef _MSC_VER
#include "spdlog/sinks/msvc_sink.h"
#endif
namespace openVulkanoCpp
{
class Logger
{ //TODO add custom sink for in game/engine console
static std::vector<spdlog::sink_ptr> sinks;
public:
static std::shared_ptr<spdlog::logger> WINDOW;
static std::shared_ptr<spdlog::logger> MANAGER;
static std::shared_ptr<spdlog::logger> RENDER;
static std::shared_ptr<spdlog::logger> PHYSIC;
static std::shared_ptr<spdlog::logger> AUDIO;
static std::shared_ptr<spdlog::logger> DATA;
static std::shared_ptr<spdlog::logger> SCENE;
static void SetupLogger(std::string logFolder = "logs", std::string logFile = "openVulkano.log")
{
static bool initialized = false;
if (initialized) return;
try
{
try
{ //TODO allow log files in folders
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logFile, 1024 * 1024 * 512, 3, true));
}
catch (const spdlog::spdlog_ex& e)
{
std::cerr << "Log create file log sink: " << e.what() << std::endl;
}
#ifndef NO_CONSOLE_LOG
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
#endif
#ifdef _MSC_VER // If it was build with msvc in debug we can use the msvc sink
sinks.push_back(std::make_shared<spdlog::sinks::msvc_sink_mt>());
#endif
// Make sure that there is always a sink for the loggers
if (sinks.empty()) sinks.push_back(std::make_shared<spdlog::sinks::null_sink_mt>());
MANAGER = CreateLogger("manager");
WINDOW = CreateLogger("window");
RENDER = CreateLogger("render");
PHYSIC = CreateLogger("physic");
AUDIO = CreateLogger("audio");
DATA = CreateLogger("data");
SCENE = CreateLogger("scene");
spdlog::flush_every(std::chrono::seconds(5));
MANAGER->info("Logger initialized");
initialized = true;
}
catch (const spdlog::spdlog_ex& e)
{
std::cerr << "Log initialization failed: " << e.what() << std::endl;
}
}
/**
* \brief Creates a new custom logger that writes to the main log file.
* \param name The name of the logger
* \param reg If set to true the logger can be accessed again with spdlog::get(name)
* \return The created logger
*/
static std::shared_ptr<spdlog::logger> CreateLogger(const std::string& name, const bool reg = true)
{
auto logger = std::make_shared<spdlog::logger>(name, sinks.begin(), sinks.end());
if (reg) spdlog::register_logger(logger);
#ifdef LOG_DATE
logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [T%t] [%^%l%$] [%n]: %v");
#else
logger->set_pattern("[%H:%M:%S.%e] [T%t] [%^%l%$] [%n]: %v");
#endif
#ifdef DEBUG
logger->set_level(spdlog::level::debug);
#endif
return logger;
}
};
}

View File

@@ -0,0 +1,45 @@
#pragma once
namespace openVulkanoCpp
{
namespace RenderAPI
{
enum RenderApi
{
VULKAN = 0,
//OpenGL,
//DirectX11,
//DirectX12,
MAX_VALUE
};
inline std::string ToString(RenderApi api)
{
switch (api)
{
case VULKAN: return "Vulkan";
}
return "Invalid";
}
}
namespace Platform
{
enum Platform
{
Windows = 0, MacOS, Linux, Android, MAX_VALUE
};
inline std::string ToString(Platform os)
{
switch (os)
{
case Windows: return "Windows";
case MacOS: return "Windows";
case Linux: return "Windows";
case Android: return "Windows";
}
return "Invalid";
}
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <string>
#include "../ITickable.hpp"
#include "../ICloseable.hpp"
#include "../../Scene/Scene.hpp"
namespace openVulkanoCpp
{
class IWindow;
class IGraphicsAppManager;
class IRenderer : virtual public ITickable, virtual public ICloseable
{
public:
virtual ~IRenderer() = default;
virtual void Init(IGraphicsAppManager* graphicsAppManager, IWindow* window) = 0;
virtual std::string GetMainRenderDeviceName() = 0;
virtual void Resize(uint32_t newWidth, uint32_t newHeight) = 0;
virtual void SetScene(Scene::Scene* scene) = 0;
virtual Scene::Scene* GetScene() = 0;
};
}

View File

@@ -0,0 +1,98 @@
#pragma once
#include <chrono>
#include <cstdint>
namespace openVulkanoCpp
{
/**
* \brief High-res timer
*/
class Timer
{ //TODO maybe add a Windows option that uses QPC https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
std::chrono::high_resolution_clock::time_point tPrev, tStop;
int64_t tickNanoseconds, tickMilliseconds;
uint64_t totalNanoseconds;
double tickSeconds, totalSeconds;
bool stopped;
public:
Timer()
{
Reset();
}
~Timer() = default;
void Reset()
{
tickNanoseconds = 0;
tickMilliseconds = 0;
tickSeconds = 0;
totalNanoseconds = 0;
totalSeconds = 0;
tPrev = std::chrono::high_resolution_clock::now();
stopped = false;
}
void Start()
{
if (stopped)
{
tPrev += std::chrono::high_resolution_clock::now() - tStop;
stopped = false;
}
}
void Stop()
{
tStop = std::chrono::high_resolution_clock::now();
stopped = true;
}
/**
* \brief Will update the timer
*/
void Tick()
{
if (stopped)
{
tickNanoseconds = 0;
}
else
{
const auto now = std::chrono::high_resolution_clock::now();
tickNanoseconds = std::chrono::duration<int64_t, std::nano>(now - tPrev).count();
tPrev = now;
if (tickNanoseconds < 0) tickNanoseconds = 0;
}
totalNanoseconds += tickNanoseconds;
tickMilliseconds = tickNanoseconds / 1000000;
tickSeconds = tickNanoseconds / 1000000000.0;
totalSeconds += tickSeconds;
}
int64_t GetTickNanoseconds() const { return tickNanoseconds; }
int64_t GetTickMilliseconds() const { return tickMilliseconds; }
double GetTickSeconds() const { return tickSeconds; }
uint64_t GetTotalNanoseconds() const { return totalNanoseconds; }
/**
* \brief Gets the total amount of seconds past since the timer has been started. This will drift over time!
* \return The summed total runtime of the timer.
*/
double GetTotalSeconds() const { return totalSeconds; }
/**
* \brief Will recalculate the past time from the total nanoseconds and return it. This is more precise but also slower.
* \return The calculated total runtime of the timer.
*/
double GetTotalSecondsPrecise()
{
totalSeconds = totalNanoseconds / 1000000000.0;
return totalSeconds;
}
};
}

View File

@@ -0,0 +1,99 @@
#pragma once
#include "IWindow.hpp"
namespace openVulkanoCpp
{
class BaseWindow : virtual public IWindow
{
const int windowId;
public:
BaseWindow() : windowId(CreateWindowId()) {}
virtual ~BaseWindow() = default;
void GetSize(int* width, int* height) override = 0;
void GetSize(uint32_t* width, uint32_t* height) override
{
int w, h;
GetSize(&w, &h);
*width = w;
*height = h;
}
uint32_t GetWidth() override
{
uint32_t width, height;
GetSize(&width, &height);
return width;
}
uint32_t GetHeight() override
{
uint32_t width, height;
GetSize(&width, &height);
return height;
}
glm::ivec2 GetSize() override
{
glm::ivec2 size;
this->GetSize(&size.x, &size.y);
return size;
}
void SetSize(uint32_t width, uint32_t height) override = 0;
void SetSize(glm::ivec2 size) override
{
SetSize(size.x, size.y);
}
void GetPosition(int* x, int* y) override = 0;
int GetPositionX() override
{
int x, y;
GetPosition(&x, &y);
return x;
}
int GetPositionY() override
{
int x, y;
GetPosition(&x, &y);
return y;
}
glm::ivec2 GetPosition() override
{
glm::ivec2 position;
GetPosition(&position.x, &position.y);
return position;
}
void SetPosition(int posX, int posY) override = 0;
void SetPosition(glm::ivec2 pos) override { SetPosition(pos.x, pos.y); }
void Show() override = 0;
void Hide() override = 0;
void Show(const bool show) override { if (show) Show(); else Hide(); }
IVulkanWindow* GetVulkanWindow() override
{
return nullptr;
}
IOpenGlWindow* GetOpenGlWindow() override
{
return nullptr;
}
int GetWindowId() const override
{
return windowId;
}
};
}

View File

@@ -0,0 +1,113 @@
#pragma once
#include <string>
#include <stdexcept>
#include <glm/glm.hpp>
#include <vulkan/vulkan.hpp>
#include "../PlatformEnums.hpp"
#include "../ITickable.hpp"
#include "../ICloseable.hpp"
namespace openVulkanoCpp
{
enum WindowMode
{
WINDOWED, BORDERLESS, FULLSCREEN, BORDERLESS_FULLSCREEN
};
class IWindowHandler;
class IVulkanWindow;
class IOpenGlWindow;
class IWindow : public ITickable, public ICloseable
{
public:
virtual ~IWindow() = default;
virtual void Init(RenderAPI::RenderApi renderApi) = 0;
virtual const std::string& GetTitle() = 0;
virtual void SetTitle(const std::string& title) = 0;
virtual WindowMode GetWindowMode() = 0;
virtual void SetWindowMode(WindowMode) = 0;
virtual void SetFullscreen() { SetWindowMode(FULLSCREEN); }
virtual void SetWindowed() { SetWindowMode(WINDOWED); }
virtual uint32_t GetWidth() = 0;
virtual uint32_t GetHeight() = 0;
virtual void GetSize(int* width, int* height) = 0;
virtual void GetSize(uint32_t* width, uint32_t* height) = 0;
virtual glm::ivec2 GetSize() = 0;
virtual void SetSize(uint32_t width, uint32_t height) = 0;
virtual void SetSize(glm::ivec2 size) { SetSize(size.x, size.y); }
virtual void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) = 0;
virtual int GetPositionX() = 0;
virtual int GetPositionY() = 0;
virtual void GetPosition(int* x, int* y) = 0;
virtual glm::ivec2 GetPosition() = 0;
virtual void SetPosition(int posX, int posY) = 0;
virtual void SetPosition(glm::ivec2 pos) = 0;
virtual void Show() = 0;
virtual void Hide() = 0;
virtual void Show(bool show) = 0;
virtual IWindowHandler* GetWindowHandler() = 0;
virtual void SetWindowHandler(IWindowHandler* handler) = 0;
/**
* \brief Gets the vulkan window implementation of the window.
* \return The IVulkanWindow reference of the window. nullptr if the current Window dose not implement IVulkanWindow
*/
virtual IVulkanWindow* GetVulkanWindow() = 0;
virtual IOpenGlWindow* GetOpenGlWindow() = 0;
virtual int GetWindowId() const = 0;
protected:
static int CreateWindowId()
{
static int id = 0;
return id++;
}
};
class IVulkanWindow : virtual public IWindow
{
public:
virtual ~IVulkanWindow() = default;
virtual vk::SurfaceKHR CreateSurface(const vk::Instance& instance, const vk::AllocationCallbacks* pAllocator = nullptr) = 0;
virtual std::vector<std::string> GetRequiredInstanceExtensions() = 0;
};
class IOpenGlWindow : virtual public IWindow
{
public:
virtual ~IOpenGlWindow() = default;
virtual void MakeCurrentThread() = 0;
virtual void Present() const = 0;
};
class IWindowHandler
{
public:
virtual ~IWindowHandler() = default;
virtual void OnWindowMinimize(IWindow* window) = 0;
virtual void OnWindowRestore(IWindow* window) = 0;
virtual void OnWindowFocusLost(IWindow* window) = 0;
virtual void OnWindowFocusGained(IWindow* window) = 0;
virtual void OnWindowMove(IWindow* window, int posX, int posY) = 0;
virtual void OnWindowResize(IWindow* window, uint32_t newWidth, uint32_t newHeight) = 0;
virtual void OnWindowClose(IWindow* window) = 0;
};
class WindowInitFailedException : public std::runtime_error
{
public:
WindowInitFailedException(char const* const message) : runtime_error(message) {}
};
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include <vector>
#include <string>
#include <set>
#include <algorithm>
namespace openVulkanoCpp
{
class Utils
{
public:
static std::vector<const char*> toCString(const std::vector<std::string>& values)
{
std::vector<const char*> result;
result.reserve(values.size());
for (const auto& string : values) {
result.push_back(string.c_str());
}
return result;
}
static std::vector<const char*> toCString(const std::set<std::string>& values)
{
std::vector<const char*> result;
result.reserve(values.size());
for (const auto& string : values) {
result.push_back(string.c_str());
}
return result;
}
template <typename T>
static bool Contains(std::vector<T>& vec, const T& element)
{
return (std::find(vec.begin(), vec.end(), element) != vec.end());
}
template <typename T>
static void Remove(std::vector<T>& vec, const T& element)
{
vec.erase(std::remove(vec.begin(), vec.end(), element), vec.end());
}
template <typename Enumeration>
static auto EnumAsInt(Enumeration const value)
-> typename std::underlying_type<Enumeration>::type
{
return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}
static bool MatchesAnyElementWise(const glm::vec3& a, const glm::vec3& b)
{
return a.x == b.x || a.y == b.y || a.z == b.z;
}
};
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include <atomic>
#include <vector>
namespace openVulkanoCpp
{
namespace Data
{
template <class T>
class ReadOnlyAtomicArrayQueue final
{
T* data;
std::atomic<size_t> size;
public:
ReadOnlyAtomicArrayQueue(std::vector<T>& data)
{
this->data = data.data();
size.store(data.size());
}
ReadOnlyAtomicArrayQueue(T* data, size_t size)
{
this->data = data;
this->size.store(size);
}
~ReadOnlyAtomicArrayQueue() = default;
size_t GetSize() const
{
return size.load(std::memory_order_relaxed);
}
T* Pop()
{
size_t s = size.load(std::memory_order_relaxed);
while (size > 0 && !size.compare_exchange_weak(s, s - 1));
if (s > 0) return &data[s - 1];
return nullptr;
}
};
}
}

View File

@@ -0,0 +1,227 @@
#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"
namespace openVulkanoCpp
{
/**
* \brief A simple GraphicsAppManager. It can only handle on window.
*/
class GraphicsAppManager : virtual public IGraphicsAppManager, virtual public IWindowHandler
{
private:
IWindow* window;
IGraphicsApp* app;
IRenderer* renderer;
RenderAPI::RenderApi renderApi;
bool paused = false, running = false;
float fpsTimer = 0, avgFps = 0, avgFrameTime = 0;
uint64_t frameCount = 0, lastFrameCount = 0;
Timer* frameTimer;
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)
{
const auto msg = "The app must not be null!";
Logger::MANAGER->error(msg);
throw std::runtime_error(msg);
}
window = PlatformProducer::CreateBestWindow(renderApi);
renderer = PlatformProducer::CreateRenderManager(renderApi);
app->SetGraphicsAppManager(this);
window->SetWindowHandler(this);
frameTimer = new Timer();
}
~GraphicsAppManager() override
{
delete renderer;
delete window;
delete frameTimer;
}
public: // Getter
RenderAPI::RenderApi GetRenderApi() const override
{
return renderApi;
}
IGraphicsApp* GetGraphicsApp() const override
{
return app;
}
IRenderer* GetRenderer() const override
{
return renderer;
}
bool IsRunning() const override
{
return running;
}
bool IsPaused() const override
{
return paused;
}
public: // Setter
void Stop() override
{
running = false;
Logger::MANAGER->info("Graphics application stopped");
}
void Pause() override
{
paused = true;
frameTimer->Stop();
Logger::MANAGER->info("Graphics application paused");
}
void Resume() override
{
paused = false;
frameTimer->Start();
Logger::MANAGER->info("Graphics application resumed");
}
public:
void Run() override
{
running = true;
StartUp();
frameTimer->Reset();
Loop(); // Runs the rendering loop
ShutDown();
}
private:
void StartUp()
{
try
{
Logger::MANAGER->info("Initializing ...");
app->Init();
window->Init(renderApi);
//TODO restore window settings if there are any set
renderer->Init((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 Loop()
{
while (running)
{
window->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 ShutDown() const
{
Logger::MANAGER->info("Shutting down ...");
renderer->Close();
window->Close();
app->Close();
Logger::MANAGER->info("Shutdown complete");
}
void 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;
window->SetTitle(fmt::format(windowTitleFormat, avgFps, avgFrameTime));
}
}
public: //FPS stuff
uint64_t GetFrameCount() const override
{
return frameCount;
}
float GetAvgFrameTime() const override
{
return avgFrameTime;
}
float GetAvgFps() const override
{
return avgFps;
}
public: // Window Manager
void OnWindowMinimize(IWindow* window) override
{
if (window != this->window) return;
Pause();
}
void OnWindowRestore(IWindow* window) override
{
if (window != this->window) return;
Resume();
}
void OnWindowFocusLost(IWindow* window) override {}
void OnWindowFocusGained(IWindow* window) override {}
void OnWindowMove(IWindow* window, int posX, int posY) override {} //TODO save window pos
void OnWindowResize(IWindow* window, const uint32_t newWidth, const uint32_t newHeight) override
{
if(window != this->window) return;
renderer->Resize(newWidth, newHeight);
}
void OnWindowClose(IWindow* window) override
{
if (window != this->window) return;
Stop();
}
};
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include <stdexcept>
#include "../Base/Logger.hpp"
#include "../Vulkan/Renderer.hpp"
#include "../Base/PlatformEnums.hpp"
#include "WindowGLFW.hpp"
namespace openVulkanoCpp
{
/**
* \brief Helper class the produces all the platform depending classes
*/
class PlatformProducer
{
public:
/**
* \brief Creates the renderer for the given render api
* \param renderApi The render api that should be used
* \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!");
}
}
/**
* \brief Creates a window that fits best for the current environment
* \param renderApi The render api that should be used when searching for the best suited window
* \return The created window. nullptr if no window is supported on the current platform
* \throws std::runtime_error if the render api is not supported
*/
static IWindow* CreateBestWindow(RenderAPI::RenderApi renderApi)
{ //TODO add more windows to chose from
switch(renderApi)
{
case RenderAPI::VULKAN: return new WindowGLFW();
default:
Logger::RENDER->error("Unsupported render api requested! Requested %d", static_cast<int>(renderApi));
throw std::runtime_error("Unsupported render api requested!");
}
}
};
}

View File

@@ -0,0 +1,374 @@
#pragma once
#include <GLFW/glfw3.h>
#include "../Base/UI/BaseWindow.hpp"
#include "../Base/Logger.hpp"
namespace openVulkanoCpp
{
class WindowGLFW : public BaseWindow, virtual public IVulkanWindow, virtual public IOpenGlWindow
{
private:
GLFWwindow* window = nullptr;
uint32_t width = 1280, height = 720;
std::string title = "Window Title";
WindowMode windowMode = WINDOWED;
IWindowHandler* handler = nullptr;
public:
WindowGLFW() = default;
virtual ~WindowGLFW() { if (window != nullptr) Close(); }
protected:
void Create()
{
window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if(!window) return;
glfwSetWindowUserPointer(window, this);
RegisterCallbacks();
}
void RegisterCallbacks() const
{
glfwSetErrorCallback(ErrorCallback);
glfwSetDropCallback(window, DropCallback);
glfwSetFramebufferSizeCallback(window, ResizeCallback);
glfwSetWindowFocusCallback(window, FocusCallback);
glfwSetWindowRefreshCallback(window, RefreshCallback);
glfwSetWindowIconifyCallback(window, MinimizeCallback);
glfwSetWindowPosCallback(window, WindowMoveCallback);
glfwSetWindowCloseCallback(window, CloseCallback);
// Input Callbacks
glfwSetKeyCallback(window, KeyboardCallback);
glfwSetMouseButtonCallback(window, MouseButtonCallback);
glfwSetCursorPosCallback(window, MouseMoveCallback);
glfwSetScrollCallback(window, MouseScrollCallback);
}
static GLFWmonitor* GetPrimaryMonitor()
{
return glfwGetPrimaryMonitor();
}
static std::vector<GLFWmonitor*> GetMonitors()
{
int count;
GLFWmonitor** monitorsArray = glfwGetMonitors(&count);
std::vector<GLFWmonitor*> monitors;
monitors.reserve(count);
for (int i = 0; i < count; i++)
{
monitors[i] = monitorsArray[i];
}
return monitors;
}
public: // IWindow implementation
void Init(RenderAPI::RenderApi renderApi) override
{
if (!glfwInit()) throw WindowInitFailedException("Failed to initialize glfw");
if(renderApi == RenderAPI::VULKAN) glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
Create();
if(!window)
{
glfwTerminate();
throw WindowInitFailedException("Failed to initialize window");
}
if (renderApi != RenderAPI::VULKAN) MakeCurrentThread();
Logger::WINDOW->info("GLFW Window created (id: {0})", GetWindowId());
}
void Close() override
{
glfwDestroyWindow(window);
window = nullptr;
glfwTerminate();
Logger::WINDOW->info("GLFW Window destroyed (id: {0})", GetWindowId());
}
void Present() const override
{
glfwSwapBuffers(window);
}
void Show() override
{
glfwShowWindow(window);
}
void Hide() override
{
glfwHideWindow(window);
}
void Tick() override
{
glfwPollEvents();
}
void SetTitle(const std::string& title) override
{
this->title = title;
glfwSetWindowTitle(window, title.c_str());
}
const std::string& GetTitle() override
{
return title;
}
void SetSize(uint32_t width, uint32_t height) override
{
if (!window)
{
this->width = width;
this->height = height;
}
else
{
glfwSetWindowSize(window, width, height);
}
}
void SetPosition(int posX, int posY) override
{
glfwSetWindowPos(window, posX, posY);
}
void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) override
{
minWidth = (minWidth < 0) ? GLFW_DONT_CARE : minWidth;
minHeight = (minHeight < 0) ? GLFW_DONT_CARE : minHeight;
maxWidth = (maxWidth < 0) ? GLFW_DONT_CARE : maxWidth;
maxHeight = (maxHeight < 0) ? GLFW_DONT_CARE : maxHeight;
glfwSetWindowSizeLimits(window, minWidth, minHeight, maxWidth, maxHeight);
}
void MakeCurrentThread() override
{
glfwMakeContextCurrent(window);
}
void SetWindowMode(WindowMode windowMode) override
{
if(windowMode == this->windowMode) return; //Nothing change here
//TODO
this->windowMode = windowMode;
}
void SetWindowHandler(IWindowHandler* handler) override
{
this->handler = handler;
}
IVulkanWindow* GetVulkanWindow() override
{
return this;
}
IOpenGlWindow* GetOpenGlWindow() override
{
return this;
}
// Status getter
WindowMode GetWindowMode() override
{
return windowMode;
}
void GetSize(int* width, int* height) override
{
glfwGetWindowSize(window, width, height);
}
void GetPosition(int* x, int* y) override
{
glfwGetWindowPos(window, x, y);
}
IWindowHandler* GetWindowHandler() override
{
return handler;
}
//IVulkanWindow stuff
vk::SurfaceKHR CreateSurface(const vk::Instance& instance, const vk::AllocationCallbacks* pAllocator) override
{
VkSurfaceKHR rawSurface;
const auto result = static_cast<vk::Result>(glfwCreateWindowSurface(static_cast<VkInstance>(instance), window, reinterpret_cast<const VkAllocationCallbacks*>(pAllocator), &rawSurface));
return createResultValue(result, rawSurface, "vk::CommandBuffer::begin");
}
std::vector<std::string> GetRequiredInstanceExtensions() override
{
return GetVulkanRequiredInstanceExtensions();
}
public: // Window events
void OnResize(const uint32_t newWidth, const uint32_t newHeight)
{
Logger::WINDOW->debug("Window (id: {0}) resized (width: {1}, height: {2})", GetWindowId(), newWidth, newHeight);
handler->OnWindowResize(this, newWidth, newHeight);
}
void OnMinimize()
{
Logger::WINDOW->debug("Window (id: {0}) minimized", GetWindowId());
handler->OnWindowMinimize(this);
}
void OnRestore()
{
Logger::WINDOW->debug("Window (id: {0}) restored", GetWindowId());
handler->OnWindowRestore(this);
}
void OnFocusLost()
{
Logger::WINDOW->debug("Window (id: {0}) focus lost", GetWindowId());
handler->OnWindowFocusLost(this);
}
void OnFocusGained()
{
Logger::WINDOW->debug("Window (id: {0}) focus gained", GetWindowId());
handler->OnWindowFocusGained(this);
}
void OnMove(const int posX, const int posY)
{
Logger::WINDOW->debug("Window (id: {0}) moved (x: {1}, y: {2})", GetWindowId(), posX, posY);
handler->OnWindowMove(this, posX, posY);
}
void OnClose()
{
Logger::WINDOW->debug("Window (id: {0}) closed", GetWindowId());
handler->OnWindowClose(this);
}
public: // Input events TODO
virtual void OnKeyPressed(int key, int mods) {}
virtual void OnKeyReleased(int key, int mods) {}
virtual void OnMousePressed(int button, int mods) {}
virtual void OnMouseReleased(int button, int mods) {}
virtual void OnMouseMoved(double posX, double posY) {}
virtual void OnMouseScrolled(double delta) {}
protected:
virtual void OnKeyEvent(int key, int scanCode, int action, int mods)
{
switch (action)
{
case GLFW_PRESS: OnKeyPressed(key, mods); break;
case GLFW_RELEASE: OnKeyReleased(key, mods); break;
default: break;
}
}
virtual void OnMouseButtonEvent(int button, int action, int mods)
{
switch (action)
{
case GLFW_PRESS: OnMousePressed(button, mods); break;
case GLFW_RELEASE: OnMouseReleased(button, mods); break;
default: break;
}
}
private: // Callbacks
static WindowGLFW* GetWindow(GLFWwindow* window)
{
return static_cast<WindowGLFW*>(glfwGetWindowUserPointer(window));
}
static void KeyboardCallback(GLFWwindow* window, int key, int scanCode, int action, int mods)
{
GetWindow(window)->OnKeyEvent(key, scanCode, action, mods);
}
static void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{
GetWindow(window)->OnMouseButtonEvent(button, action, mods);
}
static void MouseMoveCallback(GLFWwindow* window, double posX, double posY)
{
GetWindow(window)->OnMouseMoved(posX, posY);
}
static void MouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset)
{
GetWindow(window)->OnMouseScrolled(yOffset);
}
static void ResizeCallback(GLFWwindow* window, int width, int height)
{
GetWindow(window)->OnResize(width, height);
}
static void FocusCallback(GLFWwindow* window, const int focused)
{
if (focused == GLFW_TRUE)
GetWindow(window)->OnFocusGained();
else
GetWindow(window)->OnFocusLost();
}
static void MinimizeCallback(GLFWwindow* window, const int minimized)
{
if(minimized == GLFW_TRUE)
GetWindow(window)->OnMinimize();
else
GetWindow(window)->OnRestore();
}
static void RefreshCallback(GLFWwindow* window)
{
//TODO is there really anything to do? or is it ok if the window is only redrawn on the next frame?
}
static void WindowMoveCallback(GLFWwindow* window, const int posX, const int posY)
{
GetWindow(window)->OnMove(posX, posY);
}
static void CloseCallback(GLFWwindow* window)
{
GetWindow(window)->OnClose();
}
static void DropCallback(GLFWwindow* window, const int count, const char** paths)
{
//TODO something useful
}
static void ErrorCallback(const int error, const char* description)
{
Logger::WINDOW->error("GLFW error (e{0}): {1}", error, description);
}
public:
static std::vector<std::string> GetVulkanRequiredInstanceExtensions()
{
std::vector<std::string> result;
uint32_t count = 0;
const char** names = glfwGetRequiredInstanceExtensions(&count);
if (names && count)
{
for (uint32_t i = 0; i < count; ++i)
{
result.emplace_back(names[i]);
}
}
return result;
}
};
}

View File

@@ -0,0 +1,98 @@
#pragma once
#include <glm/glm.hpp>
#include "../Base/IInitable.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
/**
* \brief A class that represents an axis aligned bounding box
*/
class AABB final : public virtual IInitable
{
glm::vec3 min, max;
public:
AABB() : min(INFINITY), max(-INFINITY) {}
~AABB() = default;
/**
* \brief Initiates the AABB to min=Inf, max=-Inf
*/
void Init() override
{
min = glm::vec3(INFINITY);
max = glm::vec3(-INFINITY);
}
/**
* \brief Initiates the AABB to a single point (min=max=point)
* \param point The point that should be used as min and max of the AABB
*/
void Init(const glm::vec3& point)
{
min = max = point;
}
/**
* \brief Initiates the AABB from some other AABB
* \param other The other AABB that should be copied
*/
void Init(const AABB& other)
{
min = other.GetMin();
max = other.GetMax();
}
const glm::vec3& GetMin() const { return min; }
const glm::vec3& GetMax() const { return max; }
void Grow(const glm::vec3& point)
{
min = glm::min(min, point);
max = glm::max(max, point);
}
void Grow(const AABB& otherAABB)
{
min = glm::min(min, otherAABB.GetMin());
max = glm::max(max, otherAABB.GetMax());
}
void Grow(const AABB& otherAABB, glm::mat4x4 transformation)
{
//TODO
}
glm::vec3 GetDiagonal() const
{
return max - min;
}
glm::vec3 GetCenter() const
{
return min + (GetDiagonal() * 0.5f);
}
/**
* \brief Checks if the AABB overlaps with an other AABB
* \param other The other AABB that should be checked
* \return true if the AABB overlaps with the other, false if not
*/
bool IsOverlapping(const AABB& other) const
{
return !(other.min.x > max.x || other.max.x < min.x || other.min.y > max.y || other.max.y < min.y || other.min.z > max.z || other.max.z < min.z);
}
/**
* \brief Resets the AABB to min=Inf, max=-Inf, same as Init()
*/
void Reset()
{
Init();
}
};
}
}

View File

@@ -0,0 +1,169 @@
#pragma once
#define _USE_MATH_DEFINES
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "Node.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
class Camera : public Node
{
protected:
float nearPlane, farPlane;
float width, height;
public:
glm::mat4x4 projection, view, viewProjection;
Camera() = default;
virtual ~Camera() = default;
public:
void Init(float width, float height, float nearPlane, float farPlane)
{
this->width = width;
this->height = height;
this->nearPlane = nearPlane;
this->farPlane = farPlane;
Node::Init();
UpdateProjectionMatrix();
}
virtual void SetSize(const float& width, const float& height)
{
this->width = width;
this->height = height;
UpdateProjectionMatrix();
}
void SetNearPlane(float nearPlane)
{
this->nearPlane = nearPlane;
}
void SetFarPlane(float farPlane)
{
this->farPlane = farPlane;
}
float NearPlane() const
{
return nearPlane;
}
float FarPlane() const
{
return farPlane;
}
virtual void UpdateProjectionMatrix() = 0;
void UpdateViewProjectionMatrix()
{ // In vulkan the screen space is defined as y=0=top and y=1=bottom and thus the coordinate have to be flipped
viewProjection = projection * glm::mat4x4(1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1) * view;
}
void UpdateWorldMatrix(const glm::mat4x4& parentWorldMat) override
{
Node::UpdateWorldMatrix(parentWorldMat);
view = glm::inverse(GetWorldMatrix());
UpdateViewProjectionMatrix();
}
const glm::mat4x4& GetViewProjectionMatrix() const
{
return viewProjection;
}
const glm::mat4x4* GetViewProjectionMatrixPointer() const
{
return &viewProjection;
}
};
class PerspectiveCamera : public Camera
{
protected:
float fov, aspect;
public:
void Init(float fovDegrees, float width, float height, float nearPlane, float farPlane)
{
this->fov = glm::radians(fovDegrees);
aspect = width / height;
Camera::Init(width, height, nearPlane, farPlane);
}
void SetSize(const float& width, const float& height) override
{
aspect = width / height;
Camera::SetSize(width, height);
}
void SetAspect(const float& aspect)
{
this->aspect = aspect;
Camera::SetSize(aspect, 1);
}
void SetFovX(const float& fov)
{
SetFov(2 * atan(tan(fov * 0.5f) * aspect));
}
void SetFovXRad(const float& fov)
{
SetFovRad(2 * atan(tan(fov * 0.5f) * aspect));
}
void SetFov(const float& fov)
{
SetFovRad(glm::radians(fov));
}
void SetFovRad(const float& fov)
{
this->fov = fov;
}
float GetFov() const
{
return glm::degrees(fov);
}
float GetFovX() const
{
return 2 * atan(tan(GetFov() * 0.5f) * aspect);
}
float GetFovRad() const
{
return fov;
}
float GetFovXRad() const
{
return 2 * atan(tan(fov * 0.5f) * aspect);
}
void UpdateProjectionMatrix() override
{
projection = glm::perspectiveLH_ZO(fov, aspect, nearPlane, farPlane);
UpdateViewProjectionMatrix();
}
};
class OrthographicCamera : public Camera
{
public:
void UpdateProjectionMatrix() override
{
const float widthHalf = width * 0.5f, heightHalf = height * 0.5f;
projection = glm::orthoLH_ZO(-widthHalf, widthHalf, -heightHalf, heightHalf, nearPlane, farPlane);
UpdateViewProjectionMatrix();
}
};
}
}

View File

@@ -0,0 +1,26 @@
#include "Drawable.hpp"
#include "Scene.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
void Drawable::SetScene(Scene* scene)
{
if (this->scene == scene) return;
if (scene && this->scene) throw std::runtime_error("Drawable has been associated with a scene already!");
this->scene = scene;
if(scene) scene->RegisterDrawable(this);
}
void Drawable::RemoveNode(Node* node)
{
Utils::Remove(nodes, node);
if (nodes.empty())
{
scene = nullptr;
scene->RemoveDrawable(this);
}
}
}
}

View File

@@ -0,0 +1,82 @@
#pragma once
#include <vector>
#include "../Base/ICloseable.hpp"
#include "Geometry.hpp"
#include "Material.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
class Node;
class Scene;
struct Drawable : virtual public ICloseable
{
std::vector<Node*> nodes;
Scene* scene = nullptr;
Geometry* mesh = nullptr;
Material* material = nullptr;
public:
Drawable() = default;
explicit Drawable(const Drawable* toCopy)
{
mesh = toCopy->mesh;
material = toCopy->material;
}
virtual ~Drawable()
{
if(mesh) Drawable::Close();
}
Drawable* Copy() const
{
return new Drawable(this);
}
void Init(Geometry* mesh, Material* material)
{
if (this->mesh || this->material) throw std::runtime_error("Drawable is already initialized.");
this->mesh = mesh;
this->material = material;
}
void Init(Drawable* drawable)
{
if (mesh || material) throw std::runtime_error("Drawable is already initialized.");
this->mesh = drawable->mesh;
this->material = drawable->material;
}
void Close() override
{
if (!nodes.empty()) throw std::runtime_error("Drawable is still being used!!!");
mesh = nullptr;
material = nullptr;
}
Scene* GetScene() const
{
return scene;
}
private:
friend class Node;
friend class Scene;
void AddNode(Node* node)
{
if (!mesh) throw std::runtime_error("Drawable is not initialized.");
if (Utils::Contains(nodes, node)) throw std::runtime_error("A drawable must not use the same node more than once.");
nodes.push_back(node);
}
void SetScene(Scene* scene);
void RemoveNode(Node* node);
};
}
}

View File

@@ -0,0 +1,206 @@
#pragma once
#include <stdexcept>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include "Vertex.hpp"
#include "../Base/Logger.hpp"
#include "../Base/Utils.hpp"
#include "../Base/ICloseable.hpp"
#include "AABB.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
enum class VertexIndexType
{
UINT16 = sizeof(uint16_t), UINT32 = sizeof(uint32_t)
};
struct Geometry : public virtual ICloseable
{
uint32_t vertexCount = 0, indexCount = 0;
Vertex* vertices;
void* indices;
VertexIndexType indexType;
AABB aabb;
ICloseable* renderGeo = nullptr;
Vertex* GetVertices() const { return vertices; }
void* GetIndices() const { return indices; }
uint16_t* GetIndices16() const { return static_cast<uint16_t*>(indices); }
uint32_t* GetIndices32() const { return static_cast<uint32_t*>(indices); }
uint32_t GetIndexCount() const { return indexCount; }
uint32_t GetVertexCount() const { return vertexCount; }
static Geometry* LoadFromFile(const std::string file)
{
Geometry* mesh = new Geometry();
mesh->InitFromFile(file);
return mesh;
}
Geometry() : vertexCount(0), indexCount(0), vertices(nullptr), indices(nullptr), indexType(VertexIndexType::UINT16) {}
~Geometry()
{
if (vertices) Geometry::Close();
}
void InitFromFile(const std::string file)
{
Assimp::Importer importer;
const uint32_t flags = aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_GenNormals |
aiProcess_ImproveCacheLocality | aiProcess_RemoveRedundantMaterials | aiProcess_GenUVCoords | aiProcess_TransformUVCoords |
aiProcess_ConvertToLeftHanded | aiProcess_PreTransformVertices | aiProcess_OptimizeGraph;
const aiScene* scene = importer.ReadFile(file, flags);
if (!scene) throw std::runtime_error("Failed to load file \"" + file + "\" Error: " + importer.GetErrorString());
if (!scene->HasMeshes()) throw std::runtime_error("File \"" + file + "\" does not have any meshes");
if (scene->mNumMeshes > 1) Logger::DATA->warn("File {0} contains more than one mesh. Only first one will be loaded", file);
Init(scene->mMeshes[0]);
importer.FreeScene();
}
/**
* \brief Creates the arrays for the vertices and indices. They will not be filled!
* \param vertexCount The amount of vertices that will be used
* \param indexCount The amount of indices that will be used
*/
void Init(uint32_t vertexCount, uint32_t indexCount)
{
if (this->vertexCount || this->indexCount) throw std::runtime_error("Geometry is already initialized.");
this->vertexCount = vertexCount;
this->indexCount = indexCount;
indexType = (vertexCount > UINT16_MAX) ? VertexIndexType::UINT32 : VertexIndexType::UINT16;
vertices = new Vertex[vertexCount];
indices = malloc(static_cast<size_t>(Utils::EnumAsInt(indexType)) * indexCount);
renderGeo = nullptr;
}
void Init(aiMesh* mesh)
{
aabb.Init();
Init(mesh->mNumVertices, mesh->mNumFaces * 3); // Reserve the space for the data
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
vertices[i].Set(mesh->mVertices[i]);
if (mesh->HasNormals()) vertices[i].SetNormal(mesh->mNormals[i]);
if (mesh->HasTangentsAndBitangents())
{
vertices[i].SetTangentAndBiTangent(mesh->mTangents[i], mesh->mBitangents[i]);
}
if (mesh->HasTextureCoords(0)) vertices[i].SetTextureCoordinates(mesh->mTextureCoords[0][i]);
if (mesh->HasVertexColors(0)) vertices[i].SetColor(mesh->mColors[0][i]);
aabb.Grow(vertices[i].position);
}
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
const aiFace face = mesh->mFaces[i];
if (face.mNumIndices != 3) throw std::runtime_error("Mesh is not a triangle mesh!");
for (unsigned int j = 0; j < face.mNumIndices; j++)
{
if (indexType == VertexIndexType::UINT16)
{
static_cast<uint16_t*>(indices)[i * face.mNumIndices + j] = static_cast<uint16_t>(face.mIndices[j]);
}
else
{
static_cast<uint32_t*>(indices)[i * face.mNumIndices + j] = face.mIndices[j];
}
}
}
//TODO load bones
//TODO load materials
}
void InitCube(float x = 1, float y = 1, float z = 1, glm::vec4 color = glm::vec4(1))
{
Init(24, 36);
SetIndices(new uint32_t[indexCount]{
0, 1, 2, 0, 2, 3, // front face index data
4, 5, 6, 4, 6, 7, // back face index data
8, 9, 10, 8, 10, 11, // top face index data
12, 13, 14, 12, 14, 15, // bottom face index data
16, 17, 18, 16, 18, 19, // left face index data
20, 21, 22, 20, 22, 23 // right face index data
}, indexCount);
x *= 0.5f; y *= 0.5f; z *= 0.5f;
int i = 0;
// front face vertex data
vertices[i++].Set(-x, +y, -z, +0, +0, -1, +0, +0);
vertices[i++].Set(-x, -y, -z, +0, +0, -1, +0, +1);
vertices[i++].Set(+x, -y, -z, +0, +0, -1, +1, +1);
vertices[i++].Set(+x, +y, -z, +0, +0, -1, +1, +0);
// back face vertex data
vertices[i++].Set(-x, +y, +z, +0, +0, +1, +1, +0);
vertices[i++].Set(+x, +y, +z, +0, +0, +1, +0, +0);
vertices[i++].Set(+x, -y, +z, +0, +0, +1, +0, +1);
vertices[i++].Set(-x, -y, +z, +0, +0, +1, +1, +1);
// top face vertex data
vertices[i++].Set(-x, -y, -z, +0, +1, +0, +0, +0);
vertices[i++].Set(-x, -y, +z, +0, +1, +0, +0, +1);
vertices[i++].Set(+x, -y, +z, +0, +1, +0, +1, +1);
vertices[i++].Set(+x, -y, -z, +0, +1, +0, +1, +0);
// bottom face vertex data
vertices[i++].Set(-x, +y, -z, +0, -1, +0, +1, +0);
vertices[i++].Set(+x, +y, -z, +0, -1, +0, +0, +0);
vertices[i++].Set(+x, +y, +z, +0, -1, +0, +0, +1);
vertices[i++].Set(-x, +y, +z, +0, -1, +0, +1, +1);
// Fill in the left face vertex data
vertices[i++].Set(-x, +y, +z, -1, +0, +0, +0, +0);
vertices[i++].Set(-x, -y, +z, -1, +0, +0, +0, +1);
vertices[i++].Set(-x, -y, -z, -1, +0, +0, +1, +1);
vertices[i++].Set(-x, +y, -z, -1, +0, +0, +1, +0);
// Fill in the right face vertex data
vertices[i++].Set(+x, +y, -z, +1, +0, +0, +0, +0);
vertices[i++].Set(+x, -y, -z, +1, +0, +0, +0, +1);
vertices[i++].Set(+x, -y, +z, +1, +0, +0, +1, +1);
vertices[i].Set(+x, +y, +z, +1, +0, +0, +1, +0);
for(i = 0; i < vertexCount; i++)
{
vertices[i].color = color;
}
}
void SetIndices(const uint32_t* data, uint32_t size, uint32_t offset = 0) const
{
size += offset;
for(; offset < size; offset++)
{
if (indexType == VertexIndexType::UINT16)
{
static_cast<uint16_t*>(indices)[offset] = static_cast<uint16_t>(data[offset]);
}
else
{
static_cast<uint32_t*>(indices)[offset] = data[offset];
}
}
}
void Close() override
{
vertexCount = 0;
indexCount = 0;
Free();
renderGeo->Close();
renderGeo = nullptr;
}
void Free()
{
if(vertices) delete[] vertices;
free(indices);
vertices = nullptr;
indices = nullptr;
}
};
}
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "Shader.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
struct Material
{
Shader* shader;
};
}
}

View File

@@ -0,0 +1,9 @@
#include "Node.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
const glm::mat4x4 Node::IDENTITY = glm::mat4(1);
}
}

View File

@@ -0,0 +1,212 @@
#pragma once
#include <vector>
#include <stdexcept>
#include <glm/glm.hpp>
#include "../Base/Utils.hpp"
#include "../Base/IInitable.hpp"
#include "../Base/ICloseable.hpp"
#include "Drawable.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
class Scene;
enum class UpdateFrequency
{
Always, Sometimes, Never
};
struct Node : virtual IInitable, virtual ICloseable
{
friend Scene;
protected:
static const glm::mat4x4 IDENTITY;
public:
glm::mat4x4 localMat, worldMat;
bool enabled = true;
Node* parent = nullptr;
Scene* scene = nullptr;
std::vector<Node*> children;
std::vector<Drawable*> drawables;
UpdateFrequency matrixUpdateFrequency = UpdateFrequency::Never;
ICloseable* renderNode = nullptr;
public:
Node() = default;
virtual ~Node() = default;
void Init() override
{
if (parent || scene || !children.empty() || !drawables.empty()) throw std::runtime_error("Node already initialized");
localMat = worldMat = IDENTITY;
enabled = true;
parent = nullptr;
children = std::vector<Node*>();
drawables = std::vector<Drawable*>();
}
void Close() override
{
children.clear();
if (renderNode) renderNode->Close();
parent = nullptr;
scene = nullptr;
enabled = false;
if (!children.empty()) Logger::SCENE->warn("Closing Node that has children!");
for (Node* child : children)
{
child->SetParent(nullptr);
}
children.clear();
for(size_t i = drawables.size(); i > 0; i--)
{
RemoveDrawable(drawables[i]);
}
}
void AddChild(Node* node)
{
node->SetParent(this);
children.push_back(node);
node->UpdateWorldMatrix(worldMat);
}
void AddChild(Drawable* drawable)
{
AddDrawable(drawable);
}
void RemoveChild(Node* node)
{
if (node->parent == this)
{
Utils::Remove(children, node);
node->SetParent(nullptr);
}
}
void RemoveChild(Drawable* drawable)
{
RemoveDrawable(drawable);
}
void AddDrawable(Drawable* drawable)
{
if (scene) drawable->SetScene(scene);
else if (drawable->GetScene()) Logger::SCENE->warn("Drawable is already associated with a scene, but the node it was added to is not!");
drawable->AddNode(this);
drawables.push_back(drawable);
}
void RemoveDrawable(Drawable* drawable)
{
drawable->RemoveNode(this);
Utils::Remove(drawables, drawable);
}
void SetMatrix(glm::mat4x4 mat)
{
localMat = mat;
UpdateWorldMatrix(parent ? parent->GetWorldMatrix() : IDENTITY);
}
const glm::mat4x4& GetMatrix() const
{
return localMat;
}
const glm::mat4x4& GetWorldMatrix() const
{
return worldMat;
}
bool IsEnabled() const
{
return enabled;
}
void Enable()
{
enabled = true;
}
void Disable()
{
enabled = false;
}
Node* GetParent() const
{
return parent;
}
Scene* GetScene() const
{
return scene;
}
bool IsRoot() const
{
return scene && parent == this;
}
UpdateFrequency GetUpdateFrequency()
{
return matrixUpdateFrequency;
}
void SetUpdateFrequency(UpdateFrequency frequency)
{
if (!children.empty()) throw std::runtime_error("The update must not be changed for nodes with children.");
this->matrixUpdateFrequency = frequency;
}
protected:
virtual void UpdateWorldMatrix(const glm::mat4x4& parentWorldMat)
{
worldMat = parentWorldMat * localMat;
for (const auto& node : children)
{
node->UpdateWorldMatrix(worldMat);
}
}
private:
void SetParent(Node* parent)
{
if (this->parent && parent) throw std::runtime_error("Node already has a parent! Nodes must not be used multiple times!");
this->parent = parent;
if(parent && parent != this) this->scene = parent->scene;
if (!parent) SetScene(nullptr);
}
void SetScene(Scene* scene)
{
if (this->scene && scene) throw std::runtime_error("Node already has a scene!");
this->scene = scene;
for (const auto& node : children)
{
node->SetScene(scene);
}
if (scene)
{
for (size_t i = 0; i < drawables.size(); i++)
{
Scene* drawableScene = drawables[i]->GetScene();
if(drawableScene)
{
if(drawableScene != scene)
{
Logger::SCENE->warn("Drawable is already associated with a scene! Creating copy.");
drawables[i] = drawables[i]->Copy();
}
}
drawables[i]->SetScene(scene);
}
}
}
};
}
}

View File

@@ -0,0 +1,89 @@
#pragma once
#include "Node.hpp"
#include "Camera.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
struct Scene : virtual public IInitable, virtual public ICloseable
{
Node* root;
std::vector<Drawable*> shapeList;
Shader* shader;
Camera* camera;
public:
Scene() : root(nullptr) {}
virtual ~Scene()
{
if (root) Scene::Close();
}
void Init() override
{
Node* newRoot = new Node();
newRoot->Init();
Init(newRoot);
}
void Init(Node* root)
{
if (root->GetParent()) throw std::runtime_error("Node has a parent! Only nodes without a parent may be a root node!");
root->SetScene(this);
root->SetParent(root);
this->root = root;
}
void Close() override
{
//TODO
}
Node* GetRoot() const
{
return root;
}
void RegisterDrawable(Drawable* drawable)
{
if (drawable->GetScene() != this) drawable->SetScene(this);
if (Utils::Contains(shapeList, drawable)) return; // Prevent duplicate entries
shapeList.push_back(drawable);
}
void RemoveDrawable(Drawable* drawable)
{
Utils::Remove(shapeList, drawable);
drawable->SetScene(nullptr);
}
void SetCamera(Camera* camera)
{
this->camera = camera;
}
Camera* GetCamera() const
{
return camera;
}
/**
* \brief Checks if the scene is valid and attempts to fix problems.
*/
void Validate()
{
for (Drawable* drawable : shapeList)
{
if(drawable->GetScene() != this)
{
if (!drawable->GetScene()) drawable->SetScene(this);
else Logger::SCENE->error("Scene is linked with drawable from different scene!!!"); //TODO handle
}
}
//TODO check node tree
}
};
}
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <string>
#include <stdexcept>
#include "../Base/ICloseable.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
enum class Topology
{
PointList, LineList, LineStripe, TriangleList, TriangleStripe
};
struct Shader : public virtual ICloseable
{
std::string vertexShaderName, fragmentShaderName;
Topology topology = Topology::TriangleList;
ICloseable* renderShader = nullptr;
Shader() = default;
~Shader() { if (renderShader) Shader::Close(); }
void Init(const std::string& vertexShaderName, const std::string& fragmentShaderName)
{
if (renderShader) throw std::runtime_error("Shader already initialized!");
this->vertexShaderName = vertexShaderName;
this->fragmentShaderName = fragmentShaderName;
}
void Close() override
{
renderShader->Close();
renderShader = nullptr;
}
};
}
}

View File

@@ -0,0 +1,178 @@
#pragma once
#include <glm/glm.hpp>
#include <assimp/vector2.h>
#include <assimp/vector3.h>
namespace openVulkanoCpp
{
struct Vertex
{
glm::vec3 position, normal, tangent, biTangent, textureCoordinates;
glm::vec4 color;
Vertex() = default;
Vertex(const aiVector3D& pos) : position(pos.x, pos.y, pos.z), normal(), tangent(), biTangent(), textureCoordinates(), color()
{}
Vertex(const float& x, const float& y, const float& z, const float& nx, const float& ny, const float& nz, const float& u, const float& v)
: position({ x, y, z }), normal({ nx, ny, nz }), tangent(), biTangent(), textureCoordinates({ u, v, 0 }), color()
{}
Vertex(const glm::vec3& position, const glm::vec3& normal, const glm::vec3& tangent, const glm::vec3& biTangent, const glm::vec2& textureCoordinates)
: position(position), normal(normal), tangent(tangent), biTangent(biTangent), textureCoordinates(textureCoordinates, 0), color()
{}
Vertex(const glm::vec3 & position, const glm::vec3 & normal, const glm::vec3 & tangent, const glm::vec3 & biTangent, const glm::vec3 & textureCoordinates)
: position(position), normal(normal), tangent(tangent), biTangent(biTangent), textureCoordinates(textureCoordinates), color()
{}
~Vertex() = default;
void Set(const float& x, const float& y, const float& z)
{
position = { x, y, z };
}
void Set(const glm::vec3& position)
{
this->position = position;
}
void Set(const aiVector3D& position)
{
this->position = { position.x, position.y, position.z };
}
void Set(const float& x, const float& y, const float& z, const float& nx, const float& ny, const float& nz, const float& u, const float& v)
{
this->position = { x, y, z };
this->normal = { nx, ny, nz };
this->textureCoordinates = { u, v, 0 };
}
void Set(const glm::vec3& position, const glm::vec3& normal, const glm::vec2& textureCoordinates)
{
this->position = position;
this->normal = normal;
this->textureCoordinates = { textureCoordinates, 0 };
}
void Set(const glm::vec3& position, const glm::vec3& normal, const glm::vec3& tangent, const glm::vec3& biTangent, const glm::vec2& textureCoordinates)
{
this->position = position;
this->normal = normal;
this->tangent = tangent;
this->biTangent = biTangent;
this->textureCoordinates = { textureCoordinates,0 };
}
void SetNormal(const float& nx, const float& ny, const float& nz)
{
this->normal = { nx, ny, nz };
}
void SetNormal(const glm::vec3& normal)
{
this->normal = normal;
}
void SetNormal(const aiVector3D& normal)
{
this->normal = { normal.x, normal.y, normal.z };
}
void SetTangent(const float& tx, const float& ty, const float& tz)
{
this->tangent = { tx, ty, tz };
}
void SetTangent(const glm::vec3& tangent)
{
this->tangent = tangent;
}
void SetTangent(const aiVector3D& tangent)
{
this->tangent = { tangent.x, tangent.y, tangent.z };
}
void SetTangentAndBiTangent(const float& tx, const float& ty, const float& tz, const float& bx, const float& by, const float& bz)
{
this->biTangent = { bx, by, bz };
this->tangent = { tx, ty, tz };
}
void SetTangentAndBiTangent(const glm::vec3& tangent, const glm::vec3& biTangent)
{
this->biTangent = biTangent;
this->tangent = tangent;
}
void SetTangentAndBiTangent(const aiVector3D& tangent, const aiVector3D& biTangent)
{
this->tangent = { tangent.x, tangent.y, tangent.z };
this->biTangent = { biTangent.x, biTangent.y, biTangent.z };
}
void SetBiTangent(const float& bx, const float& by, const float& bz)
{
this->biTangent = { bx, by, bz };
}
void SetBiTangent(const glm::vec3& biTangent)
{
this->biTangent = biTangent;
}
void SetBiTangent(const aiVector3D& biTangent)
{
this->biTangent = { biTangent.x, biTangent.y, biTangent.z };
}
void SetTextureCoordinates(const float& u, const float& v)
{
this->textureCoordinates = { u, v, 0 };
}
void SetTextureCoordinates(const float& u, const float& v, const float& w)
{
this->textureCoordinates = { u, v, w };
}
void SetTextureCoordinates(const glm::vec2& textureCoordinates)
{
this->textureCoordinates = { textureCoordinates, 0 };
}
void SetTextureCoordinates(const glm::vec3& textureCoordinates)
{
this->textureCoordinates = textureCoordinates;
}
void SetTextureCoordinates(const aiVector2D& textureCoordinates)
{
this->textureCoordinates = { textureCoordinates.x, textureCoordinates.y, 0 };
}
void SetTextureCoordinates(const aiVector3D& textureCoordinates)
{
this->textureCoordinates = { textureCoordinates.x, textureCoordinates.y, textureCoordinates.z };
}
void SetColor(const float& r, const float& g, const float& b, const float& a = 1)
{
color = { r,g,b,a };
}
void SetColor(const glm::vec4& color)
{
this->color = color;
}
void SetColor(const aiColor4D& color)
{
this->color = { color.r, color.g, color.b, color.a };
}
};
}

View File

@@ -0,0 +1,7 @@
REM Make the directory batch file resides in as the working directory.
pushd %~dp0
glslangvalidator -V basic.vert -o basic.vert.spv
glslangvalidator -V basic.frag -o basic.frag.spv
popd

View File

@@ -0,0 +1,9 @@
#version 450
layout(location = 0) in vec4 color;
layout(location = 0) out vec4 outColor;
void main()
{
outColor = color;
}

Binary file not shown.

View File

@@ -0,0 +1,29 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec3 tangent;
layout(location = 3) in vec3 biTangent;
layout(location = 4) in vec3 textureCoordinates;
layout(location = 5) in vec4 color;
layout(location = 0) out vec4 outColor;
layout(binding = 0) uniform NodeData
{
mat4 world;
} node;
layout(std140, push_constant) uniform CameraData {
mat4 viewProjection;
} cam;
void main()
{
vec3 light = normalize(vec3(1));
vec4 worldPos = node.world * vec4(position, 1.0);
vec3 worldNormal = normalize(transpose(inverse(mat3(node.world))) * normal);
float brightness = max(0.0, dot(worldNormal, light));
outColor = vec4(clamp(color.rgb * (0.5 + brightness / 2), 0, 1), 1);
gl_Position = normalize(cam.viewProjection * worldPos);
}

Binary file not shown.

View File

@@ -0,0 +1,109 @@
#pragma once
#include "../Base/ICloseable.hpp"
#include "Device.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
/**
* \brief A not managed buffer. This should be used rarely.
*/
struct Buffer : virtual public ICloseable
{
vk::Device device;
vk::DeviceMemory memory;
vk::DeviceSize size = 0, alignment = 0, allocSize = 0;
vk::MemoryPropertyFlags memoryPropertyFlags;
void* mapped = nullptr;
/**
* \brief Maps the buffer into the memory of the host.
* \tparam T The type of the buffers data.
* \param offset The offset from where to map the buffer.
* \param size The size to be mapped. VK_WHOLE_SIZE to map the whole buffer.
* \return The pointer to the mapped buffer.
*/
template <typename T = void>
T * Map(size_t offset = 0, VkDeviceSize size = VK_WHOLE_SIZE)
{
mapped = device.mapMemory(memory, offset, size, vk::MemoryMapFlags());
return static_cast<T*>(mapped);
}
/**
* \brief Un-maps the buffer from the host.
*/
void UnMap()
{
device.unmapMemory(memory);
mapped = nullptr;
}
/**
* \brief Copies data into the mapped buffer. Will not do anything if the buffer is not mapped!
* \param size The size of the data to copy.
* \param data The data to copy
* \param offset The offset for where to copy the data to in the buffer.
*/
void Copy(size_t size, const void* data, VkDeviceSize offset = 0) const
{
if (!mapped) return;
memcpy(static_cast<uint8_t*>(mapped) + offset, data, size);
}
/**
* \brief Copies data into the mapped buffer. Will not do anything if the buffer is not mapped!
* \param data The data to copy.
* \param offset The offset for where to copy the data to in the buffer.
*/
template <typename T>
void Copy(const T& data, VkDeviceSize offset = 0) const
{
Copy(sizeof(T), &data, offset);
}
/**
* \brief Copies data into the mapped buffer. Will not do anything if the buffer is not mapped!
* \param data The data to copy.
* \param offset The offset for where to copy the data to in the buffer.
*/
template <typename T>
void Copy(const std::vector<T>& data, VkDeviceSize offset = 0) const
{
copy(sizeof(T) * data.size(), data.data(), offset);
}
/**
* \brief Flushes the memory region of the buffer to the device. This should be only necessary for non coherent memory.
* \param size The amount to flush. VK_WHOLE_SIZE flushes the entire buffer.
* \param offset The offset from where to start the flush.
*/
void Flush(vk::DeviceSize size = VK_WHOLE_SIZE, vk::DeviceSize offset = 0) const
{
device.flushMappedMemoryRanges(vk::MappedMemoryRange(memory, offset, size));
}
/**
* \brief Invalidates the memory region of the buffer to allow access from the host. This should be only necessary for non coherent memory.
* \param size The amount to make available. VK_WHOLE_SIZE invalidate the entire buffer.
* \param offset The offset from where to make the memory available.
*/
void Invalidate(vk::DeviceSize size = VK_WHOLE_SIZE, vk::DeviceSize offset = 0) const
{
device.invalidateMappedMemoryRanges(vk::MappedMemoryRange(memory, offset, size));
}
void Close() override
{
if (mapped) UnMap();
if(memory)
{
device.free(memory);
memory = vk::DeviceMemory();
}
}
};
}
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include "../Base/ICloseable.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
struct CommandHelper : virtual ICloseable
{
vk::Device device;
vk::CommandPool cmdPool;
vk::CommandBuffer cmdBuffer;
vk::CommandBufferLevel level;
CommandHelper() = default;
~CommandHelper() { if (cmdPool) CommandHelper::Close(); }
void Init(vk::Device device, uint32_t queueIndex, vk::CommandBufferLevel level = vk::CommandBufferLevel::eSecondary)
{
this->level = level;
this->device = device;
cmdPool = device.createCommandPool(vk::CommandPoolCreateInfo({}, queueIndex));
vk::CommandBufferAllocateInfo bufferAllocInfo = { cmdPool, level, 1 };
cmdBuffer = device.allocateCommandBuffers(bufferAllocInfo)[0];
}
void Reset() const
{
device.resetCommandPool(cmdPool, {});
}
vk::CommandBufferLevel GetLevel() const
{
return level;
}
void Close() override
{
device.freeCommandBuffers(cmdPool, 1, &cmdBuffer);
device.destroyCommandPool(cmdPool);
}
};
}
}

View File

@@ -0,0 +1,119 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include "../Base/IGraphicsApp.hpp"
#include "../Base/IGraphicsAppManager.hpp"
#include "../Base/EngineConstants.hpp"
#include "../Base/Utils.hpp"
#include "Debuging/ValidationLayer.hpp"
#include "DeviceManager.hpp"
#include "SwapChain.hpp"
#include "RenderPass.hpp"
#include "Pipeline.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
class Context : virtual public ICloseable
{
bool enableValidationLayer, initialized;
std::set<std::string> requiredExtensions;
public:
DeviceManager deviceManager;
vk::Instance instance; // Vulkan instance
vk::DispatchLoaderDynamic dynamicDispatch; // for access to features not available in statically linked Vulkan lib
vk::SurfaceKHR surface; // Vulkan surface to display framebuffer on
Device* device = nullptr;
SwapChain swapChain;
RenderPass swapChainRenderPass;
IVulkanWindow* window = nullptr;
IGraphicsAppManager* graphicsAppManager = nullptr;
Pipeline pipeline;
Context() : initialized(false)
{
#ifdef DEBUG
enableValidationLayer = true;
#else
enableValidationLayer = false;
#endif
}
virtual ~Context()
{
if (initialized) Close();
}
void Init(IGraphicsAppManager* graphicsAppManager, IVulkanWindow* window)
{
if (initialized) throw std::runtime_error("The context is already initialized");
this->graphicsAppManager = graphicsAppManager;
this->window = window;
// Get the extensions required to display on the window
for (const auto& requiredExtension : window->GetRequiredInstanceExtensions()) { RequireExtension(requiredExtension.c_str()); }
CreateInstance(); // Create the vulkan instance
surface = window->CreateSurface(instance); // Create the surface from the window
CreateDevice();
swapChain.Init(device, surface, window);
swapChainRenderPass.Init(device, &swapChain);
pipeline.Init(device->device);
initialized = true;
}
void Close() override
{
if (!initialized) return;
device->WaitIdle();
pipeline.Close();
swapChainRenderPass.Close();
swapChain.Close();
deviceManager.Close();
//TODO
if (enableValidationLayer) Debug::CloseValidationLayers(instance);
initialized = false;
}
void Resize(const uint32_t newWidth, const uint32_t newHeight)
{
device->WaitIdle();
swapChain.Resize(newWidth, newHeight);
}
void RequireExtension(const char* extension)
{
requiredExtensions.emplace(extension);
}
private:
void CreateInstance()
{
if (enableValidationLayer) RequireExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
vk::ApplicationInfo appInfo(graphicsAppManager->GetGraphicsApp()->GetAppName().c_str(), graphicsAppManager->GetGraphicsApp()->GetAppVersionAsInt(), ENGINE_NAME, ENGINE_VERSION.intVersion, VK_MAKE_VERSION(1, 1, 0));
std::vector<const char*> extensions = Utils::toCString(requiredExtensions), layers = Debug::GetValidationLayers();
const vk::InstanceCreateInfo createInfo(vk::InstanceCreateFlags(), &appInfo, enableValidationLayer ? layers.size() : 0,
layers.data(), extensions.size(), extensions.data());
instance = vk::createInstance(createInfo);
if (enableValidationLayer) Debug::SetupValidationLayers(instance, vk::DebugReportFlagBitsEXT::eError | vk::DebugReportFlagBitsEXT::eWarning | vk::DebugReportFlagBitsEXT::ePerformanceWarning /*| vk::DebugReportFlagBitsEXT::eInformation | vk::DebugReportFlagBitsEXT::eDebug*/);
dynamicDispatch.init(instance, &vkGetInstanceProcAddr);
}
void CreateDevice()
{
deviceManager.Init(instance);
device = deviceManager.GetCompatibleDevice({ VK_KHR_SWAPCHAIN_EXTENSION_NAME });
device->PrepareDevice({ VK_KHR_SWAPCHAIN_EXTENSION_NAME }, surface);
dynamicDispatch.init(instance, &vkGetInstanceProcAddr, device->device, &vkGetDeviceProcAddr);
Logger::RENDER->info("Found device: {0}", device->GetDeviceName());;
}
};
}
}

View File

@@ -0,0 +1,97 @@
#pragma once
#include <set>
#include <vulkan/vulkan.hpp>
#define RENDER_DOC
namespace openVulkanoCpp
{
namespace Vulkan
{
namespace Debug
{
std::list<std::string> activeValidationLayerNames = {
"VK_LAYER_LUNARG_assistant_layer",
"VK_LAYER_LUNARG_standard_validation",
//"VK_EXT_debug_marker",
#ifdef RENDER_DOC
"VK_LAYER_RENDERDOC_Capture", // RenderDoc must be open for this layer to work!
#endif
};
static std::set<std::string> GetAvailableValidationLayers()
{
auto layers = vk::enumerateInstanceLayerProperties();
std::set<std::string> layersVector;
std::string layerList = "";
for(const auto& layer : layers)
{
std::string name = layer.layerName;
layersVector.insert(name);
if (layerList.length() > 0) layerList += ", ";
layerList += name;
}
Logger::RENDER->debug("Available Vulkan Validation Layers: {0}", layerList);
return layersVector;
}
static std::vector<const char*> GetValidationLayers()
{
std::set<std::string> availableLayers = GetAvailableValidationLayers();
std::vector<const char*> layers;
std::string layerList = "";
for (const auto& name : activeValidationLayerNames)
{
if (availableLayers.count(name) != 0)
{
layers.push_back(name.c_str());
if (layerList.length() > 0) layerList += ", ";
layerList += name;
}
}
Logger::RENDER->debug("Active Vulkan Validation Layers: {0}", layerList);
return layers;
}
static std::once_flag dispatcherInitFlag;
vk::DispatchLoaderDynamic dispatcher;
vk::DebugReportCallbackEXT msgCallback;
inline VkBool32 ValidationLayerCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType,
uint64_t srcObject, size_t location, int32_t msgCode, const char* layerPrefix,
const char* msg, void* pUserData)
{
std::string prefix = "VK_DEBUG:";
spdlog::level::level_enum level = spdlog::level::info;
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) level = spdlog::level::err;
else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) level = spdlog::level::warn;
else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
{
level = spdlog::level::warn;
prefix = "[PERF] " + prefix;
}
else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) level = spdlog::level::debug;
Logger::RENDER->log(level, "{0} [{1}] Code {2}: {3}", prefix, layerPrefix, msgCode, msg);
return false;
}
static void SetupValidationLayers(const vk::Instance& instance, const vk::DebugReportFlagsEXT& flags)
{
Logger::RENDER->info("Setting up Vulkan Validation Layer");
std::call_once(dispatcherInitFlag, [&] { dispatcher.init(instance, &vkGetInstanceProcAddr); });
vk::DebugReportCallbackCreateInfoEXT dbgCreateInfo = {};
dbgCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)ValidationLayerCallback;
dbgCreateInfo.flags = flags;
msgCallback = instance.createDebugReportCallbackEXT(dbgCreateInfo, nullptr, dispatcher);
Logger::RENDER->info("Vulkan Validation Layer setup");
}
static void CloseValidationLayers(const vk::Instance& instance) {
std::call_once(dispatcherInitFlag, [&] { dispatcher.init(instance, &vkGetInstanceProcAddr); });
instance.destroyDebugReportCallbackEXT(msgCallback, nullptr, dispatcher);
}
};
}
}

View File

@@ -0,0 +1,279 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include <set>
#include <functional>
#include <fstream>
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:
vk::PhysicalDevice physicalDevice;
std::vector<vk::QueueFamilyProperties> queueFamilyProperties; // Queue family properties
vk::PhysicalDeviceProperties properties; // Physical device properties (for e.g. checking device limits)
vk::PhysicalDeviceFeatures features; // Physical device features (for e.g. checking if a feature is available)
vk::PhysicalDeviceMemoryProperties memoryProperties; // available memory properties
vk::Device device; // Logical device, application's view of the physical device (GPU)
vk::PipelineCache pipelineCache;
vk::CommandPool graphicsCommandPool;
std::set<std::string> supportedExtensions;
vk::Queue graphicsQueue;
struct QueueIndices {
uint32_t graphics = VK_QUEUE_FAMILY_IGNORED;
uint32_t compute = VK_QUEUE_FAMILY_IGNORED;
uint32_t transfer = VK_QUEUE_FAMILY_IGNORED;
uint32_t GetGraphics() const { return graphics; }
uint32_t GetCompute() const { return compute != graphics ? compute : VK_QUEUE_FAMILY_IGNORED; }
uint32_t GetTransfer() const { return (transfer != graphics && transfer != compute) ? transfer : VK_QUEUE_FAMILY_IGNORED; }
} queueIndices;
bool useDebugMarkers;
public:
Device(vk::PhysicalDevice& physicalDevice)
{
this->physicalDevice = physicalDevice;
useDebugMarkers = false;
QueryDevice();
}
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());
graphicsQueue = device.getQueue(queueIndices.graphics, 0);
graphicsCommandPool = device.createCommandPool({ vk::CommandPoolCreateFlagBits::eResetCommandBuffer, queueIndices.graphics, });
}
std::set<std::string> GetExtensions() const
{
return supportedExtensions;
}
bool IsExtensionAvailable(const vk::ArrayProxy<const std::string>& extensions) const
{
for(const auto& extension : extensions)
{
if (supportedExtensions.count(extension) == 0) return false;
}
return true;
}
void WaitIdle() const
{ //TODO wait all queues idle
graphicsQueue.waitIdle();
device.waitIdle();
}
vk::CommandBuffer CreateCommandBuffer(vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary) const
{
const vk::CommandBufferAllocateInfo cmdBufferAllocInfo(graphicsCommandPool, level, 1);
return device.allocateCommandBuffers(cmdBufferAllocInfo)[0];
}
void FlushCommandBuffer(vk::CommandBuffer& cmdBuffer) const
{
graphicsQueue.submit(vk::SubmitInfo{ 0, nullptr, nullptr, 1, &cmdBuffer }, vk::Fence());
WaitIdle();
}
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);
}
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(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); }
// 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)
{
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;
}
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.
* \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;
}
}
throw std::runtime_error("No supported depth format");
}
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;
}
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;
}
void Close() override
{
device.destroyCommandPool(graphicsCommandPool);
//TODO fill
}
};
}
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include <stdexcept>
#include <vector>
#include "../Base/ICloseable.hpp"
#include "Device.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
class DeviceManager : virtual public ICloseable
{
std::vector<Device> devices;
public:
void Init(const vk::Instance& instance)
{
devices = std::vector<Device>();
for (auto& physicalDevice : instance.enumeratePhysicalDevices())
{
devices.emplace_back(physicalDevice);
}
}
Device* GetCompatibleDevice(const std::vector<std::string>& deviceExtensions)
{
for (auto& device : devices)
{
if (device.IsExtensionAvailable(deviceExtensions)) return &device;
}
throw std::runtime_error("No device with required extensions found!");
}
void Close() override
{
for (auto& device : devices)
{
device.Close();
}
devices.clear();
}
};
}
}

View File

@@ -0,0 +1,76 @@
#include "FrameBuffer.hpp"
#include "RenderPass.hpp"
void openVulkanoCpp::Vulkan::FrameBuffer::Init(Device* device, vk::Extent3D size, bool useDepthBuffer)
{
this->size = size;
this->device = device;
this->useDepthBuffer = useDepthBuffer;
colorFormat = FindColorFormat();
if (useDepthBuffer)
{
depthBufferFormat = FindDepthFormat();
CreateDepthStencil();
}
}
void openVulkanoCpp::Vulkan::FrameBuffer::InitRenderPass(RenderPass* renderPass)
{
if (!device) throw std::
runtime_error("The frame buffer needs to be initialized before binding it to a render pass");
this->renderPass = renderPass;
CreateFrameBuffer();
}
void openVulkanoCpp::Vulkan::FrameBuffer::Resize(vk::Extent3D size)
{
this->size = size;
DestroyFrameBuffer();
if (depthBuffer) depthBuffer.Close();
if (useDepthBuffer) CreateDepthStencil();
CreateFrameBuffer();
renderPass->UpdateBeginInfo();
}
void openVulkanoCpp::Vulkan::FrameBuffer::CreateDepthStencil()
{
vk::ImageCreateInfo depthStencilCreateInfo({}, vk::ImageType::e2D, depthBufferFormat,
size, 1, 1);
depthStencilCreateInfo.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::
eTransferSrc;
const vk::ImageAspectFlags aspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
const vk::ImageViewCreateInfo depthStencilViewCreateInfo({}, {}, vk::ImageViewType::e2D, depthBufferFormat,
{}, vk::ImageSubresourceRange(aspectMask, 0, 1, 0, 1));
depthBuffer.Init(device, depthStencilCreateInfo, depthStencilViewCreateInfo);
device->ExecuteNow([&](auto commandBuffer)
{
depthBuffer.SetLayout(commandBuffer, aspectMask, vk::ImageLayout::eDepthStencilAttachmentOptimal);
});
}
void openVulkanoCpp::Vulkan::FrameBuffer::CreateFrameBuffer()
{
vk::ImageView attachments[2]; // First attachment is the color buffer, second (optional) the depth buffer
if (useDepthBuffer) attachments[1] = depthBuffer.view; //Depth buffer is the same for all frame buffers
const vk::FramebufferCreateInfo fbCreateInfo({}, renderPass->renderPass, useDepthBuffer ? 2 : 1, attachments, size.width,
size.height, 1);
auto images = GetImages();
frameBuffers.resize(images.size());
for (uint32_t i = 0; i < frameBuffers.size(); i++)
{
attachments[0] = images[i]->GetView();
frameBuffers[i] = device->device.createFramebuffer(fbCreateInfo);
}
}
void openVulkanoCpp::Vulkan::FrameBuffer::DestroyFrameBuffer()
{
for (const auto frameBuffer : frameBuffers)
{
device->device.destroyFramebuffer(frameBuffer);
}
frameBuffers.clear();
}

View File

@@ -0,0 +1,107 @@
#pragma once
#include <cstdint>
#include <vulkan/vulkan.hpp>
#include "../Base/ICloseable.hpp"
#include "Image.hpp"
#include "Device.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
class RenderPass;
class FrameBuffer : ICloseable
{
Image depthBuffer;
std::vector<vk::Framebuffer> frameBuffers;
vk::Format depthBufferFormat = vk::Format::eUndefined, colorFormat = vk::Format::eUndefined;
vk::Extent3D size;
RenderPass* renderPass;
bool useDepthBuffer;
Device* device = nullptr;
protected:
uint32_t currentFrameBufferId = 0;
FrameBuffer() = default;
virtual ~FrameBuffer()
{
if (device) FrameBuffer::Close();
}
void Init(Device* device, vk::Extent3D size, bool useDepthBuffer = true);
void SetCurrentFrameId(uint32_t id)
{
currentFrameBufferId = id;
}
uint32_t GetCurrentFrameId() const
{
return currentFrameBufferId;
}
public:
void InitRenderPass(RenderPass* renderPass);
protected:
void Resize(vk::Extent3D size);
void Close() override
{
DestroyFrameBuffer();
if(depthBuffer) depthBuffer.Close();
device = nullptr;
}
protected:
virtual void CreateDepthStencil();
virtual void CreateFrameBuffer();
void DestroyFrameBuffer();
virtual vk::Format FindColorFormat() = 0;
virtual vk::Format FindDepthFormat()
{
return device->GetSupportedDepthFormat();
}
public:
virtual vk::Format GetColorFormat()
{
return colorFormat;
}
virtual vk::Format GetDepthFormat()
{
return depthBufferFormat;
}
virtual std::vector<IImage*> GetImages() = 0;
bool UseDepthBuffer() const
{
return useDepthBuffer;
}
vk::Extent3D GetSize3D() const
{
return size;
}
vk::Extent2D GetSize2D() const
{
return { size.width, size.height };
}
vk::Framebuffer& GetCurrentFrameBuffer()
{
return frameBuffers[currentFrameBufferId];
}
};
}
}

View File

@@ -0,0 +1,98 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include "Buffer.hpp"
#include "VulkanUtils.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
class IImage
{
public:
virtual ~IImage() = default;
virtual vk::Image GetImage() = 0;
virtual vk::ImageView GetView() = 0;
};
struct Image : public Buffer, virtual public IImage
{
vk::Image image;
vk::Extent3D extent;
vk::ImageView view;
vk::Sampler sampler;
vk::Format format = vk::Format::eUndefined;
/**
* \brief
* \param device
* \param imageCreateInfo
* \param imageViewCreateInfo The image will be set automatically after it's creation
* \param memoryPropertyFlags
*/
void Init(const Device* device, const vk::ImageCreateInfo& imageCreateInfo, vk::ImageViewCreateInfo imageViewCreateInfo, const vk::MemoryPropertyFlags& memoryPropertyFlags = vk::MemoryPropertyFlagBits::eDeviceLocal)
{
this->device = device->device;
image = device->device.createImage(imageCreateInfo);
format = imageCreateInfo.format;
extent = imageCreateInfo.extent;
const vk::MemoryRequirements memRequirements = device->device.getImageMemoryRequirements(image);
size = allocSize = memRequirements.size;
const vk::MemoryAllocateInfo memAllocInfo = { allocSize, device->GetMemoryType(memRequirements.memoryTypeBits, memoryPropertyFlags) };
memory = device->device.allocateMemory(memAllocInfo);
device->device.bindImageMemory(image, memory, 0);
imageViewCreateInfo.image = image;
view = device->device.createImageView(imageViewCreateInfo);
}
void SetLayout(vk::CommandBuffer& cmdBuffer, vk::ImageSubresourceRange subResourceRange, vk::ImageLayout newLayout, vk::ImageLayout oldLayout = vk::ImageLayout::eUndefined) const
{
const vk::ImageMemoryBarrier imgMemBarrier(VulkanUtils::GetAccessFlagsForLayout(oldLayout), VulkanUtils::GetAccessFlagsForLayout(newLayout), oldLayout,
newLayout, 0, 0, image, subResourceRange);
cmdBuffer.pipelineBarrier(VulkanUtils::GetPipelineStageForLayout(oldLayout), VulkanUtils::GetPipelineStageForLayout(newLayout),
{}, nullptr, nullptr, imgMemBarrier);
}
void SetLayout(vk::CommandBuffer& cmdBuffer, vk::ImageAspectFlags aspectMask, vk::ImageLayout newLayout, vk::ImageLayout oldLayout = vk::ImageLayout::eUndefined) const
{
SetLayout(cmdBuffer, vk::ImageSubresourceRange(aspectMask, 0, 1, 0, 1), newLayout, oldLayout);
}
void Close() override
{
if(sampler)
{
device.destroySampler(sampler);
sampler = vk::Sampler();
}
if(view)
{
device.destroyImageView(view);
view = vk::ImageView();
}
if(image)
{
device.destroyImage(image);
image = vk::Image();
}
Buffer::Close();
}
operator bool() const { return image.operator bool(); }
vk::Image GetImage() override
{
return image;
}
vk::ImageView GetView() override
{
return view;
}
};
}
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include "../Base/ICloseable.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
struct Pipeline : virtual ICloseable
{
vk::Device device;
vk::DescriptorSetLayout descriptorSetLayout;
vk::PipelineLayout pipelineLayout;
vk::DescriptorPool descriptorPool;
void Init(vk::Device& device)
{
this->device = device;
CreatePipelineLayout();
}
void Close() override
{
device.destroyPipelineLayout(pipelineLayout);
device.destroyDescriptorSetLayout(descriptorSetLayout);
}
private:
void CreatePipelineLayout()
{
vk::PushConstantRange camPushConstantDesc = { vk::ShaderStageFlagBits::eVertex, 0, 64 };
vk::DescriptorSetLayoutBinding nodeLayoutBinding = { 0, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex };
std::array<vk::DescriptorSetLayoutBinding, 1> layoutBindings = { nodeLayoutBinding };
vk::DescriptorSetLayoutCreateInfo dslci = { {}, layoutBindings.size(), layoutBindings.data() };
descriptorSetLayout = device.createDescriptorSetLayout(dslci);
vk::PipelineLayoutCreateInfo plci = { {}, 1, &descriptorSetLayout, 1, &camPushConstantDesc };
pipelineLayout = this->device.createPipelineLayout(plci);
}
};
}
}

View File

@@ -0,0 +1,146 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include "Device.hpp"
#include "FrameBuffer.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
class RenderPass : virtual public ICloseable
{
protected:
vk::Device device;
vk::RenderPassBeginInfo beginInfo;
vk::ClearColorValue clearColor;
vk::ClearDepthStencilValue clearDepth;
std::vector<vk::ClearValue> clearValues;
FrameBuffer* frameBuffer = nullptr;
bool useClearColor = false, useClearDepth = true;
public:
vk::RenderPass renderPass;
RenderPass() = default;
~RenderPass()
{
if (frameBuffer) RenderPass::Close();
}
void Init(Device* device, FrameBuffer* frameBuffer)
{
this->device = device->device;
this->frameBuffer = frameBuffer;
CreateRenderPass();
frameBuffer->InitRenderPass(this);
SetClearColor();
}
void Close() override
{
device.destroy(renderPass);
frameBuffer = nullptr;
clearValues.clear();
}
void SetClearColor(vk::ClearColorValue clearColor = vk::ClearColorValue(std::array<float, 4>{ 0.39f, 0.58f, 0.93f, 1.0f }))
{
this->clearColor = clearColor;
useClearColor = true;
UpdateBeginInfo();
}
void DisableClearColor()
{
useClearColor = false;
UpdateBeginInfo();
}
void SetClearDepth(vk::ClearDepthStencilValue clearDepth = vk::ClearDepthStencilValue(1, 0))
{
this->clearDepth = clearDepth;
useClearDepth = true;
UpdateBeginInfo();
}
void DisableClearDepth()
{
useClearDepth = false;
}
virtual void UpdateBeginInfo()
{ //TODO allow to control the render rect size
clearValues.clear();
if (useClearColor) clearValues.emplace_back(clearColor);
if(frameBuffer->UseDepthBuffer() && useClearDepth) clearValues.emplace_back(clearDepth);
beginInfo = vk::RenderPassBeginInfo(renderPass, vk::Framebuffer(),
vk::Rect2D(vk::Offset2D(), frameBuffer->GetSize2D()),
clearValues.size(), clearValues.data());
}
virtual void Begin(vk::CommandBuffer& commandBuffer)
{
beginInfo.framebuffer = frameBuffer->GetCurrentFrameBuffer();
commandBuffer.beginRenderPass(beginInfo, vk::SubpassContents::eSecondaryCommandBuffers);
}
virtual void End(vk::CommandBuffer& commandBuffer)
{
commandBuffer.endRenderPass();
}
FrameBuffer* GetFrameBuffer() const
{
return frameBuffer;
}
protected:
virtual void CreateRenderPass()
{
std::vector<vk::AttachmentDescription> attachments;
attachments.reserve(frameBuffer->UseDepthBuffer() ? 2 : 1);
// Color attachment
attachments.emplace_back(vk::AttachmentDescriptionFlags(), frameBuffer->GetColorFormat(), vk::SampleCountFlagBits::e1,
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eLoad,
vk::AttachmentStoreOp::eStore, vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR);
vk::AttachmentReference* depthReference = nullptr;;
if (frameBuffer->UseDepthBuffer())
{ // Depth attachment
attachments.emplace_back(vk::AttachmentDescriptionFlags(), frameBuffer->GetDepthFormat(), vk::SampleCountFlagBits::e1,
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, vk::AttachmentLoadOp::eClear,
vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal);
depthReference = new vk::AttachmentReference(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);
}
std::vector<vk::AttachmentReference> colorAttachmentReferences;
colorAttachmentReferences.emplace_back(0, vk::ImageLayout::eColorAttachmentOptimal);
std::vector<vk::SubpassDescription> subPasses;
subPasses.emplace_back(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr,
colorAttachmentReferences.size(), colorAttachmentReferences.data(), nullptr, depthReference, 0, nullptr);
std::vector<vk::SubpassDependency> subPassDependencies;
subPassDependencies.emplace_back(0, VK_SUBPASS_EXTERNAL, vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::PipelineStageFlagBits::eBottomOfPipe, vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
vk::AccessFlagBits::eMemoryRead, vk::DependencyFlagBits::eByRegion);
subPassDependencies.emplace_back(VK_SUBPASS_EXTERNAL, 0, vk::PipelineStageFlagBits::eBottomOfPipe,
vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eMemoryRead,
vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
vk::DependencyFlagBits::eByRegion);
const vk::RenderPassCreateInfo createInfo(vk::RenderPassCreateFlags(), attachments.size(), attachments.data(),
subPasses.size(), subPasses.data(), subPassDependencies.size(), subPassDependencies.data());
CreateRenderPass(createInfo);
delete depthReference;
}
void CreateRenderPass(const vk::RenderPassCreateInfo& renderPassCreateInfo)
{
renderPass = device.createRenderPass(renderPassCreateInfo);
}
};
}
}

View File

@@ -0,0 +1,220 @@
#pragma once
#include <stdexcept>
#include <iostream>
#include <fstream>
#include "../Base/Render/IRenderer.hpp"
#include "../Base/UI/IWindow.hpp"
#include "../Base/Logger.hpp"
#include "Context.hpp"
#include "Resources/ResourceManager.hpp"
#include "../Data/ReadOnlyAtomicArrayQueue.hpp"
#include "CommandHelper.hpp"
#include "../Base/EngineConfiguration.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
struct WaitSemaphores
{
std::vector<vk::Semaphore> renderReady, renderComplete;
};
class Renderer : public IRenderer
{
Context context;
std::shared_ptr<spdlog::logger> logger;
std::vector<WaitSemaphores> waitSemaphores;
Scene::Scene* scene = nullptr;
std::ofstream perfFile;
ResourceManager resourceManager;
uint32_t currentImageId = -1;
std::vector<std::thread> threadPool;
std::vector<std::vector<CommandHelper>> commands;
std::vector<std::vector<vk::CommandBuffer>> submitBuffers;
VulkanShader* shader;
public:
Renderer() = default;
virtual ~Renderer() = default;
void Init(IGraphicsAppManager* graphicsAppManager, IWindow* window) override
{
logger = Logger::RENDER;
logger->info("Initializing Vulkan renderer ...");
IVulkanWindow* vulkanWindow = window->GetVulkanWindow();
if (!vulkanWindow)
{
logger->error("The provided window is not compatible with Vulkan.");
throw std::runtime_error("The provided window is not compatible with Vulkan.");
}
context.Init(graphicsAppManager, vulkanWindow);
for (int i = 0; i < context.swapChain.GetImageCount(); i++)
{
waitSemaphores.emplace_back();
waitSemaphores[i].renderComplete.push_back(context.device->device.createSemaphore({}));
waitSemaphores[i].renderReady.resize(2);
}
resourceManager.Init(&context, context.swapChain.GetImageCount());
threadPool.resize(EngineConfiguration::GetEngineConfiguration()->GetNumThreads() - 1);
//Setup cmd pools and buffers
commands.resize(threadPool.size() + 2); // One extra cmd object for the primary buffer and one for the main thread
for(uint32_t i = 0; i < commands.size(); i++)
{
commands[i] = std::vector<CommandHelper>(context.swapChain.GetImageCount());
for(size_t j = 0; j < commands[i].size(); j++)
{
commands[i][j].Init(context.device->device, context.device->queueIndices.GetGraphics(),
(i == commands.size() - 1) ? vk::CommandBufferLevel::ePrimary : vk::CommandBufferLevel::eSecondary);
}
}
submitBuffers.resize(context.swapChain.GetImageCount());
for(uint32_t i = 0; i < submitBuffers.size(); i++)
{
submitBuffers[i].resize(commands.size() - 1);
for (size_t j = 0; j < submitBuffers[i].size(); j++)
{
submitBuffers[i][j] = commands[j][i].cmdBuffer;
}
}
shader = resourceManager.CreateShader(scene->shader);
perfFile.open("perf.csv");
perfFile << "sep=,\ntotal,fps\n";
logger->info("Vulkan renderer initialized");
}
void Tick() override
{
currentImageId = context.swapChain.AcquireNextImage();
auto tickStart= std::chrono::high_resolution_clock::now();
Render();
// Perf logging
auto tickDone = std::chrono::high_resolution_clock::now();
auto time = std::chrono::duration_cast<std::chrono::microseconds>(tickDone - tickStart).count();
perfFile << time << ',' << 1000000000.0 / time << '\n';
}
void Close() override
{
perfFile.close();
//context.Close();
}
std::string GetMainRenderDeviceName() override
{
return (context.device) ? context.device->GetDeviceName() : "Unknown";
}
void Resize(const uint32_t newWidth, const uint32_t newHeight) override
{
context.Resize(newWidth, newHeight);
resourceManager.Resize();
}
void SetScene(Scene::Scene* scene) override
{
this->scene = scene;
}
Scene::Scene* GetScene() override
{
return scene;
}
CommandHelper* GetCommandData(uint32_t poolId)
{
return &commands[poolId][currentImageId];
}
static void RunThread(Renderer* renderer, Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*>* jobQueue, uint32_t id)
{
renderer->RecordSecondaryBuffer(jobQueue, id);
}
void StartThreads(Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*>* jobQueue)
{
for(uint32_t i = 0; i < threadPool.size(); i++)
{
threadPool[i] = std::thread(RunThread, this, jobQueue, i);
}
}
void RecordPrimaryBuffer()
{
CommandHelper* cmdHelper = GetCommandData(commands.size() - 1);
cmdHelper->Reset();
cmdHelper->cmdBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
context.swapChainRenderPass.Begin(cmdHelper->cmdBuffer);
}
void Submit()
{
for (auto& thread : threadPool) { thread.join(); } // Wait till everything is recorded
CommandHelper* cmdHelper = GetCommandData(commands.size() - 1);
cmdHelper->cmdBuffer.executeCommands(submitBuffers[currentImageId].size(), submitBuffers[currentImageId].data());
context.swapChainRenderPass.End(cmdHelper->cmdBuffer);
cmdHelper->cmdBuffer.end();
std::array<vk::PipelineStageFlags, 2> stateFlags = { vk::PipelineStageFlags(vk::PipelineStageFlagBits::eColorAttachmentOutput), vk::PipelineStageFlags(vk::PipelineStageFlagBits::eColorAttachmentOutput) };
waitSemaphores[currentImageId].renderReady[0] = resourceManager.EndFrame();
waitSemaphores[currentImageId].renderReady[1] = context.swapChain.imageAvailableSemaphore;
vk::SubmitInfo si = vk::SubmitInfo(
waitSemaphores[currentImageId].renderReady.size(), waitSemaphores[currentImageId].renderReady.data(), stateFlags.data(),
1, &cmdHelper->cmdBuffer,
waitSemaphores[currentImageId].renderComplete.size(), waitSemaphores[currentImageId].renderComplete.data());
context.device->graphicsQueue.submit(1, &si, context.swapChain.GetCurrentSubmitFence());
context.swapChain.Present(context.device->graphicsQueue, waitSemaphores[currentImageId].renderComplete);
}
void Render()
{
resourceManager.StartFrame(currentImageId);
Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*> jobQueue(scene->shapeList);
StartThreads(&jobQueue);
RecordPrimaryBuffer();
RecordSecondaryBuffer(&jobQueue, threadPool.size());
Submit();
}
void RecordSecondaryBuffer(Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*>* jobQueue, uint32_t poolId)
{
Scene::Geometry* lastGeo = nullptr;
Scene::Node* lastNode = nullptr;
CommandHelper* cmdHelper = GetCommandData(poolId);
cmdHelper->Reset();
vk::CommandBufferInheritanceInfo inheritance = { context.swapChainRenderPass.renderPass, 0, context.swapChainRenderPass.GetFrameBuffer()->GetCurrentFrameBuffer() };
cmdHelper->cmdBuffer.begin(vk::CommandBufferBeginInfo{ vk::CommandBufferUsageFlagBits::eOneTimeSubmit | vk::CommandBufferUsageFlagBits::eRenderPassContinue, &inheritance });
shader->Record(cmdHelper->cmdBuffer, currentImageId);
cmdHelper->cmdBuffer.pushConstants(context.pipeline.pipelineLayout, vk::ShaderStageFlagBits::eVertex, 0, 64, scene->GetCamera()->GetViewProjectionMatrixPointer());
Scene::Drawable** drawablePointer;
while((drawablePointer = jobQueue->Pop()) != nullptr)
{
Scene::Drawable* drawable = *drawablePointer;
Scene::Geometry* mesh = drawable->mesh;
if (mesh != lastGeo)
{
if (!mesh->renderGeo) resourceManager.PrepareGeometry(mesh);
dynamic_cast<VulkanGeometry*>(mesh->renderGeo)->Record(cmdHelper->cmdBuffer, currentImageId);
lastGeo = mesh;
}
for(Scene::Node* node : drawable->nodes)
{
if (node != lastNode)
{
if (!node->renderNode) resourceManager.PrepareNode(node);
dynamic_cast<VulkanNode*>(node->renderNode)->Record(cmdHelper->cmdBuffer, currentImageId);
lastNode = node;
}
cmdHelper->cmdBuffer.drawIndexed(mesh->GetIndexCount(), 1, 0, 0, 0);
}
}
cmdHelper->cmdBuffer.end();
}
};
}
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "../Scene/VulkanShader.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
struct VulkanShader;
class IShaderOwner
{
public:
virtual void RemoveShader(VulkanShader* shader) = 0;
};
}
}

View File

@@ -0,0 +1,93 @@
#pragma once
#include <vulkan/vulkan.hpp>
namespace openVulkanoCpp
{
namespace Vulkan
{
struct MemoryAllocation
{
vk::DeviceMemory memory;
size_t used, size;
uint32_t type;
MemoryAllocation(size_t size, uint32_t type)
{
memory = nullptr;
this->size = size;
used = 0;
this->type = type;
}
size_t FreeSpace() const
{
return size - used;
}
};
struct ManagedBuffer
{
MemoryAllocation* allocation;
vk::DeviceSize offset, size;
vk::Buffer buffer;
vk::BufferUsageFlags usage;
vk::MemoryPropertyFlags properties;
vk::Device device;
void* mapped = nullptr;
bool IsLast()
{
return (offset + size == allocation->used);
}
/**
* \brief Maps the buffer into the memory of the host.
* \tparam T The type of the buffers data.
* \param offset The offset from where to map the buffer.
* \param size The size to be mapped. VK_WHOLE_SIZE to map the whole buffer.
* \return The pointer to the mapped buffer.
*/
template <typename T = void>
T* Map(size_t offset = 0, vk::DeviceSize size = VK_WHOLE_SIZE)
{
if (size == VK_WHOLE_SIZE) size = this->size;
mapped = device.mapMemory(allocation->memory, this->offset + offset, size, vk::MemoryMapFlags());
return static_cast<T*>(mapped);
}
/**
* \brief Un-maps the buffer from the host.
*/
void UnMap()
{
device.unmapMemory(allocation->memory);
mapped = nullptr;
}
void Copy(void* data) const
{
if(mapped)
{
memcpy(mapped, data, size);
}
else
{
void* dataMapped = device.mapMemory(allocation->memory, offset, size);
memcpy(dataMapped, data, size);
device.unmapMemory(allocation->memory);
}
}
void Copy(void* data, uint32_t size, uint32_t offset) const
{
if(mapped) memcpy(static_cast<char*>(mapped) + offset, data, size);
else
{
void* dataMapped = device.mapMemory(allocation->memory, this->offset + offset, size);
memcpy(dataMapped, data, size);
device.unmapMemory(allocation->memory);
}
}
};
}
}

View File

@@ -0,0 +1,258 @@
#pragma once
#include "vulkan/vulkan.hpp"
#include "../Device.hpp"
#include "../../Base/ICloseable.hpp"
#include "../Scene/VulkanShader.hpp"
#include "IShaderOwner.hpp"
#include "../Scene/VulkanGeometry.hpp"
#include "ManagedResource.hpp"
#include "../Scene/VulkanNode.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
class ResourceManager : virtual public ICloseable, virtual public IShaderOwner
{
Context* context;
vk::Device device = nullptr;
vk::Queue transferQueue = nullptr;
vk::CommandPool* cmdPools = nullptr;
vk::CommandBuffer* cmdBuffers = nullptr;
vk::Semaphore* semaphores = nullptr;
std::vector<MemoryAllocation*> allocations;
std::vector<VulkanShader*> shaders;
MemoryAllocation* lastAllocation = nullptr;
std::mutex mutex;
vk::DeviceSize uniformBufferAlignment;
std::vector<std::vector<ManagedBuffer*>> toFree;
std::vector<ManagedBuffer*> recycleBuffers;
int buffers = -1, currentBuffer = -1;
public:
ResourceManager() = default;
virtual ~ResourceManager() { if (device) ResourceManager::Close(); }
void Init(Context* context, int buffers = 2)
{
this->context = context;
this->device = context->device->device;
this->buffers = buffers;
uniformBufferAlignment = context->device->properties.limits.minUniformBufferOffsetAlignment;
cmdPools = new vk::CommandPool[buffers];
cmdBuffers = new vk::CommandBuffer[buffers];
semaphores = new vk::Semaphore[buffers];
for (int i = 0; i < buffers; i++)
{
cmdPools[i] = this->device.createCommandPool({ {}, context->device->queueIndices.transfer });
cmdBuffers[i] = this->device.allocateCommandBuffers({ cmdPools[i], vk::CommandBufferLevel::ePrimary, 1 })[0];
semaphores[i] = this->device.createSemaphore({});
}
toFree.resize(buffers);
transferQueue = this->device.getQueue(context->device->queueIndices.transfer, 0);
}
void Close() override
{
transferQueue.waitIdle();
for (int i = 0; i < buffers; i++)
{
device.freeCommandBuffers(cmdPools[i], 1, &cmdBuffers[i]);
device.destroyCommandPool(cmdPools[0]);
}
for (auto shader : shaders)
{
shader->Close();
}
cmdBuffers = nullptr;
cmdPools = nullptr;
device = nullptr;
}
void StartFrame(uint64_t frameId)
{
currentBuffer = frameId;
FreeBuffers();
device.resetCommandPool(cmdPools[currentBuffer], {});
cmdBuffers[currentBuffer].begin({ vk::CommandBufferUsageFlagBits::eOneTimeSubmit });
}
vk::Semaphore EndFrame()
{
cmdBuffers[currentBuffer].end();
vk::SubmitInfo si = { 0, nullptr, nullptr, 1, &cmdBuffers[currentBuffer], 1, &semaphores[currentBuffer] };
transferQueue.submit(1, &si, vk::Fence());
return semaphores[currentBuffer];
}
void Resize()
{
for (auto shader : shaders)
{
Scene::Shader* s = shader->shader;
shader->Close();
shader->Init(context, s, this);
}
}
void PrepareGeometry(Scene::Geometry* geometry)
{
mutex.lock();
if(!geometry->renderGeo)
{
VulkanGeometry* vkGeometry = new VulkanGeometry();
ManagedBuffer* vertexBuffer = CreateDeviceOnlyBufferWithData(sizeof(Vertex) * geometry->GetVertexCount(), vk::BufferUsageFlagBits::eVertexBuffer, geometry->GetVertices());
ManagedBuffer* indexBuffer = CreateDeviceOnlyBufferWithData(Utils::EnumAsInt(geometry->indexType) * geometry->GetIndexCount(), vk::BufferUsageFlagBits::eIndexBuffer, geometry->GetIndices());
vkGeometry->Init(geometry, vertexBuffer->buffer, indexBuffer->buffer);
geometry->renderGeo = vkGeometry;
}
mutex.unlock();
}
void PrepareMaterial(Scene::Material* material)
{
mutex.lock();
if(!material->shader->renderShader)
{
material->shader->renderShader = CreateShader(material->shader);
}
mutex.unlock();
}
void PrepareNode(Scene::Node* node)
{
mutex.lock();
if (!node->renderNode)
{
UniformBuffer* uBuffer = new UniformBuffer();
ManagedBuffer* buffer;
VulkanNode* vkNode;
const vk::DeviceSize allocSize = aligned(sizeof(glm::mat4x4), uniformBufferAlignment);
if (node->GetUpdateFrequency() != Scene::UpdateFrequency::Never)
{
vkNode = new VulkanNodeDynamic();
uint32_t imgs = context->swapChain.GetImageCount();
buffer = CreateBuffer(imgs * allocSize, vk::BufferUsageFlagBits::eUniformBuffer, vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible);
buffer->Map();
}
else
{
vkNode = new VulkanNode();
buffer = CreateDeviceOnlyBufferWithData(sizeof(glm::mat4), vk::BufferUsageFlagBits::eUniformBuffer, &node->worldMat);
}
uBuffer->Init(buffer, allocSize, &context->pipeline.descriptorSetLayout, context->pipeline.pipelineLayout);
vkNode->Init(node, uBuffer);
node->renderNode = vkNode;
}
mutex.unlock();
}
void RemoveShader(VulkanShader* shader) override
{
Utils::Remove(shaders, shader);
}
protected: // Allocation management
static vk::DeviceSize aligned(vk::DeviceSize size, vk::DeviceSize byteAlignment)
{
return (size + byteAlignment - 1) & ~(byteAlignment - 1);
}
void FreeBuffer(ManagedBuffer* buffer)
{
toFree[currentBuffer].push_back(buffer);
}
void DoFreeBuffer(ManagedBuffer* buffer)
{
if (buffer->IsLast())
{
device.destroyBuffer(buffer->buffer);
buffer->allocation->used -= buffer->size;
}
else
{
recycleBuffers.push_back(buffer);
}
}
void FreeBuffers()
{
for (auto& i : toFree[currentBuffer])
{
DoFreeBuffer(i);
}
toFree[currentBuffer].clear();
}
ManagedBuffer* CreateDeviceOnlyBufferWithData(vk::DeviceSize size, vk::BufferUsageFlagBits usage, void* data)
{
ManagedBuffer* target = CreateBuffer(size, usage | vk::BufferUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal);
ManagedBuffer* uploadBuffer = CreateBuffer(size, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible);
uploadBuffer->Copy(data, size, 0);
RecordCopy(uploadBuffer->buffer, target->buffer, size);
FreeBuffer(uploadBuffer);
return target;
}
void RecordCopy(vk::Buffer src, vk::Buffer dest, vk::DeviceSize size) const
{
vk::BufferCopy copyRegion = { 0, 0, size };
cmdBuffers[currentBuffer].copyBuffer(src, dest, 1, &copyRegion);
}
ManagedBuffer* CreateBuffer(vk::DeviceSize size, const vk::BufferUsageFlags& usage, const vk::MemoryPropertyFlags& properties)
{
size = aligned(size, uniformBufferAlignment);
const vk::BufferCreateInfo bufferCreateInfo = { {}, size, usage, vk::SharingMode::eExclusive };
vk::Buffer buffer = device.createBuffer(bufferCreateInfo);
const vk::MemoryRequirements memoryRequirements = device.getBufferMemoryRequirements(buffer);
uint32_t memtype = context->device->GetMemoryType(memoryRequirements.memoryTypeBits, properties);
if (memoryRequirements.size != size) Logger::DATA->warn("Memory Requirement Size ({0}) != Size ({1})", memoryRequirements.size, size);
MemoryAllocation* allocation = GetFreeMemoryAllocation(memoryRequirements.size, memtype);
uint32_t offset = allocation->used;
device.bindBufferMemory(buffer, allocation->memory, offset);
allocation->used += memoryRequirements.size;
return new ManagedBuffer{ allocation, offset, size, buffer, usage, properties, device, nullptr };
}
MemoryAllocation* CreateMemoryAllocation(size_t size, uint32_t type, bool addToCache = true)
{
MemoryAllocation* alloc = new MemoryAllocation(size, type);
const vk::MemoryAllocateInfo allocInfo = { size, type };
alloc->memory = device.allocateMemory(allocInfo);
if (addToCache) allocations.push_back(alloc);
return alloc;
}
MemoryAllocation* GetFreeMemoryAllocation(size_t size, uint32_t type, bool createIfAllFull = true)
{
MemoryAllocation* alloc = nullptr;
for (MemoryAllocation* allocation : allocations)
{
if (allocation->type == type && allocation->FreeSpace() >= size)
{
alloc = allocation;
break;
}
}
if(!alloc && createIfAllFull) alloc = CreateMemoryAllocation(256 * 1024 * 1024, type, true);
if(alloc) lastAllocation = alloc;
return alloc;
}
public:
VulkanShader* CreateShader(Scene::Shader* shader)
{
VulkanShader* vkShader = new VulkanShader();
vkShader->Init(context, shader, this);
shaders.push_back(vkShader);
return vkShader;
}
};
}
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include "../../Base/ICloseable.hpp"
#include "../Scene/IRecordable.hpp"
#include "ManagedResource.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
struct UniformBuffer : virtual ICloseable, virtual IRecordable
{
ManagedBuffer* buffer;
vk::DescriptorPool descPool;
vk::DescriptorSet descSet;
vk::PipelineLayout layout;
uint32_t allocSizeFrame;
void Init(ManagedBuffer* buffer, uint32_t allocSizeFrame, vk::DescriptorSetLayout* descriptorSetLayout, vk::PipelineLayout layout)
{
this->buffer = buffer;
this->layout = layout;
this->allocSizeFrame = allocSizeFrame;
vk::DescriptorPoolSize poolSize = { vk::DescriptorType::eUniformBufferDynamic, 1 };
const vk::DescriptorPoolCreateInfo poolCreateInfo = { {}, 1, 1, &poolSize };
descPool = buffer->device.createDescriptorPool(poolCreateInfo);
const vk::DescriptorSetAllocateInfo descSetAllocInfo = { descPool, 1, descriptorSetLayout };
descSet = buffer->device.allocateDescriptorSets(descSetAllocInfo)[0];
vk::DescriptorBufferInfo bufferInfo = { buffer->buffer, 0, allocSizeFrame };
vk::WriteDescriptorSet writeDescriptorSet = { descSet };
writeDescriptorSet.descriptorCount = 1;
writeDescriptorSet.descriptorType = vk::DescriptorType::eUniformBufferDynamic;
writeDescriptorSet.pBufferInfo = &bufferInfo;
buffer->device.updateDescriptorSets(1, &writeDescriptorSet, 0, nullptr);
}
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
{
uint32_t frameOffset = allocSizeFrame * bufferId;
cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, 1,
&descSet, 1, &frameOffset);
}
void Update(void* data, uint32_t size, uint32_t bufferId) const
{
buffer->Copy(data, size, allocSizeFrame * bufferId);
}
void Close() override
{
buffer->device.destroyDescriptorPool(descPool);
}
};
}
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "vulkan/vulkan.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
class IRecordable
{
public:
virtual ~IRecordable() = default;
virtual void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) = 0;
};
}
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include "IRecordable.hpp"
#include "../../Scene/Scene.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
class VulkanGeometry : virtual public IRecordable, virtual public ICloseable
{
Scene::Geometry* geometry = nullptr;
vk::Buffer vertexBuffer, indexBuffer;
vk::IndexType indexType;
vk::DeviceSize* offsets = new vk::DeviceSize();
public:
VulkanGeometry() = default;
virtual ~VulkanGeometry() { if (vertexBuffer) VulkanGeometry::Close(); };
void Init(Scene::Geometry* geo, vk::Buffer vertexBuffer, vk::Buffer indexBuffer)
{
this->geometry = geo;
offsets[0] = 0;
indexType = (geo->indexType == Scene::VertexIndexType::UINT16) ? vk::IndexType::eUint16 : vk::IndexType::eUint32;
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
}
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
{
cmdBuffer.bindVertexBuffers(0, 1, &vertexBuffer, offsets);
cmdBuffer.bindIndexBuffer(indexBuffer, 0, indexType);
}
void Close() override
{
}
};
}
}

View File

@@ -0,0 +1,53 @@
#pragma once
#include "../../Base/ICloseable.hpp"
#include "IRecordable.hpp"
#include "../../Scene/Camera.hpp"
#include "../Resources/UniformBuffer.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
struct VulkanNode : virtual IRecordable, virtual ICloseable
{
Scene::Node* node = nullptr;
UniformBuffer* buffer = nullptr;
virtual void Init(Scene::Node* node, UniformBuffer* uniformBuffer)
{
this->node = node;
this->buffer = uniformBuffer;
}
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
{
buffer->Record(cmdBuffer, 0);
}
void Close() override {}
};
struct VulkanNodeDynamic : VulkanNode
{
uint32_t lastUpdate = -1;
void Init(Scene::Node* node, UniformBuffer* uniformBuffer) override
{
VulkanNode::Init(node, uniformBuffer);
lastUpdate = -1;
}
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
{
if(bufferId != lastUpdate)
{
lastUpdate = bufferId;
buffer->Update(&node->worldMat, sizeof(glm::mat4x4), bufferId);
}
buffer->Record(cmdBuffer, bufferId);
}
void Close() override{}
};
}
}

View File

@@ -0,0 +1,97 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include "../Device.hpp"
#include "../../Scene/Shader.hpp"
#include "../../Scene/Vertex.hpp"
#include "../../Base/ICloseable.hpp"
#include "../Resources/IShaderOwner.hpp"
#include "IRecordable.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
vk::PrimitiveTopology ToVkTopology(Scene::Topology topology)
{
switch (topology) {
case Scene::Topology::PointList: return vk::PrimitiveTopology::ePointList;
case Scene::Topology::LineList: return vk::PrimitiveTopology::eLineList;
case Scene::Topology::LineStripe: return vk::PrimitiveTopology::eLineStrip;
case Scene::Topology::TriangleList: return vk::PrimitiveTopology::eTriangleList;
case Scene::Topology::TriangleStripe: return vk::PrimitiveTopology::eTriangleStrip;
default: throw std::runtime_error("Unknown topology!");
}
}
struct VulkanShader : virtual public ICloseable, virtual public IRecordable
{
Scene::Shader* shader = nullptr;
vk::Device device;
vk::ShaderModule shaderModuleVertex, shaderModuleFragment;
vk::Pipeline pipeline;
IShaderOwner* owner;
VulkanShader() = default;
virtual ~VulkanShader() { if (shader) VulkanShader::Close(); }
void Init(Context* context, Scene::Shader* shader, IShaderOwner* owner)
{
this->device = context->device->device;
this->shader = shader;
this->owner = owner;
shaderModuleVertex = context->device->CreateShaderModule(shader->vertexShaderName + ".vert.spv");
shaderModuleFragment = context->device->CreateShaderModule(shader->fragmentShaderName + ".frag.spv");
std::vector<vk::PipelineShaderStageCreateInfo> shaderStageCreateInfos(2);
shaderStageCreateInfos[0] = { {}, vk::ShaderStageFlagBits::eVertex, shaderModuleVertex, "main" };
shaderStageCreateInfos[1] = { {}, vk::ShaderStageFlagBits::eFragment, shaderModuleFragment, "main" };
auto vertexBindDesc = vk::VertexInputBindingDescription(0, sizeof(Vertex), vk::VertexInputRate::eVertex);
std::vector<vk::VertexInputAttributeDescription> attributeDescriptions;
attributeDescriptions.emplace_back(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, position));
attributeDescriptions.emplace_back(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, normal));
attributeDescriptions.emplace_back(2, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, tangent));
attributeDescriptions.emplace_back(3, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, biTangent));
attributeDescriptions.emplace_back(4, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, textureCoordinates));
attributeDescriptions.emplace_back(5, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(Vertex, color));
auto viewport = context->swapChain.GetFullscreenViewport();
auto scissor = context->swapChain.GetFullscreenScissor();
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = { {}, 1, &viewport, 1, &scissor };
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = { {}, 1, &vertexBindDesc,
static_cast<uint32_t>(attributeDescriptions.size()), attributeDescriptions.data() };
vk::PipelineInputAssemblyStateCreateInfo inputAssembly = { {}, ToVkTopology(shader->topology), 0 };
vk::PipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.cullMode = vk::CullModeFlagBits::eBack;
vk::PipelineMultisampleStateCreateInfo msaa = {};
vk::PipelineDepthStencilStateCreateInfo depth = { {}, 1, 1, vk::CompareOp::eGreater };
vk::PipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eA | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eR;
vk::PipelineColorBlendStateCreateInfo colorInfo = {};
colorInfo.attachmentCount = 1;
colorInfo.pAttachments = &colorBlendAttachment;
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = { {}, static_cast<uint32_t>(shaderStageCreateInfos.size()), shaderStageCreateInfos.data(), &pipelineVertexInputStateCreateInfo, &inputAssembly,
nullptr, &viewportStateCreateInfo, &rasterizer, &msaa, &depth, &colorInfo, nullptr, context->pipeline.pipelineLayout, context->swapChainRenderPass.renderPass };
pipeline = this->device.createGraphicsPipeline({}, pipelineCreateInfo);
}
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
{
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
}
void Close() override
{
owner->RemoveShader(this);
shader = nullptr;
device.destroyPipeline(pipeline);
device.destroyShaderModule(shaderModuleVertex);
device.destroyShaderModule(shaderModuleFragment);
}
};
}
}

View File

@@ -0,0 +1,261 @@
#pragma once
#include <algorithm>
#include <vulkan/vulkan.hpp>
#include "Device.hpp"
#include "Image.hpp"
#include "FrameBuffer.hpp"
namespace openVulkanoCpp
{
namespace Vulkan
{
struct SwapChainImage : virtual public IImage
{
vk::Image image;
vk::ImageView view;
vk::Fence fence;
vk::Image GetImage() override
{
return image;
}
vk::ImageView GetView() override
{
return view;
}
};
class SwapChain : public FrameBuffer
{
vk::SurfaceKHR surface;
std::vector<SwapChainImage> images;
Device* device = nullptr;
IVulkanWindow* window = nullptr;
vk::SurfaceFormatKHR surfaceFormat;
vk::PresentModeKHR presentMode;
vk::Viewport fullscreenViewport;
bool useVsync = false;
uint32_t preferredImageCount = 2; //TODO add option
vk::Extent2D size{0,0};
public:
vk::SwapchainKHR swapChain;
vk::Semaphore imageAvailableSemaphore;
SwapChain() = default;
~SwapChain() { if (device) SwapChain::Close(); }
void Init(Device* device, vk::SurfaceKHR surface, IVulkanWindow* window)
{
if (!device) throw std::runtime_error("The device must not be null");
if (!window) throw std::runtime_error("The window must not be null");
this->device = device;
this->surface = surface;
this->window = window;
imageAvailableSemaphore = device->device.createSemaphore({});
CreateSwapChain({window->GetWidth(), window->GetHeight() });
FrameBuffer::Init(device, vk::Extent3D(size, 1));
}
void Close() override
{
DestroySwapChain();
device->device.destroySemaphore(imageAvailableSemaphore);
device = nullptr;
FrameBuffer::Close();
}
void Resize(const uint32_t newWidth, const uint32_t newHeight)
{
if(newWidth == 0 || newHeight == 0) return; // Swap chain size of 0 pixel is not allowed
CreateSwapChain({ newWidth, newHeight });
FrameBuffer::Resize(vk::Extent3D(size, 1));
}
vk::Extent2D GetSize() const
{
return size;
}
vk::Viewport GetFullscreenViewport() const
{
return fullscreenViewport;
}
vk::Rect2D GetFullscreenScissor() const
{
return { {0,0}, GetSize() };
}
uint32_t AcquireNextImage(const vk::Fence fence = vk::Fence())
{
const auto resultValue = device->device.acquireNextImageKHR(swapChain, UINT64_MAX, imageAvailableSemaphore, fence);
const vk::Result result = resultValue.result;
if (result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR) throw std::error_code(result);
SetCurrentFrameId(resultValue.value);
vk::Fence submitFence = GetCurrentSubmitFence();
device->device.waitForFences(1, &submitFence, true, -1);
device->device.resetFences(1, &submitFence);
return currentFrameBufferId;
}
vk::Fence& GetCurrentSubmitFence()
{
return images[currentFrameBufferId].fence;
}
void Present(vk::Queue& queue ,std::vector<vk::Semaphore>& semaphores) const
{
queue.presentKHR(vk::PresentInfoKHR(semaphores.size(), semaphores.data(),
1, &swapChain, &currentFrameBufferId));
}
bool UseVsync() { return useVsync; }
void SetVsync(bool useVsync)
{ //TODO change swap chain
this->useVsync = useVsync;
}
uint32_t GetImageCount() const
{
return images.size();
}
private:
void CreateSwapChain(vk::Extent2D size)
{
Logger::RENDER->debug("Creating swap chain for window {0} ...", window->GetWindowId());
surfaceFormat = ChoseSurfaceFormat();
presentMode = ChosePresentMode();
const vk::SurfaceCapabilitiesKHR surfaceCapabilities = device->physicalDevice.getSurfaceCapabilitiesKHR(surface);
if(surfaceCapabilities.currentExtent.width != ~static_cast<uint32_t>(0))
{ // The surface does provide it's size to the vulkan driver
size = surfaceCapabilities.currentExtent;
}
vk::SurfaceTransformFlagBitsKHR preTransform; //TODO add option to allow rotation and other modifications
if (surfaceCapabilities.supportedTransforms & vk::SurfaceTransformFlagBitsKHR::eIdentity)
{ preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity; }
else { preTransform = surfaceCapabilities.currentTransform; }
uint32_t usingImages = std::max(preferredImageCount, surfaceCapabilities.minImageCount);
if (surfaceCapabilities.maxImageCount > 0) //GPU has limit of swap chain images
usingImages = std::min(usingImages, surfaceCapabilities.maxImageCount);
Logger::RENDER->debug("GPU supports {0} to {1} swap chain images. Preferred: {2}, Using: {3}", surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount, preferredImageCount, usingImages);
const vk::SwapchainCreateInfoKHR createInfo({}, surface, usingImages, surfaceFormat.format,
surfaceFormat.colorSpace, size, 1,
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst,
vk::SharingMode::eExclusive, 0, nullptr, preTransform,
vk::CompositeAlphaFlagBitsKHR::eOpaque, presentMode, VK_TRUE, swapChain);
const vk::SwapchainKHR newSwapChain = device->device.createSwapchainKHR(createInfo);
DestroySwapChain();
swapChain = newSwapChain;
this->size = size;
CreateSwapChainImages();
fullscreenViewport = vk::Viewport{ 0, 0, (float)size.width, (float)size.height, 0, 1 };
Logger::RENDER->debug("Swap chain for window {0} created", window->GetWindowId());
}
void CreateSwapChainImages()
{
vk::ImageViewCreateInfo imgViewCreateInfo;
imgViewCreateInfo.format = surfaceFormat.format;
imgViewCreateInfo.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
imgViewCreateInfo.subresourceRange.levelCount = 1;
imgViewCreateInfo.subresourceRange.layerCount = 1;
imgViewCreateInfo.viewType = vk::ImageViewType::e2D;
auto swapChainImages = device->device.getSwapchainImagesKHR(swapChain);
images.resize(swapChainImages.size());
for (uint32_t i = 0; i < swapChainImages.size(); i++)
{
images[i].image = swapChainImages[i];
imgViewCreateInfo.image = swapChainImages[i];
images[i].view = device->device.createImageView(imgViewCreateInfo);
images[i].fence = device->device.createFence({ vk::FenceCreateFlags(vk::FenceCreateFlagBits::eSignaled)});
}
}
void DestroySwapChain() const
{
for(auto& image : images)
{
device->device.destroyImageView(image.view);
device->device.destroyFence(image.fence);
}
device->device.destroySwapchainKHR(swapChain);
}
protected:
vk::Format FindColorFormat() override
{
return surfaceFormat.format;
}
virtual vk::PresentModeKHR ChosePresentMode()
{
std::vector<vk::PresentModeKHR> presentModes = device->physicalDevice.getSurfacePresentModesKHR(surface);
#ifdef DEBUG
std::string availableModes = "";
for (const auto& presentMode : presentModes)
{
if (availableModes.length() > 0) availableModes += ", ";
availableModes += vk::to_string(presentMode);
}
Logger::RENDER->debug("Available swap chain present modes {0}. Searching best mode for: vsync={1}", availableModes, useVsync);
#endif
vk::PresentModeKHR mode = vk::PresentModeKHR::eFifo;
if (useVsync)
{
if (Utils::Contains(presentModes, vk::PresentModeKHR::eMailbox)) mode = vk::PresentModeKHR::eMailbox;
}
else
{
if (Utils::Contains(presentModes, vk::PresentModeKHR::eImmediate)) mode = vk::PresentModeKHR::eImmediate;
else if (Utils::Contains(presentModes, vk::PresentModeKHR::eFifoRelaxed)) mode = vk::PresentModeKHR::eFifoRelaxed;
}
Logger::RENDER->debug("Using swap chain present mode {0}", vk::to_string(mode));
return mode;
}
virtual vk::SurfaceFormatKHR ChoseSurfaceFormat()
{ //TODO allow to chose output format
std::vector<vk::SurfaceFormatKHR> surfaceFormats = device->physicalDevice.getSurfaceFormatsKHR(surface);
if(surfaceFormats.size() == 1 && surfaceFormats[0].format == vk::Format::eUndefined)
{ // GPU does not have a preferred surface format
return vk::SurfaceFormatKHR{ vk::Format::eB8G8R8A8Unorm, surfaceFormats[0].colorSpace };
}
else
{ //TODO chose best fitting
return surfaceFormats[0];
}
}
public:
std::vector<IImage*> GetImages() override
{
std::vector<IImage*> imgs;
for (auto& image : images)
{
imgs.push_back(&image);
}
return imgs;
}
};
}
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include <vulkan/vulkan.hpp>
namespace openVulkanoCpp
{
namespace Vulkan
{
class VulkanUtils
{
public:
static vk::AccessFlags GetAccessFlagsForLayout(vk::ImageLayout layout)
{
switch (layout)
{
case vk::ImageLayout::ePreinitialized: return vk::AccessFlagBits::eHostWrite;
case vk::ImageLayout::eTransferSrcOptimal: return vk::AccessFlagBits::eTransferRead;
case vk::ImageLayout::eTransferDstOptimal: return vk::AccessFlagBits::eTransferWrite;
case vk::ImageLayout::eShaderReadOnlyOptimal: return vk::AccessFlagBits::eShaderRead;
case vk::ImageLayout::eColorAttachmentOptimal: return vk::AccessFlagBits::eColorAttachmentWrite;
case vk::ImageLayout::eDepthStencilAttachmentOptimal: return vk::AccessFlagBits::eDepthStencilAttachmentWrite;
default: return vk::AccessFlags();
}
}
static vk::PipelineStageFlags GetPipelineStageForLayout(vk::ImageLayout layout)
{
switch (layout)
{
case vk::ImageLayout::ePreinitialized: return vk::PipelineStageFlagBits::eHost;
case vk::ImageLayout::eTransferDstOptimal:
case vk::ImageLayout::eTransferSrcOptimal: return vk::PipelineStageFlagBits::eTransfer;
case vk::ImageLayout::eShaderReadOnlyOptimal: return vk::PipelineStageFlagBits::eFragmentShader;
case vk::ImageLayout::eColorAttachmentOptimal: return vk::PipelineStageFlagBits::eColorAttachmentOutput;
case vk::ImageLayout::eDepthStencilAttachmentOptimal: return vk::PipelineStageFlagBits::eEarlyFragmentTests;
case vk::ImageLayout::eUndefined: return vk::PipelineStageFlagBits::eTopOfPipe;
default: return vk::PipelineStageFlagBits::eBottomOfPipe;
}
}
};
}
}

108
openVulkanoCpp/main.cpp Normal file
View File

@@ -0,0 +1,108 @@
#include "Host/GraphicsAppManager.hpp"
#include "Scene/Scene.hpp"
#include "Scene/Shader.hpp"
#include "Base/EngineConfiguration.hpp"
using namespace openVulkanoCpp::Scene;
uint32_t GEOS = 3000, OBJECTS = 10000, DYNAMIC = 1000;
class ExampleApp : public openVulkanoCpp::IGraphicsApp
{
Scene scene;
PerspectiveCamera cam;
Material mat;
Shader shader;
std::vector<Drawable*> drawablesPool;
std::vector<Node*> nodesPool;
public:
std::string GetAppName() override { return "ExampleApp"; }
std::string GetAppVersion() override { return "v1.0"; }
int GetAppVersionAsInt() override { return 1; }
void Init() override
{
std::srand(1);
scene.Init();
cam.Init(70, 16, 9, 0.1f, 100);
scene.SetCamera(&cam);
cam.SetMatrix(glm::translate(glm::mat4(1), glm::vec3(0,0,-10)));
shader.Init("Shader\\basic", "Shader\\basic");
drawablesPool.resize(GEOS);
for(int i = 0; i < GEOS; i++)
{
Geometry* geo = new Geometry();
geo->InitCube(std::rand() % 1000 / 1000.0f + 0.01f, std::rand() % 1000 / 1000.0f + 0.01f, std::rand() % 1000 / 1000.0f + 0.01f, glm::vec4((std::rand() % 255) / 255.0f, (std::rand() % 255) / 255.0f, (std::rand() % 255) / 255.0f, 1));
drawablesPool[i] = new Drawable();
drawablesPool[i]->Init(geo, &mat);
}
nodesPool.resize(OBJECTS);
for(int i = 0; i < OBJECTS; i++)
{
nodesPool[i] = new Node();
nodesPool[i]->Init();
scene.GetRoot()->AddChild(nodesPool[i]);
if (i < DYNAMIC) nodesPool[i]->SetUpdateFrequency(UpdateFrequency::Always);
nodesPool[i]->AddDrawable(drawablesPool[std::rand() % GEOS]);
nodesPool[i]->SetMatrix(glm::translate(glm::mat4x4(1), glm::vec3((std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5)));
}
scene.shader = &shader;
GetGraphicsAppManager()->GetRenderer()->SetScene(&scene);
}
void Tick() override
{
for(int i = 0; i < DYNAMIC; i++)
{
nodesPool[i]->SetMatrix(glm::translate(glm::mat4x4(1), glm::vec3((std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5)));
}
}
void Close() override{}
};
#include <iostream>
#include <sstream>
#include <string>
int main(int argc, char** argv)
{
std::cout << "Amount of Threads to use [2]: ";
int threads = 2;
std::string input;
std::getline(std::cin, input);
if (!input.empty())
{
std::istringstream stream(input);
stream >> threads;
}
std::cout << "Amount of geometries to produce [" << GEOS << "]: ";
std::getline(std::cin, input);
if (!input.empty())
{
std::istringstream stream(input);
stream >> GEOS;
}
std::cout << "Amount of objects to render [" << OBJECTS << "]: ";
std::getline(std::cin, input);
if (!input.empty())
{
std::istringstream stream(input);
stream >> OBJECTS;
}
std::cout << "Amount of moving objects [" << DYNAMIC << "]: ";
std::getline(std::cin, input);
if (!input.empty())
{
std::istringstream stream(input);
stream >> DYNAMIC;
}
DYNAMIC = std::min(DYNAMIC, OBJECTS);
openVulkanoCpp::EngineConfiguration::GetEngineConfiguration()->SetNumThreads(threads);
openVulkanoCpp::IGraphicsAppManager* manager = new openVulkanoCpp::GraphicsAppManager(new ExampleApp());
manager->Run();
return 0;
}

View File

@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{D546A70B-536A-487A-91E1-1CD4563A0104}</ProjectGuid>
<RootNamespace>openVulkanoCpp</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(VULKAN_SDK)\Include;$(SolutionDir)\external\spdlog\include;C:\Program Files\Assimp\include</AdditionalIncludeDirectories>
<ConformanceMode>true</ConformanceMode>
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);DEBUG</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>vulkan-1.lib;assimp-vc140-mt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(VULKAN_SDK)\Lib;C:\Program Files\Assimp\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
</Link>
<PostBuildEvent>
<Command>call "$(ProjectDir)Shader\CompileShaders.bat"
xcopy /y "$(ProjectDir)Shader\*.spv" "$(OutDir)\Shader\"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(VULKAN_SDK)\Include;$(SolutionDir)\external\spdlog\include;C:\Program Files\Assimp\include</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>vulkan-1.lib;assimp-vc140-mt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(VULKAN_SDK)\Lib;C:\Program Files\Assimp\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
</Link>
<PostBuildEvent>
<Command>call "$(ProjectDir)Shader\CompileShaders.bat"
xcopy /y "$(ProjectDir)Shader\*.spv" "$(OutDir)\Shader\"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Shader\basic.frag" />
<None Include="Shader\basic.frag.spv" />
<None Include="Shader\basic.vert" />
<None Include="Shader\basic.vert.spv" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Base\Logger.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="Scene\Drawable.cpp" />
<ClCompile Include="Scene\Node.cpp" />
<ClCompile Include="Vulkan\FrameBuffer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Base\EngineConstants.hpp" />
<ClInclude Include="Base\ICloseable.hpp" />
<ClInclude Include="Base\IGraphicsApp.hpp" />
<ClInclude Include="Base\IGraphicsAppManager.hpp" />
<ClInclude Include="Base\IInitable.hpp" />
<ClInclude Include="Base\Logger.hpp" />
<ClInclude Include="Base\PlatformEnums.hpp" />
<ClInclude Include="Base\ITickable.hpp" />
<ClInclude Include="Base\Timer.hpp" />
<ClInclude Include="Base\UI\BaseWindow.hpp" />
<ClInclude Include="Base\UI\IWindow.hpp" />
<ClInclude Include="Base\Render\IRenderer.hpp" />
<ClInclude Include="Base\Utils.hpp" />
<ClInclude Include="Data\ReadOnlyAtomicArrayQueue.hpp" />
<ClInclude Include="Base\EngineConfiguration.hpp" />
<ClInclude Include="Scene\AABB.hpp" />
<ClInclude Include="Scene\Drawable.hpp" />
<ClInclude Include="Scene\Material.hpp" />
<ClInclude Include="Scene\Geometry.hpp" />
<ClInclude Include="Scene\Scene.hpp" />
<ClInclude Include="Scene\Shader.hpp" />
<ClInclude Include="Scene\Vertex.hpp" />
<ClInclude Include="Host\GraphicsAppManager.hpp" />
<ClInclude Include="Host\PlatformProducer.hpp" />
<ClInclude Include="Host\WindowGLFW.hpp" />
<ClInclude Include="Vulkan\Buffer.hpp" />
<ClInclude Include="Vulkan\CommandHelper.hpp" />
<ClInclude Include="Vulkan\Context.hpp" />
<ClInclude Include="Vulkan\Debuging\ValidationLayer.hpp" />
<ClInclude Include="Vulkan\Device.hpp" />
<ClInclude Include="Vulkan\DeviceManager.hpp" />
<ClInclude Include="Vulkan\FrameBuffer.hpp" />
<ClInclude Include="Vulkan\Image.hpp" />
<ClInclude Include="Scene\Camera.hpp" />
<ClInclude Include="Scene\Node.hpp" />
<ClInclude Include="Vulkan\Pipeline.hpp" />
<ClInclude Include="Vulkan\Renderer.hpp" />
<ClInclude Include="Vulkan\RenderPass.hpp" />
<ClInclude Include="Vulkan\Resources\ManagedResource.hpp" />
<ClInclude Include="Vulkan\Resources\ResourceManager.hpp" />
<ClInclude Include="Vulkan\Resources\IShaderOwner.hpp" />
<ClInclude Include="Vulkan\Resources\UniformBuffer.hpp" />
<ClInclude Include="Vulkan\Scene\IRecordable.hpp" />
<ClInclude Include="Vulkan\Scene\VulkanGeometry.hpp" />
<ClInclude Include="Vulkan\Scene\VulkanNode.hpp" />
<ClInclude Include="Vulkan\Scene\VulkanShader.hpp" />
<ClInclude Include="Vulkan\SwapChain.hpp" />
<ClInclude Include="Vulkan\VulkanUtils.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\packages\glm.0.9.9.500\build\native\glm.targets" Condition="Exists('..\packages\glm.0.9.9.500\build\native\glm.targets')" />
<Import Project="..\packages\glfw.3.3.0.1\build\native\glfw.targets" Condition="Exists('..\packages\glfw.3.3.0.1\build\native\glfw.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\glm.0.9.9.500\build\native\glm.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\glm.0.9.9.500\build\native\glm.targets'))" />
<Error Condition="!Exists('..\packages\glfw.3.3.0.1\build\native\glfw.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\glfw.3.3.0.1\build\native\glfw.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="glfw" version="3.3.0.1" targetFramework="native" />
<package id="glm" version="0.9.9.500" targetFramework="native" />
</packages>