Compare commits
	
		
			No commits in common. "49767732186eb4167e0a2a938118c5af9aeb55c3" and "607e6864b457eda6a02b4ec7a08d32a603e6d4f7" have entirely different histories.
		
	
	
		
			4976773218
			...
			607e6864b4
		
	
		
					 114 changed files with 5343 additions and 2274 deletions
				
			
		| 
						 | 
				
			
			@ -8,7 +8,6 @@ using lt::test::expect_unreachable;
 | 
			
		|||
using lt::test::Suite;
 | 
			
		||||
 | 
			
		||||
using lt::test::expect_eq;
 | 
			
		||||
using lt::test::expect_ne;
 | 
			
		||||
 | 
			
		||||
using lt::test::expect_false;
 | 
			
		||||
using lt::test::expect_true;
 | 
			
		||||
| 
						 | 
				
			
			@ -63,12 +62,12 @@ struct std::formatter<Component_B>
 | 
			
		|||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite raii = "raii"_suite = [] {
 | 
			
		||||
Suite raii = [] {
 | 
			
		||||
	Case { "happy path won't throw" } = [] {
 | 
			
		||||
		std::ignore = Registry {};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "many won't freeze/throw" } = [] {
 | 
			
		||||
	Case { "many won't throw" } = [] {
 | 
			
		||||
		for (auto idx : std::views::iota(0, 100'000))
 | 
			
		||||
		{
 | 
			
		||||
			std::ignore = Registry {};
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +83,7 @@ Suite raii = "raii"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite entity_raii = "entity_raii"_suite = [] {
 | 
			
		||||
Suite entity_raii = [] {
 | 
			
		||||
	Case { "create_entity returns unique values" } = [] {
 | 
			
		||||
		auto registry = Registry {};
 | 
			
		||||
		auto set = std::unordered_set<EntityId> {};
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +119,7 @@ Suite entity_raii = "entity_raii"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite component_raii = "component_raii"_suite = [] {
 | 
			
		||||
Suite component_raii = [] {
 | 
			
		||||
	Case { "add has correct state" } = [] {
 | 
			
		||||
		auto registry = Registry {};
 | 
			
		||||
		for (auto idx : std::views::iota(0, 100'000))
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +130,7 @@ Suite component_raii = "component_raii"_suite = [] {
 | 
			
		|||
			    { .m_int = idx, .m_string = std::to_string(idx) }
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			expect_ne(component.m_int, idx);
 | 
			
		||||
			expect_eq(component.m_int, idx);
 | 
			
		||||
			expect_eq(component.m_string, std::to_string(idx));
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +151,7 @@ Suite component_raii = "component_raii"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite callbacks = "callbacks"_suite = [] {
 | 
			
		||||
Suite callbacks = [] {
 | 
			
		||||
	Case { "connecting on_construct/on_destruct won't throw" } = [] {
 | 
			
		||||
		auto registry = Registry {};
 | 
			
		||||
		registry.connect_on_construct<Component>([&](Registry &, EntityId) {});
 | 
			
		||||
| 
						 | 
				
			
			@ -206,7 +205,7 @@ Suite callbacks = "callbacks"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite each = "each"_suite = [] {
 | 
			
		||||
Suite each = [] {
 | 
			
		||||
	auto registry = Registry {};
 | 
			
		||||
 | 
			
		||||
	auto shared_entity_counter = 0u;
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +276,7 @@ Suite each = "each"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite views = "views"_suite = [] {
 | 
			
		||||
Suite views = [] {
 | 
			
		||||
	auto registry = Registry {};
 | 
			
		||||
 | 
			
		||||
	auto shared_entity_counter = 0u;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ using lt::test::expect_true;
 | 
			
		|||
using Set = lt::ecs::SparseSet<int>;
 | 
			
		||||
constexpr auto capacity = 100;
 | 
			
		||||
 | 
			
		||||
Suite raii = "raii"_suite = [] {
 | 
			
		||||
Suite raii = [] {
 | 
			
		||||
	Case { "happy path won't throw" } = [] {
 | 
			
		||||
		std::ignore = Set {};
 | 
			
		||||
		std::ignore = Set { Set::max_capacity };
 | 
			
		||||
| 
						 | 
				
			
			@ -32,8 +32,8 @@ Suite raii = "raii"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite element_raii = "element_raii"_suite = [] {
 | 
			
		||||
	Case { "many inserts/removes won't freeze/throw" } = [] {
 | 
			
		||||
Suite element_raii = [] {
 | 
			
		||||
	Case { "many inserts/removes won't throw" } = [] {
 | 
			
		||||
		auto set = Set {};
 | 
			
		||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +107,7 @@ Suite element_raii = "element_raii"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite getters = "getters"_suite = [] {
 | 
			
		||||
Suite getters = [] {
 | 
			
		||||
	Case { "get_size returns correct values" } = [] {
 | 
			
		||||
		auto set = Set {};
 | 
			
		||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +149,7 @@ Suite getters = "getters"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite clear = "clear"_suite = [] {
 | 
			
		||||
Suite clear = [] {
 | 
			
		||||
	Case { "post clear has correct state" } = [] {
 | 
			
		||||
		auto set = Set { 0 };
 | 
			
		||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,12 +15,6 @@ public:
 | 
			
		|||
		ensure(m_registry, "Failed to create Entity ({}): null registry", m_identifier);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template<typename Component_T>
 | 
			
		||||
	auto add(Component_T component) -> Component_T &
 | 
			
		||||
	{
 | 
			
		||||
		return m_registry->add(m_identifier, component);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template<typename Component_T>
 | 
			
		||||
	auto get() -> Component_T &
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,7 +58,7 @@ private:
 | 
			
		|||
	Ref<ecs::Registry> m_registry = create_ref<ecs::Registry>();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite raii = "raii"_suite = "raii"_suite = [] {
 | 
			
		||||
Suite raii = [] {
 | 
			
		||||
	Case { "happy path won't throw" } = [&] {
 | 
			
		||||
		System { Fixture {}.registry() };
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +76,7 @@ Suite raii = "raii"_suite = "raii"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite system_events = "system_events"_suite = [] {
 | 
			
		||||
Suite system_events = [] {
 | 
			
		||||
	Case { "on_register won't throw" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto registry = fixture.registry();
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +97,7 @@ Suite system_events = "system_events"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite registry_events = "registry_events"_suite = [] {
 | 
			
		||||
Suite registry_events = [] {
 | 
			
		||||
	Case { "on_construct<InputComnent>" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto registry = fixture.registry();
 | 
			
		||||
| 
						 | 
				
			
			@ -127,7 +127,7 @@ Suite registry_events = "registry_events"_suite = [] {
 | 
			
		|||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite tick = "tick"_suite = [] {
 | 
			
		||||
Suite tick = [] {
 | 
			
		||||
	Case { "Empty tick won't throw" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto registry = fixture.registry();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,37 +1,15 @@
 | 
			
		|||
add_library_module(renderer
 | 
			
		||||
    system.cpp
 | 
			
		||||
    vk/debug/messenger.cpp
 | 
			
		||||
    vk/context/instance.cpp
 | 
			
		||||
    vk/context/surface.cpp
 | 
			
		||||
    vk/context/device.cpp
 | 
			
		||||
    vk/context/swapchain.cpp
 | 
			
		||||
    vk/context/context.cpp
 | 
			
		||||
    vk/pipeline.cpp
 | 
			
		||||
    vk/context.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(renderer
 | 
			
		||||
PUBLIC 
 | 
			
		||||
    app 
 | 
			
		||||
    ecs
 | 
			
		||||
    memory
 | 
			
		||||
PRIVATE
 | 
			
		||||
    surface
 | 
			
		||||
    pthread
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_test_module(renderer 
 | 
			
		||||
    system.test.cpp
 | 
			
		||||
    vk/test_utils.cpp
 | 
			
		||||
    vk/debug/messenger.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
 | 
			
		||||
) 
 | 
			
		||||
target_link_libraries(renderer_tests
 | 
			
		||||
PRIVATE
 | 
			
		||||
    surface
 | 
			
		||||
    pthread
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								modules/renderer/legacy/private/backend.test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								modules/renderer/legacy/private/backend.test.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
#include <ranges>
 | 
			
		||||
#include <renderer/system.hpp>
 | 
			
		||||
#include <test/test.hpp>
 | 
			
		||||
 | 
			
		||||
using namespace lt;
 | 
			
		||||
#include <renderer/backend.hpp>
 | 
			
		||||
 | 
			
		||||
using lt::test::Case;
 | 
			
		||||
using lt::test::Suite;
 | 
			
		||||
 | 
			
		||||
Suite raii = [] {
 | 
			
		||||
	Case { "happy path won't throw" } = [] {
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										35
									
								
								modules/renderer/legacy/private/blender.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								modules/renderer/legacy/private/blender.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
#include <renderer/blender.hpp>
 | 
			
		||||
#include <renderer/gl/blender.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef LIGHT_PLATFORM_WINDOWS
 | 
			
		||||
	#include <renderer/dx/blender.hpp>
 | 
			
		||||
	#include <renderer/dx/sharedcontext.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
auto Blender::create(const Ref<SharedContext> & /*sharedContext*/) -> Scope<Blender>
 | 
			
		||||
{
 | 
			
		||||
	switch (GraphicsContext::get_graphics_api())
 | 
			
		||||
	{
 | 
			
		||||
	case GraphicsAPI::OpenGL: return create_scope<glBlender>();
 | 
			
		||||
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    return create_scope<dxBlender>(
 | 
			
		||||
		               std::static_pointer_cast<dxSharedContext>(sharedContext)
 | 
			
		||||
		    );
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		    default
 | 
			
		||||
		    : ensure(
 | 
			
		||||
		          false,
 | 
			
		||||
		          "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		          static_cast<uint32_t>(GraphicsContext::get_graphics_api())
 | 
			
		||||
		      );
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										104
									
								
								modules/renderer/legacy/private/buffers.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								modules/renderer/legacy/private/buffers.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
#include <lt_debug/assertions.hpp>
 | 
			
		||||
#include <renderer/buffers.hpp>
 | 
			
		||||
#include <renderer/gl/buffers.hpp>
 | 
			
		||||
#include <renderer/shared_context.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef LIGHT_PLATFORM_WINDOWS
 | 
			
		||||
	#include <renderer/dx/buffers.hpp>
 | 
			
		||||
	#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
auto ConstantBuffer::create(
 | 
			
		||||
    ConstantBufferIndex index,
 | 
			
		||||
    unsigned int size,
 | 
			
		||||
    const Ref<SharedContext> & /*sharedContext*/
 | 
			
		||||
) -> Scope<ConstantBuffer>
 | 
			
		||||
{
 | 
			
		||||
	switch (GraphicsContext::get_graphics_api())
 | 
			
		||||
	{
 | 
			
		||||
	case GraphicsAPI::OpenGL: return create_scope<glConstantBuffer>(index, size);
 | 
			
		||||
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    return create_scope<dxConstantBuffer>(
 | 
			
		||||
		               index,
 | 
			
		||||
		               size,
 | 
			
		||||
		               std::static_pointer_cast<dxSharedContext>(sharedContext)
 | 
			
		||||
		    );
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		    default
 | 
			
		||||
		    : ensure(
 | 
			
		||||
		          false,
 | 
			
		||||
		          "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		          static_cast<uint32_t>(GraphicsContext::get_graphics_api())
 | 
			
		||||
		      );
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto VertexBuffer::create(
 | 
			
		||||
    float *vertices,
 | 
			
		||||
    unsigned int stride,
 | 
			
		||||
    unsigned int count,
 | 
			
		||||
    const Ref<SharedContext> & /*sharedContext*/
 | 
			
		||||
) -> Ref<VertexBuffer>
 | 
			
		||||
{
 | 
			
		||||
	switch (GraphicsContext::get_graphics_api())
 | 
			
		||||
	{
 | 
			
		||||
	case GraphicsAPI::OpenGL: return create_ref<glVertexBuffer>(vertices, stride, count);
 | 
			
		||||
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    return create_ref<dxVertexBuffer>(
 | 
			
		||||
		               vertices,
 | 
			
		||||
		               stride,
 | 
			
		||||
		               count,
 | 
			
		||||
		               std::static_pointer_cast<dxSharedContext>(sharedContext)
 | 
			
		||||
		    );
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		    default
 | 
			
		||||
		    : ensure(
 | 
			
		||||
		          false,
 | 
			
		||||
		          "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		          static_cast<uint32_t>(GraphicsContext::get_graphics_api())
 | 
			
		||||
		      );
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto IndexBuffer::create(
 | 
			
		||||
    unsigned int *indices,
 | 
			
		||||
    unsigned int count,
 | 
			
		||||
    const Ref<SharedContext> & /*sharedContext*/
 | 
			
		||||
) -> Ref<IndexBuffer>
 | 
			
		||||
{
 | 
			
		||||
	switch (GraphicsContext::get_graphics_api())
 | 
			
		||||
	{
 | 
			
		||||
	case GraphicsAPI::OpenGL: return create_ref<glIndexBuffer>(indices, count);
 | 
			
		||||
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    return create_ref<dxIndexBuffer>(
 | 
			
		||||
		               indices,
 | 
			
		||||
		               count,
 | 
			
		||||
		               std::dynamic_pointer_cast<dxSharedContext>(sharedContext)
 | 
			
		||||
		    );
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		    default
 | 
			
		||||
		    : ensure(
 | 
			
		||||
		          false,
 | 
			
		||||
		          "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		          static_cast<uint32_t>(GraphicsContext::get_graphics_api())
 | 
			
		||||
		      );
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										81
									
								
								modules/renderer/legacy/private/dx/blender.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								modules/renderer/legacy/private/dx/blender.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
#include <renderer/dx/blender.hpp>
 | 
			
		||||
#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
dxBlender::dxBlender(Ref<dxSharedContext> sharedContext)
 | 
			
		||||
    : m_context(sharedContext), m_factor_map { // constants
 | 
			
		||||
	                                          { BlendFactor::ZERO, D3D11_BLEND_ZERO },
 | 
			
		||||
	                                          { BlendFactor::ONE, D3D11_BLEND_ONE },
 | 
			
		||||
 | 
			
		||||
	                                          // source
 | 
			
		||||
	                                          { BlendFactor::SRC_COLOR, D3D11_BLEND_SRC_COLOR },
 | 
			
		||||
	                                          { BlendFactor::INVERSE_SRC_COLOR, D3D11_BLEND_INV_SRC_COLOR },
 | 
			
		||||
 | 
			
		||||
	                                          { BlendFactor::SRC_ALPHA, D3D11_BLEND_SRC_ALPHA },
 | 
			
		||||
	                                          { BlendFactor::INVERSE_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA },
 | 
			
		||||
 | 
			
		||||
	                                          // destination
 | 
			
		||||
	                                          { BlendFactor::DST_COLOR, D3D11_BLEND_DEST_COLOR },
 | 
			
		||||
	                                          { BlendFactor::INVERSE_DST_COLOR, D3D11_BLEND_INV_DEST_COLOR },
 | 
			
		||||
 | 
			
		||||
	                                          { BlendFactor::DST_ALPHA, D3D11_BLEND_DEST_ALPHA },
 | 
			
		||||
	                                          { BlendFactor::INVERSE_DST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA },
 | 
			
		||||
 | 
			
		||||
	                                          // source1
 | 
			
		||||
	                                          { BlendFactor::SRC1_COLOR, D3D11_BLEND_SRC1_COLOR },
 | 
			
		||||
	                                          { BlendFactor::INVERSE_SRC1_COLOR, D3D11_BLEND_INV_SRC1_COLOR },
 | 
			
		||||
 | 
			
		||||
	                                          { BlendFactor::SRC1_ALPHA, D3D11_BLEND_SRC1_ALPHA },
 | 
			
		||||
	                                          { BlendFactor::INVERSE_SRC1_ALPHA, D3D11_BLEND_INV_SRC1_ALPHA }
 | 
			
		||||
    }
 | 
			
		||||
    , m_blend_state(nullptr)
 | 
			
		||||
    , m_desc {}
 | 
			
		||||
{
 | 
			
		||||
	// factor map
 | 
			
		||||
	// blender desc
 | 
			
		||||
	m_desc = {};
 | 
			
		||||
 | 
			
		||||
	m_desc.RenderTarget[0].BlendEnable = true;
 | 
			
		||||
	m_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ZERO;
 | 
			
		||||
	m_desc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;
 | 
			
		||||
	m_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
 | 
			
		||||
	m_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
 | 
			
		||||
	m_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
 | 
			
		||||
	m_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
 | 
			
		||||
	m_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
 | 
			
		||||
 | 
			
		||||
	// create blend state
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateBlendState(&m_desc, &m_blend_state));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxBlender::enable(BlendFactor srcFactor, BlendFactor dstFactor)
 | 
			
		||||
{
 | 
			
		||||
	// update desc
 | 
			
		||||
	m_desc.RenderTarget[0].BlendEnable = true;
 | 
			
		||||
	m_desc.RenderTarget[0].SrcBlend = m_factor_map.at(srcFactor);
 | 
			
		||||
	m_desc.RenderTarget[0].DestBlend = m_factor_map.at(dstFactor);
 | 
			
		||||
 | 
			
		||||
	// re-create blind state
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateBlendState(&m_desc, &m_blend_state));
 | 
			
		||||
 | 
			
		||||
	// bind blend state
 | 
			
		||||
	m_context->get_device_context()->OMSetBlendState(m_blend_state.Get(), nullptr, 0x0000000f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxBlender::disable()
 | 
			
		||||
{
 | 
			
		||||
	// update desc
 | 
			
		||||
	m_desc.RenderTarget[0].BlendEnable = false;
 | 
			
		||||
 | 
			
		||||
	// re-create blind state
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateBlendState(&m_desc, &m_blend_state));
 | 
			
		||||
 | 
			
		||||
	// bind blend state
 | 
			
		||||
	m_context->get_device_context()->OMSetBlendState(m_blend_state.Get(), nullptr, 0xffffffff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										30
									
								
								modules/renderer/legacy/private/dx/blender.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								modules/renderer/legacy/private/dx/blender.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <renderer/blender.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class dxSharedContext;
 | 
			
		||||
 | 
			
		||||
class dxBlender: public Blender
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxBlender(Ref<dxSharedContext> sharedContext);
 | 
			
		||||
 | 
			
		||||
	void enable(BlendFactor srcFactor, BlendFactor dstFactor) override;
 | 
			
		||||
 | 
			
		||||
	void disable() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<dxSharedContext> m_context;
 | 
			
		||||
 | 
			
		||||
	const std::unordered_map<BlendFactor, D3D11_BLEND> m_factor_map;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11BlendState> m_blend_state;
 | 
			
		||||
 | 
			
		||||
	D3D11_BLEND_DESC m_desc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										188
									
								
								modules/renderer/legacy/private/dx/buffers.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								modules/renderer/legacy/private/dx/buffers.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,188 @@
 | 
			
		|||
#include <renderer/dx/buffers.hpp>
 | 
			
		||||
#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
//======================================== CONSTANT_BUFFER
 | 
			
		||||
//========================================//
 | 
			
		||||
dxConstantBuffer::dxConstantBuffer(
 | 
			
		||||
    ConstantBufferIndex index,
 | 
			
		||||
    unsigned int size,
 | 
			
		||||
    Ref<dxSharedContext> sharedContext
 | 
			
		||||
)
 | 
			
		||||
    : m_context(sharedContext)
 | 
			
		||||
    , m_buffer(nullptr)
 | 
			
		||||
    , m_map {}
 | 
			
		||||
    , m_index(static_cast<int>(index))
 | 
			
		||||
{
 | 
			
		||||
	auto bDesc = D3D11_BUFFER_DESC {};
 | 
			
		||||
 | 
			
		||||
	bDesc.ByteWidth = size;
 | 
			
		||||
	bDesc.Usage = D3D11_USAGE_DYNAMIC;
 | 
			
		||||
	bDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
 | 
			
		||||
	bDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
 | 
			
		||||
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateBuffer(&bDesc, nullptr, &m_buffer));
 | 
			
		||||
	m_context->get_device_context()->VSSetConstantBuffers(m_index, 1u, m_buffer.GetAddressOf());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxConstantBuffer::bind()
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->VSSetConstantBuffers(m_index, 1u, m_buffer.GetAddressOf());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto dxConstantBuffer::map() -> void *
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->VSSetConstantBuffers(m_index, 1u, m_buffer.GetAddressOf());
 | 
			
		||||
	m_context->get_device_context()
 | 
			
		||||
	    ->map(m_buffer.Get(), NULL, D3D11_MAP_WRITE_DISCARD, NULL, &m_map);
 | 
			
		||||
	return m_map.pData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxConstantBuffer::un_map()
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->Unmap(m_buffer.Get(), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dxVertexBuffer::dxVertexBuffer(
 | 
			
		||||
    float *vertices,
 | 
			
		||||
    unsigned int stride,
 | 
			
		||||
    unsigned int count,
 | 
			
		||||
    Ref<dxSharedContext> sharedContext
 | 
			
		||||
)
 | 
			
		||||
    : m_context(sharedContext)
 | 
			
		||||
    , m_buffer(nullptr)
 | 
			
		||||
    , m_map {}
 | 
			
		||||
    , m_stride(stride)
 | 
			
		||||
{
 | 
			
		||||
	// buffer desc
 | 
			
		||||
	auto bDesc = D3D11_BUFFER_DESC {};
 | 
			
		||||
 | 
			
		||||
	bDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
 | 
			
		||||
	bDesc.Usage = D3D11_USAGE_DYNAMIC;
 | 
			
		||||
	bDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
 | 
			
		||||
 | 
			
		||||
	bDesc.ByteWidth = count * stride;
 | 
			
		||||
	bDesc.StructureByteStride = stride;
 | 
			
		||||
 | 
			
		||||
	// create buffer
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateBuffer(&bDesc, nullptr, &m_buffer));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dxVertexBuffer::~dxVertexBuffer()
 | 
			
		||||
{
 | 
			
		||||
	un_bind();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto dxVertexBuffer::map() -> void *
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()
 | 
			
		||||
	    ->map(m_buffer.Get(), NULL, D3D11_MAP_WRITE_DISCARD, NULL, &m_map);
 | 
			
		||||
	return m_map.pData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxVertexBuffer::un_map()
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->Unmap(m_buffer.Get(), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxVertexBuffer::bind()
 | 
			
		||||
{
 | 
			
		||||
	static const auto offset = unsigned int { 0u };
 | 
			
		||||
	m_context->get_device_context()
 | 
			
		||||
	    ->IASetVertexBuffers(0u, 1u, m_buffer.GetAddressOf(), &m_stride, &offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxVertexBuffer::un_bind()
 | 
			
		||||
{
 | 
			
		||||
	static const auto offset = unsigned int { 0u };
 | 
			
		||||
	static auto *buffer = (ID3D11Buffer *)(nullptr);
 | 
			
		||||
 | 
			
		||||
	m_context->get_device_context()->IASetVertexBuffers(0u, 1u, &buffer, &m_stride, &offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dxIndexBuffer::dxIndexBuffer(
 | 
			
		||||
    unsigned int *indices,
 | 
			
		||||
    unsigned int count,
 | 
			
		||||
    Ref<dxSharedContext> sharedContext
 | 
			
		||||
)
 | 
			
		||||
    : m_context(sharedContext)
 | 
			
		||||
    , m_buffer(nullptr)
 | 
			
		||||
{
 | 
			
		||||
	// generate indices if not provided
 | 
			
		||||
	auto hasIndices = !!indices;
 | 
			
		||||
	if (!hasIndices)
 | 
			
		||||
	{
 | 
			
		||||
		// check
 | 
			
		||||
		if (count % 6 != 0)
 | 
			
		||||
		{
 | 
			
		||||
			log_wrn("'indices' can only be null if count is multiple of 6");
 | 
			
		||||
			lt_log(
 | 
			
		||||
			    warn,
 | 
			
		||||
			    "Adding {} to 'count' -> {}",
 | 
			
		||||
			    (6 - (count % 6)),
 | 
			
		||||
			    count + (6 - (count % 6))
 | 
			
		||||
			);
 | 
			
		||||
			count = count + (6 - (count % 6));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// create indices
 | 
			
		||||
		indices = new unsigned int[count];
 | 
			
		||||
		auto offset = 0;
 | 
			
		||||
		for (unsigned int i = 0; i < count; i += 6)
 | 
			
		||||
		{
 | 
			
		||||
			indices[i + 0] = offset + 0u;
 | 
			
		||||
			indices[i + 1] = offset + 3u;
 | 
			
		||||
			indices[i + 2] = offset + 2u;
 | 
			
		||||
 | 
			
		||||
			indices[i + 3] = offset + 2u;
 | 
			
		||||
			indices[i + 4] = offset + 1u;
 | 
			
		||||
			indices[i + 5] = offset + 0u;
 | 
			
		||||
 | 
			
		||||
			offset += 4u;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// buffer desc
 | 
			
		||||
	auto bDesc = D3D11_BUFFER_DESC {};
 | 
			
		||||
	bDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
 | 
			
		||||
	bDesc.Usage = D3D11_USAGE_DEFAULT;
 | 
			
		||||
 | 
			
		||||
	bDesc.ByteWidth = count * sizeof(unsigned int);
 | 
			
		||||
	bDesc.StructureByteStride = sizeof(unsigned int);
 | 
			
		||||
 | 
			
		||||
	// subresource data
 | 
			
		||||
	auto sDesc = D3D11_SUBRESOURCE_DATA {};
 | 
			
		||||
	sDesc.pSysMem = indices;
 | 
			
		||||
 | 
			
		||||
	// create buffer
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateBuffer(&bDesc, &sDesc, &m_buffer));
 | 
			
		||||
 | 
			
		||||
	// delete indices
 | 
			
		||||
	if (!hasIndices)
 | 
			
		||||
		delete[] indices;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dxIndexBuffer::~dxIndexBuffer()
 | 
			
		||||
{
 | 
			
		||||
	un_bind();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxIndexBuffer::bind()
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->IASetIndexBuffer(m_buffer.Get(), DXGI_FORMAT_R32_UINT, 0u);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxIndexBuffer::un_bind()
 | 
			
		||||
{
 | 
			
		||||
	static const auto offset = (unsigned int) { 0u };
 | 
			
		||||
	static auto *buffer = (ID3D11Buffer *)(nullptr);
 | 
			
		||||
 | 
			
		||||
	m_context->get_device_context()->IASetIndexBuffer(buffer, DXGI_FORMAT_R32_UINT, offset);
 | 
			
		||||
}
 | 
			
		||||
//======================================== INDEX_BUFFER ========================================//
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										83
									
								
								modules/renderer/legacy/private/dx/buffers.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								modules/renderer/legacy/private/dx/buffers.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,83 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <renderer/buffers.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class dxSharedContext;
 | 
			
		||||
 | 
			
		||||
class dxConstantBuffer: public ConstantBuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxConstantBuffer(
 | 
			
		||||
	    ConstantBufferIndex index,
 | 
			
		||||
	    unsigned int size,
 | 
			
		||||
	    Ref<dxSharedContext> sharedContext
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	void *map() override;
 | 
			
		||||
 | 
			
		||||
	void un_map() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<dxSharedContext> m_context;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11Buffer> m_buffer;
 | 
			
		||||
 | 
			
		||||
	D3D11_MAPPED_SUBRESOURCE m_map;
 | 
			
		||||
 | 
			
		||||
	unsigned int m_index;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class dxVertexBuffer: public VertexBuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxVertexBuffer(
 | 
			
		||||
	    float *vertices,
 | 
			
		||||
	    unsigned int stride,
 | 
			
		||||
	    unsigned int count,
 | 
			
		||||
	    Ref<dxSharedContext> sharedContext
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	~dxVertexBuffer();
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	void un_bind() override;
 | 
			
		||||
 | 
			
		||||
	auto map() -> void * override;
 | 
			
		||||
 | 
			
		||||
	void un_map() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<dxSharedContext> m_context;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11Buffer> m_buffer;
 | 
			
		||||
 | 
			
		||||
	D3D11_MAPPED_SUBRESOURCE m_map;
 | 
			
		||||
 | 
			
		||||
	unsigned int m_stride;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class dxIndexBuffer: public IndexBuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxIndexBuffer(unsigned int *indices, unsigned int count, Ref<dxSharedContext> sharedContext);
 | 
			
		||||
 | 
			
		||||
	~dxIndexBuffer();
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	void un_bind() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<dxSharedContext> m_context;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11Buffer> m_buffer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										113
									
								
								modules/renderer/legacy/private/dx/framebuffers.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								modules/renderer/legacy/private/dx/framebuffers.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
#include <renderer/dx/framebuffers.hpp>
 | 
			
		||||
#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
dxFramebuffer::dxFramebuffer(
 | 
			
		||||
    const FramebufferSpecification &specification,
 | 
			
		||||
    Ref<dxSharedContext> sharedContext
 | 
			
		||||
)
 | 
			
		||||
    : m_context(sharedContext)
 | 
			
		||||
    , m_specification(specification)
 | 
			
		||||
    , m_render_target_view(nullptr)
 | 
			
		||||
    , m_color_attachment(nullptr)
 | 
			
		||||
    , m_depth_stencil_attachment(nullptr)
 | 
			
		||||
    , m_shader_resource_view(nullptr)
 | 
			
		||||
    , m_depth_stencil_view(nullptr)
 | 
			
		||||
{
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
 | 
			
		||||
	auto t2dDesc = D3D11_TEXTURE2D_DESC {};
 | 
			
		||||
	t2dDesc.Width = specification.width;
 | 
			
		||||
	t2dDesc.Height = specification.height;
 | 
			
		||||
	t2dDesc.MipLevels = 1;
 | 
			
		||||
	t2dDesc.ArraySize = 1;
 | 
			
		||||
	t2dDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
 | 
			
		||||
	t2dDesc.SampleDesc.Count = 1u;
 | 
			
		||||
	t2dDesc.SampleDesc.Quality = 0u;
 | 
			
		||||
	t2dDesc.Usage = D3D11_USAGE_DEFAULT;
 | 
			
		||||
	t2dDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
 | 
			
		||||
	t2dDesc.CPUAccessFlags = NULL;
 | 
			
		||||
	t2dDesc.MiscFlags = NULL;
 | 
			
		||||
	dxc(m_context->get_device()->CreateTexture2D(&t2dDesc, nullptr, &m_color_attachment));
 | 
			
		||||
 | 
			
		||||
	auto srvDesc = D3D11_SHADER_RESOURCE_VIEW_DESC {};
 | 
			
		||||
	srvDesc.Format = t2dDesc.Format;
 | 
			
		||||
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
 | 
			
		||||
	srvDesc.Texture2D.MipLevels = 1;
 | 
			
		||||
	srvDesc.Texture2D.MostDetailedMip = 0;
 | 
			
		||||
	dxc(m_context->get_device()->CreateShaderResourceView(
 | 
			
		||||
	    m_color_attachment.Get(),
 | 
			
		||||
	    &srvDesc,
 | 
			
		||||
	    &m_shader_resource_view
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	auto rtvDesc = D3D11_RENDER_TARGET_VIEW_DESC {};
 | 
			
		||||
	rtvDesc.Format = t2dDesc.Format;
 | 
			
		||||
	rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
 | 
			
		||||
	rtvDesc.Texture2D.MipSlice = 0u;
 | 
			
		||||
	dxc(m_context->get_device()
 | 
			
		||||
	        ->CreateRenderTargetView(m_color_attachment.Get(), &rtvDesc, &m_render_target_view));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxFramebuffer::bind_as_target(const glm::vec4 &clearColor)
 | 
			
		||||
{
 | 
			
		||||
	FLOAT color[] = {
 | 
			
		||||
		clearColor.r,
 | 
			
		||||
		clearColor.g,
 | 
			
		||||
		clearColor.b,
 | 
			
		||||
		clearColor.a,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	m_context->get_device_context()
 | 
			
		||||
	    ->OMSetRenderTargets(1u, m_render_target_view.GetAddressOf(), nullptr);
 | 
			
		||||
	m_context->get_device_context()->ClearRenderTargetView(m_render_target_view.Get(), color);
 | 
			
		||||
 | 
			
		||||
	auto viewport = D3D11_VIEWPORT {};
 | 
			
		||||
 | 
			
		||||
	viewport.TopLeftX = 0;
 | 
			
		||||
	viewport.TopLeftY = 0;
 | 
			
		||||
 | 
			
		||||
	viewport.Width = m_specification.width;
 | 
			
		||||
	viewport.Height = m_specification.height;
 | 
			
		||||
 | 
			
		||||
	viewport.MinDepth = 0.0f;
 | 
			
		||||
	viewport.MaxDepth = 1.0f;
 | 
			
		||||
 | 
			
		||||
	// set viewport
 | 
			
		||||
	m_context->get_device_context()->RSSetViewports(1u, &viewport);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxFramebuffer::bind_as_resource()
 | 
			
		||||
{
 | 
			
		||||
	log_err("NO_IMPLEMENT");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxFramebuffer::resize(const glm::uvec2 &size)
 | 
			
		||||
{
 | 
			
		||||
	m_specification.width = std::clamp(size.x, 1u, 16384u);
 | 
			
		||||
	m_specification.height = std::clamp(size.y, 1u, 16384u);
 | 
			
		||||
 | 
			
		||||
	auto textureDesc = D3D11_TEXTURE2D_DESC {};
 | 
			
		||||
	auto rtvDesc = D3D11_RENDER_TARGET_VIEW_DESC {};
 | 
			
		||||
	auto srvDesc = D3D11_SHADER_RESOURCE_VIEW_DESC {};
 | 
			
		||||
 | 
			
		||||
	m_color_attachment->GetDesc(&textureDesc);
 | 
			
		||||
	m_render_target_view->GetDesc(&rtvDesc);
 | 
			
		||||
	m_shader_resource_view->GetDesc(&srvDesc);
 | 
			
		||||
 | 
			
		||||
	textureDesc.Width = m_specification.width;
 | 
			
		||||
	textureDesc.Height = m_specification.height;
 | 
			
		||||
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateTexture2D(&textureDesc, nullptr, &m_color_attachment));
 | 
			
		||||
	dxc(m_context->get_device()
 | 
			
		||||
	        ->CreateRenderTargetView(m_color_attachment.Get(), &rtvDesc, &m_render_target_view));
 | 
			
		||||
	dxc(m_context->get_device()->CreateShaderResourceView(
 | 
			
		||||
	    m_color_attachment.Get(),
 | 
			
		||||
	    &srvDesc,
 | 
			
		||||
	    &m_shader_resource_view
 | 
			
		||||
	));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										46
									
								
								modules/renderer/legacy/private/dx/framebuffers.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								modules/renderer/legacy/private/dx/framebuffers.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <renderer/framebuffer.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class dxSharedContext;
 | 
			
		||||
 | 
			
		||||
class dxFramebuffer: public Framebuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxFramebuffer(
 | 
			
		||||
	    const FramebufferSpecification &specification,
 | 
			
		||||
	    Ref<dxSharedContext> sharedContext
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	auto get_color_attachment() -> void * override
 | 
			
		||||
	{
 | 
			
		||||
		return (void *)m_shader_resource_view.Get();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void bind_as_target(const glm::vec4 &clearColor) override;
 | 
			
		||||
 | 
			
		||||
	void bind_as_resource() override;
 | 
			
		||||
 | 
			
		||||
	void resize(const glm::uvec2 &size) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<dxSharedContext> m_context;
 | 
			
		||||
 | 
			
		||||
	FramebufferSpecification m_specification;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_render_target_view;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11Texture2D> m_color_attachment;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11Texture2D> m_depth_stencil_attachment;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_shader_resource_view;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11DepthStencilView> m_depth_stencil_view;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										159
									
								
								modules/renderer/legacy/private/dx/graphics_context.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								modules/renderer/legacy/private/dx/graphics_context.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,159 @@
 | 
			
		|||
#include <input/events/window.hpp>
 | 
			
		||||
#include <renderer/blender.hpp"            // required for forward declaratio>
 | 
			
		||||
#include <renderer/buffers.hpp"            // required for forward declaratio>
 | 
			
		||||
#include <renderer/dx/graphics_context.hpp>
 | 
			
		||||
#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
#include <renderer/render_command.hpp> // required for forward declaratio>
 | 
			
		||||
#include <renderer/renderer.hpp>       // required for forward declaratio>
 | 
			
		||||
#include <ui/ui.hpp>                   // required for forward declaratio>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
dxGraphicsContext::dxGraphicsContext(): m_window_handle(windowHandle), m_debug_interface(nullptr)
 | 
			
		||||
{
 | 
			
		||||
	// set 'GraphicsAPI';
 | 
			
		||||
	m_graphics_api = GraphicsAPI::DirectX;
 | 
			
		||||
 | 
			
		||||
	m_shared_context = std::make_shared<dxSharedContext>();
 | 
			
		||||
 | 
			
		||||
	// setup stuff
 | 
			
		||||
	setup_device_and_swap_chain(windowHandle);
 | 
			
		||||
	setup_render_targets();
 | 
			
		||||
	setup_debug_interface();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxGraphicsContext::setup_device_and_swap_chain()
 | 
			
		||||
{
 | 
			
		||||
	auto context = std::static_pointer_cast<dxSharedContext>(m_shared_context);
 | 
			
		||||
 | 
			
		||||
	// swap chain desc
 | 
			
		||||
	auto sd = DXGI_SWAP_CHAIN_DESC { 0 };
 | 
			
		||||
 | 
			
		||||
	// buffer desc
 | 
			
		||||
	sd.BufferDesc.Width = 1u;
 | 
			
		||||
	sd.BufferDesc.Height = 1u;
 | 
			
		||||
	sd.BufferDesc.RefreshRate.Numerator = NULL;   // :#todo
 | 
			
		||||
	sd.BufferDesc.RefreshRate.Denominator = NULL; // :#todo
 | 
			
		||||
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
 | 
			
		||||
	sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
 | 
			
		||||
	sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
 | 
			
		||||
 | 
			
		||||
	// sample desc (for multi sampling) #todo: implement multi-samplingz
 | 
			
		||||
	sd.SampleDesc.Count = 1u;
 | 
			
		||||
	sd.SampleDesc.Quality = 0u;
 | 
			
		||||
 | 
			
		||||
	// #todo: support swap chains with more than 1 back-buffer
 | 
			
		||||
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 | 
			
		||||
	sd.BufferCount = 1u;
 | 
			
		||||
 | 
			
		||||
	sd.OutputWindow = {}; // ...
 | 
			
		||||
	sd.Windowed = true;
 | 
			
		||||
 | 
			
		||||
	sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
 | 
			
		||||
 | 
			
		||||
	sd.Flags = NULL;
 | 
			
		||||
 | 
			
		||||
	// determine device flags
 | 
			
		||||
	auto flags = UINT { NULL };
 | 
			
		||||
#ifdef LIGHT_DEBUG
 | 
			
		||||
	flags = D3D11_CREATE_DEVICE_DEBUG;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// create device and swap chain
 | 
			
		||||
	dxc(D3D11CreateDeviceAndSwapChain(
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    D3D_DRIVER_TYPE_HARDWARE,
 | 
			
		||||
	    NULL,
 | 
			
		||||
	    flags,
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    NULL,
 | 
			
		||||
	    D3D11_SDK_VERSION,
 | 
			
		||||
	    &sd,
 | 
			
		||||
	    &context->GetSwapChainRef(),
 | 
			
		||||
	    &context->GetDeviceRef(),
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    &context->GetDeviceContextRef()
 | 
			
		||||
	));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxGraphicsContext::setup_render_targets()
 | 
			
		||||
{
 | 
			
		||||
	auto context = std::static_pointer_cast<dxSharedContext>(m_shared_context);
 | 
			
		||||
 | 
			
		||||
	// set primitive topology
 | 
			
		||||
	context->get_device_context()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
 | 
			
		||||
 | 
			
		||||
	// create render target view
 | 
			
		||||
	auto backBuffer = Microsoft::WRL::ComPtr<ID3D11Resource> {};
 | 
			
		||||
 | 
			
		||||
	dxc(context->get_swap_chain()->GetBuffer(0u, __uuidof(ID3D11Resource), &backBuffer));
 | 
			
		||||
	dxc(context->get_device()->CreateRenderTargetView(
 | 
			
		||||
	    backBuffer.Get(),
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    &context->GetRenderTargetViewRef()
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	// set render target view
 | 
			
		||||
	context->get_device_context()
 | 
			
		||||
	    ->OMSetRenderTargets(1u, context->get_render_target_view().GetAddressOf(), nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxGraphicsContext::setup_debug_interface()
 | 
			
		||||
{
 | 
			
		||||
#ifdef LIGHT_DEBUG
 | 
			
		||||
	Ref<dxSharedContext> context = std::static_pointer_cast<dxSharedContext>(m_shared_context);
 | 
			
		||||
 | 
			
		||||
	HRESULT hr;
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11Debug> debugInterface = nullptr;
 | 
			
		||||
	dxc(context->get_device()->QueryInterface(__uuidof(ID3D11Debug), &debugInterface));
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11InfoQueue> infoQueue = nullptr;
 | 
			
		||||
	dxc(debugInterface->QueryInterface(__uuidof(ID3D11InfoQueue), &infoQueue));
 | 
			
		||||
 | 
			
		||||
	infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
 | 
			
		||||
	infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
 | 
			
		||||
 | 
			
		||||
	D3D11_MESSAGE_ID hide[] = {
 | 
			
		||||
		D3D11_MESSAGE_ID_UNKNOWN,
 | 
			
		||||
		// #todo: add more messages here as needed
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	D3D11_INFO_QUEUE_FILTER filter = {};
 | 
			
		||||
	filter.DenyList.NumIDs = _countof(hide);
 | 
			
		||||
	filter.DenyList.pIDList = hide;
 | 
			
		||||
	infoQueue->AddStorageFilterEntries(&filter);
 | 
			
		||||
	infoQueue->release();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxGraphicsContext::log_debug_data()
 | 
			
		||||
{
 | 
			
		||||
	auto context = std::static_pointer_cast<dxSharedContext>(m_shared_context);
 | 
			
		||||
 | 
			
		||||
	// locals
 | 
			
		||||
	auto *DXGIDevice = (IDXGIDevice *) {};
 | 
			
		||||
	auto *DXGIAdapter = (IDXGIAdapter *) {};
 | 
			
		||||
	auto *DXGIAdapterDesc = (DXGI_ADAPTER_DESC *) {};
 | 
			
		||||
 | 
			
		||||
	context->get_device()->QueryInterface(__uuidof(IDXGIDevice), (void **)&DXGIDevice);
 | 
			
		||||
	DXGIDevice->GetAdapter(&DXGIAdapter);
 | 
			
		||||
	DXGIAdapter->GetDesc(&DXGIAdapterDesc);
 | 
			
		||||
 | 
			
		||||
	// get the adapter's description
 | 
			
		||||
	auto DefChar = ' ';
 | 
			
		||||
	char ch[180];
 | 
			
		||||
	WideCharToMultiByte(CP_ACP, 0, DXGIAdapterDesc.Description, -1, ch, 180, &DefChar, NULL);
 | 
			
		||||
	auto adapterDesc = std::string { ch };
 | 
			
		||||
 | 
			
		||||
	// release memory
 | 
			
		||||
	DXGIDevice->release();
 | 
			
		||||
	DXGIAdapter->release();
 | 
			
		||||
 | 
			
		||||
	// #todo: log more information
 | 
			
		||||
	log_inf("________________________________________");
 | 
			
		||||
	log_inf("dxGraphicsContext:");
 | 
			
		||||
	log_inf("        renderer: {}", adapterDesc);
 | 
			
		||||
	log_inf("________________________________________");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										26
									
								
								modules/renderer/legacy/private/dx/graphics_context.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								modules/renderer/legacy/private/dx/graphics_context.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class dxGraphicsContext: public GraphicsContext
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxGraphicsContext();
 | 
			
		||||
 | 
			
		||||
	virtual void log_debug_data() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11Debug> m_debug_interface;
 | 
			
		||||
 | 
			
		||||
	void setup_device_and_swap_chain();
 | 
			
		||||
 | 
			
		||||
	void setup_render_targets();
 | 
			
		||||
 | 
			
		||||
	void setup_debug_interface();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										105
									
								
								modules/renderer/legacy/private/dx/render_command.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								modules/renderer/legacy/private/dx/render_command.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,105 @@
 | 
			
		|||
#include <renderer/dx/render_command.hpp>
 | 
			
		||||
#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
dxRenderCommand::dxRenderCommand(Ref<dxSharedContext> sharedContext): m_context(sharedContext)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxRenderCommand::swap_buffers()
 | 
			
		||||
{
 | 
			
		||||
#ifdef LIGHT_DEBUG
 | 
			
		||||
	HRESULT hr;
 | 
			
		||||
	if (FAILED(hr = m_context->get_swap_chain()->Present(1u, 0u)))
 | 
			
		||||
	{
 | 
			
		||||
		if (hr == DXGI_ERROR_DEVICE_REMOVED)
 | 
			
		||||
		{
 | 
			
		||||
			log_crt("dxRenderCommand::swap_buffers: DeviceRemoved:");
 | 
			
		||||
			log_crt("        {}", m_context->get_device()->GetDeviceRemovedReason());
 | 
			
		||||
			throw dxException(hr, __FILE__, __LINE__);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
	m_context->get_swap_chain()->Present(0u, 0u);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxRenderCommand::clear_back_buffer(const glm::vec4 &clearColor)
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->ClearRenderTargetView(
 | 
			
		||||
	    m_context->get_render_target_view().Get(),
 | 
			
		||||
	    &clearColor[0]
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxRenderCommand::draw(unsigned int count)
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->draw(count, 0u);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxRenderCommand::draw_indexed(unsigned int count)
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->draw_indexed(count, 0u, 0u);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxRenderCommand::default_target_framebuffer()
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()
 | 
			
		||||
	    ->OMSetRenderTargets(1, m_context->get_render_target_view().GetAddressOf(), nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxRenderCommand::set_viewport(
 | 
			
		||||
    unsigned int x,
 | 
			
		||||
    unsigned int y,
 | 
			
		||||
    unsigned int width,
 | 
			
		||||
    unsigned int height
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
	// #todo: maybe call this somewhere else??
 | 
			
		||||
	set_resolution(width, height);
 | 
			
		||||
 | 
			
		||||
	// create viewport
 | 
			
		||||
	auto viewport = D3D11_VIEWPORT {};
 | 
			
		||||
 | 
			
		||||
	viewport.TopLeftX = x;
 | 
			
		||||
	viewport.TopLeftY = y;
 | 
			
		||||
 | 
			
		||||
	viewport.Width = width;
 | 
			
		||||
	viewport.Height = height;
 | 
			
		||||
 | 
			
		||||
	viewport.MinDepth = 0.0f;
 | 
			
		||||
	viewport.MaxDepth = 1.0f;
 | 
			
		||||
 | 
			
		||||
	// set viewport
 | 
			
		||||
	m_context->get_device_context()->RSSetViewports(1u, &viewport);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxRenderCommand::set_resolution(unsigned int width, unsigned int height)
 | 
			
		||||
{
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
 | 
			
		||||
	// remove render target
 | 
			
		||||
	auto *nullViews[] = (ID3D11RenderTargetView *) { nullptr };
 | 
			
		||||
	m_context->get_device_context()->OMSetRenderTargets(1u, nullViews, nullptr);
 | 
			
		||||
	m_context->GetRenderTargetViewRef().reset();
 | 
			
		||||
 | 
			
		||||
	// resize buffer
 | 
			
		||||
	dxc(m_context->get_swap_chain()
 | 
			
		||||
	        ->ResizeBuffers(0u, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, NULL));
 | 
			
		||||
 | 
			
		||||
	// create render target
 | 
			
		||||
	auto backBuffer = Microsoft::WRL::ComPtr<ID3D11Resource> { nullptr };
 | 
			
		||||
	dxc(m_context->get_swap_chain()->GetBuffer(0u, __uuidof(ID3D11Resource), &backBuffer));
 | 
			
		||||
	dxc(m_context->get_device()->CreateRenderTargetView(
 | 
			
		||||
	    backBuffer.Get(),
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    &m_context->GetRenderTargetViewRef()
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	// set render target
 | 
			
		||||
	m_context->get_device_context()
 | 
			
		||||
	    ->OMSetRenderTargets(1u, m_context->get_render_target_view().GetAddressOf(), nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										39
									
								
								modules/renderer/legacy/private/dx/render_command.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								modules/renderer/legacy/private/dx/render_command.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <renderer/render_command.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class dxSharedContext;
 | 
			
		||||
 | 
			
		||||
class dxRenderCommand: public RenderCommand
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxRenderCommand(Ref<dxSharedContext> sharedContext);
 | 
			
		||||
 | 
			
		||||
	virtual void swap_buffers() override;
 | 
			
		||||
 | 
			
		||||
	virtual void clear_back_buffer(const glm::vec4 &clearColor) override;
 | 
			
		||||
 | 
			
		||||
	virtual void draw(unsigned int count) override;
 | 
			
		||||
 | 
			
		||||
	virtual void draw_indexed(unsigned int count) override;
 | 
			
		||||
 | 
			
		||||
	virtual void default_target_framebuffer() override;
 | 
			
		||||
 | 
			
		||||
	virtual void set_viewport(
 | 
			
		||||
	    unsigned int x,
 | 
			
		||||
	    unsigned int y,
 | 
			
		||||
	    unsigned int width,
 | 
			
		||||
	    unsigned int height
 | 
			
		||||
	) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<dxSharedContext> m_context;
 | 
			
		||||
 | 
			
		||||
	void set_resolution(unsigned int width, unsigned int height);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										85
									
								
								modules/renderer/legacy/private/dx/shader.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								modules/renderer/legacy/private/dx/shader.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
#include <d3dcompiler.h>
 | 
			
		||||
#include <renderer/dx/shader.hpp>
 | 
			
		||||
#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
dxShader::dxShader(
 | 
			
		||||
    BasicFileHandle vertexFile,
 | 
			
		||||
    BasicFileHandle pixelFile,
 | 
			
		||||
    Ref<dxSharedContext> sharedContext
 | 
			
		||||
)
 | 
			
		||||
    : m_context(sharedContext)
 | 
			
		||||
    , m_vertex_shader(nullptr)
 | 
			
		||||
    , m_pixel_shader(nullptr)
 | 
			
		||||
    , m_vertex_blob(nullptr)
 | 
			
		||||
{
 | 
			
		||||
	auto ps = Microsoft::WRL::ComPtr<ID3DBlob> { nullptr };
 | 
			
		||||
	auto vsErr = Microsoft::WRL::ComPtr<ID3DBlob> { nullptr };
 | 
			
		||||
	auto psErr = Microsoft::WRL::ComPtr<ID3DBlob> { nullptr };
 | 
			
		||||
 | 
			
		||||
	// compile shaders (we don't use dxc here because if d3_d_compile fails it throws a dxException
 | 
			
		||||
	// without logging the vsErr/psErr
 | 
			
		||||
	d3_d_compile(
 | 
			
		||||
	    vertexFile.GetData(),
 | 
			
		||||
	    vertexFile.get_size(),
 | 
			
		||||
	    NULL,
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    "main",
 | 
			
		||||
	    "vs_4_0",
 | 
			
		||||
	    NULL,
 | 
			
		||||
	    NULL,
 | 
			
		||||
	    &m_vertex_blob,
 | 
			
		||||
	    &vsErr
 | 
			
		||||
	);
 | 
			
		||||
	d3_d_compile(
 | 
			
		||||
	    pixelFile.GetData(),
 | 
			
		||||
	    pixelFile.get_size(),
 | 
			
		||||
	    NULL,
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    "main",
 | 
			
		||||
	    "ps_4_0",
 | 
			
		||||
	    NULL,
 | 
			
		||||
	    NULL,
 | 
			
		||||
	    &ps,
 | 
			
		||||
	    &psErr
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	// check
 | 
			
		||||
	ensure(!vsErr.Get(), "Vertex shader compile error: {}", (char *)vsErr->GetBufferPointer());
 | 
			
		||||
	ensure(!psErr.Get(), "Pixels shader compile error: {}", (char *)psErr->GetBufferPointer());
 | 
			
		||||
 | 
			
		||||
	// create shaders
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateVertexShader(
 | 
			
		||||
	    m_vertex_blob->GetBufferPointer(),
 | 
			
		||||
	    m_vertex_blob->GetBufferSize(),
 | 
			
		||||
	    NULL,
 | 
			
		||||
	    &m_vertex_shader
 | 
			
		||||
	));
 | 
			
		||||
	dxc(
 | 
			
		||||
	    m_context->get_device()
 | 
			
		||||
	        ->CreatePixelShader(ps->GetBufferPointer(), ps->GetBufferSize(), NULL, &m_pixel_shader)
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dxShader::~dxShader()
 | 
			
		||||
{
 | 
			
		||||
	un_bind();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxShader::bind()
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->VSSetShader(m_vertex_shader.Get(), nullptr, 0u);
 | 
			
		||||
	m_context->get_device_context()->PSSetShader(m_pixel_shader.Get(), nullptr, 0u);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxShader::un_bind()
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->VSSetShader(nullptr, nullptr, 0u);
 | 
			
		||||
	m_context->get_device_context()->PSSetShader(nullptr, nullptr, 0u);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										41
									
								
								modules/renderer/legacy/private/dx/shader.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								modules/renderer/legacy/private/dx/shader.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <renderer/shader.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class dxSharedContext;
 | 
			
		||||
 | 
			
		||||
class dxShader: public Shader
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxShader(
 | 
			
		||||
	    BasicFileHandle vertexFile,
 | 
			
		||||
	    BasicFileHandle pixelFile,
 | 
			
		||||
	    Ref<dxSharedContext> sharedContext
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	~dxShader();
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	void un_bind() override;
 | 
			
		||||
 | 
			
		||||
	auto get_vertex_blob() -> Microsoft::WRL::ComPtr<ID3DBlob>
 | 
			
		||||
	{
 | 
			
		||||
		return m_vertex_blob;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<dxSharedContext> m_context;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11VertexShader> m_vertex_shader;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11PixelShader> m_pixel_shader;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3DBlob> m_vertex_blob;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										62
									
								
								modules/renderer/legacy/private/dx/shared_context.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								modules/renderer/legacy/private/dx/shared_context.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <renderer/shared_context.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class dxSharedContext: public SharedContext
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	[[nodiscard]] auto get_device() -> Microsoft::WRL::ComPtr<ID3D11Device>
 | 
			
		||||
	{
 | 
			
		||||
		return m_device;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto get_device_context() -> Microsoft::WRL::ComPtr<ID3D11DeviceContext>
 | 
			
		||||
	{
 | 
			
		||||
		return m_deviceContext;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto get_swap_chain() -> Microsoft::WRL::ComPtr<IDXGISwapChain>
 | 
			
		||||
	{
 | 
			
		||||
		return m_swap_chain;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto get_render_target_view() -> Microsoft::WRL::ComPtr<ID3D11RenderTargetView>
 | 
			
		||||
	{
 | 
			
		||||
		return m_render_target_view;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto GetDeviceRef() -> Microsoft::WRL::ComPtr<ID3D11Device>
 | 
			
		||||
	{
 | 
			
		||||
		return m_device;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto GetDeviceContextRef() -> Microsoft::WRL::ComPtr<ID3D11DeviceContext>
 | 
			
		||||
	{
 | 
			
		||||
		return m_deviceContext;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto GetSwapChainRef() -> Microsoft::WRL::ComPtr<IDXGISwapChain>
 | 
			
		||||
	{
 | 
			
		||||
		return m_swap_chain;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto GetRenderTargetViewRef() -> Microsoft::WRL::ComPtr<ID3D11RenderTargetView>
 | 
			
		||||
	{
 | 
			
		||||
		return m_render_target_view;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11Device> m_device = nullptr;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11DeviceContext> m_deviceContext = nullptr;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<IDXGISwapChain> m_swap_chain = nullptr;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_render_target_view = nullptr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										80
									
								
								modules/renderer/legacy/private/dx/texture.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								modules/renderer/legacy/private/dx/texture.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
#include <renderer/dx/texture.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
dxTexture::dxTexture(
 | 
			
		||||
    unsigned int width,
 | 
			
		||||
    unsigned int height,
 | 
			
		||||
    unsigned int components,
 | 
			
		||||
    unsigned char *pixels,
 | 
			
		||||
    Ref<dxSharedContext> sharedContext,
 | 
			
		||||
    const std::string &filePath
 | 
			
		||||
)
 | 
			
		||||
    : Texture(filePath)
 | 
			
		||||
    , m_context(sharedContext)
 | 
			
		||||
    , m_texture_2d(nullptr)
 | 
			
		||||
    , m_shader_resource_view(nullptr)
 | 
			
		||||
    , m_sampler_state(nullptr)
 | 
			
		||||
{
 | 
			
		||||
	// texture2d desc
 | 
			
		||||
	auto t2dDesc = D3D11_TEXTURE2D_DESC {};
 | 
			
		||||
	t2dDesc.Width = width;
 | 
			
		||||
	t2dDesc.Height = height;
 | 
			
		||||
	t2dDesc.MipLevels = 0u;
 | 
			
		||||
	t2dDesc.ArraySize = 1u;
 | 
			
		||||
	t2dDesc.Format = components == 4u ? DXGI_FORMAT_R8G8B8A8_UNORM :
 | 
			
		||||
	                 components == 3u ? DXGI_FORMAT_R8G8B8A8_UNORM :
 | 
			
		||||
	                                    // #todo: figure out what to do with this bitch ._.
 | 
			
		||||
	                     components == 2u ? DXGI_FORMAT_R8G8_UNORM :
 | 
			
		||||
	                 components == 1u     ? DXGI_FORMAT_R8_UNORM :
 | 
			
		||||
	                                        DXGI_FORMAT_UNKNOWN;
 | 
			
		||||
	t2dDesc.SampleDesc.Count = 1u;
 | 
			
		||||
	t2dDesc.SampleDesc.Quality = 0u;
 | 
			
		||||
	t2dDesc.Usage = D3D11_USAGE_DEFAULT;
 | 
			
		||||
	t2dDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
 | 
			
		||||
	t2dDesc.CPUAccessFlags = NULL;
 | 
			
		||||
	t2dDesc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;
 | 
			
		||||
 | 
			
		||||
	// create texture
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateTexture2D(&t2dDesc, nullptr, &m_texture_2d));
 | 
			
		||||
	m_context->get_device_context()
 | 
			
		||||
	    ->UpdateSubresource(m_texture_2d.Get(), 0u, nullptr, pixels, width * 4u, 0u);
 | 
			
		||||
 | 
			
		||||
	m_texture_2d->GetDesc(&t2dDesc);
 | 
			
		||||
 | 
			
		||||
	// shader resource view desc
 | 
			
		||||
	auto srvDesc = D3D11_SHADER_RESOURCE_VIEW_DESC {};
 | 
			
		||||
	srvDesc.Format = t2dDesc.Format;
 | 
			
		||||
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
 | 
			
		||||
	srvDesc.Texture2D.MostDetailedMip = 0u;
 | 
			
		||||
	srvDesc.Texture2D.MipLevels = -1;
 | 
			
		||||
 | 
			
		||||
	// create shader resource view
 | 
			
		||||
	m_context->get_device()
 | 
			
		||||
	    ->CreateShaderResourceView(m_texture_2d.Get(), &srvDesc, &m_shader_resource_view);
 | 
			
		||||
	m_context->get_device_context()->GenerateMips(m_shader_resource_view.Get());
 | 
			
		||||
 | 
			
		||||
	// sampler desc
 | 
			
		||||
	auto sDesc = D3D11_SAMPLER_DESC {};
 | 
			
		||||
	sDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
 | 
			
		||||
	sDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
 | 
			
		||||
	sDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
 | 
			
		||||
	sDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
 | 
			
		||||
	sDesc.MinLOD = 0.0f;
 | 
			
		||||
	sDesc.MipLODBias = 0.0f;
 | 
			
		||||
	sDesc.MaxLOD = D3D11_FLOAT32_MAX;
 | 
			
		||||
 | 
			
		||||
	// create sampler
 | 
			
		||||
	m_context->get_device()->CreateSamplerState(&sDesc, &m_sampler_state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxTexture::bind(unsigned int slot /* = 0u */)
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->PSSetSamplers(slot, 1u, m_sampler_state.GetAddressOf());
 | 
			
		||||
	m_context->get_device_context()
 | 
			
		||||
	    ->PSSetShaderResources(slot, 1u, m_shader_resource_view.GetAddressOf());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										35
									
								
								modules/renderer/legacy/private/dx/texture.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								modules/renderer/legacy/private/dx/texture.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <renderer/texture.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class dxSharedContext;
 | 
			
		||||
 | 
			
		||||
class dxTexture: public Texture
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxTexture(
 | 
			
		||||
	    unsigned int width,
 | 
			
		||||
	    unsigned int height,
 | 
			
		||||
	    unsigned int components,
 | 
			
		||||
	    unsigned char *pixels,
 | 
			
		||||
	    Ref<dxSharedContext> sharedContext,
 | 
			
		||||
	    const std::string &filePath
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	void bind(unsigned int slot = 0u) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<dxSharedContext> m_context;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11Texture2D> m_texture_2d;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_shader_resource_view;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11SamplerState> m_sampler_state;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										27
									
								
								modules/renderer/legacy/private/dx/user_interface.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								modules/renderer/legacy/private/dx/user_interface.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <ui/ui.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class dxSharedContext;
 | 
			
		||||
 | 
			
		||||
class dxUserInterface: public UserInterface
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxUserInterface() = default;
 | 
			
		||||
 | 
			
		||||
	~dxUserInterface();
 | 
			
		||||
 | 
			
		||||
	void platform_implementation(Ref<SharedContext> sharedContext) override;
 | 
			
		||||
 | 
			
		||||
	void begin() override;
 | 
			
		||||
 | 
			
		||||
	void end() override;
 | 
			
		||||
 | 
			
		||||
	void log_debug_data() override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										97
									
								
								modules/renderer/legacy/private/dx/vertex_layout.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								modules/renderer/legacy/private/dx/vertex_layout.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,97 @@
 | 
			
		|||
#include <renderer/dx/shader.hpp>
 | 
			
		||||
#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
#include <renderer/dx/vertex_layout.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
dxVertexLayout::dxVertexLayout(
 | 
			
		||||
    Ref<Shader> shader,
 | 
			
		||||
    const std::vector<std::pair<std::string, VertexElementType>> &elements,
 | 
			
		||||
    Ref<dxSharedContext> sharedContext
 | 
			
		||||
)
 | 
			
		||||
    : m_context(sharedContext)
 | 
			
		||||
    , m_input_layout(nullptr)
 | 
			
		||||
{
 | 
			
		||||
	auto inputElementsDesc = std::vector<D3D11_INPUT_ELEMENT_DESC> {};
 | 
			
		||||
	inputElementsDesc.reserve(elements.size());
 | 
			
		||||
 | 
			
		||||
	// extract elements desc
 | 
			
		||||
	for (const auto &element : elements)
 | 
			
		||||
	{
 | 
			
		||||
		inputElementsDesc.emplace_back(
 | 
			
		||||
		    D3D11_INPUT_ELEMENT_DESC { element.first.c_str(),
 | 
			
		||||
		                               NULL,
 | 
			
		||||
		                               get_dxgi_format(element.second),
 | 
			
		||||
		                               0u,
 | 
			
		||||
		                               D3D11_APPEND_ALIGNED_ELEMENT,
 | 
			
		||||
		                               D3D11_INPUT_PER_VERTEX_DATA,
 | 
			
		||||
		                               0u }
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto dxpShader = std::dynamic_pointer_cast<dxShader>(shader);
 | 
			
		||||
	ensure(dxpShader, "Failed to cast 'Shader' to 'dxShader'");
 | 
			
		||||
 | 
			
		||||
	// create input layout (vertex layout)
 | 
			
		||||
	auto hr = HRESULT {};
 | 
			
		||||
	dxc(m_context->get_device()->CreateInputLayout(
 | 
			
		||||
	    &inputElementsDesc[0],
 | 
			
		||||
	    inputElementsDesc.size(),
 | 
			
		||||
	    dxpShader->get_vertex_blob().Get()->GetBufferPointer(),
 | 
			
		||||
	    dxpShader->get_vertex_blob().Get()->GetBufferSize(),
 | 
			
		||||
	    &m_input_layout
 | 
			
		||||
	));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dxVertexLayout::~dxVertexLayout()
 | 
			
		||||
{
 | 
			
		||||
	un_bind();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxVertexLayout::bind()
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->IASetInputLayout(m_input_layout.Get());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dxVertexLayout::un_bind()
 | 
			
		||||
{
 | 
			
		||||
	m_context->get_device_context()->IASetInputLayout(nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto dxVertexLayout::get_dxgi_format(VertexElementType type) -> DXGI_FORMAT
 | 
			
		||||
{
 | 
			
		||||
	switch (type)
 | 
			
		||||
	{
 | 
			
		||||
	/* byte */
 | 
			
		||||
	case lt::VertexElementType::Byte1: return DXGI_FORMAT_R8_SINT;
 | 
			
		||||
	case lt::VertexElementType::Byte2: return DXGI_FORMAT_R8G8_SINT;
 | 
			
		||||
	case lt::VertexElementType::Byte4: return DXGI_FORMAT_R8_SINT;
 | 
			
		||||
 | 
			
		||||
	/* ubyte */
 | 
			
		||||
	case lt::VertexElementType::UByte1: return DXGI_FORMAT_R8_UINT;
 | 
			
		||||
	case lt::VertexElementType::UByte2: return DXGI_FORMAT_R8G8_UINT;
 | 
			
		||||
	case lt::VertexElementType::UByte4: return DXGI_FORMAT_R8G8B8A8_UINT;
 | 
			
		||||
 | 
			
		||||
	/* int */
 | 
			
		||||
	case lt::VertexElementType::Int1: return DXGI_FORMAT_R32_SINT;
 | 
			
		||||
	case lt::VertexElementType::Int2: return DXGI_FORMAT_R32G32_SINT;
 | 
			
		||||
	case lt::VertexElementType::Int3: return DXGI_FORMAT_R32G32B32_SINT;
 | 
			
		||||
	case lt::VertexElementType::Int4: return DXGI_FORMAT_R32G32B32A32_SINT;
 | 
			
		||||
 | 
			
		||||
	/* uint */
 | 
			
		||||
	case lt::VertexElementType::UInt1: return DXGI_FORMAT_R32_UINT;
 | 
			
		||||
	case lt::VertexElementType::UInt2: return DXGI_FORMAT_R32G32_UINT;
 | 
			
		||||
	case lt::VertexElementType::UInt3: return DXGI_FORMAT_R32G32B32_UINT;
 | 
			
		||||
	case lt::VertexElementType::UInt4: return DXGI_FORMAT_R32G32B32A32_UINT;
 | 
			
		||||
 | 
			
		||||
	/* float */
 | 
			
		||||
	case lt::VertexElementType::Float1: return DXGI_FORMAT_R32_FLOAT;
 | 
			
		||||
	case lt::VertexElementType::Float2: return DXGI_FORMAT_R32G32_FLOAT;
 | 
			
		||||
	case lt::VertexElementType::Float3: return DXGI_FORMAT_R32G32B32_FLOAT;
 | 
			
		||||
	case lt::VertexElementType::Float4: return DXGI_FORMAT_R32G32B32A32_FLOAT;
 | 
			
		||||
 | 
			
		||||
	default: ensure(false, "Invalid 'VertexElementType'"); return DXGI_FORMAT_UNKNOWN;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										35
									
								
								modules/renderer/legacy/private/dx/vertex_layout.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								modules/renderer/legacy/private/dx/vertex_layout.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <renderer/vertex_layout.hpp>
 | 
			
		||||
#include <wrl.h>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class Shader;
 | 
			
		||||
class dxSharedContext;
 | 
			
		||||
 | 
			
		||||
class dxVertexLayout: public VertexLayout
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dxVertexLayout(
 | 
			
		||||
	    Ref<Shader> shader,
 | 
			
		||||
	    const std::vector<std::pair<std::string, VertexElementType>> &elements,
 | 
			
		||||
	    Ref<dxSharedContext> sharedContext
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	~dxVertexLayout();
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	void un_bind() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	DXGI_FORMAT get_dxgi_format(VertexElementType type);
 | 
			
		||||
 | 
			
		||||
	Ref<dxSharedContext> m_context;
 | 
			
		||||
 | 
			
		||||
	Microsoft::WRL::ComPtr<ID3D11InputLayout> m_input_layout;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										41
									
								
								modules/renderer/legacy/private/framebuffer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								modules/renderer/legacy/private/framebuffer.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
#include <renderer/framebuffer.hpp>
 | 
			
		||||
#include <renderer/gl/framebuffers.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef LIGHT_PLATFORM_WINDOWS
 | 
			
		||||
	#include <renderer/dx/framebuffer.hpp>
 | 
			
		||||
	#include <renderer/dx/sharedcontext.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
auto Framebuffer::create(
 | 
			
		||||
    const FramebufferSpecification &specification,
 | 
			
		||||
    const Ref<SharedContext> & /*sharedContext*/
 | 
			
		||||
) -> Ref<Framebuffer>
 | 
			
		||||
{
 | 
			
		||||
	switch (GraphicsContext::get_graphics_api())
 | 
			
		||||
	{
 | 
			
		||||
	case GraphicsAPI::OpenGL: return create_ref<glFramebuffer>(specification);
 | 
			
		||||
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    return create_ref<dxFramebuffer>(
 | 
			
		||||
		               specification,
 | 
			
		||||
		               std::static_pointer_cast<dxSharedContext>(sharedContext)
 | 
			
		||||
		    );
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		ensure(
 | 
			
		||||
		    false,
 | 
			
		||||
		    "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		    static_cast<uint32_t>(GraphicsContext::get_graphics_api())
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										45
									
								
								modules/renderer/legacy/private/gl/blender.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								modules/renderer/legacy/private/gl/blender.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
#include <renderer/gl/blender.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
glBlender::glBlender()
 | 
			
		||||
    : m_factor_map { // constants
 | 
			
		||||
	                 { BlendFactor::ZERO, GL_ZERO },
 | 
			
		||||
	                 { BlendFactor::ONE, GL_ONE },
 | 
			
		||||
 | 
			
		||||
	                 // source						 ,
 | 
			
		||||
	                 { BlendFactor::SRC_COLOR, GL_SRC_COLOR },
 | 
			
		||||
	                 { BlendFactor::INVERSE_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR },
 | 
			
		||||
 | 
			
		||||
	                 { BlendFactor::SRC_ALPHA, GL_SRC_ALPHA },
 | 
			
		||||
	                 { BlendFactor::INVERSE_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
 | 
			
		||||
 | 
			
		||||
	                 // destination					 ,
 | 
			
		||||
	                 { BlendFactor::DST_COLOR, GL_DST_COLOR },
 | 
			
		||||
	                 { BlendFactor::INVERSE_DST_COLOR, GL_ONE_MINUS_DST_COLOR },
 | 
			
		||||
 | 
			
		||||
	                 { BlendFactor::DST_ALPHA, GL_DST_ALPHA },
 | 
			
		||||
	                 { BlendFactor::INVERSE_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA },
 | 
			
		||||
 | 
			
		||||
	                 // source1						 ,
 | 
			
		||||
	                 { BlendFactor::SRC1_COLOR, GL_SRC1_COLOR },
 | 
			
		||||
	                 { BlendFactor::INVERSE_SRC1_COLOR, GL_ONE_MINUS_SRC1_COLOR },
 | 
			
		||||
 | 
			
		||||
	                 { BlendFactor::SRC1_ALPHA, GL_SRC1_ALPHA },
 | 
			
		||||
	                 { BlendFactor::INVERSE_SRC1_ALPHA, GL_ONE_MINUS_SRC_ALPHA }
 | 
			
		||||
    }
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glBlender::enable(BlendFactor srcFactor, BlendFactor dstFactor)
 | 
			
		||||
{
 | 
			
		||||
	glEnable(GL_BLEND);
 | 
			
		||||
	glBlendFunc(m_factor_map.at(srcFactor), m_factor_map.at(dstFactor));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glBlender::disable()
 | 
			
		||||
{
 | 
			
		||||
	glDisable(GL_BLEND);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										22
									
								
								modules/renderer/legacy/private/gl/blender.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								modules/renderer/legacy/private/gl/blender.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <renderer/blender.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class glBlender: public Blender
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	~glBlender() override = default;
 | 
			
		||||
	glBlender();
 | 
			
		||||
 | 
			
		||||
	void enable(BlendFactor srcFactor, BlendFactor dstFactor) override;
 | 
			
		||||
 | 
			
		||||
	void disable() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	std::unordered_map<BlendFactor, unsigned int> m_factor_map;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										138
									
								
								modules/renderer/legacy/private/gl/buffers.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								modules/renderer/legacy/private/gl/buffers.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,138 @@
 | 
			
		|||
#include <cstddef>
 | 
			
		||||
#include <logger/logger.hpp>
 | 
			
		||||
#include <renderer/gl/buffers.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
//==================== CONSTANT_BUFFER ====================//
 | 
			
		||||
glConstantBuffer::glConstantBuffer(ConstantBufferIndex index, unsigned int size)
 | 
			
		||||
    : m_buffer_id()
 | 
			
		||||
    , m_index(static_cast<int>(index))
 | 
			
		||||
{
 | 
			
		||||
	glCreateBuffers(1, &m_buffer_id);
 | 
			
		||||
	glNamedBufferData(m_buffer_id, size, nullptr, GL_DYNAMIC_DRAW);
 | 
			
		||||
 | 
			
		||||
	bind();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glConstantBuffer::~glConstantBuffer()
 | 
			
		||||
{
 | 
			
		||||
	glDeleteBuffers(1, &m_buffer_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glConstantBuffer::bind()
 | 
			
		||||
{
 | 
			
		||||
	glBindBufferBase(GL_UNIFORM_BUFFER, m_index, m_buffer_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto glConstantBuffer::map() -> void *
 | 
			
		||||
{
 | 
			
		||||
	auto *map = glMapNamedBuffer(m_buffer_id, GL_WRITE_ONLY);
 | 
			
		||||
	return map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glConstantBuffer::un_map()
 | 
			
		||||
{
 | 
			
		||||
	glUnmapNamedBuffer(m_buffer_id);
 | 
			
		||||
}
 | 
			
		||||
//==================== CONSTANT_BUFFER ====================//
 | 
			
		||||
 | 
			
		||||
//==================== VERTEX_BUFFER ====================//
 | 
			
		||||
glVertexBuffer::glVertexBuffer(float *vertices, unsigned int stride, unsigned int count)
 | 
			
		||||
    : m_buffer_id()
 | 
			
		||||
{
 | 
			
		||||
	glCreateBuffers(1, &m_buffer_id);
 | 
			
		||||
	glNamedBufferData(
 | 
			
		||||
	    m_buffer_id,
 | 
			
		||||
	    static_cast<GLsizeiptr>(stride * count),
 | 
			
		||||
	    vertices,
 | 
			
		||||
	    GL_DYNAMIC_DRAW
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glVertexBuffer::~glVertexBuffer()
 | 
			
		||||
{
 | 
			
		||||
	glDeleteBuffers(1, &m_buffer_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glVertexBuffer::bind()
 | 
			
		||||
{
 | 
			
		||||
	glBindBuffer(GL_ARRAY_BUFFER, m_buffer_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glVertexBuffer::un_bind()
 | 
			
		||||
{
 | 
			
		||||
	glBindBuffer(GL_ARRAY_BUFFER, {});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto glVertexBuffer::map() -> void *
 | 
			
		||||
{
 | 
			
		||||
	return glMapNamedBuffer(m_buffer_id, GL_WRITE_ONLY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glVertexBuffer::un_map()
 | 
			
		||||
{
 | 
			
		||||
	glUnmapNamedBuffer(m_buffer_id);
 | 
			
		||||
}
 | 
			
		||||
//==================== VERTEX_BUFFER ====================//
 | 
			
		||||
 | 
			
		||||
//==================== INDEX_BUFFER ====================//
 | 
			
		||||
glIndexBuffer::glIndexBuffer(unsigned int *indices, unsigned int count): m_buffer_id()
 | 
			
		||||
{
 | 
			
		||||
	// generate indices if not provided
 | 
			
		||||
	auto hasIndices = !!indices;
 | 
			
		||||
	if (!hasIndices)
 | 
			
		||||
	{
 | 
			
		||||
		// check
 | 
			
		||||
		if (count % 6 != 0)
 | 
			
		||||
		{
 | 
			
		||||
			log_wrn("'indices' can only be null if count is multiple of 6");
 | 
			
		||||
			log_wrn("Adding {} to 'count' -> {}", (6 - (count % 6)), count + (6 - (count % 6)));
 | 
			
		||||
			count = count + (6 - (count % 6));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// create indices
 | 
			
		||||
		indices = new unsigned int[count];
 | 
			
		||||
		auto offset = 0u;
 | 
			
		||||
		for (auto i = 0u; i < count; i += 6u)
 | 
			
		||||
		{
 | 
			
		||||
			indices[i + 0] = offset + 0u;
 | 
			
		||||
			indices[i + 1] = offset + 1u;
 | 
			
		||||
			indices[i + 2] = offset + 2u;
 | 
			
		||||
 | 
			
		||||
			indices[i + 3] = offset + 2u;
 | 
			
		||||
			indices[i + 4] = offset + 3u;
 | 
			
		||||
			indices[i + 5] = offset + 0u;
 | 
			
		||||
 | 
			
		||||
			offset += 4u;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// create buffer
 | 
			
		||||
	glCreateBuffers(1, &m_buffer_id);
 | 
			
		||||
	glNamedBufferData(m_buffer_id, count * sizeof(unsigned int), indices, GL_STATIC_DRAW);
 | 
			
		||||
 | 
			
		||||
	// delete indices
 | 
			
		||||
	if (!hasIndices)
 | 
			
		||||
	{
 | 
			
		||||
		delete[] indices;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glIndexBuffer::~glIndexBuffer()
 | 
			
		||||
{
 | 
			
		||||
	glDeleteBuffers(1, &m_buffer_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glIndexBuffer::bind()
 | 
			
		||||
{
 | 
			
		||||
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glIndexBuffer::un_bind()
 | 
			
		||||
{
 | 
			
		||||
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, {});
 | 
			
		||||
}
 | 
			
		||||
//==================== INDEX_BUFFER ====================//
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										61
									
								
								modules/renderer/legacy/private/gl/buffers.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								modules/renderer/legacy/private/gl/buffers.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <renderer/buffers.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class glConstantBuffer: public ConstantBuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glConstantBuffer(ConstantBufferIndex index, unsigned int size);
 | 
			
		||||
 | 
			
		||||
	~glConstantBuffer() override;
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	auto map() -> void * override;
 | 
			
		||||
 | 
			
		||||
	void un_map() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	unsigned int m_buffer_id;
 | 
			
		||||
 | 
			
		||||
	unsigned int m_index;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class glVertexBuffer: public VertexBuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glVertexBuffer(float *vertices, unsigned int stride, unsigned int count);
 | 
			
		||||
 | 
			
		||||
	~glVertexBuffer() override;
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	void un_bind() override;
 | 
			
		||||
 | 
			
		||||
	auto map() -> void * override;
 | 
			
		||||
 | 
			
		||||
	void un_map() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	unsigned int m_buffer_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class glIndexBuffer: public IndexBuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glIndexBuffer(unsigned int *indices, unsigned int count);
 | 
			
		||||
 | 
			
		||||
	~glIndexBuffer() override;
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	void un_bind() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	unsigned int m_buffer_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										83
									
								
								modules/renderer/legacy/private/gl/framebuffers.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								modules/renderer/legacy/private/gl/framebuffers.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,83 @@
 | 
			
		|||
#include <renderer/gl/framebuffers.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
glFramebuffer::glFramebuffer(const FramebufferSpecification &specification)
 | 
			
		||||
    : m_specification(specification)
 | 
			
		||||
    , m_buffer_id()
 | 
			
		||||
    , m_color_attachment_id()
 | 
			
		||||
    , m_depth_stencil_attachment_id()
 | 
			
		||||
{
 | 
			
		||||
	resize({ specification.width, specification.height });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glFramebuffer::~glFramebuffer()
 | 
			
		||||
{
 | 
			
		||||
	glDeleteFramebuffers(1, &m_buffer_id);
 | 
			
		||||
	glDeleteTextures(1, &m_color_attachment_id);
 | 
			
		||||
	// glDeleteTextures(1, &m_depth_stencil_attachment_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glFramebuffer::bind_as_target(const math::vec4 &clearColor)
 | 
			
		||||
{
 | 
			
		||||
	// #todo: use viewport instead of default x=0, y=0
 | 
			
		||||
	glBindFramebuffer(GL_FRAMEBUFFER, m_buffer_id);
 | 
			
		||||
	glViewport(0, 0, m_specification.width, m_specification.height);
 | 
			
		||||
 | 
			
		||||
	glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
 | 
			
		||||
	glClear(GL_COLOR_BUFFER_BIT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glFramebuffer::bind_as_resource()
 | 
			
		||||
{
 | 
			
		||||
	log_err("NO_IMPLEMENT!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glFramebuffer::resize(const math::uvec2 &size)
 | 
			
		||||
{
 | 
			
		||||
	if (m_buffer_id)
 | 
			
		||||
	{
 | 
			
		||||
		glDeleteFramebuffers(1, &m_buffer_id);
 | 
			
		||||
		glDeleteTextures(1, &m_color_attachment_id);
 | 
			
		||||
		// glDeleteTextures(1, &m_depth_stencil_attachment_id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_specification.width = std::clamp(size.x, 1u, (unsigned int)GL_MAX_TEXTURE_SIZE);
 | 
			
		||||
	m_specification.height = std::clamp(size.y, 1u, (unsigned int)GL_MAX_TEXTURE_SIZE);
 | 
			
		||||
 | 
			
		||||
	glCreateFramebuffers(1, &m_buffer_id);
 | 
			
		||||
	glBindFramebuffer(GL_FRAMEBUFFER, m_buffer_id);
 | 
			
		||||
 | 
			
		||||
	// create color attachment
 | 
			
		||||
	glCreateTextures(GL_TEXTURE_2D, 1, &m_color_attachment_id);
 | 
			
		||||
	glBindTexture(GL_TEXTURE_2D, m_color_attachment_id);
 | 
			
		||||
	glTexImage2D(
 | 
			
		||||
	    GL_TEXTURE_2D,
 | 
			
		||||
	    0,
 | 
			
		||||
	    GL_RGBA8,
 | 
			
		||||
	    m_specification.width,
 | 
			
		||||
	    m_specification.height,
 | 
			
		||||
	    {},
 | 
			
		||||
	    GL_RGBA,
 | 
			
		||||
	    GL_UNSIGNED_BYTE,
 | 
			
		||||
	    nullptr
 | 
			
		||||
	);
 | 
			
		||||
	glTextureParameteri(m_color_attachment_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
			
		||||
	glTextureParameteri(m_color_attachment_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
			
		||||
	glFramebufferTexture2D(
 | 
			
		||||
	    GL_FRAMEBUFFER,
 | 
			
		||||
	    GL_COLOR_ATTACHMENT0,
 | 
			
		||||
	    GL_TEXTURE_2D,
 | 
			
		||||
	    m_color_attachment_id,
 | 
			
		||||
	    0
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	ensure(
 | 
			
		||||
	    (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE),
 | 
			
		||||
	    "Framebuffer is incomplete"
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										37
									
								
								modules/renderer/legacy/private/gl/framebuffers.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								modules/renderer/legacy/private/gl/framebuffers.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <math/vec2.hpp>
 | 
			
		||||
#include <math/vec4.hpp>
 | 
			
		||||
#include <renderer/framebuffer.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class glFramebuffer: public Framebuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glFramebuffer(const FramebufferSpecification &specification);
 | 
			
		||||
 | 
			
		||||
	~glFramebuffer() override;
 | 
			
		||||
 | 
			
		||||
	void bind_as_target(const math::vec4 &clearColor) override;
 | 
			
		||||
 | 
			
		||||
	void bind_as_resource() override;
 | 
			
		||||
 | 
			
		||||
	void resize(const math::uvec2 &size) override;
 | 
			
		||||
 | 
			
		||||
	auto get_color_attachment() -> void * override
 | 
			
		||||
	{
 | 
			
		||||
		return (void *)m_color_attachment_id;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	FramebufferSpecification m_specification;
 | 
			
		||||
 | 
			
		||||
	unsigned int m_buffer_id;
 | 
			
		||||
 | 
			
		||||
	unsigned int m_color_attachment_id;
 | 
			
		||||
 | 
			
		||||
	unsigned int m_depth_stencil_attachment_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										103
									
								
								modules/renderer/legacy/private/gl/graphics_context.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								modules/renderer/legacy/private/gl/graphics_context.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,103 @@
 | 
			
		|||
#include <input/events/window.hpp>
 | 
			
		||||
#include <renderer/blender.hpp> // required for forward declaratio>
 | 
			
		||||
#include <renderer/buffers.hpp> // required for forward declaratio>
 | 
			
		||||
#include <renderer/gl/graphics_context.hpp>
 | 
			
		||||
#include <renderer/render_command.hpp> // required for forward declaratio>
 | 
			
		||||
#include <renderer/renderer.hpp>       // required for forward declaratio>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
glGraphicsContext::glGraphicsContext()
 | 
			
		||||
{
 | 
			
		||||
	m_graphics_api = GraphicsAPI::OpenGL;
 | 
			
		||||
	set_debug_message_callback();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glGraphicsContext::log_debug_data()
 | 
			
		||||
{
 | 
			
		||||
	log_inf("________________________________________");
 | 
			
		||||
	log_inf("GraphicsContext::");
 | 
			
		||||
	log_inf("        API     : OpenGL");
 | 
			
		||||
	log_inf("________________________________________");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glGraphicsContext::set_debug_message_callback()
 | 
			
		||||
{
 | 
			
		||||
	// determine log level
 | 
			
		||||
	// #todo: set filters from config.h
 | 
			
		||||
#if defined(LIGHT_DEBUG)
 | 
			
		||||
	glEnable(GL_DEBUG_OUTPUT);
 | 
			
		||||
	glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
 | 
			
		||||
 | 
			
		||||
	GLuint ids[] = { 131185 };
 | 
			
		||||
	glDebugMessageControl(
 | 
			
		||||
	    GL_DEBUG_SOURCE_API,
 | 
			
		||||
	    GL_DEBUG_TYPE_OTHER,
 | 
			
		||||
	    GL_DONT_CARE,
 | 
			
		||||
	    _countof(ids),
 | 
			
		||||
	    ids,
 | 
			
		||||
	    GL_FALSE
 | 
			
		||||
	);
 | 
			
		||||
#elif defined(LIGHT_RELEASE)
 | 
			
		||||
	glEnable(GL_DEBUG_OUTPUT);
 | 
			
		||||
	glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_FALSE);
 | 
			
		||||
	glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE);
 | 
			
		||||
	glDebugMessageControl(
 | 
			
		||||
	    GL_DONT_CARE,
 | 
			
		||||
	    GL_DONT_CARE,
 | 
			
		||||
	    GL_DEBUG_SEVERITY_MEDIUM,
 | 
			
		||||
	    0,
 | 
			
		||||
	    nullptr,
 | 
			
		||||
	    GL_TRUE
 | 
			
		||||
	);
 | 
			
		||||
#else // LIGHT_DIST
 | 
			
		||||
	return;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* setup message callback */
 | 
			
		||||
	glDebugMessageCallback(
 | 
			
		||||
	    [](unsigned int source,
 | 
			
		||||
	       unsigned int type,
 | 
			
		||||
	       unsigned int id,
 | 
			
		||||
	       unsigned int severity,
 | 
			
		||||
	       int /*length*/,
 | 
			
		||||
	       const char *message,
 | 
			
		||||
	       const void * /*userParam*/) {
 | 
			
		||||
		    switch (severity)
 | 
			
		||||
		    {
 | 
			
		||||
		    case GL_DEBUG_SEVERITY_HIGH:
 | 
			
		||||
			    // TODO(Light): Add gl exception class
 | 
			
		||||
			    throw std::runtime_error { "gl exception" };
 | 
			
		||||
			    return;
 | 
			
		||||
 | 
			
		||||
		    case GL_DEBUG_SEVERITY_MEDIUM:
 | 
			
		||||
		    case GL_DEBUG_SEVERITY_LOW:
 | 
			
		||||
			    log_wrn(
 | 
			
		||||
			        "glMessageCallback: Severity: {} :: Source: {} :: Type: {} :: ID: {}",
 | 
			
		||||
			        // TODO(Light): Stringify!
 | 
			
		||||
			        severity,
 | 
			
		||||
			        source,
 | 
			
		||||
			        type,
 | 
			
		||||
			        id
 | 
			
		||||
			    );
 | 
			
		||||
			    log_wrn("      {}", message);
 | 
			
		||||
			    return;
 | 
			
		||||
 | 
			
		||||
		    case GL_DEBUG_SEVERITY_NOTIFICATION:
 | 
			
		||||
			    log_wrn(
 | 
			
		||||
			        "Severity: {} :: Source: {} :: Type: {} :: ID: {}",
 | 
			
		||||
			        // TODO(Light): Stringify!
 | 
			
		||||
			        severity,
 | 
			
		||||
			        source,
 | 
			
		||||
			        type,
 | 
			
		||||
			        id
 | 
			
		||||
			    );
 | 
			
		||||
			    log_trc("        {}", message);
 | 
			
		||||
			    return;
 | 
			
		||||
		    }
 | 
			
		||||
	    },
 | 
			
		||||
	    nullptr
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										19
									
								
								modules/renderer/legacy/private/gl/graphics_context.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								modules/renderer/legacy/private/gl/graphics_context.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class glGraphicsContext: public GraphicsContext
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glGraphicsContext();
 | 
			
		||||
 | 
			
		||||
	void log_debug_data() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void set_debug_message_callback();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										44
									
								
								modules/renderer/legacy/private/gl/render_command.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								modules/renderer/legacy/private/gl/render_command.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
#include <renderer/gl/render_command.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
glRenderCommand::glRenderCommand(): m_window_handle(windowHandle)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glRenderCommand::swap_buffers()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glRenderCommand::clear_back_buffer(const math::vec4 &clearColor)
 | 
			
		||||
{
 | 
			
		||||
	glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
 | 
			
		||||
	glClear(GL_COLOR_BUFFER_BIT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glRenderCommand::draw(unsigned int count)
 | 
			
		||||
{
 | 
			
		||||
	glDrawArrays(GL_TRIANGLES, 0, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glRenderCommand::draw_indexed(unsigned int count)
 | 
			
		||||
{
 | 
			
		||||
	glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glRenderCommand::default_target_framebuffer()
 | 
			
		||||
{
 | 
			
		||||
	glBindFramebuffer(GL_FRAMEBUFFER, {});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glRenderCommand::set_viewport(
 | 
			
		||||
    unsigned int x,
 | 
			
		||||
    unsigned int y,
 | 
			
		||||
    unsigned int width,
 | 
			
		||||
    unsigned int height
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
	glViewport(x, y, width, height);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										31
									
								
								modules/renderer/legacy/private/gl/render_command.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								modules/renderer/legacy/private/gl/render_command.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <math/vec4.hpp>
 | 
			
		||||
#include <renderer/render_command.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class glRenderCommand: public RenderCommand
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glRenderCommand();
 | 
			
		||||
 | 
			
		||||
	void swap_buffers() override;
 | 
			
		||||
 | 
			
		||||
	void clear_back_buffer(const math::vec4 &clearColor) override;
 | 
			
		||||
 | 
			
		||||
	void draw(unsigned int count) override;
 | 
			
		||||
 | 
			
		||||
	void draw_indexed(unsigned int count) override;
 | 
			
		||||
 | 
			
		||||
	void default_target_framebuffer() override;
 | 
			
		||||
 | 
			
		||||
	void set_viewport(
 | 
			
		||||
	    unsigned int x,
 | 
			
		||||
	    unsigned int y,
 | 
			
		||||
	    unsigned int width,
 | 
			
		||||
	    unsigned int height
 | 
			
		||||
	) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										110
									
								
								modules/renderer/legacy/private/gl/shader.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								modules/renderer/legacy/private/gl/shader.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,110 @@
 | 
			
		|||
#include <asset_parser/assets/text.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
glShader::glShader(
 | 
			
		||||
    const Ref<Assets::TextAsset> &vertex_asset,
 | 
			
		||||
    const Ref<Assets::TextAsset> &pixel_asset
 | 
			
		||||
)
 | 
			
		||||
    : m_shader_id(glCreateProgram())
 | 
			
		||||
{
 | 
			
		||||
	auto vertex_blob_metadata = vertex_asset->get_blob_metadata(Assets::BlobMetadata::Tag::text);
 | 
			
		||||
	auto pixel_blob_metadata = pixel_asset->get_blob_metadata(Assets::BlobMetadata::Tag::text);
 | 
			
		||||
 | 
			
		||||
	auto vertex_blob = Assets::Blob(vertex_blob_metadata.uncompressed_size);
 | 
			
		||||
	auto pixel_blob = Assets::Blob(pixel_blob_metadata.uncompressed_size);
 | 
			
		||||
 | 
			
		||||
	vertex_asset->unpack_blob(vertex_blob_metadata.tag, vertex_blob.data(), vertex_blob.size());
 | 
			
		||||
	pixel_asset->unpack_blob(pixel_blob_metadata.tag, pixel_blob.data(), pixel_blob.size());
 | 
			
		||||
 | 
			
		||||
	auto vertex_source = std::string {
 | 
			
		||||
		reinterpret_cast<char *>(vertex_blob.data()),
 | 
			
		||||
		reinterpret_cast<char *>(vertex_blob.data()) + vertex_blob.size(), // NOLINT
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	auto pixel_source = std::string {
 | 
			
		||||
		reinterpret_cast<char *>(pixel_blob.data()),
 | 
			
		||||
		reinterpret_cast<char *>(pixel_blob.data()) + pixel_blob.size(), // NOLINT
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const auto vertex_shader = compile_shader(vertex_source, Shader::Stage::vertex);
 | 
			
		||||
	const auto pixel_shader = compile_shader(pixel_source, Shader::Stage::pixel);
 | 
			
		||||
 | 
			
		||||
	glAttachShader(m_shader_id, vertex_shader);
 | 
			
		||||
	glAttachShader(m_shader_id, pixel_shader);
 | 
			
		||||
 | 
			
		||||
	glLinkProgram(m_shader_id);
 | 
			
		||||
 | 
			
		||||
	glDeleteShader(vertex_shader);
 | 
			
		||||
	glDeleteShader(pixel_shader);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glShader::~glShader()
 | 
			
		||||
{
 | 
			
		||||
	glDeleteProgram(m_shader_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glShader::bind()
 | 
			
		||||
{
 | 
			
		||||
	glUseProgram(m_shader_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glShader::un_bind()
 | 
			
		||||
{
 | 
			
		||||
	glUseProgram({});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto glShader::compile_shader(const std::string &source, Shader::Stage stage) -> unsigned int
 | 
			
		||||
{
 | 
			
		||||
	// &(address of) needs an lvalue
 | 
			
		||||
	const auto *lvalue_source = source.c_str();
 | 
			
		||||
	auto shader = glCreateShader(
 | 
			
		||||
	    stage == Shader::Stage::vertex   ? GL_VERTEX_SHADER :
 | 
			
		||||
	    stage == Shader::Stage::pixel    ? GL_FRAGMENT_SHADER :
 | 
			
		||||
	    stage == Shader::Stage::geometry ? GL_GEOMETRY_SHADER :
 | 
			
		||||
	                                       0
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	// compile
 | 
			
		||||
	glShaderSource(shader, 1, &lvalue_source, nullptr);
 | 
			
		||||
	glCompileShader(shader);
 | 
			
		||||
 | 
			
		||||
	// check
 | 
			
		||||
	auto isCompiled = 0;
 | 
			
		||||
	glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
 | 
			
		||||
	if (isCompiled == GL_FALSE)
 | 
			
		||||
	{
 | 
			
		||||
		auto logLength = 0;
 | 
			
		||||
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
 | 
			
		||||
 | 
			
		||||
		auto *errorLog = (char *)alloca(logLength);
 | 
			
		||||
		glGetShaderInfoLog(shader, logLength, &logLength, &errorLog[0]);
 | 
			
		||||
 | 
			
		||||
		log_err(
 | 
			
		||||
		    "glShader::glShader: failed to compile {} shader:\n        {}",
 | 
			
		||||
		    stage == Shader::Stage::vertex ? "Vertex" : "Pixel",
 | 
			
		||||
		    errorLog
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
#define LIGHT_OPENGL_ENABLE_SHADER_INFO_LOG
 | 
			
		||||
#ifdef LIGHT_OPENGL_ENABLE_SHADER_INFO_LOG
 | 
			
		||||
	// info log
 | 
			
		||||
	{
 | 
			
		||||
		auto logLength = 0;
 | 
			
		||||
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
 | 
			
		||||
		if (logLength)
 | 
			
		||||
		{
 | 
			
		||||
			char *infoLog = (char *)alloca(logLength);
 | 
			
		||||
			glGetShaderInfoLog(shader, logLength, &logLength, &infoLog[0]);
 | 
			
		||||
 | 
			
		||||
			log_wrn("Shader info: {}", infoLog);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return shader;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										24
									
								
								modules/renderer/legacy/private/gl/shader.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								modules/renderer/legacy/private/gl/shader.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <renderer/shader.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class glShader: public Shader
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glShader(const Ref<Assets::TextAsset> &vertex_asset, const Ref<Assets::TextAsset> &pixel_asset);
 | 
			
		||||
 | 
			
		||||
	~glShader() override;
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	void un_bind() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	unsigned int compile_shader(const std::string &source, Shader::Stage stage);
 | 
			
		||||
 | 
			
		||||
	unsigned int m_shader_id { 0u };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										12
									
								
								modules/renderer/legacy/private/gl/shared_context.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								modules/renderer/legacy/private/gl/shared_context.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <renderer/shared_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class glSharedContext: public SharedContext
 | 
			
		||||
{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										81
									
								
								modules/renderer/legacy/private/gl/texture.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								modules/renderer/legacy/private/gl/texture.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
#include <asset_parser/assets/texture.hpp>
 | 
			
		||||
#include <lt_debug/assertions.hpp>
 | 
			
		||||
#include <renderer/gl/texture.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
glTexture::glTexture(const Ref<Assets::TextureAsset> &asset)
 | 
			
		||||
{
 | 
			
		||||
	const auto metadata = asset->get_metadata();
 | 
			
		||||
	const auto blob_metadata = asset->get_blob_metadata(Assets::BlobMetadata::Tag::color);
 | 
			
		||||
 | 
			
		||||
	glCreateTextures(GL_TEXTURE_2D, 1, &m_texture_id);
 | 
			
		||||
	glTextureParameteri(m_texture_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
 | 
			
		||||
	glTextureParameteri(m_texture_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
			
		||||
	glTextureParameteri(m_texture_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
			
		||||
	glTextureParameteri(m_texture_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 | 
			
		||||
 | 
			
		||||
	auto blob = std::vector<std::byte>(blob_metadata.uncompressed_size);
 | 
			
		||||
	asset->unpack_blob(blob_metadata.tag, blob.data(), blob.size());
 | 
			
		||||
 | 
			
		||||
	bind();
 | 
			
		||||
	glTexImage2D(
 | 
			
		||||
	    GL_TEXTURE_2D,
 | 
			
		||||
	    0,
 | 
			
		||||
	    map_num_components_to_internal_format(metadata.num_components),
 | 
			
		||||
	    static_cast<int>(metadata.pixel_size[0]),
 | 
			
		||||
	    static_cast<int>(metadata.pixel_size[1]),
 | 
			
		||||
	    0,
 | 
			
		||||
	    map_num_components_to_format(metadata.num_components),
 | 
			
		||||
	    GL_UNSIGNED_BYTE,
 | 
			
		||||
	    std::bit_cast<unsigned char *>(blob.data())
 | 
			
		||||
	);
 | 
			
		||||
	glGenerateMipmap(GL_TEXTURE_2D);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glTexture::~glTexture()
 | 
			
		||||
{
 | 
			
		||||
	glDeleteTextures(1, &m_texture_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glTexture::bind(unsigned int slot /* = 0u */)
 | 
			
		||||
{
 | 
			
		||||
	glActiveTexture(GL_TEXTURE0 + slot);
 | 
			
		||||
	glBindTexture(GL_TEXTURE_2D, m_texture_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto glTexture::get_texture() -> void *
 | 
			
		||||
{
 | 
			
		||||
	return (void *)(intptr_t)m_texture_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto glTexture::map_num_components_to_format(uint32_t num_components) const -> int
 | 
			
		||||
{
 | 
			
		||||
	switch (num_components)
 | 
			
		||||
	{
 | 
			
		||||
	case 4u: return GL_RGBA;
 | 
			
		||||
	case 3u: return GL_RGB;
 | 
			
		||||
	case 2u: return GL_RG;
 | 
			
		||||
	case 1u: return GL_RED;
 | 
			
		||||
	default: ensure(false, "Invalid number of components: {}", num_components);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto glTexture::map_num_components_to_internal_format(uint32_t num_components) const
 | 
			
		||||
    -> int
 | 
			
		||||
{
 | 
			
		||||
	switch (num_components)
 | 
			
		||||
	{
 | 
			
		||||
	case 4u: return GL_RGBA8;
 | 
			
		||||
	case 3u: return GL_RGB8;
 | 
			
		||||
	case 2u: return GL_RG8;
 | 
			
		||||
	case 1u: return GL_R8;
 | 
			
		||||
	default: ensure(false, "Invalid number of components: {}", num_components);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										26
									
								
								modules/renderer/legacy/private/gl/texture.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								modules/renderer/legacy/private/gl/texture.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <renderer/texture.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class glTexture: public Texture
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glTexture(const Ref<Assets::TextureAsset> &asset);
 | 
			
		||||
 | 
			
		||||
	~glTexture() override;
 | 
			
		||||
 | 
			
		||||
	void bind(unsigned int slot = 0u) override;
 | 
			
		||||
 | 
			
		||||
	auto get_texture() -> void * override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	[[nodiscard]] auto map_num_components_to_format(uint32_t num_components) const -> int;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto map_num_components_to_internal_format(uint32_t num_components) const -> int;
 | 
			
		||||
 | 
			
		||||
	uint32_t m_texture_id {};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										1
									
								
								modules/renderer/legacy/private/gl/user_interface.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								modules/renderer/legacy/private/gl/user_interface.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
 | 
			
		||||
							
								
								
									
										24
									
								
								modules/renderer/legacy/private/gl/user_interface.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								modules/renderer/legacy/private/gl/user_interface.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <ui/ui.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class glUserInterface: public UserInterface
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glUserInterface() = default;
 | 
			
		||||
 | 
			
		||||
	~glUserInterface() override;
 | 
			
		||||
 | 
			
		||||
	void platform_implementation(Ref<SharedContext> sharedContext) override;
 | 
			
		||||
 | 
			
		||||
	void begin() override;
 | 
			
		||||
 | 
			
		||||
	void end() override;
 | 
			
		||||
 | 
			
		||||
	void log_debug_data() override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										146
									
								
								modules/renderer/legacy/private/gl/vertex_layout.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								modules/renderer/legacy/private/gl/vertex_layout.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
#include <lt_debug/assertions.hpp>
 | 
			
		||||
#include <renderer/gl/buffers.hpp>
 | 
			
		||||
#include <renderer/gl/vertex_layout.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
glVertexLayout::glVertexLayout(
 | 
			
		||||
    const Ref<VertexBuffer> &buffer,
 | 
			
		||||
    const std::vector<std::pair<std::string, VertexElementType>> &elements
 | 
			
		||||
)
 | 
			
		||||
    : m_array_id()
 | 
			
		||||
{
 | 
			
		||||
	// check
 | 
			
		||||
	ensure(
 | 
			
		||||
	    std::dynamic_pointer_cast<glVertexBuffer>(buffer),
 | 
			
		||||
	    "Failed to cast 'VertexBuffer' to 'glVertexBuffer'"
 | 
			
		||||
	);
 | 
			
		||||
	ensure(!elements.empty(), "'elements' is empty");
 | 
			
		||||
 | 
			
		||||
	// local
 | 
			
		||||
	auto elementsDesc = std::vector<glVertexElementDesc> {};
 | 
			
		||||
	elementsDesc.reserve(elements.size());
 | 
			
		||||
	auto stride = 0u;
 | 
			
		||||
 | 
			
		||||
	// extract elements desc
 | 
			
		||||
	for (const auto &element : elements)
 | 
			
		||||
	{
 | 
			
		||||
		elementsDesc.push_back(get_element_desc(element.second, stride));
 | 
			
		||||
		stride += elementsDesc.back().typeSize * elementsDesc.back().count;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// create vertex array
 | 
			
		||||
	glCreateVertexArrays(1, &m_array_id);
 | 
			
		||||
 | 
			
		||||
	// bind buffer and array
 | 
			
		||||
	buffer->bind();
 | 
			
		||||
	bind();
 | 
			
		||||
 | 
			
		||||
	// enable vertex attributes
 | 
			
		||||
	auto index = 0u;
 | 
			
		||||
	for (const auto &elementDesc : elementsDesc)
 | 
			
		||||
	{
 | 
			
		||||
		glVertexAttribPointer(
 | 
			
		||||
		    index,
 | 
			
		||||
		    elementDesc.count,
 | 
			
		||||
		    elementDesc.type,
 | 
			
		||||
		    GL_FALSE,
 | 
			
		||||
		    stride,
 | 
			
		||||
		    (const void *)elementDesc.offset
 | 
			
		||||
		);
 | 
			
		||||
		glEnableVertexAttribArray(index++);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glVertexLayout::~glVertexLayout()
 | 
			
		||||
{
 | 
			
		||||
	glDeleteVertexArrays(1, &m_array_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glVertexLayout::bind()
 | 
			
		||||
{
 | 
			
		||||
	glBindVertexArray(m_array_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glVertexLayout::un_bind()
 | 
			
		||||
{
 | 
			
		||||
	glBindVertexArray({});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto glVertexLayout::get_element_desc(VertexElementType type, unsigned int offset)
 | 
			
		||||
    -> glVertexElementDesc
 | 
			
		||||
{
 | 
			
		||||
	switch (type)
 | 
			
		||||
	{
 | 
			
		||||
	/* byte */
 | 
			
		||||
	case lt::VertexElementType::Byte1:
 | 
			
		||||
		return { .type = GL_BYTE, .count = 1u, .typeSize = sizeof(GLbyte), .offset = offset };
 | 
			
		||||
	case lt::VertexElementType::Byte2:
 | 
			
		||||
		return { .type = GL_BYTE, .count = 1u, .typeSize = sizeof(GLbyte), .offset = offset };
 | 
			
		||||
	case lt::VertexElementType::Byte4:
 | 
			
		||||
		return { .type = GL_BYTE, .count = 1u, .typeSize = sizeof(GLbyte), .offset = offset };
 | 
			
		||||
 | 
			
		||||
	/* ubyte */
 | 
			
		||||
	case lt::VertexElementType::UByte1:
 | 
			
		||||
		return { .type = GL_UNSIGNED_BYTE,
 | 
			
		||||
			     .count = 1u,
 | 
			
		||||
			     .typeSize = sizeof(GLubyte),
 | 
			
		||||
			     .offset = offset };
 | 
			
		||||
	case lt::VertexElementType::UByte2:
 | 
			
		||||
		return { .type = GL_UNSIGNED_BYTE,
 | 
			
		||||
			     .count = 2u,
 | 
			
		||||
			     .typeSize = sizeof(GLubyte),
 | 
			
		||||
			     .offset = offset };
 | 
			
		||||
	case lt::VertexElementType::UByte4:
 | 
			
		||||
		return { .type = GL_UNSIGNED_BYTE,
 | 
			
		||||
			     .count = 4u,
 | 
			
		||||
			     .typeSize = sizeof(GLubyte),
 | 
			
		||||
			     .offset = offset };
 | 
			
		||||
 | 
			
		||||
	/* int */
 | 
			
		||||
	case VertexElementType::Int1:
 | 
			
		||||
		return { .type = GL_INT, .count = 1u, .typeSize = sizeof(GLint), .offset = offset };
 | 
			
		||||
	case VertexElementType::Int2:
 | 
			
		||||
		return { .type = GL_INT, .count = 2u, .typeSize = sizeof(GLint), .offset = offset };
 | 
			
		||||
	case VertexElementType::Int3:
 | 
			
		||||
		return { .type = GL_INT, .count = 3u, .typeSize = sizeof(GLint), .offset = offset };
 | 
			
		||||
	case VertexElementType::Int4:
 | 
			
		||||
		return { .type = GL_INT, .count = 4u, .typeSize = sizeof(GLint), .offset = offset };
 | 
			
		||||
 | 
			
		||||
	/* uint */
 | 
			
		||||
	case VertexElementType::UInt1:
 | 
			
		||||
		return { .type = GL_UNSIGNED_INT,
 | 
			
		||||
			     .count = 1u,
 | 
			
		||||
			     .typeSize = sizeof(GLuint),
 | 
			
		||||
			     .offset = offset };
 | 
			
		||||
	case VertexElementType::UInt2:
 | 
			
		||||
		return { .type = GL_UNSIGNED_INT,
 | 
			
		||||
			     .count = 2u,
 | 
			
		||||
			     .typeSize = sizeof(GLuint),
 | 
			
		||||
			     .offset = offset };
 | 
			
		||||
	case VertexElementType::UInt3:
 | 
			
		||||
		return { .type = GL_UNSIGNED_INT,
 | 
			
		||||
			     .count = 3u,
 | 
			
		||||
			     .typeSize = sizeof(GLuint),
 | 
			
		||||
			     .offset = offset };
 | 
			
		||||
	case VertexElementType::UInt4:
 | 
			
		||||
		return { .type = GL_UNSIGNED_INT,
 | 
			
		||||
			     .count = 4u,
 | 
			
		||||
			     .typeSize = sizeof(GLuint),
 | 
			
		||||
			     .offset = offset };
 | 
			
		||||
 | 
			
		||||
	/* float */
 | 
			
		||||
	case VertexElementType::Float1:
 | 
			
		||||
		return { .type = GL_FLOAT, .count = 1u, .typeSize = sizeof(GLfloat), .offset = offset };
 | 
			
		||||
	case VertexElementType::Float2:
 | 
			
		||||
		return { .type = GL_FLOAT, .count = 2u, .typeSize = sizeof(GLfloat), .offset = offset };
 | 
			
		||||
	case VertexElementType::Float3:
 | 
			
		||||
		return { .type = GL_FLOAT, .count = 3u, .typeSize = sizeof(GLfloat), .offset = offset };
 | 
			
		||||
	case VertexElementType::Float4:
 | 
			
		||||
		return { .type = GL_FLOAT, .count = 4u, .typeSize = sizeof(GLfloat), .offset = offset };
 | 
			
		||||
 | 
			
		||||
	default: ensure(false, "Invalid 'VertexElementType'"); return {};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										41
									
								
								modules/renderer/legacy/private/gl/vertex_layout.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								modules/renderer/legacy/private/gl/vertex_layout.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <renderer/vertex_layout.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class VertexBuffer;
 | 
			
		||||
 | 
			
		||||
struct glVertexElementDesc
 | 
			
		||||
{
 | 
			
		||||
	unsigned int type;
 | 
			
		||||
 | 
			
		||||
	unsigned int count;
 | 
			
		||||
 | 
			
		||||
	unsigned int typeSize;
 | 
			
		||||
 | 
			
		||||
	unsigned int offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class glVertexLayout: public VertexLayout
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	glVertexLayout(
 | 
			
		||||
	    const Ref<VertexBuffer> &buffer,
 | 
			
		||||
	    const std::vector<std::pair<std::string, VertexElementType>> &elements
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	~glVertexLayout() override;
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	void un_bind() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	auto get_element_desc(VertexElementType type, unsigned int offset) -> glVertexElementDesc;
 | 
			
		||||
 | 
			
		||||
	unsigned int m_array_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										58
									
								
								modules/renderer/legacy/private/graphics_context.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								modules/renderer/legacy/private/graphics_context.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
#include <renderer/gl/graphics_context.hpp>
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef LIGHT_PLATFORM_WINDOWS
 | 
			
		||||
	#include <renderer/dx/graphics_context.hpp>
 | 
			
		||||
	#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
GraphicsContext *GraphicsContext::s_context = nullptr;
 | 
			
		||||
 | 
			
		||||
GraphicsContext::~GraphicsContext() = default;
 | 
			
		||||
 | 
			
		||||
auto GraphicsContext::create(GraphicsAPI api) -> Scope<GraphicsContext>
 | 
			
		||||
{
 | 
			
		||||
	delete s_context;
 | 
			
		||||
 | 
			
		||||
	if (api == GraphicsAPI::Default)
 | 
			
		||||
	{
 | 
			
		||||
#if defined(LIGHT_PLATFORM_WINDOWS)
 | 
			
		||||
		api = GraphicsAPI::DirectX;
 | 
			
		||||
#elif defined(LIGHT_PLATFORM_LINUX)
 | 
			
		||||
		api = GraphicsAPI::OpenGL;
 | 
			
		||||
#elif defined(LIGHT_PLATFORM_MAC)
 | 
			
		||||
		api = GraphicsAPI::OpenGL;
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto scope_gfx = Scope<GraphicsContext> {};
 | 
			
		||||
	switch (api)
 | 
			
		||||
	{
 | 
			
		||||
	// opengl
 | 
			
		||||
	case GraphicsAPI::OpenGL:
 | 
			
		||||
		scope_gfx = create_scope<glGraphicsContext>(window_handle);
 | 
			
		||||
		s_context = scope_gfx.get();
 | 
			
		||||
		break;
 | 
			
		||||
	// directx
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    scope_gfx = create_scope<dxGraphicsContext>(window_handle); s_context = scope_gfx.get();
 | 
			
		||||
		    break;
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		    default
 | 
			
		||||
		    : ensure(
 | 
			
		||||
		          false,
 | 
			
		||||
		          "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		          // TODO(Light): Stringifier::graphics_api_to_string(api),
 | 
			
		||||
		          "TODO"
 | 
			
		||||
		      );
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return std::move(scope_gfx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										71
									
								
								modules/renderer/legacy/private/programs/quad.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								modules/renderer/legacy/private/programs/quad.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
#include <camera/camera.hpp>
 | 
			
		||||
#include <renderer/buffers.hpp>
 | 
			
		||||
#include <renderer/programs/quad.hpp>
 | 
			
		||||
#include <renderer/shader.hpp>
 | 
			
		||||
#include <renderer/vertex_layout.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
QuadRendererProgram::QuadRendererProgram(
 | 
			
		||||
    unsigned int max_vertices,
 | 
			
		||||
    const Ref<SharedContext> &shared_context,
 | 
			
		||||
    Ref<Shader> shader
 | 
			
		||||
)
 | 
			
		||||
    : m_index_buffer(nullptr)
 | 
			
		||||
    , m_vertex_layout(nullptr)
 | 
			
		||||
    , m_max_vertices(max_vertices)
 | 
			
		||||
    , m_shader(std::move(shader))
 | 
			
		||||
{
 | 
			
		||||
	m_vertex_buffer = Ref<VertexBuffer>(
 | 
			
		||||
	    VertexBuffer::create(nullptr, sizeof(QuadVertexData), max_vertices, shared_context)
 | 
			
		||||
	);
 | 
			
		||||
	m_index_buffer = Ref<IndexBuffer>(
 | 
			
		||||
	    IndexBuffer::create(nullptr, (max_vertices / 4) * 6, shared_context)
 | 
			
		||||
	);
 | 
			
		||||
	m_vertex_layout = Ref<VertexLayout>(VertexLayout::create(
 | 
			
		||||
	    m_vertex_buffer,
 | 
			
		||||
	    m_shader,
 | 
			
		||||
	    { { "POSITION", VertexElementType::Float4 }, { "COLOR", VertexElementType::Float4 } },
 | 
			
		||||
	    shared_context
 | 
			
		||||
	));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto QuadRendererProgram::advance() -> bool
 | 
			
		||||
{
 | 
			
		||||
	m_idx += 4;
 | 
			
		||||
	if (m_idx >= m_map.size())
 | 
			
		||||
	{
 | 
			
		||||
		log_wrn("'VertexBuffer' map went beyond 'MaxVertices': {}", m_max_vertices);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_quad_count++;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QuadRendererProgram::map()
 | 
			
		||||
{
 | 
			
		||||
	m_map = std::span<QuadVertexData> {
 | 
			
		||||
		static_cast<QuadRendererProgram::QuadVertexData *>(m_vertex_buffer->map()),
 | 
			
		||||
		m_max_vertices,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	m_quad_count = 0u;
 | 
			
		||||
	m_idx = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QuadRendererProgram::un_map()
 | 
			
		||||
{
 | 
			
		||||
	m_vertex_buffer->un_map();
 | 
			
		||||
	m_idx = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QuadRendererProgram::bind()
 | 
			
		||||
{
 | 
			
		||||
	m_shader->bind();
 | 
			
		||||
	m_vertex_layout->bind();
 | 
			
		||||
	m_vertex_buffer->bind();
 | 
			
		||||
	m_index_buffer->bind();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										74
									
								
								modules/renderer/legacy/private/programs/quad.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								modules/renderer/legacy/private/programs/quad.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <math/vec4.hpp>
 | 
			
		||||
#include <renderer/programs/renderer_program.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class Shader;
 | 
			
		||||
class VertexBuffer;
 | 
			
		||||
class IndexBuffer;
 | 
			
		||||
class VertexLayout;
 | 
			
		||||
class OrthographicCamera;
 | 
			
		||||
class SharedContext;
 | 
			
		||||
class Shader;
 | 
			
		||||
 | 
			
		||||
class QuadRendererProgram: RendererProgram
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	~QuadRendererProgram() override = default;
 | 
			
		||||
	struct QuadVertexData
 | 
			
		||||
	{
 | 
			
		||||
		math::vec4 position;
 | 
			
		||||
 | 
			
		||||
		math::vec4 tint;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	QuadRendererProgram(
 | 
			
		||||
	    unsigned int max_vertices,
 | 
			
		||||
	    const Ref<SharedContext> &shared_context,
 | 
			
		||||
	    Ref<Shader> shader
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	auto advance() -> bool;
 | 
			
		||||
 | 
			
		||||
	void map() override;
 | 
			
		||||
 | 
			
		||||
	void un_map() override;
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	auto get_map_current() -> QuadVertexData *
 | 
			
		||||
	{
 | 
			
		||||
		return &m_map[m_idx];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto get_quad_count() const -> unsigned int
 | 
			
		||||
	{
 | 
			
		||||
		return m_quad_count;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] constexpr auto get_vertex_size() const -> unsigned int
 | 
			
		||||
	{
 | 
			
		||||
		return sizeof(QuadVertexData);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<Shader> m_shader;
 | 
			
		||||
 | 
			
		||||
	Ref<VertexBuffer> m_vertex_buffer;
 | 
			
		||||
 | 
			
		||||
	Ref<IndexBuffer> m_index_buffer;
 | 
			
		||||
 | 
			
		||||
	Ref<VertexLayout> m_vertex_layout;
 | 
			
		||||
 | 
			
		||||
	std::span<QuadVertexData> m_map;
 | 
			
		||||
 | 
			
		||||
	size_t m_idx {};
 | 
			
		||||
 | 
			
		||||
	unsigned int m_quad_count = 0u;
 | 
			
		||||
 | 
			
		||||
	unsigned int m_max_vertices = 0u;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class RendererProgram
 | 
			
		||||
{
 | 
			
		||||
	virtual void map() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void un_map() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void bind() = 0;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	virtual ~RendererProgram() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										94
									
								
								modules/renderer/legacy/private/programs/texture.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								modules/renderer/legacy/private/programs/texture.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,94 @@
 | 
			
		|||
#include <camera/camera.hpp>
 | 
			
		||||
#include <renderer/buffers.hpp>
 | 
			
		||||
#include <renderer/programs/texture.hpp>
 | 
			
		||||
#include <renderer/shader.hpp>
 | 
			
		||||
#include <renderer/vertex_layout.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
TextureRendererProgram::TextureRendererProgram(
 | 
			
		||||
    unsigned int max_vertices,
 | 
			
		||||
    const Ref<SharedContext> &shared_context,
 | 
			
		||||
    Ref<Shader> shader
 | 
			
		||||
)
 | 
			
		||||
    : m_shader(std::move(shader))
 | 
			
		||||
    , m_index_buffer(nullptr)
 | 
			
		||||
    , m_vertex_layout(nullptr)
 | 
			
		||||
    , m_max_vertices(max_vertices)
 | 
			
		||||
{
 | 
			
		||||
	// #todo: don't use relative path
 | 
			
		||||
	// AssetManager::load_shader(
 | 
			
		||||
	//     "LT_ENGINE_RESOURCES_TEXTURE_SHADER",
 | 
			
		||||
	//     "data/assets/shaders/texture/vs.asset",
 | 
			
		||||
	//     "data/assets/shaders/texture/ps.asset"
 | 
			
		||||
	// );
 | 
			
		||||
	//
 | 
			
		||||
	// AssetManager::load_shader(
 | 
			
		||||
	//     "LT_ENGINE_RESOURCES_TINTED_TEXTURE_SHADER",
 | 
			
		||||
	//     "data/assets/shaders/tinted_texture/vs.asset",
 | 
			
		||||
	//     "data/assets/shaders/tinted_texture/ps.asset"
 | 
			
		||||
	// );
 | 
			
		||||
	//
 | 
			
		||||
	// AssetManager::load_shader(
 | 
			
		||||
	//     "LT_ENGINE_RESOURCES_QUAD_SHADER",
 | 
			
		||||
	//     "data/assets/shaders/quads/vs.asset",
 | 
			
		||||
	//     "data/assets/shaders/quads/ps.asset"
 | 
			
		||||
	// );
 | 
			
		||||
	//
 | 
			
		||||
	// m_shader = AssetManager::get_shader("LT_ENGINE_RESOURCES_TEXTURE_SHADER");
 | 
			
		||||
	// m_shader = AssetManager::get_shader("LT_ENGINE_RESOURCES_TINTED_TEXTURE_SHADER");
 | 
			
		||||
	// m_shader = AssetManager::get_shader("LT_ENGINE_RESOURCES_QUAD_SHADER");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	m_vertex_buffer = Ref<VertexBuffer>(
 | 
			
		||||
	    VertexBuffer::create(nullptr, sizeof(TextureVertexData), max_vertices, shared_context)
 | 
			
		||||
	);
 | 
			
		||||
	m_index_buffer = Ref<IndexBuffer>(
 | 
			
		||||
	    IndexBuffer::create(nullptr, (max_vertices / 4) * 6, shared_context)
 | 
			
		||||
	);
 | 
			
		||||
	m_vertex_layout = Ref<VertexLayout>(VertexLayout::create(
 | 
			
		||||
	    m_vertex_buffer,
 | 
			
		||||
	    m_shader,
 | 
			
		||||
	    { { "POSITION", VertexElementType::Float4 }, { "TEXCOORD", VertexElementType::Float2 } },
 | 
			
		||||
	    shared_context
 | 
			
		||||
	));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto TextureRendererProgram::advance() -> bool
 | 
			
		||||
{
 | 
			
		||||
	m_idx += 4;
 | 
			
		||||
	if (m_idx >= m_map.size())
 | 
			
		||||
	{
 | 
			
		||||
		log_wrn("'VertexBuffer' map went beyond 'MaxVertices': {}", m_max_vertices);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_quad_count++;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TextureRendererProgram::map()
 | 
			
		||||
{
 | 
			
		||||
	m_map = std::span<TextureVertexData> {
 | 
			
		||||
		static_cast<TextureVertexData *>(m_vertex_buffer->map()),
 | 
			
		||||
		m_max_vertices,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	m_quad_count = 0u;
 | 
			
		||||
	m_idx = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TextureRendererProgram::un_map()
 | 
			
		||||
{
 | 
			
		||||
	m_vertex_buffer->un_map();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TextureRendererProgram::bind()
 | 
			
		||||
{
 | 
			
		||||
	m_shader->bind();
 | 
			
		||||
	m_vertex_layout->bind();
 | 
			
		||||
	m_vertex_buffer->bind();
 | 
			
		||||
	m_index_buffer->bind();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										75
									
								
								modules/renderer/legacy/private/programs/texture.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								modules/renderer/legacy/private/programs/texture.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,75 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <math/vec2.hpp>
 | 
			
		||||
#include <math/vec4.hpp>
 | 
			
		||||
#include <renderer/programs/renderer_program.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class Shader;
 | 
			
		||||
class VertexBuffer;
 | 
			
		||||
class IndexBuffer;
 | 
			
		||||
class VertexLayout;
 | 
			
		||||
class OrthographicCamera;
 | 
			
		||||
class SharedContext;
 | 
			
		||||
 | 
			
		||||
class TextureRendererProgram: RendererProgram
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	~TextureRendererProgram() override = default;
 | 
			
		||||
 | 
			
		||||
	struct TextureVertexData
 | 
			
		||||
	{
 | 
			
		||||
		math::vec4 position;
 | 
			
		||||
 | 
			
		||||
		math::vec2 texcoord;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	TextureRendererProgram(
 | 
			
		||||
	    unsigned int max_vertices,
 | 
			
		||||
	    const Ref<SharedContext> &shared_context,
 | 
			
		||||
	    Ref<Shader> shader
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	auto advance() -> bool;
 | 
			
		||||
 | 
			
		||||
	void map() override;
 | 
			
		||||
 | 
			
		||||
	void un_map() override;
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	auto get_map_current() -> TextureVertexData *
 | 
			
		||||
	{
 | 
			
		||||
		return &m_map[m_idx];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto get_quad_count() const -> unsigned int
 | 
			
		||||
	{
 | 
			
		||||
		return m_quad_count;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] constexpr auto get_vertex_size() const -> unsigned int
 | 
			
		||||
	{
 | 
			
		||||
		return sizeof(TextureVertexData);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<Shader> m_shader;
 | 
			
		||||
 | 
			
		||||
	Ref<VertexBuffer> m_vertex_buffer;
 | 
			
		||||
 | 
			
		||||
	Ref<IndexBuffer> m_index_buffer;
 | 
			
		||||
 | 
			
		||||
	Ref<VertexLayout> m_vertex_layout;
 | 
			
		||||
 | 
			
		||||
	std::span<TextureVertexData> m_map;
 | 
			
		||||
 | 
			
		||||
	size_t m_idx {};
 | 
			
		||||
 | 
			
		||||
	unsigned int m_quad_count { 0u };
 | 
			
		||||
 | 
			
		||||
	unsigned int m_max_vertices;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										73
									
								
								modules/renderer/legacy/private/programs/tinted_texture.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								modules/renderer/legacy/private/programs/tinted_texture.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
#include <camera/camera.hpp>
 | 
			
		||||
#include <renderer/buffers.hpp>
 | 
			
		||||
#include <renderer/programs/tinted_texture.hpp>
 | 
			
		||||
#include <renderer/shader.hpp>
 | 
			
		||||
#include <renderer/vertex_layout.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
TintedTextureRendererProgram::TintedTextureRendererProgram(
 | 
			
		||||
    unsigned int max_vertices,
 | 
			
		||||
    const Ref<SharedContext> &shared_context,
 | 
			
		||||
    Ref<Shader> shader
 | 
			
		||||
 | 
			
		||||
)
 | 
			
		||||
    : m_shader(std::move(shader))
 | 
			
		||||
    , m_index_buffer(nullptr)
 | 
			
		||||
    , m_vertex_layout(nullptr)
 | 
			
		||||
    , m_max_vertices(max_vertices)
 | 
			
		||||
{
 | 
			
		||||
	m_vertex_buffer = Ref<VertexBuffer>(
 | 
			
		||||
	    VertexBuffer::create(nullptr, sizeof(TintedTextureVertexData), max_vertices, shared_context)
 | 
			
		||||
	);
 | 
			
		||||
	m_index_buffer = Ref<IndexBuffer>(
 | 
			
		||||
	    IndexBuffer::create(nullptr, (max_vertices / 4) * 6, shared_context)
 | 
			
		||||
	);
 | 
			
		||||
	m_vertex_layout = Ref<VertexLayout>(VertexLayout::create(
 | 
			
		||||
	    m_vertex_buffer,
 | 
			
		||||
	    m_shader,
 | 
			
		||||
	    { { "POSITION", VertexElementType::Float4 },
 | 
			
		||||
	      { "TINT", VertexElementType::Float4 },
 | 
			
		||||
	      { "TEXCOORD", VertexElementType::Float2 } },
 | 
			
		||||
	    shared_context
 | 
			
		||||
	));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto TintedTextureRendererProgram::advance() -> bool
 | 
			
		||||
{
 | 
			
		||||
	m_idx += 4;
 | 
			
		||||
	if (m_idx >= m_map.size())
 | 
			
		||||
	{
 | 
			
		||||
		log_wrn("'VertexBuffer' map went beyond 'MaxVertices': {}", m_max_vertices);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_quad_count++;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TintedTextureRendererProgram::map()
 | 
			
		||||
{
 | 
			
		||||
	m_map = std::span<TintedTextureVertexData> {
 | 
			
		||||
		static_cast<TintedTextureVertexData *>(m_vertex_buffer->map()),
 | 
			
		||||
		m_max_vertices,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	m_quad_count = 0u;
 | 
			
		||||
	m_idx = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TintedTextureRendererProgram::un_map()
 | 
			
		||||
{
 | 
			
		||||
	m_vertex_buffer->un_map();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TintedTextureRendererProgram::bind()
 | 
			
		||||
{
 | 
			
		||||
	m_shader->bind();
 | 
			
		||||
	m_vertex_layout->bind();
 | 
			
		||||
	m_vertex_buffer->bind();
 | 
			
		||||
	m_index_buffer->bind();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										77
									
								
								modules/renderer/legacy/private/programs/tinted_texture.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								modules/renderer/legacy/private/programs/tinted_texture.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,77 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <math/vec2.hpp>
 | 
			
		||||
#include <math/vec4.hpp>
 | 
			
		||||
#include <renderer/programs/renderer_program.hpp>
 | 
			
		||||
#include <span>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class Shader;
 | 
			
		||||
class VertexBuffer;
 | 
			
		||||
class IndexBuffer;
 | 
			
		||||
class VertexLayout;
 | 
			
		||||
class OrthographicCamera;
 | 
			
		||||
class SharedContext;
 | 
			
		||||
 | 
			
		||||
class TintedTextureRendererProgram: RendererProgram
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	~TintedTextureRendererProgram() override = default;
 | 
			
		||||
	struct TintedTextureVertexData
 | 
			
		||||
	{
 | 
			
		||||
		math::vec4 position;
 | 
			
		||||
 | 
			
		||||
		math::vec4 tint;
 | 
			
		||||
 | 
			
		||||
		math::vec2 texcoord;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	TintedTextureRendererProgram(
 | 
			
		||||
	    unsigned int max_vertices,
 | 
			
		||||
	    const Ref<SharedContext> &shared_context,
 | 
			
		||||
	    Ref<Shader> shader
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	auto advance() -> bool;
 | 
			
		||||
 | 
			
		||||
	void map() override;
 | 
			
		||||
 | 
			
		||||
	void un_map() override;
 | 
			
		||||
 | 
			
		||||
	void bind() override;
 | 
			
		||||
 | 
			
		||||
	auto get_map_current() -> TintedTextureVertexData *
 | 
			
		||||
	{
 | 
			
		||||
		return &m_map[m_idx];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto get_quad_count() const -> unsigned int
 | 
			
		||||
	{
 | 
			
		||||
		return m_quad_count;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] constexpr auto get_vertex_size() const -> unsigned int
 | 
			
		||||
	{
 | 
			
		||||
		return sizeof(TintedTextureVertexData);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<Shader> m_shader;
 | 
			
		||||
 | 
			
		||||
	Ref<VertexBuffer> m_vertex_buffer;
 | 
			
		||||
 | 
			
		||||
	Ref<IndexBuffer> m_index_buffer;
 | 
			
		||||
 | 
			
		||||
	Ref<VertexLayout> m_vertex_layout;
 | 
			
		||||
 | 
			
		||||
	std::span<TintedTextureVertexData> m_map;
 | 
			
		||||
 | 
			
		||||
	size_t m_idx {};
 | 
			
		||||
 | 
			
		||||
	unsigned int m_quad_count { 0u };
 | 
			
		||||
 | 
			
		||||
	unsigned int m_max_vertices;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										37
									
								
								modules/renderer/legacy/private/render_command.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								modules/renderer/legacy/private/render_command.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
#include <lt_debug/assertions.hpp>
 | 
			
		||||
#include <renderer/gl/render_command.hpp>
 | 
			
		||||
#include <renderer/render_command.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef LIGHT_PLATFORM_WINDOWS
 | 
			
		||||
	#include <renderer/dx/render_command.hpp>
 | 
			
		||||
	#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
auto RenderCommand::create(const Ref<SharedContext> & /*sharedContext*/) -> Scope<RenderCommand>
 | 
			
		||||
{
 | 
			
		||||
	switch (GraphicsContext::get_graphics_api())
 | 
			
		||||
	{
 | 
			
		||||
	case GraphicsAPI::OpenGL: return create_scope<glRenderCommand>();
 | 
			
		||||
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    return create_scope<dxRenderCommand>(
 | 
			
		||||
		               (std::static_pointer_cast<dxSharedContext>)(sharedContext)
 | 
			
		||||
		    );
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		    default
 | 
			
		||||
		    : ensure(
 | 
			
		||||
		          false,
 | 
			
		||||
		          "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		          static_cast<uint32_t>(GraphicsContext::get_graphics_api())
 | 
			
		||||
		      );
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										330
									
								
								modules/renderer/legacy/private/renderer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								modules/renderer/legacy/private/renderer.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,330 @@
 | 
			
		|||
#include <ecs/registry.hpp>
 | 
			
		||||
#include <input/events/window.hpp>
 | 
			
		||||
#include <lt_debug/assertions.hpp>
 | 
			
		||||
#include <renderer/blender.hpp>
 | 
			
		||||
#include <renderer/buffers.hpp>
 | 
			
		||||
#include <renderer/framebuffer.hpp>
 | 
			
		||||
#include <renderer/programs/quad.hpp>
 | 
			
		||||
#include <renderer/programs/texture.hpp>
 | 
			
		||||
#include <renderer/programs/tinted_texture.hpp>
 | 
			
		||||
#include <renderer/render_command.hpp>
 | 
			
		||||
#include <renderer/renderer.hpp>
 | 
			
		||||
#include <renderer/shader.hpp>
 | 
			
		||||
#include <renderer/texture.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
Renderer *Renderer::s_context = nullptr;
 | 
			
		||||
 | 
			
		||||
Renderer::Renderer(const Ref<SharedContext> &shared_context, CreateInfo create_info)
 | 
			
		||||
    : m_quad_renderer(
 | 
			
		||||
          create_scope<QuadRendererProgram>(
 | 
			
		||||
              LT_MAX_QUAD_RENDERER_VERTICES,
 | 
			
		||||
              shared_context,
 | 
			
		||||
              std::move(create_info.quad_renderer_shader)
 | 
			
		||||
          )
 | 
			
		||||
      )
 | 
			
		||||
    , m_texture_renderer(
 | 
			
		||||
          create_scope<TextureRendererProgram>(
 | 
			
		||||
              LT_MAX_TEXTURE_RENDERER_VERTICES,
 | 
			
		||||
              shared_context,
 | 
			
		||||
              std::move(create_info.texture_renderer_shader)
 | 
			
		||||
          )
 | 
			
		||||
      )
 | 
			
		||||
    , m_tinted_texture_renderer(
 | 
			
		||||
          create_scope<TintedTextureRendererProgram>(
 | 
			
		||||
              LT_MAX_TINTED_TEXTURE_RENDERER_VERTICES,
 | 
			
		||||
              shared_context,
 | 
			
		||||
              std::move(create_info.tinted_texture_renderer_shader)
 | 
			
		||||
          )
 | 
			
		||||
      )
 | 
			
		||||
    , m_view_projection_buffer(nullptr)
 | 
			
		||||
    , m_render_command(nullptr)
 | 
			
		||||
    , m_blender(nullptr)
 | 
			
		||||
    , m_target_framebuffer(nullptr)
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	ensure(!s_context, "An instance of 'renderer' already exists, do not construct this class!");
 | 
			
		||||
	s_context = this;
 | 
			
		||||
 | 
			
		||||
	m_view_projection_buffer = ConstantBuffer::create(
 | 
			
		||||
	    ConstantBufferIndex::ViewProjection,
 | 
			
		||||
	    sizeof(math::mat4),
 | 
			
		||||
	    shared_context
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	m_render_command = RenderCommand::create(window_handle, shared_context);
 | 
			
		||||
	m_blender = Blender::create(shared_context);
 | 
			
		||||
	m_blender->enable(BlendFactor::SRC_ALPHA, BlendFactor::INVERSE_SRC_ALPHA);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Renderer::~Renderer() // NOLINT
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto Renderer::create(Ref<SharedContext> sharedContext, CreateInfo create_info) -> Scope<Renderer>
 | 
			
		||||
{
 | 
			
		||||
	return make_scope<Renderer>(new Renderer(std::move(sharedContext), std::move(create_info)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::on_window_resize(const WindowResizedEvent &event)
 | 
			
		||||
{
 | 
			
		||||
	m_render_command->set_viewport(0u, 0u, event.get_size().x, event.get_size().y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//======================================== DRAW_QUAD ========================================//
 | 
			
		||||
/* tinted textures */
 | 
			
		||||
void Renderer::draw_quad_impl(
 | 
			
		||||
    const math::vec3 &position,
 | 
			
		||||
    const math::vec2 &size,
 | 
			
		||||
    const math::vec4 &tint,
 | 
			
		||||
    Ref<Texture> texture
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
	draw_quad(
 | 
			
		||||
	    math::translate(position) * math::scale(math::vec3 { size.x, size.y, 1.0f }),
 | 
			
		||||
	    tint,
 | 
			
		||||
	    texture
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* tint */
 | 
			
		||||
void Renderer::draw_quad_impl(
 | 
			
		||||
    const math::vec3 &position,
 | 
			
		||||
    const math::vec2 &size,
 | 
			
		||||
    const math::vec4 &tint
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
	draw_quad(math::translate(position) * math::scale(math::vec3 { size.x, size.y, 1.0f }), tint);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* texture */
 | 
			
		||||
void Renderer::draw_quad_impl(
 | 
			
		||||
    const math::vec3 &position,
 | 
			
		||||
    const math::vec2 &size,
 | 
			
		||||
    Ref<Texture> texture
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
	draw_quad(
 | 
			
		||||
	    math::translate(position) * math::scale(math::vec3 { size.x, size.y, 1.0f }),
 | 
			
		||||
	    texture
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
//======================================== DRAW_QUAD ========================================//
 | 
			
		||||
 | 
			
		||||
//==================== DRAW_QUAD_TINT ====================//
 | 
			
		||||
void Renderer::draw_quad_impl(const math::mat4 &transform, const math::vec4 &tint)
 | 
			
		||||
{
 | 
			
		||||
	auto map = std::span<QuadRendererProgram::QuadVertexData> { m_quad_renderer->get_map_current(),
 | 
			
		||||
		                                                        4 };
 | 
			
		||||
 | 
			
		||||
	// top left
 | 
			
		||||
	map[0].position = transform * math::vec4(-0.5f, -0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[0].tint = tint;
 | 
			
		||||
 | 
			
		||||
	// top right
 | 
			
		||||
	map[1].position = transform * math::vec4(0.5f, -0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[1].tint = tint;
 | 
			
		||||
 | 
			
		||||
	// bottom right
 | 
			
		||||
	map[2].position = transform * math::vec4(0.5f, 0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[2].tint = tint;
 | 
			
		||||
 | 
			
		||||
	// bottom left
 | 
			
		||||
	map[3].position = transform * math::vec4(-0.5f, 0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[3].tint = tint;
 | 
			
		||||
 | 
			
		||||
	// advance
 | 
			
		||||
	if (!m_quad_renderer->advance())
 | 
			
		||||
	{
 | 
			
		||||
		log_wrn("Exceeded LT_MAX_QUAD_RENDERER_VERTICES: {}", LT_MAX_QUAD_RENDERER_VERTICES);
 | 
			
		||||
		flush_scene();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::draw_quad_impl(const math::mat4 &transform, const Ref<Texture> &texture)
 | 
			
		||||
{
 | 
			
		||||
	ensure(texture, "Texture passed to renderer::draw_quad_impl");
 | 
			
		||||
 | 
			
		||||
	texture->bind();
 | 
			
		||||
	auto map = std::span<TextureRendererProgram::TextureVertexData> {
 | 
			
		||||
		m_texture_renderer->get_map_current(),
 | 
			
		||||
		4
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// top left
 | 
			
		||||
	map[0].position = transform * math::vec4(-0.5f, -0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[0].texcoord = { 0.0f, 0.0f };
 | 
			
		||||
 | 
			
		||||
	// top right
 | 
			
		||||
	map[1].position = transform * math::vec4(0.5f, -0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[1].texcoord = { 1.0f, 0.0f };
 | 
			
		||||
 | 
			
		||||
	// bottom right
 | 
			
		||||
	map[2].position = transform * math::vec4(0.5f, 0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[2].texcoord = { 1.0f, 1.0f };
 | 
			
		||||
 | 
			
		||||
	// bottom left
 | 
			
		||||
	map[3].position = transform * math::vec4(-0.5f, 0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[3].texcoord = { 0.0f, 1.0f };
 | 
			
		||||
 | 
			
		||||
	// advance
 | 
			
		||||
	if (!m_texture_renderer->advance())
 | 
			
		||||
	{
 | 
			
		||||
		log_wrn("Exceeded LT_MAX_TEXTURE_RENDERER_VERTICES: {}", LT_MAX_TEXTURE_RENDERER_VERTICES);
 | 
			
		||||
		flush_scene();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::draw_quad_impl(
 | 
			
		||||
    const math::mat4 &transform,
 | 
			
		||||
    const math::vec4 &tint,
 | 
			
		||||
    const Ref<Texture> &texture
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
	ensure(texture, "Texture passed to renderer::draw_quad_impl");
 | 
			
		||||
 | 
			
		||||
	texture->bind();
 | 
			
		||||
	auto map = std::span<TintedTextureRendererProgram::TintedTextureVertexData> {
 | 
			
		||||
		m_tinted_texture_renderer->get_map_current(),
 | 
			
		||||
		4
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// top left
 | 
			
		||||
	map[0].position = transform * math::vec4(-0.5f, -0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[0].tint = tint;
 | 
			
		||||
	map[0].texcoord = { 0.0f, 0.0f };
 | 
			
		||||
 | 
			
		||||
	// top right
 | 
			
		||||
	map[1].position = transform * math::vec4(0.5f, -0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[1].tint = tint;
 | 
			
		||||
	map[1].texcoord = { 1.0f, 0.0f };
 | 
			
		||||
 | 
			
		||||
	// bottom right
 | 
			
		||||
	map[2].position = transform * math::vec4(0.5f, 0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[2].tint = tint;
 | 
			
		||||
	map[2].texcoord = { 1.0f, 1.0f };
 | 
			
		||||
 | 
			
		||||
	// bottom left
 | 
			
		||||
	map[3].position = transform * math::vec4(-0.5f, 0.5f, 0.0f, 1.0f);
 | 
			
		||||
	map[3].tint = tint;
 | 
			
		||||
	map[3].texcoord = { 0.0f, 1.0f };
 | 
			
		||||
 | 
			
		||||
	if (!m_tinted_texture_renderer->advance())
 | 
			
		||||
	{
 | 
			
		||||
		log_wrn("Exceeded LT_MAX_TEXTURE_RENDERER_VERTICES: {}", LT_MAX_TEXTURE_RENDERER_VERTICES);
 | 
			
		||||
		flush_scene();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::begin_frame()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::end_frame()
 | 
			
		||||
{
 | 
			
		||||
	m_render_command->swap_buffers();
 | 
			
		||||
	m_render_command->clear_back_buffer(
 | 
			
		||||
	    m_default_framebuffer_camera ? m_default_framebuffer_camera->get_background_color() :
 | 
			
		||||
	                                   math::vec4(0.0f)
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	m_default_framebuffer_camera = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::begin_scene_impl(
 | 
			
		||||
    Camera *camera,
 | 
			
		||||
    const math::mat4 &cameraTransform,
 | 
			
		||||
    const Ref<Framebuffer> &targetFrameBuffer /* = nullptr */
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
	// determine the target frame buffer
 | 
			
		||||
	m_target_framebuffer = targetFrameBuffer;
 | 
			
		||||
 | 
			
		||||
	if (targetFrameBuffer)
 | 
			
		||||
	{
 | 
			
		||||
		targetFrameBuffer->bind_as_target(camera->get_background_color());
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		m_default_framebuffer_camera = camera;
 | 
			
		||||
		m_render_command->default_target_framebuffer();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// update view projection buffer
 | 
			
		||||
	auto *map = (math::mat4 *)m_view_projection_buffer->map();
 | 
			
		||||
	map[0] = camera->get_projection() * math::inverse(cameraTransform);
 | 
			
		||||
	m_view_projection_buffer->un_map();
 | 
			
		||||
 | 
			
		||||
	// map renderers
 | 
			
		||||
	m_quad_renderer->map();
 | 
			
		||||
	m_texture_renderer->map();
 | 
			
		||||
	m_tinted_texture_renderer->map();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::flush_scene()
 | 
			
		||||
{
 | 
			
		||||
	/* tinted texture renderer */
 | 
			
		||||
	m_tinted_texture_renderer->un_map();
 | 
			
		||||
	if (m_tinted_texture_renderer->get_quad_count())
 | 
			
		||||
	{
 | 
			
		||||
		m_tinted_texture_renderer->bind();
 | 
			
		||||
		m_render_command->draw_indexed(m_tinted_texture_renderer->get_quad_count() * 6u);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* quad renderer */
 | 
			
		||||
	m_quad_renderer->un_map();
 | 
			
		||||
	if (m_quad_renderer->get_quad_count())
 | 
			
		||||
	{
 | 
			
		||||
		m_quad_renderer->bind();
 | 
			
		||||
		m_render_command->draw_indexed(m_quad_renderer->get_quad_count() * 6u);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* texture renderer */
 | 
			
		||||
	m_texture_renderer->un_map();
 | 
			
		||||
	if (m_texture_renderer->get_quad_count())
 | 
			
		||||
	{
 | 
			
		||||
		m_texture_renderer->bind();
 | 
			
		||||
		m_render_command->draw_indexed(m_texture_renderer->get_quad_count() * 6u);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_quad_renderer->map();
 | 
			
		||||
	m_texture_renderer->map();
 | 
			
		||||
	m_tinted_texture_renderer->map();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Renderer::end_scene_impl()
 | 
			
		||||
{
 | 
			
		||||
	/* tinted texture renderer */
 | 
			
		||||
	m_tinted_texture_renderer->un_map();
 | 
			
		||||
	if (m_tinted_texture_renderer->get_quad_count())
 | 
			
		||||
	{
 | 
			
		||||
		m_tinted_texture_renderer->bind();
 | 
			
		||||
		m_render_command->draw_indexed(m_tinted_texture_renderer->get_quad_count() * 6u);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* quad renderer */
 | 
			
		||||
	m_quad_renderer->un_map();
 | 
			
		||||
	if (m_quad_renderer->get_quad_count())
 | 
			
		||||
	{
 | 
			
		||||
		m_quad_renderer->bind();
 | 
			
		||||
		m_render_command->draw_indexed(m_quad_renderer->get_quad_count() * 6u);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* texture renderer */
 | 
			
		||||
	m_texture_renderer->un_map();
 | 
			
		||||
	if (m_texture_renderer->get_quad_count())
 | 
			
		||||
	{
 | 
			
		||||
		m_texture_renderer->bind();
 | 
			
		||||
		m_render_command->draw_indexed(m_texture_renderer->get_quad_count() * 6u);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// reset frame buffer
 | 
			
		||||
	if (m_target_framebuffer)
 | 
			
		||||
	{
 | 
			
		||||
		m_target_framebuffer = nullptr;
 | 
			
		||||
		m_render_command->default_target_framebuffer();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										47
									
								
								modules/renderer/legacy/private/shader.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								modules/renderer/legacy/private/shader.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
#include <asset_parser/assets/text.hpp>
 | 
			
		||||
#include <renderer/gl/shader.hpp>
 | 
			
		||||
#include <renderer/shader.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef LIGHT_PLATFORM_WINDOWS
 | 
			
		||||
	#include <renderer/dx/shader.hpp>
 | 
			
		||||
	#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
/* static */ auto Shader::create(
 | 
			
		||||
    const Ref<Assets::TextAsset> &vertex_asset,
 | 
			
		||||
    const Ref<Assets::TextAsset> &pixel_asset,
 | 
			
		||||
    const Ref<SharedContext> &shared_context
 | 
			
		||||
) -> Ref<Shader>
 | 
			
		||||
{
 | 
			
		||||
	std::ignore = shared_context;
 | 
			
		||||
 | 
			
		||||
	// load shader source
 | 
			
		||||
	switch (GraphicsContext::get_graphics_api())
 | 
			
		||||
	{
 | 
			
		||||
	case GraphicsAPI::OpenGL:
 | 
			
		||||
		return create_ref<glShader>(std::move(vertex_asset), std::move(pixel_asset));
 | 
			
		||||
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    return create_ref<dxShader>(
 | 
			
		||||
		               vertex_asset,
 | 
			
		||||
		               pixel_asset,
 | 
			
		||||
		               std::static_pointer_cast<dxSharedContext>(sharedContext)
 | 
			
		||||
		    );
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		ensure(
 | 
			
		||||
		    false,
 | 
			
		||||
		    "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		    static_cast<uint32_t>(GraphicsContext::get_graphics_api())
 | 
			
		||||
		);
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										17
									
								
								modules/renderer/legacy/private/system.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								modules/renderer/legacy/private/system.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
#include <lt_debug/assertions.hpp>
 | 
			
		||||
#include <renderer/system.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer {
 | 
			
		||||
 | 
			
		||||
System::System(InitRequirements requirements): m_registry(std::move(requirements.registry))
 | 
			
		||||
{
 | 
			
		||||
	ensure(m_registry, "null registry");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
System::~System() = default;
 | 
			
		||||
 | 
			
		||||
void System::tick(TickRequirements requirements)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer
 | 
			
		||||
							
								
								
									
										44
									
								
								modules/renderer/legacy/private/system.test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								modules/renderer/legacy/private/system.test.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
#include <ranges>
 | 
			
		||||
#include <renderer/system.hpp>
 | 
			
		||||
#include <test/test.hpp>
 | 
			
		||||
#include <window/window.hpp>
 | 
			
		||||
 | 
			
		||||
using namespace lt;
 | 
			
		||||
 | 
			
		||||
using lt::test::Case;
 | 
			
		||||
using lt::test::Suite;
 | 
			
		||||
 | 
			
		||||
Suite raii = [] {
 | 
			
		||||
	using lt::test::expect_true;
 | 
			
		||||
	using lt::test::expect_throw;
 | 
			
		||||
	using renderer::System;
 | 
			
		||||
 | 
			
		||||
	Case { "happy" } = [=] {
 | 
			
		||||
		std::ignore = System { {
 | 
			
		||||
			.registry = create_ref<ecs::Registry>(),
 | 
			
		||||
		} };
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "unhappy" } = [=] {
 | 
			
		||||
		expect_throw([=] {
 | 
			
		||||
			std::ignore = System { {
 | 
			
		||||
				.registry = {},
 | 
			
		||||
			} };
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		expect_throw([=] {
 | 
			
		||||
			std::ignore = System { {
 | 
			
		||||
				.registry = create_ref<ecs::Registry>(),
 | 
			
		||||
			} };
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "plenty" } = [=] {
 | 
			
		||||
		for (auto idx : std::views::iota(0, 100'001))
 | 
			
		||||
		{
 | 
			
		||||
			std::ignore = System { {
 | 
			
		||||
				.registry = create_ref<ecs::Registry>(),
 | 
			
		||||
			} };
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										45
									
								
								modules/renderer/legacy/private/texture.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								modules/renderer/legacy/private/texture.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
#include <logger/logger.hpp>
 | 
			
		||||
#include <renderer/gl/texture.hpp>
 | 
			
		||||
#include <renderer/texture.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef LIGHT_PLATFORM_WINDOWS
 | 
			
		||||
	#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
	#include <renderer/dx/texture.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
/* static */ auto Texture::create(
 | 
			
		||||
    const Ref<Assets::TextureAsset> &asset,
 | 
			
		||||
    const Ref<SharedContext> & /*shared_context*/
 | 
			
		||||
) -> Ref<Texture>
 | 
			
		||||
{
 | 
			
		||||
	switch (GraphicsContext::get_graphics_api())
 | 
			
		||||
	{
 | 
			
		||||
	case GraphicsAPI::OpenGL: return create_ref<glTexture>(std::move(asset));
 | 
			
		||||
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    return create_ref<dxTexture>(
 | 
			
		||||
		               width,
 | 
			
		||||
		               height,
 | 
			
		||||
		               components,
 | 
			
		||||
		               pixels,
 | 
			
		||||
		               std::static_pointer_cast<dxSharedContext>(sharedContext),
 | 
			
		||||
		               filePath
 | 
			
		||||
		    );
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		    default
 | 
			
		||||
		    : ensure(
 | 
			
		||||
		          false,
 | 
			
		||||
		          "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		          static_cast<uint32_t>(GraphicsContext::get_graphics_api())
 | 
			
		||||
		      );
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										44
									
								
								modules/renderer/legacy/private/vertex_layout.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								modules/renderer/legacy/private/vertex_layout.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
#include <lt_debug/assertions.hpp>
 | 
			
		||||
#include <renderer/gl/vertex_layout.hpp>
 | 
			
		||||
#include <renderer/vertex_layout.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef LIGHT_PLATFORM_WINDOWS
 | 
			
		||||
	#include <renderer/dx/shared_context.hpp>
 | 
			
		||||
	#include <renderer/dx/vertex_layout.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <renderer/graphics_context.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
auto VertexLayout::create(
 | 
			
		||||
    const Ref<VertexBuffer> &vertexBuffer,
 | 
			
		||||
    const Ref<Shader> & /*shader*/,
 | 
			
		||||
    const std::vector<std::pair<std::string, VertexElementType>> &elements,
 | 
			
		||||
    const Ref<SharedContext> & /*sharedContext*/
 | 
			
		||||
) -> Ref<VertexLayout>
 | 
			
		||||
{
 | 
			
		||||
	switch (GraphicsContext::get_graphics_api())
 | 
			
		||||
	{
 | 
			
		||||
	case GraphicsAPI::OpenGL: return create_ref<glVertexLayout>(vertexBuffer, elements);
 | 
			
		||||
 | 
			
		||||
	case GraphicsAPI::DirectX:
 | 
			
		||||
		lt_win(
 | 
			
		||||
		    return create_ref<dxVertexLayout>(
 | 
			
		||||
		               shader,
 | 
			
		||||
		               elements,
 | 
			
		||||
		               std::static_pointer_cast<dxSharedContext>(sharedContext)
 | 
			
		||||
		    );
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		    default
 | 
			
		||||
		    : ensure(
 | 
			
		||||
		          false,
 | 
			
		||||
		          "Invalid/unsupported 'GraphicsAPI' {}",
 | 
			
		||||
		          static_cast<uint32_t>(GraphicsContext::get_graphics_api())
 | 
			
		||||
		      );
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										5
									
								
								modules/renderer/legacy/private/vk/instance.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								modules/renderer/legacy/private/vk/instance.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
#include <renderer/vk/instance.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::vk {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								modules/renderer/legacy/private/vk/instance.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								modules/renderer/legacy/private/vk/instance.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vulkan/vulkan.h>
 | 
			
		||||
#include <vulkan/vulkan.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::vk {
 | 
			
		||||
 | 
			
		||||
class Instance
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	Instance()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	::vk::Instance m_instace;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt::vk
 | 
			
		||||
							
								
								
									
										8
									
								
								modules/renderer/legacy/private/vk/instance.test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								modules/renderer/legacy/private/vk/instance.test.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
#include <renderer/vk/instance.hpp>
 | 
			
		||||
#include <test/test.hpp>
 | 
			
		||||
 | 
			
		||||
lt::test::Suite raii = [] {
 | 
			
		||||
	lt::test::Case { "raii" } = [] {
 | 
			
		||||
		auto instance = lt::vk::Instance {};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										29
									
								
								modules/renderer/legacy/public/backend.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								modules/renderer/legacy/public/backend.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer {
 | 
			
		||||
 | 
			
		||||
class Backend
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	enum class API : uint8_t
 | 
			
		||||
	{
 | 
			
		||||
		vulkan,
 | 
			
		||||
		directx,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Backend() = default;
 | 
			
		||||
 | 
			
		||||
	Backend(Backend &&) = default;
 | 
			
		||||
 | 
			
		||||
	auto operator=(Backend &&) -> Backend & = default;
 | 
			
		||||
 | 
			
		||||
	Backend(const Backend &) = delete;
 | 
			
		||||
 | 
			
		||||
	auto operator=(const Backend &) -> Backend & = delete;
 | 
			
		||||
 | 
			
		||||
	virtual ~Backend() = default;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] virtual auto get_api() const -> API = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer
 | 
			
		||||
							
								
								
									
										50
									
								
								modules/renderer/legacy/public/blender.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								modules/renderer/legacy/public/blender.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class SharedContext;
 | 
			
		||||
 | 
			
		||||
enum class BlendFactor : uint8_t
 | 
			
		||||
{
 | 
			
		||||
	// constants
 | 
			
		||||
	ZERO,
 | 
			
		||||
	ONE,
 | 
			
		||||
 | 
			
		||||
	// source
 | 
			
		||||
	SRC_COLOR,
 | 
			
		||||
	INVERSE_SRC_COLOR,
 | 
			
		||||
 | 
			
		||||
	SRC_ALPHA,
 | 
			
		||||
	INVERSE_SRC_ALPHA,
 | 
			
		||||
 | 
			
		||||
	// destination
 | 
			
		||||
	DST_COLOR,
 | 
			
		||||
	INVERSE_DST_COLOR,
 | 
			
		||||
 | 
			
		||||
	DST_ALPHA,
 | 
			
		||||
	INVERSE_DST_ALPHA,
 | 
			
		||||
 | 
			
		||||
	// source1
 | 
			
		||||
	SRC1_COLOR,
 | 
			
		||||
	INVERSE_SRC1_COLOR,
 | 
			
		||||
 | 
			
		||||
	SRC1_ALPHA,
 | 
			
		||||
	INVERSE_SRC1_ALPHA,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Blender
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	virtual ~Blender() = default;
 | 
			
		||||
	static auto create(const Ref<SharedContext> &sharedContext) -> Scope<Blender>;
 | 
			
		||||
 | 
			
		||||
	virtual void enable(BlendFactor srcFactor, BlendFactor dstFactor) = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void disable() = 0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	Blender() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										76
									
								
								modules/renderer/legacy/public/buffers.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								modules/renderer/legacy/public/buffers.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,76 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class SharedContext;
 | 
			
		||||
 | 
			
		||||
enum class ConstantBufferIndex
 | 
			
		||||
{
 | 
			
		||||
	ViewProjection = 0u
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ConstantBuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	virtual ~ConstantBuffer() = default;
 | 
			
		||||
	static auto create(
 | 
			
		||||
	    ConstantBufferIndex index,
 | 
			
		||||
	    unsigned int size,
 | 
			
		||||
	    const Ref<SharedContext> &sharedContext
 | 
			
		||||
	) -> Scope<ConstantBuffer>;
 | 
			
		||||
 | 
			
		||||
	virtual auto map() -> void * = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void un_map() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void bind() = 0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	ConstantBuffer() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class VertexBuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	static auto create(
 | 
			
		||||
	    float *vertices,
 | 
			
		||||
	    unsigned int stride,
 | 
			
		||||
	    unsigned int count,
 | 
			
		||||
	    const Ref<SharedContext> &sharedContext
 | 
			
		||||
	) -> Ref<VertexBuffer>;
 | 
			
		||||
 | 
			
		||||
	virtual ~VertexBuffer() = default;
 | 
			
		||||
 | 
			
		||||
	virtual auto map() -> void * = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void un_map() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void bind() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void un_bind() = 0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	VertexBuffer() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class IndexBuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	static auto create(
 | 
			
		||||
	    unsigned int *indices,
 | 
			
		||||
	    unsigned int count,
 | 
			
		||||
	    const Ref<SharedContext> &sharedContext
 | 
			
		||||
	) -> Ref<IndexBuffer>;
 | 
			
		||||
 | 
			
		||||
	virtual ~IndexBuffer() = default;
 | 
			
		||||
 | 
			
		||||
	virtual void bind() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void un_bind() = 0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	IndexBuffer() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										12
									
								
								modules/renderer/legacy/public/components.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								modules/renderer/legacy/public/components.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer {
 | 
			
		||||
 | 
			
		||||
/** Requires a Transform Component
 | 
			
		||||
 * @todo(Light): Figure out how to enforce a component requirement list for a component
 | 
			
		||||
 */
 | 
			
		||||
struct RendererComponent
 | 
			
		||||
{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer
 | 
			
		||||
							
								
								
									
										9
									
								
								modules/renderer/legacy/public/context.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								modules/renderer/legacy/public/context.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer {
 | 
			
		||||
 | 
			
		||||
class Context
 | 
			
		||||
{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer
 | 
			
		||||
							
								
								
									
										40
									
								
								modules/renderer/legacy/public/framebuffer.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								modules/renderer/legacy/public/framebuffer.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <math/vec2.hpp>
 | 
			
		||||
#include <math/vec4.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class SharedContext;
 | 
			
		||||
 | 
			
		||||
struct FramebufferSpecification
 | 
			
		||||
{
 | 
			
		||||
	unsigned int width {};
 | 
			
		||||
 | 
			
		||||
	unsigned int height {};
 | 
			
		||||
 | 
			
		||||
	unsigned int samples = 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Framebuffer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	virtual ~Framebuffer() = default;
 | 
			
		||||
	static auto create(
 | 
			
		||||
	    const FramebufferSpecification &specification,
 | 
			
		||||
	    const Ref<SharedContext> &sharedContext
 | 
			
		||||
	) -> Ref<Framebuffer>;
 | 
			
		||||
 | 
			
		||||
	virtual void bind_as_target(const math::vec4 &clearColor) = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void bind_as_resource() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void resize(const math::uvec2 &size) = 0;
 | 
			
		||||
 | 
			
		||||
	virtual auto get_color_attachment() -> void * = 0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	Framebuffer() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										50
									
								
								modules/renderer/legacy/public/graphics_context.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								modules/renderer/legacy/public/graphics_context.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class SharedContext;
 | 
			
		||||
class WindowResizedEvent;
 | 
			
		||||
 | 
			
		||||
enum class GraphicsAPI
 | 
			
		||||
{
 | 
			
		||||
	Default = 0,
 | 
			
		||||
	OpenGL,
 | 
			
		||||
	DirectX,
 | 
			
		||||
	Vulkan,
 | 
			
		||||
	Metal
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GraphicsContext
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	static auto create(GraphicsAPI api) -> Scope<GraphicsContext>;
 | 
			
		||||
	GraphicsContext(const GraphicsContext &) = delete;
 | 
			
		||||
 | 
			
		||||
	GraphicsContext &operator=(const GraphicsContext &) = delete;
 | 
			
		||||
 | 
			
		||||
	virtual ~GraphicsContext();
 | 
			
		||||
 | 
			
		||||
	virtual void log_debug_data() = 0;
 | 
			
		||||
 | 
			
		||||
	static GraphicsAPI get_graphics_api()
 | 
			
		||||
	{
 | 
			
		||||
		return s_context->m_graphics_api;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static Ref<SharedContext> get_shared_context()
 | 
			
		||||
	{
 | 
			
		||||
		return s_context->m_shared_context;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	GraphicsContext() = default;
 | 
			
		||||
 | 
			
		||||
	GraphicsAPI m_graphics_api;
 | 
			
		||||
 | 
			
		||||
	Ref<SharedContext> m_shared_context = nullptr;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	static GraphicsContext *s_context;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										41
									
								
								modules/renderer/legacy/public/render_command.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								modules/renderer/legacy/public/render_command.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <math/vec4.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class SharedContext;
 | 
			
		||||
 | 
			
		||||
class RenderCommand
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	static auto create(const Ref<SharedContext> &sharedContext) -> Scope<RenderCommand>;
 | 
			
		||||
 | 
			
		||||
	RenderCommand(const RenderCommand &) = delete;
 | 
			
		||||
 | 
			
		||||
	auto operator=(const RenderCommand &) -> RenderCommand & = delete;
 | 
			
		||||
 | 
			
		||||
	virtual ~RenderCommand() = default;
 | 
			
		||||
 | 
			
		||||
	virtual void swap_buffers() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void clear_back_buffer(const math::vec4 &clearColor) = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void draw(unsigned int count) = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void draw_indexed(unsigned int count) = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void default_target_framebuffer() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void set_viewport(
 | 
			
		||||
	    unsigned int x,
 | 
			
		||||
	    unsigned int y,
 | 
			
		||||
	    unsigned int width,
 | 
			
		||||
	    unsigned int height
 | 
			
		||||
	) = 0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	RenderCommand() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										165
									
								
								modules/renderer/legacy/public/renderer.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								modules/renderer/legacy/public/renderer.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,165 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <math/mat4.hpp>
 | 
			
		||||
#include <math/vec3.hpp>
 | 
			
		||||
#include <math/vec4.hpp>
 | 
			
		||||
#include <renderer/blender.hpp>
 | 
			
		||||
#include <renderer/buffers.hpp>
 | 
			
		||||
#include <renderer/render_command.hpp>
 | 
			
		||||
#include <renderer/renderer.hpp>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#define LT_MAX_QUAD_RENDERER_VERTICES           (1028u * 4u)
 | 
			
		||||
#define LT_MAX_TEXTURE_RENDERER_VERTICES        (1028u * 4u)
 | 
			
		||||
#define LT_MAX_TINTED_TEXTURE_RENDERER_VERTICES (1028u * 4u)
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class ConstantBuffer;
 | 
			
		||||
class Framebuffer;
 | 
			
		||||
class RenderCommand;
 | 
			
		||||
class Texture;
 | 
			
		||||
class SharedContext;
 | 
			
		||||
class Camera;
 | 
			
		||||
class WindowResizedEvent;
 | 
			
		||||
class Shader;
 | 
			
		||||
 | 
			
		||||
class TintedTextureRendererProgram;
 | 
			
		||||
class QuadRendererProgram;
 | 
			
		||||
class TextureRendererProgram;
 | 
			
		||||
 | 
			
		||||
class Renderer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	struct CreateInfo
 | 
			
		||||
	{
 | 
			
		||||
		Ref<Shader> quad_renderer_shader;
 | 
			
		||||
 | 
			
		||||
		Ref<Shader> texture_renderer_shader;
 | 
			
		||||
 | 
			
		||||
		Ref<Shader> tinted_texture_renderer_shader;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static auto create(Ref<SharedContext> sharedContext, CreateInfo create_info) -> Scope<Renderer>;
 | 
			
		||||
 | 
			
		||||
	static void draw_quad(
 | 
			
		||||
	    const math::vec3 &position,
 | 
			
		||||
	    const math::vec2 &size,
 | 
			
		||||
	    const math::vec4 &tint,
 | 
			
		||||
	    Ref<Texture> texture
 | 
			
		||||
	)
 | 
			
		||||
	{
 | 
			
		||||
		s_context->draw_quad_impl(position, size, tint, std::move(texture));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void draw_quad(
 | 
			
		||||
	    const math::vec3 &position,
 | 
			
		||||
	    const math::vec2 &size,
 | 
			
		||||
	    const math::vec4 &tint
 | 
			
		||||
	)
 | 
			
		||||
	{
 | 
			
		||||
		s_context->draw_quad_impl(position, size, tint);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void draw_quad(const math::vec3 &position, const math::vec2 &size, Ref<Texture> texture)
 | 
			
		||||
	{
 | 
			
		||||
		s_context->draw_quad_impl(position, size, std::move(texture));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void draw_quad(
 | 
			
		||||
	    const math::mat4 &transform,
 | 
			
		||||
	    const math::vec4 &tint,
 | 
			
		||||
	    const Ref<Texture> &texture
 | 
			
		||||
	)
 | 
			
		||||
	{
 | 
			
		||||
		s_context->draw_quad_impl(transform, tint, texture);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void draw_quad(const math::mat4 &transform, const math::vec4 &tint)
 | 
			
		||||
	{
 | 
			
		||||
		s_context->draw_quad_impl(transform, tint);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void draw_quad(const math::mat4 &transform, const Ref<Texture> &texture)
 | 
			
		||||
	{
 | 
			
		||||
		s_context->draw_quad_impl(transform, texture);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void begin_scene(
 | 
			
		||||
	    Camera *camera,
 | 
			
		||||
	    const math::mat4 &cameraTransform,
 | 
			
		||||
	    const Ref<Framebuffer> &targetFrameBuffer = nullptr
 | 
			
		||||
	)
 | 
			
		||||
	{
 | 
			
		||||
		s_context->begin_scene_impl(camera, cameraTransform, targetFrameBuffer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void end_scene()
 | 
			
		||||
	{
 | 
			
		||||
		s_context->end_scene_impl();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	~Renderer();
 | 
			
		||||
 | 
			
		||||
	void on_window_resize(const WindowResizedEvent &event);
 | 
			
		||||
 | 
			
		||||
	void begin_frame();
 | 
			
		||||
 | 
			
		||||
	void end_frame();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	static Renderer *s_context;
 | 
			
		||||
 | 
			
		||||
	Scope<QuadRendererProgram> m_quad_renderer;
 | 
			
		||||
 | 
			
		||||
	Scope<TextureRendererProgram> m_texture_renderer;
 | 
			
		||||
 | 
			
		||||
	Scope<TintedTextureRendererProgram> m_tinted_texture_renderer;
 | 
			
		||||
 | 
			
		||||
	Scope<ConstantBuffer> m_view_projection_buffer;
 | 
			
		||||
 | 
			
		||||
	Scope<RenderCommand> m_render_command;
 | 
			
		||||
 | 
			
		||||
	Scope<Blender> m_blender;
 | 
			
		||||
 | 
			
		||||
	Camera *m_default_framebuffer_camera { nullptr };
 | 
			
		||||
 | 
			
		||||
	Ref<Framebuffer> m_target_framebuffer;
 | 
			
		||||
 | 
			
		||||
	bool m_should_clear_backbuffer { false };
 | 
			
		||||
 | 
			
		||||
	Renderer(const Ref<SharedContext> &shared_context, CreateInfo create_info);
 | 
			
		||||
 | 
			
		||||
	void draw_quad_impl(
 | 
			
		||||
	    const math::vec3 &position,
 | 
			
		||||
	    const math::vec2 &size,
 | 
			
		||||
	    const math::vec4 &tint,
 | 
			
		||||
	    Ref<Texture> texture
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	void draw_quad_impl(const math::vec3 &position, const math::vec2 &size, const math::vec4 &tint);
 | 
			
		||||
 | 
			
		||||
	void draw_quad_impl(const math::vec3 &position, const math::vec2 &size, Ref<Texture> texture);
 | 
			
		||||
 | 
			
		||||
	void draw_quad_impl(
 | 
			
		||||
	    const math::mat4 &transform,
 | 
			
		||||
	    const math::vec4 &tint,
 | 
			
		||||
	    const Ref<Texture> &texture
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	void draw_quad_impl(const math::mat4 &transform, const math::vec4 &tint);
 | 
			
		||||
 | 
			
		||||
	void draw_quad_impl(const math::mat4 &transform, const Ref<Texture> &texture);
 | 
			
		||||
 | 
			
		||||
	void begin_scene_impl(
 | 
			
		||||
	    Camera *camera,
 | 
			
		||||
	    const math::mat4 &cameraTransform,
 | 
			
		||||
	    const Ref<Framebuffer> &targetFrameBuffer = nullptr
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	void flush_scene();
 | 
			
		||||
 | 
			
		||||
	void end_scene_impl();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										41
									
								
								modules/renderer/legacy/public/shader.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								modules/renderer/legacy/public/shader.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace Assets {
 | 
			
		||||
 | 
			
		||||
class TextAsset;
 | 
			
		||||
 | 
			
		||||
} // namespace Assets
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class SharedContext;
 | 
			
		||||
 | 
			
		||||
class Shader
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	enum Stage : uint8_t
 | 
			
		||||
	{
 | 
			
		||||
		none = 0,
 | 
			
		||||
 | 
			
		||||
		vertex,
 | 
			
		||||
		pixel,
 | 
			
		||||
		geometry,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static auto create(
 | 
			
		||||
	    const Ref<Assets::TextAsset> &vertex_asset,
 | 
			
		||||
	    const Ref<Assets::TextAsset> &pixel_asset,
 | 
			
		||||
	    const Ref<SharedContext> &shared_context
 | 
			
		||||
	) -> Ref<Shader>;
 | 
			
		||||
 | 
			
		||||
	virtual ~Shader() = default;
 | 
			
		||||
 | 
			
		||||
	virtual void bind() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void un_bind() = 0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	Shader() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										12
									
								
								modules/renderer/legacy/public/shared_context.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								modules/renderer/legacy/public/shared_context.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class SharedContext
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	virtual ~SharedContext() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										56
									
								
								modules/renderer/legacy/public/system.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								modules/renderer/legacy/public/system.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <base/base.hpp>
 | 
			
		||||
#include <ecs/registry.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer {
 | 
			
		||||
 | 
			
		||||
/** The system for putting gore on your display
 | 
			
		||||
 *
 | 
			
		||||
 * Exclusively operates on components:
 | 
			
		||||
 * - RendererComponent
 | 
			
		||||
 * - PostEffectsComponent
 | 
			
		||||
 * - UserInterfaceComponent
 | 
			
		||||
 *
 | 
			
		||||
 * Requires read acces on components:
 | 
			
		||||
 * - TransformComponent
 | 
			
		||||
 */
 | 
			
		||||
class System
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	/** The configurations of this system. */
 | 
			
		||||
	struct Properties
 | 
			
		||||
	{
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/** The requirements for this system to initialize. */
 | 
			
		||||
	struct InitRequirements
 | 
			
		||||
	{
 | 
			
		||||
		Ref<ecs::Registry> registry;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/** The requirements for this system to tick. */
 | 
			
		||||
	struct TickRequirements
 | 
			
		||||
	{
 | 
			
		||||
		double delta_time;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] System(InitRequirements requirements);
 | 
			
		||||
 | 
			
		||||
	System(System &&) = default;
 | 
			
		||||
 | 
			
		||||
	System(const System &) = delete;
 | 
			
		||||
 | 
			
		||||
	auto operator=(System &&) -> System & = default;
 | 
			
		||||
 | 
			
		||||
	auto operator=(const System &) -> System & = delete;
 | 
			
		||||
 | 
			
		||||
	~System();
 | 
			
		||||
 | 
			
		||||
	void tick(TickRequirements requirements);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<ecs::Registry> m_registry;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer
 | 
			
		||||
							
								
								
									
										38
									
								
								modules/renderer/legacy/public/texture.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								modules/renderer/legacy/public/texture.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace Assets {
 | 
			
		||||
 | 
			
		||||
class TextureAsset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class SharedContext;
 | 
			
		||||
 | 
			
		||||
class Texture
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	static Ref<Texture> create(
 | 
			
		||||
	    const Ref<Assets::TextureAsset> &asset,
 | 
			
		||||
	    const Ref<SharedContext> &shared_context
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	virtual ~Texture() = default;
 | 
			
		||||
 | 
			
		||||
	Texture(Texture &&) = default;
 | 
			
		||||
 | 
			
		||||
	auto operator=(Texture &&) -> Texture & = default;
 | 
			
		||||
 | 
			
		||||
	Texture(const Texture &) = delete;
 | 
			
		||||
 | 
			
		||||
	auto operator=(const Texture &) -> Texture & = delete;
 | 
			
		||||
 | 
			
		||||
	virtual void bind(unsigned int slot = 0) = 0;
 | 
			
		||||
 | 
			
		||||
	virtual auto get_texture() -> void * = 0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	Texture() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
							
								
								
									
										53
									
								
								modules/renderer/legacy/public/vertex_layout.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								modules/renderer/legacy/public/vertex_layout.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <lt_debug/assertions.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class VertexBuffer;
 | 
			
		||||
class Shader;
 | 
			
		||||
class SharedContext;
 | 
			
		||||
 | 
			
		||||
enum class VertexElementType
 | 
			
		||||
{
 | 
			
		||||
	Byte1,
 | 
			
		||||
	Byte2,
 | 
			
		||||
	Byte4,
 | 
			
		||||
	UByte1,
 | 
			
		||||
	UByte2,
 | 
			
		||||
	UByte4,
 | 
			
		||||
	Int1,
 | 
			
		||||
	Int2,
 | 
			
		||||
	Int3,
 | 
			
		||||
	Int4,
 | 
			
		||||
	UInt1,
 | 
			
		||||
	UInt2,
 | 
			
		||||
	UInt3,
 | 
			
		||||
	UInt4,
 | 
			
		||||
	Float1,
 | 
			
		||||
	Float2,
 | 
			
		||||
	Float3,
 | 
			
		||||
	Float4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class VertexLayout
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	static auto create(
 | 
			
		||||
	    const Ref<VertexBuffer> &vertexBuffer,
 | 
			
		||||
	    const Ref<Shader> &shader,
 | 
			
		||||
	    const std::vector<std::pair<std::string, VertexElementType>> &elements,
 | 
			
		||||
	    const Ref<SharedContext> &sharedContext
 | 
			
		||||
	) -> Ref<VertexLayout>;
 | 
			
		||||
 | 
			
		||||
	virtual ~VertexLayout() = default;
 | 
			
		||||
 | 
			
		||||
	virtual void bind() = 0;
 | 
			
		||||
 | 
			
		||||
	virtual void un_bind() = 0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	VertexLayout() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
| 
						 | 
				
			
			@ -69,73 +69,29 @@ 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 = "raii"_suite = [] {
 | 
			
		||||
Suite raii = [] {
 | 
			
		||||
	Case { "happy path won't throw" } = [&] {
 | 
			
		||||
		ignore = create_system();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "happy path has no validation errors" } = [&] {
 | 
			
		||||
		auto fixture = SystemTest {};
 | 
			
		||||
		std::ignore = System(
 | 
			
		||||
		    {
 | 
			
		||||
		        .registry = fixture.registry(),
 | 
			
		||||
		        .surface_entity = fixture.surface_entity(),
 | 
			
		||||
		        .system_stats = fixture.stats(),
 | 
			
		||||
		    }
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		expect_true(fixture.stats()->empty_diagnosis());
 | 
			
		||||
		auto [surface, renderer] = create_system();
 | 
			
		||||
		expect_true(renderer.system.get_stats().empty_diagnosis());
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "unhappy path throws" } = [] {
 | 
			
		||||
		auto fixture = SystemTest {};
 | 
			
		||||
		auto empty_entity = ecs::Entity { fixture.registry(), fixture.registry()->create_entity() };
 | 
			
		||||
		auto [surface_system, surface_entity] = create_surface();
 | 
			
		||||
		auto empty_registry = create_ref<ecs::Registry>();
 | 
			
		||||
		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([&] {
 | 
			
		||||
			ignore = System(
 | 
			
		||||
			    {
 | 
			
		||||
			        .registry = {},
 | 
			
		||||
			        .surface_entity = fixture.surface_entity(),
 | 
			
		||||
			        .system_stats = fixture.stats(),
 | 
			
		||||
			        .surface_entity = surface_entity,
 | 
			
		||||
			        .system_stats = stats,
 | 
			
		||||
			    }
 | 
			
		||||
			);
 | 
			
		||||
		});
 | 
			
		||||
| 
						 | 
				
			
			@ -143,9 +99,9 @@ Suite raii = "raii"_suite = [] {
 | 
			
		|||
		expect_throw([&] {
 | 
			
		||||
			ignore = System(
 | 
			
		||||
			    System::CreateInfo {
 | 
			
		||||
			        .registry = fixture.registry(),
 | 
			
		||||
			        .registry = surface_entity.get_registry(),
 | 
			
		||||
			        .surface_entity = empty_entity,
 | 
			
		||||
			        .system_stats = fixture.stats(),
 | 
			
		||||
			        .system_stats = stats,
 | 
			
		||||
			    }
 | 
			
		||||
			);
 | 
			
		||||
		});
 | 
			
		||||
| 
						 | 
				
			
			@ -153,8 +109,8 @@ Suite raii = "raii"_suite = [] {
 | 
			
		|||
		expect_throw([&] {
 | 
			
		||||
			ignore = System(
 | 
			
		||||
			    System::CreateInfo {
 | 
			
		||||
			        .registry = fixture.registry(),
 | 
			
		||||
			        .surface_entity = fixture.surface_entity(),
 | 
			
		||||
			        .registry = surface_entity.get_registry(),
 | 
			
		||||
			        .surface_entity = surface_entity,
 | 
			
		||||
			        .system_stats = {},
 | 
			
		||||
			    }
 | 
			
		||||
			);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										694
									
								
								modules/renderer/private/vk/context.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										694
									
								
								modules/renderer/private/vk/context.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,694 @@
 | 
			
		|||
#include <ranges>
 | 
			
		||||
#include <renderer/vk/context.hpp>
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
 | 
			
		||||
#elif defined(__unix__)
 | 
			
		||||
	#include <dlfcn.h>
 | 
			
		||||
namespace {
 | 
			
		||||
void *library = nullptr; // NOLINT
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer::vk {
 | 
			
		||||
 | 
			
		||||
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
// global functions
 | 
			
		||||
PFN_vkGetInstanceProcAddr vk_get_instance_proc_address;
 | 
			
		||||
PFN_vkCreateInstance vk_create_instance;
 | 
			
		||||
PFN_vkEnumerateInstanceExtensionProperties vk_enumerate_instance_extension_properties;
 | 
			
		||||
PFN_vkEnumerateInstanceLayerProperties vk_enumerate_instance_layer_properties;
 | 
			
		||||
 | 
			
		||||
// instance functions
 | 
			
		||||
PFN_vkDestroyInstance vk_destroy_instance;
 | 
			
		||||
PFN_vkEnumeratePhysicalDevices vk_enumerate_physical_devices;
 | 
			
		||||
PFN_vkGetPhysicalDeviceProperties vk_get_physical_device_properties;
 | 
			
		||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue_family_properties;
 | 
			
		||||
PFN_vkCreateDevice vk_create_device;
 | 
			
		||||
PFN_vkGetDeviceProcAddr vk_get_device_proc_address;
 | 
			
		||||
PFN_vkDestroyDevice vk_destroy_device;
 | 
			
		||||
PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features;
 | 
			
		||||
PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties;
 | 
			
		||||
 | 
			
		||||
// extension instance functions
 | 
			
		||||
PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label;
 | 
			
		||||
PFN_vkCmdEndDebugUtilsLabelEXT vk_cmd_end_debug_label;
 | 
			
		||||
PFN_vkCmdInsertDebugUtilsLabelEXT vk_cmd_insert_debug_label;
 | 
			
		||||
PFN_vkCreateDebugUtilsMessengerEXT vk_create_debug_messenger;
 | 
			
		||||
PFN_vkDestroyDebugUtilsMessengerEXT vk_destroy_debug_messenger;
 | 
			
		||||
PFN_vkQueueBeginDebugUtilsLabelEXT vk_queue_begin_debug_label;
 | 
			
		||||
PFN_vkQueueEndDebugUtilsLabelEXT vk_queue_end_debug_label;
 | 
			
		||||
PFN_vkQueueInsertDebugUtilsLabelEXT vk_queue_insert_debug_label;
 | 
			
		||||
PFN_vkSetDebugUtilsObjectNameEXT vk_set_debug_object_name;
 | 
			
		||||
PFN_vkSetDebugUtilsObjectTagEXT vk_set_debug_object_tag;
 | 
			
		||||
PFN_vkSubmitDebugUtilsMessageEXT vk_submit_debug_message;
 | 
			
		||||
 | 
			
		||||
// device functions
 | 
			
		||||
PFN_vkGetDeviceQueue vk_get_device_queue;
 | 
			
		||||
PFN_vkCreateCommandPool vk_create_command_pool;
 | 
			
		||||
PFN_vkDestroyCommandPool vk_destroy_command_pool;
 | 
			
		||||
PFN_vkAllocateCommandBuffers vk_allocate_command_buffers;
 | 
			
		||||
PFN_vkFreeCommandBuffers vk_free_command_buffers;
 | 
			
		||||
PFN_vkBeginCommandBuffer vk_begin_command_buffer;
 | 
			
		||||
PFN_vkEndCommandBuffer vk_end_command_buffer;
 | 
			
		||||
PFN_vkCmdPipelineBarrier vk_cmd_pipeline_barrier;
 | 
			
		||||
PFN_vkQueueSubmit vk_queue_submit;
 | 
			
		||||
PFN_vkQueueWaitIdle vk_queue_wait_idle;
 | 
			
		||||
PFN_vkDeviceWaitIdle vk_device_wait_idle;
 | 
			
		||||
PFN_vkCreateFence vk_create_fence;
 | 
			
		||||
PFN_vkDestroyFence vk_destroy_fence;
 | 
			
		||||
PFN_vkWaitForFences vk_wait_for_fences;
 | 
			
		||||
PFN_vkResetFences vk_reset_fences;
 | 
			
		||||
PFN_vkCreateSemaphore vk_create_semaphore;
 | 
			
		||||
PFN_vkDestroySemaphore vk_destroy_semaphore;
 | 
			
		||||
PFN_vkCreateSwapchainKHR vk_create_swapchain_khr;
 | 
			
		||||
PFN_vkDestroySwapchainKHR vk_destroy_swapchain_khr;
 | 
			
		||||
PFN_vkGetSwapchainImagesKHR vk_get_swapchain_images_khr;
 | 
			
		||||
PFN_vkAcquireNextImageKHR vk_acquire_next_image_khr;
 | 
			
		||||
PFN_vkQueuePresentKHR vk_queue_present_khr;
 | 
			
		||||
PFN_vkCreateImageView vk_create_image_view;
 | 
			
		||||
PFN_vkDestroyImageView vk_destroy_image_view;
 | 
			
		||||
PFN_vkCreateRenderPass vk_create_render_pass;
 | 
			
		||||
PFN_vkDestroyRenderPass vk_destroy_render_pass;
 | 
			
		||||
PFN_vkCreateFramebuffer vk_create_frame_buffer;
 | 
			
		||||
PFN_vkDestroyFramebuffer vk_destroy_frame_buffer;
 | 
			
		||||
PFN_vkCreateShaderModule vk_create_shader_module;
 | 
			
		||||
PFN_vkDestroyShaderModule vk_destroy_shader_module;
 | 
			
		||||
PFN_vkCreatePipelineLayout vk_create_pipeline_layout;
 | 
			
		||||
PFN_vkDestroyPipelineLayout vk_destroy_pipeline_layout;
 | 
			
		||||
PFN_vkCreateGraphicsPipelines vk_create_graphics_pipelines;
 | 
			
		||||
PFN_vkDestroyPipeline vk_destroy_pipeline;
 | 
			
		||||
PFN_vkCmdBeginRenderPass vk_cmd_begin_render_pass;
 | 
			
		||||
PFN_vkCmdEndRenderPass vk_cmd_end_render_pass;
 | 
			
		||||
PFN_vkCmdBindPipeline vk_cmd_bind_pipeline;
 | 
			
		||||
PFN_vkCmdDraw vk_cmd_draw;
 | 
			
		||||
PFN_vkCmdSetViewport vk_cmd_set_viewport;
 | 
			
		||||
PFN_vkCmdSetScissor vk_cmd_set_scissors;
 | 
			
		||||
 | 
			
		||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support;
 | 
			
		||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities;
 | 
			
		||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats;
 | 
			
		||||
 | 
			
		||||
PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
 | 
			
		||||
PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
 | 
			
		||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *;
 | 
			
		||||
 | 
			
		||||
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
 | 
			
		||||
    -> app::SystemDiagnosis::Severity;
 | 
			
		||||
 | 
			
		||||
auto validation_layers_callback(
 | 
			
		||||
    VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity,
 | 
			
		||||
    VkDebugUtilsMessageTypeFlagsEXT const message_types,
 | 
			
		||||
    VkDebugUtilsMessengerCallbackDataEXT const *const callback_data,
 | 
			
		||||
    void *const vulkan_user_data
 | 
			
		||||
) -> VkBool32;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Context::Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats)
 | 
			
		||||
    : m_stats(std::move(system_stats))
 | 
			
		||||
{
 | 
			
		||||
	ensure(m_stats, "Failed to create Vulkan Context: null stats");
 | 
			
		||||
 | 
			
		||||
	load_library();
 | 
			
		||||
	load_global_functions();
 | 
			
		||||
 | 
			
		||||
	initialize_instance();
 | 
			
		||||
	load_instance_functions();
 | 
			
		||||
 | 
			
		||||
	initialize_debug_messenger();
 | 
			
		||||
 | 
			
		||||
	initialize_physical_device();
 | 
			
		||||
	initialize_logical_device();
 | 
			
		||||
	load_device_functions();
 | 
			
		||||
 | 
			
		||||
	initialize_surface(surface_entity);
 | 
			
		||||
	initialize_queue();
 | 
			
		||||
	initialize_swapchain();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Context::~Context()
 | 
			
		||||
{
 | 
			
		||||
	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);
 | 
			
		||||
			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());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Context::initialize_instance()
 | 
			
		||||
{
 | 
			
		||||
	auto app_info = VkApplicationInfo {
 | 
			
		||||
		.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
 | 
			
		||||
		.pApplicationName = "Hallo Hallo Hallo :3",
 | 
			
		||||
		.applicationVersion = VK_MAKE_VERSION(1, 4, 0),
 | 
			
		||||
		.pEngineName = "light",
 | 
			
		||||
		.engineVersion = VK_MAKE_VERSION(1, 4, 0),
 | 
			
		||||
		.apiVersion = VK_API_VERSION_1_4,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	auto extensions = std::vector<const char *> {
 | 
			
		||||
		VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
 | 
			
		||||
		VK_KHR_SURFACE_EXTENSION_NAME,
 | 
			
		||||
		VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
 | 
			
		||||
	};
 | 
			
		||||
	auto layers = std::vector<const char *> {
 | 
			
		||||
		"VK_LAYER_KHRONOS_validation",
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	auto instance_info = VkInstanceCreateInfo {
 | 
			
		||||
		.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
 | 
			
		||||
		.pApplicationInfo = &app_info,
 | 
			
		||||
		.enabledLayerCount = static_cast<uint32_t>(layers.size()),
 | 
			
		||||
		.ppEnabledLayerNames = layers.data(),
 | 
			
		||||
		.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
 | 
			
		||||
		.ppEnabledExtensionNames = extensions.data(),
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		auto count = 0u;
 | 
			
		||||
		vkc(vk_enumerate_instance_extension_properties(nullptr, &count, nullptr));
 | 
			
		||||
 | 
			
		||||
		auto extensions = std::vector<VkExtensionProperties>(count);
 | 
			
		||||
		vkc(vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data()));
 | 
			
		||||
 | 
			
		||||
		// log_inf("Available vulkan instance extensions:");
 | 
			
		||||
		for (auto &ext : extensions)
 | 
			
		||||
		{
 | 
			
		||||
			// log_inf("\t{} @ {}", ext.extensionName, ext.specVersion);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vkc(vk_create_instance(&instance_info, nullptr, &m_instance));
 | 
			
		||||
 | 
			
		||||
	ensure(m_instance, "Failed to create vulkan instance");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Context::initialize_debug_messenger()
 | 
			
		||||
{
 | 
			
		||||
	const auto info = VkDebugUtilsMessengerCreateInfoEXT {
 | 
			
		||||
		.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
 | 
			
		||||
 | 
			
		||||
		.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
 | 
			
		||||
		                   | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
 | 
			
		||||
		                   | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
 | 
			
		||||
		                   | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
 | 
			
		||||
 | 
			
		||||
		.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
 | 
			
		||||
		               | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
 | 
			
		||||
		               | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		.pfnUserCallback = &validation_layers_callback,
 | 
			
		||||
 | 
			
		||||
		.pUserData = &m_stats,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	ensure(
 | 
			
		||||
	    !vk_create_debug_messenger(m_instance, &info, nullptr, &m_debug_messenger),
 | 
			
		||||
	    "Failed to create vulkan debug utils messenger"
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Context::initialize_physical_device()
 | 
			
		||||
{
 | 
			
		||||
	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);
 | 
			
		||||
	ensure(library, "Failed to dlopen libvulkan.so");
 | 
			
		||||
 | 
			
		||||
	// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
			
		||||
	vk_get_instance_proc_address = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
 | 
			
		||||
	    dlsym(library, "vkGetInstanceProcAddr")
 | 
			
		||||
	);
 | 
			
		||||
	ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Context::load_global_functions()
 | 
			
		||||
{
 | 
			
		||||
	constexpr auto load_fn = []<typename T>(T &pfn, const char *fn_name) {
 | 
			
		||||
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
			
		||||
		pfn = reinterpret_cast<T>(vk_get_instance_proc_address(nullptr, fn_name));
 | 
			
		||||
		ensure(pfn, "Failed to load vulkan global function: {}", fn_name);
 | 
			
		||||
		// log_trc("Loaded global function: {}", fn_name);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_create_instance, "vkCreateInstance");
 | 
			
		||||
	load_fn(vk_enumerate_instance_extension_properties, "vkEnumerateInstanceExtensionProperties");
 | 
			
		||||
	load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Context::load_instance_functions()
 | 
			
		||||
{
 | 
			
		||||
	const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
 | 
			
		||||
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
			
		||||
		pfn = reinterpret_cast<T>(vk_get_instance_proc_address(m_instance, fn_name));
 | 
			
		||||
		ensure(pfn, "Failed to load vulkan instance function: {}", fn_name);
 | 
			
		||||
		// log_trc("Loaded instance function: {}", fn_name);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_destroy_instance, "vkDestroyInstance");
 | 
			
		||||
	load_fn(vk_enumerate_physical_devices, "vkEnumeratePhysicalDevices");
 | 
			
		||||
	load_fn(vk_get_physical_device_properties, "vkGetPhysicalDeviceProperties");
 | 
			
		||||
	load_fn(
 | 
			
		||||
	    vk_get_physical_device_queue_family_properties,
 | 
			
		||||
	    "vkGetPhysicalDeviceQueueFamilyProperties"
 | 
			
		||||
	);
 | 
			
		||||
	load_fn(vk_create_device, "vkCreateDevice");
 | 
			
		||||
	load_fn(vk_get_device_proc_address, "vkGetDeviceProcAddr");
 | 
			
		||||
	load_fn(vk_destroy_device, "vkDestroyDevice");
 | 
			
		||||
	load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures");
 | 
			
		||||
	load_fn(vk_enumerate_device_extension_properties, "vkEnumerateDeviceExtensionProperties");
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_cmd_begin_debug_label, "vkCmdBeginDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_cmd_end_debug_label, "vkCmdEndDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_cmd_insert_debug_label, "vkCmdInsertDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_create_debug_messenger, "vkCreateDebugUtilsMessengerEXT");
 | 
			
		||||
	load_fn(vk_destroy_debug_messenger, "vkDestroyDebugUtilsMessengerEXT");
 | 
			
		||||
	load_fn(vk_queue_begin_debug_label, "vkQueueBeginDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_queue_end_debug_label, "vkQueueEndDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_queue_insert_debug_label, "vkQueueInsertDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_set_debug_object_name, "vkSetDebugUtilsObjectNameEXT");
 | 
			
		||||
	load_fn(vk_set_debug_object_tag, "vkSetDebugUtilsObjectTagEXT");
 | 
			
		||||
	load_fn(vk_submit_debug_message, "vkSubmitDebugUtilsMessageEXT");
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_get_physical_device_surface_support, "vkGetPhysicalDeviceSurfaceSupportKHR");
 | 
			
		||||
	load_fn(
 | 
			
		||||
	    vk_get_physical_device_surface_capabilities,
 | 
			
		||||
	    "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"
 | 
			
		||||
	);
 | 
			
		||||
	load_fn(vk_get_physical_device_surface_formats, "vkGetPhysicalDeviceSurfaceFormatsKHR");
 | 
			
		||||
	load_fn(vk_create_xlib_surface_khr, "vkCreateXlibSurfaceKHR");
 | 
			
		||||
	load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Context::load_device_functions()
 | 
			
		||||
{
 | 
			
		||||
	const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
 | 
			
		||||
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
			
		||||
		pfn = reinterpret_cast<T>(vk_get_device_proc_address(m_device, fn_name));
 | 
			
		||||
		ensure(pfn, "Failed to load vulkan device function: {}", fn_name);
 | 
			
		||||
		// log_trc("Loaded device function: {}", fn_name);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_get_device_queue, "vkGetDeviceQueue");
 | 
			
		||||
	load_fn(vk_create_command_pool, "vkCreateCommandPool");
 | 
			
		||||
	load_fn(vk_destroy_command_pool, "vkDestroyCommandPool");
 | 
			
		||||
	load_fn(vk_allocate_command_buffers, "vkAllocateCommandBuffers");
 | 
			
		||||
	load_fn(vk_free_command_buffers, "vkFreeCommandBuffers");
 | 
			
		||||
	load_fn(vk_begin_command_buffer, "vkBeginCommandBuffer");
 | 
			
		||||
	load_fn(vk_end_command_buffer, "vkEndCommandBuffer");
 | 
			
		||||
	load_fn(vk_cmd_pipeline_barrier, "vkCmdPipelineBarrier");
 | 
			
		||||
	load_fn(vk_queue_submit, "vkQueueSubmit");
 | 
			
		||||
	load_fn(vk_queue_wait_idle, "vkQueueWaitIdle");
 | 
			
		||||
	load_fn(vk_device_wait_idle, "vkDeviceWaitIdle");
 | 
			
		||||
	load_fn(vk_create_fence, "vkCreateFence");
 | 
			
		||||
	load_fn(vk_destroy_fence, "vkDestroyFence");
 | 
			
		||||
	load_fn(vk_wait_for_fences, "vkWaitForFences");
 | 
			
		||||
	load_fn(vk_reset_fences, "vkResetFences");
 | 
			
		||||
	load_fn(vk_create_semaphore, "vkCreateSemaphore");
 | 
			
		||||
	load_fn(vk_destroy_semaphore, "vkDestroySemaphore");
 | 
			
		||||
	load_fn(vk_create_swapchain_khr, "vkCreateSwapchainKHR");
 | 
			
		||||
	load_fn(vk_destroy_swapchain_khr, "vkDestroySwapchainKHR");
 | 
			
		||||
	load_fn(vk_get_swapchain_images_khr, "vkGetSwapchainImagesKHR");
 | 
			
		||||
	load_fn(vk_acquire_next_image_khr, "vkAcquireNextImageKHR");
 | 
			
		||||
	load_fn(vk_queue_present_khr, "vkQueuePresentKHR");
 | 
			
		||||
	load_fn(vk_create_image_view, "vkCreateImageView");
 | 
			
		||||
	load_fn(vk_destroy_image_view, "vkDestroyImageView");
 | 
			
		||||
	load_fn(vk_create_render_pass, "vkCreateRenderPass");
 | 
			
		||||
	load_fn(vk_destroy_render_pass, "vkDestroyRenderPass");
 | 
			
		||||
	load_fn(vk_create_frame_buffer, "vkCreateFramebuffer");
 | 
			
		||||
	load_fn(vk_destroy_frame_buffer, "vkDestroyFramebuffer");
 | 
			
		||||
	load_fn(vk_create_shader_module, "vkCreateShaderModule");
 | 
			
		||||
	load_fn(vk_destroy_shader_module, "vkDestroyShaderModule");
 | 
			
		||||
	load_fn(vk_create_pipeline_layout, "vkCreatePipelineLayout");
 | 
			
		||||
	load_fn(vk_destroy_pipeline_layout, "vkDestroyPipelineLayout");
 | 
			
		||||
	load_fn(vk_create_graphics_pipelines, "vkCreateGraphicsPipelines");
 | 
			
		||||
	load_fn(vk_destroy_pipeline, "vkDestroyPipeline");
 | 
			
		||||
	load_fn(vk_cmd_begin_render_pass, "vkCmdBeginRenderPass");
 | 
			
		||||
	load_fn(vk_cmd_end_render_pass, "vkCmdEndRenderPass");
 | 
			
		||||
	load_fn(vk_cmd_bind_pipeline, "vkCmdBindPipeline");
 | 
			
		||||
	load_fn(vk_cmd_draw, "vkCmdDraw");
 | 
			
		||||
	load_fn(vk_cmd_set_viewport, "vkCmdSetViewport");
 | 
			
		||||
	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 *
 | 
			
		||||
{
 | 
			
		||||
	if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
 | 
			
		||||
	{
 | 
			
		||||
		return "GENERAL";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (message_types
 | 
			
		||||
	    == (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
 | 
			
		||||
	        | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT))
 | 
			
		||||
	{
 | 
			
		||||
		return "VALIDATION | PERFORMANCE";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
 | 
			
		||||
	{
 | 
			
		||||
		return "VALIDATION";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "PERFORMANCE";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
 | 
			
		||||
    -> app::SystemDiagnosis::Severity
 | 
			
		||||
{
 | 
			
		||||
	using enum app::SystemDiagnosis::Severity;
 | 
			
		||||
 | 
			
		||||
	switch (message_severity)
 | 
			
		||||
	{
 | 
			
		||||
	case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return verbose;
 | 
			
		||||
	case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return info;
 | 
			
		||||
	case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return warning;
 | 
			
		||||
	case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return error;
 | 
			
		||||
	default: ensure(false, "Invalid message severity: {}", static_cast<int>(message_severity));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto validation_layers_callback(
 | 
			
		||||
    VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity,
 | 
			
		||||
    VkDebugUtilsMessageTypeFlagsEXT const message_types,
 | 
			
		||||
    VkDebugUtilsMessengerCallbackDataEXT const *const callback_data,
 | 
			
		||||
    void *const vulkan_user_data
 | 
			
		||||
) -> VkBool32
 | 
			
		||||
{
 | 
			
		||||
	log_dbg("VALIDATION: {}", callback_data->pMessage);
 | 
			
		||||
	ensure(vulkan_user_data, "Validation layers's user data is not set!");
 | 
			
		||||
 | 
			
		||||
	auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT
 | 
			
		||||
 | 
			
		||||
	const auto &type = parse_message_type(message_types);
 | 
			
		||||
 | 
			
		||||
	auto message = std::format(
 | 
			
		||||
	    "Vulkan Validation Message:\ntype: {}\nseverity: {}\nmessage: {}",
 | 
			
		||||
	    type,
 | 
			
		||||
	    std::to_underlying(parse_message_severity(message_severity)),
 | 
			
		||||
	    callback_data->pMessage
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	auto severity = parse_message_severity(message_severity);
 | 
			
		||||
	if (std::to_underlying(severity) < 2)
 | 
			
		||||
	{
 | 
			
		||||
		return static_cast<VkBool32>(VK_FALSE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stats->push_diagnosis(
 | 
			
		||||
	    app::SystemDiagnosis {
 | 
			
		||||
	        .message = message,
 | 
			
		||||
	        .code = {}, // TODO(Light): extract vulkan validation-layers code from the message
 | 
			
		||||
	        .severity = severity,
 | 
			
		||||
	    }
 | 
			
		||||
	);
 | 
			
		||||
	return static_cast<VkBool32>(VK_FALSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer::vk
 | 
			
		||||
| 
						 | 
				
			
			@ -5,8 +5,16 @@
 | 
			
		|||
#include <vulkan/vulkan.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 {
 | 
			
		||||
 | 
			
		||||
using memory::NullOnMove;
 | 
			
		||||
 | 
			
		||||
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
// global functions
 | 
			
		||||
extern PFN_vkGetInstanceProcAddr vk_get_instance_proc_address;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,13 +46,6 @@ extern PFN_vkSetDebugUtilsObjectNameEXT vk_set_debug_object_name;
 | 
			
		|||
extern PFN_vkSetDebugUtilsObjectTagEXT vk_set_debug_object_tag;
 | 
			
		||||
extern PFN_vkSubmitDebugUtilsMessageEXT vk_submit_debug_message;
 | 
			
		||||
 | 
			
		||||
// surface instance functions
 | 
			
		||||
extern PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support;
 | 
			
		||||
extern PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities;
 | 
			
		||||
extern PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats;
 | 
			
		||||
extern PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
 | 
			
		||||
extern PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
 | 
			
		||||
 | 
			
		||||
// device functions
 | 
			
		||||
extern PFN_vkGetDeviceQueue vk_get_device_queue;
 | 
			
		||||
extern PFN_vkCreateCommandPool vk_create_command_pool;
 | 
			
		||||
| 
						 | 
				
			
			@ -86,6 +87,128 @@ extern PFN_vkCmdBindPipeline vk_cmd_bind_pipeline;
 | 
			
		|||
extern PFN_vkCmdDraw vk_cmd_draw;
 | 
			
		||||
extern PFN_vkCmdSetViewport vk_cmd_set_viewport;
 | 
			
		||||
extern PFN_vkCmdSetScissor vk_cmd_set_scissors;
 | 
			
		||||
 | 
			
		||||
// Surface
 | 
			
		||||
extern PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support;
 | 
			
		||||
extern PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities;
 | 
			
		||||
extern PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats;
 | 
			
		||||
 | 
			
		||||
extern PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
 | 
			
		||||
extern PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
 | 
			
		||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
inline void vkc(VkResult result)
 | 
			
		||||
{
 | 
			
		||||
	if (result)
 | 
			
		||||
	{
 | 
			
		||||
		throw std::runtime_error {
 | 
			
		||||
			std::format("Vulkan call failed with result: {}", std::to_underlying(result))
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Context
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats);
 | 
			
		||||
 | 
			
		||||
	~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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto physical_device() -> VkPhysicalDevice
 | 
			
		||||
	{
 | 
			
		||||
		return m_physical_device;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto device() -> VkDevice
 | 
			
		||||
	{
 | 
			
		||||
		return m_device;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto queue() -> VkQueue
 | 
			
		||||
	{
 | 
			
		||||
		return m_queue;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	auto debug_messenger() -> VkDebugUtilsMessengerEXT
 | 
			
		||||
	{
 | 
			
		||||
		return m_debug_messenger;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto get_stats() const -> const app::SystemStats &
 | 
			
		||||
	{
 | 
			
		||||
		return *m_stats;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void initialize_instance();
 | 
			
		||||
 | 
			
		||||
	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_global_functions();
 | 
			
		||||
 | 
			
		||||
	void load_instance_functions();
 | 
			
		||||
 | 
			
		||||
	void load_device_functions();
 | 
			
		||||
 | 
			
		||||
	auto get_optimal_swapchain_image_count(
 | 
			
		||||
	    VkSurfaceCapabilitiesKHR capabilities,
 | 
			
		||||
	    uint32_t desired_image_count = 3
 | 
			
		||||
	) -> uint32_t;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto find_suitable_queue_family() const -> uint32_t;
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,14 +0,0 @@
 | 
			
		|||
#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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,48 +0,0 @@
 | 
			
		|||
#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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,156 +0,0 @@
 | 
			
		|||
#include <renderer/vk/context/device.hpp>
 | 
			
		||||
#include <renderer/vk/context/instance.hpp>
 | 
			
		||||
#include <renderer/vk/context/surface.hpp>
 | 
			
		||||
#include <renderer/vk/debug/validation.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer::vk {
 | 
			
		||||
 | 
			
		||||
Device::Device(const Surface &surface)
 | 
			
		||||
{
 | 
			
		||||
	ensure(surface.vk(), "Failed to initialize vk::Device: null vulkan 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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,59 +0,0 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory/pointer_types/null_on_move.hpp>
 | 
			
		||||
#include <renderer/vk/vulkan.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer::vk {
 | 
			
		||||
 | 
			
		||||
class Device
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	Device(const class 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 class 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,99 +0,0 @@
 | 
			
		|||
#include <ranges>
 | 
			
		||||
#include <renderer/vk/context/device.hpp>
 | 
			
		||||
#include <renderer/vk/context/surface.hpp>
 | 
			
		||||
#include <surface/components.hpp>
 | 
			
		||||
#include <surface/system.hpp>
 | 
			
		||||
#include <test/test.hpp>
 | 
			
		||||
 | 
			
		||||
using namespace lt;
 | 
			
		||||
using renderer::vk::Device;
 | 
			
		||||
using renderer::vk::Surface;
 | 
			
		||||
using test::Case;
 | 
			
		||||
using test::expect_ne;
 | 
			
		||||
using test::expect_throw;
 | 
			
		||||
using test::Suite;
 | 
			
		||||
 | 
			
		||||
constexpr auto resolution = math::uvec2 { 800u, 600u };
 | 
			
		||||
 | 
			
		||||
Suite raii = "device_raii"_suite = [] {
 | 
			
		||||
	Case { "happy path won't throw" } = [] {
 | 
			
		||||
		auto registry = create_ref<ecs::Registry>();
 | 
			
		||||
		auto surface_system = surface::System { registry };
 | 
			
		||||
		auto entity = ecs::Entity { registry, registry->create_entity() };
 | 
			
		||||
		entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
 | 
			
		||||
		    .resolution = resolution,
 | 
			
		||||
		    .visible = true,
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		auto surface = Surface { entity };
 | 
			
		||||
		auto device = Device { surface };
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "many won't freeze/throw" } = [] {
 | 
			
		||||
		auto registry = create_ref<ecs::Registry>();
 | 
			
		||||
		auto surface_system = surface::System { registry };
 | 
			
		||||
		auto entity = ecs::Entity { registry, registry->create_entity() };
 | 
			
		||||
		entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
 | 
			
		||||
		    .resolution = resolution,
 | 
			
		||||
		    .visible = true,
 | 
			
		||||
		});
 | 
			
		||||
		auto surface = Surface { entity };
 | 
			
		||||
 | 
			
		||||
		// it takes a loong time to initialize vulkan + setup device
 | 
			
		||||
		for (auto idx : std::views::iota(0, 10))
 | 
			
		||||
		{
 | 
			
		||||
			Device { surface };
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "unhappy path throws" } = [] {
 | 
			
		||||
		auto registry = create_ref<ecs::Registry>();
 | 
			
		||||
		auto surface_system = surface::System { registry };
 | 
			
		||||
		auto entity = ecs::Entity { registry, registry->create_entity() };
 | 
			
		||||
		entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
 | 
			
		||||
		    .resolution = resolution,
 | 
			
		||||
		    .visible = true,
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		auto moved_out_surface = Surface { entity };
 | 
			
		||||
		auto surface = std::move(moved_out_surface);
 | 
			
		||||
 | 
			
		||||
		expect_throw([&] { Device { moved_out_surface }; });
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "post construct has correct state" } = [] {
 | 
			
		||||
		auto registry = create_ref<ecs::Registry>();
 | 
			
		||||
		auto surface_system = surface::System { registry };
 | 
			
		||||
		auto entity = ecs::Entity { registry, registry->create_entity() };
 | 
			
		||||
		entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
 | 
			
		||||
		    .resolution = resolution,
 | 
			
		||||
		    .visible = true,
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		auto surface = Surface { entity };
 | 
			
		||||
		auto device = Device { surface };
 | 
			
		||||
 | 
			
		||||
		for (auto &index : device.get_family_indices())
 | 
			
		||||
		{
 | 
			
		||||
			expect_ne(index, VK_QUEUE_FAMILY_IGNORED);
 | 
			
		||||
		}
 | 
			
		||||
		test::expect_true(device.physical());
 | 
			
		||||
		test::expect_true(device.vk());
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "post destruct has correct state" } = [] {
 | 
			
		||||
		auto registry = create_ref<ecs::Registry>();
 | 
			
		||||
		auto surface_system = surface::System { registry };
 | 
			
		||||
		auto entity = ecs::Entity { registry, registry->create_entity() };
 | 
			
		||||
		entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
 | 
			
		||||
		    .resolution = resolution,
 | 
			
		||||
		    .visible = true,
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		auto surface = Surface { entity };
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			auto device = Device { surface };
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,455 +0,0 @@
 | 
			
		|||
#include <app/system.hpp>
 | 
			
		||||
#include <renderer/vk/context/instance.hpp>
 | 
			
		||||
#include <renderer/vk/debug/validation.hpp>
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
	#error "Unsupported platform (currently)"
 | 
			
		||||
#elif defined(__unix__)
 | 
			
		||||
	#include <dlfcn.h>
 | 
			
		||||
namespace {
 | 
			
		||||
void *library = nullptr; // NOLINT
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer::vk {
 | 
			
		||||
 | 
			
		||||
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
// global functions
 | 
			
		||||
PFN_vkGetInstanceProcAddr vk_get_instance_proc_address {};
 | 
			
		||||
PFN_vkCreateInstance vk_create_instance {};
 | 
			
		||||
PFN_vkEnumerateInstanceExtensionProperties vk_enumerate_instance_extension_properties {};
 | 
			
		||||
PFN_vkEnumerateInstanceLayerProperties vk_enumerate_instance_layer_properties {};
 | 
			
		||||
 | 
			
		||||
// instance functions
 | 
			
		||||
PFN_vkDestroyInstance vk_destroy_instance {};
 | 
			
		||||
PFN_vkEnumeratePhysicalDevices vk_enumerate_physical_devices {};
 | 
			
		||||
PFN_vkGetPhysicalDeviceProperties vk_get_physical_device_properties {};
 | 
			
		||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue_family_properties {};
 | 
			
		||||
PFN_vkCreateDevice vk_create_device {};
 | 
			
		||||
PFN_vkGetDeviceProcAddr vk_get_device_proc_address {};
 | 
			
		||||
PFN_vkDestroyDevice vk_destroy_device {};
 | 
			
		||||
PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features {};
 | 
			
		||||
PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties {};
 | 
			
		||||
 | 
			
		||||
// extension instance functions
 | 
			
		||||
PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label {};
 | 
			
		||||
PFN_vkCmdEndDebugUtilsLabelEXT vk_cmd_end_debug_label {};
 | 
			
		||||
PFN_vkCmdInsertDebugUtilsLabelEXT vk_cmd_insert_debug_label {};
 | 
			
		||||
PFN_vkCreateDebugUtilsMessengerEXT vk_create_debug_messenger {};
 | 
			
		||||
PFN_vkDestroyDebugUtilsMessengerEXT vk_destroy_debug_messenger {};
 | 
			
		||||
PFN_vkQueueBeginDebugUtilsLabelEXT vk_queue_begin_debug_label {};
 | 
			
		||||
PFN_vkQueueEndDebugUtilsLabelEXT vk_queue_end_debug_label {};
 | 
			
		||||
PFN_vkQueueInsertDebugUtilsLabelEXT vk_queue_insert_debug_label {};
 | 
			
		||||
PFN_vkSetDebugUtilsObjectNameEXT vk_set_debug_object_name {};
 | 
			
		||||
PFN_vkSetDebugUtilsObjectTagEXT vk_set_debug_object_tag {};
 | 
			
		||||
PFN_vkSubmitDebugUtilsMessageEXT vk_submit_debug_message {};
 | 
			
		||||
 | 
			
		||||
// device functions
 | 
			
		||||
PFN_vkGetDeviceQueue vk_get_device_queue {};
 | 
			
		||||
PFN_vkCreateCommandPool vk_create_command_pool {};
 | 
			
		||||
PFN_vkDestroyCommandPool vk_destroy_command_pool {};
 | 
			
		||||
PFN_vkAllocateCommandBuffers vk_allocate_command_buffers {};
 | 
			
		||||
PFN_vkFreeCommandBuffers vk_free_command_buffers {};
 | 
			
		||||
PFN_vkBeginCommandBuffer vk_begin_command_buffer {};
 | 
			
		||||
PFN_vkEndCommandBuffer vk_end_command_buffer {};
 | 
			
		||||
PFN_vkCmdPipelineBarrier vk_cmd_pipeline_barrier {};
 | 
			
		||||
PFN_vkQueueSubmit vk_queue_submit {};
 | 
			
		||||
PFN_vkQueueWaitIdle vk_queue_wait_idle {};
 | 
			
		||||
PFN_vkDeviceWaitIdle vk_device_wait_idle {};
 | 
			
		||||
PFN_vkCreateFence vk_create_fence {};
 | 
			
		||||
PFN_vkDestroyFence vk_destroy_fence {};
 | 
			
		||||
PFN_vkWaitForFences vk_wait_for_fences {};
 | 
			
		||||
PFN_vkResetFences vk_reset_fences {};
 | 
			
		||||
PFN_vkCreateSemaphore vk_create_semaphore {};
 | 
			
		||||
PFN_vkDestroySemaphore vk_destroy_semaphore {};
 | 
			
		||||
PFN_vkCreateSwapchainKHR vk_create_swapchain_khr {};
 | 
			
		||||
PFN_vkDestroySwapchainKHR vk_destroy_swapchain_khr {};
 | 
			
		||||
PFN_vkGetSwapchainImagesKHR vk_get_swapchain_images_khr {};
 | 
			
		||||
PFN_vkAcquireNextImageKHR vk_acquire_next_image_khr {};
 | 
			
		||||
PFN_vkQueuePresentKHR vk_queue_present_khr {};
 | 
			
		||||
PFN_vkCreateImageView vk_create_image_view {};
 | 
			
		||||
PFN_vkDestroyImageView vk_destroy_image_view {};
 | 
			
		||||
PFN_vkCreateRenderPass vk_create_render_pass {};
 | 
			
		||||
PFN_vkDestroyRenderPass vk_destroy_render_pass {};
 | 
			
		||||
PFN_vkCreateFramebuffer vk_create_frame_buffer {};
 | 
			
		||||
PFN_vkDestroyFramebuffer vk_destroy_frame_buffer {};
 | 
			
		||||
PFN_vkCreateShaderModule vk_create_shader_module {};
 | 
			
		||||
PFN_vkDestroyShaderModule vk_destroy_shader_module {};
 | 
			
		||||
PFN_vkCreatePipelineLayout vk_create_pipeline_layout {};
 | 
			
		||||
PFN_vkDestroyPipelineLayout vk_destroy_pipeline_layout {};
 | 
			
		||||
PFN_vkCreateGraphicsPipelines vk_create_graphics_pipelines {};
 | 
			
		||||
PFN_vkDestroyPipeline vk_destroy_pipeline {};
 | 
			
		||||
PFN_vkCmdBeginRenderPass vk_cmd_begin_render_pass {};
 | 
			
		||||
PFN_vkCmdEndRenderPass vk_cmd_end_render_pass {};
 | 
			
		||||
PFN_vkCmdBindPipeline vk_cmd_bind_pipeline {};
 | 
			
		||||
PFN_vkCmdDraw vk_cmd_draw {};
 | 
			
		||||
PFN_vkCmdSetViewport vk_cmd_set_viewport {};
 | 
			
		||||
PFN_vkCmdSetScissor vk_cmd_set_scissors {};
 | 
			
		||||
 | 
			
		||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {};
 | 
			
		||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {};
 | 
			
		||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {};
 | 
			
		||||
 | 
			
		||||
PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr {};
 | 
			
		||||
PFN_vkDestroySurfaceKHR vk_destroy_surface_khr {};
 | 
			
		||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *;
 | 
			
		||||
 | 
			
		||||
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
 | 
			
		||||
    -> app::SystemDiagnosis::Severity;
 | 
			
		||||
 | 
			
		||||
auto validation_layers_callback(
 | 
			
		||||
    VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
 | 
			
		||||
    VkDebugUtilsMessageTypeFlagsEXT message_types,
 | 
			
		||||
    VkDebugUtilsMessengerCallbackDataEXT const *callback_data,
 | 
			
		||||
    void *vulkan_user_data
 | 
			
		||||
) -> VkBool32;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Instance::Instance()
 | 
			
		||||
{
 | 
			
		||||
	load_library();
 | 
			
		||||
	load_global_functions();
 | 
			
		||||
 | 
			
		||||
	initialize_instance();
 | 
			
		||||
	load_instance_functions();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Instance::~Instance()
 | 
			
		||||
{
 | 
			
		||||
	vk_destroy_instance(m_instance, nullptr);
 | 
			
		||||
	unload_library();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Instance::initialize_instance()
 | 
			
		||||
{
 | 
			
		||||
	auto app_info = VkApplicationInfo {
 | 
			
		||||
		.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
 | 
			
		||||
		.pApplicationName = "Hallo Hallo Hallo :3",
 | 
			
		||||
		.applicationVersion = VK_MAKE_VERSION(1, 4, 0),
 | 
			
		||||
		.pEngineName = "light",
 | 
			
		||||
		.engineVersion = VK_MAKE_VERSION(1, 4, 0),
 | 
			
		||||
		.apiVersion = VK_API_VERSION_1_4,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	auto extensions = std::vector<const char *> {
 | 
			
		||||
		VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
 | 
			
		||||
		VK_KHR_SURFACE_EXTENSION_NAME,
 | 
			
		||||
		VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const char *layer_name = "VK_LAYER_KHRONOS_validation";
 | 
			
		||||
	const auto setting_validate_core = VkBool32 { VK_TRUE };
 | 
			
		||||
	const auto setting_validate_sync = VkBool32 { VK_TRUE };
 | 
			
		||||
	const auto setting_thread_safety = VkBool32 { VK_TRUE };
 | 
			
		||||
	const auto *setting_debug_action = "";
 | 
			
		||||
	const auto setting_enable_message_limit = VkBool32 { VK_TRUE };
 | 
			
		||||
	const auto setting_duplicate_message_limit = uint32_t { 3u };
 | 
			
		||||
	auto setting_report_flags = std::array<const char *, 5> {
 | 
			
		||||
		"info", "warn", "perf", "error", "verbose",
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const auto settings = std::array<VkLayerSettingEXT, 7>({
 | 
			
		||||
	    {
 | 
			
		||||
	        .pLayerName = layer_name,
 | 
			
		||||
	        .pSettingName = "validate_core",
 | 
			
		||||
	        .type = VK_LAYER_SETTING_TYPE_BOOL32_EXT,
 | 
			
		||||
	        .valueCount = 1,
 | 
			
		||||
	        .pValues = &setting_validate_core,
 | 
			
		||||
	    },
 | 
			
		||||
	    {
 | 
			
		||||
	        .pLayerName = layer_name,
 | 
			
		||||
	        .pSettingName = "validate_sync",
 | 
			
		||||
	        .type = VK_LAYER_SETTING_TYPE_BOOL32_EXT,
 | 
			
		||||
	        .valueCount = 1,
 | 
			
		||||
	        .pValues = &setting_validate_sync,
 | 
			
		||||
	    },
 | 
			
		||||
	    {
 | 
			
		||||
	        .pLayerName = layer_name,
 | 
			
		||||
	        .pSettingName = "thread_safety",
 | 
			
		||||
	        .type = VK_LAYER_SETTING_TYPE_BOOL32_EXT,
 | 
			
		||||
	        .valueCount = 1,
 | 
			
		||||
	        .pValues = &setting_thread_safety,
 | 
			
		||||
	    },
 | 
			
		||||
	    {
 | 
			
		||||
	        .pLayerName = layer_name,
 | 
			
		||||
	        .pSettingName = "debug_action",
 | 
			
		||||
	        .type = VK_LAYER_SETTING_TYPE_STRING_EXT,
 | 
			
		||||
	        .valueCount = 1,
 | 
			
		||||
	        .pValues = static_cast<const void *>(&setting_debug_action),
 | 
			
		||||
	    },
 | 
			
		||||
	    {
 | 
			
		||||
	        .pLayerName = layer_name,
 | 
			
		||||
	        .pSettingName = "report_flags",
 | 
			
		||||
	        .type = VK_LAYER_SETTING_TYPE_STRING_EXT,
 | 
			
		||||
	        .valueCount = setting_report_flags.size(),
 | 
			
		||||
	        .pValues = static_cast<const void *>(setting_report_flags.data()),
 | 
			
		||||
	    },
 | 
			
		||||
	    {
 | 
			
		||||
	        .pLayerName = layer_name,
 | 
			
		||||
	        .pSettingName = "enable_message_limit",
 | 
			
		||||
	        .type = VK_LAYER_SETTING_TYPE_BOOL32_EXT,
 | 
			
		||||
	        .valueCount = 1,
 | 
			
		||||
	        .pValues = &setting_enable_message_limit,
 | 
			
		||||
	    },
 | 
			
		||||
	    {
 | 
			
		||||
	        .pLayerName = layer_name,
 | 
			
		||||
	        .pSettingName = "duplicate_message_limit",
 | 
			
		||||
	        .type = VK_LAYER_SETTING_TYPE_UINT32_EXT,
 | 
			
		||||
	        .valueCount = 1u,
 | 
			
		||||
	        .pValues = &setting_duplicate_message_limit,
 | 
			
		||||
	    },
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const VkLayerSettingsCreateInfoEXT layer_settings_create_info = {
 | 
			
		||||
		.sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT,
 | 
			
		||||
		.settingCount = settings.size(),
 | 
			
		||||
		.pSettings = settings.data()
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	auto layers = std::vector<const char *> {
 | 
			
		||||
		"VK_LAYER_KHRONOS_validation",
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	auto instance_info = VkInstanceCreateInfo {
 | 
			
		||||
		.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
 | 
			
		||||
		.pNext = &layer_settings_create_info,
 | 
			
		||||
		.pApplicationInfo = &app_info,
 | 
			
		||||
		.enabledLayerCount = static_cast<uint32_t>(layers.size()),
 | 
			
		||||
		.ppEnabledLayerNames = layers.data(),
 | 
			
		||||
		.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
 | 
			
		||||
		.ppEnabledExtensionNames = extensions.data(),
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		auto count = 0u;
 | 
			
		||||
		vkc(vk_enumerate_instance_extension_properties(nullptr, &count, nullptr));
 | 
			
		||||
 | 
			
		||||
		auto extensions = std::vector<VkExtensionProperties>(count);
 | 
			
		||||
		vkc(vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data()));
 | 
			
		||||
 | 
			
		||||
		// log_inf("Available vulkan instance extensions:");
 | 
			
		||||
		for (auto &ext : extensions)
 | 
			
		||||
		{
 | 
			
		||||
			// log_inf("\t{} @ {}", ext.extensionName, ext.specVersion);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vkc(vk_create_instance(&instance_info, nullptr, &m_instance));
 | 
			
		||||
	ensure(m_instance, "Failed to create vulkan instance");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Instance::load_library()
 | 
			
		||||
{
 | 
			
		||||
	library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
 | 
			
		||||
	ensure(library, "Failed to dlopen libvulkan.so");
 | 
			
		||||
 | 
			
		||||
	// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
			
		||||
	vk_get_instance_proc_address = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
 | 
			
		||||
	    dlsym(library, "vkGetInstanceProcAddr")
 | 
			
		||||
	);
 | 
			
		||||
	ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
			
		||||
		pfn = reinterpret_cast<T>(vk_get_instance_proc_address(nullptr, fn_name));
 | 
			
		||||
		ensure(pfn, "Failed to load vulkan global function: {}", fn_name);
 | 
			
		||||
		// log_trc("Loaded global function: {}", fn_name);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_create_instance, "vkCreateInstance");
 | 
			
		||||
	load_fn(vk_enumerate_instance_extension_properties, "vkEnumerateInstanceExtensionProperties");
 | 
			
		||||
	load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Instance::load_instance_functions()
 | 
			
		||||
{
 | 
			
		||||
	const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
 | 
			
		||||
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
			
		||||
		pfn = reinterpret_cast<T>(vk_get_instance_proc_address(m_instance, fn_name));
 | 
			
		||||
		ensure(pfn, "Failed to load vulkan instance function: {}", fn_name);
 | 
			
		||||
		// log_trc("Loaded instance function: {}", fn_name);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_destroy_instance, "vkDestroyInstance");
 | 
			
		||||
	load_fn(vk_enumerate_physical_devices, "vkEnumeratePhysicalDevices");
 | 
			
		||||
	load_fn(vk_get_physical_device_properties, "vkGetPhysicalDeviceProperties");
 | 
			
		||||
	load_fn(
 | 
			
		||||
	    vk_get_physical_device_queue_family_properties,
 | 
			
		||||
	    "vkGetPhysicalDeviceQueueFamilyProperties"
 | 
			
		||||
	);
 | 
			
		||||
	load_fn(vk_create_device, "vkCreateDevice");
 | 
			
		||||
	load_fn(vk_get_device_proc_address, "vkGetDeviceProcAddr");
 | 
			
		||||
	load_fn(vk_destroy_device, "vkDestroyDevice");
 | 
			
		||||
	load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures");
 | 
			
		||||
	load_fn(vk_enumerate_device_extension_properties, "vkEnumerateDeviceExtensionProperties");
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_cmd_begin_debug_label, "vkCmdBeginDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_cmd_end_debug_label, "vkCmdEndDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_cmd_insert_debug_label, "vkCmdInsertDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_create_debug_messenger, "vkCreateDebugUtilsMessengerEXT");
 | 
			
		||||
	load_fn(vk_destroy_debug_messenger, "vkDestroyDebugUtilsMessengerEXT");
 | 
			
		||||
	load_fn(vk_queue_begin_debug_label, "vkQueueBeginDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_queue_end_debug_label, "vkQueueEndDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_queue_insert_debug_label, "vkQueueInsertDebugUtilsLabelEXT");
 | 
			
		||||
	load_fn(vk_set_debug_object_name, "vkSetDebugUtilsObjectNameEXT");
 | 
			
		||||
	load_fn(vk_set_debug_object_tag, "vkSetDebugUtilsObjectTagEXT");
 | 
			
		||||
	load_fn(vk_submit_debug_message, "vkSubmitDebugUtilsMessageEXT");
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_get_physical_device_surface_support, "vkGetPhysicalDeviceSurfaceSupportKHR");
 | 
			
		||||
	load_fn(
 | 
			
		||||
	    vk_get_physical_device_surface_capabilities,
 | 
			
		||||
	    "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"
 | 
			
		||||
	);
 | 
			
		||||
	load_fn(vk_get_physical_device_surface_formats, "vkGetPhysicalDeviceSurfaceFormatsKHR");
 | 
			
		||||
	load_fn(vk_create_xlib_surface_khr, "vkCreateXlibSurfaceKHR");
 | 
			
		||||
	load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Instance::load_device_functions_impl(VkDevice device)
 | 
			
		||||
{
 | 
			
		||||
	const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
 | 
			
		||||
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 | 
			
		||||
		pfn = reinterpret_cast<T>(vk_get_device_proc_address(device, fn_name));
 | 
			
		||||
		ensure(pfn, "Failed to load vulkan device function: {}", fn_name);
 | 
			
		||||
		// log_trc("Loaded device function: {}", fn_name);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	load_fn(vk_get_device_queue, "vkGetDeviceQueue");
 | 
			
		||||
	load_fn(vk_create_command_pool, "vkCreateCommandPool");
 | 
			
		||||
	load_fn(vk_destroy_command_pool, "vkDestroyCommandPool");
 | 
			
		||||
	load_fn(vk_allocate_command_buffers, "vkAllocateCommandBuffers");
 | 
			
		||||
	load_fn(vk_free_command_buffers, "vkFreeCommandBuffers");
 | 
			
		||||
	load_fn(vk_begin_command_buffer, "vkBeginCommandBuffer");
 | 
			
		||||
	load_fn(vk_end_command_buffer, "vkEndCommandBuffer");
 | 
			
		||||
	load_fn(vk_cmd_pipeline_barrier, "vkCmdPipelineBarrier");
 | 
			
		||||
	load_fn(vk_queue_submit, "vkQueueSubmit");
 | 
			
		||||
	load_fn(vk_queue_wait_idle, "vkQueueWaitIdle");
 | 
			
		||||
	load_fn(vk_device_wait_idle, "vkDeviceWaitIdle");
 | 
			
		||||
	load_fn(vk_create_fence, "vkCreateFence");
 | 
			
		||||
	load_fn(vk_destroy_fence, "vkDestroyFence");
 | 
			
		||||
	load_fn(vk_wait_for_fences, "vkWaitForFences");
 | 
			
		||||
	load_fn(vk_reset_fences, "vkResetFences");
 | 
			
		||||
	load_fn(vk_create_semaphore, "vkCreateSemaphore");
 | 
			
		||||
	load_fn(vk_destroy_semaphore, "vkDestroySemaphore");
 | 
			
		||||
	load_fn(vk_create_swapchain_khr, "vkCreateSwapchainKHR");
 | 
			
		||||
	load_fn(vk_destroy_swapchain_khr, "vkDestroySwapchainKHR");
 | 
			
		||||
	load_fn(vk_get_swapchain_images_khr, "vkGetSwapchainImagesKHR");
 | 
			
		||||
	load_fn(vk_acquire_next_image_khr, "vkAcquireNextImageKHR");
 | 
			
		||||
	load_fn(vk_queue_present_khr, "vkQueuePresentKHR");
 | 
			
		||||
	load_fn(vk_create_image_view, "vkCreateImageView");
 | 
			
		||||
	load_fn(vk_destroy_image_view, "vkDestroyImageView");
 | 
			
		||||
	load_fn(vk_create_render_pass, "vkCreateRenderPass");
 | 
			
		||||
	load_fn(vk_destroy_render_pass, "vkDestroyRenderPass");
 | 
			
		||||
	load_fn(vk_create_frame_buffer, "vkCreateFramebuffer");
 | 
			
		||||
	load_fn(vk_destroy_frame_buffer, "vkDestroyFramebuffer");
 | 
			
		||||
	load_fn(vk_create_shader_module, "vkCreateShaderModule");
 | 
			
		||||
	load_fn(vk_destroy_shader_module, "vkDestroyShaderModule");
 | 
			
		||||
	load_fn(vk_create_pipeline_layout, "vkCreatePipelineLayout");
 | 
			
		||||
	load_fn(vk_destroy_pipeline_layout, "vkDestroyPipelineLayout");
 | 
			
		||||
	load_fn(vk_create_graphics_pipelines, "vkCreateGraphicsPipelines");
 | 
			
		||||
	load_fn(vk_destroy_pipeline, "vkDestroyPipeline");
 | 
			
		||||
	load_fn(vk_cmd_begin_render_pass, "vkCmdBeginRenderPass");
 | 
			
		||||
	load_fn(vk_cmd_end_render_pass, "vkCmdEndRenderPass");
 | 
			
		||||
	load_fn(vk_cmd_bind_pipeline, "vkCmdBindPipeline");
 | 
			
		||||
	load_fn(vk_cmd_draw, "vkCmdDraw");
 | 
			
		||||
	load_fn(vk_cmd_set_viewport, "vkCmdSetViewport");
 | 
			
		||||
	load_fn(vk_cmd_set_scissors, "vkCmdSetScissor");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *
 | 
			
		||||
{
 | 
			
		||||
	if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
 | 
			
		||||
	{
 | 
			
		||||
		return "GENERAL";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (message_types
 | 
			
		||||
	    == (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
 | 
			
		||||
	        | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT))
 | 
			
		||||
	{
 | 
			
		||||
		return "VALIDATION | PERFORMANCE";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
 | 
			
		||||
	{
 | 
			
		||||
		return "VALIDATION";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "PERFORMANCE";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
 | 
			
		||||
    -> app::SystemDiagnosis::Severity
 | 
			
		||||
{
 | 
			
		||||
	using enum app::SystemDiagnosis::Severity;
 | 
			
		||||
 | 
			
		||||
	switch (message_severity)
 | 
			
		||||
	{
 | 
			
		||||
	case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return verbose;
 | 
			
		||||
	case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return info;
 | 
			
		||||
	case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return warning;
 | 
			
		||||
	case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return error;
 | 
			
		||||
	default: ensure(false, "Invalid message severity: {}", static_cast<int>(message_severity));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto validation_layers_callback(
 | 
			
		||||
    VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity,
 | 
			
		||||
    VkDebugUtilsMessageTypeFlagsEXT const message_types,
 | 
			
		||||
    VkDebugUtilsMessengerCallbackDataEXT const *const callback_data,
 | 
			
		||||
    void *const vulkan_user_data
 | 
			
		||||
) -> VkBool32
 | 
			
		||||
{
 | 
			
		||||
	std::cout << callback_data->pMessage << std::endl;
 | 
			
		||||
	return VK_FALSE;
 | 
			
		||||
 | 
			
		||||
	log_dbg("VALIDATION: {}", callback_data->pMessage);
 | 
			
		||||
 | 
			
		||||
	ensure(vulkan_user_data, "Validation layers's user data is not set!");
 | 
			
		||||
 | 
			
		||||
	auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT
 | 
			
		||||
 | 
			
		||||
	const auto &type = parse_message_type(message_types);
 | 
			
		||||
 | 
			
		||||
	auto message = std::format(
 | 
			
		||||
	    "Vulkan Validation Message:\ntype: {}\nseverity: {}\nmessage: {}",
 | 
			
		||||
	    type,
 | 
			
		||||
	    std::to_underlying(parse_message_severity(message_severity)),
 | 
			
		||||
	    callback_data->pMessage
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	auto severity = parse_message_severity(message_severity);
 | 
			
		||||
	if (std::to_underlying(severity) < 2)
 | 
			
		||||
	{
 | 
			
		||||
		return static_cast<VkBool32>(VK_FALSE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stats->push_diagnosis(
 | 
			
		||||
	    app::SystemDiagnosis {
 | 
			
		||||
	        .message = message,
 | 
			
		||||
	        .code = {}, // TODO(Light): extract vulkan validation-layers code from the message
 | 
			
		||||
	        .severity = severity,
 | 
			
		||||
	    }
 | 
			
		||||
	);
 | 
			
		||||
	return static_cast<VkBool32>(VK_FALSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer::vk
 | 
			
		||||
| 
						 | 
				
			
			@ -1,63 +0,0 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <renderer/vk/vulkan.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer::vk {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Responsible for dynamically loading Vulkan library/functions.
 | 
			
		||||
 *
 | 
			
		||||
 * @note: The delayed vkInstance destruction is due to a work-around for libx11:
 | 
			
		||||
 * @ref:
 | 
			
		||||
 * 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
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	static auto get() -> VkInstance
 | 
			
		||||
	{
 | 
			
		||||
		return Instance::instance().m_instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static auto load_device_functions(VkDevice device)
 | 
			
		||||
	{
 | 
			
		||||
		instance().load_device_functions_impl(device);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Instance(Instance &&) = delete;
 | 
			
		||||
 | 
			
		||||
	Instance(const Instance &) = delete;
 | 
			
		||||
 | 
			
		||||
	auto operator=(const Instance &) -> Instance & = delete;
 | 
			
		||||
 | 
			
		||||
	auto operator=(Instance &&) -> Instance & = delete;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	static auto instance() -> Instance &
 | 
			
		||||
	{
 | 
			
		||||
		static auto instance = Instance {};
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Instance();
 | 
			
		||||
 | 
			
		||||
	~Instance();
 | 
			
		||||
 | 
			
		||||
	void initialize_instance();
 | 
			
		||||
 | 
			
		||||
	void load_library();
 | 
			
		||||
 | 
			
		||||
	void unload_library();
 | 
			
		||||
 | 
			
		||||
	void load_global_functions();
 | 
			
		||||
 | 
			
		||||
	void load_instance_functions();
 | 
			
		||||
 | 
			
		||||
	void load_device_functions_impl(VkDevice device);
 | 
			
		||||
 | 
			
		||||
	VkInstance m_instance = VK_NULL_HANDLE;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer::vk
 | 
			
		||||
| 
						 | 
				
			
			@ -1,95 +0,0 @@
 | 
			
		|||
#include <renderer/vk/context/instance.hpp>
 | 
			
		||||
#include <test/test.hpp>
 | 
			
		||||
 | 
			
		||||
using namespace lt;
 | 
			
		||||
using renderer::vk::Instance;
 | 
			
		||||
using test::Case;
 | 
			
		||||
using test::expect_not_nullptr;
 | 
			
		||||
using test::Suite;
 | 
			
		||||
 | 
			
		||||
// NOLINTNEXTLINE
 | 
			
		||||
Suite raii = "raii"_suite = [] {
 | 
			
		||||
	Case { "post singleton insantiation state is correct" } = [] {
 | 
			
		||||
		expect_not_nullptr(Instance::get());
 | 
			
		||||
 | 
			
		||||
		using namespace renderer::vk;
 | 
			
		||||
		expect_not_nullptr(vk_get_instance_proc_address);
 | 
			
		||||
		expect_not_nullptr(vk_create_instance);
 | 
			
		||||
		expect_not_nullptr(vk_enumerate_instance_extension_properties);
 | 
			
		||||
		expect_not_nullptr(vk_enumerate_instance_layer_properties);
 | 
			
		||||
 | 
			
		||||
		expect_not_nullptr(vk_destroy_instance);
 | 
			
		||||
		expect_not_nullptr(vk_enumerate_physical_devices);
 | 
			
		||||
		expect_not_nullptr(vk_get_physical_device_properties);
 | 
			
		||||
		expect_not_nullptr(vk_get_physical_device_queue_family_properties);
 | 
			
		||||
		expect_not_nullptr(vk_create_device);
 | 
			
		||||
		expect_not_nullptr(vk_get_device_proc_address);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_device);
 | 
			
		||||
		expect_not_nullptr(vk_get_physical_device_features);
 | 
			
		||||
		expect_not_nullptr(vk_enumerate_device_extension_properties);
 | 
			
		||||
 | 
			
		||||
		expect_not_nullptr(vk_cmd_begin_debug_label);
 | 
			
		||||
		expect_not_nullptr(vk_cmd_end_debug_label);
 | 
			
		||||
		expect_not_nullptr(vk_cmd_insert_debug_label);
 | 
			
		||||
		expect_not_nullptr(vk_create_debug_messenger);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_debug_messenger);
 | 
			
		||||
		expect_not_nullptr(vk_queue_begin_debug_label);
 | 
			
		||||
		expect_not_nullptr(vk_queue_end_debug_label);
 | 
			
		||||
		expect_not_nullptr(vk_queue_insert_debug_label);
 | 
			
		||||
		expect_not_nullptr(vk_set_debug_object_name);
 | 
			
		||||
		expect_not_nullptr(vk_set_debug_object_tag);
 | 
			
		||||
		expect_not_nullptr(vk_submit_debug_message);
 | 
			
		||||
 | 
			
		||||
		expect_not_nullptr(vk_get_physical_device_surface_support);
 | 
			
		||||
		expect_not_nullptr(vk_get_physical_device_surface_capabilities);
 | 
			
		||||
		expect_not_nullptr(vk_get_physical_device_surface_formats);
 | 
			
		||||
		expect_not_nullptr(vk_create_xlib_surface_khr);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_surface_khr);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "post load device functions state is correct" } = [] {
 | 
			
		||||
		using namespace renderer::vk;
 | 
			
		||||
		expect_not_nullptr(Instance::get());
 | 
			
		||||
 | 
			
		||||
		expect_not_nullptr(vk_get_device_queue);
 | 
			
		||||
		expect_not_nullptr(vk_create_command_pool);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_command_pool);
 | 
			
		||||
		expect_not_nullptr(vk_allocate_command_buffers);
 | 
			
		||||
		expect_not_nullptr(vk_free_command_buffers);
 | 
			
		||||
		expect_not_nullptr(vk_begin_command_buffer);
 | 
			
		||||
		expect_not_nullptr(vk_end_command_buffer);
 | 
			
		||||
		expect_not_nullptr(vk_cmd_pipeline_barrier);
 | 
			
		||||
		expect_not_nullptr(vk_queue_submit);
 | 
			
		||||
		expect_not_nullptr(vk_queue_wait_idle);
 | 
			
		||||
		expect_not_nullptr(vk_device_wait_idle);
 | 
			
		||||
		expect_not_nullptr(vk_create_fence);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_fence);
 | 
			
		||||
		expect_not_nullptr(vk_wait_for_fences);
 | 
			
		||||
		expect_not_nullptr(vk_reset_fences);
 | 
			
		||||
		expect_not_nullptr(vk_create_semaphore);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_semaphore);
 | 
			
		||||
		expect_not_nullptr(vk_create_swapchain_khr);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_swapchain_khr);
 | 
			
		||||
		expect_not_nullptr(vk_get_swapchain_images_khr);
 | 
			
		||||
		expect_not_nullptr(vk_acquire_next_image_khr);
 | 
			
		||||
		expect_not_nullptr(vk_queue_present_khr);
 | 
			
		||||
		expect_not_nullptr(vk_create_image_view);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_image_view);
 | 
			
		||||
		expect_not_nullptr(vk_create_render_pass);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_render_pass);
 | 
			
		||||
		expect_not_nullptr(vk_create_frame_buffer);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_frame_buffer);
 | 
			
		||||
		expect_not_nullptr(vk_create_shader_module);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_shader_module);
 | 
			
		||||
		expect_not_nullptr(vk_create_pipeline_layout);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_pipeline_layout);
 | 
			
		||||
		expect_not_nullptr(vk_create_graphics_pipelines);
 | 
			
		||||
		expect_not_nullptr(vk_destroy_pipeline);
 | 
			
		||||
		expect_not_nullptr(vk_cmd_begin_render_pass);
 | 
			
		||||
		expect_not_nullptr(vk_cmd_end_render_pass);
 | 
			
		||||
		expect_not_nullptr(vk_cmd_bind_pipeline);
 | 
			
		||||
		expect_not_nullptr(vk_cmd_draw);
 | 
			
		||||
		expect_not_nullptr(vk_cmd_set_viewport);
 | 
			
		||||
		expect_not_nullptr(vk_cmd_set_scissors);
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,37 +0,0 @@
 | 
			
		|||
#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>();
 | 
			
		||||
 | 
			
		||||
	ensure(component.get_native_data().display, "Failed to initialize vk::Surface: null x-display");
 | 
			
		||||
	ensure(component.get_native_data().window, "Failed to initialize vk::Surface: null x-window");
 | 
			
		||||
 | 
			
		||||
	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();
 | 
			
		||||
	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()
 | 
			
		||||
{
 | 
			
		||||
	if (Instance::get())
 | 
			
		||||
	{
 | 
			
		||||
		vk_destroy_surface_khr(Instance::get(), m_surface, nullptr);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer::vk
 | 
			
		||||
| 
						 | 
				
			
			@ -1,40 +0,0 @@
 | 
			
		|||
#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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,51 +0,0 @@
 | 
			
		|||
#include <renderer/vk/context/surface.hpp>
 | 
			
		||||
#include <renderer/vk/debug/messenger.hpp>
 | 
			
		||||
#include <renderer/vk/test_utils.hpp>
 | 
			
		||||
#include <surface/components.hpp>
 | 
			
		||||
#include <surface/system.hpp>
 | 
			
		||||
#include <test/test.hpp>
 | 
			
		||||
 | 
			
		||||
using ::lt::ecs::Entity;
 | 
			
		||||
using ::lt::ecs::Registry;
 | 
			
		||||
using ::lt::renderer::vk::Surface;
 | 
			
		||||
using ::lt::surface::SurfaceComponent;
 | 
			
		||||
using ::lt::surface::System;
 | 
			
		||||
 | 
			
		||||
Suite raii = "surface"_suite = [] {
 | 
			
		||||
	Case { "happy path won't throw" } = [&] {
 | 
			
		||||
		auto observer = ValidationObserver {};
 | 
			
		||||
 | 
			
		||||
		auto registry = lt::create_ref<Registry>();
 | 
			
		||||
		auto entity = Entity { registry, registry->create_entity() };
 | 
			
		||||
		auto surface_system = System(registry);
 | 
			
		||||
 | 
			
		||||
		entity.add<SurfaceComponent>(SurfaceComponent::CreateInfo {
 | 
			
		||||
		    .title = "",
 | 
			
		||||
		    .resolution = constants::resolution,
 | 
			
		||||
		    .visible = true,
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		const auto surface = Surface { entity };
 | 
			
		||||
		const auto &[x, y] = surface.get_framebuffer_size();
 | 
			
		||||
 | 
			
		||||
		expect_eq(x, constants::resolution.x);
 | 
			
		||||
		expect_eq(y, constants::resolution.y);
 | 
			
		||||
		expect_not_nullptr(surface.vk());
 | 
			
		||||
		expect_false(observer.had_any_messages());
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "unhappy path throws" } = [&] {
 | 
			
		||||
		auto observer = ValidationObserver {};
 | 
			
		||||
		auto registry = lt::create_ref<Registry>();
 | 
			
		||||
		auto entity = Entity { registry, registry->create_entity() };
 | 
			
		||||
 | 
			
		||||
		entity.add<SurfaceComponent>(SurfaceComponent::CreateInfo {
 | 
			
		||||
		    .title = "",
 | 
			
		||||
		    .resolution = constants::resolution,
 | 
			
		||||
		    .visible = true,
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		expect_throw([&] { Surface { entity }; });
 | 
			
		||||
		expect_false(observer.had_any_messages());
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,139 +0,0 @@
 | 
			
		|||
#include <ranges>
 | 
			
		||||
#include <renderer/vk/context/device.hpp>
 | 
			
		||||
#include <renderer/vk/context/instance.hpp>
 | 
			
		||||
#include <renderer/vk/context/surface.hpp>
 | 
			
		||||
#include <renderer/vk/context/swapchain.hpp>
 | 
			
		||||
#include <renderer/vk/debug/validation.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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,38 +0,0 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory/pointer_types/null_on_move.hpp>
 | 
			
		||||
#include <renderer/vk/vulkan.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer::vk {
 | 
			
		||||
 | 
			
		||||
class Swapchain
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	Swapchain(const class Device &device, const class 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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,143 +0,0 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory/pointer_types/null_on_move.hpp>
 | 
			
		||||
#include <renderer/vk/context/instance.hpp>
 | 
			
		||||
#include <renderer/vk/vulkan.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer::vk {
 | 
			
		||||
 | 
			
		||||
class Messenger
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	// NOLINTNEXTLINE(performance-enum-size)
 | 
			
		||||
	enum Severity : decltype(std::to_underlying(
 | 
			
		||||
	    VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT
 | 
			
		||||
	))
 | 
			
		||||
	{
 | 
			
		||||
		verbose = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
 | 
			
		||||
		info = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT,
 | 
			
		||||
		warning = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT,
 | 
			
		||||
		error = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
 | 
			
		||||
 | 
			
		||||
		all_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
 | 
			
		||||
		               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
 | 
			
		||||
		               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
 | 
			
		||||
		               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// NOLINTNEXTLINE(performance-enum-size)
 | 
			
		||||
	enum Type : decltype(std::to_underlying(VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT))
 | 
			
		||||
	{
 | 
			
		||||
		general = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT,
 | 
			
		||||
		validation = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
 | 
			
		||||
		performance = VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
 | 
			
		||||
 | 
			
		||||
		// address_binding = VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT,
 | 
			
		||||
 | 
			
		||||
		/** @note: does not include address binding yet. */
 | 
			
		||||
		all_type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
 | 
			
		||||
		           | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
 | 
			
		||||
		           | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	using CallbackData_T = const VkDebugUtilsMessengerCallbackDataEXT *;
 | 
			
		||||
 | 
			
		||||
	using Callback_T = std::function<void(
 | 
			
		||||
	    Severity message_severity,
 | 
			
		||||
	    Type message_type,
 | 
			
		||||
	    CallbackData_T vulkan_data,
 | 
			
		||||
	    void *user_data
 | 
			
		||||
	)>;
 | 
			
		||||
 | 
			
		||||
	struct CreateInfo
 | 
			
		||||
	{
 | 
			
		||||
		Severity severity;
 | 
			
		||||
 | 
			
		||||
		Type type;
 | 
			
		||||
 | 
			
		||||
		Callback_T callback;
 | 
			
		||||
 | 
			
		||||
		void *user_data;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	explicit Messenger(CreateInfo info)
 | 
			
		||||
	    : m_callback(std::move(info.callback))
 | 
			
		||||
	    , m_user_data(info.user_data)
 | 
			
		||||
	{
 | 
			
		||||
		ensure(m_callback, "Failed to initialize vk::Messenger: null callback");
 | 
			
		||||
		ensure(info.severity != Severity {}, "Failed to initialize vk::Messenger: null severity");
 | 
			
		||||
		ensure(info.type != Type {}, "Failed to initialize vk::Messenger: null type");
 | 
			
		||||
 | 
			
		||||
		// Instance may not be initialized yet
 | 
			
		||||
		// Making it get initialized inside a call to vk_create_debug_messenger
 | 
			
		||||
		// Which would invoke a nullptr...
 | 
			
		||||
		if (!vk_create_debug_messenger)
 | 
			
		||||
		{
 | 
			
		||||
			Instance::get();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const auto vulkan_info = VkDebugUtilsMessengerCreateInfoEXT {
 | 
			
		||||
			.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
 | 
			
		||||
			.messageSeverity = info.severity,
 | 
			
		||||
			.messageType = info.type,
 | 
			
		||||
			.pfnUserCallback = &validation_layers_callback,
 | 
			
		||||
			.pUserData = this,
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		ensure(
 | 
			
		||||
		    !vk_create_debug_messenger(Instance::get(), &vulkan_info, nullptr, &m_debug_messenger),
 | 
			
		||||
		    "Failed to create vulkan debug utils messenger"
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	~Messenger()
 | 
			
		||||
	{
 | 
			
		||||
		vk_destroy_debug_messenger(Instance::get(), m_debug_messenger, nullptr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Messenger(Messenger &&) = default;
 | 
			
		||||
 | 
			
		||||
	Messenger(const Messenger &) = delete;
 | 
			
		||||
 | 
			
		||||
	auto operator=(Messenger &&) -> Messenger & = default;
 | 
			
		||||
 | 
			
		||||
	auto operator=(const Messenger &) const -> Messenger & = delete;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	static auto validation_layers_callback(
 | 
			
		||||
	    VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity,
 | 
			
		||||
	    VkDebugUtilsMessageTypeFlagsEXT const message_type,
 | 
			
		||||
	    VkDebugUtilsMessengerCallbackDataEXT const *const callback_data,
 | 
			
		||||
	    void *const vulkan_user_data
 | 
			
		||||
	) -> VkBool32
 | 
			
		||||
	{
 | 
			
		||||
		auto *messenger = (Messenger *)vulkan_user_data; // NOLINT
 | 
			
		||||
		ensure(messenger, "Null vulkan_user_data received in messenger callback");
 | 
			
		||||
 | 
			
		||||
		messenger->validation_layers_callback_impl(message_severity, message_type, callback_data);
 | 
			
		||||
		return VK_FALSE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void validation_layers_callback_impl(
 | 
			
		||||
	    VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
 | 
			
		||||
	    VkDebugUtilsMessageTypeFlagsEXT message_type,
 | 
			
		||||
	    const VkDebugUtilsMessengerCallbackDataEXT *callback_data
 | 
			
		||||
	)
 | 
			
		||||
	{
 | 
			
		||||
		m_callback(
 | 
			
		||||
		    static_cast<Severity>(message_severity),
 | 
			
		||||
		    static_cast<Type>(message_type),
 | 
			
		||||
		    callback_data,
 | 
			
		||||
		    m_user_data
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memory::NullOnMove<VkDebugUtilsMessengerEXT> m_debug_messenger = VK_NULL_HANDLE;
 | 
			
		||||
 | 
			
		||||
	Callback_T m_callback;
 | 
			
		||||
 | 
			
		||||
	void *m_user_data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer::vk
 | 
			
		||||
| 
						 | 
				
			
			@ -1,149 +0,0 @@
 | 
			
		|||
#include <renderer/vk/context/surface.hpp>
 | 
			
		||||
#include <renderer/vk/debug/messenger.hpp>
 | 
			
		||||
#include <renderer/vk/test_utils.hpp>
 | 
			
		||||
#include <surface/components.hpp>
 | 
			
		||||
#include <surface/system.hpp>
 | 
			
		||||
#include <test/expects.hpp>
 | 
			
		||||
#include <test/test.hpp>
 | 
			
		||||
 | 
			
		||||
using namespace lt;
 | 
			
		||||
using renderer::vk::Messenger;
 | 
			
		||||
using renderer::vk::Surface;
 | 
			
		||||
using enum Messenger::Severity;
 | 
			
		||||
using enum Messenger::Type;
 | 
			
		||||
 | 
			
		||||
void noop_callback(
 | 
			
		||||
    Messenger::Severity message_severity,
 | 
			
		||||
    Messenger::Type message_type,
 | 
			
		||||
    Messenger::CallbackData_T vulkan_data,
 | 
			
		||||
    void *user_data
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Suite raii = "messenger_raii"_suite = [] {
 | 
			
		||||
	Case { "happy path won't throw" } = [] {
 | 
			
		||||
		Messenger(
 | 
			
		||||
		    Messenger::CreateInfo {
 | 
			
		||||
		        .severity = all_severity,
 | 
			
		||||
		        .type = all_type,
 | 
			
		||||
		        .callback = &noop_callback,
 | 
			
		||||
		    }
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "unhappy path throws" } = [] {
 | 
			
		||||
		expect_throw([] {
 | 
			
		||||
			Messenger(
 | 
			
		||||
			    Messenger::CreateInfo {
 | 
			
		||||
			        .severity = all_severity,
 | 
			
		||||
			        .type = all_type,
 | 
			
		||||
			        .callback = {},
 | 
			
		||||
			    }
 | 
			
		||||
			);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		expect_throw([] {
 | 
			
		||||
			Messenger(
 | 
			
		||||
			    Messenger::CreateInfo {
 | 
			
		||||
			        .severity = {},
 | 
			
		||||
			        .type = all_type,
 | 
			
		||||
			        .callback = &noop_callback,
 | 
			
		||||
			    }
 | 
			
		||||
			);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		expect_throw([] {
 | 
			
		||||
			Messenger(
 | 
			
		||||
			    Messenger::CreateInfo {
 | 
			
		||||
			        .severity = all_severity,
 | 
			
		||||
			        .type = {},
 | 
			
		||||
			        .callback = &noop_callback,
 | 
			
		||||
			    }
 | 
			
		||||
			);
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// NOLINTNEXTLINE(cppcoreguidelines-interfaces-global-init)
 | 
			
		||||
Suite callback = "messenger_callback"_suite = [] {
 | 
			
		||||
	Case { "callback gets called with correct arguments" } = [] {
 | 
			
		||||
		auto total_messages = 0u;
 | 
			
		||||
		auto first_message_is_severity = false;
 | 
			
		||||
		auto second_message_is_type = false;
 | 
			
		||||
		auto third_message_is_user_callback = false;
 | 
			
		||||
		auto all_messages_are_error = true;
 | 
			
		||||
		auto all_messages_are_validation = true;
 | 
			
		||||
		auto user_data_is_always_69 = true;
 | 
			
		||||
 | 
			
		||||
		auto callback = [&](Messenger::Severity message_severity,
 | 
			
		||||
		                    Messenger::Type message_type,
 | 
			
		||||
		                    Messenger::CallbackData_T vulkan_data,
 | 
			
		||||
		                    void *user_data) {
 | 
			
		||||
			++total_messages;
 | 
			
		||||
			auto message = std::string_view { vulkan_data->pMessage };
 | 
			
		||||
 | 
			
		||||
			// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
 | 
			
		||||
			user_data_is_always_69 = user_data_is_always_69 && *(size_t *)user_data == 69u;
 | 
			
		||||
 | 
			
		||||
			all_messages_are_validation = all_messages_are_validation && message_type == validation;
 | 
			
		||||
			all_messages_are_error = all_messages_are_error && message_severity == error;
 | 
			
		||||
 | 
			
		||||
			if (total_messages == 1)
 | 
			
		||||
			{
 | 
			
		||||
				first_message_is_severity = message.starts_with(
 | 
			
		||||
				    "vkCreateDebugUtilsMessengerEXT(): pCreateInfo->messageSeverity is zero."
 | 
			
		||||
				);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			if (total_messages == 2)
 | 
			
		||||
			{
 | 
			
		||||
				second_message_is_type = message.starts_with(
 | 
			
		||||
				    "vkCreateDebugUtilsMessengerEXT(): pCreateInfo->messageType is zero."
 | 
			
		||||
				);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (total_messages == 3)
 | 
			
		||||
			{
 | 
			
		||||
				third_message_is_user_callback = message.starts_with(
 | 
			
		||||
				    "vkCreateDebugUtilsMessengerEXT(): pCreateInfo->pfnUserCallback is NULL."
 | 
			
		||||
				);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		auto user_data = size_t { 69 };
 | 
			
		||||
		auto messenger = Messenger(
 | 
			
		||||
		    Messenger::CreateInfo {
 | 
			
		||||
		        .severity = all_severity,
 | 
			
		||||
		        .type = all_type,
 | 
			
		||||
		        .callback = callback,
 | 
			
		||||
		        .user_data = &user_data,
 | 
			
		||||
		    }
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			auto *messenger = VkDebugUtilsMessengerEXT {};
 | 
			
		||||
			auto info = VkDebugUtilsMessengerCreateInfoEXT {
 | 
			
		||||
				.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
 | 
			
		||||
			};
 | 
			
		||||
			renderer::vk::vk_create_debug_messenger(
 | 
			
		||||
			    renderer::vk::Instance::get(),
 | 
			
		||||
			    &info,
 | 
			
		||||
			    nullptr,
 | 
			
		||||
			    &messenger
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		expect_eq(total_messages, 3u);
 | 
			
		||||
		expect_true(first_message_is_severity);
 | 
			
		||||
		expect_true(second_message_is_type);
 | 
			
		||||
		expect_true(third_message_is_user_callback);
 | 
			
		||||
		expect_true(all_messages_are_error);
 | 
			
		||||
		expect_true(all_messages_are_validation);
 | 
			
		||||
		expect_true(user_data_is_always_69);
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,17 +0,0 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <renderer/vk/vulkan.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::renderer::vk {
 | 
			
		||||
 | 
			
		||||
inline void vkc(VkResult result)
 | 
			
		||||
{
 | 
			
		||||
	if (result)
 | 
			
		||||
	{
 | 
			
		||||
		throw std::runtime_error {
 | 
			
		||||
			std::format("Vulkan call failed with result: {}", std::to_underlying(result))
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt::renderer::vk
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		
		Reference in a new issue