2#ifndef LIBSBX_GRAPHICS_GRAPHICS_MODULE_HPP_
3#define LIBSBX_GRAPHICS_GRAPHICS_MODULE_HPP_
6#include <unordered_map>
10#include <libsbx/core/module.hpp>
13#include <libsbx/math/uuid.hpp>
15#include <libsbx/memory/tracking_allocator.hpp>
17#include <libsbx/devices/devices_module.hpp>
20#include <libsbx/utility/concepts.hpp>
24#include <libsbx/signals/signal.hpp>
26#include <libsbx/filesystem/filesystem_module.hpp>
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>
35#include <libsbx/graphics/commands/command_pool.hpp>
36#include <libsbx/graphics/commands/command_buffer.hpp>
38#include <libsbx/graphics/render_pass/swapchain.hpp>
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>
46#include <libsbx/graphics/buffers/buffer.hpp>
47#include <libsbx/graphics/buffers/storage_buffer.hpp>
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>
54#include <libsbx/graphics/renderer.hpp>
55#include <libsbx/graphics/viewport_registry.hpp>
57#include <libsbx/graphics/resource_storage.hpp>
59namespace sbx::graphics {
66auto validate(VkResult result) -> void;
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);
76 std::uint32_t frames_to_live;
86 inline static const auto is_registered = register_module(stage::rendering, dependencies<devices::devices_module, filesystem::filesystem_module>{});
88 inline static constexpr auto max_deletion_queue_size = std::size_t{16u};
96 auto update() ->
void override;
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>&;
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)...);
115 _recreate_swapchain();
117 return *
static_cast<Renderer*
>(_renderer.get());
122 auto current_frame()
const noexcept -> std::uint32_t {
123 return _current_frame;
128 template<
typename Type,
typename... Args>
129 requires (std::is_constructible_v<Type, Args...>)
131 return _storage<Type>().emplace(std::forward<Args>(args)...);
134 template<
typename Type>
136 return _storage<Type>().get(handle);
139 template<
typename Type>
141 return _storage<Type>().get(handle);
144 template<
typename Type>
146 return _storage<Type>().remove(handle);
153 template<
typename Type = graphics::renderer>
155 return *
static_cast<Type*
>(_renderer.get());
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);
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(),
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(),
192 auto gpu_timings()
const ->
const std::map<std::string, units::millisecond>& {
197 auto frame_base = _current_frame * max_queries_per_frame;
198 auto scope_index = _get_scope_index(name);
200 _query_pool.write_timestamp(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, frame_base + (scope_index * 2));
204 auto frame_base = _current_frame * max_queries_per_frame;
205 auto scope_index = _get_scope_index(name);
207 _query_pool.write_timestamp(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frame_base + (scope_index * 2) + 1);
209 _per_frame_data[_current_frame].active_scopes.push_back(name);
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});
220 static constexpr auto _access_mask_from_stage(VkPipelineStageFlagBits2 stage) -> VkAccessFlagBits2 {
222 case VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT: {
223 return VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT;
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;
232 case VK_PIPELINE_STAGE_2_TRANSFER_BIT: {
233 return VK_ACCESS_2_TRANSFER_READ_BIT;
236 return VK_ACCESS_2_MEMORY_READ_BIT;
241 auto _reset_render_stages() -> void;
243 auto _recreate_swapchain() -> void;
245 auto _recreate_per_frame_data() -> void;
247 auto _recreate_per_image_data() -> void;
249 auto _recreate_command_buffers() -> void;
251 auto _recreate_attachments() -> void;
253 struct per_frame_data {
255 VkSemaphore image_available_semaphore{
nullptr};
256 VkFence graphics_in_flight_fence{
nullptr};
258 VkSemaphore compute_finished_semaphore{
nullptr};
259 VkFence compute_in_flight_fence{
nullptr};
262 std::vector<std::string> active_scopes;
265 struct per_image_data {
266 VkSemaphore render_finished_semaphore{
nullptr};
269 struct command_pool_key {
270 VkQueueFlagBits queue_type;
271 std::thread::id thread_id;
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);
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;
289 static_assert(std::is_class_v<graphics::graphics_pipeline>,
"graphics_pipeline is not a class in sbx::graphics");
291 template<
typename Type>
293 if constexpr (std::is_same_v<Type, shader>) {
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>) {
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>) {
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>) {
311 }
else if constexpr (std::is_same_v<Type, sampler_state>) {
312 return _sampler_states;
314 static_assert(!
sizeof(Type),
"Invalid resource type");
318 template<
typename Type>
320 if constexpr (std::is_same_v<Type, shader>) {
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>) {
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>) {
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>) {
338 }
else if constexpr (std::is_same_v<Type, sampler_state>) {
339 return _sampler_states;
341 static_assert(!
sizeof(Type),
"Invalid resource type");
345 auto _get_scope_index(
const std::string& name) -> std::uint32_t {
346 if (
auto it = _scope_registry.find(name); it != _scope_registry.end()) {
350 auto index =
static_cast<std::uint32_t
>(_scope_registry.size());
352 if (index >= max_scopes) {
353 throw std::runtime_error(
"Exceeded max GPU profiling scopes");
356 _scope_registry[name] = index;
361 auto _poll_deletion_queue() -> void;
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{};
367 std::unordered_map<command_pool_key, std::shared_ptr<graphics::command_pool>, command_pool_key_hash, command_pool_key_equality> _command_pools{};
369 std::map<std::string, memory::observer_ptr<const descriptor>> _attachments{};
371 std::unique_ptr<graphics::surface> _surface{};
373 std::unique_ptr<graphics::swapchain> _swapchain{};
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;
380 std::unique_ptr<graphics::renderer> _renderer{};
398 std::vector<command_buffer::buffer_acquire_data> _acquire_ownership_data;
399 std::vector<command_buffer::buffer_release_data> _release_ownership_data;
401 std::uint32_t _current_frame{};
402 bool _is_framebuffer_resized{};
406 static constexpr auto max_queries_per_frame = 256u;
407 static constexpr auto max_scopes = max_queries_per_frame / 2;
409 std::map<std::string, std::uint32_t> _scope_registry;
410 std::map<std::string, units::millisecond> _gpu_timings;
412 std::vector<deferred_deletion> _deletion_queue;
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: command_buffer.hpp:34
Definition: command_buffer.hpp:44
Definition: graphics_module.hpp:74