feat(renderer): wip hello triangle

This commit is contained in:
light7734 2025-09-22 18:50:17 +03:30
parent dd0f8ebf0a
commit a58b0c030f
Signed by: light7734
GPG key ID: 8C30176798F1A6BA
85 changed files with 484 additions and 148 deletions

View file

@ -1,45 +1,17 @@
add_library_module(renderer add_library_module(renderer
system.cpp system.cpp
blender.cpp vk/context.cpp
buffers.cpp
framebuffer.cpp
graphics_context.cpp
render_command.cpp
renderer.cpp
shader.cpp
texture.cpp
vertex_layout.cpp
programs/quad.cpp
programs/texture.cpp
programs/tinted_texture.cpp
gl/blender.cpp
gl/buffers.cpp
gl/framebuffers.cpp
gl/graphics_context.cpp
gl/render_command.cpp
gl/shader.cpp
gl/texture.cpp
gl/vertex_layout.cpp
vk/instance.cpp
) )
target_link_libraries( target_link_libraries(renderer
renderer PUBLIC
PUBLIC camera app
PUBLIC input ecs
PUBLIC logger vulkan
PUBLIC imgui memory
PUBLIC asset_parser surface
PRIVATE lt_debug
PRIVATE window
PUBLIC vulkan
) )
add_test_module(renderer add_test_module(renderer
system.test.cpp system.test.cpp
) )
target_link_libraries(
renderer_tests
PRIVATE lt_debug
PRIVATE window
)

View 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

View 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>(),
} };
}
};
};

View 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

View file

