Skip to content

Commit

Permalink
[WIP] Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
IAmNotHanni committed Jul 13, 2024
1 parent 669c13e commit 838523f
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 285 deletions.
57 changes: 20 additions & 37 deletions include/inexor/vulkan-renderer/render-graph/graphics_pass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ namespace inexor::vulkan_renderer::render_graph {
// Forward declaration
class RenderGraph;

// These using instructions make our life easier
// TODO: The second part of the pair is std::optional because not all buffers are read in some specific shader stage(?)
using BufferRead = std::pair<std::weak_ptr<Buffer>, std::optional<VkShaderStageFlagBits>>;
using BufferReads = std::vector<BufferRead>;
using TextureRead = std::pair<std::weak_ptr<Texture>, std::optional<VkShaderStageFlagBits>>;
using TextureReads = std::vector<TextureRead>;
using TextureWrites = std::vector<std::weak_ptr<Texture>>;

using wrapper::descriptors::DescriptorSetLayout;

/// A wrapper for graphics passes inside of rendergraph
Expand All @@ -43,49 +35,40 @@ class GraphicsPass {
/// Add members which describe data related to graphics passes here
std::function<void(const CommandBuffer &)> m_on_record{[](auto &) {}};

/// The buffers the graphics passes reads from
/// If the buffer's ``BufferType`` is ``UNIFORM_BUFFER``, a value for the shader stage flag must be specified,
/// because uniform buffers can be read from vertex or fragment stage bit.
BufferReads m_buffer_reads;
/// The textures the graphics passes reads from
TextureReads m_texture_reads;
/// The textures the graphics passes writes to
TextureWrites m_texture_writes;
/// Enable MSAA for this pass
bool m_enable_msaa{false};
/// Clear the color attachment
bool m_clear_color_attachment{false};
/// Clear the stencil attachment
bool m_clear_stencil_attachment{false};

/// The vertex buffers (will be set by the rendergraph)
std::vector<VkBuffer> m_vertex_buffers;
/// The index buffer (will be set by the rendergraph)
VkBuffer m_index_buffer{VK_NULL_HANDLE};
std::weak_ptr<Texture> m_color_attachment;
std::weak_ptr<Texture> m_depth_attachment;
std::weak_ptr<Texture> m_stencil_attachment;
std::weak_ptr<Texture> m_msaa_color_attachment;
std::weak_ptr<Texture> m_msaa_depth_attachment;

/// The descriptor set layout of the pass (will be created by rendergraph)
std::unique_ptr<DescriptorSetLayout> m_descriptor_set_layout;

/// The descriptor set of the pass (will be created by rendergraph)
VkDescriptorSet m_descriptor_set{VK_NULL_HANDLE};

[[nodiscard]] bool has_index_buffer() const noexcept {
return m_index_buffer != VK_NULL_HANDLE;
}

public:
/// Default constructor
/// @param name The name of the graphics pass
/// @param buffer_reads The buffers (vertex-, index-, or uniform buffers) the graphics passes reads from
/// @param texture_reads The textures the graphics passes reads from
/// @param texture_writes The textures the graphics passes writes to
/// @param on_record The function which is called when the command buffer of the passes is being recorded
/// @param clear_screen If specified, ``VkAttachmentLoadOp`` in ``VkRenderingAttachmentInfo`` will be set to
/// ``VK_ATTACHMENT_LOAD_OP_CLEAR``, and the clear values specified here are used (``std::nullopt`` by default, in
/// which case ``VK_ATTACHMENT_LOAD_OP_LOAD`` is used)
/// @exception std::runtime_error More than one index buffer is specified
GraphicsPass(std::string name,
BufferReads buffer_reads,
TextureReads texture_reads,
TextureWrites texture_writes,
std::function<void(const CommandBuffer &)> on_record,
std::weak_ptr<Texture> m_color_attachment,
std::weak_ptr<Texture> m_depth_attachment,
std::weak_ptr<Texture> m_stencil_attachment,
std::weak_ptr<Texture> m_msaa_color_attachment,
std::weak_ptr<Texture> m_msaa_depth_attachment,
bool enable_msaa,
bool clear_color_attachment,
bool clear_stencil_attachment,
std::optional<VkClearValue> clear_values);

GraphicsPass(const GraphicsPass &) = delete;
// TODO: Fix me!
GraphicsPass(GraphicsPass &&other) noexcept;
~GraphicsPass() = default;

Expand Down
125 changes: 62 additions & 63 deletions include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@ namespace inexor::vulkan_renderer::render_graph {
/// @warning Make sure that the order or add calls for buffers and textures matches the binding order!
class GraphicsPassBuilder {
private:
/// Indicates if the screen is cleared at the beginning of this pass
std::optional<VkClearValue> m_clear_value;
/// Add members which describe data related to graphics passes here
std::function<void(const wrapper::commands::CommandBuffer &)> m_on_record;
/// Depth testing
bool m_depth_test;

/// The buffers which are read by the graphics pass
/// If the buffer's ``BufferType`` is ``UNIFORM_BUFFER``, a value for the shader stage flag must be specified,
/// because uniform buffers can be read from vertex or fragment stage bit.
BufferReads m_buffer_reads;
/// The textures the graphics pass reads from
TextureReads m_texture_reads;
/// The textures the graphics pass writes to
TextureWrites m_texture_writes;

// TODO: Merge push constant ranges into one block and put it as member here?
// TODO: Copy all data into one piece of memory and call vkCmdPushConstants only once?
void compile_push_constants();
bool m_enable_depth_test{false};
/// Multisample anti-aliasing (MSAA)
bool m_enable_msaa{false};
/// Clear the color attachment
bool m_clear_color{false};
/// Clear the stencil attachment
bool m_clear_stencil{false};
/// Indicates if the screen is cleared at the beginning of the pass
VkClearValue m_clear_value{};

// TODO: Multiple color attachments!
std::weak_ptr<Texture> m_color_attachment;
std::weak_ptr<Texture> m_depth_attachment;
std::weak_ptr<Texture> m_stencil_attachment;
std::weak_ptr<Texture> m_msaa_color_attachment;
std::weak_ptr<Texture> m_msaa_depth_attachment;

/// Reset all data of the graphics pass builder
void reset();
Expand All @@ -53,69 +53,68 @@ class GraphicsPassBuilder {
GraphicsPassBuilder &operator=(const GraphicsPassBuilder &) = delete;
GraphicsPassBuilder &operator=(GraphicsPassBuilder &&) noexcept;

/// Add a color attachment to the pass
/// @param color_attachment The color attachment
/// @param clear_color The clear color for the color attachment
/// @return A const reference to the this pointer (allowing method calls to be chained)
[[nodiscard]] auto &add_color_attachment(std::weak_ptr<Texture> color_attachment,
std::optional<VkClearColorValue> clear_color = std::nullopt) {
if (color_attachment.expired()) {
throw std::invalid_argument(
"[GraphicsPassBuilder::add_color_attachment] Error: 'color_attachment' is nullptr!");
}
m_color_attachment = color_attachment;
if (clear_color) {
m_clear_color = true;
m_clear_value.color = clear_color.value();
}
return *this;
}

/// Build the graphics pass
/// @param name The name of the graphics pass
/// @return The graphics pass that was just created
[[nodiscard]] auto build(std::string name) {
auto graphics_pass = std::make_shared<GraphicsPass>(std::move(name), std::move(m_buffer_reads),
std::move(m_texture_reads), std::move(m_texture_writes),
std::move(m_on_record), std::move(m_clear_value));
// Don't forget to reset the builder automatically before returning the graphics pass that was just created
auto graphics_pass = std::make_shared<GraphicsPass>(
std::move(name), std::move(m_on_record), std::move(m_color_attachment), std::move(m_depth_attachment),
std::move(m_stencil_attachment), std::move(m_msaa_color_attachment), std::move(m_msaa_depth_attachment),
m_enable_msaa, m_clear_color, m_clear_stencil, std::move(m_clear_value));

// Reset the builder so the builder can be re-used
reset();
// Return the graphics pass that was created
return graphics_pass;
}

// TODO: We must specify buffer reads for vertex and index buffers, but bind manually... is that good?
// TODO: std::optional<VkShaderStageFlagBits> or better default VkShaderStageFlagBits to VK_SHADER_STAGE_VERTEX_BIT?

/// Specify that the pass reads from a buffer
/// @param buffer The buffer the pass reads from
/// @param shader_stage The shader stage the buffer is read from
/// Enable depth testing for the pass
/// @param depth_buffer
/// @return A const reference to the this pointer (allowing method calls to be chained)
[[nodiscard]] auto &reads_from_buffer(std::weak_ptr<Buffer> buffer,
std::optional<VkShaderStageFlagBits> shader_stage = std::nullopt) {
if (buffer.expired()) {
throw std::invalid_argument("[GraphicsPassBuilder::reads_from_buffer] Error: buffer is nullptr!");
[[nodiscard]] auto &enable_depth_test(std::weak_ptr<Texture> depth_attachment) {
if (depth_attachment.expired()) {
throw std::invalid_argument("[GraphicsPassBuilder::enable_depth_test] Error: 'depth_buffer' is nullptr!");
}
m_buffer_reads.emplace_back(std::move(buffer), shader_stage);
m_enable_depth_test = true;
m_depth_attachment = depth_attachment;
return *this;
}

/// Specify that the pass reads from a texture
/// @param texture The texture the pass reads from
/// @param shader_stage The shader stage the texture is read from
/// Enable multisample anti-aliasing (MSAA) for the pass
/// @param sample_count The MSAA sample count
/// @param msaa_back_attachment The MSAA attachment
/// @param msaa_depth_attachment The MSAA depth attachment
/// @return A const reference to the this pointer (allowing method calls to be chained)
[[nodiscard]] auto &reads_from_texture(std::weak_ptr<Texture> texture,
std::optional<VkShaderStageFlagBits> shader_stage = std::nullopt) {
if (texture.expired()) {
throw std::invalid_argument("[GraphicsPassBuilder::reads_from_texture] Error: texture is nullptr!");
[[nodiscard]] auto &enable_msaa(VkSampleCountFlagBits sample_count,
std::weak_ptr<Texture> msaa_back_attachment,
std::weak_ptr<Texture> msaa_depth_attachment) {
if (msaa_back_attachment.expired()) {
throw std::invalid_argument("[GraphicsPassBuilder::enable_msaa] Error: 'msaa_back_buffer' is nullptr!");
}
m_texture_reads.emplace_back(std::move(texture), shader_stage);
return *this;
}

/// Specify that the pass writes to a texture
/// @param texture The texture the pass writes to
/// @return A const reference to the this pointer (allowing method calls to be chained)
[[nodiscard]] auto &writes_to_texture(std::weak_ptr<Texture> texture) {
m_texture_writes.emplace_back(texture);
return *this;
}

/// Set the clear status for the pass
/// @param clear_value The clear value for color and depth
/// @return A const reference to the this pointer (allowing method calls to be chained)
[[nodiscard]] auto &set_clear_value(VkClearValue clear_value) {
m_clear_value = clear_value;
return *this;
}

/// Enable or disable depth testing
/// @param depth_test ``true`` if depth testing is enabled for this pass
/// @return A const reference to the this pointer (allowing method calls to be chained)
[[nodiscard]] auto &set_depth_test(bool depth_test) {
m_depth_test = depth_test;
if (msaa_depth_attachment.expired()) {
throw std::invalid_argument("[GraphicsPassBuilder::enable_msaa] Error: 'msaa_depth_buffer' is nullptr!");
}
m_enable_msaa = true;
m_msaa_color_attachment = msaa_back_attachment;
m_msaa_depth_attachment = msaa_depth_attachment;
return *this;
}

Expand Down
13 changes: 7 additions & 6 deletions include/inexor/vulkan-renderer/render-graph/render_graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ class RenderGraph {

// TODO: Add @exception to documentation of other methods/code parts!

/// 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();
Expand All @@ -140,6 +143,7 @@ class RenderGraph {
/// @param cmd_buf The command buffer to record into
void create_buffers();

/// Create the descriptor set layouts
void create_descriptor_set_layouts();

/// Create the graphics passes
Expand Down Expand Up @@ -175,12 +179,12 @@ class RenderGraph {
/// @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 descriptor sets
void update_descriptor_sets();

/// Update dynamic textures
void update_textures();

/// Make sure all required resources are specified so rendergraph is ready to be compiled
void validate_render_graph();

Expand Down Expand Up @@ -219,9 +223,6 @@ class RenderGraph {
[[nodiscard]] std::shared_ptr<Buffer>
add_buffer(std::string buffer_name, BufferType buffer_type, std::function<void()> on_update);

///
void allocate_descriptor_sets();

/// Add a descriptor to rendergraph
/// @param on_create_descriptor_set_layout
/// @param on_allocate_descriptor_set
Expand Down
5 changes: 3 additions & 2 deletions include/inexor/vulkan-renderer/renderers/imgui.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class ImGuiRenderer {
/// It will be called at the beginning of set_on_update
std::function<void()> m_on_update_user_data{[]() {}};

std::weak_ptr<render_graph::Texture> m_color_attachment;

void load_font_data_from_file();

/// Customize ImGui style like text color for example
Expand All @@ -76,8 +78,7 @@ class ImGuiRenderer {
ImGuiRenderer(const Device &device,
const Swapchain &swapchain,
render_graph::RenderGraph &render_graph,
std::weak_ptr<render_graph::Texture> back_buffer,
std::weak_ptr<render_graph::Texture> depth_buffer,
std::weak_ptr<render_graph::Texture> color_attachment,
std::function<void()> on_update_user_data);

ImGuiRenderer(const ImGuiRenderer &) = delete;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class CommandBuffer {
/// @note ``begin_render_pass`` has been deprecated because of dynamic rendering (``VK_KHR_dynamic_rendering``)
/// @param rendering_info The info for dynamic rendering
/// @return A const reference to the this pointer (allowing method calls to be chained)
const CommandBuffer &begin_rendering(const VkRenderingInfo *rendering_info) const;
const CommandBuffer &begin_rendering(const VkRenderingInfo &rendering_info) const;

/// Call vkCmdBindDescriptorSets
/// @param desc_sets The descriptor set to bind
Expand Down
14 changes: 2 additions & 12 deletions include/inexor/vulkan-renderer/wrapper/pipelines/pipeline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,8 @@ class GraphicsPipeline {
GraphicsPipeline &operator=(const GraphicsPipeline &) = delete;
GraphicsPipeline &operator=(GraphicsPipeline &&) = delete;

// TODO: Make private and use friend declaration only?

[[nodiscard]] auto &descriptor_set_layouts() const {
return m_descriptor_set_layouts;
}

[[nodiscard]] auto &name() const {
return m_name;
}

[[nodiscard]] auto &push_constant_ranges() const {
return m_push_constant_ranges;
[[nodiscard]] auto pipeline_layout() const {
return m_pipeline_layout;
}
};

Expand Down
32 changes: 12 additions & 20 deletions src/vulkan-renderer/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ void Application::setup_render_graph() {
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
},
})
.set_multisampling(m_device->get_max_usable_sample_count(), 1.0f)
.add_default_color_blend_attachment()
.add_color_attachment(m_swapchain->image_format())
.set_depth_attachment_format(VK_FORMAT_D32_SFLOAT_S8_UINT)
Expand Down Expand Up @@ -533,39 +534,30 @@ void Application::setup_render_graph() {
using wrapper::commands::CommandBuffer;
m_render_graph->add_graphics_pass([&](GraphicsPassBuilder &graphics_pass_builder) {
m_octree_pass =
graphics_pass_builder
.set_clear_value({
// TODO: Define default color values for these, like COLOR::RED, COLOR::BLUE...
.color = {1.0f, 0.0f, 0.0f},
})
.set_depth_test(true)
graphics_pass_builder.add_color_attachment(m_back_buffer, VkClearColorValue{1.0f, 0.0f, 0.0f, 1.0f})
.enable_depth_test(m_depth_buffer)
.enable_msaa(m_device->get_max_usable_sample_count(), m_msaa_color, m_msaa_depth)
.set_on_record([&](const CommandBuffer &cmd_buf) {
// Record the command buffer for rendering the octree
cmd_buf
.bind_pipeline(m_octree_pipeline)
// NOTE: It's the responsibility of the programmer to bind pipelines, descriptor sets,
// and buffers manually inside of this on_record lambda! It's also the responsibility of
// the programmer to make sure that every variable captured by reference inside of this
// lambda is still in a valid state at the time of execution of the lambda!
cmd_buf.bind_pipeline(m_octree_pipeline)
.bind_descriptor_set(m_descriptor_set, m_octree_pipeline->pipeline_layout())
// TODO: Binding vertex buffer must automatically respect current swapchain img index!
.bind_vertex_buffer(m_vertex_buffer)
// TODO: Binding index buffer must automatically respect current swapchain img index!
.bind_index_buffer(m_index_buffer)
// TODO: Automatically respect current swapchain img index!
.draw_indexed(static_cast<std::uint32_t>(m_octree_indices.size()));
})
// TOOD: Even simpler API like .reads_from() and .writes_to() without templates (just overloading)?
// TODO: Since we don't bind vertex or index buffers, do we even need these calls to reads_from_buffer?
// TODO: We could imagine some API where we have vertex_buffer.bind_me() in on_record and then check
// which ones were not bound
.reads_from_buffer(m_index_buffer)
.reads_from_buffer(m_vertex_buffer)
.reads_from_buffer(m_uniform_buffer, VK_SHADER_STAGE_VERTEX_BIT)
.writes_to_texture(m_back_buffer)
.writes_to_texture(m_depth_buffer)
.build("Octree");
return m_octree_pass;
});

// TODO: We don't need to recreate the imgui overlay when swapchain is recreated, use a .recreate() method instead?
m_imgui_overlay = std::make_unique<renderers::ImGuiRenderer>(
*m_device, *m_swapchain, *m_render_graph.get(), m_back_buffer, m_msaa_color, [&]() { update_imgui_overlay(); });
// m_imgui_overlay = std::make_unique<renderers::ImGuiRenderer>(*m_device, *m_swapchain, *m_render_graph.get(),
// m_back_buffer, [&]() { update_imgui_overlay(); });

m_render_graph->compile();
}
Expand Down
Loading

0 comments on commit 838523f

Please sign in to comment.