Compare commits
	
		
			3 commits
		
	
	
		
			1ce8aed8a2
			...
			b05762c95b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b05762c95b | |||
| 6af758643e | |||
| ef2f728cd6 | 
					 19 changed files with 519 additions and 160 deletions
				
			
		| 
						 | 
					@ -66,6 +66,11 @@ public:
 | 
				
			||||||
		return m_value;
 | 
							return m_value;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto get() -> Underlying_T
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_value;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	Underlying_T m_value;
 | 
						Underlying_T m_value;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ target_link_libraries(
 | 
				
			||||||
  time
 | 
					  time
 | 
				
			||||||
  input 
 | 
					  input 
 | 
				
			||||||
  surface
 | 
					  surface
 | 
				
			||||||
 | 
					  renderer
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_test_module(libmirror 
 | 
					add_test_module(libmirror 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@
 | 
				
			||||||
#include <input/components.hpp>
 | 
					#include <input/components.hpp>
 | 
				
			||||||
#include <input/system.hpp>
 | 
					#include <input/system.hpp>
 | 
				
			||||||
#include <math/vec2.hpp>
 | 
					#include <math/vec2.hpp>
 | 
				
			||||||
 | 
					#include <renderer/system.hpp>
 | 
				
			||||||
#include <surface/events/keyboard.hpp>
 | 
					#include <surface/events/keyboard.hpp>
 | 
				
			||||||
#include <surface/events/surface.hpp>
 | 
					#include <surface/events/surface.hpp>
 | 
				
			||||||
#include <surface/system.hpp>
 | 
					#include <surface/system.hpp>
 | 
				
			||||||
| 
						 | 
					@ -126,6 +127,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		unregister_system(m_input_system);
 | 
							unregister_system(m_input_system);
 | 
				
			||||||
		unregister_system(m_surface_system);
 | 
							unregister_system(m_surface_system);
 | 
				
			||||||
 | 
							unregister_system(m_renderer_system);
 | 
				
			||||||
		unregister_system(m_mirror_system);
 | 
							unregister_system(m_mirror_system);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -189,6 +191,15 @@ public:
 | 
				
			||||||
		    quit_action_key,
 | 
							    quit_action_key,
 | 
				
			||||||
		    debug_action_keys
 | 
							    debug_action_keys
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto entity = ecs::Entity { m_editor_registry, m_window };
 | 
				
			||||||
 | 
							Ref<app::SystemStats> system_stats = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							m_renderer_system = std::make_shared<renderer::System>(renderer::System::CreateInfo {
 | 
				
			||||||
 | 
							    .registry = m_editor_registry,
 | 
				
			||||||
 | 
							    .surface_entity = entity,
 | 
				
			||||||
 | 
							    .system_stats = system_stats,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void setup_input_system()
 | 
						void setup_input_system()
 | 
				
			||||||
| 
						 | 
					@ -199,6 +210,7 @@ public:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		register_system(m_surface_system);
 | 
							register_system(m_surface_system);
 | 
				
			||||||
		register_system(m_input_system);
 | 
							register_system(m_input_system);
 | 
				
			||||||
 | 
							register_system(m_renderer_system);
 | 
				
			||||||
		register_system(m_mirror_system);
 | 
							register_system(m_mirror_system);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -209,6 +221,8 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Ref<lt::input::System> m_input_system;
 | 
						Ref<lt::input::System> m_input_system;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ref<lt::renderer::System> m_renderer_system;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Ref<MirrorSystem> m_mirror_system;
 | 
						Ref<MirrorSystem> m_mirror_system;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lt::ecs::EntityId m_window = lt::ecs::null_entity;
 | 
						lt::ecs::EntityId m_window = lt::ecs::null_entity;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,97 @@
 | 
				
			||||||
#include <renderer/system.hpp>
 | 
					#include <renderer/system.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/context.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/debug/messenger.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/renderer/pass.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/renderer/renderer.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using ::lt::assets::ShaderAsset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					class ValidationObserver
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						using Messenger = lt::renderer::vk::Messenger;
 | 
				
			||||||
 | 
						using enum Messenger::Type;
 | 
				
			||||||
 | 
						using enum Messenger::Severity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						ValidationObserver()
 | 
				
			||||||
 | 
						    : m_messenger(
 | 
				
			||||||
 | 
						          Messenger::CreateInfo {
 | 
				
			||||||
 | 
						              .severity = static_cast<Messenger::Severity>(warning | error),
 | 
				
			||||||
 | 
						              .type = lt::renderer::vk::Messenger::all_type,
 | 
				
			||||||
 | 
						              .callback = &callback,
 | 
				
			||||||
 | 
						              .user_data = &m_had_any_messages,
 | 
				
			||||||
 | 
						          }
 | 
				
			||||||
 | 
						      )
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~ValidationObserver()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto had_any_messages() const -> bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_had_any_messages;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						static void callback(
 | 
				
			||||||
 | 
						    Messenger::Severity message_severity,
 | 
				
			||||||
 | 
						    Messenger::Type message_type,
 | 
				
			||||||
 | 
						    Messenger::CallbackData_T vulkan_data,
 | 
				
			||||||
 | 
						    void *user_data
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::ignore = message_severity;
 | 
				
			||||||
 | 
							std::ignore = message_type;
 | 
				
			||||||
 | 
							for (auto idx = 0; idx < vulkan_data->objectCount; ++idx)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								auto object = vulkan_data->pObjects[idx];
 | 
				
			||||||
 | 
								std::println(
 | 
				
			||||||
 | 
								    "0x{:x}({}) = {}",
 | 
				
			||||||
 | 
								    object.objectHandle,
 | 
				
			||||||
 | 
								    string_VkObjectType(object.objectType),
 | 
				
			||||||
 | 
								    object.pObjectName ? object.pObjectName : "unnamed"
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							std::println("Validation message: {}", vulkan_data->pMessage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
 | 
				
			||||||
 | 
							*(bool *)user_data = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Messenger m_messenger;
 | 
				
			||||||
 | 
						bool m_had_any_messages = false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace lt::renderer {
 | 
					namespace lt::renderer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					System::System(CreateInfo info)
 | 
				
			||||||
 | 
					    : m_registry(std::move(info.registry))
 | 
				
			||||||
 | 
					    , m_stats(info.system_stats)
 | 
				
			||||||
 | 
					    , m_context(create_scope<vk::Context>(info.surface_entity))
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						m_validation_observer = new vk::ValidationObserver();
 | 
				
			||||||
 | 
						// ensure(m_stats, "Failed to initialize system: null stats");
 | 
				
			||||||
 | 
						ensure(m_registry, "Failed to initialize renderer system: null registry");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_pass = create_ref<vk::Pass>(
 | 
				
			||||||
 | 
						    *m_context,
 | 
				
			||||||
 | 
						    ShaderAsset { "./data/test_assets/triangle.vert.asset" },
 | 
				
			||||||
 | 
						    ShaderAsset { "./data/test_assets/triangle.frag.asset" }
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_renderer = create_scope<vk::Renderer>(*m_context, m_pass);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					System::~System()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void System::on_register()
 | 
					void System::on_register()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -12,6 +102,15 @@ void System::on_unregister()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void System::tick(app::TickInfo tick)
 | 
					void System::tick(app::TickInfo tick)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						if (!m_renderer->draw(m_frame_idx))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							m_context->recreate_swapchain();
 | 
				
			||||||
 | 
							m_renderer->replace_swapchain(m_context->swapchain());
 | 
				
			||||||
 | 
							m_pass->replace_swapchain(m_context->swapchain());
 | 
				
			||||||
 | 
							m_renderer->draw(m_frame_idx);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_frame_idx = (m_frame_idx + 1) % vk::Renderer::max_frames_in_flight;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace lt::renderer
 | 
					} // namespace lt::renderer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,8 @@
 | 
				
			||||||
#include <ranges>
 | 
					#include <ranges>
 | 
				
			||||||
#include <renderer/system.hpp>
 | 
					#include <renderer/system.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/context.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/renderer/renderer.hpp>
 | 
				
			||||||
 | 
					#include <surface/components.hpp>
 | 
				
			||||||
#include <surface/system.hpp>
 | 
					#include <surface/system.hpp>
 | 
				
			||||||
#include <test/test.hpp>
 | 
					#include <test/test.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,9 @@ Context::Context(const ecs::Entity &surface_entity)
 | 
				
			||||||
    , m_device(m_surface)
 | 
					    , m_device(m_surface)
 | 
				
			||||||
    , m_swapchain(m_device, m_surface)
 | 
					    , m_swapchain(m_device, m_surface)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						ensure(m_surface.vk(), "Failed to create vulkan context: null surface");
 | 
				
			||||||
 | 
						ensure(m_device.vk(), "Failed to create vulkan context: null device");
 | 
				
			||||||
 | 
						ensure(m_swapchain.vk(), "Failed to create vulkan context: null swapchain");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace lt::renderer::vk
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,6 +35,12 @@ public:
 | 
				
			||||||
		return m_swapchain;
 | 
							return m_swapchain;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void recreate_swapchain()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							m_swapchain.destroy();
 | 
				
			||||||
 | 
							m_swapchain = Swapchain { m_device, m_surface };
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	Surface m_surface;
 | 
						Surface m_surface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,16 @@ Device::Device(const Surface &surface)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vk_get_device_queue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue);
 | 
						vk_get_device_queue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue);
 | 
				
			||||||
	vk_get_device_queue(m_device, m_present_queue_family_index, 0, &m_present_queue);
 | 
						vk_get_device_queue(m_device, m_present_queue_family_index, 0, &m_present_queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (m_present_queue.get() == m_graphics_queue.get())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							set_object_name(m_device, m_present_queue.get(), "unified queue");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							set_object_name(m_device, m_graphics_queue.get(), "graphics queue");
 | 
				
			||||||
 | 
							set_object_name(m_device, m_present_queue.get(), "present queue");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Device::~Device()
 | 
					Device::~Device()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -147,7 +147,7 @@ void Instance::initialize_instance()
 | 
				
			||||||
	const auto setting_thread_safety = VkBool32 { VK_TRUE };
 | 
						const auto setting_thread_safety = VkBool32 { VK_TRUE };
 | 
				
			||||||
	const auto *setting_debug_action = "";
 | 
						const auto *setting_debug_action = "";
 | 
				
			||||||
	const auto setting_enable_message_limit = VkBool32 { VK_TRUE };
 | 
						const auto setting_enable_message_limit = VkBool32 { VK_TRUE };
 | 
				
			||||||
	const auto setting_duplicate_message_limit = uint32_t { 3u };
 | 
						const auto setting_duplicate_message_limit = uint32_t { UINT32_MAX };
 | 
				
			||||||
	auto setting_report_flags = std::array<const char *, 5> {
 | 
						auto setting_report_flags = std::array<const char *, 5> {
 | 
				
			||||||
		"info", "warn", "perf", "error", "verbose",
 | 
							"info", "warn", "perf", "error", "verbose",
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace lt::renderer::vk {
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Surface::Surface(const ecs::Entity &surface_entity)
 | 
					Surface::Surface(ecs::Entity surface_entity): m_surface_entity(surface_entity)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const auto &component = surface_entity.get<surface::SurfaceComponent>();
 | 
						const auto &component = surface_entity.get<surface::SurfaceComponent>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,12 +18,6 @@ Surface::Surface(const ecs::Entity &surface_entity)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto *instance = Instance::get();
 | 
						auto *instance = Instance::get();
 | 
				
			||||||
	auto result = vk_create_xlib_surface_khr(instance, &create_info, nullptr, &m_surface);
 | 
						auto result = vk_create_xlib_surface_khr(instance, &create_info, nullptr, &m_surface);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	const auto &[width, height] = component.get_resolution();
 | 
					 | 
				
			||||||
	m_framebuffer_size = {
 | 
					 | 
				
			||||||
		.width = width,
 | 
					 | 
				
			||||||
		.height = height,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Surface::~Surface()
 | 
					Surface::~Surface()
 | 
				
			||||||
| 
						 | 
					@ -34,4 +28,15 @@ Surface::~Surface()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[nodiscard]] auto Surface::get_framebuffer_size() const -> VkExtent2D
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const auto &[width, height] = //
 | 
				
			||||||
 | 
						    m_surface_entity.get<surface::SurfaceComponent>().get_resolution();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							.width = width,
 | 
				
			||||||
 | 
							.height = height,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace lt::renderer::vk
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ namespace lt::renderer::vk {
 | 
				
			||||||
class Surface
 | 
					class Surface
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	Surface(const ecs::Entity &surface_entity);
 | 
						Surface(ecs::Entity surface_entity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	~Surface();
 | 
						~Surface();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,15 +26,12 @@ public:
 | 
				
			||||||
		return m_surface;
 | 
							return m_surface;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto get_framebuffer_size() const -> VkExtent2D
 | 
						[[nodiscard]] auto get_framebuffer_size() const -> VkExtent2D;
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_framebuffer_size;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	memory::NullOnMove<VkSurfaceKHR> m_surface = VK_NULL_HANDLE;
 | 
						memory::NullOnMove<VkSurfaceKHR> m_surface = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	VkExtent2D m_framebuffer_size {};
 | 
						ecs::Entity m_surface_entity;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace lt::renderer::vk
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@ Swapchain::Swapchain(const Device &device, const Surface &surface)
 | 
				
			||||||
    : m_device(device.vk())
 | 
					    : m_device(device.vk())
 | 
				
			||||||
    , m_resolution(surface.get_framebuffer_size())
 | 
					    , m_resolution(surface.get_framebuffer_size())
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						static auto idx = 0u;
 | 
				
			||||||
	auto *physical_device = device.physical();
 | 
						auto *physical_device = device.physical();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto capabilities = VkSurfaceCapabilitiesKHR {};
 | 
						auto capabilities = VkSurfaceCapabilitiesKHR {};
 | 
				
			||||||
| 
						 | 
					@ -55,15 +56,16 @@ Swapchain::Swapchain(const Device &device, const Surface &surface)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vkc(vk_create_swapchain_khr(device.vk(), &create_info, nullptr, &m_swapchain));
 | 
						vkc(vk_create_swapchain_khr(device.vk(), &create_info, nullptr, &m_swapchain));
 | 
				
			||||||
	vkc(vk_device_wait_idle(device.vk()));
 | 
						vkc(vk_device_wait_idle(device.vk()));
 | 
				
			||||||
 | 
						set_object_name(device.vk(), m_swapchain.get(), "swapchain {}", idx++);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto image_count = uint32_t { 0u };
 | 
						auto image_count = uint32_t { 0u };
 | 
				
			||||||
	vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, nullptr);
 | 
						vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m_swapchain_images.resize(image_count);
 | 
						m_images.resize(image_count);
 | 
				
			||||||
	m_swapchain_image_views.resize(image_count);
 | 
						m_image_views.resize(image_count);
 | 
				
			||||||
	vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, m_swapchain_images.data());
 | 
						vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, m_images.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (auto [image, view] : std::views::zip(m_swapchain_images, m_swapchain_image_views))
 | 
						for (auto [image, view] : std::views::zip(m_images, m_image_views))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		auto create_info = VkImageViewCreateInfo {
 | 
							auto create_info = VkImageViewCreateInfo {
 | 
				
			||||||
			.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
 | 
								.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
 | 
				
			||||||
| 
						 | 
					@ -87,29 +89,11 @@ Swapchain::Swapchain(const Device &device, const Surface &surface)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		vkc(vk_create_image_view(device.vk(), &create_info, nullptr, &view));
 | 
							vkc(vk_create_image_view(device.vk(), &create_info, nullptr, &view));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Swapchain::~Swapchain()
 | 
					Swapchain::~Swapchain()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	try
 | 
						destroy();
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (m_device)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			vkc(vk_device_wait_idle(m_device));
 | 
					 | 
				
			||||||
			for (auto &view : m_swapchain_image_views)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				vk_destroy_image_view(m_device, view, nullptr);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			vk_destroy_swapchain_khr(m_device, m_swapchain, nullptr);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	catch (const std::exception &exp)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		log_err("Failed to destroy swapchain:");
 | 
					 | 
				
			||||||
		log_err("\twhat: {}", exp.what());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[nodiscard]] auto Swapchain::get_optimal_image_count(
 | 
					[[nodiscard]] auto Swapchain::get_optimal_image_count(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,28 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto operator=(const Swapchain &) const -> Swapchain & = delete;
 | 
						auto operator=(const Swapchain &) const -> Swapchain & = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void destroy()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							try
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (m_device)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									vkc(vk_device_wait_idle(m_device));
 | 
				
			||||||
 | 
									for (auto &view : m_image_views)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										vk_destroy_image_view(m_device, view, nullptr);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									vk_destroy_swapchain_khr(m_device, m_swapchain, nullptr);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							catch (const std::exception &exp)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								log_err("Failed to destroy swapchain:");
 | 
				
			||||||
 | 
								log_err("\twhat: {}", exp.what());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto vk() const -> VkSwapchainKHR
 | 
						[[nodiscard]] auto vk() const -> VkSwapchainKHR
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_swapchain;
 | 
							return m_swapchain;
 | 
				
			||||||
| 
						 | 
					@ -36,10 +58,15 @@ public:
 | 
				
			||||||
		return m_format;
 | 
							return m_format;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [[nodiscard]] auto get_image_count() const -> size_t
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return m_images.size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto create_framebuffers_for_pass(VkRenderPass pass) const
 | 
						[[nodiscard]] auto create_framebuffers_for_pass(VkRenderPass pass) const
 | 
				
			||||||
	    -> std::vector<VkFramebuffer>
 | 
						    -> std::vector<VkFramebuffer>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		auto framebuffers = std::vector<VkFramebuffer>(m_swapchain_image_views.size());
 | 
							auto framebuffers = std::vector<VkFramebuffer>(m_image_views.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (auto idx = 0u; auto &framebuffer : framebuffers)
 | 
							for (auto idx = 0u; auto &framebuffer : framebuffers)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -47,7 +74,7 @@ public:
 | 
				
			||||||
				.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
 | 
									.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
 | 
				
			||||||
				.renderPass = pass,
 | 
									.renderPass = pass,
 | 
				
			||||||
				.attachmentCount = 1u,
 | 
									.attachmentCount = 1u,
 | 
				
			||||||
				.pAttachments = &m_swapchain_image_views[idx++],
 | 
									.pAttachments = &m_image_views[idx++],
 | 
				
			||||||
				.width = m_resolution.width,
 | 
									.width = m_resolution.width,
 | 
				
			||||||
				.height = m_resolution.height,
 | 
									.height = m_resolution.height,
 | 
				
			||||||
				.layers = 1u
 | 
									.layers = 1u
 | 
				
			||||||
| 
						 | 
					@ -69,9 +96,9 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memory::NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
 | 
						memory::NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::vector<VkImage> m_swapchain_images;
 | 
						std::vector<VkImage> m_images;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::vector<VkImageView> m_swapchain_image_views;
 | 
						std::vector<VkImageView> m_image_views;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	VkExtent2D m_resolution;
 | 
						VkExtent2D m_resolution;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <renderer/vk/vulkan.hpp>
 | 
					#include <renderer/vk/vulkan.hpp>
 | 
				
			||||||
 | 
					#include <vulkan/vk_enum_string_helper.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace lt::renderer::vk {
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +10,57 @@ inline void vkc(VkResult result)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (result)
 | 
						if (result)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		throw std::runtime_error {
 | 
							throw std::runtime_error { std::format(
 | 
				
			||||||
			std::format("Vulkan call failed with result: {}", std::to_underlying(result))
 | 
								"Vulkan call failed with result: {}({})",
 | 
				
			||||||
		};
 | 
								string_VkResult(result),
 | 
				
			||||||
 | 
								std::to_underlying(result)
 | 
				
			||||||
 | 
							) };
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename T>
 | 
				
			||||||
 | 
					inline auto get_object_type(T object) -> VkObjectType
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if constexpr (std::is_same_v<T, VkQueue>)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return VK_OBJECT_TYPE_QUEUE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if constexpr (std::is_same_v<T, VkFence>)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return VK_OBJECT_TYPE_FENCE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if constexpr (std::is_same_v<T, VkSemaphore>)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return VK_OBJECT_TYPE_SEMAPHORE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if constexpr (std::is_same_v<T, VkSwapchainKHR>)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return VK_OBJECT_TYPE_SWAPCHAIN_KHR;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static_assert("invalid type");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename T, typename... Args>
 | 
				
			||||||
 | 
					inline void set_object_name(
 | 
				
			||||||
 | 
					    VkDevice device,
 | 
				
			||||||
 | 
					    T object,
 | 
				
			||||||
 | 
					    std::format_string<Args...> fmt,
 | 
				
			||||||
 | 
					    Args &&...args
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const auto name = std::format(fmt, std::forward<Args>(args)...);
 | 
				
			||||||
 | 
						auto info = VkDebugUtilsObjectNameInfoEXT {
 | 
				
			||||||
 | 
							.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
 | 
				
			||||||
 | 
							.objectType = get_object_type(object),
 | 
				
			||||||
 | 
							.objectHandle = (uint64_t)(object),
 | 
				
			||||||
 | 
							.pObjectName = name.c_str(),
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vk_set_debug_object_name(device, &info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace lt::renderer::vk
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,20 +62,6 @@ public:
 | 
				
			||||||
			.primitiveRestartEnable = VK_FALSE,
 | 
								.primitiveRestartEnable = VK_FALSE,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto viewport = VkViewport {
 | 
					 | 
				
			||||||
			.x = 0u,
 | 
					 | 
				
			||||||
			.y = 0u,
 | 
					 | 
				
			||||||
			.width = static_cast<float>(context.swapchain().get_resolution().width),
 | 
					 | 
				
			||||||
			.height = static_cast<float>(context.swapchain().get_resolution().height),
 | 
					 | 
				
			||||||
			.minDepth = 0.0f,
 | 
					 | 
				
			||||||
			.maxDepth = 0.0f,
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto scissor = VkRect2D {
 | 
					 | 
				
			||||||
			.offset = { 0u, 0u },
 | 
					 | 
				
			||||||
			.extent = context.swapchain().get_resolution(),
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto viewport_state = VkPipelineViewportStateCreateInfo {
 | 
							auto viewport_state = VkPipelineViewportStateCreateInfo {
 | 
				
			||||||
			.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
 | 
								.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
 | 
				
			||||||
			.viewportCount = 1u,
 | 
								.viewportCount = 1u,
 | 
				
			||||||
| 
						 | 
					@ -103,8 +89,6 @@ public:
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto color_blend_attachment = VkPipelineColorBlendAttachmentState {
 | 
							auto color_blend_attachment = VkPipelineColorBlendAttachmentState {
 | 
				
			||||||
			.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
 | 
					 | 
				
			||||||
			                  | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
 | 
					 | 
				
			||||||
			.blendEnable = VK_FALSE,
 | 
								.blendEnable = VK_FALSE,
 | 
				
			||||||
			.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
 | 
								.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
 | 
				
			||||||
			.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
 | 
								.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
 | 
				
			||||||
| 
						 | 
					@ -112,6 +96,8 @@ public:
 | 
				
			||||||
			.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
 | 
								.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
 | 
				
			||||||
			.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
 | 
								.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
 | 
				
			||||||
			.alphaBlendOp = VK_BLEND_OP_ADD,
 | 
								.alphaBlendOp = VK_BLEND_OP_ADD,
 | 
				
			||||||
 | 
								.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
 | 
				
			||||||
 | 
								                  | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto color_blend = VkPipelineColorBlendStateCreateInfo {
 | 
							auto color_blend = VkPipelineColorBlendStateCreateInfo {
 | 
				
			||||||
| 
						 | 
					@ -235,6 +221,22 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto operator=(const Pass &) -> Pass & = delete;
 | 
						auto operator=(const Pass &) -> Pass & = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void replace_swapchain(const Swapchain &swapchain)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (!m_device)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vk_device_wait_idle(m_device);
 | 
				
			||||||
 | 
							for (auto &framebuffer : m_framebuffers)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								vk_destroy_frame_buffer(m_device, framebuffer, nullptr);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							m_framebuffers = swapchain.create_framebuffers_for_pass(m_pass);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto get_pass() -> VkRenderPass
 | 
						[[nodiscard]] auto get_pass() -> VkRenderPass
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_pass;
 | 
							return m_pass;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ranges>
 | 
				
			||||||
#include <renderer/vk/context/context.hpp>
 | 
					#include <renderer/vk/context/context.hpp>
 | 
				
			||||||
#include <renderer/vk/debug/validation.hpp>
 | 
					#include <renderer/vk/debug/validation.hpp>
 | 
				
			||||||
#include <renderer/vk/renderer/pass.hpp>
 | 
					#include <renderer/vk/renderer/pass.hpp>
 | 
				
			||||||
| 
						 | 
					@ -10,6 +11,8 @@ namespace lt::renderer::vk {
 | 
				
			||||||
class Renderer
 | 
					class Renderer
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
						static constexpr auto max_frames_in_flight = uint32_t { 3u };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Renderer(Context &context, Ref<Pass> pass)
 | 
						Renderer(Context &context, Ref<Pass> pass)
 | 
				
			||||||
	    : m_device(context.device().vk())
 | 
						    : m_device(context.device().vk())
 | 
				
			||||||
	    , m_graphics_queue(context.device().get_graphics_queue())
 | 
						    , m_graphics_queue(context.device().get_graphics_queue())
 | 
				
			||||||
| 
						 | 
					@ -18,6 +21,11 @@ public:
 | 
				
			||||||
	    , m_pass(std::move(pass))
 | 
						    , m_pass(std::move(pass))
 | 
				
			||||||
	    , m_resolution(context.swapchain().get_resolution())
 | 
						    , m_resolution(context.swapchain().get_resolution())
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							ensure(m_device, "Failed to initialize renderer: null device");
 | 
				
			||||||
 | 
							ensure(m_graphics_queue, "Failed to initialize renderer: null graphics queue");
 | 
				
			||||||
 | 
							ensure(m_present_queue, "Failed to initialize renderer: null present queue");
 | 
				
			||||||
 | 
							ensure(m_swapchain, "Failed to initialize renderer: null swapchain");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto pool_info = VkCommandPoolCreateInfo {
 | 
							auto pool_info = VkCommandPoolCreateInfo {
 | 
				
			||||||
			.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
 | 
								.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
 | 
				
			||||||
			.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
 | 
								.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
 | 
				
			||||||
| 
						 | 
					@ -30,9 +38,9 @@ public:
 | 
				
			||||||
			.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
 | 
								.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
 | 
				
			||||||
			.commandPool = m_pool,
 | 
								.commandPool = m_pool,
 | 
				
			||||||
			.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
 | 
								.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
 | 
				
			||||||
			.commandBufferCount = 1u,
 | 
								.commandBufferCount = static_cast<uint32_t>(m_cmds.size()),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
		vkc(vk_allocate_command_buffers(m_device, &cmd_info, &m_cmd));
 | 
							vkc(vk_allocate_command_buffers(m_device, &cmd_info, &m_cmds[0]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto semaphore_info = VkSemaphoreCreateInfo {
 | 
							auto semaphore_info = VkSemaphoreCreateInfo {
 | 
				
			||||||
			.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
 | 
								.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
 | 
				
			||||||
| 
						 | 
					@ -43,11 +51,168 @@ public:
 | 
				
			||||||
			.flags = VK_FENCE_CREATE_SIGNALED_BIT,
 | 
								.flags = VK_FENCE_CREATE_SIGNALED_BIT,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		vkc(vk_create_semaphore(m_device, &semaphore_info, nullptr, &m_image_available_semaphore));
 | 
							for (auto idx : std::views::iota(0u, max_frames_in_flight))
 | 
				
			||||||
		vkc(vk_create_semaphore(m_device, &semaphore_info, nullptr, &m_render_finished_semaphore));
 | 
							{
 | 
				
			||||||
		vkc(vk_create_fence(m_device, &fence_info, nullptr, &m_in_flight_fence));
 | 
								vkc(vk_create_semaphore(
 | 
				
			||||||
 | 
								    m_device,
 | 
				
			||||||
 | 
								    &semaphore_info,
 | 
				
			||||||
 | 
								    nullptr,
 | 
				
			||||||
 | 
								    &m_aquire_image_semaphores[idx]
 | 
				
			||||||
 | 
								));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								vkc(vk_create_fence(m_device, &fence_info, nullptr, &m_in_flight_fences[idx]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								set_object_name(
 | 
				
			||||||
 | 
								    m_device,
 | 
				
			||||||
 | 
								    m_aquire_image_semaphores[idx].get(),
 | 
				
			||||||
 | 
								    "aquire semaphore {}",
 | 
				
			||||||
 | 
								    idx
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								set_object_name(m_device, m_in_flight_fences[idx].get(), "frame fence {}", idx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									const auto name = std::format("frame fence {}", idx);
 | 
				
			||||||
 | 
									auto debug_info = VkDebugUtilsObjectNameInfoEXT {
 | 
				
			||||||
 | 
										.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
 | 
				
			||||||
 | 
										.objectType = VK_OBJECT_TYPE_FENCE,
 | 
				
			||||||
 | 
										.objectHandle = reinterpret_cast<uint64_t>(
 | 
				
			||||||
 | 
										    static_cast<VkFence_T *>(m_in_flight_fences[idx].get())
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
										.pObjectName = name.c_str(),
 | 
				
			||||||
 | 
									};
 | 
				
			||||||
 | 
									vk_set_debug_object_name(m_device, &debug_info);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							m_submit_semaphores.resize(context.swapchain().get_image_count());
 | 
				
			||||||
 | 
							for (auto idx = 0; auto &semaphore : m_submit_semaphores)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								vkc(vk_create_semaphore(m_device, &semaphore_info, nullptr, &semaphore));
 | 
				
			||||||
 | 
								set_object_name(m_device, semaphore.get(), "submit semaphore {}", idx++);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~Renderer()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (!m_device)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vkc(vk_device_wait_idle(m_device));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (auto [semaphore, fence] :
 | 
				
			||||||
 | 
							     std::views::zip(m_aquire_image_semaphores, m_in_flight_fences))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								vk_destroy_semaphore(m_device, semaphore, nullptr);
 | 
				
			||||||
 | 
								vk_destroy_fence(m_device, fence, nullptr);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (auto &semaphore : m_submit_semaphores)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								vk_destroy_semaphore(m_device, semaphore, nullptr);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vk_destroy_command_pool(m_device, m_pool, nullptr);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Renderer(Renderer &&) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Renderer(const Renderer &) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(Renderer &&) -> Renderer & = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(const Renderer &) -> Renderer & = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto draw(uint32_t frame_idx) -> bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ensure(
 | 
				
			||||||
 | 
							    frame_idx < max_frames_in_flight,
 | 
				
			||||||
 | 
							    "Failed to draw: frame_idx >= max_frames_in_flight"
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto &flight_fence = m_in_flight_fences[frame_idx];
 | 
				
			||||||
 | 
							auto &aquire_semaphore = m_aquire_image_semaphores[frame_idx];
 | 
				
			||||||
 | 
							auto &cmd = m_cmds[frame_idx];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								vkc(vk_wait_for_fences(
 | 
				
			||||||
 | 
								    m_device,
 | 
				
			||||||
 | 
								    1u,
 | 
				
			||||||
 | 
								    &flight_fence,
 | 
				
			||||||
 | 
								    VK_TRUE,
 | 
				
			||||||
 | 
								    std::numeric_limits<uint64_t>::max()
 | 
				
			||||||
 | 
								));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto image_idx = uint32_t {};
 | 
				
			||||||
 | 
								auto result = vk_acquire_next_image_khr(
 | 
				
			||||||
 | 
								    m_device,
 | 
				
			||||||
 | 
								    m_swapchain,
 | 
				
			||||||
 | 
								    UINT64_MAX,
 | 
				
			||||||
 | 
								    aquire_semaphore,
 | 
				
			||||||
 | 
								    VK_NULL_HANDLE,
 | 
				
			||||||
 | 
								    &image_idx
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									return false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								vkc(vk_reset_fences(m_device, 1u, &flight_fence));
 | 
				
			||||||
 | 
								vkc(vk_reset_command_buffer(cmd, {}));
 | 
				
			||||||
 | 
								record_cmd(cmd, image_idx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto wait_stage = VkPipelineStageFlags {
 | 
				
			||||||
 | 
									VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								auto &submit_semaphore = m_submit_semaphores[image_idx];
 | 
				
			||||||
 | 
								auto submit_info = VkSubmitInfo {
 | 
				
			||||||
 | 
									.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
 | 
				
			||||||
 | 
									.waitSemaphoreCount = 1u,
 | 
				
			||||||
 | 
									.pWaitSemaphores = &aquire_semaphore,
 | 
				
			||||||
 | 
									.pWaitDstStageMask = &wait_stage,
 | 
				
			||||||
 | 
									.commandBufferCount = 1u,
 | 
				
			||||||
 | 
									.pCommandBuffers = &cmd,
 | 
				
			||||||
 | 
									.signalSemaphoreCount = 1u,
 | 
				
			||||||
 | 
									.pSignalSemaphores = &submit_semaphore,
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								vkc(vk_queue_submit(m_graphics_queue, 1u, &submit_info, flight_fence));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto present_info = VkPresentInfoKHR {
 | 
				
			||||||
 | 
									.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
 | 
				
			||||||
 | 
									.waitSemaphoreCount = 1u,
 | 
				
			||||||
 | 
									.pWaitSemaphores = &submit_semaphore,
 | 
				
			||||||
 | 
									.swapchainCount = 1u,
 | 
				
			||||||
 | 
									.pSwapchains = &m_swapchain,
 | 
				
			||||||
 | 
									.pImageIndices = &image_idx,
 | 
				
			||||||
 | 
									.pResults = nullptr,
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								vk_queue_present_khr(m_present_queue, &present_info);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							catch (const std::exception &exp)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								log_dbg("EXCEPTION: {}", exp.what());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void replace_swapchain(const Swapchain &swapchain)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							vk_device_wait_idle(m_device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							m_swapchain = swapchain.vk();
 | 
				
			||||||
 | 
							m_resolution = swapchain.get_resolution();
 | 
				
			||||||
 | 
							ensure(m_swapchain, "Failed to replace renderer's swapchain: null swapchain");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
	void record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
 | 
						void record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		auto cmd_begin_info = VkCommandBufferBeginInfo {
 | 
							auto cmd_begin_info = VkCommandBufferBeginInfo {
 | 
				
			||||||
| 
						 | 
					@ -101,93 +266,18 @@ public:
 | 
				
			||||||
		vkc(vk_end_command_buffer(cmd));
 | 
							vkc(vk_end_command_buffer(cmd));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void draw()
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		try
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			vkc(vk_wait_for_fences(m_device, 1u, &m_in_flight_fence, VK_TRUE, UINT64_MAX));
 | 
					 | 
				
			||||||
			vkc(vk_reset_fences(m_device, 1u, &m_in_flight_fence));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			auto image_idx = uint32_t {};
 | 
					 | 
				
			||||||
			vkc(vk_acquire_next_image_khr(
 | 
					 | 
				
			||||||
			    m_device,
 | 
					 | 
				
			||||||
			    m_swapchain,
 | 
					 | 
				
			||||||
			    UINT64_MAX,
 | 
					 | 
				
			||||||
			    m_image_available_semaphore,
 | 
					 | 
				
			||||||
			    VK_NULL_HANDLE,
 | 
					 | 
				
			||||||
			    &image_idx
 | 
					 | 
				
			||||||
			));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			vkc(vk_reset_command_buffer(m_cmd, {}));
 | 
					 | 
				
			||||||
			record_cmd(m_cmd, image_idx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			auto wait_stage = VkPipelineStageFlags {
 | 
					 | 
				
			||||||
				VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
			auto submit_info = VkSubmitInfo {
 | 
					 | 
				
			||||||
				.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
 | 
					 | 
				
			||||||
				.waitSemaphoreCount = 1u,
 | 
					 | 
				
			||||||
				.pWaitSemaphores = &m_image_available_semaphore,
 | 
					 | 
				
			||||||
				.pWaitDstStageMask = &wait_stage,
 | 
					 | 
				
			||||||
				.commandBufferCount = 1u,
 | 
					 | 
				
			||||||
				.pCommandBuffers = &m_cmd,
 | 
					 | 
				
			||||||
				.signalSemaphoreCount = 1u,
 | 
					 | 
				
			||||||
				.pSignalSemaphores = &m_render_finished_semaphore,
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			vkc(vk_queue_submit(m_graphics_queue, 1u, &submit_info, m_in_flight_fence));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			auto present_info = VkPresentInfoKHR {
 | 
					 | 
				
			||||||
				.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
 | 
					 | 
				
			||||||
				.waitSemaphoreCount = 1u,
 | 
					 | 
				
			||||||
				.pWaitSemaphores = &m_render_finished_semaphore,
 | 
					 | 
				
			||||||
				.swapchainCount = 1u,
 | 
					 | 
				
			||||||
				.pSwapchains = &m_swapchain,
 | 
					 | 
				
			||||||
				.pImageIndices = &image_idx,
 | 
					 | 
				
			||||||
				.pResults = nullptr,
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			vk_queue_present_khr(m_present_queue, &present_info);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		catch (const std::exception &exp)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			log_dbg("EXCEPTION: {}", exp.what());
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~Renderer()
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (!m_device)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		vk_destroy_semaphore(m_device, m_render_finished_semaphore, nullptr);
 | 
					 | 
				
			||||||
		vk_destroy_semaphore(m_device, m_image_available_semaphore, nullptr);
 | 
					 | 
				
			||||||
		vk_destroy_fence(m_device, m_in_flight_fence, nullptr);
 | 
					 | 
				
			||||||
		vk_destroy_command_pool(m_device, m_pool, nullptr);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Renderer(Renderer &&) = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Renderer(const Renderer &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto operator=(Renderer &&) -> Renderer & = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto operator=(const Renderer &) -> Renderer & = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	memory::NullOnMove<VkDevice> m_device = VK_NULL_HANDLE;
 | 
						memory::NullOnMove<VkDevice> m_device = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memory::NullOnMove<VkCommandPool> m_pool = VK_NULL_HANDLE;
 | 
						memory::NullOnMove<VkCommandPool> m_pool = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memory::NullOnMove<VkCommandBuffer> m_cmd = VK_NULL_HANDLE;
 | 
						std::array<memory::NullOnMove<VkCommandBuffer>, max_frames_in_flight> m_cmds {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memory::NullOnMove<VkSemaphore> m_image_available_semaphore = VK_NULL_HANDLE;
 | 
						std::array<memory::NullOnMove<VkSemaphore>, max_frames_in_flight> m_aquire_image_semaphores {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memory::NullOnMove<VkSemaphore> m_render_finished_semaphore = VK_NULL_HANDLE;
 | 
						std::vector<memory::NullOnMove<VkSemaphore>> m_submit_semaphores;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memory::NullOnMove<VkFence> m_in_flight_fence = VK_NULL_HANDLE;
 | 
						std::array<memory::NullOnMove<VkFence>, max_frames_in_flight> m_in_flight_fences {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memory::NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
 | 
						memory::NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,18 +10,62 @@ Suite raii = "renderer_raii"_suite = [] {
 | 
				
			||||||
		auto observer = ValidationObserver {};
 | 
							auto observer = ValidationObserver {};
 | 
				
			||||||
		auto [context, _] = create_context();
 | 
							auto [context, _] = create_context();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							std::ignore = Renderer(
 | 
				
			||||||
 | 
							    context,
 | 
				
			||||||
 | 
							    lt::create_ref<Pass>(
 | 
				
			||||||
 | 
							        context,
 | 
				
			||||||
 | 
							        ShaderAsset { "./data/test_assets/triangle.vert.asset" },
 | 
				
			||||||
 | 
							        ShaderAsset { "./data/test_assets/triangle.frag.asset" }
 | 
				
			||||||
 | 
							    )
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							expect_false(observer.had_any_messages());
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Suite draw = "renderer_draw"_suite = [] {
 | 
				
			||||||
 | 
						Case { "renderer draw" } = [] {
 | 
				
			||||||
 | 
							auto observer = ValidationObserver {};
 | 
				
			||||||
 | 
							auto [context, _] = create_context();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto renderer = Renderer(
 | 
				
			||||||
 | 
							    context,
 | 
				
			||||||
 | 
							    lt::create_ref<Pass>(
 | 
				
			||||||
 | 
							        context,
 | 
				
			||||||
 | 
							        ShaderAsset { "./data/test_assets/triangle.vert.asset" },
 | 
				
			||||||
 | 
							        ShaderAsset { "./data/test_assets/triangle.frag.asset" }
 | 
				
			||||||
 | 
							    )
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (auto frame_idx : std::views::iota(0u, 30u))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								expect_true(renderer.draw(frame_idx % Renderer::max_frames_in_flight));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							expect_false(observer.had_any_messages());
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Case { "post swapchain replacement renderer draw" } = [] {
 | 
				
			||||||
 | 
							auto observer = ValidationObserver {};
 | 
				
			||||||
 | 
							auto [context, _] = create_context();
 | 
				
			||||||
		auto pass = lt::create_ref<Pass>(
 | 
							auto pass = lt::create_ref<Pass>(
 | 
				
			||||||
		    context,
 | 
							    context,
 | 
				
			||||||
		    ShaderAsset { "./data/test_assets/triangle.vert.asset" },
 | 
							    ShaderAsset { "./data/test_assets/triangle.vert.asset" },
 | 
				
			||||||
		    ShaderAsset { "./data/test_assets/triangle.frag.asset" }
 | 
							    ShaderAsset { "./data/test_assets/triangle.frag.asset" }
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto renderer = Renderer { context, pass };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto renderer = Renderer(context, pass);
 | 
							for (auto frame_idx : std::views::iota(0u, 15u))
 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (;;)
 | 
					 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			renderer.draw();
 | 
								expect_true(renderer.draw(frame_idx % Renderer::max_frames_in_flight));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							context.recreate_swapchain();
 | 
				
			||||||
 | 
							renderer.replace_swapchain(context.swapchain());
 | 
				
			||||||
 | 
							pass->replace_swapchain(context.swapchain());
 | 
				
			||||||
 | 
							for (auto frame_idx : std::views::iota(0u, 15u))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								expect_true(renderer.draw(frame_idx % Renderer::max_frames_in_flight));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		expect_false(observer.had_any_messages());
 | 
							expect_false(observer.had_any_messages());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,16 @@ private:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		std::ignore = message_severity;
 | 
							std::ignore = message_severity;
 | 
				
			||||||
		std::ignore = message_type;
 | 
							std::ignore = message_type;
 | 
				
			||||||
 | 
							for (auto idx = 0; idx < vulkan_data->objectCount; ++idx)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								auto object = vulkan_data->pObjects[idx];
 | 
				
			||||||
 | 
								std::println(
 | 
				
			||||||
 | 
								    "0x{:x}({}) = {}",
 | 
				
			||||||
 | 
								    object.objectHandle,
 | 
				
			||||||
 | 
								    string_VkObjectType(object.objectType),
 | 
				
			||||||
 | 
								    object.pObjectName ? object.pObjectName : "unnamed"
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		std::println("Validation message: {}", vulkan_data->pMessage);
 | 
							std::println("Validation message: {}", vulkan_data->pMessage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,11 +2,17 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <app/system.hpp>
 | 
					#include <app/system.hpp>
 | 
				
			||||||
#include <ecs/entity.hpp>
 | 
					#include <ecs/entity.hpp>
 | 
				
			||||||
#include <renderer/vk/context/context.hpp>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace lt::renderer {
 | 
					namespace lt::renderer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class System: app::ISystem
 | 
					namespace vk {
 | 
				
			||||||
 | 
					class Context;
 | 
				
			||||||
 | 
					class Renderer;
 | 
				
			||||||
 | 
					class Pass;
 | 
				
			||||||
 | 
					class ValidationObserver;
 | 
				
			||||||
 | 
					} // namespace vk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class System: public app::ISystem
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	struct CreateInfo
 | 
						struct CreateInfo
 | 
				
			||||||
| 
						 | 
					@ -18,16 +24,9 @@ public:
 | 
				
			||||||
		Ref<app::SystemStats> system_stats;
 | 
							Ref<app::SystemStats> system_stats;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] System(CreateInfo info)
 | 
						System(CreateInfo info);
 | 
				
			||||||
	    : m_registry(std::move(info.registry))
 | 
					 | 
				
			||||||
	    , m_stats(info.system_stats)
 | 
					 | 
				
			||||||
	    , m_context(info.surface_entity)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		ensure(m_stats, "Failed to initialize system: null stats");
 | 
					 | 
				
			||||||
		ensure(m_registry, "Failed to initialize renderer system: null registry");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	~System() override = default;
 | 
						~System() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	System(System &&) = default;
 | 
						System(System &&) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,8 +40,10 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void on_unregister() override;
 | 
						void on_unregister() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void tick(app::TickInfo tick) override;
 | 
						void tick(app::TickInfo tick) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto get_stats() const -> const app::SystemStats &
 | 
						[[nodiscard]] auto get_stats() const -> const app::SystemStats &
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return *m_stats;
 | 
							return *m_stats;
 | 
				
			||||||
| 
						 | 
					@ -54,13 +55,22 @@ public:
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
						class vk::ValidationObserver *m_validation_observer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Ref<ecs::Registry> m_registry;
 | 
						Ref<ecs::Registry> m_registry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Ref<app::SystemStats> m_stats;
 | 
						Ref<app::SystemStats> m_stats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vk::Context m_context;
 | 
						Scope<class vk::Context> m_context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ref<class vk::Pass> m_pass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Scope<class vk::Renderer> m_renderer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app::TickResult m_last_tick_result {};
 | 
						app::TickResult m_last_tick_result {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32_t m_frame_idx {};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace lt::renderer
 | 
					} // namespace lt::renderer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue