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