@ -1,17 +1,5 @@
#include <lt_debug/assertions.hpp>
#include <renderer/system.hpp> #include <renderer/system.hpp>
namespace lt::renderer { 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

View file

@ -1,44 +1,123 @@
#include <ranges> #include <ranges>
#include <renderer/system.hpp> #include <renderer/system.hpp>
#include <surface/system.hpp>
#include <test/test.hpp> #include <test/test.hpp>
#include <window/window.hpp>
using namespace lt; using namespace lt;
using std::ignore;
using test::Case;
using test::expect_eq;
using test::expect_ne;
using test::expect_not_nullptr;
using test::expect_throw;
using test::expect_true;
using test::Suite;
using lt::test::Case; using renderer::System;
using lt::test::Suite;
constexpr auto resolution = math::uvec2 { 800, 600 };
struct SurfaceContext
{
surface::System system;
ecs::Entity entity;
};
struct RendererContext
{
Ref<ecs::Registry> registry;
System system;
};
[[nodiscard]] auto create_surface() -> SurfaceContext
{
using surface::SurfaceComponent;
auto surface_registry = create_ref<ecs::Registry>();
auto surface_entity = surface_registry->create_entity();
auto surface_system = surface::System(surface_registry);
surface_registry->add<SurfaceComponent>(
surface_entity,
SurfaceComponent::CreateInfo {
.title = "",
.resolution = resolution,
}
);
return {
.system = std::move(surface_system),
.entity = ecs::Entity { surface_registry, surface_entity },
};
}
[[nodiscard]] auto create_system() -> std::pair<RendererContext, SurfaceContext>
{
auto surface_context = create_surface();
auto &[surface_system, surface_entity] = surface_context;
auto registry = create_ref<ecs::Registry>();
auto stats = create_ref<app::SystemStats>();
return {
RendererContext {
.registry = registry,
.system = System(
{
.registry = registry,
.surface_entity = surface_entity,
.system_stats = stats,
}
),
},
std::move(surface_context),
};
}
Suite raii = [] { Suite raii = [] {
using lt::test::expect_true; Case { "happy path won't throw" } = [&] {
using lt::test::expect_throw; std::ignore = create_system();
using renderer::System;
Case { "happy" } = [=] {
std::ignore = System { {
.registry = create_ref<ecs::Registry>(),
} };
}; };
Case { "unhappy" } = [=] { Case { "happy path has no validation errors" } = [&] {
expect_throw([=] { auto [renderer, surface] = create_system();
std::ignore = System { { expect_true(renderer.system.get_stats().empty_diagnosis());
.registry = {},
} };
});
expect_throw([=] {
std::ignore = System { {
.registry = create_ref<ecs::Registry>(),
} };
});
}; };
Case { "plenty" } = [=] { Case { "unhappy path throws" } = [] {
for (auto idx : std::views::iota(0, 100'001)) 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([&] {
std::ignore = System(
{ {
std::ignore = System { { .registry = {},
.registry = create_ref<ecs::Registry>(), .surface_entity = surface_entity,
} }; .system_stats = stats,
} }
);
});
expect_throw([&] {
std::ignore = System(
System::CreateInfo {
.registry = surface_entity.get_registry(),
.surface_entity = empty_entity,
.system_stats = stats,
}
);
});
expect_throw([&] {
std::ignore = System(
System::CreateInfo {
.registry = surface_entity.get_registry(),
.surface_entity = surface_entity,
.system_stats = {},
}
);
});
}; };
}; };

View file

@ -1,4 +1,4 @@
#include <renderer/vk/backend.hpp> #include <renderer/vk/context.hpp>
#if defined(_WIN32) #if defined(_WIN32)
@ -83,10 +83,16 @@ PFN_vkCmdBindPipeline vk_cmd_bind_pipeline;
PFN_vkCmdDraw vk_cmd_draw; PFN_vkCmdDraw vk_cmd_draw;
PFN_vkCmdSetViewport vk_cmd_set_viewport; PFN_vkCmdSetViewport vk_cmd_set_viewport;
PFN_vkCmdSetScissor vk_cmd_set_scissors; PFN_vkCmdSetScissor vk_cmd_set_scissors;
PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
Backend::Backend() 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_library();
load_global_functions(); load_global_functions();
@ -100,16 +106,31 @@ Backend::Backend()
load_device_functions(); load_device_functions();
initialize_queue(); initialize_queue();
const auto &component = surface_entity.get<surface::SurfaceComponent>();
auto xlib_surface_create_info = VkXlibSurfaceCreateInfoKHR {
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
.dpy = component.get_native_data().display,
.window = component.get_native_data().window,
};
vk_create_xlib_surface_khr(m_instance, &xlib_surface_create_info, nullptr, &m_surface);
} }
Backend::~Backend() Context::~Context()
{ {
vk_destroy_device(m_device, 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); vk_destroy_debug_messenger(m_instance, m_debug_messenger, nullptr);
}
vk_destroy_instance(m_instance, nullptr); vk_destroy_instance(m_instance, nullptr);
} }
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char * auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *
{ {
if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
@ -132,14 +153,17 @@ auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const
return "PERFORMANCE"; return "PERFORMANCE";
} }
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity) -> LogLvl auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
-> app::SystemDiagnosis::Severity
{ {
using enum app::SystemDiagnosis::Severity;
switch (message_severity) switch (message_severity)
{ {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return LogLvl::trace; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return verbose;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return LogLvl::info; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return info;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return LogLvl::warn; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return warning;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return LogLvl::error; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return error;
default: ensure(false, "Invalid message severity: {}", static_cast<int>(message_severity)); default: ensure(false, "Invalid message severity: {}", static_cast<int>(message_severity));
} }
@ -153,16 +177,34 @@ auto validation_layers_callback(
void *const vulkan_user_data void *const vulkan_user_data
) -> VkBool32 ) -> VkBool32
{ {
std::ignore = vulkan_user_data; auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT
const auto &type = parse_message_type(message_types); const auto &type = parse_message_type(message_types);
const auto level = parse_message_severity(message_severity);
Logger::log(level, ":: <{}> :: {}", type, callback_data->pMessage); 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); return static_cast<VkBool32>(VK_FALSE);
} }
void Backend::initialize_instance() void Context::initialize_instance()
{ {
auto app_info = VkApplicationInfo { auto app_info = VkApplicationInfo {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
@ -198,10 +240,10 @@ void Backend::initialize_instance()
auto extensions = std::vector<VkExtensionProperties>(count); auto extensions = std::vector<VkExtensionProperties>(count);
vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data()); vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data());
log_inf("Available vulkan instance extensions:"); // log_inf("Available vulkan instance extensions:");
for (auto &ext : extensions) for (auto &ext : extensions)
{ {
log_inf("\t{} @ {}", ext.extensionName, ext.specVersion); // log_inf("\t{} @ {}", ext.extensionName, ext.specVersion);
} }
} }
@ -210,7 +252,7 @@ void Backend::initialize_instance()
ensure(m_instance, "Failed to create vulkan instance"); ensure(m_instance, "Failed to create vulkan instance");
} }
void Backend::initialize_debug_messenger() void Context::initialize_debug_messenger()
{ {
const auto info = VkDebugUtilsMessengerCreateInfoEXT { const auto info = VkDebugUtilsMessengerCreateInfoEXT {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
@ -226,6 +268,8 @@ void Backend::initialize_debug_messenger()
.pfnUserCallback = &validation_layers_callback, .pfnUserCallback = &validation_layers_callback,
.pUserData = &m_stats,
}; };
ensure( ensure(
@ -234,7 +278,7 @@ void Backend::initialize_debug_messenger()
); );
} }
void Backend::initialize_physical_device() void Context::initialize_physical_device()
{ {
auto count = 0u; auto count = 0u;
vk_enumerate_physical_devices(m_instance, &count, nullptr); vk_enumerate_physical_devices(m_instance, &count, nullptr);
@ -260,7 +304,7 @@ void Backend::initialize_physical_device()
ensure(m_physical_device, "Failed to find any suitable Vulkan physical device"); ensure(m_physical_device, "Failed to find any suitable Vulkan physical device");
} }
void Backend::initialize_logical_device() void Context::initialize_logical_device()
{ {
const float priorities = .0f; const float priorities = .0f;
@ -292,12 +336,12 @@ void Backend::initialize_logical_device()
); );
} }
void Backend::initialize_queue() void Context::initialize_queue()
{ {
vk_get_device_queue(m_device, find_suitable_queue_family(), 0, &m_queue); vk_get_device_queue(m_device, find_suitable_queue_family(), 0, &m_queue);
} }
void Backend::load_library() void Context::load_library()
{ {
library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
ensure(library, "Failed to dlopen libvulkan.so"); ensure(library, "Failed to dlopen libvulkan.so");
@ -309,13 +353,13 @@ void Backend::load_library()
ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr"); ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr");
} }
void Backend::load_global_functions() void Context::load_global_functions()
{ {
constexpr auto load_fn = []<typename T>(T &pfn, const char *fn_name) { constexpr auto load_fn = []<typename T>(T &pfn, const char *fn_name) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
pfn = reinterpret_cast<T>(vk_get_instance_proc_address(nullptr, fn_name)); pfn = reinterpret_cast<T>(vk_get_instance_proc_address(nullptr, fn_name));
ensure(pfn, "Failed to load vulkan global function: {}", fn_name); ensure(pfn, "Failed to load vulkan global function: {}", fn_name);
log_trc("Loaded global function: {}", fn_name); // log_trc("Loaded global function: {}", fn_name);
}; };
load_fn(vk_create_instance, "vkCreateInstance"); load_fn(vk_create_instance, "vkCreateInstance");
@ -323,13 +367,13 @@ void Backend::load_global_functions()
load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties"); load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties");
} }
void Backend::load_instance_functions() void Context::load_instance_functions()
{ {
const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) { const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
pfn = reinterpret_cast<T>(vk_get_instance_proc_address(m_instance, fn_name)); pfn = reinterpret_cast<T>(vk_get_instance_proc_address(m_instance, fn_name));
ensure(pfn, "Failed to load vulkan instance function: {}", fn_name); ensure(pfn, "Failed to load vulkan instance function: {}", fn_name);
log_trc("Loaded instance function: {}", fn_name); // log_trc("Loaded instance function: {}", fn_name);
}; };
load_fn(vk_destroy_instance, "vkDestroyInstance"); load_fn(vk_destroy_instance, "vkDestroyInstance");
@ -356,15 +400,18 @@ void Backend::load_instance_functions()
load_fn(vk_set_debug_object_name, "vkSetDebugUtilsObjectNameEXT"); load_fn(vk_set_debug_object_name, "vkSetDebugUtilsObjectNameEXT");
load_fn(vk_set_debug_object_tag, "vkSetDebugUtilsObjectTagEXT"); load_fn(vk_set_debug_object_tag, "vkSetDebugUtilsObjectTagEXT");
load_fn(vk_submit_debug_message, "vkSubmitDebugUtilsMessageEXT"); load_fn(vk_submit_debug_message, "vkSubmitDebugUtilsMessageEXT");
load_fn(vk_create_xlib_surface_khr, "vkCreateXlibSurfaceKHR");
load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR");
} }
void Backend::load_device_functions() void Context::load_device_functions()
{ {
const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) { const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
pfn = reinterpret_cast<T>(vk_get_device_proc_address(m_device, fn_name)); pfn = reinterpret_cast<T>(vk_get_device_proc_address(m_device, fn_name));
ensure(pfn, "Failed to load vulkan device function: {}", fn_name); ensure(pfn, "Failed to load vulkan device function: {}", fn_name);
log_trc("Loaded device function: {}", fn_name); // log_trc("Loaded device function: {}", fn_name);
}; };
load_fn(vk_get_device_queue, "vkGetDeviceQueue"); load_fn(vk_get_device_queue, "vkGetDeviceQueue");
@ -409,7 +456,7 @@ void Backend::load_device_functions()
load_fn(vk_cmd_set_scissors, "vkCmdSetScissor"); load_fn(vk_cmd_set_scissors, "vkCmdSetScissor");
} }
[[nodiscard]] auto Backend::find_suitable_queue_family() const -> uint32_t [[nodiscard]] auto Context::find_suitable_queue_family() const -> uint32_t
{ {
auto count = 0u; auto count = 0u;
vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr); vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);

