sandbox
Loading...
Searching...
No Matches
graphics_module.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_GRAPHICS_GRAPHICS_MODULE_HPP_
3#define LIBSBX_GRAPHICS_GRAPHICS_MODULE_HPP_
4
5#include <memory>
6#include <unordered_map>
7#include <vector>
8#include <typeindex>
9
10#include <libsbx/core/module.hpp>
12
13#include <libsbx/math/uuid.hpp>
14
15#include <libsbx/memory/tracking_allocator.hpp>
16
17#include <libsbx/devices/devices_module.hpp>
18
20#include <libsbx/utility/concepts.hpp>
21
22#include <libsbx/signals/signal.hpp>
23
24#include <libsbx/graphics/devices/instance.hpp>
25#include <libsbx/graphics/devices/physical_device.hpp>
26#include <libsbx/graphics/devices/logical_device.hpp>
27#include <libsbx/graphics/devices/allocator.hpp>
28#include <libsbx/graphics/devices/surface.hpp>
29#include <libsbx/graphics/devices/query_pool.hpp>
30
31#include <libsbx/graphics/commands/command_pool.hpp>
32#include <libsbx/graphics/commands/command_buffer.hpp>
33
34#include <libsbx/graphics/render_pass/swapchain.hpp>
35
36#include <libsbx/graphics/pipeline/pipeline.hpp>
37#include <libsbx/graphics/pipeline/shader.hpp>
38#include <libsbx/graphics/pipeline/graphics_pipeline.hpp>
39#include <libsbx/graphics/pipeline/compute_pipeline.hpp>
40#include <libsbx/graphics/pipeline/compiler.hpp>
41
42#include <libsbx/graphics/buffers/buffer.hpp>
43#include <libsbx/graphics/buffers/storage_buffer.hpp>
44
45#include <libsbx/graphics/images/image2d.hpp>
46#include <libsbx/graphics/images/cube_image.hpp>
47#include <libsbx/graphics/images/sampler_state.hpp>
48#include <libsbx/graphics/images/depth_image.hpp>
49
50#include <libsbx/graphics/renderer.hpp>
51
52#include <libsbx/graphics/resource_storage.hpp>
53
54namespace sbx::graphics {
55
61auto validate(VkResult result) -> void;
62
63template<typename VkEnum, typename Enum>
64requires ((std::is_enum_v<VkEnum> || std::is_same_v<VkEnum, VkFlags>) && std::is_enum_v<Enum> && std::is_same_v<std::underlying_type_t<Enum>, std::int32_t>)
65constexpr auto to_vk_enum(Enum value) -> VkEnum {
66 return static_cast<VkEnum>(value);
67}
68
74class graphics_module final : public core::module<graphics_module> {
75
76 inline static const auto is_registered = register_module(stage::rendering, dependencies<devices::devices_module>{});
77
78 inline static constexpr auto max_deletion_queue_size = std::size_t{16u};
79
80public:
81
83
84 ~graphics_module() override;
85
86 auto update() -> void override;
87
88 auto instance() -> instance&;
89
91
93
94 auto surface() -> surface&;
95
96 auto command_pool(VkQueueFlagBits queue_type = VK_QUEUE_GRAPHICS_BIT, const std::thread::id& thread_id = std::this_thread::get_id()) -> const std::shared_ptr<command_pool>&;
97
98 auto swapchain() -> swapchain&;
99
100 template<utility::implements<renderer> Renderer, typename... Args>
101 requires (std::is_constructible_v<Renderer, Args...>)
102 auto set_renderer(Args&&... args) -> Renderer& {
103 _renderer = std::make_unique<Renderer>(std::forward<Args>(args)...);
104 _recreate_swapchain();
105
106 return *static_cast<Renderer*>(_renderer.get());
107 }
108
109 // auto render_stage(const pipeline::stage& stage) -> graphics::render_stage&;
110
111 auto current_frame() const noexcept -> std::uint32_t {
112 return _current_frame;
113 }
114
115 auto attachment(const std::string& name) const -> const descriptor&;
116
117 template<typename Type, typename... Args>
118 requires (std::is_constructible_v<Type, Args...>)
119 auto add_resource(Args&&... args) -> resource_handle<Type> {
120 return _storage<Type>().emplace(std::forward<Args>(args)...);
121 }
122
123 template<typename Type>
124 auto get_resource(const resource_handle<Type>& handle) -> Type& {
125 return _storage<Type>().get(handle);
126 }
127
128 template<typename Type>
129 auto get_resource(const resource_handle<Type>& handle) const -> const Type& {
130 return _storage<Type>().get(handle);
131 }
132
133 template<typename Type>
134 auto remove_resource(const resource_handle<Type>& handle) -> void {
135 return _storage<Type>().remove(handle);
136 }
137
138 auto allocator() const noexcept -> const graphics::allocator& {
139 return _allocator;
140 }
141
142 template<typename Type = graphics::renderer>
143 auto renderer() -> Type& {
144 return *static_cast<Type*>(_renderer.get());
145 }
146
147 template<queue::type Source, queue::type Destination, typename Type>
148 requires (std::is_same_v<Type, graphics::buffer> || std::is_same_v<Type, graphics::storage_buffer>)
149 auto transfer_ownership(const resource_handle<Type>& handle, const VkPipelineStageFlagBits2 stage = VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT) -> void {
150 auto& buffer = get_resource<Type>(handle);
151
152 _release_ownership_data.push_back(command_buffer::buffer_release_data{
153 .src_stage_mask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
154 .src_access_mask = VK_ACCESS_2_SHADER_WRITE_BIT,
155 .src_queue_family = _logical_device->queue<Source>().family(),
156 .dst_queue_family = _logical_device->queue<Destination>().family(),
157 .buffer = buffer
158 });
159
160 _acquire_ownership_data.push_back(command_buffer::buffer_acquire_data{
161 .dst_stage_mask = stage,
162 .dst_access_mask = _access_mask_from_stage(stage),
163 .src_queue_family = _logical_device->queue<Source>().family(),
164 .dst_queue_family = _logical_device->queue<Destination>().family(),
165 .buffer = buffer
166 });
167 }
168
169 template<typename Callable>
170 requires (std::is_invocable_r_v<math::vector2u, Callable>)
171 auto set_dynamic_size_callback(Callable&& callback) -> void {
172 _dynamic_size_callback = std::forward<Callable>(callback);
173 }
174
175 auto viewport() const -> const math::vector2u& {
176 return _viewport;
177 }
178
179 template<typename Callable>
180 requires (std::is_invocable_v<Callable, const math::vector2u&>)
181 auto connect_on_viewport_changed(Callable&& callable) -> void {
182 _on_viewport_changed.connect(std::forward<Callable>(callable));
183 }
184
185 auto compiler() -> graphics::compiler& {
186 return _compiler;
187 }
188
189 auto gpu_timings() const -> const std::map<std::string, units::millisecond>& {
190 return _gpu_timings;
191 }
192
193 auto profile_begin(graphics::command_buffer& cmd, const std::string& name) -> void {
194 auto frame_base = _current_frame * max_queries_per_frame;
195 auto scope_index = _get_scope_index(name);
196
197 _query_pool.write_timestamp(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, frame_base + (scope_index * 2));
198 }
199
200 auto profile_end(graphics::command_buffer& cmd, const std::string& name) -> void {
201 auto frame_base = _current_frame * max_queries_per_frame;
202 auto scope_index = _get_scope_index(name);
203
204 _query_pool.write_timestamp(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frame_base + (scope_index * 2) + 1);
205
206 _per_frame_data[_current_frame].active_scopes.push_back(name);
207 }
208
209private:
210
211 static constexpr auto _access_mask_from_stage(VkPipelineStageFlagBits2 stage) -> VkAccessFlagBits2 {
212 switch (stage) {
213 case VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT: {
214 return VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT;
215 }
216 case VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT:
217 case VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT:
218 case VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT:
219 case VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_EXT:
220 case VK_PIPELINE_STAGE_2_TASK_SHADER_BIT_EXT: {
221 return VK_ACCESS_2_SHADER_READ_BIT;
222 }
223 case VK_PIPELINE_STAGE_2_TRANSFER_BIT: {
224 return VK_ACCESS_2_TRANSFER_READ_BIT;
225 }
226 default: {
227 return VK_ACCESS_2_MEMORY_READ_BIT;
228 }
229 }
230 }
231
232 auto _reset_render_stages() -> void;
233
234 auto _recreate_viewport() -> void;
235
236 auto _recreate_swapchain() -> void;
237
238 auto _recreate_per_frame_data() -> void;
239
240 auto _recreate_per_image_data() -> void;
241
242 auto _recreate_command_buffers() -> void;
243
244 auto _recreate_attachments() -> void;
245
246 struct per_frame_data {
247 // graphics
248 VkSemaphore image_available_semaphore{nullptr};
249 VkFence graphics_in_flight_fence{nullptr};
250 // compute
251 VkSemaphore compute_finished_semaphore{nullptr};
252 VkFence compute_in_flight_fence{nullptr};
253
254 // general
255 std::vector<std::string> active_scopes;
256 }; // struct per_frame_data
257
258 struct per_image_data {
259 VkSemaphore render_finished_semaphore{nullptr};
260 }; // struct per_image_data
261
262 struct command_pool_key {
263 VkQueueFlagBits queue_type;
264 std::thread::id thread_id;
265 }; // struct command_pool_key
266
267 struct command_pool_key_hash {
268 auto operator()(const command_pool_key& key) const noexcept -> std::size_t {
269 auto hast = std::size_t{0};
270 utility::hash_combine(hast, key.queue_type, key.thread_id);
271 return hast;
272 }
273 }; // struct command_pool_key_hash
274
275 struct command_pool_key_equality {
276 auto operator()(const command_pool_key& lhs, const command_pool_key& rhs) const noexcept -> bool {
277 return lhs.queue_type == rhs.queue_type && lhs.thread_id == rhs.thread_id;
278 }
279 }; // struct command_pool_key_equal
280
281
282 static_assert(std::is_class_v<graphics::graphics_pipeline>, "graphics_pipeline is not a class in sbx::graphics");
283
284 template<typename Type>
285 auto _storage() -> resource_storage<Type>& {
286 if constexpr (std::is_same_v<Type, shader>) {
287 return _shaders;
288 } else if constexpr (std::is_same_v<Type, graphics_pipeline>) {
289 return _graphics_pipelines;
290 } else if constexpr (std::is_same_v<Type, compute_pipeline>) {
291 return _compute_pipelines;
292 } else if constexpr (std::is_same_v<Type, buffer>) {
293 return _buffers;
294 } else if constexpr (std::is_same_v<Type, storage_buffer>) {
295 return _storage_buffers;
296 } else if constexpr (std::is_same_v<Type, uniform_buffer>) {
297 return _uniform_buffers;
298 } if constexpr (std::is_same_v<Type, image2d>) {
299 return _images;
300 } else if constexpr (std::is_same_v<Type, depth_image>) {
301 return _depth_images;
302 } else if constexpr (std::is_same_v<Type, cube_image>) {
303 return _cube_images;
304 } else if constexpr (std::is_same_v<Type, sampler_state>) {
305 return _sampler_states;
306 }
307
308 utility::assert_that(false, "Invalid resource type");
309 }
310
311 template<typename Type>
312 auto _storage() const -> const resource_storage<Type>& {
313 if constexpr (std::is_same_v<Type, shader>) {
314 return _shaders;
315 } else if constexpr (std::is_same_v<Type, graphics_pipeline>) {
316 return _graphics_pipelines;
317 } else if constexpr (std::is_same_v<Type, compute_pipeline>) {
318 return _compute_pipelines;
319 } else if constexpr (std::is_same_v<Type, buffer>) {
320 return _buffers;
321 } else if constexpr (std::is_same_v<Type, storage_buffer>) {
322 return _storage_buffers;
323 } else if constexpr (std::is_same_v<Type, uniform_buffer>) {
324 return _uniform_buffers;
325 } if constexpr (std::is_same_v<Type, image2d>) {
326 return _images;
327 } else if constexpr (std::is_same_v<Type, depth_image>) {
328 return _depth_images;
329 } else if constexpr (std::is_same_v<Type, cube_image>) {
330 return _cube_images;
331 } else if constexpr (std::is_same_v<Type, sampler_state>) {
332 return _sampler_states;
333 }
334
335 utility::assert_that(false, "Invalid resource type");
336 }
337
338 auto _get_scope_index(const std::string& name) -> std::uint32_t {
339 if (auto it = _scope_registry.find(name); it != _scope_registry.end()) {
340 return it->second;
341 }
342
343 auto index = static_cast<std::uint32_t>(_scope_registry.size());
344
345 if (index >= max_scopes) {
346 throw std::runtime_error("Exceeded max GPU profiling scopes");
347 }
348
349 _scope_registry[name] = index;
350
351 return index;
352 }
353
354 std::unique_ptr<graphics::instance> _instance{};
355 std::unique_ptr<graphics::physical_device> _physical_device{};
356 std::unique_ptr<graphics::logical_device> _logical_device{};
357
358 std::unordered_map<command_pool_key, std::shared_ptr<graphics::command_pool>, command_pool_key_hash, command_pool_key_equality> _command_pools{};
359
360 std::map<std::string, memory::observer_ptr<const descriptor>> _attachments{};
361
362 std::unique_ptr<graphics::surface> _surface{};
363
364 std::unique_ptr<graphics::swapchain> _swapchain{};
365
366 std::array<per_frame_data, swapchain::max_frames_in_flight> _per_frame_data{};
367 std::vector<per_image_data> _per_image_data{};
368 std::vector<graphics::command_buffer> _graphics_command_buffers;
369 std::vector<graphics::command_buffer> _compute_command_buffers;
370
371 std::unique_ptr<graphics::renderer> _renderer{};
372
383
384 graphics::allocator _allocator;
385 graphics::query_pool _query_pool;
386
387 graphics::compiler _compiler;
388
389 std::vector<command_buffer::buffer_acquire_data> _acquire_ownership_data;
390 std::vector<command_buffer::buffer_release_data> _release_ownership_data;
391
392 std::uint32_t _current_frame{};
393 bool _is_framebuffer_resized{};
394 bool _is_viewport_resized{};
395
396 math::vector2u _viewport{};
397
398 signals::signal<const math::vector2u&> _on_viewport_changed;
399
400 core::delegate<math::vector2u()> _dynamic_size_callback;
401
402 static constexpr auto max_queries_per_frame = 256u;
403 static constexpr auto max_scopes = max_queries_per_frame / 2;
404
405 std::map<std::string, std::uint32_t> _scope_registry;
406 std::map<std::string, units::millisecond> _gpu_timings;
407
408}; // class graphics_module
409
411
412public:
413
415
416 scoped_gpu_timer(const scoped_gpu_timer&) = delete;
417
419
420 auto operator=(const scoped_gpu_timer&) -> scoped_gpu_timer& = delete;
421
422private:
423
424 command_buffer& _command_buffer;
425 std::string _name;
426
427}; // class scoped_gpu_timer
428
429} // namespace sbx::graphics
430
431#endif // LIBSBX_GRAPHICS_GRAPHICS_MODULE_HPP_
Definition: delegate.hpp:56
Definition: module.hpp:92
Definition: allocator.hpp:13
Definition: render_graph.hpp:93
Definition: buffer.hpp:21
Definition: command_buffer.hpp:15
Definition: command_pool.hpp:13
Definition: compiler.hpp:19
Definition: descriptor.hpp:38
Module for managing rendering specific tasks.
Definition: graphics_module.hpp:74
Definition: instance.hpp:11
Definition: logical_device.hpp:61
Definition: physical_device.hpp:17
Definition: query_pool.hpp:14
Definition: renderer.hpp:27
Definition: resource_storage.hpp:18
Definition: resource_storage.hpp:66
Definition: graphics_module.hpp:410
Definition: surface.hpp:15
Definition: swapchain.hpp:15
Definition: viewport.hpp:13
A vector in two-dimensional space.
Definition: vector2.hpp:28
Definition: signal.hpp:17