refactor(renderer): split up context into multiple objectes & fix some edge cases
This commit is contained in:
		
							parent
							
								
									d411c9ab2c
								
							
						
					
					
						commit
						fa1bfaae1e
					
				
					 18 changed files with 801 additions and 478 deletions
				
			
		| 
						 | 
					@ -1,15 +1,28 @@
 | 
				
			||||||
add_library_module(renderer
 | 
					add_library_module(renderer
 | 
				
			||||||
    system.cpp
 | 
					    system.cpp
 | 
				
			||||||
    vk/context.cpp
 | 
					    vk/context/instance.cpp
 | 
				
			||||||
 | 
					    vk/context/surface.cpp
 | 
				
			||||||
 | 
					    vk/context/device.cpp
 | 
				
			||||||
 | 
					    vk/context/swapchain.cpp
 | 
				
			||||||
 | 
					    vk/context/context.cpp
 | 
				
			||||||
 | 
					    vk/pipeline.cpp
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
target_link_libraries(renderer
 | 
					target_link_libraries(renderer
 | 
				
			||||||
PUBLIC 
 | 
					PUBLIC 
 | 
				
			||||||
    app 
 | 
					    app 
 | 
				
			||||||
    ecs
 | 
					    ecs
 | 
				
			||||||
 | 
					PRIVATE
 | 
				
			||||||
    surface
 | 
					    surface
 | 
				
			||||||
 | 
					    pthread
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_test_module(renderer 
 | 
					add_test_module(renderer 
 | 
				
			||||||
    system.test.cpp
 | 
					    system.test.cpp
 | 
				
			||||||
 | 
					    vk/context/instance.test.cpp
 | 
				
			||||||
 | 
					    vk/context/surface.test.cpp
 | 
				
			||||||
 | 
					    vk/context/device.test.cpp
 | 
				
			||||||
 | 
					    vk/context/swapchain.test.cpp
 | 
				
			||||||
 | 
					    vk/context/context.test.cpp
 | 
				
			||||||
 | 
					    vk/pipeline.test.cpp
 | 
				
			||||||
) 
 | 
					) 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,29 +69,73 @@ struct RendererContext
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SystemTest
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						SystemTest()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							m_surface_entity->add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
 | 
				
			||||||
 | 
							    .title = "",
 | 
				
			||||||
 | 
							    .resolution = resolution,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto registry() const -> Ref<ecs::Registry>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_registry;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto surface_entity() const -> const ecs::Entity &
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return *m_surface_entity;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto stats() const -> Ref<app::SystemStats>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_stats;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						Ref<app::SystemStats> m_stats = create_ref<app::SystemStats>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ref<ecs::Registry> m_registry = create_ref<ecs::Registry>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ref<surface::System> m_surface_system = create_ref<surface::System>(m_registry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Scope<ecs::Entity> m_surface_entity = create_scope<ecs::Entity>(
 | 
				
			||||||
 | 
						    m_registry,
 | 
				
			||||||
 | 
						    m_registry->create_entity()
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Suite raii = [] {
 | 
					Suite raii = [] {
 | 
				
			||||||
	Case { "happy path won't throw" } = [&] {
 | 
						Case { "happy path won't throw" } = [&] {
 | 
				
			||||||
		ignore = create_system();
 | 
							ignore = create_system();
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Case { "happy path has no validation errors" } = [&] {
 | 
						Case { "happy path has no validation errors" } = [&] {
 | 
				
			||||||
		auto [surface, renderer] = create_system();
 | 
							auto fixture = SystemTest {};
 | 
				
			||||||
		expect_true(renderer.system.get_stats().empty_diagnosis());
 | 
							std::ignore = System(
 | 
				
			||||||
 | 
							    {
 | 
				
			||||||
 | 
							        .registry = fixture.registry(),
 | 
				
			||||||
 | 
							        .surface_entity = fixture.surface_entity(),
 | 
				
			||||||
 | 
							        .system_stats = fixture.stats(),
 | 
				
			||||||
 | 
							    }
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							expect_true(fixture.stats()->empty_diagnosis());
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Case { "unhappy path throws" } = [] {
 | 
						Case { "unhappy path throws" } = [] {
 | 
				
			||||||
		auto [surface_system, surface_entity] = create_surface();
 | 
							auto fixture = SystemTest {};
 | 
				
			||||||
		auto empty_registry = create_ref<ecs::Registry>();
 | 
							auto empty_entity = ecs::Entity { fixture.registry(), fixture.registry()->create_entity() };
 | 
				
			||||||
		auto empty_entity = ecs::Entity { empty_registry, empty_registry->create_entity() };
 | 
					 | 
				
			||||||
		auto registry = create_ref<ecs::Registry>();
 | 
					 | 
				
			||||||
		auto stats = create_ref<app::SystemStats>();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		expect_throw([&] {
 | 
							expect_throw([&] {
 | 
				
			||||||
			ignore = System(
 | 
								ignore = System(
 | 
				
			||||||
			    {
 | 
								    {
 | 
				
			||||||
			        .registry = {},
 | 
								        .registry = {},
 | 
				
			||||||
			        .surface_entity = surface_entity,
 | 
								        .surface_entity = fixture.surface_entity(),
 | 
				
			||||||
			        .system_stats = stats,
 | 
								        .system_stats = fixture.stats(),
 | 
				
			||||||
			    }
 | 
								    }
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -99,9 +143,9 @@ Suite raii = [] {
 | 
				
			||||||
		expect_throw([&] {
 | 
							expect_throw([&] {
 | 
				
			||||||
			ignore = System(
 | 
								ignore = System(
 | 
				
			||||||
			    System::CreateInfo {
 | 
								    System::CreateInfo {
 | 
				
			||||||
			        .registry = surface_entity.get_registry(),
 | 
								        .registry = fixture.registry(),
 | 
				
			||||||
			        .surface_entity = empty_entity,
 | 
								        .surface_entity = empty_entity,
 | 
				
			||||||
			        .system_stats = stats,
 | 
								        .system_stats = fixture.stats(),
 | 
				
			||||||
			    }
 | 
								    }
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -109,8 +153,8 @@ Suite raii = [] {
 | 
				
			||||||
		expect_throw([&] {
 | 
							expect_throw([&] {
 | 
				
			||||||
			ignore = System(
 | 
								ignore = System(
 | 
				
			||||||
			    System::CreateInfo {
 | 
								    System::CreateInfo {
 | 
				
			||||||
			        .registry = surface_entity.get_registry(),
 | 
								        .registry = fixture.registry(),
 | 
				
			||||||
			        .surface_entity = surface_entity,
 | 
								        .surface_entity = fixture.surface_entity(),
 | 
				
			||||||
			        .system_stats = {},
 | 
								        .system_stats = {},
 | 
				
			||||||
			    }
 | 
								    }
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								modules/renderer/private/vk/context/context.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								modules/renderer/private/vk/context/context.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					#include <ranges>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/context.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/instance.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Context::Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats)
 | 
				
			||||||
 | 
					    : m_stats(std::move(system_stats))
 | 
				
			||||||
 | 
					    , m_surface(surface_entity)
 | 
				
			||||||
 | 
					    , m_device(m_surface)
 | 
				
			||||||
 | 
					    , m_swapchain(m_device, m_surface)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										48
									
								
								modules/renderer/private/vk/context/context.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								modules/renderer/private/vk/context/context.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <app/system.hpp>
 | 
				
			||||||
 | 
					#include <ecs/entity.hpp>
 | 
				
			||||||
 | 
					#include <memory/pointer_types/null_on_move.hpp>
 | 
				
			||||||
 | 
					#include <surface/components.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					#include <renderer/vk/context/device.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/instance.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/surface.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/swapchain.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using memory::NullOnMove;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Context
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto instance() const -> VkInstance
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return Instance::get();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto device() const -> const Device &
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_device;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto swapchain() const -> const Swapchain &
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_swapchain;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						Ref<app::SystemStats> m_stats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Surface m_surface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Device m_device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Swapchain m_swapchain;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										151
									
								
								modules/renderer/private/vk/context/device.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								modules/renderer/private/vk/context/device.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,151 @@
 | 
				
			||||||
 | 
					#include <renderer/vk/context/device.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Device::Device(const Surface &surface)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						initialize_physical_device();
 | 
				
			||||||
 | 
						initialize_logical_device();
 | 
				
			||||||
 | 
						Instance::load_device_functions(m_device);
 | 
				
			||||||
 | 
						initialize_queue(surface);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Device::~Device()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (m_device)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							vkc(vk_device_wait_idle(m_device));
 | 
				
			||||||
 | 
							vk_destroy_device(m_device, nullptr);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Device::initialize_physical_device()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto count = 0u;
 | 
				
			||||||
 | 
						vkc(vk_enumerate_physical_devices(Instance::get(), &count, nullptr));
 | 
				
			||||||
 | 
						ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto devices = std::vector<VkPhysicalDevice>(count);
 | 
				
			||||||
 | 
						vkc(vk_enumerate_physical_devices(Instance::get(), &count, devices.data()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto &device : devices)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto properties = VkPhysicalDeviceProperties {};
 | 
				
			||||||
 | 
							auto features = VkPhysicalDeviceFeatures {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vk_get_physical_device_properties(device, &properties);
 | 
				
			||||||
 | 
							vk_get_physical_device_features(device, &features);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
 | 
				
			||||||
 | 
							    && features.geometryShader)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								m_physical_device = device;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ensure(m_physical_device, "Failed to find any suitable Vulkan physical device");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Device::initialize_logical_device()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const float priorities = .0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto queue_info = VkDeviceQueueCreateInfo {
 | 
				
			||||||
 | 
							.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
 | 
				
			||||||
 | 
							.queueFamilyIndex = find_suitable_queue_family(),
 | 
				
			||||||
 | 
							.queueCount = 1u,
 | 
				
			||||||
 | 
							.pQueuePriorities = &priorities,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto physical_device_features = VkPhysicalDeviceFeatures {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto extensions = std::vector<const char *> {
 | 
				
			||||||
 | 
							VK_KHR_SWAPCHAIN_EXTENSION_NAME,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto device_info = VkDeviceCreateInfo {
 | 
				
			||||||
 | 
							.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
 | 
				
			||||||
 | 
							.queueCreateInfoCount = 1,
 | 
				
			||||||
 | 
							.pQueueCreateInfos = &queue_info,
 | 
				
			||||||
 | 
							.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
 | 
				
			||||||
 | 
							.ppEnabledExtensionNames = extensions.data(),
 | 
				
			||||||
 | 
							.pEnabledFeatures = &physical_device_features,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ensure(
 | 
				
			||||||
 | 
						    !vk_create_device(m_physical_device, &device_info, nullptr, &m_device),
 | 
				
			||||||
 | 
						    "Failed to create logical vulkan device"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[nodiscard]] auto Device::find_suitable_queue_family() const -> uint32_t
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto count = 0u;
 | 
				
			||||||
 | 
						vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
 | 
				
			||||||
 | 
						ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto families = std::vector<VkQueueFamilyProperties>(count);
 | 
				
			||||||
 | 
						vk_get_physical_device_queue_family_properties(m_physical_device, &count, families.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto required_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
 | 
				
			||||||
 | 
						for (auto idx = 0u; auto &family : families)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if ((family.queueFlags & required_flags) == required_flags)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return idx;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ensure(false, "Failed to find a suitable Vulkan queue family");
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Device::initialize_queue(const Surface &surface)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vk_get_device_queue(m_device, find_suitable_queue_family(), 0, &m_queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto count = uint32_t { 0u };
 | 
				
			||||||
 | 
						vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto properties = std::vector<VkQueueFamilyProperties>(count);
 | 
				
			||||||
 | 
						vk_get_physical_device_queue_family_properties(m_physical_device, &count, properties.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto idx = uint32_t { 0u }; const auto &property : properties)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (property.queueFlags & VK_QUEUE_GRAPHICS_BIT)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								m_graphics_queue_family_index = idx;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto has_presentation_support = VkBool32 { false };
 | 
				
			||||||
 | 
							vkc(vk_get_physical_device_surface_support(
 | 
				
			||||||
 | 
							    m_physical_device,
 | 
				
			||||||
 | 
							    idx,
 | 
				
			||||||
 | 
							    surface.vk(),
 | 
				
			||||||
 | 
							    &has_presentation_support
 | 
				
			||||||
 | 
							));
 | 
				
			||||||
 | 
							if (has_presentation_support)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								m_present_queue_family_index = idx;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							++idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED
 | 
				
			||||||
 | 
							    && m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ensure(
 | 
				
			||||||
 | 
						    m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
 | 
				
			||||||
 | 
						    "Failed to find graphics queue family"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ensure(
 | 
				
			||||||
 | 
						    m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
 | 
				
			||||||
 | 
						    "Failed to find presentation queue family"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										60
									
								
								modules/renderer/private/vk/context/device.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								modules/renderer/private/vk/context/device.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <memory/pointer_types/null_on_move.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/instance.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/surface.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Device
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						Device(const Surface &surface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~Device();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Device(Device &&) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Device(const Device &) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(Device &&) -> Device & = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(const Device &) const -> Device & = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto vk() const -> VkDevice
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_device;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto physical() const -> VkPhysicalDevice
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_physical_device;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto get_family_indices() const -> std::array<uint32_t, 2>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return { m_graphics_queue_family_index, m_present_queue_family_index };
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void initialize_physical_device();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initialize_logical_device();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initialize_queue(const Surface &surface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto find_suitable_queue_family() const -> uint32_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// logical device
 | 
				
			||||||
 | 
						memory::NullOnMove<VkPhysicalDevice> m_physical_device = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memory::NullOnMove<VkDevice> m_device = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memory::NullOnMove<VkQueue> m_queue = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32_t m_graphics_queue_family_index = VK_QUEUE_FAMILY_IGNORED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32_t m_present_queue_family_index = VK_QUEUE_FAMILY_IGNORED;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
#include <ranges>
 | 
					#include <app/system.hpp>
 | 
				
			||||||
#include <renderer/vk/context.hpp>
 | 
					#include <renderer/vk/context/instance.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(_WIN32)
 | 
					#if defined(_WIN32)
 | 
				
			||||||
 | 
						#error "Unsupported platform (currently)"
 | 
				
			||||||
#elif defined(__unix__)
 | 
					#elif defined(__unix__)
 | 
				
			||||||
	#include <dlfcn.h>
 | 
						#include <dlfcn.h>
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
| 
						 | 
					@ -99,70 +99,35 @@ auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_sever
 | 
				
			||||||
    -> app::SystemDiagnosis::Severity;
 | 
					    -> app::SystemDiagnosis::Severity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
auto validation_layers_callback(
 | 
					auto validation_layers_callback(
 | 
				
			||||||
    VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity,
 | 
					    VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
 | 
				
			||||||
    VkDebugUtilsMessageTypeFlagsEXT const message_types,
 | 
					    VkDebugUtilsMessageTypeFlagsEXT message_types,
 | 
				
			||||||
    VkDebugUtilsMessengerCallbackDataEXT const *const callback_data,
 | 
					    VkDebugUtilsMessengerCallbackDataEXT const *callback_data,
 | 
				
			||||||
    void *const vulkan_user_data
 | 
					    void *vulkan_user_data
 | 
				
			||||||
) -> VkBool32;
 | 
					) -> VkBool32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Context::Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats)
 | 
					Instance::Instance()
 | 
				
			||||||
    : m_stats(std::move(system_stats))
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ensure(m_stats, "Failed to create Vulkan Context: null stats");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	load_library();
 | 
						load_library();
 | 
				
			||||||
	load_global_functions();
 | 
						load_global_functions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	initialize_instance();
 | 
						initialize_instance();
 | 
				
			||||||
	load_instance_functions();
 | 
						load_instance_functions();
 | 
				
			||||||
 | 
					 | 
				
			||||||
	initialize_debug_messenger();
 | 
						initialize_debug_messenger();
 | 
				
			||||||
 | 
					 | 
				
			||||||
	initialize_physical_device();
 | 
					 | 
				
			||||||
	initialize_logical_device();
 | 
					 | 
				
			||||||
	load_device_functions();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	initialize_surface(surface_entity);
 | 
					 | 
				
			||||||
	initialize_queue();
 | 
					 | 
				
			||||||
	initialize_swapchain();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Context::~Context()
 | 
					Instance::~Instance()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	try
 | 
						if (m_instance)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (m_device)
 | 
							vk_destroy_debug_messenger(m_instance, m_debug_messenger, nullptr);
 | 
				
			||||||
		{
 | 
							vk_destroy_instance(m_instance, nullptr);
 | 
				
			||||||
			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);
 | 
					 | 
				
			||||||
			vk_destroy_device(m_device, nullptr);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (m_instance)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			vk_destroy_surface_khr(m_instance, m_surface, nullptr);
 | 
					 | 
				
			||||||
			vk_destroy_debug_messenger(m_instance, m_debug_messenger, nullptr);
 | 
					 | 
				
			||||||
			// TODO(Light): fix this issue
 | 
					 | 
				
			||||||
			// @ref:
 | 
					 | 
				
			||||||
			// https://git.light7734.com/light7734/light/commit/f268724034a2ceb63b90dc13aedf86a1eecac62e
 | 
					 | 
				
			||||||
			// vk_destroy_instance(m_instance, nullptr);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	catch (const std::exception &exp)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		log_err("Exception: {}", exp.what());
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unload_library();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Instance::initialize_instance()
 | 
				
			||||||
void Context::initialize_instance()
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	auto app_info = VkApplicationInfo {
 | 
						auto app_info = VkApplicationInfo {
 | 
				
			||||||
		.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
 | 
							.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
 | 
				
			||||||
| 
						 | 
					@ -206,11 +171,10 @@ void Context::initialize_instance()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vkc(vk_create_instance(&instance_info, nullptr, &m_instance));
 | 
						vkc(vk_create_instance(&instance_info, nullptr, &m_instance));
 | 
				
			||||||
 | 
					 | 
				
			||||||
	ensure(m_instance, "Failed to create vulkan instance");
 | 
						ensure(m_instance, "Failed to create vulkan instance");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Context::initialize_debug_messenger()
 | 
					void Instance::initialize_debug_messenger()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const auto info = VkDebugUtilsMessengerCreateInfoEXT {
 | 
						const auto info = VkDebugUtilsMessengerCreateInfoEXT {
 | 
				
			||||||
		.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
 | 
							.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
 | 
				
			||||||
| 
						 | 
					@ -227,7 +191,7 @@ void Context::initialize_debug_messenger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		.pfnUserCallback = &validation_layers_callback,
 | 
							.pfnUserCallback = &validation_layers_callback,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		.pUserData = &m_stats,
 | 
							.pUserData = {},
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ensure(
 | 
						ensure(
 | 
				
			||||||
| 
						 | 
					@ -236,242 +200,7 @@ void Context::initialize_debug_messenger()
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Context::initialize_physical_device()
 | 
					void Instance::load_library()
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto count = 0u;
 | 
					 | 
				
			||||||
	vkc(vk_enumerate_physical_devices(m_instance, &count, nullptr));
 | 
					 | 
				
			||||||
	ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto devices = std::vector<VkPhysicalDevice>(count);
 | 
					 | 
				
			||||||
	vkc(vk_enumerate_physical_devices(m_instance, &count, devices.data()));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (auto &device : devices)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto properties = VkPhysicalDeviceProperties {};
 | 
					 | 
				
			||||||
		auto features = VkPhysicalDeviceFeatures {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		vk_get_physical_device_properties(device, &properties);
 | 
					 | 
				
			||||||
		vk_get_physical_device_features(device, &features);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
 | 
					 | 
				
			||||||
		    && features.geometryShader)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			m_physical_device = device;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ensure(m_physical_device, "Failed to find any suitable Vulkan physical device");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Context::initialize_logical_device()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const float priorities = .0f;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto queue_info = VkDeviceQueueCreateInfo {
 | 
					 | 
				
			||||||
		.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
 | 
					 | 
				
			||||||
		.queueFamilyIndex = find_suitable_queue_family(),
 | 
					 | 
				
			||||||
		.queueCount = 1u,
 | 
					 | 
				
			||||||
		.pQueuePriorities = &priorities,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto physical_device_features = VkPhysicalDeviceFeatures {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto extensions = std::vector<const char *> {
 | 
					 | 
				
			||||||
		VK_KHR_SWAPCHAIN_EXTENSION_NAME,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto device_info = VkDeviceCreateInfo {
 | 
					 | 
				
			||||||
		.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
 | 
					 | 
				
			||||||
		.queueCreateInfoCount = 1,
 | 
					 | 
				
			||||||
		.pQueueCreateInfos = &queue_info,
 | 
					 | 
				
			||||||
		.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
 | 
					 | 
				
			||||||
		.ppEnabledExtensionNames = extensions.data(),
 | 
					 | 
				
			||||||
		.pEnabledFeatures = &physical_device_features,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ensuree
 | 
					 | 
				
			||||||
	    !vk_create_device(m_physical_device, &device_info, nullptr, &m_device),
 | 
					 | 
				
			||||||
	    "Failed to create logical vulkan device"
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Context::initialize_surface(const ecs::Entity &surface_entity)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const auto &component = surface_entity.get<surface::SurfaceComponent>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto create_info = VkXlibSurfaceCreateInfoKHR {
 | 
					 | 
				
			||||||
		.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
 | 
					 | 
				
			||||||
		.dpy = component.get_native_data().display,
 | 
					 | 
				
			||||||
		.window = component.get_native_data().window,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vkc(vk_create_xlib_surface_khr(m_instance, &create_info, nullptr, &m_surface));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const auto &[width, height] = component.get_resolution();
 | 
					 | 
				
			||||||
	m_framebuffer_size = {
 | 
					 | 
				
			||||||
		.width = width,
 | 
					 | 
				
			||||||
		.height = height,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Context::initialize_queue()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	vk_get_device_queue(m_device, find_suitable_queue_family(), 0, &m_queue);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto count = uint32_t { 0u };
 | 
					 | 
				
			||||||
	vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto properties = std::vector<VkQueueFamilyProperties>(count);
 | 
					 | 
				
			||||||
	vk_get_physical_device_queue_family_properties(m_physical_device, &count, properties.data());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (auto idx = uint32_t { 0u }; const auto &property : properties)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (property.queueFlags & VK_QUEUE_GRAPHICS_BIT)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			m_graphics_queue_family_index = idx;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto has_presentation_support = VkBool32 { false };
 | 
					 | 
				
			||||||
		vkc(vk_get_physical_device_surface_support(
 | 
					 | 
				
			||||||
		    m_physical_device,
 | 
					 | 
				
			||||||
		    idx,
 | 
					 | 
				
			||||||
		    m_surface,
 | 
					 | 
				
			||||||
		    &has_presentation_support
 | 
					 | 
				
			||||||
		));
 | 
					 | 
				
			||||||
		if (has_presentation_support)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			m_present_queue_family_index = idx;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		++idx;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED
 | 
					 | 
				
			||||||
		    && m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ensure(
 | 
					 | 
				
			||||||
	    m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
 | 
					 | 
				
			||||||
	    "Failed to find graphics queue family"
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ensure(
 | 
					 | 
				
			||||||
	    m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
 | 
					 | 
				
			||||||
	    "Failed to find presentation queue family"
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Context::initialize_swapchain()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto capabilities = VkSurfaceCapabilitiesKHR {};
 | 
					 | 
				
			||||||
	vkc(vk_get_physical_device_surface_capabilities(m_physical_device, m_surface, &capabilities));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto count = uint32_t { 0 };
 | 
					 | 
				
			||||||
	vkc(vk_get_physical_device_surface_formats(m_physical_device, m_surface, &count, nullptr));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto formats = std::vector<VkSurfaceFormatKHR>(count);
 | 
					 | 
				
			||||||
	vkc(
 | 
					 | 
				
			||||||
	    vk_get_physical_device_surface_formats(m_physical_device, m_surface, &count, formats.data())
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
	ensure(!formats.empty(), "Surface has no formats!");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO(Light): parameterize
 | 
					 | 
				
			||||||
	constexpr auto desired_swapchain_image_count = uint32_t { 3 };
 | 
					 | 
				
			||||||
	const auto surface_format = formats.front();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const auto queue_indices = std::array<uint32_t, 2> {
 | 
					 | 
				
			||||||
		m_graphics_queue_family_index,
 | 
					 | 
				
			||||||
		m_present_queue_family_index,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto create_info = VkSwapchainCreateInfoKHR {
 | 
					 | 
				
			||||||
		.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
 | 
					 | 
				
			||||||
		.surface = m_surface,
 | 
					 | 
				
			||||||
		.minImageCount = get_optimal_swapchain_image_count(
 | 
					 | 
				
			||||||
		    capabilities,
 | 
					 | 
				
			||||||
		    desired_swapchain_image_count
 | 
					 | 
				
			||||||
		),
 | 
					 | 
				
			||||||
		.imageFormat = surface_format.format,
 | 
					 | 
				
			||||||
		.imageColorSpace = surface_format.colorSpace,
 | 
					 | 
				
			||||||
		.imageExtent = m_framebuffer_size,
 | 
					 | 
				
			||||||
		.imageArrayLayers = 1u,
 | 
					 | 
				
			||||||
		.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
 | 
					 | 
				
			||||||
		.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
 | 
					 | 
				
			||||||
		.queueFamilyIndexCount = queue_indices.size(),
 | 
					 | 
				
			||||||
		.pQueueFamilyIndices = queue_indices.data(),
 | 
					 | 
				
			||||||
		.preTransform = capabilities.currentTransform,
 | 
					 | 
				
			||||||
		.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
 | 
					 | 
				
			||||||
		.presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR, // TODO(Light): parameterize
 | 
					 | 
				
			||||||
		.clipped = VK_TRUE,
 | 
					 | 
				
			||||||
		.oldSwapchain = nullptr,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vkc(vk_create_swapchain_khr(m_device, &create_info, nullptr, &m_swapchain));
 | 
					 | 
				
			||||||
	vkc(vk_device_wait_idle(m_device));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto image_count = uint32_t { 0u };
 | 
					 | 
				
			||||||
	vk_get_swapchain_images_khr(m_device, m_swapchain, &image_count, nullptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	m_swapchain_images.resize(image_count);
 | 
					 | 
				
			||||||
	m_swapchain_image_views.resize(image_count);
 | 
					 | 
				
			||||||
	vk_get_swapchain_images_khr(m_device, m_swapchain, &image_count, m_swapchain_images.data());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (auto [image, view] : std::views::zip(m_swapchain_images, m_swapchain_image_views))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto create_info = VkImageViewCreateInfo {
 | 
					 | 
				
			||||||
			.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
 | 
					 | 
				
			||||||
			.image = image,
 | 
					 | 
				
			||||||
			.viewType = VK_IMAGE_VIEW_TYPE_2D,
 | 
					 | 
				
			||||||
			.format = surface_format.format,
 | 
					 | 
				
			||||||
            .components = VkComponentMapping {
 | 
					 | 
				
			||||||
                .r = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
					 | 
				
			||||||
                .g = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
					 | 
				
			||||||
                .b = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
					 | 
				
			||||||
                .a = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            .subresourceRange = VkImageSubresourceRange {
 | 
					 | 
				
			||||||
                .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
 | 
					 | 
				
			||||||
                .baseMipLevel = 0u,
 | 
					 | 
				
			||||||
                .levelCount = 1u,
 | 
					 | 
				
			||||||
                .baseArrayLayer = 0u,
 | 
					 | 
				
			||||||
                .layerCount = 1u,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		vkc(vk_create_image_view(m_device, &create_info, nullptr, &view));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
auto Context::get_optimal_swapchain_image_count(
 | 
					 | 
				
			||||||
    VkSurfaceCapabilitiesKHR capabilities,
 | 
					 | 
				
			||||||
    uint32_t desired_image_count
 | 
					 | 
				
			||||||
) -> uint32_t
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const auto min_image_count = capabilities.minImageCount;
 | 
					 | 
				
			||||||
	const auto max_image_count = capabilities.maxImageCount;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const auto has_max_limit = max_image_count != 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Desired image count is in range
 | 
					 | 
				
			||||||
	if ((!has_max_limit || max_image_count >= desired_image_count)
 | 
					 | 
				
			||||||
	    && min_image_count <= desired_image_count)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return desired_image_count;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Fall-back to 2 if in ange
 | 
					 | 
				
			||||||
	if (min_image_count <= 2 && max_image_count >= 2)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return 2;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Fall-back to min_image_count
 | 
					 | 
				
			||||||
	return min_image_count;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Context::load_library()
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
 | 
						library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
 | 
				
			||||||
	ensure(library, "Failed to dlopen libvulkan.so");
 | 
						ensure(library, "Failed to dlopen libvulkan.so");
 | 
				
			||||||
| 
						 | 
					@ -483,7 +212,18 @@ void Context::load_library()
 | 
				
			||||||
	ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr");
 | 
						ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Context::load_global_functions()
 | 
					void Instance::unload_library()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!library)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// dlclose(library);
 | 
				
			||||||
 | 
						//  library = nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Instance::load_global_functions()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	constexpr auto load_fn = []<typename T>(T &pfn, const char *fn_name) {
 | 
						constexpr auto load_fn = []<typename T>(T &pfn, const char *fn_name) {
 | 
				
			||||||
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
							// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
				
			||||||
| 
						 | 
					@ -497,7 +237,7 @@ void Context::load_global_functions()
 | 
				
			||||||
	load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties");
 | 
						load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Context::load_instance_functions()
 | 
					void Instance::load_instance_functions()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
 | 
						const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
 | 
				
			||||||
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
							// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
				
			||||||
| 
						 | 
					@ -541,11 +281,11 @@ void Context::load_instance_functions()
 | 
				
			||||||
	load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR");
 | 
						load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Context::load_device_functions()
 | 
					void Instance::load_device_functions_impl(VkDevice device)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
 | 
						const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
 | 
				
			||||||
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
							// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
				
			||||||
		pfn = reinterpret_cast<T>(vk_get_device_proc_address(m_device, fn_name));
 | 
							pfn = reinterpret_cast<T>(vk_get_device_proc_address(device, fn_name));
 | 
				
			||||||
		ensure(pfn, "Failed to load vulkan device function: {}", fn_name);
 | 
							ensure(pfn, "Failed to load vulkan device function: {}", fn_name);
 | 
				
			||||||
		// log_trc("Loaded device function: {}", fn_name);
 | 
							// log_trc("Loaded device function: {}", fn_name);
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
| 
						 | 
					@ -592,28 +332,6 @@ void Context::load_device_functions()
 | 
				
			||||||
	load_fn(vk_cmd_set_scissors, "vkCmdSetScissor");
 | 
						load_fn(vk_cmd_set_scissors, "vkCmdSetScissor");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[nodiscard]] auto Context::find_suitable_queue_family() const -> uint32_t
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto count = 0u;
 | 
					 | 
				
			||||||
	vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
 | 
					 | 
				
			||||||
	ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto families = std::vector<VkQueueFamilyProperties>(count);
 | 
					 | 
				
			||||||
	vk_get_physical_device_queue_family_properties(m_physical_device, &count, families.data());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const auto required_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
 | 
					 | 
				
			||||||
	for (auto idx = 0u; auto &family : families)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if ((family.queueFlags & required_flags) == required_flags)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			return idx;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ensure(false, "Failed to find a suitable Vulkan queue family");
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *
 | 
					auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
 | 
						if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
 | 
				
			||||||
| 
						 | 
					@ -660,7 +378,11 @@ auto validation_layers_callback(
 | 
				
			||||||
    void *const vulkan_user_data
 | 
					    void *const vulkan_user_data
 | 
				
			||||||
) -> VkBool32
 | 
					) -> VkBool32
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						std::cout << callback_data->pMessage << std::endl;
 | 
				
			||||||
 | 
						return VK_FALSE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log_dbg("VALIDATION: {}", callback_data->pMessage);
 | 
						log_dbg("VALIDATION: {}", callback_data->pMessage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ensure(vulkan_user_data, "Validation layers's user data is not set!");
 | 
						ensure(vulkan_user_data, "Validation layers's user data is not set!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT
 | 
						auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT
 | 
				
			||||||
| 
						 | 
					@ -690,5 +412,4 @@ auto validation_layers_callback(
 | 
				
			||||||
	return static_cast<VkBool32>(VK_FALSE);
 | 
						return static_cast<VkBool32>(VK_FALSE);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace lt::renderer::vk
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
| 
						 | 
					@ -5,16 +5,17 @@
 | 
				
			||||||
#include <vulkan/vulkan.h>
 | 
					#include <vulkan/vulkan.h>
 | 
				
			||||||
#include <vulkan/vulkan_xlib.h>
 | 
					#include <vulkan/vulkan_xlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
#include <app/system.hpp>
 | 
					 | 
				
			||||||
#include <ecs/entity.hpp>
 | 
					 | 
				
			||||||
#include <memory/pointer_types/null_on_move.hpp>
 | 
					 | 
				
			||||||
#include <surface/components.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace lt::renderer::vk {
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using memory::NullOnMove;
 | 
					inline void vkc(VkResult result)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (result)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							throw std::runtime_error {
 | 
				
			||||||
 | 
								std::format("Vulkan call failed with result: {}", std::to_underlying(result))
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
 | 
					// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
 | 
				
			||||||
// global functions
 | 
					// global functions
 | 
				
			||||||
extern PFN_vkGetInstanceProcAddr vk_get_instance_proc_address;
 | 
					extern PFN_vkGetInstanceProcAddr vk_get_instance_proc_address;
 | 
				
			||||||
| 
						 | 
					@ -97,118 +98,64 @@ extern PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
 | 
				
			||||||
extern PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
 | 
					extern PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
 | 
				
			||||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
 | 
					// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline void vkc(VkResult result)
 | 
					/**
 | 
				
			||||||
{
 | 
					 * Responsible for dynamically loading Vulkan library/functions.
 | 
				
			||||||
	if (result)
 | 
					 *
 | 
				
			||||||
	{
 | 
					 * @note: The delayed vkInstance destruction is due to a work-around for libx11:
 | 
				
			||||||
		throw std::runtime_error {
 | 
					 * @ref:
 | 
				
			||||||
			std::format("Vulkan call failed with result: {}", std::to_underlying(result))
 | 
					 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/commit/0017308648b6bf8eef10ef0ffb9470576c0c2e9e
 | 
				
			||||||
		};
 | 
					 * https://www.xfree86.org/4.7.0/DRI11.html
 | 
				
			||||||
	}
 | 
					 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/1894
 | 
				
			||||||
}
 | 
					 */
 | 
				
			||||||
 | 
					class Instance
 | 
				
			||||||
class Context
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats);
 | 
						static auto get() -> VkInstance
 | 
				
			||||||
 | 
					 | 
				
			||||||
	~Context();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Context(Context &&other) noexcept = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto operator=(Context &&other) noexcept -> Context & = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Context(const Context &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto operator=(const Context &) -> Context & = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] auto instance() -> VkInstance
 | 
					 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_instance;
 | 
							return Instance::instance().m_instance;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto physical_device() -> VkPhysicalDevice
 | 
						static auto load_device_functions(VkDevice device)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_physical_device;
 | 
							instance().load_device_functions_impl(device);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto device() -> VkDevice
 | 
						Instance(Instance &&) = delete;
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_device;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto queue() -> VkQueue
 | 
						Instance(const Instance &) = delete;
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_queue;
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto debug_messenger() -> VkDebugUtilsMessengerEXT
 | 
						auto operator=(const Instance &) -> Instance & = delete;
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_debug_messenger;
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto get_stats() const -> const app::SystemStats &
 | 
						auto operator=(Instance &&) -> Instance & = delete;
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return *m_stats;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
						static auto instance() -> Instance &
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							static auto instance = Instance {};
 | 
				
			||||||
 | 
							return instance;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Instance();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~Instance();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void initialize_instance();
 | 
						void initialize_instance();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void initialize_debug_messenger();
 | 
						void initialize_debug_messenger();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void initialize_physical_device();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initialize_logical_device();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initialize_queue();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initialize_surface(const ecs::Entity &surface_entity);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initialize_swapchain();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void load_library();
 | 
						void load_library();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void unload_library();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void load_global_functions();
 | 
						void load_global_functions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void load_instance_functions();
 | 
						void load_instance_functions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void load_device_functions();
 | 
						void load_device_functions_impl(VkDevice device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto get_optimal_swapchain_image_count(
 | 
						VkInstance m_instance = VK_NULL_HANDLE;
 | 
				
			||||||
	    VkSurfaceCapabilitiesKHR capabilities,
 | 
					 | 
				
			||||||
	    uint32_t desired_image_count = 3
 | 
					 | 
				
			||||||
	) -> uint32_t;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto find_suitable_queue_family() const -> uint32_t;
 | 
						VkDebugUtilsMessengerEXT m_debug_messenger = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	Ref<ecs::Registry> m_registry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	NullOnMove<VkInstance> m_instance = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	NullOnMove<VkPhysicalDevice> m_physical_device = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	NullOnMove<VkDevice> m_device = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	NullOnMove<VkQueue> m_queue = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	NullOnMove<VkDebugUtilsMessengerEXT> m_debug_messenger = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	NullOnMove<VkSurfaceKHR> m_surface = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VkExtent2D m_framebuffer_size {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t m_graphics_queue_family_index = VK_QUEUE_FAMILY_IGNORED;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t m_present_queue_family_index = VK_QUEUE_FAMILY_IGNORED;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::vector<VkImage> m_swapchain_images;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::vector<VkImageView> m_swapchain_image_views;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Ref<app::SystemStats> m_stats;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace lt::renderer::vk
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										34
									
								
								modules/renderer/private/vk/context/surface.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								modules/renderer/private/vk/context/surface.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					#include <renderer/vk/context/surface.hpp>
 | 
				
			||||||
 | 
					#include <surface/components.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Surface::Surface(const ecs::Entity &surface_entity)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const auto &component = surface_entity.get<surface::SurfaceComponent>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto create_info = VkXlibSurfaceCreateInfoKHR {
 | 
				
			||||||
 | 
							.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
 | 
				
			||||||
 | 
							.dpy = component.get_native_data().display,
 | 
				
			||||||
 | 
							.window = component.get_native_data().window,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto *instance = Instance::get();
 | 
				
			||||||
 | 
						vkc(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()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (Instance::get())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							vk_destroy_surface_khr(Instance::get(), m_surface, nullptr);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										40
									
								
								modules/renderer/private/vk/context/surface.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								modules/renderer/private/vk/context/surface.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ecs/entity.hpp>
 | 
				
			||||||
 | 
					#include <memory/pointer_types/null_on_move.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/instance.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Surface
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						Surface(const ecs::Entity &surface_entity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~Surface();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Surface(Surface &&) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Surface(const Surface &) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(Surface &&) -> Surface & = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(const Surface &) const -> Surface & = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto vk() const -> VkSurfaceKHR
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_surface;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto get_framebuffer_size() const -> VkExtent2D
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_framebuffer_size;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						memory::NullOnMove<VkSurfaceKHR> m_surface = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						VkExtent2D m_framebuffer_size {};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										135
									
								
								modules/renderer/private/vk/context/swapchain.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								modules/renderer/private/vk/context/swapchain.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,135 @@
 | 
				
			||||||
 | 
					#include <ranges>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/swapchain.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Swapchain::Swapchain(const Device &device, const Surface &surface): m_device(device.vk())
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto *physical_device = device.physical();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto capabilities = VkSurfaceCapabilitiesKHR {};
 | 
				
			||||||
 | 
						vkc(vk_get_physical_device_surface_capabilities(physical_device, surface.vk(), &capabilities));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto count = uint32_t { 0 };
 | 
				
			||||||
 | 
						vkc(vk_get_physical_device_surface_formats(physical_device, surface.vk(), &count, nullptr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto formats = std::vector<VkSurfaceFormatKHR>(count);
 | 
				
			||||||
 | 
						vkc(vk_get_physical_device_surface_formats(
 | 
				
			||||||
 | 
						    physical_device,
 | 
				
			||||||
 | 
						    surface.vk(),
 | 
				
			||||||
 | 
						    &count,
 | 
				
			||||||
 | 
						    formats.data()
 | 
				
			||||||
 | 
						));
 | 
				
			||||||
 | 
						ensure(!formats.empty(), "Surface has no formats!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO(Light): parameterize
 | 
				
			||||||
 | 
						constexpr auto desired_swapchain_image_count = uint32_t { 3 };
 | 
				
			||||||
 | 
						const auto surface_format = formats.front();
 | 
				
			||||||
 | 
						const auto queue_indices = device.get_family_indices();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto create_info = VkSwapchainCreateInfoKHR {
 | 
				
			||||||
 | 
							.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
 | 
				
			||||||
 | 
							.surface = surface.vk(),
 | 
				
			||||||
 | 
							.minImageCount = get_optimal_image_count(capabilities, desired_swapchain_image_count),
 | 
				
			||||||
 | 
							.imageFormat = surface_format.format,
 | 
				
			||||||
 | 
							.imageColorSpace = surface_format.colorSpace,
 | 
				
			||||||
 | 
							.imageExtent = surface.get_framebuffer_size(),
 | 
				
			||||||
 | 
							.imageArrayLayers = 1u,
 | 
				
			||||||
 | 
							.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
 | 
				
			||||||
 | 
							.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
 | 
				
			||||||
 | 
							.queueFamilyIndexCount = queue_indices.size(),
 | 
				
			||||||
 | 
							.pQueueFamilyIndices = queue_indices.data(),
 | 
				
			||||||
 | 
							.preTransform = capabilities.currentTransform,
 | 
				
			||||||
 | 
							.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
 | 
				
			||||||
 | 
							.presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR, // TODO(Light): parameterize
 | 
				
			||||||
 | 
							.clipped = VK_TRUE,
 | 
				
			||||||
 | 
							.oldSwapchain = nullptr,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vkc(vk_create_swapchain_khr(device.vk(), &create_info, nullptr, &m_swapchain));
 | 
				
			||||||
 | 
						vkc(vk_device_wait_idle(device.vk()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto image_count = uint32_t { 0u };
 | 
				
			||||||
 | 
						vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_swapchain_images.resize(image_count);
 | 
				
			||||||
 | 
						m_swapchain_image_views.resize(image_count);
 | 
				
			||||||
 | 
						vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, m_swapchain_images.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto [image, view] : std::views::zip(m_swapchain_images, m_swapchain_image_views))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto create_info = VkImageViewCreateInfo {
 | 
				
			||||||
 | 
								.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
 | 
				
			||||||
 | 
								.image = image,
 | 
				
			||||||
 | 
								.viewType = VK_IMAGE_VIEW_TYPE_2D,
 | 
				
			||||||
 | 
								.format = surface_format.format,
 | 
				
			||||||
 | 
					            .components = VkComponentMapping {
 | 
				
			||||||
 | 
					                .r = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
				
			||||||
 | 
					                .g = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
				
			||||||
 | 
					                .b = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
				
			||||||
 | 
					                .a = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            .subresourceRange = VkImageSubresourceRange {
 | 
				
			||||||
 | 
					                .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
 | 
				
			||||||
 | 
					                .baseMipLevel = 0u,
 | 
				
			||||||
 | 
					                .levelCount = 1u,
 | 
				
			||||||
 | 
					                .baseArrayLayer = 0u,
 | 
				
			||||||
 | 
					                .layerCount = 1u,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vkc(vk_create_image_view(device.vk(), &create_info, nullptr, &view));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Swapchain::~Swapchain()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						try
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							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(
 | 
				
			||||||
 | 
					    VkSurfaceCapabilitiesKHR capabilities,
 | 
				
			||||||
 | 
					    uint32_t desired_image_count
 | 
				
			||||||
 | 
					) const -> uint32_t
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const auto min_image_count = capabilities.minImageCount;
 | 
				
			||||||
 | 
						const auto max_image_count = capabilities.maxImageCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto has_max_limit = max_image_count != 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Desired image count is in range
 | 
				
			||||||
 | 
						if ((!has_max_limit || max_image_count >= desired_image_count)
 | 
				
			||||||
 | 
						    && min_image_count <= desired_image_count)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return desired_image_count;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Fall-back to 2 if in ange
 | 
				
			||||||
 | 
						if (min_image_count <= 2 && max_image_count >= 2)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Fall-back to min_image_count
 | 
				
			||||||
 | 
						return min_image_count;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										39
									
								
								modules/renderer/private/vk/context/swapchain.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								modules/renderer/private/vk/context/swapchain.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,39 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <memory/pointer_types/null_on_move.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/device.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/instance.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Swapchain
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						Swapchain(const Device &device, const Surface &surface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~Swapchain();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Swapchain(Swapchain &&) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Swapchain(const Swapchain &) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(Swapchain &&) -> Swapchain & = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(const Swapchain &) const -> Swapchain & = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						[[nodiscard]] auto get_optimal_image_count(
 | 
				
			||||||
 | 
						    VkSurfaceCapabilitiesKHR capabilities,
 | 
				
			||||||
 | 
						    uint32_t desired_image_count
 | 
				
			||||||
 | 
						) const -> uint32_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memory::NullOnMove<VkDevice> m_device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memory::NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::vector<VkImage> m_swapchain_images;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::vector<VkImageView> m_swapchain_image_views;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										18
									
								
								modules/renderer/private/vk/pipeline.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								modules/renderer/private/vk/pipeline.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					#include <renderer/vk/pipeline.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pipeline::Pipeline(CreateInfo info): m_context(std::move(info.context))
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ensure(m_context, "Failed to create vk pipeline: null context");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pipeline::~Pipeline()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (m_context)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										36
									
								
								modules/renderer/private/vk/pipeline.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								modules/renderer/private/vk/pipeline.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <memory/pointer_types/null_on_move.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/context.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt::renderer::vk {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Pipeline
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						struct CreateInfo
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Ref<Context> context;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Pipeline(CreateInfo info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~Pipeline();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Pipeline(Pipeline &&) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Pipeline(const Pipeline &) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(Pipeline &&) -> Pipeline & = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto operator=(const Pipeline &) -> Pipeline & = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						VkPipeline m_pipeline = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						VkPipelineLayout m_pipeline_layout = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ref<Context> m_context;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; // namespace lt::renderer::vk
 | 
				
			||||||
							
								
								
									
										69
									
								
								modules/renderer/private/vk/pipeline.test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								modules/renderer/private/vk/pipeline.test.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,69 @@
 | 
				
			||||||
 | 
					#include <ranges>
 | 
				
			||||||
 | 
					#include <renderer/vk/context/context.hpp>
 | 
				
			||||||
 | 
					#include <renderer/vk/pipeline.hpp>
 | 
				
			||||||
 | 
					#include <surface/system.hpp>
 | 
				
			||||||
 | 
					#include <test/test.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace lt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using std::ignore;
 | 
				
			||||||
 | 
					using test::Case;
 | 
				
			||||||
 | 
					using test::expect_throw;
 | 
				
			||||||
 | 
					using test::expect_true;
 | 
				
			||||||
 | 
					using test::Suite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using renderer::vk::Context;
 | 
				
			||||||
 | 
					using renderer::vk::Pipeline;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr auto resolution = math::uvec2 { 800, 600 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VkPipelineTest
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						VkPipelineTest()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							m_registry = create_ref<ecs::Registry>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							m_surface_system = create_ref<surface::System>(m_registry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							m_surface_entity = create_scope<ecs::Entity>(m_registry, m_registry->create_entity());
 | 
				
			||||||
 | 
							m_surface_entity->add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
 | 
				
			||||||
 | 
							    .title = "",
 | 
				
			||||||
 | 
							    .resolution = resolution,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							m_context = create_ref<Context>(*m_surface_entity, m_stats);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto context() -> Ref<Context>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_context;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto device() -> VkDevice
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_context->device().vk();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						Ref<app::SystemStats> m_stats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ref<ecs::Registry> m_registry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ref<surface::System> m_surface_system;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Scope<ecs::Entity> m_surface_entity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ref<Context> m_context;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Suite raii = [] {
 | 
				
			||||||
 | 
						Case { "happy path won't throw" } = [] {
 | 
				
			||||||
 | 
							auto fixture = VkPipelineTest {};
 | 
				
			||||||
 | 
							std::ignore = Pipeline { { .context = fixture.context() } };
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Case { "unhappy path throws" } = [] {
 | 
				
			||||||
 | 
							expect_throw([] { std::ignore = Pipeline { { .context = nullptr } }; });
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -1,26 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define VK_NO_PROTOTYPES
 | 
					 | 
				
			||||||
#define VK_USE_PLATFORM_XLIB_KHR
 | 
					 | 
				
			||||||
#include <vulkan/vulkan.h>
 | 
					 | 
				
			||||||
#include <vulkan/vulkan_xlib.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
#include <ecs/entity.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace lt::renderer::vk {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Surface
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	Surface(ecs::Entity entity)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~Surface();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	VkSurfaceKHR m_surface = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace lt::renderer::vk
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,22 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define VK_NO_PROTOTYPES
 | 
					 | 
				
			||||||
#include <vulkan/vulkan.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace lt::renderer::vk {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Swapchain
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	Swapchain()
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	VkSwapchainKHR m_swapchain {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::vector<VkImage> images;
 | 
					 | 
				
			||||||
	std::vector<VkImageView> image_views;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace lt::renderer::vk
 | 
					 | 
				
			||||||
| 
						 | 
					@ -2,8 +2,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <app/system.hpp>
 | 
					#include <app/system.hpp>
 | 
				
			||||||
#include <ecs/entity.hpp>
 | 
					#include <ecs/entity.hpp>
 | 
				
			||||||
#include <renderer/validation.hpp>
 | 
					#include <renderer/vk/context/context.hpp>
 | 
				
			||||||
#include <renderer/vk/context.hpp>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace lt::renderer {
 | 
					namespace lt::renderer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,8 +18,10 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] System(CreateInfo info)
 | 
						[[nodiscard]] System(CreateInfo info)
 | 
				
			||||||
	    : m_registry(std::move(info.registry))
 | 
						    : m_registry(std::move(info.registry))
 | 
				
			||||||
	    , m_context(info.surface_entity, std::move(info.system_stats))
 | 
						    , m_stats(info.system_stats)
 | 
				
			||||||
 | 
						    , m_context(info.surface_entity, info.system_stats)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							ensure(m_stats, "Failed to initialize system: null stats");
 | 
				
			||||||
		ensure(m_registry, "Failed to initialize renderer system: null registry");
 | 
							ensure(m_registry, "Failed to initialize renderer system: null registry");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,7 +51,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto get_stats() const -> const app::SystemStats &
 | 
						[[nodiscard]] auto get_stats() const -> const app::SystemStats &
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_context.get_stats();
 | 
							return *m_stats;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override
 | 
						[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override
 | 
				
			||||||
| 
						 | 
					@ -61,7 +62,7 @@ public:
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	Ref<ecs::Registry> m_registry;
 | 
						Ref<ecs::Registry> m_registry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	renderer::Validation m_validation;
 | 
						Ref<app::SystemStats> m_stats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vk::Context m_context;
 | 
						vk::Context m_context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue