Compare commits

..

No commits in common. "80639033443e434f79abb7396e1ea7e30417293f" and "dd0f8ebf0a362712352fa0c909752cd282798806" have entirely different histories.

104 changed files with 237 additions and 904 deletions

View file

@ -1,6 +1,5 @@
# engine # engine
add_subdirectory(./base) add_subdirectory(./base)
add_subdirectory(./memory)
add_subdirectory(./time) add_subdirectory(./time)
add_subdirectory(./logger) add_subdirectory(./logger)
add_subdirectory(./debug) add_subdirectory(./debug)
@ -15,7 +14,7 @@ add_subdirectory(./input)
# add_subdirectory(./ui) # add_subdirectory(./ui)
# #
add_subdirectory(./surface) add_subdirectory(./surface)
add_subdirectory(./renderer) # add_subdirectory(./renderer)
add_subdirectory(./ecs) add_subdirectory(./ecs)
# #
add_subdirectory(./app) add_subdirectory(./app)

View file

@ -9,16 +9,10 @@ void Application::game_loop()
{ {
for (auto &system : m_systems) for (auto &system : m_systems)
{ {
const auto &last_tick = system->get_last_tick_result(); if (system->tick())
const auto now = std::chrono::steady_clock::now(); {
return;
system->tick(
TickInfo {
.delta_time = now - last_tick.end_time,
.budget = std::chrono::milliseconds { 10 },
.start_time = now,
} }
);
} }
for (auto &system : m_systems_to_be_registered) for (auto &system : m_systems_to_be_registered)

View file

@ -1,89 +1,7 @@
#pragma once #pragma once
#include <chrono>
namespace lt::app { namespace lt::app {
/** Information required to tick a system.
* @note May be used across an entire application-frame (consisting of multiple systems ticking)
*/
struct TickInfo
{
using Timepoint_T = std::chrono::time_point<std::chrono::steady_clock>;
using Duration_T = std::chrono::duration<double>;
/** Duration since previous tick's end_time to current tick's start_time. */
Duration_T delta_time {};
/** Maximum duration the system is expected to finish ticking in.
*
* if end_time - start_time > budget -> the system exceeded its ticking budget.
* else end_time - start_time < budget -> the system ticked properly.
*
* In other words, end_time is expected to be less than start_time + budget.
*/
Duration_T budget {};
/** Exact time which ticking started. */
Timepoint_T start_time;
};
/** Information about how a system's tick performed */
struct TickResult
{
using Timepoint_T = std::chrono::time_point<std::chrono::steady_clock>;
using Duration_T = std::chrono::duration<double>;
/** The info supplied to the system for ticking. */
TickInfo info;
/** Equivalent to end_time - info.start_time. */
Duration_T duration {};
/** Exact time which ticking ended. */
Timepoint_T end_time;
};
struct SystemDiagnosis
{
enum class Severity : uint8_t
{
verbose,
info,
warning,
error,
fatal,
};
std::string message;
std::string code;
Severity severity;
};
class SystemStats
{
public:
void push_diagnosis(SystemDiagnosis &&diagnosis)
{
auto diag = m_diagnosis.emplace_back(std::move(diagnosis));
log_dbg("message: {}", diag.message);
}
[[nodiscard]] auto empty_diagnosis() const -> bool
{
return m_diagnosis.empty();
}
private:
std::vector<SystemDiagnosis> m_diagnosis;
};
class ISystem class ISystem
{ {
public: public:
@ -103,9 +21,7 @@ public:
virtual void on_unregister() = 0; virtual void on_unregister() = 0;
virtual void tick(TickInfo tick) = 0; virtual auto tick() -> bool = 0;
[[nodiscard]] virtual auto get_last_tick_result() const -> const TickResult & = 0;
}; };
} // namespace lt::app } // namespace lt::app

View file

@ -12,7 +12,7 @@ using lt::test::expect_eq;
using lt::test::expect_false; using lt::test::expect_false;
using lt::test::expect_true; using lt::test::expect_true;
using lt::ecs::EntityId; using lt::ecs::Entity;
using lt::ecs::Registry; using lt::ecs::Registry;
struct Component struct Component
@ -86,7 +86,7 @@ Suite raii = [] {
Suite entity_raii = [] { Suite entity_raii = [] {
Case { "create_entity returns unique values" } = [] { Case { "create_entity returns unique values" } = [] {
auto registry = Registry {}; auto registry = Registry {};
auto set = std::unordered_set<EntityId> {}; auto set = std::unordered_set<Entity> {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
@ -101,7 +101,7 @@ Suite entity_raii = [] {
Case { "post create/destroy_entity has correct state" } = [] { Case { "post create/destroy_entity has correct state" } = [] {
auto registry = Registry {}; auto registry = Registry {};
auto entities = std::vector<EntityId> {}; auto entities = std::vector<Entity> {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
entities.emplace_back(registry.create_entity()); entities.emplace_back(registry.create_entity());
@ -154,18 +154,14 @@ Suite component_raii = [] {
Suite callbacks = [] { Suite callbacks = [] {
Case { "connecting on_construct/on_destruct won't throw" } = [] { Case { "connecting on_construct/on_destruct won't throw" } = [] {
auto registry = Registry {}; auto registry = Registry {};
registry.connect_on_construct<Component>([&](Registry &, EntityId) {}); registry.connect_on_construct<Component>([&](Registry &, Entity) {});
registry.connect_on_destruct<Component>([&](Registry &, EntityId) {}); registry.connect_on_destruct<Component>([&](Registry &, Entity) {});
}; };
Case { "on_construct/on_destruct won't get called on unrelated component" } = [] { Case { "on_construct/on_destruct won't get called on unrelated component" } = [] {
auto registry = Registry {}; auto registry = Registry {};
registry.connect_on_construct<Component>([&](Registry &, EntityId) { registry.connect_on_construct<Component>([&](Registry &, Entity) { expect_unreachable(); });
expect_unreachable(); registry.connect_on_destruct<Component>([&](Registry &, Entity) { expect_unreachable(); });
});
registry.connect_on_destruct<Component>([&](Registry &, EntityId) {
expect_unreachable();
});
for (auto idx : std::views::iota(0, 100'000)) for (auto idx : std::views::iota(0, 100'000))
{ {
@ -175,14 +171,14 @@ Suite callbacks = [] {
Case { "on_construct/on_destruct gets called" } = [] { Case { "on_construct/on_destruct gets called" } = [] {
auto registry = Registry {}; auto registry = Registry {};
auto all_entities = std::vector<EntityId> {}; auto all_entities = std::vector<Entity> {};
auto on_construct_called = std::vector<EntityId> {}; auto on_construct_called = std::vector<Entity> {};
auto on_destruct_called = std::vector<EntityId> {}; auto on_destruct_called = std::vector<Entity> {};
registry.connect_on_construct<Component>([&](Registry &, EntityId entity) { registry.connect_on_construct<Component>([&](Registry &, Entity entity) {
on_construct_called.emplace_back(entity); on_construct_called.emplace_back(entity);
}); });
registry.connect_on_destruct<Component>([&](Registry &, EntityId entity) { registry.connect_on_destruct<Component>([&](Registry &, Entity entity) {
on_destruct_called.emplace_back(entity); on_destruct_called.emplace_back(entity);
}); });
@ -210,8 +206,8 @@ Suite each = [] {
auto shared_entity_counter = 0u; auto shared_entity_counter = 0u;
auto component_map_a = std::unordered_map<EntityId, Component> {}; auto component_map_a = std::unordered_map<Entity, Component> {};
auto entities_a = std::vector<EntityId> {}; auto entities_a = std::vector<Entity> {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
@ -224,10 +220,10 @@ Suite each = [] {
component_map_a[entity] = component; component_map_a[entity] = component;
} }
auto component_map_b = std::unordered_map<lt::ecs::EntityId, Component_B> {}; auto component_map_b = std::unordered_map<lt::ecs::Entity, Component_B> {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
auto entity = EntityId {}; auto entity = Entity {};
if (idx % 3 == 0) if (idx % 3 == 0)
{ {
entity = entities_a[idx]; entity = entities_a[idx];
@ -247,7 +243,7 @@ Suite each = [] {
Case { "each one element" } = [&] { Case { "each one element" } = [&] {
auto counter = 0u; auto counter = 0u;
registry.each<Component>([&](EntityId entity, Component &component) { registry.each<Component>([&](Entity entity, Component &component) {
++counter; ++counter;
expect_eq(component_map_a[entity], component); expect_eq(component_map_a[entity], component);
}); });
@ -255,7 +251,7 @@ Suite each = [] {
expect_eq(component_map_a.size(), counter); expect_eq(component_map_a.size(), counter);
counter = 0u; counter = 0u;
registry.each<Component_B>([&](EntityId entity, Component_B &component) { registry.each<Component_B>([&](Entity entity, Component_B &component) {
++counter; ++counter;
expect_eq(component_map_b[entity], component); expect_eq(component_map_b[entity], component);
}); });
@ -265,7 +261,7 @@ Suite each = [] {
Case { "each two element" } = [&] { Case { "each two element" } = [&] {
auto counter = 0u; auto counter = 0u;
registry.each<Component, Component_B>( registry.each<Component, Component_B>(
[&](EntityId entity, Component &component_a, Component_B &component_b) { [&](Entity entity, Component &component_a, Component_B &component_b) {
expect_eq(component_map_a[entity], component_a); expect_eq(component_map_a[entity], component_a);
expect_eq(component_map_b[entity], component_b); expect_eq(component_map_b[entity], component_b);
++counter; ++counter;
@ -281,8 +277,8 @@ Suite views = [] {
auto shared_entity_counter = 0u; auto shared_entity_counter = 0u;
auto component_map_a = std::unordered_map<EntityId, Component> {}; auto component_map_a = std::unordered_map<Entity, Component> {};
auto entities_a = std::vector<EntityId> {}; auto entities_a = std::vector<Entity> {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
@ -295,10 +291,10 @@ Suite views = [] {
component_map_a[entity] = component; component_map_a[entity] = component;
} }
auto component_map_b = std::unordered_map<EntityId, Component_B> {}; auto component_map_b = std::unordered_map<Entity, Component_B> {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
auto entity = EntityId {}; auto entity = Entity {};
if (idx % 3 == 0) if (idx % 3 == 0)
{ {
entity = entities_a[idx]; entity = entities_a[idx];

View file

@ -1,42 +1,5 @@
#pragma once #pragma once
#include <ecs/registry.hpp> namespace lt {
namespace lt::ecs { } // namespace lt
/** High-level entity convenience wrapper */
class Entity
{
public:
Entity(Ref<Registry> registry, EntityId identifier)
: m_registry(std::move(registry))
, m_identifier(identifier)
{
ensure(m_registry, "Failed to create Entity ({}): null registry", m_identifier);
}
template<typename Component_T>
auto get() -> Component_T &
{
return m_registry->get<Component_T>(m_identifier);
}
template<typename Component_T>
auto get() const -> const Component_T &
{
return m_registry->get<Component_T>(m_identifier);
}
auto get_registry() -> Ref<Registry>
{
return m_registry;
}
private:
Ref<Registry> m_registry;
EntityId m_identifier;
};
} // namespace lt::ecs

View file

@ -4,9 +4,9 @@
namespace lt::ecs { namespace lt::ecs {
using EntityId = uint32_t; using Entity = uint32_t;
constexpr auto null_entity = std::numeric_limits<EntityId>::max(); constexpr auto null_entity = std::numeric_limits<Entity>::max();
/** A registry of components, the heart of an ECS architecture. /** A registry of components, the heart of an ECS architecture.
* *
@ -24,9 +24,9 @@ constexpr auto null_entity = std::numeric_limits<EntityId>::max();
class Registry class Registry
{ {
public: public:
using UnderlyingSparseSet_T = TypeErasedSparseSet<EntityId>; using UnderlyingSparseSet_T = TypeErasedSparseSet<Entity>;
using Callback_T = std::function<void(Registry &, EntityId)>; using Callback_T = std::function<void(Registry &, Entity)>;
template<typename Component_T> template<typename Component_T>
void connect_on_construct(Callback_T callback) void connect_on_construct(Callback_T callback)
@ -52,13 +52,13 @@ public:
m_on_destruct_hooks.erase(get_type_id<Component_T>()); m_on_destruct_hooks.erase(get_type_id<Component_T>());
} }
auto create_entity() -> EntityId auto create_entity() -> Entity
{ {
++m_entity_count; ++m_entity_count;
return m_current++; return m_current++;
} }
void destroy_entity(EntityId entity) void destroy_entity(Entity entity)
{ {
for (const auto &[key, set] : m_sparsed_sets) for (const auto &[key, set] : m_sparsed_sets)
{ {
@ -69,21 +69,14 @@ public:
} }
template<typename Component_T> template<typename Component_T>
auto get(EntityId entity) const -> const Component_T & auto get(Entity entity) -> Component_T &
{ {
auto &derived_set = get_derived_set<Component_T>(); auto &derived_set = get_derived_set<Component_T>();
return derived_set.at(entity).second; return derived_set.at(entity).second;
} }
template<typename Component_T> template<typename Component_T>
auto get(EntityId entity) -> Component_T & auto add(Entity entity, Component_T component) -> Component_T &
{
auto &derived_set = get_derived_set<Component_T>();
return derived_set.at(entity).second;
}
template<typename Component_T>
auto add(EntityId entity, Component_T component) -> Component_T &
{ {
auto &derived_set = get_derived_set<Component_T>(); auto &derived_set = get_derived_set<Component_T>();
auto &added_component = derived_set.insert(entity, std::move(component)).second; auto &added_component = derived_set.insert(entity, std::move(component)).second;
@ -97,7 +90,7 @@ public:
}; };
template<typename Component_T> template<typename Component_T>
void remove(EntityId entity) void remove(Entity entity)
{ {
if (m_on_destruct_hooks.contains(get_type_id<Component_T>())) if (m_on_destruct_hooks.contains(get_type_id<Component_T>()))
{ {
@ -109,18 +102,18 @@ public:
} }
template<typename Component_T> template<typename Component_T>
auto view() -> SparseSet<Component_T, EntityId> & auto view() -> SparseSet<Component_T, Entity> &
{ {
return get_derived_set<Component_T>(); return get_derived_set<Component_T>();
}; };
template<typename ComponentA_T, typename ComponentB_T> template<typename ComponentA_T, typename ComponentB_T>
requires(!std::is_same_v<ComponentA_T, ComponentB_T>) requires(!std::is_same_v<ComponentA_T, ComponentB_T>)
auto view() -> std::vector<std::tuple<EntityId, ComponentA_T &, ComponentB_T &>> auto view() -> std::vector<std::tuple<Entity, ComponentA_T &, ComponentB_T &>>
{ {
auto &set_a = get_derived_set<ComponentA_T>(); auto &set_a = get_derived_set<ComponentA_T>();
auto &set_b = get_derived_set<ComponentB_T>(); auto &set_b = get_derived_set<ComponentB_T>();
auto view = std::vector<std::tuple<EntityId, ComponentA_T &, ComponentB_T &>> {}; auto view = std::vector<std::tuple<Entity, ComponentA_T &, ComponentB_T &>> {};
/* iterate over the "smaller" component-set, and check if its entities have the other /* iterate over the "smaller" component-set, and check if its entities have the other
* component */ * component */
@ -149,7 +142,7 @@ public:
}; };
template<typename Component_T> template<typename Component_T>
void each(std::function<void(EntityId, Component_T &)> functor) void each(std::function<void(Entity, Component_T &)> functor)
{ {
for (auto &[entity, component] : get_derived_set<Component_T>()) for (auto &[entity, component] : get_derived_set<Component_T>())
{ {
@ -159,7 +152,7 @@ public:
template<typename ComponentA_T, typename ComponentB_T> template<typename ComponentA_T, typename ComponentB_T>
requires(!std::is_same_v<ComponentA_T, ComponentB_T>) requires(!std::is_same_v<ComponentA_T, ComponentB_T>)
void each(std::function<void(EntityId, ComponentA_T &, ComponentB_T &)> functor) void each(std::function<void(Entity, ComponentA_T &, ComponentB_T &)> functor)
{ {
auto &set_a = get_derived_set<ComponentA_T>(); auto &set_a = get_derived_set<ComponentA_T>();
auto &set_b = get_derived_set<ComponentB_T>(); auto &set_b = get_derived_set<ComponentB_T>();
@ -230,22 +223,22 @@ private:
} }
template<typename T> template<typename T>
auto get_derived_set() -> SparseSet<T, EntityId> & auto get_derived_set() -> SparseSet<T, Entity> &
{ {
constexpr auto type_id = get_type_id<T>(); constexpr auto type_id = get_type_id<T>();
if (!m_sparsed_sets.contains(type_id)) if (!m_sparsed_sets.contains(type_id))
{ {
m_sparsed_sets[type_id] = create_scope<SparseSet<T, EntityId>>(); m_sparsed_sets[type_id] = create_scope<SparseSet<T, Entity>>();
} }
auto *base_set = m_sparsed_sets[type_id].get(); auto *base_set = m_sparsed_sets[type_id].get();
auto *derived_set = dynamic_cast<SparseSet<T, EntityId> *>(base_set); auto *derived_set = dynamic_cast<SparseSet<T, Entity> *>(base_set);
ensure(derived_set, "Failed to downcast to derived set"); ensure(derived_set, "Failed to downcast to derived set");
return *derived_set; return *derived_set;
} }
EntityId m_current; Entity m_current;
TypeId m_entity_count; TypeId m_entity_count;

View file

@ -128,11 +128,6 @@ public:
return m_dense.end(); return m_dense.end();
} }
[[nodiscard]] auto at(Identifier_T identifier) const -> const Dense_T &
{
return m_dense.at(m_sparse.at(identifier));
}
[[nodiscard]] auto at(Identifier_T identifier) -> Dense_T & [[nodiscard]] auto at(Identifier_T identifier) -> Dense_T &
{ {
return m_dense.at(m_sparse.at(identifier)); return m_dense.at(m_sparse.at(identifier));

View file

@ -14,7 +14,7 @@ System::System(Ref<ecs::Registry> registry): m_registry(std::move(registry))
ensure(m_registry, "Failed to initialize input system: null registry"); ensure(m_registry, "Failed to initialize input system: null registry");
} }
void System::tick(app::TickInfo tick) auto System::tick() -> bool
{ {
for (auto &[entity, surface] : m_registry->view<surface::SurfaceComponent>()) for (auto &[entity, surface] : m_registry->view<surface::SurfaceComponent>())
{ {
@ -51,12 +51,7 @@ void System::tick(app::TickInfo tick)
} }
} }
const auto now = std::chrono::steady_clock::now(); return false;
m_last_tick_result = app::TickResult {
.info = tick,
.duration = now - tick.start_time,
.end_time = now,
};
} }
void System::on_register() void System::on_register()

View file

@ -18,15 +18,6 @@ using test::expect_throw;
using test::Suite; using test::Suite;
// NOLINTEND // NOLINTEND
[[nodiscard]] auto tick_info() -> app::TickInfo
{
return {
.delta_time = std::chrono::milliseconds { 16 },
.budget = std::chrono::milliseconds { 10 },
.start_time = std::chrono::steady_clock::now(),
};
}
class Fixture class Fixture
{ {
public: public:
@ -35,7 +26,7 @@ public:
return m_registry; return m_registry;
} }
auto add_input_component() -> ecs::EntityId auto add_input_component() -> ecs::Entity
{ {
auto entity = m_registry->create_entity(); auto entity = m_registry->create_entity();
m_registry->add<InputComponent>(entity, {}); m_registry->add<InputComponent>(entity, {});
@ -43,7 +34,7 @@ public:
return entity; return entity;
} }
auto add_surface_component() -> ecs::EntityId auto add_surface_component() -> ecs::Entity
{ {
auto entity = m_registry->create_entity(); auto entity = m_registry->create_entity();
m_registry->add<surface::SurfaceComponent>( m_registry->add<surface::SurfaceComponent>(
@ -133,7 +124,7 @@ Suite tick = [] {
auto registry = fixture.registry(); auto registry = fixture.registry();
auto system = System { fixture.registry() }; auto system = System { fixture.registry() };
system.tick(tick_info()); expect_false(system.tick());
}; };
Case { "Tick triggers input action" } = [] { Case { "Tick triggers input action" } = [] {
@ -155,23 +146,23 @@ Suite tick = [] {
); );
expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive); expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
system.tick(tick_info()); system.tick();
expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive); expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
surface.push_event(surface::KeyPressedEvent(69)); surface.push_event(surface::KeyPressedEvent(69));
system.tick(tick_info()); system.tick();
expect_eq(input.get_action(action_key).state, input::InputAction::State::triggered); expect_eq(input.get_action(action_key).state, input::InputAction::State::triggered);
system.tick(tick_info()); system.tick();
expect_eq(input.get_action(action_key).state, input::InputAction::State::active); expect_eq(input.get_action(action_key).state, input::InputAction::State::active);
system.tick(tick_info()); system.tick();
system.tick(tick_info()); system.tick();
system.tick(tick_info()); system.tick();
expect_eq(input.get_action(action_key).state, input::InputAction::State::active); expect_eq(input.get_action(action_key).state, input::InputAction::State::active);
surface.push_event(surface::KeyReleasedEvent(69)); surface.push_event(surface::KeyReleasedEvent(69));
system.tick(tick_info()); system.tick();
expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive); expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
}; };

View file

@ -13,17 +13,12 @@ class System: public app::ISystem
public: public:
System(Ref<ecs::Registry> registry); System(Ref<ecs::Registry> registry);
void tick(app::TickInfo tick) override; auto tick() -> bool override;
void on_register() override; void on_register() override;
void on_unregister() override; void on_unregister() override;
[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override
{
return m_last_tick_result;
}
private: private:
void handle_event(const surface::SurfaceComponent::Event &event); void handle_event(const surface::SurfaceComponent::Event &event);
@ -46,8 +41,6 @@ private:
std::array<bool, 512> m_buttons {}; std::array<bool, 512> m_buttons {};
math::vec2 m_pointer_position; math::vec2 m_pointer_position;
app::TickResult m_last_tick_result {};
}; };

View file

@ -1 +0,0 @@
add_library_module(memory)

View file

@ -1,73 +0,0 @@
#pragma once
namespace lt::memory {
/** Holds an `Underlying_T`, assigns it to `null_value` when this object is moved.
*
* @note For avoiding the need to explicitly implement the move constructor for objects that hold
* Vulkan objects. But may server other purposes, hence why I kept the implementation generic.
*/
template<typename Underlying_T, Underlying_T null_value = nullptr>
class NullOnMove
{
public:
NullOnMove() = default;
NullOnMove(Underlying_T value): m_value(value)
{
}
~NullOnMove() = default;
NullOnMove(const NullOnMove &) = delete;
auto operator=(const NullOnMove &) -> NullOnMove & = delete;
NullOnMove(NullOnMove &&other) noexcept
{
*this = std::move(other);
}
auto operator=(NullOnMove &&other) noexcept -> NullOnMove &
{
if (this->m_value == other.m_value)
{
return *this;
}
m_value = other.m_value;
other.m_value = null_value;
return *this;
}
auto operator&() const -> const Underlying_T *
{
return &m_value;
}
auto operator&() -> Underlying_T *
{
return &m_value;
}
operator bool() const
{
return m_value != null_value;
}
operator Underlying_T() const
{
return m_value;
}
operator Underlying_T()
{
return m_value;
}
private:
Underlying_T m_value;
};
} // namespace lt::memory

View file

@ -1,28 +0,0 @@
#pragma once
#include <memory>
namespace lt::memory {
/** Wrapper around std::shared_ptr. */
template<typename t>
using Ref = std::shared_ptr<t>;
/** Allocates memory for an `Underlying_T` and directly constructs it there.
*
* @return A Ref<Underlying_T> to the constructed object.
*/
template<typename Underlying_T, typename... Args>
constexpr Ref<Underlying_T> create_ref(Args &&...args)
{
return std::make_shared<Underlying_T>(std::forward<Args>(args)...);
}
/** Converts c-style pointer of type `Underlying_T` to a `Ref<Underlying_T>`. */
template<typename Underlying_T>
constexpr Ref<Underlying_T> make_ref(Underlying_T *raw_pointer)
{
return Ref<Underlying_T>(raw_pointer);
}
} // namespace lt::memory

View file

@ -1,28 +0,0 @@
#pragma once
#include <memory>
namespace lt::memory {
/** Wrapper around std::unique_ptr. */
template<typename t>
using Scope = std::unique_ptr<t>;
/** Allocates memory for an `Underlying_T` and directly constructs it there.
*
* @return A Scope<Underlying_T> to the constructed object.
*/
template<typename Underlying_T, typename... Args>
constexpr Scope<Underlying_T> create_scope(Args &&...args)
{
return std::make_unique<Underlying_T>(std::forward<Args>(args)...);
}
/** Converts c-style pointer of type `Underlying_T` to a `Scope<Underlying_T>`. */
template<typename Underlying_T>
constexpr Scope<Underlying_T> make_scope(Underlying_T *raw_pointer)
{
return Scope<Underlying_T>(raw_pointer);
}
} // namespace lt::memory

View file

@ -33,11 +33,13 @@ public:
} }
} }
void tick(app::TickInfo tick) override auto tick() -> bool override
{ {
using Surface = lt::surface::SurfaceComponent; using Surface = lt::surface::SurfaceComponent;
using Input = lt::input::InputComponent; using Input = lt::input::InputComponent;
static lt::Timer timer;
std::this_thread::sleep_for(std::chrono::milliseconds { 10 }); std::this_thread::sleep_for(std::chrono::milliseconds { 10 });
auto should_quit = false; auto should_quit = false;
for (auto &[entity, surface, input] : m_registry->view<Surface, Input>()) for (auto &[entity, surface, input] : m_registry->view<Surface, Input>())
@ -76,12 +78,8 @@ public:
} }
} }
const auto now = std::chrono::steady_clock::now(); timer.reset();
m_last_tick_result = app::TickResult { return should_quit;
.info = tick,
.duration = now - tick.start_time,
.end_time = now,
};
} }
void on_register() override void on_register() override
@ -92,19 +90,11 @@ public:
{ {
} }
[[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;
lt::input::InputAction::Key m_quit_action_key; lt::input::InputAction::Key m_quit_action_key;
std::array<lt::input::InputAction::Key, 4> m_debug_action_keys {}; std::array<lt::input::InputAction::Key, 4> m_debug_action_keys {};
app::TickResult m_last_tick_result {};
}; };
class Mirror: public app::Application class Mirror: public app::Application
@ -211,7 +201,7 @@ private:
Ref<MirrorSystem> m_mirror_system; Ref<MirrorSystem> m_mirror_system;
lt::ecs::EntityId m_window = lt::ecs::null_entity; lt::ecs::Entity m_window = lt::ecs::null_entity;
}; };
auto app::create_application() -> Scope<app::Application> auto app::create_application() -> Scope<app::Application>

View file

@ -1,17 +1,45 @@
add_library_module(renderer add_library_module(renderer
system.cpp system.cpp
vk/context.cpp blender.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(renderer target_link_libraries(
PUBLIC renderer
app PUBLIC camera
ecs PUBLIC input
vulkan PUBLIC logger
memory PUBLIC imgui
surface PUBLIC asset_parser
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

@ -1,17 +0,0 @@
#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

@ -1,44 +0,0 @@
#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

@ -1,56 +0,0 @@
#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,5 +1,17 @@
#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,123 +1,44 @@
#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 renderer::System; using lt::test::Case;
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 = [] {
Case { "happy path won't throw" } = [&] { using lt::test::expect_true;
std::ignore = create_system(); using lt::test::expect_throw;
using renderer::System;
Case { "happy" } = [=] {
std::ignore = System { {
.registry = create_ref<ecs::Registry>(),
} };
}; };
Case { "happy path has no validation errors" } = [&] { Case { "unhappy" } = [=] {
auto [renderer, surface] = create_system(); expect_throw([=] {
expect_true(renderer.system.get_stats().empty_diagnosis()); std::ignore = System { {
};
Case { "unhappy path throws" } = [] {
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(
{
.registry = {}, .registry = {},
.surface_entity = surface_entity, } };
.system_stats = stats,
}
);
}); });
expect_throw([&] { expect_throw([=] {
std::ignore = System( std::ignore = System { {
System::CreateInfo { .registry = create_ref<ecs::Registry>(),
.registry = surface_entity.get_registry(), } };
.surface_entity = empty_entity,
.system_stats = stats,
}
);
}); });
};
expect_throw([&] { Case { "plenty" } = [=] {
std::ignore = System( for (auto idx : std::views::iota(0, 100'001))
System::CreateInfo { {
.registry = surface_entity.get_registry(), std::ignore = System { {
.surface_entity = surface_entity, .registry = create_ref<ecs::Registry>(),
.system_stats = {}, } };
} }
);
});
}; };
}; };

View file

@ -1,4 +1,4 @@
#include <renderer/vk/context.hpp> #include <renderer/vk/backend.hpp>
#if defined(_WIN32) #if defined(_WIN32)
@ -83,16 +83,10 @@ 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)
Context::Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats) Backend::Backend()
: 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();
@ -106,31 +100,16 @@ Context::Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system
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);
} }
Context::~Context() Backend::~Backend()
{ {
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)
@ -153,17 +132,14 @@ auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const
return "PERFORMANCE"; return "PERFORMANCE";
} }
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity) auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity) -> LogLvl
-> app::SystemDiagnosis::Severity
{ {
using enum app::SystemDiagnosis::Severity;
switch (message_severity) switch (message_severity)
{ {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return verbose; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return LogLvl::trace;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return info; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return LogLvl::info;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return warning; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return LogLvl::warn;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return error; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return LogLvl::error;
default: ensure(false, "Invalid message severity: {}", static_cast<int>(message_severity)); default: ensure(false, "Invalid message severity: {}", static_cast<int>(message_severity));
} }
@ -177,34 +153,16 @@ auto validation_layers_callback(
void *const vulkan_user_data void *const vulkan_user_data
) -> VkBool32 ) -> VkBool32
{ {
auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT std::ignore = vulkan_user_data;
const auto &type = parse_message_type(message_types); const auto &type = parse_message_type(message_types);
const auto level = parse_message_severity(message_severity);
auto message = std::format( Logger::log(level, ":: <{}> :: {}", type, callback_data->pMessage);
"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); return static_cast<VkBool32>(VK_FALSE);
} }
stats->push_diagnosis( void Backend::initialize_instance()
app::SystemDiagnosis {
.message = message,
.code = {}, // TODO(Light): extract vulkan validation-layers code from the message
.severity = severity,
}
);
return static_cast<VkBool32>(VK_FALSE);
}
void Context::initialize_instance()
{ {
auto app_info = VkApplicationInfo { auto app_info = VkApplicationInfo {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
@ -240,10 +198,10 @@ void Context::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);
} }
} }
@ -252,7 +210,7 @@ void Context::initialize_instance()
ensure(m_instance, "Failed to create vulkan instance"); ensure(m_instance, "Failed to create vulkan instance");
} }
void Context::initialize_debug_messenger() void Backend::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,
@ -268,8 +226,6 @@ void Context::initialize_debug_messenger()
.pfnUserCallback = &validation_layers_callback, .pfnUserCallback = &validation_layers_callback,
.pUserData = &m_stats,
}; };
ensure( ensure(
@ -278,7 +234,7 @@ void Context::initialize_debug_messenger()
); );
} }
void Context::initialize_physical_device() void Backend::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);
@ -304,7 +260,7 @@ void Context::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 Context::initialize_logical_device() void Backend::initialize_logical_device()
{ {
const float priorities = .0f; const float priorities = .0f;
@ -336,12 +292,12 @@ void Context::initialize_logical_device()
); );
} }
void Context::initialize_queue() void Backend::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 Context::load_library() void Backend::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");
@ -353,13 +309,13 @@ void Context::load_library()
ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr"); ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr");
} }
void Context::load_global_functions() void Backend::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");
@ -367,13 +323,13 @@ void Context::load_global_functions()
load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties"); load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties");
} }
void Context::load_instance_functions() void Backend::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");
@ -400,18 +356,15 @@ void Context::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 Context::load_device_functions() void Backend::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");
@ -456,7 +409,7 @@ void Context::load_device_functions()
load_fn(vk_cmd_set_scissors, "vkCmdSetScissor"); load_fn(vk_cmd_set_scissors, "vkCmdSetScissor");
} }
[[nodiscard]] auto Context::find_suitable_queue_family() const -> uint32_t [[nodiscard]] auto Backend::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,20 +1,11 @@
#pragma once #pragma once
#define VK_NO_PROTOTYPES #define VK_NO_PROTOTYPES
#define VK_USE_PLATFORM_XLIB_KHR #include <renderer/backend.hpp>
#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;
@ -87,54 +78,26 @@ 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 Context class Backend: public ::lt::renderer::Backend
{ {
public: public:
Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats); Backend();
~Context(); Backend(Backend &&) = default;
Context(Context &&other) noexcept = default; auto operator=(Backend &&) -> Backend & = default;
auto operator=(Context &&other) noexcept -> Context & = default; Backend(const Backend &) = delete;
Context(const Context &) = delete; auto operator=(const Backend &) -> Backend & = delete;
auto operator=(const Context &) -> Context & = delete; ~Backend() override;
[[nodiscard]] auto instance() -> VkInstance [[nodiscard]] constexpr auto get_api() const -> API override
{ {
return m_instance; return API::vulkan;
}
[[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:
@ -158,23 +121,15 @@ private:
[[nodiscard]] auto find_suitable_queue_family() const -> uint32_t; [[nodiscard]] auto find_suitable_queue_family() const -> uint32_t;
Ref<ecs::Registry> m_registry; VkInstance m_instance {};
NullOnMove<VkInstance> m_instance = VK_NULL_HANDLE; VkPhysicalDevice m_physical_device {};
NullOnMove<VkPhysicalDevice> m_physical_device = VK_NULL_HANDLE; VkDevice m_device {};
NullOnMove<VkDevice> m_device = VK_NULL_HANDLE; VkQueue m_queue {};
NullOnMove<VkQueue> m_queue = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT m_debug_messenger {};
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

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

View file

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

View file

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

@ -1,25 +0,0 @@
#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

Some files were not shown because too many files have changed in this diff Show more