sandbox
Loading...
Searching...
No Matches
render_graph.hpp
1#ifndef LIBSBX_GRAPHICS_RENDER_GRAPH_HPP_
2#define LIBSBX_GRAPHICS_RENDER_GRAPH_HPP_
3
4#include <vector>
5#include <variant>
6#include <string>
7#include <memory>
8#include <unordered_map>
9#include <optional>
10#include <functional>
11
12#include <vulkan/vulkan.h>
13
14#include <libsbx/utility/enum.hpp>
15#include <libsbx/utility/exception.hpp>
16#include <libsbx/utility/hashed_string.hpp>
17#include <libsbx/utility/logger.hpp>
18
19#include <libsbx/math/color.hpp>
20
21#include <libsbx/memory/observer_ptr.hpp>
22
23#include <libsbx/graphics/viewport.hpp>
24#include <libsbx/graphics/draw_list.hpp>
25
26#include <libsbx/graphics/images/image2d.hpp>
27#include <libsbx/graphics/images/depth_image.hpp>
28
29#include <libsbx/graphics/render_pass/swapchain.hpp>
30
31namespace sbx::graphics {
32
33enum class format : std::int32_t {
34 undefined = VK_FORMAT_UNDEFINED,
35 r16_sfloat = VK_FORMAT_R16_SFLOAT,
36 r32_sfloat = VK_FORMAT_R32_SFLOAT,
37 r32_uint = VK_FORMAT_R32_UINT,
38 r64_uint = VK_FORMAT_R64_UINT,
39 r32g32_sfloat = VK_FORMAT_R32G32_SFLOAT,
40 r32g32_uint = VK_FORMAT_R32G32_UINT,
41 r8g8b8a8_unorm = VK_FORMAT_R8G8B8A8_UNORM,
42 b8g8r8a8_srgb = VK_FORMAT_B8G8R8A8_SRGB,
43 r16g16b16a16_sfloat = VK_FORMAT_R16G16B16A16_SFLOAT,
44 r32g32b32a32_sfloat = VK_FORMAT_R32G32B32A32_SFLOAT
45}; // enum class format
46
47enum class address_mode : std::uint32_t {
48 repeat = VK_SAMPLER_ADDRESS_MODE_REPEAT,
49 clamp_to_edge = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE
50}; // enum class address_mode
51
52enum class blend_factor : std::uint32_t {
53 zero = VK_BLEND_FACTOR_ZERO,
54 one = VK_BLEND_FACTOR_ONE,
55 source_color = VK_BLEND_FACTOR_SRC_COLOR,
56 one_minus_source_color = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
57 source_alpha = VK_BLEND_FACTOR_SRC_ALPHA,
58 one_minus_source_alpha = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
59 destination_color = VK_BLEND_FACTOR_DST_COLOR,
60 one_minus_destination_color = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
61 destination_alpha = VK_BLEND_FACTOR_DST_ALPHA,
62 one_minus_destination_alpha = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
63 constant_color = VK_BLEND_FACTOR_CONSTANT_COLOR,
64 one_minus_constant_color = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
65 constant_alpha = VK_BLEND_FACTOR_CONSTANT_ALPHA,
66 one_minus_constant_alpha = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
67 source_alpha_saturate = VK_BLEND_FACTOR_SRC_ALPHA_SATURATE
68}; // enum class blend_factor
69
70enum class blend_operation : std::uint32_t {
71 add = VK_BLEND_OP_ADD,
72 subtract = VK_BLEND_OP_SUBTRACT,
73 reverse_subtract = VK_BLEND_OP_REVERSE_SUBTRACT,
74 min = VK_BLEND_OP_MIN,
75 max = VK_BLEND_OP_MAX
76}; // enum class blend_operation
77
78enum class color_component : std::uint32_t {
79 r = VK_COLOR_COMPONENT_R_BIT,
80 g = VK_COLOR_COMPONENT_G_BIT,
81 b = VK_COLOR_COMPONENT_B_BIT,
82 a = VK_COLOR_COMPONENT_A_BIT
83}; // enum class color_component
84
85inline constexpr auto operator|(const color_component lhs, const color_component rhs) noexcept -> color_component {
86 return static_cast<color_component>(static_cast<std::underlying_type_t<color_component>>(lhs) | static_cast<std::underlying_type_t<color_component>>(rhs));
87}
88
89inline constexpr auto operator&(const color_component lhs, const color_component rhs) noexcept -> color_component {
90 return static_cast<color_component>(static_cast<std::underlying_type_t<color_component>>(lhs) & static_cast<std::underlying_type_t<color_component>>(rhs));
91}
92
94 blend_factor color_source{blend_factor::source_alpha};
95 blend_factor color_destination{blend_factor::one_minus_source_alpha};
96 blend_operation color_operation{blend_operation::add};
97 blend_factor alpha_source{blend_factor::one};
98 blend_factor alpha_destination{blend_factor::zero};
99 blend_operation alpha_operation{blend_operation::add};
100 color_component color_write_mask{color_component::r | color_component::g | color_component::b | color_component::a};
101}; // struct blend_state
102
104
105public:
106
107 enum class type {
108 image,
109 depth,
110 storage,
112 }; // enum class type
113
114 attachment(const utility::hashed_string& name, type type, const math::color& clear_color = math::color::black(), const format format = format::r8g8b8a8_unorm, const graphics::blend_state& blend_state = graphics::blend_state{}, const address_mode address_mode = address_mode::repeat) noexcept;
115
116 attachment(const utility::hashed_string& name, type type, const math::color& clear_color, const format format, const address_mode address_mode) noexcept;
117
118 auto name() const noexcept -> const utility::hashed_string&;
119
120 auto image_type() const noexcept -> type;
121
122 auto format() const noexcept -> graphics::format;
123
124 auto address_mode() const noexcept -> graphics::address_mode;
125
126 auto clear_color() const noexcept -> const math::color&;
127
128 auto blend_state() const noexcept -> const graphics::blend_state&;
129
130private:
131
133 type _type;
134 math::color _clear_color;
135 graphics::format _format;
136 graphics::address_mode _address_mode;
137 graphics::blend_state _blend_state;
138
139}; // class attachment
140
141namespace detail {
142
144
145 friend class graphics_pass;
146 friend class graph_builder;
147
148public:
149
150 graphics_node(const utility::hashed_string& name, const viewport& viewport = viewport::window());
151
152private:
153
155
156 viewport _viewport;
157 render_area _render_area;
158
159 std::vector<utility::hashed_string> _inputs;
160 std::vector<attachment> _outputs;
161
162}; // class graphics_node
163
165
166 friend class compute_pass;
167 friend class graph_builder;
168
169public:
170
172
173private:
174
176
177}; // class compute_node
178
180
181 friend class graph_builder;
182 friend class graphics_pass;
183
184public:
185
186 template<typename Type, typename... Args>
187 auto emplace_back(Args&&... args) -> Type&;
188
189 auto reserve(const std::size_t graphics, const std::size_t compute) -> void;
190
191private:
192
193 std::vector<graphics_node> _graphics_nodes;
194 std::vector<compute_node> _compute_nodes;
195 std::unordered_map<utility::hashed_string, std::unique_ptr<graphics::draw_list>> _draw_lists;
196
197}; // class graph_base
198
200
201 friend class context;
202
203public:
204
205 template<typename... Names>
206 requires (... && (std::is_same_v<std::remove_cvref_t<Names>, utility::hashed_string> || std::is_constructible_v<utility::hashed_string, Names>))
207 auto uses(Names&&... names) -> void;
208
209 template<typename... Args>
210 requires (std::is_constructible_v<attachment, Args...>)
211 auto produces(Args&&... args) -> void;
212
213 // template<typename Type, typename... Args>
214 // requires (std::is_constructible_v<Type, Args...>)
215 // auto add_draw_list(const utility::hashed_string& name, Args&&... args) -> Type&;
216
217 auto name() const -> const utility::hashed_string&;
218
219 auto inputs() const -> const std::vector<utility::hashed_string>& ;
220
221 auto outputs() const -> const std::vector<attachment>&;
222
223 template<typename Type>
224 requires (std::is_base_of_v<draw_list, Type>)
225 auto draw_list(const utility::hashed_string& name) -> Type& {
226 if (auto entry = _graph._draw_lists.find(name); entry != _graph._draw_lists.end()) {
227 return *static_cast<Type*>(entry->second.get());
228 }
229
230 throw utility::runtime_error{"Draw list with name '{}' not found in graphics pass '{}'", name.str(), _node._name.str()};
231 }
232
233private:
234
236
237 graph_base& _graph;
238 graphics_node& _node;
239
240}; // class graphics_pass
241
243
244 friend class context;
245
246public:
247
248private:
249
251
252 compute_node& _node;
253
254}; // class compute_pass
255
256class context {
257
258 friend class graph_builder;
259
260public:
261
262 auto graphics_pass(const utility::hashed_string& name, const viewport& viewport = viewport::window()) -> detail::graphics_pass;
263
265
266private:
267
268 context(graph_base& graph);
269
270 graph_base& _graph;
271
272}; // class context
273
276 VkImageLayout old_layout;
277 VkImageLayout new_layout;
278}; // struct transition_instruction
279
282 std::vector<utility::hashed_string> attachments;
283}; // struct pass_instruction
284
285using instruction = std::variant<transition_instruction, pass_instruction>;
286
287template<typename... Callables>
288struct overload : Callables... {
289 using Callables::operator()...;
290};
291
292// deduction guide
293template<typename... Callables>
294overload(Callables...) -> overload<Callables...>;
295
297
298public:
299
301
302 virtual ~graph_builder() {
303 _clear_all_attachments();
304 }
305
306 template <typename Callable>
307 requires (std::is_invocable_r_v<graphics_pass, Callable, context&>)
308 auto emplace(Callable&& callable) -> graphics_pass;
309
310 template <typename Callable>
311 requires (std::is_invocable_r_v<compute_pass, Callable, context&>)
312 auto emplace(Callable&& callable) -> compute_pass;
313
314 template<typename... Callables>
315 requires (sizeof...(Callables) > 1u)
316 auto emplace(Callables&&... callables) -> decltype(auto);
317
318 template<typename Type, typename... Args>
319 requires (std::is_constructible_v<Type, Args...>)
320 auto add_draw_list(const utility::hashed_string& name, Args&&... args) -> Type& {
321 auto result = _graph._draw_lists.emplace(name, std::make_unique<Type>(std::forward<Args>(args)...));
322 return *static_cast<Type*>(result.first->second.get());
323 }
324
325 auto build() -> void;
326
327 auto resize(const viewport::type flags) -> void;
328
329 auto attachment(const std::string& name) const -> const descriptor&;
330
331 template<typename Callable>
332 auto execute(command_buffer& command_buffer, const swapchain& swapchain, Callable&& callable) -> void {
333 for (auto& [key, draw_list] : _graph._draw_lists) {
334 draw_list->clear();
335 draw_list->update();
336 }
337
338 // Reset clear states so we can correctly set loadOp
339 for (auto& [key, state] : _attachment_states) {
340 state.is_first_use = true;
341 }
342
343 for (const auto& instruction : _instructions) {
344 std::visit(overload{
345 [this, &command_buffer, &swapchain](const transition_instruction& instruction) {
346 auto& state = _attachment_states.at(instruction.attachment);
347
348 if (state.type == attachment::type::swapchain) {
349 image::transition_image_layout(command_buffer, swapchain.image(swapchain.active_image_index()), swapchain.formt(), instruction.old_layout, instruction.new_layout, VK_IMAGE_ASPECT_COLOR_BIT, 1, 0, 1, 0);
350 } else {
351 image::transition_image_layout(command_buffer, state.image, state.format, instruction.old_layout, instruction.new_layout, (state.type == attachment::type::depth) ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) : VK_IMAGE_ASPECT_COLOR_BIT, 1, 0, 1, 0);
352 }
353 },
354 [this, &command_buffer, &swapchain, &callable](const pass_instruction& instruction) {
355 const auto& area = _pass_render_areas[instruction.node._name];
356
357 const auto& offset = area.offset();
358 const auto& extent = area.extent();
359
360 auto render_area = VkRect2D{};
361 render_area.offset = VkOffset2D{offset.x(), offset.y()};
362 render_area.extent = VkExtent2D{extent.x(), extent.y()};
363
364 auto viewport = VkViewport{};
365 viewport.x = 0.0f;
366 viewport.y = 0.0f;
367 viewport.width = static_cast<std::float_t>(render_area.extent.width);
368 viewport.height = static_cast<std::float_t>(render_area.extent.height);
369 viewport.minDepth = 0.0f;
370 viewport.maxDepth = 1.0f;
371
372 command_buffer.set_viewport(viewport);
373
374 auto scissor = VkRect2D{};
375 scissor.offset = render_area.offset;
376 scissor.extent = render_area.extent;
377
378 command_buffer.set_scissor(scissor);
379
380 auto color_attachments = std::vector<VkRenderingAttachmentInfo>{};
381 auto depth_attachment = std::optional<VkRenderingAttachmentInfo>{};
382
383 for (const auto& attachment : instruction.attachments) {
384 const auto& clear_value = _clear_values[attachment];
385 auto& state = _attachment_states[attachment];
386
387 auto load_operation = VK_ATTACHMENT_LOAD_OP_LOAD;
388
389 if (state.is_first_use) {
390 load_operation = VK_ATTACHMENT_LOAD_OP_CLEAR;
391 state.is_first_use = false;
392 }
393
394 if (state.type == attachment::type::image) {
395 auto rendering_attachment_info = VkRenderingAttachmentInfo{};
396 rendering_attachment_info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
397 rendering_attachment_info.imageView = state.view;
398 rendering_attachment_info.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
399 rendering_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE;
400 rendering_attachment_info.loadOp = load_operation;
401 rendering_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
402 rendering_attachment_info.clearValue = clear_value;
403
404 color_attachments.push_back(rendering_attachment_info);
405 } else if (state.type == attachment::type::depth) {
406 auto rendering_attachment_info = VkRenderingAttachmentInfo{};
407 rendering_attachment_info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
408 rendering_attachment_info.imageView = state.view;
409 rendering_attachment_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
410 rendering_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE;
411 rendering_attachment_info.loadOp = load_operation;
412 rendering_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
413 rendering_attachment_info.clearValue = clear_value;
414
415 depth_attachment = rendering_attachment_info;
416 } else if (state.type == attachment::type::swapchain) {
417 auto rendering_attachment_info = VkRenderingAttachmentInfo{};
418 rendering_attachment_info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
419 rendering_attachment_info.imageView = swapchain.image_view(swapchain.active_image_index());
420 rendering_attachment_info.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
421 rendering_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE;
422 rendering_attachment_info.loadOp = load_operation;
423 rendering_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
424 rendering_attachment_info.clearValue = clear_value;
425
426 color_attachments.push_back(rendering_attachment_info);
427 }
428 }
429
430 auto rendering_info = VkRenderingInfo{};
431 rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
432 rendering_info.renderArea = render_area;
433 rendering_info.layerCount = 1;
434 rendering_info.colorAttachmentCount = static_cast<std::uint32_t>(color_attachments.size());
435 rendering_info.pColorAttachments = color_attachments.data();
436 rendering_info.pDepthAttachment = depth_attachment.has_value() ? &depth_attachment.value() : nullptr;
437 rendering_info.pStencilAttachment = depth_attachment.has_value() ? &depth_attachment.value() : nullptr;
438
439 command_buffer.begin_rendering(rendering_info);
440
441 std::invoke(callable, instruction.node._name);
442
443 command_buffer.end_rendering();
444 }
445 }, instruction);
446 }
447 }
448
449private:
450
451 struct attachment_state {
452 VkImage image;
453 VkImageView view;
454 VkImageLayout current_layout = VK_IMAGE_LAYOUT_UNDEFINED;
455 VkFormat format;
456 VkExtent2D extent;
457 attachment::type type;
458 bool is_first_use;
459 }; // struct attachment_state
460
461 auto _update_viewports() -> void;
462
463 auto _clear_all_attachments() -> void;
464
465 auto _clear_attachments(const viewport::type flags) -> void;
466
467 auto _create_attachments(const viewport::type flags, const graphics_node& node) -> void;
468
469 graph_base& _graph;
470
471 std::unordered_map<utility::hashed_string, image2d_handle> _color_images;
472 std::unordered_map<utility::hashed_string, depth_image_handle> _depth_images;
473 std::unordered_map<utility::hashed_string, VkClearValue> _clear_values;
474
475 std::vector<instruction> _instructions;
476
477 std::unordered_map<utility::hashed_string, attachment_state> _attachment_states;
478
479 std::unordered_map<utility::hashed_string, render_area> _pass_render_areas;
480
481}; // class graph_builder
482
483} // namespace detail
484
486
488
489public:
490
493 using context = detail::context;
494
495 render_graph();
496
497 ~render_graph() override = default;
498
499private:
500
501 detail::graph_base _graph;
502
503}; // class render_graph
504
505} // namespace sbx::graphics
506
507#include <libsbx/graphics/render_graph.ipp>
508
509#endif // LIBSBX_GRAPHICS_RENDER_GRAPH_HPP_
Definition: tests.cpp:5
Definition: render_graph.hpp:103
Definition: command_buffer.hpp:14
Definition: descriptor.hpp:37
Definition: render_graph.hpp:164
Definition: render_graph.hpp:242
Definition: render_graph.hpp:256
Definition: render_graph.hpp:179
Definition: render_graph.hpp:296
Definition: render_graph.hpp:143
Definition: render_graph.hpp:199
Definition: draw_list.hpp:24
Definition: image.hpp:20
Definition: viewport.hpp:98
Definition: render_graph.hpp:485
Definition: swapchain.hpp:12
Definition: viewport.hpp:12
Definition: color.hpp:14
Definition: hashed_string.hpp:15
Definition: render_graph.hpp:93
Definition: render_graph.hpp:288
Definition: render_graph.hpp:280
Definition: render_graph.hpp:274
Definition: exception.hpp:17