diff --git a/.clang-format b/.clang-format
index c94a5ff6c..68053b603 100644
--- a/.clang-format
+++ b/.clang-format
@@ -3,8 +3,10 @@ BasedOnStyle: LLVM
AccessModifierOffset: -4
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
+BinPackParameters: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
+AllowAllParametersOfDeclarationOnNextLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakTemplateDeclarations: Yes
ColumnLimit: 120
diff --git a/documentation/source/development/debugging/cla.rst b/documentation/source/development/debugging/cla.rst
index 049654741..773fdf86d 100644
--- a/documentation/source/development/debugging/cla.rst
+++ b/documentation/source/development/debugging/cla.rst
@@ -25,14 +25,6 @@ You can start vulkan-renderer with the following command line arguments:
.. warning:: You should never disable validation layers because they offer extensive error checks for debugging.
-.. option:: --no-vk-debug-markers
-
- Disables `Vulkan debug markers `__ (even if ``--renderdoc`` is specified).
-
-.. option:: --renderdoc
-
- Enables the `RenderDoc `__ debug layer.
-
.. option:: --vsync
.. warning:: Vsync is currently not implemented. The command line argument will be ignored.
diff --git a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_1.jpg b/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_1.jpg
deleted file mode 100644
index 70f77d051..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_1.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_2.jpg b/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_2.jpg
deleted file mode 100644
index c1d63044e..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_2.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_3.jpg b/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_3.jpg
deleted file mode 100644
index 99c4f28ba..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_3.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_4.jpg b/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_4.jpg
deleted file mode 100644
index 4848e56ca..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_4.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_5.jpg b/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_5.jpg
deleted file mode 100644
index 1198b583d..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_5.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_6.jpg b/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_6.jpg
deleted file mode 100644
index 2184f8685..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_6.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_7.jpg b/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_7.jpg
deleted file mode 100644
index bc182957e..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_7.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_8.jpg b/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_8.jpg
deleted file mode 100644
index 611dde4c9..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_8.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_9.jpg b/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_9.jpg
deleted file mode 100644
index 0783b1b7b..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/RenderDoc_step_9.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/VisualStudioBreakpoint.jpg b/documentation/source/development/debugging/images/renderdoc/VisualStudioBreakpoint.jpg
deleted file mode 100644
index 16a56f19f..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/VisualStudioBreakpoint.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/images/renderdoc/VisualStudioDebugging.jpg b/documentation/source/development/debugging/images/renderdoc/VisualStudioDebugging.jpg
deleted file mode 100644
index a4f83a4a7..000000000
Binary files a/documentation/source/development/debugging/images/renderdoc/VisualStudioDebugging.jpg and /dev/null differ
diff --git a/documentation/source/development/debugging/renderdoc.rst b/documentation/source/development/debugging/renderdoc.rst
index 7d8835854..0fbaf0fd5 100644
--- a/documentation/source/development/debugging/renderdoc.rst
+++ b/documentation/source/development/debugging/renderdoc.rst
@@ -2,99 +2,5 @@ RenderDoc
=========
- `RenderDoc `__ is a free and open source graphics debugger for Vulkan API (and other APIs) developed by `Baldur Karlsson `__.
-- It is a very powerful graphics debugging and visualization tool which makes debugging Vulkan application as easy as possible.
-- Inexor has full RenderDoc integration. This includes `internal resource naming using Vulkan debug markers `__.
-- The following tutorial shows how to debug Inexor using RenderDoc.
+- It is a very powerful graphics debugging and visualization tool which simplifies debugging Vulkan application a lot.
- You can read up more details in `RenderDoc's documentation `__.
-
-RenderDoc Tutorial for Windows
-------------------------------
-
-Step 1: Open Inexor in Visual Studio and add a breakpoint before Vulkan initialization
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- The best spot would be right after ``main()``:
-
-.. image:: /development/debugging/images/renderdoc/VisualStudioBreakpoint.jpg
- :width: 800
- :alt: A breakpoint after the main function in Visual Studio debugger.
-
-Step 2: Open RenderDoc.
-^^^^^^^^^^^^^^^^^^^^^^^
-
-.. image:: /development/debugging/images/renderdoc/RenderDoc_step_1.jpg
- :width: 800
- :alt: RenderDoc right after starting it.
-
-Step 3: Start debugging inexor-vulkan-renderer and halt at the breakpoint
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. image:: /development/debugging/images/renderdoc/VisualStudioDebugging.jpg
- :width: 800
- :alt: Visual Studio interrupts the program because of a breakpoint.
-
-Step 4: "Inject into process" inexor-vulkan-renderer.exe using RenderDoc
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. image:: /development/debugging/images/renderdoc/RenderDoc_step_2.jpg
- :width: 800
- :alt: "Inject into process" in RenderDoc's menu.
-
-Step 5: Search for "inexor-vulkan-renderer.exe" and click "inject"
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- You will see a warning Windows Firewall the first time you do this.
-- This is because RenderDoc is reading memory from inexor-vulkan-renderer.
-- Accept the Windows Firewall warning to allow RenderDoc to read memory.
-
-.. image:: /development/debugging/images/renderdoc/RenderDoc_step_3.jpg
- :width: 800
- :alt: Injecting into inexor-vulkan-renderer.
-
-Step 6: Continue debugging in Visual Studio
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- RenderDoc should now look like this.
-
-.. image:: /development/debugging/images/renderdoc/RenderDoc_step_4.jpg
- :width: 800
- :alt: Injecting into inexor-vulkan-renderer.
-
-- Press ``F5`` to continue program execution from the breakpoint.
-- RenderDoc is now connected to inexor-vulkan-renderer:
-
-.. image:: /development/debugging/images/renderdoc/RenderDoc_step_5.jpg
- :width: 800
- :alt: RenderDoc is connected inexor-vulkan-renderer.
-
-- You can see RenderDoc's overlay in inexor-vulkan-renderer.exe:
-
-.. image:: /development/debugging/images/renderdoc/RenderDoc_step_6.jpg
- :width: 800
- :alt: Taking a RenderDoc snapshot.
-
-Step 7: Debug inexor-vulkan-renderer.exe as usual and press F12 to take RenderDoc snapshots
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- You can take multiple snapshots with either ``PRINT`` or ``F12`` key.
-
-.. image:: /development/debugging/images/renderdoc/RenderDoc_step_7.jpg
- :width: 800
- :alt: Taking a RenderDoc snapshot.
-
-- You can see the snapshots in RenderDoc right after you took them:
-
-.. image:: /development/debugging/images/renderdoc/RenderDoc_step_8.jpg
- :width: 800
- :alt: Taking a RenderDoc snapshot.
-
-Step 8: Open a snapshot to analyze the rendering of this frame
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- Double click on a snapshot to open it:
-
-.. image:: /development/debugging/images/renderdoc/RenderDoc_step_9.jpg
- :width: 800
- :alt: Taking a RenderDoc snapshot.
-
-- Have fun inspecting!
diff --git a/documentation/source/development/getting-started.rst b/documentation/source/development/getting-started.rst
index 09c9effbc..9a5754685 100644
--- a/documentation/source/development/getting-started.rst
+++ b/documentation/source/development/getting-started.rst
@@ -37,7 +37,7 @@ Optional Software
Improve your build times with ninja.
`RenderDoc `__
- Powerful open source graphics debugger. Inexor has full RenderDoc integration.
+ A very powerful open source graphics debugger.
`Doxygen `__
Required for generating the documentation.
diff --git a/example-app/include/base/example_app_base.hpp b/example-app/include/base/example_app_base.hpp
new file mode 100644
index 000000000..e69de29bb
diff --git a/example-app/include/example_app.hpp b/example-app/include/example_app.hpp
new file mode 100644
index 000000000..e69de29bb
diff --git a/example-app/src/base/example_app_base.cpp b/example-app/src/base/example_app_base.cpp
new file mode 100644
index 000000000..e69de29bb
diff --git a/example-app/src/example_app.cpp b/example-app/src/example_app.cpp
new file mode 100644
index 000000000..e69de29bb
diff --git a/example/main.cpp b/example/main.cpp
index f85737397..e167c9b1a 100644
--- a/example/main.cpp
+++ b/example/main.cpp
@@ -1,33 +1,12 @@
#include "inexor/vulkan-renderer/application.hpp"
-#include
-#include
-#include
-#include
-
#include
int main(int argc, char *argv[]) {
- spdlog::init_thread_pool(8192, 2);
-
- auto console_sink = std::make_shared();
- auto file_sink = std::make_shared("vulkan-renderer.log", true);
- auto vulkan_renderer_log =
- std::make_shared("vulkan-renderer", spdlog::sinks_init_list{console_sink, file_sink},
- spdlog::thread_pool(), spdlog::async_overflow_policy::block);
- vulkan_renderer_log->set_level(spdlog::level::trace);
- vulkan_renderer_log->set_pattern("%Y-%m-%d %T.%f %^%l%$ %5t [%-10n] %v");
- vulkan_renderer_log->flush_on(spdlog::level::debug); // TODO: as long as we don't have a flush on crash
-
- spdlog::set_default_logger(vulkan_renderer_log);
-
- spdlog::trace("Inexor vulkan-renderer, BUILD " + std::string(__DATE__) + ", " + __TIME__);
- spdlog::trace("Parsing command line arguments");
-
- std::unique_ptr renderer;
-
try {
- renderer = std::make_unique(argc, argv);
+ using inexor::vulkan_renderer::Application;
+ std::unique_ptr renderer = std::make_unique(argc, argv);
+ renderer->run();
} catch (const std::runtime_error &exception) {
spdlog::critical(exception.what());
return 1;
@@ -35,7 +14,5 @@ int main(int argc, char *argv[]) {
spdlog::critical(exception.what());
return 1;
}
-
- renderer->run();
- spdlog::trace("Window closed");
+ return 0;
}
diff --git a/include/inexor/vulkan-renderer/application.hpp b/include/inexor/vulkan-renderer/application.hpp
index 9542318e6..be7d4eb0d 100644
--- a/include/inexor/vulkan-renderer/application.hpp
+++ b/include/inexor/vulkan-renderer/application.hpp
@@ -1,9 +1,18 @@
#pragma once
+#include "inexor/vulkan-renderer/camera.hpp"
+#include "inexor/vulkan-renderer/fps_counter.hpp"
#include "inexor/vulkan-renderer/input/keyboard_mouse_data.hpp"
-#include "inexor/vulkan-renderer/renderer.hpp"
+#include "inexor/vulkan-renderer/octree_gpu_vertex.hpp"
+#include "inexor/vulkan-renderer/render-graph/render_graph.hpp"
+#include "inexor/vulkan-renderer/renderers/imgui.hpp"
+#include "inexor/vulkan-renderer/time_step.hpp"
#include "inexor/vulkan-renderer/world/collision_query.hpp"
#include "inexor/vulkan-renderer/world/cube.hpp"
+#include "inexor/vulkan-renderer/wrapper/instance.hpp"
+#include "inexor/vulkan-renderer/wrapper/surface.hpp"
+#include "inexor/vulkan-renderer/wrapper/swapchain.hpp"
+#include "inexor/vulkan-renderer/wrapper/window.hpp"
// Forward declarations
namespace inexor::vulkan_renderer::input {
@@ -12,16 +21,73 @@ class KeyboardMouseInputData;
namespace inexor::vulkan_renderer {
-class Application : public VulkanRenderer {
- std::vector m_vertex_shader_files;
- std::vector m_fragment_shader_files;
- std::vector m_texture_files;
- std::vector m_gltf_model_files;
+// Using declarations
+using input::KeyboardMouseInputData;
+using wrapper::Device;
+using wrapper::Instance;
+using wrapper::Surface;
+using wrapper::Swapchain;
+using wrapper::Window;
+
+class Application {
+private:
+ TimeStep m_stopwatch;
+ FPSCounter m_fps_counter;
+ bool m_vsync_enabled{false};
+
+ PFN_vkDebugUtilsMessengerCallbackEXT m_debug_callbacks{VK_NULL_HANDLE};
+
+ bool m_debug_report_callback_initialised{false};
+
+ std::unique_ptr m_camera;
+ std::unique_ptr m_window;
+ std::unique_ptr m_instance;
+ std::unique_ptr m_device;
+ std::shared_ptr m_swapchain;
+ std::shared_ptr m_surface;
+ std::unique_ptr m_imgui_overlay;
+
+ std::vector m_octree_vertices;
+ std::vector m_octree_indices;
+
+ std::shared_ptr m_render_graph;
+ std::weak_ptr m_color_attachment;
+ std::weak_ptr m_depth_attachment;
+ std::weak_ptr m_index_buffer;
+ std::weak_ptr m_vertex_buffer;
+ std::weak_ptr m_uniform_buffer;
+ std::shared_ptr m_octree_vert;
+ std::shared_ptr m_octree_frag;
+
+ VkDescriptorSetLayout m_descriptor_set_layout{VK_NULL_HANDLE};
+ VkDescriptorSet m_descriptor_set{VK_NULL_HANDLE};
+
+ std::shared_ptr m_octree_pipeline;
+ std::weak_ptr m_octree_pass;
+ struct ModelViewPerspectiveMatrices {
+ glm::mat4 model{1.0f};
+ glm::mat4 view{1.0f};
+ glm::mat4 proj{1.0f};
+ } m_mvp_matrices;
+
+ void setup_render_graph();
+ void generate_octree_indices();
+ void recreate_swapchain();
+ void render_frame();
+
+ float m_time_passed{0.0f};
+
+ std::uint32_t m_wnd_width{0};
+ std::uint32_t m_wnd_height{0};
+ std::string m_wnd_title;
+ bool m_wnd_resized{false};
+ wrapper::Window::Mode m_wnd_mode{wrapper::Window::Mode::WINDOWED};
+
+ std::vector m_gltf_model_files;
std::unique_ptr m_input_data;
bool m_enable_validation_layers{true};
- /// Inexor engine supports a variable number of octrees.
std::vector> m_worlds;
// If the user specified command line argument "--stop-on-validation-message", the program will call
@@ -32,20 +98,32 @@ class Application : public VulkanRenderer {
/// @brief file_name The TOML configuration file.
/// @note It was collectively decided not to use JSON for configuration files.
void load_toml_configuration_file(const std::string &file_name);
- void load_textures();
- void load_shaders();
+
+ void check_octree_collisions();
+ void initialize_spdlog();
+
/// @param initialize Initialize worlds with a fixed seed, which is useful for benchmarking and testing
void load_octree_geometry(bool initialize);
- void setup_vulkan_debug_callback();
+
+ void process_keyboard_input();
+ void process_mouse_input();
void setup_window_and_input_callbacks();
void update_imgui_overlay();
- void update_uniform_buffers();
- /// Use the camera's position and view direction vector to check for ray-octree collisions with all octrees.
- void check_octree_collisions();
- void process_mouse_input();
public:
Application(int argc, char **argv);
+ Application(const Application &) = delete;
+ Application(Application &&) = delete;
+ ~Application();
+
+ Application &operator=(const Application &) = delete;
+ Application &operator=(Application &&) = delete;
+
+ /// @brief Call glfwSetCursorPosCallback.
+ /// @param window The window that received the event.
+ /// @param x_pos The new x-coordinate, in screen coordinates, of the cursor.
+ /// @param y_pos The new y-coordinate, in screen coordinates, of the cursor.
+ void cursor_position_callback(GLFWwindow *window, double x_pos, double y_pos);
/// @brief Call glfwSetKeyCallback.
/// @param window The window that received the event.
@@ -55,12 +133,6 @@ class Application : public VulkanRenderer {
/// @param mods Bit field describing which modifier keys were held down.
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods);
- /// @brief Call glfwSetCursorPosCallback.
- /// @param window The window that received the event.
- /// @param x_pos The new x-coordinate, in screen coordinates, of the cursor.
- /// @param y_pos The new y-coordinate, in screen coordinates, of the cursor.
- void cursor_position_callback(GLFWwindow *window, double x_pos, double y_pos);
-
/// @brief Call glfwSetMouseButtonCallback.
/// @param window The window that received the event.
/// @param button The mouse button that was pressed or released.
diff --git a/include/inexor/vulkan-renderer/exception.hpp b/include/inexor/vulkan-renderer/exception.hpp
index b92d917e9..d43361843 100644
--- a/include/inexor/vulkan-renderer/exception.hpp
+++ b/include/inexor/vulkan-renderer/exception.hpp
@@ -7,16 +7,17 @@
namespace inexor::vulkan_renderer {
-/// @brief A custom base class for exceptions
+/// A custom base class for exceptions
class InexorException : public std::runtime_error {
public:
// No need to define own constructors.
using std::runtime_error::runtime_error;
};
-/// @brief InexorException for Vulkan specific things.
+/// InexorException for Vulkan specific things.
class VulkanException final : public InexorException {
public:
+ /// Default constructor
/// @param message The exception message.
/// @param result The VkResult value of the Vulkan API call which failed.
VulkanException(std::string message, VkResult result);
diff --git a/include/inexor/vulkan-renderer/imgui.hpp b/include/inexor/vulkan-renderer/imgui.hpp
deleted file mode 100644
index 0ec531814..000000000
--- a/include/inexor/vulkan-renderer/imgui.hpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#pragma once
-
-#include "inexor/vulkan-renderer/render_graph.hpp"
-#include "inexor/vulkan-renderer/wrapper/descriptor.hpp"
-#include "inexor/vulkan-renderer/wrapper/gpu_texture.hpp"
-#include "inexor/vulkan-renderer/wrapper/shader.hpp"
-
-#include
-#include
-#include
-
-#include
-#include
-
-// Forward declarations
-namespace inexor::vulkan_renderer::wrapper {
-class Device;
-class Swapchain;
-} // namespace inexor::vulkan_renderer::wrapper
-
-namespace inexor::vulkan_renderer {
-
-class ImGUIOverlay {
- const wrapper::Device &m_device;
- const wrapper::Swapchain &m_swapchain;
- float m_scale{1.0f};
-
- BufferResource *m_index_buffer{nullptr};
- BufferResource *m_vertex_buffer{nullptr};
- GraphicsStage *m_stage{nullptr};
-
- std::unique_ptr m_imgui_texture;
- std::unique_ptr m_vertex_shader;
- std::unique_ptr m_fragment_shader;
- std::unique_ptr m_descriptor;
- std::vector m_index_data;
- std::vector m_vertex_data;
-
- struct PushConstBlock {
- glm::vec2 scale;
- glm::vec2 translate;
- } m_push_const_block{};
-
-public:
- /// @brief Construct a new ImGUI overlay.
- /// @param device A reference to the device wrapper
- /// @param swapchain A reference to the swapchain
- /// @param render_graph A pointer to the render graph
- /// @param back_buffer A pointer to the target of the ImGUI rendering
- ImGUIOverlay(const wrapper::Device &device, const wrapper::Swapchain &swapchain, RenderGraph *render_graph,
- TextureResource *back_buffer);
- ImGUIOverlay(const ImGUIOverlay &) = delete;
- ImGUIOverlay(ImGUIOverlay &&) = delete;
- ~ImGUIOverlay();
-
- ImGUIOverlay &operator=(const ImGUIOverlay &) = delete;
- ImGUIOverlay &operator=(ImGUIOverlay &&) = delete;
-
- void update();
-
- [[nodiscard]] float scale() const {
- return m_scale;
- }
-};
-
-} // namespace inexor::vulkan_renderer
diff --git a/include/inexor/vulkan-renderer/io/exception.hpp b/include/inexor/vulkan-renderer/io/io_exception.hpp
similarity index 100%
rename from include/inexor/vulkan-renderer/io/exception.hpp
rename to include/inexor/vulkan-renderer/io/io_exception.hpp
diff --git a/include/inexor/vulkan-renderer/msaa_target.hpp b/include/inexor/vulkan-renderer/msaa_target.hpp
deleted file mode 100644
index d6884b43f..000000000
--- a/include/inexor/vulkan-renderer/msaa_target.hpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include "inexor/vulkan-renderer/wrapper/image.hpp"
-
-#include
-
-namespace inexor::vulkan_renderer {
-
-struct MSAATarget {
- // The color buffer.
- std::unique_ptr m_color;
-
- // The depth buffer.
- std::unique_ptr m_depth;
-};
-
-} // namespace inexor::vulkan_renderer
diff --git a/include/inexor/vulkan-renderer/render-graph/buffer.hpp b/include/inexor/vulkan-renderer/render-graph/buffer.hpp
new file mode 100644
index 000000000..677344e06
--- /dev/null
+++ b/include/inexor/vulkan-renderer/render-graph/buffer.hpp
@@ -0,0 +1,159 @@
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace inexor::vulkan_renderer::wrapper {
+/// Forward declaration
+class Device;
+} // namespace inexor::vulkan_renderer::wrapper
+
+namespace inexor::vulkan_renderer::wrapper::commands {
+/// Forward declaration
+class CommandBuffer;
+} // namespace inexor::vulkan_renderer::wrapper::commands
+
+namespace inexor::vulkan_renderer::wrapper::descriptors {
+/// Forward declaration
+class WriteDescriptorSetBuilder;
+} // namespace inexor::vulkan_renderer::wrapper::descriptors
+
+namespace inexor::vulkan_renderer::render_graph {
+
+/// The buffer type describes the internal usage of the buffer resource inside of the rendergraph
+enum class BufferType {
+ VERTEX_BUFFER,
+ INDEX_BUFFER,
+ UNIFORM_BUFFER,
+};
+
+// Forward declaration
+class GraphicsPass;
+
+// Using declarations
+using wrapper::Device;
+using wrapper::commands::CommandBuffer;
+using wrapper::descriptors::WriteDescriptorSetBuilder;
+
+// TODO: Store const reference to rendergraph and retrieve the swapchain image index for automatic buffer tripling
+
+/// RAII wrapper for buffer resources inside of the rendergraph
+/// A buffer resource can be a vertex buffer, index buffer, or uniform buffer
+class Buffer {
+ //
+ friend class RenderGraph;
+ friend class GraphicsPass;
+ friend class CommandBuffer;
+ friend class WriteDescriptorSetBuilder;
+
+private:
+ /// The device wrapper
+ const Device &m_device;
+ /// The internal debug name of the buffer resource
+ std::string m_name;
+
+ /// The buffer type will be set depending on which constructor of the Buffer wrapper is called by rendergraph. The
+ /// engine currently supports three different types of buffers in the Buffer wrapper class: vertex buffers, index
+ /// buffers, and uniform buffers. The instances of the Buffer wrapper class are managed by rendergraph only. One
+ /// solution to deal with the different buffer types would be to use a BufferBase class and to make three distinct
+ /// classes VertexBuffer, IndexBuffer, and UniformBuffer. However, we aimed for simplicity and wanted to avoid
+ /// polymorphism in the rendergraph for performance reasons. We also refrained from using templates for this use
+ /// case. Therefore, we have chosen to use only one Buffer wrapper class which contains members for all three
+ /// different buffer types. The type of the buffer will be set depending on which Buffer constructor is called by
+ /// rendergraph. The actual memory management for the buffers is done by Vulkan Memory Allocator (VMA) internally.
+ BufferType m_buffer_type;
+
+ /// The buffer update function which is called by rendergraph to update the buffer's data. This update function is
+ /// called, no matter what the type of the buffer is. With the currently supported buffer types (vertex-, index-,
+ /// and uniform buffers) there is always a discussion about whether some update lambdas can be made std::optional.
+ /// For example we could have one vertex buffer with an index buffer and the index buffer is updated together with
+ /// the vertex buffer in the update function of the vertex buffer. From the design of the engine there is no
+ /// limitation which buffer is updated in which update function, as long as the handle to that buffer has been
+ /// created in rendergraph. In our example, the update function of the index buffer could be std::nullopt. In this
+ /// case, rendergraph could separate all buffers into those which require an update and those who do not. For
+ /// simplicity however, we made the update function not std::optional.
+
+ // TODO: Rewrite description
+ std::function m_on_check_for_update;
+
+ /// TODO: Is this is relevant for uniform buffers only?
+ /// TODO: Maybe buffer updates should be done immediately, and no m_src_data should be stored!
+ /// It's the responsibility of the programmer to make sure the data m_src_data points to is still valid when
+ /// update_buffer() is called!
+ void *m_src_data{nullptr};
+ std::size_t m_src_data_size{0};
+ bool m_update_requested{false};
+
+ /// The resources for actual memory management of the buffer
+ VkBuffer m_buffer{VK_NULL_HANDLE};
+ VmaAllocation m_alloc{VK_NULL_HANDLE};
+ VmaAllocationInfo m_alloc_info{};
+
+ /// The descriptor buffer info (required for uniform buffers)
+ VkDescriptorBufferInfo m_descriptor_buffer_info{};
+
+ /// The staging buffer (if required)
+ VkBuffer m_staging_buffer{VK_NULL_HANDLE};
+ VmaAllocation m_staging_buffer_alloc{VK_NULL_HANDLE};
+ VmaAllocationInfo m_staging_buffer_alloc_info{};
+
+ /// Create the buffer using Vulkan Memory Allocator (VMA) library
+ /// @param cmd_buf The command buffer
+ void create(const CommandBuffer &cmd_buf);
+
+ /// Call destroy_buffer and destroy_staging_buffer
+ void destroy_all();
+
+ /// Call vmaDestroyBuffer for the actual buffer
+ void destroy_buffer();
+
+ /// Call vmaDestroyBuffer for the staging bufffer
+ void destroy_staging_buffer();
+
+public:
+ /// Default constructor
+ /// @param device The device wrapper
+ /// @param buffer_name The name of the buffer
+ /// @param buffer_type The type of the buffer
+ /// @param on_update The buffer update function
+ Buffer(const Device &device, std::string buffer_name, BufferType buffer_type, std::function on_update);
+
+ Buffer(const Buffer &) = delete;
+ Buffer(Buffer &&other) noexcept;
+
+ /// Call destroy_buffer
+ ~Buffer();
+
+ Buffer &operator=(const Buffer &) = delete;
+ Buffer &operator=(Buffer &&) = delete;
+
+ /// Request a buffer update
+ /// @param src_data A pointer to the data to copy the updated data from
+ /// @warning It is the responsibility of the programmer to make sure src_data still points to valid memory when
+ /// update_buffer() is called!
+ /// @param src_data_size The size of the data to copy
+ void request_update(void *src_data, std::size_t src_data_size);
+
+ /// Request a buffer update
+ /// @tparam BufferDataType
+ /// @param data
+ template
+ void request_update(BufferDataType &data) {
+ return request_update(std::addressof(data), sizeof(data));
+ }
+
+ /// Request a buffer update
+ /// @tparam BufferDataType
+ /// @param data
+ template
+ void request_update(std::vector &data) {
+ return request_update(data.data(), sizeof(BufferDataType) * data.size());
+ }
+};
+
+} // namespace inexor::vulkan_renderer::render_graph
diff --git a/include/inexor/vulkan-renderer/render-graph/graphics_pass.hpp b/include/inexor/vulkan-renderer/render-graph/graphics_pass.hpp
new file mode 100644
index 000000000..5aa64bbe7
--- /dev/null
+++ b/include/inexor/vulkan-renderer/render-graph/graphics_pass.hpp
@@ -0,0 +1,108 @@
+#pragma once
+
+#include
+
+#include "inexor/vulkan-renderer/render-graph/buffer.hpp"
+#include "inexor/vulkan-renderer/render-graph/texture.hpp"
+#include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_layout.hpp"
+#include "inexor/vulkan-renderer/wrapper/device.hpp"
+#include "inexor/vulkan-renderer/wrapper/swapchain.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace inexor::vulkan_renderer::wrapper::commands {
+// Forward declaration
+class CommandBuffer;
+} // namespace inexor::vulkan_renderer::wrapper::commands
+
+namespace inexor::vulkan_renderer::render_graph {
+
+// Forward declaration
+class RenderGraph;
+
+// Using declarations
+using wrapper::Swapchain;
+using wrapper::descriptors::DescriptorSetLayout;
+
+/// Using declaration
+using OnRecordCommandBufferForPass = std::function;
+
+/// A wrapper for graphics passes inside of rendergraph
+class GraphicsPass {
+ friend class RenderGraph;
+
+private:
+ /// The name of the graphics pass
+ std::string m_name;
+
+ /// The command buffer recording function of the graphics pass
+ OnRecordCommandBufferForPass m_on_record_cmd_buffer{[](auto &) {}};
+
+ /// The descriptor set layout of the pass (this will be created by rendergraph)
+ std::unique_ptr m_descriptor_set_layout;
+ /// The descriptor set of the pass (this will be created by rendergraph)
+ VkDescriptorSet m_descriptor_set{VK_NULL_HANDLE};
+
+ /// The color of the debug label region (visible in graphics debuggers like RenderDoc)
+ std::array m_debug_label_color;
+
+ /// The extent
+ VkExtent2D m_extent{0, 0};
+ /// The graphics passes this pass reads from
+ std::vector> m_graphics_pass_reads;
+ /// A weak pointer to the next graphics pass
+ std::weak_ptr m_next_pass{};
+
+ /// The texture attachments of this pass (unified means color, depth, stencil attachment or a swapchain)
+ std::vector, std::optional>> m_write_attachments{};
+ /// The swapchains this graphics pass writes to
+ std::vector, std::optional>> m_write_swapchains{};
+
+ // All the data below will be filled and used by rendergraph only
+
+ /// The rendering info will be filled during rendergraph compilation so we don't have to do this while rendering.
+ /// This means we must make sure that the memory of the attachment infos below is still valid during rendering,
+ /// which is why we store them as members here.
+ VkRenderingInfo m_rendering_info{};
+ /// The color attachments inside of m_rendering_info
+ std::vector m_color_attachments{};
+ /// Does this graphics pass have any depth attachment?
+ bool m_has_depth_attachment{false};
+ /// The depth attachment inside of m_rendering_info
+ VkRenderingAttachmentInfo m_depth_attachment{};
+ /// Does this graphics pass have any stencil attachment?
+ bool m_has_stencil_attachment{false};
+ /// The stencil attachment inside of m_rendering_info
+ VkRenderingAttachmentInfo m_stencil_attachment{};
+
+ /// Reset the rendering info
+ void reset_rendering_info();
+
+public:
+ /// Default constructor
+ /// @param name The name of the graphics pass
+ /// @param on_record_cmd_buffer The command buffer recording function of the graphics pass
+ /// @param graphics_pass_reads The graphics passes this graphics pass reads from
+ /// @param write_attachments The attachment this graphics pass writes to
+ /// @param write_swapchains The swapchains this graphics pass writes to
+ /// @param pass_debug_label_color The debug label of the pass (visible in graphics debuggers like RenderDoc)
+ GraphicsPass(std::string name,
+ OnRecordCommandBufferForPass on_record_cmd_buffer,
+ std::vector> graphics_pass_reads,
+ std::vector, std::optional>> write_attachments,
+ std::vector, std::optional>> write_swapchains,
+ wrapper::DebugLabelColor pass_debug_label_color);
+
+ GraphicsPass(const GraphicsPass &) = delete;
+ GraphicsPass(GraphicsPass &&other) noexcept;
+ ~GraphicsPass() = default;
+
+ GraphicsPass &operator=(const GraphicsPass &) = delete;
+ GraphicsPass &operator=(GraphicsPass &&) = delete;
+};
+
+} // namespace inexor::vulkan_renderer::render_graph
diff --git a/include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp b/include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp
new file mode 100644
index 000000000..dced82bb0
--- /dev/null
+++ b/include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp
@@ -0,0 +1,84 @@
+#pragma once
+
+#include "inexor/vulkan-renderer/render-graph/buffer.hpp"
+#include "inexor/vulkan-renderer/render-graph/graphics_pass.hpp"
+#include "inexor/vulkan-renderer/render-graph/texture.hpp"
+#include "inexor/vulkan-renderer/wrapper/make_info.hpp"
+#include "inexor/vulkan-renderer/wrapper/swapchain.hpp"
+
+#include
+#include
+#include
+#include
+
+namespace inexor::vulkan_renderer::wrapper::commands {
+// Forward declaration
+class CommandBuffer;
+} // namespace inexor::vulkan_renderer::wrapper::commands
+
+namespace inexor::vulkan_renderer::render_graph {
+
+// Using declaration
+using wrapper::DebugLabelColor;
+using wrapper::Swapchain;
+using wrapper::commands::CommandBuffer;
+
+/// A builder class for graphics passes in the rendergraph
+class GraphicsPassBuilder {
+private:
+ /// Add members which describe data related to graphics passes here
+ OnRecordCommandBufferForPass m_on_record_cmd_buffer{};
+ /// The graphics passes which are read by this graphics pass
+ std::vector> m_graphics_pass_reads{};
+ /// The texture resources this graphics pass writes to
+ std::vector, std::optional>> m_write_attachments{};
+ /// The swapchain this graphics pass writes to
+ std::vector, std::optional>> m_write_swapchains{};
+
+ /// Reset the data of the graphics pass builder
+ void reset();
+
+public:
+ GraphicsPassBuilder();
+ GraphicsPassBuilder(const GraphicsPassBuilder &) = delete;
+ GraphicsPassBuilder(GraphicsPassBuilder &&) noexcept;
+ ~GraphicsPassBuilder() = default;
+
+ GraphicsPassBuilder &operator=(const GraphicsPassBuilder &) = delete;
+ GraphicsPassBuilder &operator=(GraphicsPassBuilder &&) = delete;
+
+ /// Build the graphics pass
+ /// @param name The name of the graphics pass
+ /// @param color The debug label color (debug labels are specified per pass and are visible in RenderDoc debugger)
+ /// @return The graphics pass that was just created
+ [[nodiscard]] std::shared_ptr build(std::string name, DebugLabelColor color);
+
+ /// Specify that this graphics pass A reads from another graphics pass B (if the weak_ptr to B is not expired),
+ /// meaning B should be rendered before A. It is perfect valid for 'graphics_pass' to be an invalid pointer, in
+ /// which case the read is not added.
+ /// @param condition The condition under which the pass is read from
+ /// @param graphics_pass The graphics pass (can be an invalid pointer)
+ /// @return A const reference to the this pointer (allowing method calls to be chained)
+ [[nodiscard]] GraphicsPassBuilder &conditionally_reads_from(std::weak_ptr graphics_pass,
+ bool condition);
+
+ /// Specify that this graphics pass A reads from another graphics pass B, meaning B should be rendered before A
+ /// @param graphics_pass The graphics pass which is read by this graphics pass
+ /// @return A const reference to the this pointer (allowing method calls to be chained)
+ [[nodiscard]] GraphicsPassBuilder &reads_from(std::weak_ptr graphics_pass);
+
+ /// Set the function which will be called when the command buffer for rendering of the pass is being recorded
+ /// @param on_record_cmd_buffer The command buffer recording function
+ /// @return A const reference to the this pointer (allowing method calls to be chained)
+ [[nodiscard]] GraphicsPassBuilder &set_on_record(OnRecordCommandBufferForPass on_record_cmd_buffer);
+
+ /// Specify that this graphics pass writes to an either a std::weak_ptr or a std::weak_ptr
+ /// @param attachment The attachment (either a std::weak_ptr or a std::weak_ptr)
+ /// @param clear_value The optional clear value of the attachment (``std::nullopt`` by default)
+ /// @return A const reference to the this pointer (allowing method calls to be chained)
+ [[nodiscard]] GraphicsPassBuilder &
+ writes_to(std::variant, std::weak_ptr> write_attachment,
+ std::optional clear_value = std::nullopt);
+};
+
+} // namespace inexor::vulkan_renderer::render_graph
diff --git a/include/inexor/vulkan-renderer/render-graph/image.hpp b/include/inexor/vulkan-renderer/render-graph/image.hpp
new file mode 100644
index 000000000..3126a5adb
--- /dev/null
+++ b/include/inexor/vulkan-renderer/render-graph/image.hpp
@@ -0,0 +1,89 @@
+#pragma once
+
+#include
+
+#include
+#include
+
+namespace inexor::vulkan_renderer::wrapper {
+// Forward declaration
+class Device;
+class Sampler;
+} // namespace inexor::vulkan_renderer::wrapper
+
+namespace inexor::vulkan_renderer::wrapper::commands {
+// Forward declaration
+class CommandBuffer;
+} // namespace inexor::vulkan_renderer::wrapper::commands
+
+namespace inexor::vulkan_renderer::render_graph {
+
+// Forward declarations
+class RenderGraph;
+class Texture;
+
+// Using declaration
+using render_graph::RenderGraph;
+using render_graph::Texture;
+using wrapper::Device;
+using wrapper::Sampler;
+using wrapper::commands::CommandBuffer;
+
+// NOTE: Originally we did not want to have a RAII wrapper for VkImage and VkImageView and put this into the Texture
+// wrapper directly, but since the Texture wrapper contains 2 Images depending on whether MSAA is enabled or not, we
+// have chosen to use the Image RAII wrapper.
+
+// TODO: Move this to wrapper/ folder again (it is not really part of rendergraph)
+
+/// RAII wrapper for VkImage and VkImageView
+/// @note Multisample anti-aliasing (MSAA) can be enabled on a per-texture basis
+class Image {
+ friend class CommandBuffer;
+ friend class RenderGraph;
+ friend class Texture;
+
+private:
+ /// The device wrapper
+ const Device &m_device;
+ /// The internal debug name of the image
+ std::string m_name;
+
+ VkImageCreateInfo m_img_ci{};
+ VkImageViewCreateInfo m_img_view_ci{};
+
+ VkImage m_img{VK_NULL_HANDLE};
+ VkImageView m_img_view{VK_NULL_HANDLE};
+ VmaAllocation m_alloc{VK_NULL_HANDLE};
+ VmaAllocationInfo m_alloc_info{};
+
+ const VmaAllocationCreateInfo m_alloc_ci{
+ .usage = VMA_MEMORY_USAGE_AUTO,
+ };
+
+ /// The combined image sampler for the texture
+ /// This is only relevant if the texture is used as TextureUsage::NORMAL
+ std::unique_ptr m_sampler;
+
+ /// Create the image and the image view
+ /// @param img_ci The image create info
+ /// @param img_view_ci The image view create info
+ void create(VkImageCreateInfo img_ci, VkImageViewCreateInfo img_view_ci);
+
+ /// Destroy the image view, the image, and the sampler
+ void destroy();
+
+public:
+ /// Default constructor
+ /// @param device The device wrapper
+ /// @param name The name of the Image
+ Image(const Device &device, std::string name);
+
+ Image(const Image &) = delete;
+ Image(Image &&) noexcept;
+ ~Image();
+
+ Image &operator=(const Image &other) = delete;
+ Image &operator=(Image &&other) = delete;
+};
+
+} // namespace inexor::vulkan_renderer::render_graph
diff --git a/include/inexor/vulkan-renderer/render-graph/render_graph.hpp b/include/inexor/vulkan-renderer/render-graph/render_graph.hpp
new file mode 100644
index 000000000..4cfd6368a
--- /dev/null
+++ b/include/inexor/vulkan-renderer/render-graph/render_graph.hpp
@@ -0,0 +1,380 @@
+#pragma once
+
+#include "inexor/vulkan-renderer/render-graph/buffer.hpp"
+#include "inexor/vulkan-renderer/render-graph/graphics_pass.hpp"
+#include "inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp"
+#include "inexor/vulkan-renderer/render-graph/texture.hpp"
+#include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_allocator.hpp"
+#include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_layout_builder.hpp"
+#include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_layout_cache.hpp"
+#include "inexor/vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.hpp"
+#include "inexor/vulkan-renderer/wrapper/pipelines/pipeline.hpp"
+#include "inexor/vulkan-renderer/wrapper/pipelines/pipeline_builder.hpp"
+#include "inexor/vulkan-renderer/wrapper/pipelines/pipeline_layout.hpp"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace inexor::vulkan_renderer::wrapper {
+/// Forward declarations
+class Device;
+class Swapchain;
+} // namespace inexor::vulkan_renderer::wrapper
+
+namespace inexor::vulkan_renderer::render_graph {
+
+// Using declarations
+using wrapper::Device;
+using wrapper::Swapchain;
+using wrapper::commands::CommandBuffer;
+using wrapper::descriptors::DescriptorSetAllocator;
+using wrapper::descriptors::DescriptorSetLayoutBuilder;
+using wrapper::descriptors::DescriptorSetLayoutCache;
+using wrapper::descriptors::WriteDescriptorSetBuilder;
+using wrapper::pipelines::GraphicsPipeline;
+using wrapper::pipelines::GraphicsPipelineBuilder;
+using wrapper::pipelines::PipelineLayout;
+
+/// A rendergraph is a generic solution for rendering architecture.
+///
+///
+///
+///
+///
+///
+class RenderGraph {
+private:
+ /// The device wrapper
+ Device &m_device;
+
+ // The rendergraph has its own logger
+ std::shared_ptr m_log{spdlog::default_logger()->clone("render-graph")};
+
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// GRAPHICS PASSES
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// Graphics passes are build inside of graphics pass create functions. Those functions are given to the
+ /// rendergraph, and they are all called sequentially during rendergraph compilation. Inside of the graphics pass
+ /// create function, the GraphicsPassBuilder can be used to build the graphics pass. The graphics pass which is
+ /// created is stored internally inside of the rendergraph. Each graphics pass specifies to which attachments
+ /// (color, depth, stencil) it writes by using the writes_to method of the GraphicsPassBuilder. The attachments will
+ /// be used by rendergraph in the VkRenderingInfo in dynamic rendering. Each pass must also specify from which other
+ /// passes it reads. For example if we specify B.reads_from(A), it means that graphics pass B is reading from
+ /// graphics pass A. Rendergraph can then automatically determine the oder of all passes during rendergraph
+ /// compilation using depth first search (DFS) algorithm. Rendergraph also makes sure that the graph is acyclic, so
+ /// there must not be any cycles in it! Note that graphics passes only specify reads_from for previous passes and
+ /// writes_to for attachments, as a graphics pass does not directly write into another graphics pass, but only into
+ /// attachments. Why can rendergraph only reason about cycles after creating the graphics passes, at the and of
+ /// compilation? To check for cycles, we would need to reason about the reads of each graphics pass before the
+ /// passes have been created. The problem here is that we can't reference a piece of memory unless it as been
+ /// created, meaning we can't read_from a pass unless its shared_ptr has been allocated. A workaround would be to
+ /// have a default constructor which already creates the object before rendergraph could call another constructor
+ /// which actually creates the pass. This is however much more complex than the current solution, and from what we
+ /// understand it's the fact that we can't reference a piece of memory unless it has been created that makes it very
+ /// hard to actually make cycles in rendergraph passes! You can only call reads_from for passes which came before
+ /// the pass you are currently creating, you can't call reads_from for passes which come later.
+ /// -----------------------------------------------------------------------------------------------------------------
+
+ /// The graphics pass builder of the rendergraph
+ GraphicsPassBuilder m_graphics_pass_builder{};
+ /// The graphics passes used in the rendergraph
+ std::vector> m_graphics_passes;
+
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// SWAPCHAINS
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// Swapchains are not managed by rendergraph and they are not even stored as a std::weak_ptr inside of rendergraph.
+ /// Instead, if a graphics pass writes to a swapchain, it can be specified by using the writes_to method in
+ /// GraphicsPassBuilder. To unify textures and swapchains as write attachments, The GraphicsPassBuilder class uses
+ /// std::variant, std::weak_ptr> as type for write attachments. When creating the
+ /// graphics pass, The constructor of the GraphicsPass class will automatically sort the write attachment by type
+ /// (texture or swapchain). The textures and swapchains which are used as write attachments are processed by
+ /// rendergraph in the fill_graphics_pass_rendering_info method. To fill VkRenderingInfo of a graphics pass, we need
+ /// to fill VkRenderingInfo for every texture and swapchain that is written to. This means we only need to know the
+ /// image view of the texture (the current image view in case of a swapchain), the image layout, and the optional
+ /// clear value for the swapchain or texture. The clear value is stored in the graphics pass as well.
+ ///
+ /// Rendergraph automatically performs image layout transitions for swapchains when they are used as write
+ /// attachment. To avoid unnecessary transitions, rendergraph will check if a swapchain is already in
+ /// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL layout before starting dynamic rendering. To avoid more unnecessary
+ /// image layout transition of the swapchain image, the layout is changed to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR only if
+ /// the next pass (if any) is not rendering to the swapchain.
+ /// -----------------------------------------------------------------------------------------------------------------
+
+ /// The unique wait semaphores of all swapchains used (This means if one swapchain is used mutliple times it's still
+ /// only one VkSemaphore in here because collect_swapchain_image_available_semaphores method will fill this vector)
+ std::vector m_swapchains_imgs_available;
+
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// GRAPHICS PIPELINES
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// Graphics pipelines are created during rendergraph compilation, but the graphics pipeline instances are not
+ /// stored inside of the rendergraph (unlike textures or buffers for example). The reason for this is that graphics
+ /// pipelines are also not bound by the rendergraph automatically before calling the on_record command buffer
+ /// recording function of a graphics pass. This is because while the rendergraph could bind a graphics pipeline just
+ /// before calling on_record, inside of the on_record function, the use of graphics pipelines could be arbitrarily
+ /// complex. We also didn't want to have a rendergraph API which exposes binding one graphics pipeline before
+ /// calling on_record because this could let the programmer think that binding pipelines is fully covered internally
+ /// in rendergraph, which is not the case. You might wonder why we decided to use create functions for the graphics
+ /// pipelines if the graphics pipeline is not stored inside of rendergraph? To create a graphics pipeline, you need
+ /// a pipeline layout. The pipeline layout is part of the GraphicsPipeline wrapper and every graphics pipeline has
+ /// exactly one pipeline layout. To create the pipeline layout, you need to know the descriptor set layout! To be
+ /// precise, you need to know the descriptor set layoput if the graphics pipeline uses resource descriptors. You
+ /// also need to know the push constant ranges, but they are relatively easy to specify in GraphicsPipelienBuilder.
+ /// This complex order of initialization of Vulkan resources must be respected and one of the advantages of having a
+ /// rendergraph is that is makes all this very easy. After the descriptor set layout has been created by the
+ /// rendergraph, the graphics pipelines can be created because we then know about the descriptor set layout which is
+ /// required for the pipeline layout.
+ /// -----------------------------------------------------------------------------------------------------------------
+
+ // TODO: Support compute pipelines
+ // TODO: Use pipeline cache and write pipeline cache to file, and support loading them
+
+ /// The graphics pipeline builder of the rendergraph
+ GraphicsPipelineBuilder m_graphics_pipeline_builder;
+ /// In these callback functions, the graphics pipelines will be created
+ using OnCreateGraphicsPipeline = std::function;
+ /// The graphics pipeline create functions which are called by rendergraph to create the graphics pipelines
+ std::vector m_pipeline_create_functions;
+
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// BUFFERS
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// We use Vulkan Memory Allocator (VMA) for memory management of resources under the hood. Buffers are created and
+ /// stored inside of rendergraph exclusively. To code outside of rendergraph, we give std::weak_ptr of the Buffer
+ /// handles we create. This way, the buffers can be used in the on_record function directly without having to pass
+ /// the buffers as parameters to the on_record function. Also, the use of std::weak_ptr makes the memory ownership
+ /// of the buffer handle clear. The memory is owned by the rendergraph in a std::shared_ptr, but it can be used
+ /// outside of rendergraph through the std::weak_ptr. The Buffer wrapper can be a VERTEX_BUFFER, INDEX_BUFFER, or
+ /// UNIFORM_BUFFER. The rendergraph can be instructed to update a buffer in code outside of the rendergraph using
+ /// the request_update method, and the update is then carried out by rendergraph. All buffer updates are carried out
+ /// on a per-frame basis, meaning that all updates will always be coherent with respect to one frame. In the future,
+ /// we could improve rendergraph so it automatically double or triple buffers all resources (buffers, textures..),
+ /// so it can use one index for rendering, while update on the next index is already being carried out. This will
+ /// parallelization using taskflow and proper synchronization must be used. While this would increase memory
+ /// consumption, it would improve rendering performance by reducing cpu and gpu stalls. Every buffer must have an
+ /// update function. If a vertex buffer and an index buffer is updated, each buffer should be updated in their own
+ /// update function rather than updating the index buffer in the vertex buffer as well. While this is technically
+ /// also correct, it makes no sense to do it this way because vertex buffer and index buffer are updated on a
+ /// per-frame basis coherently anyways. So it doesn't really matter.
+ /// -----------------------------------------------------------------------------------------------------------------
+
+ /// The vertex buffers, index buffers, and uniform buffers
+ std::vector> m_buffers;
+
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// TEXTURES
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// Like buffers, textures are created by rendergraph and stored in the rendergraph as a shared_ptr. This means just
+ /// like buffers, the rendergraph owns the underlying memory, and all external code must use std::weak_ptr to it.
+ /// There are different types of textures, which are listed in TextureUsage. In principle, the Texture wrapper
+ /// allows each texture to have an on_init and an on_update function. Depending on the texture usage, textures can
+ /// be created in two different ways: Textures which are used as color attachment (back buffer) or depth attachment
+ /// (depth buffer) are created inside of rendergraph only. This means code outside of rendergraph is not allowed to
+ /// modify the data in the texture directly, but instead the external code must specify which attachment is written
+ /// to by which pass. Rendergraph will then write to the attachments automatically. This means for texture which are
+ /// of TextureUsage COLOR_ATTACHMENT or DEPTH_ATTACHMENT, both on_init and on_update are std::nullopt (Rendergraph
+ /// initializes these textures directly rather than specifying an unnecessary on_init function which is then called
+ /// directly). The other type of texture is TextureUsage::NORMAL, which means this texture is used in for example in
+ /// combined image samplers. This could be the font texture of ImGui for example. Because the data to fill this
+ /// texture is loaded from a file by the ImGui wrapper, we must specify an on_init function to rendergraph which
+ /// copies the font texture data into the texture wrapper. TextureUsage NORMAL is the only texture type which is
+ /// allowed to have an on_update function. The on_update function is called per-frame (if it's not std::nullopt).
+ /// The on_update function allows for dynamic textures which are updated every frame. Note that so far, our code
+ /// base does not use this feature yet.
+
+ /// The textures
+ std::vector> m_textures;
+
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// RESOURCE DESCRIPTORS
+ /// -----------------------------------------------------------------------------------------------------------------
+ /// After a lot of discussion we decided to keep the actual VkDescriptorSet handles not inside of of
+ /// rendergraph. Originally we planed to have one descriptor set per pass (or several of them per pass) which are
+ /// bound using vkBindDescriptorSet before on_record is called. However, the binding of descriptor sets inside of
+ /// the on_record command buffer recording function of the pass can be much more complex than just binding one
+ /// descriptor set (or several) before calling on_record. In fact, the descriptor set binding could be arbitrarily
+ /// complex inside of on_record. Associating the descriptor sets with the pass would introduce another level of
+ /// unnecessary indirection without any benefits which we do not want. We then thought we could automatically create
+ /// the descriptor set layout of a pass by analyzing the resources the pass reads from. This in theory would also
+ /// allow us to allocate the descriptor sets and to update them. However, the descriptor set layout is also required
+ /// for creating the pipeline layout! There are two problems with this: 1) The programmer would either have to
+ /// specify the reads of the pass in the order of the descriptor set layout or associate the read of a pass with a
+ /// binding in the descriptor set layout. Otherwise, the descriptor set layout would be messed up. 2) Because the
+ /// descriptor set layout is required when creating the graphics pipeline, we would have to associate pipelines with
+ /// passes somehow. However, we also decided to keep pipelines (instances of the GraphicsPipeline wrapper) out of
+ /// rendergraph. In summary, keeping VkDescriptorSet handles in rendergraph would complicate the API unnecessarily.
+ /// Rendergraph now manages resource descriptors as follows: Descriptors need a descriptor set layout, which is
+ /// created from DescriptorSetLayoutBuilder (the first function). Inside of OnBuildDescriptorSetLayout, with the
+ /// help of DescriptorSetLayoutBuilder, the programmer simply specifies which descriptors are inside of the
+ /// descriptor set. The descriptor set is then created by the builder. DescriptorSetLayoutBuilder uses
+ /// DescriptorSetLayoutCache internally to potentially speed up (re)creation of descriptor set layouts. The
+ /// VkDescriptorSetLayout created by DescriptorSetLayoutBuilder inside of on_build_descriptor_set_layout must be
+ /// stored externally, and it is the responsibility of the programmer to make sure the VkDescriptorSetLayout is
+ /// valid memory when it is used in the on_record function! The descriptor sets are allocated by rendergraph via
+ /// OnAllocateDescriptorSet functions using the DescriptorSetAllocator of the rendergraph. Descriptor sets are
+ /// updated in the OnUpdateDescriptorSet functions using the DescriptorSetUpdateBuilder of the rendergraph.
+ ///
+ /// TODO: Mention batching to vkUpdateDescriptorSets...
+ /// -----------------------------------------------------------------------------------------------------------------
+
+ /// The descriptor set layout builder (a builder pattern for descriptor set layouts)
+ DescriptorSetLayoutBuilder m_descriptor_set_layout_builder;
+ /// The descriptor set allocator
+ DescriptorSetAllocator m_descriptor_set_allocator;
+ /// The write descriptor set builder (a builder pattern for write descriptor sets)
+ WriteDescriptorSetBuilder m_write_descriptor_set_builder;
+
+ /// A user-defined function which creates the descriptor set layout
+ using OnBuildDescriptorSetLayout = std::function;
+ /// A user-defined function which allocates a descriptor set
+ using OnAllocateDescriptorSet = std::function;
+ /// A user-defined function which builds the descriptor set write for the pass
+ using OnBuildWriteDescriptorSets = std::function(WriteDescriptorSetBuilder &)>;
+
+ /// Resource descriptors are managed by specifying those three functions to the rendergraph
+ /// Rendergraph will then call those function in the correct order during rendergraph compilation
+ using ResourceDescriptor =
+ std::tuple;
+ /// The resource descriptors of the rendergraph
+ std::vector m_resource_descriptors;
+ /// All write descriptor sets will be stored in here so we can have one batched call to vkUpdateDescriptorSets
+ std::vector m_write_descriptor_sets;
+
+ /// Allocate the descriptor sets
+ void allocate_descriptor_sets();
+
+ /// The rendergraph must not have any cycles in it!
+ /// @exception std::logic_error The rendergraph is not acyclic!
+ void check_for_cycles();
+
+ /// Collect all image available semaphores of all swapchains which are used into one std::vector
+ void collect_swapchain_image_available_semaphores();
+
+ /// Create the descriptor set layouts
+ void create_descriptor_set_layouts();
+
+ /// Create the graphics pipelines
+ void create_graphics_pipelines();
+
+ /// Determine the order of execution of the graphics passes by using depth first search (DFS) algorithm
+ void determine_pass_order();
+
+ /// Fill the VkRenderingInfo for a graphics pass
+ /// @param pass The graphics pass
+ void fill_graphics_pass_rendering_info(GraphicsPass &pass);
+
+ /// Record the command buffer of a pass. After a lot of discussions about the API design of rendergraph, we came to
+ /// the conclusion that it's the full responsibility of the programmer to manually bind pipelines, descriptors sets,
+ /// and buffers inside of the on_record function instead of attempting to abstract all of this in rendergraph. This
+ /// means rendergraph will not automatically bind pipelines, buffers, or descriptor sets! The reason for this is
+ /// that there could be complex rendering going on inside of the on_record function with an arbitrary number of
+ /// pipelines descriptor sets, and buffers being bound in a nontrivial order or under conditional cases. We then
+ /// refrained from designing a simple API inside of rendergraph which automatically binds one graphics pipeline,
+ /// descriptor set, or a set of buffers at the beginning of rendering before calling on_record because it would
+ /// cause confusion about the correct API usage for the advanced use cases. Nonetheless, the creation of buffers,
+ /// descriptors, or pipelines is still the full responsibility of the rendergraph, but you need to use them manually
+ /// inside of the on_record function.
+ /// @param cmd_buf The command buffer to record the pass into
+ /// @param pass The graphics pass to record the command buffer for
+ void record_command_buffer_for_pass(const CommandBuffer &cmd_buf, GraphicsPass &pass);
+
+ /// Update the vertex-, index-, and uniform-buffers
+ /// @note If a uniform buffer has been updated, an update of the associated descriptor set will be performed
+ void update_buffers();
+
+ /// Update dynamic textures
+ void update_textures();
+
+ /// Update the write descriptor sets
+ /// @note This function must only be called once during rendergraph compilation, not for every frame!
+ void update_write_descriptor_sets();
+
+public:
+ /// Default constructor
+ /// @note device and swapchain are not taken as a const reference because rendergraph needs to modify both
+ /// @param device The device wrapper
+ RenderGraph(Device &device);
+
+ RenderGraph(const RenderGraph &) = delete;
+ // TODO: Implement me!
+ RenderGraph(RenderGraph &&) noexcept;
+ ~RenderGraph() = default;
+
+ RenderGraph &operator=(const RenderGraph &) = delete;
+ RenderGraph &operator=(RenderGraph &&) = delete;
+
+ /// Add a new graphics pass to the rendergraph
+ /// @aram pass The graphics pass that was created
+ /// @return A std::weak_ptr to the graphics pass that was added
+ [[nodiscard]] std::weak_ptr add_graphics_pass(std::shared_ptr pass);
+
+ /// Add a new graphics pipeline to the rendergraph
+ /// @param on_pipeline_create A function to create the graphics pipeline using GraphicsPipelineBuilder
+ /// @note Move semantics is used to std::move on_pipeline_create
+ void add_graphics_pipeline(OnCreateGraphicsPipeline on_pipeline_create);
+
+ /// Add an buffer to rendergraph
+ /// @param name The name of the buffer
+ /// @param type The type of the buffer
+ /// @param on_update The update function of the index buffer
+ /// @return A weak pointer to the buffer resource that was created
+ [[nodiscard]] std::weak_ptr add_buffer(std::string name, BufferType type, std::function on_update);
+
+ /// Add a descriptor to rendergraph
+ /// @note This function is of type void because it does not store anything that is created in those callback
+ /// functions. As mentioned above, resource descriptors are kept outside of rendergraph.
+ /// @note This function does not perform any error checks when it comes to correct usage of descriptors, because
+ /// this is the job of validation layers. If you would give the rendergraph 3 empty functions, you would not notice
+ /// unless you attempt to use some descriptor in on_record that would not have been set up correctly.
+ /// @param on_create_descriptor_set_layout The descriptor set layout build function
+ /// @param on_allocate_descriptor_set The descriptor set allocation function
+ /// @param on_update_descriptor_set The descriptor set update function
+ void add_resource_descriptor(OnBuildDescriptorSetLayout on_build_descriptor_set_layout,
+ OnAllocateDescriptorSet on_allocate_descriptor_set,
+ OnBuildWriteDescriptorSets on_update_descriptor_set);
+
+ /// Add a texture which will be initialized externally (not inside of rendergraph)
+ /// @param name The name of the texture
+ /// @param usage The usage of the texture inside of rendergraph
+ /// @param format The format of the texture
+ /// @param width The width of the texture
+ /// @param height The height of the texture
+ /// @param channel_count The number of texture channels
+ /// @param sample_count The sample count of the texture
+ /// @param m_on_check_for_updates The texture update function (an empty lambda by default)
+ /// @return A weak pointer to the texture that was created
+ [[nodiscard]] std::weak_ptr add_texture(
+ std::string name,
+ TextureUsage usage,
+ VkFormat format,
+ std::uint32_t width,
+ std::uint32_t height,
+ std::uint32_t channels,
+ VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_1_BIT,
+ std::function m_on_check_for_updates = []() {});
+
+ /// Compile the rendergraph
+ void compile();
+
+ /// Return the rendergraph's graphics pass builder instance
+ GraphicsPassBuilder &get_graphics_pass_builder() {
+ return m_graphics_pass_builder;
+ }
+
+ /// Render a frame
+ void render();
+
+ /// Reset the entire RenderGraph
+ void reset();
+};
+
+} // namespace inexor::vulkan_renderer::render_graph
diff --git a/include/inexor/vulkan-renderer/render-graph/texture.hpp b/include/inexor/vulkan-renderer/render-graph/texture.hpp
new file mode 100644
index 000000000..c3db7e2ae
--- /dev/null
+++ b/include/inexor/vulkan-renderer/render-graph/texture.hpp
@@ -0,0 +1,156 @@
+#pragma once
+
+#include
+
+#include "inexor/vulkan-renderer/render-graph/image.hpp"
+
+#include
+#include
+#include
+#include
+
+namespace inexor::vulkan_renderer::wrapper {
+// Forward declaration
+class Device;
+} // namespace inexor::vulkan_renderer::wrapper
+
+namespace inexor::vulkan_renderer::wrapper::commands {
+// Forward delcaration
+class CommandBuffer;
+} // namespace inexor::vulkan_renderer::wrapper::commands
+
+namespace inexor::vulkan_renderer::wrapper::descriptors {
+/// Forward declaration
+class WriteDescriptorSetBuilder;
+} // namespace inexor::vulkan_renderer::wrapper::descriptors
+
+namespace inexor::vulkan_renderer::render_graph {
+
+/// Specifies the use of the texture
+/// NOTE: All usages which are not TextureUsage::NORMAL are for internal usage inside of rendergraph only
+enum class TextureUsage {
+ NORMAL,
+ COLOR_ATTACHMENT,
+ DEPTH_ATTACHMENT,
+ STENCIL_ATTACHMENT,
+};
+
+// Forward declaration
+class GraphicsPass;
+
+// Using declarations
+using wrapper::Device;
+using wrapper::commands::CommandBuffer;
+using wrapper::descriptors::WriteDescriptorSetBuilder;
+
+/// RAII wrapper for texture resources
+class Texture {
+ // These friend classes are allowed to access the private data of Texture
+ friend class WriteDescriptorSetBuilder;
+ friend class GraphicsPass;
+ friend class RenderGraph;
+
+private:
+ /// The device wrapper
+ const Device &m_device;
+ /// The name of the texture
+ std::string m_name;
+ /// The usage of this texture
+ TextureUsage m_usage;
+ /// The format of the texture
+ VkFormat m_format{VK_FORMAT_UNDEFINED};
+ /// The width of the texture
+ std::uint32_t m_width{0};
+ /// The height of the texture
+ std::uint32_t m_height{0};
+ /// The channel count of the texture (4 by default)
+ // TODO: Can we determine the number of channels based on the given format?
+ std::uint32_t m_channels{4};
+ /// The sample count of the MSAA image (if MSAA is enabled)
+ VkSampleCountFlagBits m_samples{VK_SAMPLE_COUNT_1_BIT};
+
+ /// The image of the texture
+ std::shared_ptr m_image;
+
+ /// This is only used internally inside of rendergraph in case this texture used as a back buffer, depth buffer, or
+ /// stencil buffer and MSAA is enabled.
+ std::shared_ptr m_msaa_image;
+
+ // This is used for initializing textures and for updating dynamic textures
+ bool m_update_requested{true};
+ void *m_src_texture_data{nullptr};
+ std::size_t m_src_texture_data_size{0};
+
+ /// By definition, if this is not std::nullopt, this is a dynamic texture
+ std::function m_on_check_for_updates;
+
+ // The staging buffer for updating the texture data
+ VkBuffer m_staging_buffer{VK_NULL_HANDLE};
+ VmaAllocation m_staging_buffer_alloc{VK_NULL_HANDLE};
+ VmaAllocationInfo m_staging_buffer_alloc_info{};
+
+ /// This part of the image wrapper is for external use outside of rendergraph
+ /// The descriptor image info required for descriptor updates
+ VkDescriptorImageInfo m_descriptor_img_info{};
+
+ /// Create the texture (and the MSAA texture if specified)
+ void create();
+
+ /// Destroy the texture (and the MSAA texture if specified)
+ void destroy();
+
+ /// Destroy the staging buffer used for texture updates
+ void destroy_staging_buffer();
+
+ /// Upload the data into the texture
+ /// @param cmd_buf The command buffer to record the commands into
+ void update(const CommandBuffer &cmd_buf);
+
+public:
+ /// Default constructor
+ /// @param device The device wrapper
+ /// @param name The internal debug name of the texture
+ /// @param usage The usage of the texture inside of rendergraph
+ /// @param format The format of the texture
+ /// @param width The width of the texture
+ /// @param height The height of the texture
+ /// @param channels The channel count of the texture
+ /// @param samples The sample count of the texture
+ /// @param on_check_for_updates The update function of the texture
+ Texture(const Device &device,
+ std::string name,
+ TextureUsage usage,
+ VkFormat format,
+ std::uint32_t width,
+ std::uint32_t height,
+ std::uint32_t channels,
+ VkSampleCountFlagBits samples,
+ std::function on_check_for_updates);
+
+ Texture(const Texture &) = delete;
+ Texture(Texture &&other) noexcept;
+ ~Texture();
+
+ Texture &operator=(const Texture &) = delete;
+ Texture &operator=(Texture &&) = delete;
+
+ [[nodiscard]] VkFormat format() const {
+ return m_format;
+ }
+
+ [[nodiscard]] VkExtent2D extent() const {
+ return {
+ .width = m_width,
+ .height = m_height,
+ };
+ }
+
+ /// Request rendergraph to update the texture
+ /// @param src_texture_data A pointer to the source data
+ /// @param src_texture_data_size The size of the source data
+ /// @note It is the responsibility of the programmer to make sure the memory the pointer points to is still valid
+ /// when rendergraph is carrying out the update!
+ void request_update(void *src_texture_data, std::size_t src_texture_data_size);
+};
+
+} // namespace inexor::vulkan_renderer::render_graph
diff --git a/include/inexor/vulkan-renderer/render_graph.hpp b/include/inexor/vulkan-renderer/render_graph.hpp
deleted file mode 100644
index a036c9ce7..000000000
--- a/include/inexor/vulkan-renderer/render_graph.hpp
+++ /dev/null
@@ -1,455 +0,0 @@
-#pragma once
-
-#include "inexor/vulkan-renderer/wrapper/device.hpp"
-#include "inexor/vulkan-renderer/wrapper/framebuffer.hpp"
-#include "inexor/vulkan-renderer/wrapper/swapchain.hpp"
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-// TODO: Compute stages.
-// TODO: Uniform buffers.
-
-// Forward declarations
-namespace inexor::vulkan_renderer::wrapper {
-class CommandBuffer;
-class Shader;
-}; // namespace inexor::vulkan_renderer::wrapper
-
-namespace inexor::vulkan_renderer {
-
-// Forward declarations
-class PhysicalResource;
-class PhysicalStage;
-class RenderGraph;
-
-/// @brief Base class of all render graph objects (resources and stages).
-/// @note This is just for internal use.
-struct RenderGraphObject {
- RenderGraphObject() = default;
- RenderGraphObject(const RenderGraphObject &) = delete;
- RenderGraphObject(RenderGraphObject &&) = delete;
- virtual ~RenderGraphObject() = default;
-
- RenderGraphObject &operator=(const RenderGraphObject &) = delete;
- RenderGraphObject &operator=(RenderGraphObject &&) = delete;
-
- /// @brief Casts this object to type `T`
- /// @return The object as type `T` or `nullptr` if the cast failed
- template
- [[nodiscard]] T *as();
-
- /// @copydoc as
- template
- [[nodiscard]] const T *as() const;
-};
-
-/// @brief A single resource in the render graph.
-/// @note May become multiple physical (vulkan) resources during render graph compilation.
-class RenderResource : public RenderGraphObject {
- friend RenderGraph;
-
-private:
- const std::string m_name;
- std::shared_ptr m_physical;
-
-protected:
- explicit RenderResource(std::string name) : m_name(std::move(name)) {}
-
-public:
- RenderResource(const RenderResource &) = delete;
- RenderResource(RenderResource &&) = delete;
- ~RenderResource() override = default;
-
- RenderResource &operator=(const RenderResource &) = delete;
- RenderResource &operator=(RenderResource &&) = delete;
-
- [[nodiscard]] const std::string &name() const {
- return m_name;
- }
-};
-
-enum class BufferUsage {
- /// @brief Specifies that the buffer will be used to input index data.
- INDEX_BUFFER,
-
- /// @brief Specifies that the buffer will be used to input per vertex data to a vertex shader.
- VERTEX_BUFFER,
-};
-
-class BufferResource : public RenderResource {
- friend RenderGraph;
-
-private:
- const BufferUsage m_usage;
- std::vector m_vertex_attributes;
-
- // Data to upload during render graph compilation.
- const void *m_data{nullptr};
- std::size_t m_data_size{0};
- bool m_data_upload_needed{false};
- std::size_t m_element_size{0};
-
-public:
- BufferResource(std::string &&name, BufferUsage usage) : RenderResource(name), m_usage(usage) {}
-
- /// @brief Specifies that element `offset` of this vertex buffer is of format `format`.
- /// @note Calling this function is only valid on buffers of type BufferUsage::VERTEX_BUFFER.
- void add_vertex_attribute(VkFormat format, std::uint32_t offset);
-
- /// @brief Specifies the element size of the buffer upfront if data is not to be uploaded immediately.
- /// @param element_size The element size in bytes
- void set_element_size(std::size_t element_size) {
- m_element_size = element_size;
- }
-
- /// @brief Specifies the data that should be uploaded to this buffer at the start of the next frame.
- /// @param count The number of elements (not bytes) to upload
- /// @param data A pointer to a contiguous block of memory that is at least `count * sizeof(T)` bytes long
- // TODO: Use std::span when we switch to C++ 20.
- template
- void upload_data(const T *data, std::size_t count);
-
- /// @brief @copybrief upload_data(const T *, std::size_t)
- /// @note This is equivalent to doing `upload_data(data.data(), data.size() * sizeof(T))`
- /// @see upload_data(const T *data, std::size_t count)
- template
- void upload_data(const std::vector &data);
-};
-
-enum class TextureUsage {
- /// @brief Specifies that this texture is the output of the render graph.
- // TODO: Refactor back buffer system more (remove need for BACK_BUFFER texture usage)
- BACK_BUFFER,
-
- /// @brief Specifies that this texture is a combined depth/stencil buffer.
- /// @note This may mean that this texture is completely GPU-sided and cannot be accessed by the CPU in any way.
- DEPTH_STENCIL_BUFFER,
-
- /// @brief Specifies that this texture isn't used for any special purpose.
- NORMAL,
-};
-
-class TextureResource : public RenderResource {
- friend RenderGraph;
-
-private:
- const TextureUsage m_usage;
- VkFormat m_format{VK_FORMAT_UNDEFINED};
-
-public:
- TextureResource(std::string &&name, TextureUsage usage) : RenderResource(name), m_usage(usage) {}
-
- /// @brief Specifies the format of this texture that is required when the physical texture is made.
- /// @details For TextureUsage::BACK_BUFFER textures, using the swapchain image format is preferable in most cases.
- /// For TextureUsage::DEPTH_STENCIL_BUFFER textures, a VK_FORMAT_D* must be used.
- void set_format(VkFormat format) {
- m_format = format;
- }
-};
-
-/// @brief A single render stage in the render graph.
-/// @note Not to be confused with a vulkan render pass.
-class RenderStage : public RenderGraphObject {
- friend RenderGraph;
-
-private:
- const std::string m_name;
- std::unique_ptr m_physical;
- std::vector m_writes;
- std::vector m_reads;
-
- std::vector m_descriptor_layouts;
- std::vector m_push_constant_ranges;
- std::function m_on_record{[](auto &, auto &) {}};
-
-protected:
- explicit RenderStage(std::string name) : m_name(std::move(name)) {}
-
-public:
- RenderStage(const RenderStage &) = delete;
- RenderStage(RenderStage &&) = delete;
- ~RenderStage() override = default;
-
- RenderStage &operator=(const RenderStage &) = delete;
- RenderStage &operator=(RenderStage &&) = delete;
-
- /// @brief Specifies that this stage writes to `resource`.
- void writes_to(const RenderResource *resource);
-
- /// @brief Specifies that this stage reads from `resource`.
- void reads_from(const RenderResource *resource);
-
- /// @brief Binds a descriptor set layout to this render stage.
- /// @note This function will be removed in the near future, as we are aiming for users of the API to not have to
- /// deal with descriptors at all.
- // TODO: Refactor descriptor management in the render graph
- void add_descriptor_layout(VkDescriptorSetLayout layout) {
- m_descriptor_layouts.push_back(layout);
- }
-
- /// @brief Add a push constant range to this render stage.
- /// @param range The push constant range
- void add_push_constant_range(VkPushConstantRange range) {
- m_push_constant_ranges.push_back(range);
- }
-
- [[nodiscard]] const std::string &name() const {
- return m_name;
- }
-
- /// @brief Specifies a function that will be called during command buffer recording for this stage
- /// @details This function can be used to specify other vulkan commands during command buffer recording. The most
- /// common use for this is for draw commands.
- void set_on_record(std::function on_record) {
- m_on_record = std::move(on_record);
- }
-};
-
-class GraphicsStage : public RenderStage {
- friend RenderGraph;
-
-private:
- bool m_clears_screen{false};
- bool m_depth_test{false};
- bool m_depth_write{false};
- VkPipelineColorBlendAttachmentState m_blend_attachment{};
- std::unordered_map m_buffer_bindings;
- std::vector m_shaders;
-
-public:
- explicit GraphicsStage(std::string &&name) : RenderStage(name) {}
- GraphicsStage(const GraphicsStage &) = delete;
- GraphicsStage(GraphicsStage &&) = delete;
- ~GraphicsStage() override = default;
-
- GraphicsStage &operator=(const GraphicsStage &) = delete;
- GraphicsStage &operator=(GraphicsStage &&) = delete;
-
- /// @brief Specifies that this stage should clear the screen before rendering.
- void set_clears_screen(bool clears_screen) {
- m_clears_screen = clears_screen;
- }
-
- /// @brief Specifies the depth options for this stage.
- /// @param depth_test Whether depth testing should be performed
- /// @param depth_write Whether depth writing should be performed
- void set_depth_options(bool depth_test, bool depth_write) {
- m_depth_test = depth_test;
- m_depth_write = depth_write;
- }
-
- /// @brief Set the blend attachment for this stage.
- /// @param blend_attachment The blend attachment
- void set_blend_attachment(VkPipelineColorBlendAttachmentState blend_attachment) {
- m_blend_attachment = blend_attachment;
- }
-
- /// @brief Specifies that `buffer` should map to `binding` in the shaders of this stage.
- void bind_buffer(const BufferResource *buffer, std::uint32_t binding);
-
- /// @brief Specifies that `shader` should be used during the pipeline of this stage.
- /// @note Binding two shaders of same type (e.g. two vertex shaders) is undefined behaviour.
- void uses_shader(const wrapper::Shader &shader);
-};
-
-// TODO: Add wrapper::Allocation that can be made by doing `device->make(...)`.
-class PhysicalResource : public RenderGraphObject {
- friend RenderGraph;
-
-protected:
- const wrapper::Device &m_device;
- VmaAllocation m_allocation{VK_NULL_HANDLE};
-
- explicit PhysicalResource(const wrapper::Device &device) : m_device(device) {}
-
-public:
- PhysicalResource(const PhysicalResource &) = delete;
- PhysicalResource(PhysicalResource &&) = delete;
- ~PhysicalResource() override = default;
-
- PhysicalResource &operator=(const PhysicalResource &) = delete;
- PhysicalResource &operator=(PhysicalResource &&) = delete;
-};
-
-class PhysicalBuffer : public PhysicalResource {
- friend RenderGraph;
-
-private:
- VmaAllocationInfo m_alloc_info{};
- VkBuffer m_buffer{VK_NULL_HANDLE};
-
-public:
- explicit PhysicalBuffer(const wrapper::Device &device) : PhysicalResource(device) {}
- PhysicalBuffer(const PhysicalBuffer &) = delete;
- PhysicalBuffer(PhysicalBuffer &&) = delete;
- ~PhysicalBuffer() override;
-
- PhysicalBuffer &operator=(const PhysicalBuffer &) = delete;
- PhysicalBuffer &operator=(PhysicalBuffer &&) = delete;
-};
-
-class PhysicalImage : public PhysicalResource {
- friend RenderGraph;
-
-private:
- VkImage m_image{VK_NULL_HANDLE};
- VkImageView m_image_view{VK_NULL_HANDLE};
-
-public:
- explicit PhysicalImage(const wrapper::Device &device) : PhysicalResource(device) {}
- PhysicalImage(const PhysicalImage &) = delete;
- PhysicalImage(PhysicalImage &&) = delete;
- ~PhysicalImage() override;
-
- PhysicalImage &operator=(const PhysicalImage &) = delete;
- PhysicalImage &operator=(PhysicalImage &&) = delete;
-};
-
-class PhysicalBackBuffer : public PhysicalResource {
- friend RenderGraph;
-
-private:
- const wrapper::Swapchain &m_swapchain;
-
-public:
- PhysicalBackBuffer(const wrapper::Device &device, const wrapper::Swapchain &swapchain)
- : PhysicalResource(device), m_swapchain(swapchain) {}
- PhysicalBackBuffer(const PhysicalBackBuffer &) = delete;
- PhysicalBackBuffer(PhysicalBackBuffer &&) = delete;
- ~PhysicalBackBuffer() override = default;
-
- PhysicalBackBuffer &operator=(const PhysicalBackBuffer &) = delete;
- PhysicalBackBuffer &operator=(PhysicalBackBuffer &&) = delete;
-};
-
-class PhysicalStage : public RenderGraphObject {
- friend RenderGraph;
-
-private:
- VkPipeline m_pipeline{VK_NULL_HANDLE};
- VkPipelineLayout m_pipeline_layout{VK_NULL_HANDLE};
-
-protected:
- const wrapper::Device &m_device;
-
-public:
- explicit PhysicalStage(const wrapper::Device &device) : m_device(device) {}
- PhysicalStage(const PhysicalStage &) = delete;
- PhysicalStage(PhysicalStage &&) = delete;
- ~PhysicalStage() override;
-
- PhysicalStage &operator=(const PhysicalStage &) = delete;
- PhysicalStage &operator=(PhysicalStage &&) = delete;
-
- /// @brief Retrieve the pipeline layout of this physical stage.
- // TODO: This can be removed once descriptors are properly implemented in the render graph.
- [[nodiscard]] VkPipelineLayout pipeline_layout() const {
- return m_pipeline_layout;
- }
-};
-
-class PhysicalGraphicsStage : public PhysicalStage {
- friend RenderGraph;
-
-private:
- VkRenderPass m_render_pass{VK_NULL_HANDLE};
- std::vector m_framebuffers;
-
-public:
- explicit PhysicalGraphicsStage(const wrapper::Device &device) : PhysicalStage(device) {}
- PhysicalGraphicsStage(const PhysicalGraphicsStage &) = delete;
- PhysicalGraphicsStage(PhysicalGraphicsStage &&) = delete;
- ~PhysicalGraphicsStage() override;
-
- PhysicalGraphicsStage &operator=(const PhysicalGraphicsStage &) = delete;
- PhysicalGraphicsStage &operator=(PhysicalGraphicsStage &&) = delete;
-};
-
-class RenderGraph {
-private:
- wrapper::Device &m_device;
- const wrapper::Swapchain &m_swapchain;
- std::shared_ptr m_log{spdlog::default_logger()->clone("render-graph")};
-
- // Vectors of render resources and stages.
- std::vector> m_buffer_resources;
- std::vector> m_texture_resources;
- std::vector> m_stages;
-
- // Stage execution order.
- std::vector m_stage_stack;
-
- // Functions for building resource related vulkan objects.
- void build_buffer(const BufferResource &, PhysicalBuffer &) const;
- void build_image(const TextureResource &, PhysicalImage &, VmaAllocationCreateInfo *) const;
- void build_image_view(const TextureResource &, PhysicalImage &) const;
-
- // Functions for building stage related vulkan objects.
- void build_pipeline_layout(const RenderStage *, PhysicalStage &) const;
- void record_command_buffer(const RenderStage *, const wrapper::CommandBuffer &cmd_buf,
- std::uint32_t image_index) const;
-
- // Functions for building graphics stage related vulkan objects.
- void build_render_pass(const GraphicsStage *, PhysicalGraphicsStage &) const;
- void build_graphics_pipeline(const GraphicsStage *, PhysicalGraphicsStage &) const;
-
-public:
- RenderGraph(wrapper::Device &device, const wrapper::Swapchain &swapchain)
- : m_device(device), m_swapchain(swapchain) {}
-
- /// @brief Adds either a render resource or render stage to the render graph.
- /// @return A mutable reference to the just-added resource or stage
- template
- T *add(Args &&...args) {
- auto ptr = std::make_unique(std::forward(args)...);
- if constexpr (std::is_same_v) {
- return static_cast(m_buffer_resources.emplace_back(std::move(ptr)).get());
- } else if constexpr (std::is_same_v) {
- return static_cast(m_texture_resources.emplace_back(std::move(ptr)).get());
- } else if constexpr (std::is_base_of_v) {
- return static_cast(m_stages.emplace_back(std::move(ptr)).get());
- } else {
- static_assert(!std::is_same_v, "T must be a RenderResource or RenderStage");
- }
- }
-
- /// @brief Compiles the render graph resources/stages into physical vulkan objects.
- /// @param target The target resource of the render graph (usually the back buffer)
- void compile(const RenderResource *target);
-
- /// @brief Submits the command frame's command buffers for drawing.
- /// @param image_index The current image index, retrieved from Swapchain::acquire_next_image
- /// @param cmd_buf The command buffer
- void render(std::uint32_t image_index, const wrapper::CommandBuffer &cmd_buf);
-};
-
-template
-[[nodiscard]] T *RenderGraphObject::as() {
- return dynamic_cast(this);
-}
-
-template
-[[nodiscard]] const T *RenderGraphObject::as() const {
- return dynamic_cast(this);
-}
-
-template
-void BufferResource::upload_data(const T *data, std::size_t count) {
- m_data = data;
- m_data_size = count * (m_element_size = sizeof(T));
- m_data_upload_needed = true;
-}
-
-template
-void BufferResource::upload_data(const std::vector &data) {
- upload_data(data.data(), data.size());
-}
-
-} // namespace inexor::vulkan_renderer
diff --git a/include/inexor/vulkan-renderer/renderer.hpp b/include/inexor/vulkan-renderer/renderer.hpp
index 18c476b5c..5f282702b 100644
--- a/include/inexor/vulkan-renderer/renderer.hpp
+++ b/include/inexor/vulkan-renderer/renderer.hpp
@@ -1,83 +1 @@
-#pragma once
-
-#include "inexor/vulkan-renderer/camera.hpp"
-#include "inexor/vulkan-renderer/fps_counter.hpp"
-#include "inexor/vulkan-renderer/imgui.hpp"
-#include "inexor/vulkan-renderer/msaa_target.hpp"
-#include "inexor/vulkan-renderer/octree_gpu_vertex.hpp"
-#include "inexor/vulkan-renderer/time_step.hpp"
-#include "inexor/vulkan-renderer/vk_tools/gpu_info.hpp"
-#include "inexor/vulkan-renderer/wrapper/instance.hpp"
-#include "inexor/vulkan-renderer/wrapper/uniform_buffer.hpp"
-#include "inexor/vulkan-renderer/wrapper/window.hpp"
-#include "inexor/vulkan-renderer/wrapper/window_surface.hpp"
-
-namespace inexor::vulkan_renderer {
-
-class VulkanRenderer {
-protected:
- std::vector m_shader_stages;
-
- VkDebugReportCallbackEXT m_debug_report_callback{VK_NULL_HANDLE};
-
- bool m_debug_report_callback_initialised{false};
-
- TimeStep m_time_step;
-
- std::uint32_t m_window_width{0};
- std::uint32_t m_window_height{0};
- wrapper::Window::Mode m_window_mode{wrapper::Window::Mode::WINDOWED};
-
- std::string m_window_title;
-
- FPSCounter m_fps_counter;
-
- bool m_vsync_enabled{false};
-
- std::unique_ptr m_camera;
-
- std::unique_ptr m_window;
- std::unique_ptr m_instance;
- std::unique_ptr m_device;
- std::unique_ptr m_surface;
- std::unique_ptr m_swapchain;
- std::unique_ptr m_imgui_overlay;
- std::unique_ptr m_render_graph;
-
- std::vector m_shaders;
- std::vector m_textures;
- std::vector m_uniform_buffers;
- std::vector m_descriptors;
- std::vector m_octree_vertices;
- std::vector m_octree_indices;
-
- TextureResource *m_back_buffer{nullptr};
-
- // Render graph buffers for octree geometry.
- BufferResource *m_index_buffer{nullptr};
- BufferResource *m_vertex_buffer{nullptr};
-
- void setup_render_graph();
- void generate_octree_indices();
- void recreate_swapchain();
- void render_frame();
-
-public:
- VulkanRenderer() = default;
- VulkanRenderer(const VulkanRenderer &) = delete;
- VulkanRenderer(VulkanRenderer &&) = delete;
- ~VulkanRenderer();
-
- VulkanRenderer &operator=(const VulkanRenderer &) = delete;
- VulkanRenderer &operator=(VulkanRenderer &&) = delete;
-
- bool m_window_resized{false};
-
- /// Necessary for taking into account the relative speed of the system's CPU.
- float m_time_passed{0.0f};
-
- ///
- TimeStep m_stopwatch;
-};
-
-} // namespace inexor::vulkan_renderer
+
\ No newline at end of file
diff --git a/include/inexor/vulkan-renderer/renderers/imgui.hpp b/include/inexor/vulkan-renderer/renderers/imgui.hpp
new file mode 100644
index 000000000..2a2c69020
--- /dev/null
+++ b/include/inexor/vulkan-renderer/renderers/imgui.hpp
@@ -0,0 +1,114 @@
+#pragma once
+
+#include "inexor/vulkan-renderer/render-graph/render_graph.hpp"
+
+#include
+#include
+#include
+
+#include
+#include
+
+namespace inexor::vulkan_renderer::wrapper {
+// Forward declarations
+class Device;
+class Shader;
+class Swapchain;
+} // namespace inexor::vulkan_renderer::wrapper
+
+namespace inexor::vulkan_renderer::pipelines {
+// Forward declaration
+class GraphicsPipeline;
+} // namespace inexor::vulkan_renderer::pipelines
+
+namespace inexor::vulkan_renderer::render_graph {
+// Forward declarations
+class Buffer;
+class RenderGraph;
+class GraphicsPass;
+} // namespace inexor::vulkan_renderer::render_graph
+
+namespace inexor::vulkan_renderer::renderers {
+
+// Using declarations
+using render_graph::Buffer;
+using render_graph::GraphicsPass;
+using render_graph::RenderGraph;
+using render_graph::Texture;
+using wrapper::Device;
+using wrapper::Shader;
+using wrapper::Swapchain;
+using wrapper::pipelines::GraphicsPipeline;
+
+/// A wrapper for an ImGui implementation
+class ImGuiRenderer {
+ std::weak_ptr m_index_buffer;
+ std::weak_ptr m_vertex_buffer;
+ std::weak_ptr m_imgui_texture;
+ std::shared_ptr m_imgui_pipeline;
+
+ std::weak_ptr m_imgui_pass;
+
+ // This is the color attachment we will write to
+ std::weak_ptr m_swapchain;
+ // This is the previous pass we read from
+ std::weak_ptr m_previous_pass;
+
+ std::shared_ptr m_vertex_shader;
+ std::shared_ptr m_fragment_shader;
+
+ VkDescriptorSetLayout m_descriptor_set_layout{VK_NULL_HANDLE};
+ VkDescriptorSet m_descriptor_set{VK_NULL_HANDLE};
+
+ // We need to collect the vertices and indices generated by ImGui
+ // because it does not store them in one array, but rather in chunks
+ std::vector m_index_data;
+ std::vector m_vertex_data;
+
+ ImGuiContext *m_imgui_context{nullptr};
+
+ unsigned char *m_font_texture_data{nullptr};
+ int m_font_texture_width{0};
+ int m_font_texture_height{0};
+ int m_font_texture_data_size{0};
+ bool m_font_texture_initialized{false};
+
+ // Neither scale nor translation change
+ struct PushConstBlock {
+ glm::vec2 scale{-1.0f};
+ glm::vec2 translate{-1.0f};
+ } m_push_const_block;
+
+ /// The user's ImGui data will be updated in this function
+ /// It will be called at the beginning of set_on_update
+ std::function m_on_update_user_data{[]() {}};
+
+ void load_font_data_from_file();
+
+ /// Customize ImGui style like text color for example
+ void set_imgui_style();
+
+public:
+ /// Default constructor
+ /// @param device The device wrapper
+ /// @param render_graph The rendergraph
+ /// @param previous_pass The previous pass
+ /// @param swapchain The swapchain to render to
+ /// @param on_update_user_data The user-specified ImGui update function
+ ImGuiRenderer(const Device &device,
+ std::weak_ptr render_graph,
+ std::weak_ptr previous_pass,
+ std::weak_ptr swapchain,
+ std::function on_update_user_data);
+
+ ImGuiRenderer(const ImGuiRenderer &) = delete;
+ ImGuiRenderer(ImGuiRenderer &&) noexcept;
+
+ /// Call ImGui::DestroyContext
+ ~ImGuiRenderer();
+
+ ImGuiRenderer &operator=(const ImGuiRenderer &) = delete;
+ ImGuiRenderer &operator=(ImGuiRenderer &&) = delete;
+};
+
+} // namespace inexor::vulkan_renderer::renderers
diff --git a/include/inexor/vulkan-renderer/renderers/imgui_renderer.hpp b/include/inexor/vulkan-renderer/renderers/imgui_renderer.hpp
new file mode 100644
index 000000000..83c5fdac2
--- /dev/null
+++ b/include/inexor/vulkan-renderer/renderers/imgui_renderer.hpp
@@ -0,0 +1,54 @@
+#pragma once
+
+#include "inexor/vulkan-renderer/wrapper/shader.hpp"
+
+#include
+#include
+
+// Forward declaration
+namespace inexor::vulkan_renderer::wrapper {
+class Device;
+} // namespace inexor::vulkan_renderer::wrapper
+
+namespace inexor::vulkan_renderer::render_components {
+
+/// A renderer for ImGui
+class ImGuiRenderer {
+private:
+ ImDrawData *imgui_draw_data{nullptr};
+
+ // The vertex shader and fragment shader for ImGui
+ wrapper::Shader m_vertex_shader;
+ wrapper::Shader m_fragment_shader;
+
+ // The vertex buffer and index buffer resource for ImGui
+ render_graph::BufferResource *m_vertex_buffer{nullptr};
+ render_graph::BufferResource *m_index_buffer{nullptr};
+
+ std::vector m_vertex_data;
+ std::vector m_index_data;
+
+ struct PushConstBlock {
+ glm::vec2 scale{};
+ glm::vec2 translate{};
+ } m_push_const_block{};
+
+ void initialize_imgui();
+
+ // TODO: Better name?
+ void update_imgui_windows();
+
+public:
+ /// Default constructor
+ /// @param device The device wrapper
+ /// @param render_graph The render graph
+ explicit ImGuiRenderer(const wrapper::Device &device, render_graph::RenderGraph *render_graph);
+ ImGuiRenderer(const ImGuiRenderer &) = delete;
+ ImGuiRenderer(ImGuiRenderer &&) = delete;
+ ~ImGuiRenderer() override = default;
+
+ ImGuiRenderer &operator=(const ImGuiRenderer &) = delete;
+ ImGuiRenderer &operator=(ImGuiRenderer &&) = delete;
+};
+
+} // namespace inexor::vulkan_renderer::render_components
diff --git a/include/inexor/vulkan-renderer/renderers/octree_renderer.hpp b/include/inexor/vulkan-renderer/renderers/octree_renderer.hpp
new file mode 100644
index 000000000..3440b3563
--- /dev/null
+++ b/include/inexor/vulkan-renderer/renderers/octree_renderer.hpp
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "inexor/vulkan-renderer/world/cube.hpp"
+#include "inexor/vulkan-renderer/world/octree_vertex.hpp"
+
+#include
+
+#include
+#include
+#include
+
+// Forward declaration
+namespace inexor::vulkan_renderer::wrapper {
+class Device;
+} // namespace inexor::vulkan_renderer::wrapper
+
+namespace inexor::vulkan_renderer::render_components {
+
+/// Matrices for model, view, and projection
+struct ModelViewProjectionMatrices {
+ glm::mat4 model{1.0f};
+ glm::mat4 view{1.0f};
+ glm::mat4 proj{1.0f};
+};
+
+/// A class for rendering octree geometry
+class OctreeRenderer {
+private:
+ /// The octrees to render
+ std::vector> m_octrees;
+ std::vector update_needed;
+
+ /// The shaders for octree rendering
+ wrapper::Shader m_vertex_shader;
+ wrapper::shader m_fragment_shader;
+
+ // There is one vector of vertices and indices for each octree
+ std::vector> m_octree_vertices;
+ std::vector> m_octree_indices;
+
+ // There is one vertex buffer and one index buffer for each octree
+ std::vector m_vertex_buffers;
+ std::vector m_index_buffers;
+
+ void generate_octree_vertices(std::size_t octree_index);
+ void generate_octree_indices(std::size_t octree_index);
+
+ void regenerate_all_octree_vertices();
+ void regenerate_all_octree_indices();
+
+public:
+ /// Default constructor
+ /// @param device The device wrapper
+ /// @param render_graph The render graph
+ explicit OctreeRenderer(const wrapper::Device &device, render_graph::RenderGraph *render_graph);
+ OctreeRenderer(const OctreeRenderer &) = delete;
+ OctreeRenderer(OctreeRenderer &&) = delete;
+ ~OctreeRenderer() = default;
+
+ OctreeRenderer &operator=(const OctreeRenderer &) = delete;
+ OctreeRenderer &operator=(OctreeRenderer &&) = delete;
+
+ /// Creates random octree geometry
+ void generate_random_octree_geometry();
+};
+
+} // namespace inexor::vulkan_renderer::render_components
diff --git a/include/inexor/vulkan-renderer/tools/file.hpp b/include/inexor/vulkan-renderer/tools/file.hpp
deleted file mode 100644
index 33b4461c0..000000000
--- a/include/inexor/vulkan-renderer/tools/file.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include
-#include
-
-namespace inexor::vulkan_renderer::tools {
-
-/// @brief Extract the extension of a file as lowercase string.
-/// @param file_name the name of the file. This is allowed to include the relative or complete path
-/// @return The extension of the file as lowercase
-[[nodiscard]] std::string get_file_extension_lowercase(const std::string &file_name);
-
-/// @brief Read the data of a file into memory
-/// @param file_name The name of the file
-/// @return A std::vector of type char which contains the binary data of the file
-[[nodiscard]] std::vector read_file_binary_data(const std::string &file_name);
-
-} // namespace inexor::vulkan_renderer::tools
diff --git a/include/inexor/vulkan-renderer/vk_tools/gpu_info.hpp b/include/inexor/vulkan-renderer/vk_tools/gpu_info.hpp
deleted file mode 100644
index 5e02a8778..000000000
--- a/include/inexor/vulkan-renderer/vk_tools/gpu_info.hpp
+++ /dev/null
@@ -1,67 +0,0 @@
-#pragma once
-
-#include
-
-namespace inexor::vulkan_renderer::vk_tools {
-
-/// @brief Print available version of Vulkan API.
-/// @note Inexor engine does not use a Vulkan metaloader such as Volk.
-void print_driver_vulkan_version();
-
-/// @brief Print information about a gpu's device queue families.
-/// @param gpu The regarded gpu.
-void print_physical_device_queue_families(VkPhysicalDevice gpu);
-
-/// @brief Print all available Vulkan instance layers.
-void print_instance_layers();
-
-/// @brief Print all available Vulkan instance extensions.
-void print_instance_extensions();
-
-// Note that device layers are deprecated.
-
-/// @brief Print all available Vulkan device extensions.
-/// @param gpu The regarded gpu.
-void print_device_extensions(VkPhysicalDevice gpu);
-
-/// @brief Print all supported surface capabilities of a given combination of gpu and surface.
-/// @param gpu The regarded gpu.
-/// @param surface The regarded surface.
-void print_surface_capabilities(VkPhysicalDevice gpu, VkSurfaceKHR surface);
-
-/// @brief Print all supported surface formats of a given combination of gpu and surface.
-/// @param gpu The regarded gpu.
-/// @param surface The regarded Vulkan window surface.
-void print_supported_surface_formats(VkPhysicalDevice gpu, VkSurfaceKHR surface);
-
-/// @brief Print all available presentation modes.
-/// @param gpu The regarded gpu.
-/// @param surface The regarded surface.
-void print_presentation_modes(VkPhysicalDevice gpu, VkSurfaceKHR surface);
-
-/// @brief Print information about the specified gpu.
-/// @param gpu The regarded gpu.
-void print_physical_device_info(VkPhysicalDevice gpu);
-
-/// @brief Print information about the limits of the specified gpu.
-/// @param gpu The regarded gpu.
-void print_physical_device_limits(VkPhysicalDevice gpu);
-
-/// @brief Print information about the sparse properties of the specified gpu.
-/// @param gpu The regarded gpu.
-void print_physical_device_sparse_properties(VkPhysicalDevice gpu);
-
-/// @brief Print information about the features of the gpu.
-/// @param gpu The regarded gpu.
-void print_physical_device_features(VkPhysicalDevice gpu);
-
-/// @brief Print information about the memory properties of a specified gpu.
-/// @param gpu The regarded gpu.
-void print_physical_device_memory_properties(VkPhysicalDevice gpu);
-
-/// @brief List up all available gpus.
-/// @param instance The instance of Vulkan.
-/// @param surface The regarded Vulkan window surface.
-void print_all_physical_devices(VkInstance instance, VkSurfaceKHR surface);
-
-} // namespace inexor::vulkan_renderer::vk_tools
diff --git a/include/inexor/vulkan-renderer/vk_tools/representation.hpp b/include/inexor/vulkan-renderer/vk_tools/representation.hpp
index 8d9df60c0..647496976 100644
--- a/include/inexor/vulkan-renderer/vk_tools/representation.hpp
+++ b/include/inexor/vulkan-renderer/vk_tools/representation.hpp
@@ -7,8 +7,8 @@
namespace inexor::vulkan_renderer::vk_tools {
/// @brief This function returns a textual representation of the vulkan object T.
-template
-[[nodiscard]] std::string_view as_string(T);
+template
+[[nodiscard]] std::string_view as_string(VulkanObjectType);
/// Get a feature description of a ``VkBool32`` value in the ``VkPhysicalDeviceFeatures`` struct by index.
/// @param index The index of the ``VkBool32`` value in the ``VkPhysicalDeviceFeatures`` struct.
@@ -16,6 +16,14 @@ template
/// @return A feature description
[[nodiscard]] std::string_view get_device_feature_description(std::size_t index);
+/// This template allows us to convert a template parameter name into a VkObjectType
+/// @note We have to specify a specialization for every Vulkan object type!
+/// As far as we know, there is no other easy way to do this in C++.
+/// @tparam VulkanObjectType The Vulkan object type as template parameter, for examplke VkFence
+/// @return The VkObjectType of the template parameter, for the above mentioned example ``VK_OBJECT_TYPE_FENCE``
+template