View file

@ -1,11 +1,20 @@
#pragma once #pragma once
#define VK_NO_PROTOTYPES #define VK_NO_PROTOTYPES
#include <renderer/backend.hpp> #define VK_USE_PLATFORM_XLIB_KHR
#include <vulkan/vulkan.h> #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 { namespace lt::renderer::vk {
using memory::NullOnMove;
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
// global functions // global functions
extern PFN_vkGetInstanceProcAddr vk_get_instance_proc_address; extern PFN_vkGetInstanceProcAddr vk_get_instance_proc_address;
@ -78,26 +87,54 @@ extern PFN_vkCmdBindPipeline vk_cmd_bind_pipeline;
extern PFN_vkCmdDraw vk_cmd_draw; extern PFN_vkCmdDraw vk_cmd_draw;
extern PFN_vkCmdSetViewport vk_cmd_set_viewport; extern PFN_vkCmdSetViewport vk_cmd_set_viewport;
extern PFN_vkCmdSetScissor vk_cmd_set_scissors; extern PFN_vkCmdSetScissor vk_cmd_set_scissors;
extern PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
extern PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
class Backend: public ::lt::renderer::Backend class Context
{ {
public: public:
Backend(); Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats);
Backend(Backend &&) = default; ~Context();
auto operator=(Backend &&) -> Backend & = default; Context(Context &&other) noexcept = default;
Backend(const Backend &) = delete; auto operator=(Context &&other) noexcept -> Context & = default;
auto operator=(const Backend &) -> Backend & = delete; Context(const Context &) = delete;
~Backend() override; auto operator=(const Context &) -> Context & = delete;
[[nodiscard]] constexpr auto get_api() const -> API override [[nodiscard]] auto instance() -> VkInstance
{ {
return API::vulkan; 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: private:
@ -121,15 +158,23 @@ private:
[[nodiscard]] auto find_suitable_queue_family() const -> uint32_t; [[nodiscard]] auto find_suitable_queue_family() const -> uint32_t;
VkInstance m_instance {}; Ref<ecs::Registry> m_registry;
VkPhysicalDevice m_physical_device {}; NullOnMove<VkInstance> m_instance = VK_NULL_HANDLE;
VkDevice m_device {}; NullOnMove<VkPhysicalDevice> m_physical_device = VK_NULL_HANDLE;
VkQueue m_queue {}; NullOnMove<VkDevice> m_device = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT m_debug_messenger {}; NullOnMove<VkQueue> m_queue = VK_NULL_HANDLE;
NullOnMove<VkSwapchainKHR> m_swapcha = VK_NULL_HANDLE;
NullOnMove<VkDebugUtilsMessengerEXT> m_debug_messenger = VK_NULL_HANDLE;
NullOnMove<VkSurfaceKHR> m_surface = VK_NULL_HANDLE;
Ref<app::SystemStats> m_stats;
}; };
} // namespace lt::renderer::vk } // namespace lt::renderer::vk

View file

@ -0,0 +1,26 @@
#pragma once
#define VK_NO_PROTOTYPES
#define VK_USE_PLATFORM_XLIB_KHR
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_xlib.h>
//
#include <ecs/entity.hpp>
namespace lt::renderer::vk {
class Surface
{
public:
Surface(ecs::Entity entity)
{
}
~Surface();
private:
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
};
} // namespace lt::renderer::vk

View file

@ -0,0 +1,22 @@
#pragma once
#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>
namespace lt::renderer::vk {
class Swapchain
{
public:
Swapchain()
{
}
private:
VkSwapchainKHR m_swapchain {};
std::vector<VkImage> images;
std::vector<VkImageView> image_views;
};
} // namespace lt::renderer::vk

View file

@ -1,41 +1,30 @@
#pragma once #pragma once
#include <base/base.hpp> #include <app/system.hpp>
#include <ecs/registry.hpp> #include <ecs/entity.hpp>
#include <renderer/validation.hpp>
#include <renderer/vk/context.hpp>
namespace lt::renderer { namespace lt::renderer {
/** The system for putting gore on your display class System: app::ISystem
*
* Exclusively operates on components:
* - RendererComponent
* - PostEffectsComponent
* - UserInterfaceComponent
*
* Requires read acces on components:
* - TransformComponent
*/
class System
{ {
public: public:
/** The configurations of this system. */ struct CreateInfo
struct Properties
{
};
/** The requirements for this system to initialize. */
struct InitRequirements
{ {
Ref<ecs::Registry> registry; Ref<ecs::Registry> registry;
ecs::Entity surface_entity;
Ref<app::SystemStats> system_stats;
}; };
/** The requirements for this system to tick. */ [[nodiscard]] System(CreateInfo info)
struct TickRequirements : m_registry(std::move(info.registry))
, m_context(info.surface_entity, std::move(info.system_stats))
{ {
double delta_time; ensure(m_registry, "Failed to initialize renderer system: null registry");
}; }
[[nodiscard]] System(InitRequirements requirements); ~System() override = default;
System(System &&) = default; System(System &&) = default;
@ -45,12 +34,38 @@ public:
auto operator=(const System &) -> System & = delete; auto operator=(const System &) -> System & = delete;
~System(); void on_register() override
{
}
void tick(TickRequirements requirements); void on_unregister() override
{
}
void get_validation_state();
void tick(app::TickInfo tick) override
{
}
[[nodiscard]] auto get_stats() const -> const app::SystemStats &
{
return m_context.get_stats();
}
[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override
{
return m_last_tick_result;
}
private: private:
Ref<ecs::Registry> m_registry; Ref<ecs::Registry> m_registry;
renderer::Validation m_validation;
vk::Context m_context;
app::TickResult m_last_tick_result {};
}; };
} // namespace lt::renderer } // namespace lt::renderer

View file

@ -0,0 +1,25 @@
#pragma once
namespace lt::renderer {
enum class Severity : uint8_t
{
off,
very_verbose,
verbose,
info,
warning,
error,
critical,
};
class Validation
{
public:
void push_diagnosis();
private:
};
} // namespace lt::renderer