wip: convert from include style to module import style :D
Some checks are pending
continuous-integration/drone/push Build is running

This commit is contained in:
light7734 2025-11-04 18:50:59 +03:30
parent 14c2512202
commit 63cb6dfe92
Signed by: light7734
GPG key ID: 8C30176798F1A6BA
114 changed files with 2228 additions and 1714 deletions

View file

@ -1,26 +1,37 @@
# engine
add_subdirectory(./std)
add_subdirectory(./bitwise)
add_subdirectory(./env)
add_subdirectory(./memory)
add_subdirectory(./time)
add_subdirectory(./logger)
add_subdirectory(./debug)
add_subdirectory(./math)
#
add_subdirectory(./asset_baker)
add_subdirectory(./assets)
#
add_subdirectory(./camera)
add_subdirectory(./input)
# add_subdirectory(./ui)
#
add_subdirectory(./surface)
add_subdirectory(./renderer)
add_subdirectory(./ecs)
#
add_subdirectory(./app)
# engine add_subdirectory(./std)
# apps
add_subdirectory(./mirror)
add_subdirectory(test)
add_subdirectory(./logger)
add_subdirectory(./bitwise)
add_subdirectory(./env)
add_subdirectory(./memory)
add_subdirectory(./time)
add_subdirectory(./debug)
add_subdirectory(./math)
add_subdirectory(./assets)
add_subdirectory(./asset_baker)
add_subdirectory(./camera)
add_subdirectory(./app)
add_subdirectory(./ecs)
add_subdirectory(./surface)
add_subdirectory(./input)
# add_subdirectory(./ui)
# add_subdirectory(./renderer)
#
# add_subdirectory(./mirror)

View file

@ -1,4 +1,12 @@
add_library_module(app application.cpp)
add_library_module(
NAME
app
INTERFACES
application.cppm
system.cppm
SOURCES
entrypoint.cpp)
target_link_libraries(
app
PUBLIC memory

View file

@ -0,0 +1,98 @@
export module app;
import app.system;
import memory.reference;
import memory.scope;
import std;
namespace lt::app {
/** The main application class.
* Think of this like an aggregate of systems, you register systems through this interface.
* Then they'll tick every "application frame".
*/
export class Application
{
public:
Application(const Application &) = delete;
Application(Application &&) = delete;
auto operator=(const Application &) -> Application & = delete;
auto operator=(Application &&) -> Application & = delete;
virtual ~Application() = default;
void game_loop();
void register_system(memory::Ref<app::ISystem> system);
void unregister_system(memory::Ref<app::ISystem> system);
protected:
Application() = default;
private:
std::vector<memory::Ref<app::ISystem>> m_systems;
std::vector<memory::Ref<app::ISystem>> m_systems_to_be_unregistered;
std::vector<memory::Ref<app::ISystem>> m_systems_to_be_registered;
};
export extern memory::Scope<class Application> create_application();
} // namespace lt::app
module :private;
namespace lt::app {
void Application::game_loop()
{
while (true)
{
for (auto &system : m_systems)
{
const auto &last_tick = system->get_last_tick_result();
const auto now = std::chrono::steady_clock::now();
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)
{
m_systems.emplace_back(system)->on_register();
}
for (auto &system : m_systems_to_be_unregistered)
{
m_systems.erase(
std::remove(m_systems.begin(), m_systems.end(), system),
m_systems.end()
);
}
if (m_systems.empty())
{
return;
}
}
}
void Application::register_system(memory::Ref<app::ISystem> system)
{
m_systems.emplace_back(std::move(system));
}
void Application::unregister_system(memory::Ref<app::ISystem> system)
{
m_systems_to_be_unregistered.emplace_back(std::move(system));
}
} // namespace lt::app

View file

@ -0,0 +1,31 @@
import memory.scope;
import logger;
import app;
import std;
/** The ultimate entrypoint. */
auto main(int argc, char *argv[]) -> std::int32_t
{
try
{
std::ignore = argc;
std::ignore = argv;
auto application = lt::memory::Scope<lt::app::Application> {};
application = lt::app::create_application();
if (!application)
{
throw std::runtime_error { "Failed to create application\n" };
}
application->game_loop();
return 0;
}
catch (const std::exception &exp)
{
lt::log::critical("Terminating due to uncaught exception:");
lt::log::critical("\texception.what(): {}", exp.what());
return 1;
}
}

View file

@ -1,55 +0,0 @@
#include <app/application.hpp>
#include <app/system.hpp>
#include <memory/reference.hpp>
namespace lt::app {
void Application::game_loop()
{
while (true)
{
for (auto &system : m_systems)
{
const auto &last_tick = system->get_last_tick_result();
const auto now = std::chrono::steady_clock::now();
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)
{
m_systems.emplace_back(system)->on_register();
}
for (auto &system : m_systems_to_be_unregistered)
{
m_systems.erase(
std::remove(m_systems.begin(), m_systems.end(), system),
m_systems.end()
);
}
if (m_systems.empty())
{
return;
}
}
}
void Application::register_system(memory::Ref<app::ISystem> system)
{
m_systems.emplace_back(std::move(system));
}
void Application::unregister_system(memory::Ref<app::ISystem> system)
{
m_systems_to_be_unregistered.emplace_back(std::move(system));
}
} // namespace lt::app

View file

@ -1,47 +0,0 @@
#pragma once
#include <memory/reference.hpp>
#include <memory/scope.hpp>
namespace lt::app {
class ISystem;
extern memory::Scope<class Application> create_application();
/** The main application class.
* Think of this like an aggregate of systems, you register systems through this interface.
* Then they'll tick every "application frame".
*/
class Application
{
public:
Application(const Application &) = delete;
Application(Application &&) = delete;
auto operator=(const Application &) -> Application & = delete;
auto operator=(Application &&) -> Application & = delete;
virtual ~Application() = default;
void game_loop();
void register_system(memory::Ref<app::ISystem> system);
void unregister_system(memory::Ref<app::ISystem> system);
protected:
Application() = default;
private:
std::vector<memory::Ref<app::ISystem>> m_systems;
std::vector<memory::Ref<app::ISystem>> m_systems_to_be_unregistered;
std::vector<memory::Ref<app::ISystem>> m_systems_to_be_registered;
};
} // namespace lt::app

View file

@ -1,28 +0,0 @@
#pragma once
#include <app/application.hpp>
#include <logger/logger.hpp>
#include <memory/scope.hpp>
auto main(int argc, char *argv[]) -> int32_t
try
{
std::ignore = argc;
std::ignore = argv;
auto application = lt::memory::Scope<lt::app::Application> {};
application = lt::app::create_application();
if (!application)
{
throw std::runtime_error { "Failed to create application\n" };
}
application->game_loop();
return EXIT_SUCCESS;
}
catch (const std::exception &exp)
{
lt::log::critical("Terminating due to uncaught exception:");
lt::log::critical("\texception.what(): {}", exp.what());
return EXIT_FAILURE;
}

View file

@ -1,14 +1,13 @@
#pragma once
#include <chrono>
#include <logger/logger.hpp>
export module app.system;
import logger;
import std;
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
export struct TickInfo
{
using Timepoint_T = std::chrono::time_point<std::chrono::steady_clock>;
@ -31,7 +30,7 @@ struct TickInfo
};
/** Information about how a system's tick performed */
struct TickResult
export struct TickResult
{
using Timepoint_T = std::chrono::time_point<std::chrono::steady_clock>;
@ -47,10 +46,9 @@ struct TickResult
Timepoint_T end_time;
};
struct SystemDiagnosis
export struct SystemDiagnosis
{
enum class Severity : uint8_t
enum class Severity : std::uint8_t
{
verbose,
info,
@ -66,7 +64,7 @@ struct SystemDiagnosis
Severity severity;
};
class SystemStats
export class SystemStats
{
public:
void push_diagnosis(SystemDiagnosis &&diagnosis)
@ -85,7 +83,7 @@ private:
std::vector<SystemDiagnosis> m_diagnosis;
};
class ISystem
export class ISystem
{
public:
ISystem() = default;

View file

@ -1,6 +1,5 @@
add_library_module(libasset_baker bakers.cpp)
target_link_libraries(libasset_baker PUBLIC assets logger lt_debug tbb)
add_test_module(libasset_baker bakers.test.cpp)
add_library_module(NAME libasset_baker INTERFACES bakers.cppm)
target_link_libraries(libasset_baker PUBLIC assets logger lt_debug)
add_executable_module(asset_baker entrypoint/baker.cpp)
add_executable(asset_baker entrypoint.cpp)
target_link_libraries(asset_baker PRIVATE libasset_baker)

View file

@ -0,0 +1,68 @@
export module bakers;
import debug.assertions;
import assets.metadata;
import assets.shader;
import logger;
import std;
export void bake_shader(
const std::filesystem::path &in_path,
const std::filesystem::path &out_path,
lt::assets::ShaderAsset::Type type
)
{
using lt::assets::ShaderAsset;
using enum lt::assets::ShaderAsset::Type;
auto glsl_path = in_path.string();
auto spv_path = std::format("{}.spv", glsl_path);
lt::log::trace(
"Compiling {} shader {} -> {}",
type == vertex ? "vertex" : "fragment",
glsl_path,
spv_path
);
// Don't bother linking to shaderc, just invoke the command with a system call.
// NOLINTNEXTLINE(concurrency-mt-unsafe)
std::system(
std::format(
"glslc --target-env=vulkan1.4 -std=450core -fshader-stage={} {} -o {}",
type == vertex ? "vert" : "frag",
glsl_path,
spv_path
)
.c_str()
);
auto stream = std::ifstream(spv_path, std::ios::binary);
lt::debug::ensure(
stream.is_open(),
"Failed to open compiled {} shader at: {}",
type == vertex ? "vert" : "frag",
spv_path
);
stream.seekg(0, std::ios::end);
const auto size = stream.tellg();
auto bytes = std::vector<std::byte>(size);
stream.seekg(0, std::ios::beg);
stream.read((char *)bytes.data(), size); // NOLINT
lt::log::debug("BYTES: {}", bytes.size());
stream.close();
std::filesystem::remove(spv_path);
ShaderAsset::pack(
out_path,
lt::assets::AssetMetadata {
.version = lt::assets::current_version,
.type = ShaderAsset::asset_type_identifier,
},
ShaderAsset::Metadata {
.type = type,
},
std::move(bytes)
);
}

View file

@ -1,8 +1,10 @@
#include <asset_baker/bakers.hpp>
#include <assets/shader.hpp>
#include <logger/logger.hpp>
import assets.shader;
import logger;
import bakers;
import std;
auto main(int argc, char *argv[]) -> int32_t
auto main(int argc, char *argv[]) -> std::int32_t
try
{
if (argc != 2)
@ -31,12 +33,12 @@ try
}
}
return EXIT_SUCCESS;
return 0;
}
catch (const std::exception &exp)
{
lt::log::critical("Terminating due to uncaught exception:");
lt::log::critical("\texception.what: {}:", exp.what());
return EXIT_FAILURE;
return 1;
}

View file

@ -1,2 +0,0 @@
#include <asset_baker/bakers.hpp>
#include <test/test.hpp>

View file

@ -1,7 +1,5 @@
#pragma once
#include <assets/shader.hpp>
#include <logger/logger.hpp>
inline void bake_shader(
const std::filesystem::path &in_path,
@ -34,7 +32,7 @@ inline void bake_shader(
);
auto stream = std::ifstream(spv_path, std::ios::binary);
lt::ensure(
lt::debug::ensure(
stream.is_open(),
"Failed to open compiled {} shader at: {}",
type == vertex ? "vert" : "frag",

View file

@ -1,5 +1,3 @@
add_library_module(assets shader.cpp)
add_library_module(NAME assets INTERFACES shader.cppm metadata.cppm)
target_link_libraries(assets PUBLIC logger lt_debug)
add_test_module(assets shader.test.cpp)

View file

@ -1,18 +1,19 @@
#pragma once
export module assets.metadata;
import std;
namespace lt::assets {
export namespace lt::assets {
using Type_T = std::array<const char, 16>;
using Tag_T = uint8_t;
using Tag_T = std::uint8_t;
using Version = uint8_t;
using Version = std::uint8_t;
using Blob = std::vector<std::byte>;
constexpr auto current_version = Version { 1u };
enum class CompressionType : uint8_t
enum class CompressionType : std::uint8_t
{
none,
lz4,
@ -30,13 +31,13 @@ struct BlobMetadata
{
Tag_T tag;
size_t offset;
std::size_t offset;
CompressionType compression_type;
size_t compressed_size;
std::size_t compressed_size;
size_t uncompressed_size;
std::size_t uncompressed_size;
};
} // namespace lt::assets

View file

@ -1,3 +0,0 @@
#pragma once
// TO BE DOOO

View file

@ -1,74 +0,0 @@
#pragma once
#include <assets/metadata.hpp>
namespace lt::assets {
class ShaderAsset
{
public:
static constexpr auto asset_type_identifier = Type_T { "SHADER_________" };
enum class BlobTag : Tag_T
{
code,
};
enum class Type : uint8_t
{
vertex,
fragment,
geometry,
compute,
};
struct Metadata
{
Type type;
};
static void pack(
const std::filesystem::path &destination,
AssetMetadata asset_metadata,
Metadata metadata,
Blob code_blob
);
ShaderAsset(const std::filesystem::path &path);
void unpack_to(BlobTag tag, std::span<std::byte> destination) const;
[[nodiscard]] auto unpack(BlobTag tag) const -> Blob;
[[nodiscard]] auto get_asset_metadata() const -> const AssetMetadata &
{
return m_asset_metadata;
}
[[nodiscard]] auto get_metadata() const -> const Metadata &
{
return m_metadata;
}
[[nodiscard]] auto get_blob_metadata(BlobTag tag) const -> const BlobMetadata &
{
ensure(
tag == BlobTag::code,
"Invalid blob tag for shader asset: {}",
std::to_underlying(tag)
);
return m_code_blob_metadata;
}
private:
AssetMetadata m_asset_metadata {};
Metadata m_metadata {};
BlobMetadata m_code_blob_metadata {};
mutable std::ifstream m_stream;
};
} // namespace lt::assets

View file

@ -1,4 +1,80 @@
#include <assets/shader.hpp>
export module assets.shader;
import assets.metadata;
import debug.assertions;
import std;
export namespace lt::assets {
class ShaderAsset
{
public:
static constexpr auto asset_type_identifier = Type_T { "SHADER_________" };
enum class BlobTag : Tag_T
{
code,
};
enum class Type : std::uint8_t
{
vertex,
fragment,
geometry,
compute,
};
struct Metadata
{
Type type;
};
static void pack(
const std::filesystem::path &destination,
AssetMetadata asset_metadata,
Metadata metadata,
Blob code_blob
);
ShaderAsset(const std::filesystem::path &path);
void unpack_to(BlobTag tag, std::span<std::byte> destination) const;
[[nodiscard]] auto unpack(BlobTag tag) const -> Blob;
[[nodiscard]] auto get_asset_metadata() const -> const AssetMetadata &
{
return m_asset_metadata;
}
[[nodiscard]] auto get_metadata() const -> const Metadata &
{
return m_metadata;
}
[[nodiscard]] auto get_blob_metadata(BlobTag tag) const -> const BlobMetadata &
{
debug::ensure(
tag == BlobTag::code,
"Invalid blob tag for shader asset: {}",
std::to_underlying(tag)
);
return m_code_blob_metadata;
}
private:
AssetMetadata m_asset_metadata {};
Metadata m_metadata {};
BlobMetadata m_code_blob_metadata {};
mutable std::ifstream m_stream;
};
} // namespace lt::assets
namespace lt::assets {
@ -14,14 +90,14 @@ constexpr auto total_metadata_size = //
ShaderAsset::ShaderAsset(const std::filesystem::path &path): m_stream(path)
{
ensure(m_stream.is_open(), "Failed to open shader asset at: {}", path.string());
debug::ensure(m_stream.is_open(), "Failed to open shader asset at: {}", path.string());
const auto read = [this](auto &field) {
m_stream.read(std::bit_cast<char *>(&field), sizeof(field));
};
m_stream.seekg(0, std::ifstream::end);
const auto file_size = static_cast<size_t>(m_stream.tellg());
ensure(
const auto file_size = static_cast<std::size_t>(m_stream.tellg());
debug::ensure(
file_size > total_metadata_size,
"Failed to open shader asset at: {}, file smaller than metadata: {} < {}",
path.string(),
@ -39,7 +115,7 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path): m_stream(path)
read(m_code_blob_metadata.compressed_size);
read(m_code_blob_metadata.uncompressed_size);
ensure(
debug::ensure(
m_asset_metadata.type == asset_type_identifier,
"Failed to open shader asset at: {}, incorrect asset type: {} != {}",
path.string(),
@ -47,7 +123,7 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path): m_stream(path)
asset_type_identifier
);
ensure(
debug::ensure(
m_asset_metadata.version == current_version,
"Failed to open shader asset at: {}, version mismatch: {} != {}",
path.string(),
@ -55,21 +131,21 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path): m_stream(path)
current_version
);
ensure(
debug::ensure(
std::to_underlying(m_metadata.type) <= std::to_underlying(Type::compute),
"Failed to open shader asset at: {}, invalid shader type: {}",
path.string(),
std::to_underlying(m_metadata.type)
);
ensure(
debug::ensure(
m_code_blob_metadata.tag == std::to_underlying(BlobTag::code),
"Failed to open shader asset at: {}, invalid blob tag: {}",
path.string(),
m_code_blob_metadata.tag
);
ensure(
debug::ensure(
m_code_blob_metadata.offset + m_code_blob_metadata.compressed_size <= file_size,
"Failed to open shader asset at: {}, file smaller than blob: {} > {} + {}",
path.string(),
@ -99,7 +175,7 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path): m_stream(path)
.uncompressed_size = code_blob.size(),
};
ensure(stream.is_open(), "Failed to pack shader asset to {}", destination.string());
debug::ensure(stream.is_open(), "Failed to pack shader asset to {}", destination.string());
const auto write = [&stream](auto &field) {
stream.write(std::bit_cast<char *>(&field), sizeof(field));
};
@ -116,14 +192,18 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path): m_stream(path)
void ShaderAsset::unpack_to(BlobTag tag, std::span<std::byte> destination) const
{
ensure(tag == BlobTag::code, "Invalid blob tag for shader asset: {}", std::to_underlying(tag));
debug::ensure(
tag == BlobTag::code,
"Invalid blob tag for shader asset: {}",
std::to_underlying(tag)
);
ensure(
debug::ensure(
destination.size() >= m_code_blob_metadata.uncompressed_size,
"Failed to unpack shader blob {} to destination ({}) of size {} since it's smaller "
"than the blobl's uncompressed size: {}",
std::to_underlying(tag),
std::bit_cast<size_t>(destination.data()),
std::bit_cast<std::size_t>(destination.data()),
destination.size(),
m_code_blob_metadata.uncompressed_size
);
@ -137,7 +217,11 @@ void ShaderAsset::unpack_to(BlobTag tag, std::span<std::byte> destination) const
[[nodiscard]] auto ShaderAsset::unpack(BlobTag tag) const -> Blob
{
ensure(tag == BlobTag::code, "Invalid blob tag for shader asset: {}", std::to_underlying(tag));
debug::ensure(
tag == BlobTag::code,
"Invalid blob tag for shader asset: {}",
std::to_underlying(tag)
);
auto blob = Blob(m_code_blob_metadata.uncompressed_size);
unpack_to(tag, blob);

View file

@ -1,6 +1,8 @@
#include <assets/shader.hpp>
#include <ranges>
#include <test/test.hpp>
import assets.metadata;
import assets.shader;
import test.test;
import test.expects;
import std;
using ::lt::assets::AssetMetadata;
using ::lt::assets::BlobMetadata;
@ -10,6 +12,7 @@ using ::lt::test::expect_eq;
using ::lt::test::expect_throw;
using ::lt::test::expect_true;
using ::lt::test::Suite;
using ::lt::test::operator""_suite;
const auto test_data_path = std::filesystem::path { "./data/test_assets" };
const auto tmp_path = std::filesystem::path { "/tmp/lt_assets_tests/" };
@ -70,7 +73,7 @@ Suite packing = "shader_pack"_suite = [] {
expect_true(stream.is_open());
stream.seekg(0, std::ios::end);
const auto file_size = static_cast<size_t>(stream.tellg());
const auto file_size = static_cast<std::size_t>(stream.tellg());
expect_eq(file_size, expected_size);
stream.close();

View file

@ -1 +1 @@
add_library_module(bitwise)
add_library_module(NAME bitwise INTERFACES operations.cppm)

View file

@ -1,11 +1,10 @@
#pragma once
#include <cstdint>
export module bitwise;
import std;
namespace lt::bitwise {
/* bit-wise */
constexpr auto bit(uint32_t x) -> uint32_t
constexpr auto bit(std::uint32_t x) -> std::uint32_t
{
return 1u << x;
}

View file

@ -1,3 +1,3 @@
add_library_module(camera)
add_library_module(NAME camera INTERFACES components.cppm)
target_link_libraries(camera INTERFACE math)
target_link_libraries(camera PUBLIC math)

View file

@ -1,10 +1,9 @@
#pragma once
#include <math/mat4.hpp>
export module camera.components;
import math.vec4;
namespace lt::camera::components {
struct PerspectiveCamera
export struct PerspectiveCamera
{
float vertical_fov {};

View file

@ -1,4 +1,2 @@
add_library_module(lt_debug instrumentor.cpp)
add_library_module(NAME lt_debug INTERFACES instrumentor.cppm assertions.cppm)
target_link_libraries(lt_debug PUBLIC logger)
target_precompile_headers(lt_debug PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/private/pch.hpp)

View file

@ -0,0 +1,47 @@
export module debug.assertions;
import std;
namespace lt::debug {
///////////////////////////////////////
// ----------* INTERFACE *--------- //
/////////////////////////////////////
export template<typename Expression_T, typename... Args_T>
struct ensure
{
ensure(
const Expression_T &expression,
std::format_string<Args_T...> fmt,
Args_T &&...args,
const std::source_location &location = std::source_location::current()
);
};
export template<typename Expression_T, typename... Args_T>
ensure(Expression_T, std::format_string<Args_T...>, Args_T &&...)
-> ensure<Expression_T, Args_T...>;
///////////////////////////////////////
// * IMPLEMENTATION -- TEMPLATES * //
/////////////////////////////////////
template<typename Expression_T, typename... Args_T>
ensure<Expression_T, Args_T...>::ensure(
const Expression_T &expression,
std::format_string<Args_T...> fmt,
Args_T &&...args,
const std::source_location &location
)
{
if (!static_cast<bool>(expression))
{
throw std::runtime_error { std::format(
"exception: {}\nlocation: {}:{}",
std::format(fmt, std::forward<Args_T>(args)...),
location.file_name(),
location.line()
) };
}
}
} // namespace lt::debug

View file

@ -1,7 +1,83 @@
#include <logger/logger.hpp>
#include <lt_debug/instrumentor.hpp>
export module debug.instrumentor;
namespace lt {
import std;
import logger;
namespace lt::debug {
struct ScopeProfileResult
{
std::string name;
long long start, duration;
std::uint32_t threadID;
};
class Instrumentor
{
public:
static auto instance() -> Instrumentor &
{
static auto instance = Instrumentor {};
return instance;
}
static void begin_session(const std::string &outputPath)
{
instance().begin_session_impl(outputPath);
}
static void end_session()
{
instance().end_session_impl();
}
static void submit_scope_profile(const ScopeProfileResult &profileResult)
{
instance().submit_scope_profile_impl(profileResult);
}
private:
std::ofstream m_output_file_stream;
unsigned int m_current_session_count { 0u };
Instrumentor() = default;
void begin_session_impl(const std::string &outputPath);
void end_session_impl();
void submit_scope_profile_impl(const ScopeProfileResult &profileResult);
};
class InstrumentorTimer
{
public:
InstrumentorTimer(const std::string &scopeName);
~InstrumentorTimer();
private:
ScopeProfileResult m_result;
std::chrono::time_point<std::chrono::steady_clock> m_start;
};
} // namespace lt::debug
/* scope */
#define lt_profile_scope(name) lt_profile_scope_no_redifinition(name, __LINE__)
#define lt_profile_scope_no_redifinition(name, line) lt_profile_scope_no_redifinition2(name, line)
#define lt_profile_scope_no_redifinition2(name, line) InstrumentorTimer timer##line(name)
/* function */
#define LT_PROFILE_FUNCTION lt_profile_scope(__FUNCSIG__)
/* session */
#define lt_profile_begin_session(outputPath) ::lt::Instrumentor::begin_session(outputPath)
#define lt_profile_end_session() ::lt::Instrumentor::end_session()
module :private;
using namespace lt::debug;
void Instrumentor::begin_session_impl(const std::string &outputPath)
{
@ -67,5 +143,3 @@ InstrumentorTimer::~InstrumentorTimer()
Instrumentor::submit_scope_profile(m_result);
}
} // namespace lt

View file

@ -1,3 +0,0 @@
#pragma once
#include <lt_debug/assertions.hpp>

View file

@ -1,34 +0,0 @@
#pragma once
#include <format>
#include <source_location>
namespace lt {
template<typename Expression_T, typename... Args_T>
struct ensure
{
ensure(
const Expression_T &expression,
std::format_string<Args_T...> fmt,
Args_T &&...args,
const std::source_location &location = std::source_location::current()
)
{
if (!static_cast<bool>(expression))
{
throw std::runtime_error { std::format(
"exception: {}\nlocation: {}:{}",
std::format(fmt, std::forward<Args_T>(args)...),
location.file_name(),
location.line()
) };
}
}
};
template<typename Expression_T, typename... Args_T>
ensure(Expression_T, std::format_string<Args_T...>, Args_T &&...)
-> ensure<Expression_T, Args_T...>;
} // namespace lt

View file

@ -1,77 +0,0 @@
#pragma once
#include <chrono>
#include <fstream>
namespace lt {
struct ScopeProfileResult
{
std::string name;
long long start, duration;
uint32_t threadID;
};
class Instrumentor
{
public:
static auto instance() -> Instrumentor &
{
static auto instance = Instrumentor {};
return instance;
}
static void begin_session(const std::string &outputPath)
{
instance().begin_session_impl(outputPath);
}
static void end_session()
{
instance().end_session_impl();
}
static void submit_scope_profile(const ScopeProfileResult &profileResult)
{
instance().submit_scope_profile_impl(profileResult);
}
private:
std::ofstream m_output_file_stream;
unsigned int m_current_session_count { 0u };
Instrumentor() = default;
void begin_session_impl(const std::string &outputPath);
void end_session_impl();
void submit_scope_profile_impl(const ScopeProfileResult &profileResult);
};
class InstrumentorTimer
{
public:
InstrumentorTimer(const std::string &scopeName);
~InstrumentorTimer();
private:
ScopeProfileResult m_result;
std::chrono::time_point<std::chrono::steady_clock> m_start;
};
} // namespace lt
/* scope */
#define lt_profile_scope(name) lt_profile_scope_no_redifinition(name, __LINE__)
#define lt_profile_scope_no_redifinition(name, line) lt_profile_scope_no_redifinition2(name, line)
#define lt_profile_scope_no_redifinition2(name, line) InstrumentorTimer timer##line(name)
/* function */
#define LT_PROFILE_FUNCTION lt_profile_scope(__FUNCSIG__)
/* session */
#define lt_profile_begin_session(outputPath) ::lt::Instrumentor::begin_session(outputPath)
#define lt_profile_end_session() ::lt::Instrumentor::end_session()

View file

@ -1,4 +1,5 @@
add_library_module(ecs sparse_set.cpp)
add_library_module(NAME ecs INTERFACES sparse_set.cppm registry.cppm
entity.cppm)
target_link_libraries(ecs PUBLIC logger lt_debug memory)
add_test_module(ecs sparse_set.test.cpp registry.test.cpp)

View file

@ -1,19 +1,20 @@
#pragma once
#include <ecs/registry.hpp>
#include <memory/reference.hpp>
export module ecs.entity;
import debug.assertions;
import memory.reference;
import ecs.registry;
import std;
namespace lt::ecs {
/** High-level entity convenience wrapper */
class Entity
export class Entity
{
public:
Entity(memory::Ref<Registry> registry, EntityId identifier)
: m_registry(std::move(registry))
, m_identifier(identifier)
{
ensure(m_registry, "Failed to create Entity ({}): null registry", m_identifier);
debug::ensure(m_registry, "Failed to create Entity ({}): null registry", m_identifier);
}
template<typename Component_T>

View file

@ -1,13 +1,14 @@
#pragma once
#include <ecs/sparse_set.hpp>
#include <memory/scope.hpp>
export module ecs.registry;
import debug.assertions;
import ecs.sparse_set;
import memory.scope;
import std;
namespace lt::ecs {
using EntityId = uint32_t;
export using EntityId = std::uint32_t;
constexpr auto null_entity = std::numeric_limits<EntityId>::max();
export constexpr auto null_entity = std::numeric_limits<EntityId>::max();
/** A registry of components, the heart of an ECS architecture.
*
@ -22,7 +23,7 @@ constexpr auto null_entity = std::numeric_limits<EntityId>::max();
* @ref https://github.com/skypjack/entt
* @ref https://github.com/SanderMertens/flecs
*/
class Registry
export class Registry
{
public:
using UnderlyingSparseSet_T = TypeErasedSparseSet<EntityId>;
@ -189,25 +190,25 @@ public:
}
};
[[nodiscard]] auto get_entity_count() const -> size_t
[[nodiscard]] auto get_entity_count() const -> std::size_t
{
return static_cast<size_t>(m_entity_count);
return static_cast<std::size_t>(m_entity_count);
}
private:
using TypeId = size_t;
using TypeId = std::size_t;
static consteval auto hash_cstr(const char *str) -> TypeId
{
constexpr auto fnv_offset_basis = size_t { 14695981039346656037ull };
constexpr auto fnv_prime = size_t { 1099511628211ull };
constexpr auto fnv_offset_basis = std::size_t { 14695981039346656037ull };
constexpr auto fnv_prime = std::size_t { 1099511628211ull };
auto hash = fnv_offset_basis;
for (const auto &ch : std::string_view { str })
{
hash *= fnv_prime;
hash ^= static_cast<uint8_t>(ch);
hash ^= static_cast<std::uint8_t>(ch);
}
return hash;
@ -241,7 +242,7 @@ private:
auto *base_set = m_sparsed_sets[type_id].get();
auto *derived_set = dynamic_cast<SparseSet<T, EntityId> *>(base_set);
ensure(derived_set, "Failed to downcast to derived set");
debug::ensure(derived_set, "Failed to downcast to derived set");
return *derived_set;
}

View file

@ -1,19 +1,17 @@
#include <ecs/registry.hpp>
#include <ranges>
#include <test/expects.hpp>
#include <test/test.hpp>
import ecs.registry;
import test.test;
import test.expects;
import std;
using lt::test::Case;
using lt::test::expect_unreachable;
using lt::test::Suite;
using lt::test::expect_eq;
using lt::test::expect_false;
using lt::test::expect_true;
using lt::ecs::EntityId;
using lt::ecs::Registry;
using ::lt::ecs::EntityId;
using ::lt::ecs::Registry;
using ::lt::test::Case;
using ::lt::test::expect_eq;
using ::lt::test::expect_false;
using ::lt::test::expect_true;
using ::lt::test::expect_unreachable;
using ::lt::test::Suite;
using ::lt::test::operator""_suite;
struct Component
{

View file

@ -1,12 +1,13 @@
#pragma once
export module ecs.sparse_set;
import debug.assertions;
import std;
namespace lt::ecs {
/**
*
* @ref https://programmingpraxis.com/2012/03/09/sparse-sets/
*/
template<typename Identifier_T = uint32_t>
export template<typename Identifier_T = std::uint32_t>
class TypeErasedSparseSet
{
public:
@ -25,19 +26,19 @@ public:
virtual void remove(Identifier_T identifier) = 0;
};
template<typename Value_T, typename Identifier_T = uint32_t>
export template<typename Value_T, typename Identifier_T = std::uint32_t>
class SparseSet: public TypeErasedSparseSet<Identifier_T>
{
public:
using Dense_T = std::pair<Identifier_T, Value_T>;
static constexpr auto max_capacity = size_t { 1'000'000 };
static constexpr auto max_capacity = std::size_t { 1'000'000 };
static constexpr auto null_identifier = std::numeric_limits<Identifier_T>().max();
explicit SparseSet(size_t initial_capacity = 1)
explicit SparseSet(std::size_t initial_capacity = 1)
{
ensure(
debug::ensure(
initial_capacity <= max_capacity,
"Failed to create SparseSet: capacity too large ({} > {})",
initial_capacity,
@ -52,7 +53,10 @@ public:
{
if (m_sparse.size() < identifier + 1)
{
auto new_capacity = std::max(static_cast<size_t>(identifier + 1), m_sparse.size() * 2);
auto new_capacity = std::max(
static_cast<std::size_t>(identifier + 1),
m_sparse.size() * 2
);
new_capacity = std::min(new_capacity, max_capacity);
// log::debug("Increasing sparse vector size:", m_dead_count);
@ -145,12 +149,12 @@ public:
return std::forward<Self_T>(self).m_dense[std::forward<Self_T>(self).m_sparse[identifier]];
}
[[nodiscard]] auto get_size() const noexcept -> size_t
[[nodiscard]] auto get_size() const noexcept -> std::size_t
{
return m_alive_count;
}
[[nodiscard]] auto get_capacity() const noexcept -> size_t
[[nodiscard]] auto get_capacity() const noexcept -> std::size_t
{
return m_sparse.capacity();
}
@ -165,9 +169,9 @@ private:
std::vector<Identifier_T> m_sparse;
size_t m_alive_count {};
std::size_t m_alive_count {};
size_t m_dead_count {};
std::size_t m_dead_count {};
};
} // namespace lt::ecs

View file

@ -1,16 +1,16 @@
#include <ecs/sparse_set.hpp>
#include <ranges>
#include <test/expects.hpp>
#include <test/test.hpp>
import ecs.sparse_set;
import test.test;
import test.expects;
import std;
using lt::test::Case;
using lt::test::Suite;
using lt::test::expect_eq;
using lt::test::expect_false;
using lt::test::expect_ne;
using lt::test::expect_throw;
using lt::test::expect_true;
using ::lt::test::Case;
using ::lt::test::expect_eq;
using ::lt::test::expect_false;
using ::lt::test::expect_ne;
using ::lt::test::expect_throw;
using ::lt::test::expect_true;
using ::lt::test::Suite;
using ::lt::test::operator""_suite;
using Set = lt::ecs::SparseSet<int>;
constexpr auto capacity = 100;

View file

@ -1 +1 @@
add_library_module(env)
add_library_module(NAME env INTERFACES constants.cppm)

View file

@ -1,8 +1,10 @@
#pragma once
export module env;
import std;
namespace lt {
enum class Platform : uint8_t
enum class Platform : std::uint8_t
{
/** The GNU/Linux platform.
* Tested on the following distros: arch-x86_64
@ -24,7 +26,7 @@ enum class Platform : uint8_t
};
/** The compiler that was used for compiling the project. */
enum class Compiler : uint8_t
enum class Compiler : std::uint8_t
{
clang,
gcc,

View file

@ -1,4 +1,4 @@
add_library_module(input system.cpp)
add_library_module(NAME input INTERFACES system.cpp)
target_link_libraries(input PUBLIC surface math logger tbb)
add_test_module(input system.test.cpp)

View file

@ -2,7 +2,6 @@
#include <cstdint>
namespace lt::Key {
enum : uint16_t
{
@ -177,4 +176,21 @@ enum : uint16_t
};
}
enum : uint8_t
{
Button1 = 0,
Button2 = 1,
Button3 = 2,
Button4 = 3,
Button5 = 4,
Button6 = 5,
Button7 = 6,
Button8 = 7,
LButton = Button1,
RButton = Button2,
MButton = Button3,
};
} // namespace lt::Key

View file

@ -1,4 +1,4 @@
#pragma once
export module input.components;
#include <vector>

View file

@ -1,23 +0,0 @@
#pragma once
#include <cstdint>
namespace lt::Mouse {
enum : uint8_t
{
Button1 = 0,
Button2 = 1,
Button3 = 2,
Button4 = 3,
Button5 = 4,
Button6 = 5,
Button7 = 6,
Button8 = 7,
LButton = Button1,
RButton = Button2,
MButton = Button3,
};
}

View file

@ -1,55 +0,0 @@
#pragma once
#include <app/system.hpp>
#include <ecs/registry.hpp>
#include <memory/reference.hpp>
#include <surface/components.hpp>
#include <surface/events/keyboard.hpp>
#include <surface/events/mouse.hpp>
namespace lt::input {
class System: public app::ISystem
{
public:
System(memory::Ref<ecs::Registry> registry);
void tick(app::TickInfo tick) override;
void on_register() override;
void on_unregister() override;
[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override
{
return m_last_tick_result;
}
private:
void handle_event(const surface::SurfaceComponent::Event &event);
void on_surface_lost_focus();
void on_key_press(const lt::surface::KeyPressedEvent &event);
void on_key_release(const lt::surface::KeyReleasedEvent &event);
void on_pointer_move(const lt::surface::MouseMovedEvent &event);
void on_button_press(const lt::surface::ButtonPressedEvent &event);
void on_button_release(const lt::surface::ButtonReleasedEvent &event);
memory::Ref<ecs::Registry> m_registry;
std::array<bool, 512> m_keys {};
std::array<bool, 512> m_buttons {};
math::vec2 m_pointer_position;
app::TickResult m_last_tick_result {};
};
} // namespace lt::input

View file

@ -1,3 +1,62 @@
#pragma once
#include <app/system.hpp>
#include <ecs/registry.hpp>
#include <memory/reference.hpp>
#include <surface/components.hpp>
#include <surface/events/keyboard.hpp>
#include <surface/events/mouse.hpp>
namespace lt::input {
class System: public app::ISystem
{
public:
System(memory::Ref<ecs::Registry> registry);
void tick(app::TickInfo tick) override;
void on_register() override;
void on_unregister() override;
[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override
{
return m_last_tick_result;
}
private:
void handle_event(const surface::SurfaceComponent::Event &event);
void on_surface_lost_focus();
void on_key_press(const lt::surface::KeyPressedEvent &event);
void on_key_release(const lt::surface::KeyReleasedEvent &event);
void on_pointer_move(const lt::surface::MouseMovedEvent &event);
void on_button_press(const lt::surface::ButtonPressedEvent &event);
void on_button_release(const lt::surface::ButtonReleasedEvent &event);
memory::Ref<ecs::Registry> m_registry;
std::array<bool, 512> m_keys {};
std::array<bool, 512> m_buttons {};
math::vec2 m_pointer_position;
app::TickResult m_last_tick_result {};
};
} // namespace lt::input
module :private;
#include <input/components.hpp>
#include <input/system.hpp>
#include <memory/reference.hpp>

View file

@ -1,2 +1,2 @@
add_library_module(logger)
add_test_module(logger logger.test.cpp)
add_library_module(NAME logger INTERFACES logger.cppm)
# add_test_module(logger logger.test.cpp)

View file

@ -1,17 +1,11 @@
#pragma once
export module logger;
#include <cstdint>
#include <filesystem>
#include <fmt/chrono.h>
#include <fmt/format.h>
#include <source_location>
#include <thread>
#include <utility>
import std;
namespace lt::log {
/** Severity of a log message. */
enum class Level : uint8_t
enum class Level : std::uint8_t
{
/** Lowest and most vebose log level, for tracing execution paths and events */
trace = 0,
@ -87,7 +81,7 @@ template<typename... Args>
print(Level, const std::source_location &, std::format_string<Args...>, Args &&...) noexcept
-> print<Args...>;
template<typename... Args>
export template<typename... Args>
struct [[maybe_unused]] trace
{
[[maybe_unused]] trace(
@ -100,10 +94,10 @@ struct [[maybe_unused]] trace
}
};
template<typename... Args>
export template<typename... Args>
trace(std::format_string<Args...>, Args &&...) noexcept -> trace<Args...>;
template<typename... Args>
export template<typename... Args>
struct [[maybe_unused]] debug
{
[[maybe_unused]] debug(
@ -116,10 +110,10 @@ struct [[maybe_unused]] debug
}
};
template<typename... Args>
export template<typename... Args>
debug(std::format_string<Args...>, Args &&...) noexcept -> debug<Args...>;
template<typename... Args>
export template<typename... Args>
struct [[maybe_unused]] info
{
[[maybe_unused]] info(
@ -132,10 +126,10 @@ struct [[maybe_unused]] info
}
};
template<typename... Args>
export template<typename... Args>
info(std::format_string<Args...>, Args &&...) noexcept -> info<Args...>;
template<typename... Args>
export template<typename... Args>
struct [[maybe_unused]] warn
{
[[maybe_unused]] warn(
@ -148,10 +142,10 @@ struct [[maybe_unused]] warn
}
};
template<typename... Args>
export template<typename... Args>
warn(std::format_string<Args...>, Args &&...) noexcept -> warn<Args...>;
template<typename... Args>
export template<typename... Args>
struct [[maybe_unused]] error
{
[[maybe_unused]] error(
@ -164,10 +158,10 @@ struct [[maybe_unused]] error
}
};
template<typename... Args>
export template<typename... Args>
error(std::format_string<Args...>, Args &&...) noexcept -> error<Args...>;
template<typename... Args>
export template<typename... Args>
struct [[maybe_unused]] critical
{
[[maybe_unused]] critical(
@ -180,7 +174,7 @@ struct [[maybe_unused]] critical
}
};
template<typename... Args>
export template<typename... Args>
critical(std::format_string<Args...>, Args &&...) noexcept -> critical<Args...>;
} // namespace lt::log

View file

@ -1,5 +1,5 @@
#include <logger/logger.hpp>
#include <test/test.hpp>
import logger;
import test;
using ::lt::test::Case;
using ::lt::test::Suite;

View file

@ -1 +1,11 @@
add_library_module(math)
add_library_module(
NAME
math
INTERFACES
algebra.cppm
mat4.cppm
trig.cppm
vec2.cppm
vec3.cppm
vec4.cppm
components/transform.cppm)

View file

@ -1,6 +1,6 @@
#pragma once
#include <math/mat4.hpp>
export module math.algebra;
import math.mat4;
import std;
namespace lt::math {

View file

@ -1,10 +1,10 @@
#pragma once
export module math.components;
#include <math/vec3.hpp>
import math.vec3;
namespace lt::math::components {
struct Transform
export struct Transform
{
math::vec3 translation;

View file

@ -1,11 +1,11 @@
#pragma once
#include <math/vec3.hpp>
#include <math/vec4.hpp>
export module math.mat4;
import math.vec3;
import math.vec4;
import std;
namespace lt::math {
template<typename T = float>
export template<typename T = float>
struct mat4_impl
{
using Column_T = vec4_impl<T>;
@ -54,12 +54,12 @@ struct mat4_impl
};
}
[[nodiscard]] constexpr auto operator[](size_t idx) -> Column_T &
[[nodiscard]] constexpr auto operator[](std::size_t idx) -> Column_T &
{
return values[idx];
}
[[nodiscard]] constexpr auto operator[](size_t idx) const -> const Column_T &
[[nodiscard]] constexpr auto operator[](std::size_t idx) const -> const Column_T &
{
return values[idx];
}
@ -77,34 +77,34 @@ struct mat4_impl
std::array<Column_T, 4> values; // NOLINT
};
template<typename T>
[[nodiscard]] inline auto translate(const vec3_impl<T> &value) -> mat4_impl<T>
export template<typename T>
[[nodiscard]] auto translate(const vec3_impl<T> &value) -> mat4_impl<T>
{
return mat4_impl<T> {};
}
template<typename T>
[[nodiscard]] inline auto rotate(float value, const vec3_impl<T> &xyz) -> mat4_impl<T>
export template<typename T>
[[nodiscard]] auto rotate(float value, const vec3_impl<T> &xyz) -> mat4_impl<T>
{
return mat4_impl<T> {};
}
template<typename T>
[[nodiscard]] inline auto scale(const vec3_impl<T> &value) -> mat4_impl<T>
export template<typename T>
[[nodiscard]] auto scale(const vec3_impl<T> &value) -> mat4_impl<T>
{
return mat4_impl<T> {};
}
template<typename T>
[[nodiscard]] inline auto inverse(const mat4_impl<T> &value) -> mat4_impl<T>
export template<typename T>
[[nodiscard]] auto inverse(const mat4_impl<T> &value) -> mat4_impl<T>
{
return mat4_impl<T> {};
}
using mat4 = mat4_impl<float>;
export using mat4 = mat4_impl<float>;
using imat4 = mat4_impl<int32_t>;
export using imat4 = mat4_impl<std::int32_t>;
using umat4 = mat4_impl<uint32_t>;
export using umat4 = mat4_impl<std::uint32_t>;
} // namespace lt::math

View file

@ -1,4 +1,4 @@
#pragma once
export module math.trig;
namespace lt::math {

View file

@ -1,8 +1,10 @@
#pragma once
export module math.vec2;
import std;
namespace lt::math {
template<typename T = float>
export template<typename T = float>
struct vec2_impl
{
constexpr vec2_impl(): x(), y()
@ -57,15 +59,15 @@ struct vec2_impl
};
using vec2 = vec2_impl<float>;
export using vec2 = vec2_impl<float>;
using ivec2 = vec2_impl<int32_t>;
export using ivec2 = vec2_impl<std::int32_t>;
using uvec2 = vec2_impl<uint32_t>;
export using uvec2 = vec2_impl<std::uint32_t>;
} // namespace lt::math
template<typename T>
export template<typename T>
struct std::formatter<lt::math::vec2_impl<T>>
{
constexpr auto parse(std::format_parse_context &context)

View file

@ -1,11 +1,11 @@
#pragma once
export module math.vec3;
#include <cstdint>
#include <math/vec2.hpp>
import math.vec2;
import std;
namespace lt::math {
template<typename T = float>
export template<typename T = float>
struct vec3_impl
{
constexpr vec3_impl(): x(), y(), z()
@ -61,11 +61,11 @@ struct vec3_impl
T z; // NOLINT
};
using vec3 = vec3_impl<float>;
export using vec3 = vec3_impl<float>;
using ivec3 = vec3_impl<int32_t>;
export using ivec3 = vec3_impl<std::int32_t>;
using uvec3 = vec3_impl<uint32_t>;
export using uvec3 = vec3_impl<std::uint32_t>;
} // namespace lt::math

View file

@ -1,11 +1,11 @@
#pragma once
#include <array>
#include <cstdint>
export module math.vec4;
import math.vec2;
import math.vec3;
import std;
namespace lt::math {
template<typename T = float>
export template<typename T = float>
struct vec4_impl
{
constexpr vec4_impl(): x(), y(), z(), w()
@ -40,12 +40,12 @@ struct vec4_impl
};
}
[[nodiscard]] constexpr auto operator[](size_t idx) -> T &
[[nodiscard]] constexpr auto operator[](std::size_t idx) -> T &
{
return values[idx];
}
[[nodiscard]] constexpr auto operator[](size_t idx) const -> const T &
[[nodiscard]] constexpr auto operator[](std::size_t idx) const -> const T &
{
return values[idx];
}
@ -86,15 +86,15 @@ struct vec4_impl
};
};
using vec4 = vec4_impl<float>;
export using vec4 = vec4_impl<float>;
using ivec4 = vec4_impl<int32_t>;
export using ivec4 = vec4_impl<std::int32_t>;
using uvec4 = vec4_impl<uint32_t>;
export using uvec4 = vec4_impl<std::uint32_t>;
} // namespace lt::math
template<typename T>
export template<typename T>
struct std::formatter<lt::math::vec4_impl<T>>
{
constexpr auto parse(std::format_parse_context &context)

View file

@ -1 +1,2 @@
add_library_module(memory)
add_library_module(NAME memory INTERFACES null_on_move.cppm reference.cppm
scope.cppm)

View file

@ -1,11 +1,13 @@
#pragma once
export module memory.null_on_move;
import std;
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.
* Vulkan objects. But may serve other purposes, hence why I kept the implementation generic.
*/
template<typename Underlying_T, Underlying_T null_value = nullptr>
class NullOnMove
@ -77,9 +79,9 @@ public:
return m_value;
}
operator uint64_t() const
operator std::uint64_t() const
{
return (uint64_t)m_value;
return (std::uint64_t)m_value;
}
[[nodiscard]] auto get() -> Underlying_T

View file

@ -1,7 +1,6 @@
#pragma once
export module memory.reference;
#include <memory/reference.hpp>
#include <memory>
import std;
namespace lt::memory {
@ -10,21 +9,21 @@ namespace lt::memory {
* @note Currently just an alias, might turn into an implementation later.
* @ref https://en.cppreference.com/w/cpp/memory/shared_ptr.html
*/
template<typename t>
using Ref = std::shared_ptr<t>;
export 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>
export 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>
export template<typename Underlying_T>
constexpr Ref<Underlying_T> make_ref(Underlying_T *raw_pointer)
{
return Ref<Underlying_T>(raw_pointer);

View file

@ -1,30 +1,29 @@
#pragma once
export module memory.scope;
#include <memory/scope.hpp>
#include <memory>
import std;
namespace lt::memory {
/** Wrapper around std::unique_ptr.
/** @brief Wrapper around std::unique_ptr.
*
* @note Currently just an alias, might turn into an implementation later.
* @ref https://en.cppreference.com/w/cpp/memory/unique_ptr.html
*/
template<typename t>
export 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>
export 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>
export template<typename Underlying_T>
constexpr Scope<Underlying_T> make_scope(Underlying_T *raw_pointer)
{
return Scope<Underlying_T>(raw_pointer);

View file

@ -6,11 +6,13 @@
#include <ecs/entity.hpp>
#include <input/components.hpp>
#include <input/system.hpp>
#include <math/components/transform.hpp>
#include <math/trig.hpp>
#include <math/vec2.hpp>
#include <memory/reference.hpp>
#include <memory/scope.hpp>
#include <renderer/components/messenger.hpp>
#include <renderer/components/sprite.hpp>
#include <renderer/system.hpp>
#include <surface/events/keyboard.hpp>
#include <surface/events/surface.hpp>
@ -30,7 +32,7 @@ void renderer_callback(
std::ignore = message_type;
std::ignore = user_data;
log_dbg("RENDERER CALLBACK: {}", data.message);
log::debug("RENDERER CALLBACK: {}", data.message);
}
class MirrorSystem: public lt::app::ISystem
@ -145,7 +147,7 @@ public:
void on_window_close()
{
log_inf("Window close requested...");
log::info("Window close requested...");
unregister_system(m_input_system);
unregister_system(m_surface_system);
@ -227,6 +229,21 @@ public:
.user_data = this,
} });
m_sprite_id = m_editor_registry->create_entity();
m_editor_registry->add(
m_sprite_id,
renderer::components::Sprite { .color = lt::math::vec3 { 1.0f, 0.0f, 0.0f } }
);
m_editor_registry->add(
m_sprite_id,
math::components::Transform {
.translation = { -5.0, -5.0, 0.5 },
.scale = { 5.0, 5.0, 1.0 },
.rotation = {},
}
);
m_camera_id = m_editor_registry->create_entity();
m_editor_registry->add(
@ -268,6 +285,8 @@ private:
lt::ecs::EntityId m_window = lt::ecs::null_entity;
lt::ecs::EntityId m_camera_id = lt::ecs::null_entity;
lt::ecs::EntityId m_sprite_id = lt::ecs::null_entity;
};
auto app::create_application() -> memory::Scope<app::Application>

View file

@ -109,7 +109,7 @@ void AssetBrowserPanel::on_user_interface_update()
))
{
auto serializer = SceneSerializer { m_active_scene };
log_inf("Attempting to deserialize: {}", path.string());
log::info("Attempting to deserialize: {}", path.string());
serializer.deserialize(path.string());
}
break;

View file

@ -1,4 +1,5 @@
//
#include <logger/logger.hpp>
#include <renderer/backend/vk/context/device.hpp>
#include <renderer/backend/vk/context/gpu.hpp>
#include <renderer/backend/vk/context/surface.hpp>
@ -47,8 +48,8 @@ Device::~Device()
}
catch (const std::exception &exp)
{
log_err("Failed to destroy vk device:");
log_err("\twhat: {}", exp.what());
log::error("Failed to destroy vk device:");
log::error("\twhat: {}", exp.what());
}
}
@ -76,26 +77,106 @@ void Device::initialize_logical_device()
auto extensions = std::vector<const char *> {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
};
auto descriptor_indexing_features = m_gpu->get_descriptor_indexing_features();
log::debug("");
log::debug(
"shaderInputAttachmentArrayDynamicIndexing: {}",
descriptor_indexing_features.shaderInputAttachmentArrayDynamicIndexing
);
log::debug(
" shaderUniformTexelBufferArrayDynamicIndexing: {}",
descriptor_indexing_features.shaderUniformTexelBufferArrayDynamicIndexing
);
log::debug(
" shaderStorageTexelBufferArrayDynamicIndexing: {}",
descriptor_indexing_features.shaderStorageTexelBufferArrayDynamicIndexing
);
log::debug(
" shaderUniformBufferArrayNonUniformIndexing: {}",
descriptor_indexing_features.shaderUniformBufferArrayNonUniformIndexing
);
log::debug(
" shaderSampledImageArrayNonUniformIndexing: {}",
descriptor_indexing_features.shaderSampledImageArrayNonUniformIndexing
);
log::debug(
" shaderStorageBufferArrayNonUniformIndexing: {}",
descriptor_indexing_features.shaderStorageBufferArrayNonUniformIndexing
);
log::debug(
" shaderStorageImageArrayNonUniformIndexing: {}",
descriptor_indexing_features.shaderStorageImageArrayNonUniformIndexing
);
log::debug(
" shaderInputAttachmentArrayNonUniformIndexing: {}",
descriptor_indexing_features.shaderInputAttachmentArrayNonUniformIndexing
);
log::debug(
" shaderUniformTexelBufferArrayNonUniformIndexing: {}",
descriptor_indexing_features.shaderUniformTexelBufferArrayNonUniformIndexing
);
log::debug(
" shaderStorageTexelBufferArrayNonUniformIndexing: {}",
descriptor_indexing_features.shaderStorageTexelBufferArrayNonUniformIndexing
);
log::debug(
" descriptorBindingUniformBufferUpdateAfterBind: {}",
descriptor_indexing_features.descriptorBindingUniformBufferUpdateAfterBind
);
log::debug(
" descriptorBindingSampledImageUpdateAfterBind: {}",
descriptor_indexing_features.descriptorBindingSampledImageUpdateAfterBind
);
log::debug(
" descriptorBindingStorageImageUpdateAfterBind: {}",
descriptor_indexing_features.descriptorBindingStorageImageUpdateAfterBind
);
log::debug(
" descriptorBindingStorageBufferUpdateAfterBind: {}",
descriptor_indexing_features.descriptorBindingStorageBufferUpdateAfterBind
);
log::debug(
" descriptorBindingUniformTexelBufferUpdateAfterBind: {}",
descriptor_indexing_features.descriptorBindingUniformTexelBufferUpdateAfterBind
);
log::debug(
" descriptorBindingStorageTexelBufferUpdateAfterBind: {}",
descriptor_indexing_features.descriptorBindingStorageTexelBufferUpdateAfterBind
);
log::debug(
" descriptorBindingUpdateUnusedWhilePending: {}",
descriptor_indexing_features.descriptorBindingUpdateUnusedWhilePending
);
log::debug(
" descriptorBindingPartiallyBound: {}",
descriptor_indexing_features.descriptorBindingPartiallyBound
);
log::debug(
" descriptorBindingVariableDescriptorCount: {}",
descriptor_indexing_features.descriptorBindingVariableDescriptorCount
);
log::debug(" runtimeDescriptorArray: {}", descriptor_indexing_features.runtimeDescriptorArray);
const auto dynamic_rendering_features = VkPhysicalDeviceDynamicRenderingFeatures {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES,
.pNext = &descriptor_indexing_features,
.dynamicRendering = true,
};
auto device_info = VkDeviceCreateInfo {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &dynamic_rendering_features,
.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size()),
.pQueueCreateInfos = queue_infos.data(),
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
.ppEnabledExtensionNames = extensions.data(),
.pEnabledFeatures = &physical_device_features,
};
ensure(
!vk_create_device(m_gpu->vk(), &device_info, nullptr, &m_device),
"Failed to create logical vulkan device"
m_device = m_gpu->create_device(
VkDeviceCreateInfo {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &dynamic_rendering_features,
.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size()),
.pQueueCreateInfos = queue_infos.data(),
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
.ppEnabledExtensionNames = extensions.data(),
.pEnabledFeatures = &physical_device_features,
}
);
}
@ -284,13 +365,14 @@ void Device::unmap_memory(VkDeviceMemory memory)
}
[[nodiscard]] auto Device::create_pipeline_layout(
std::vector<VkDescriptorSetLayout> descriptor_set_layout,
std::vector<VkPushConstantRange> push_constant_ranges
) const -> VkPipelineLayout
{
auto info = VkPipelineLayoutCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0u,
.pSetLayouts = nullptr,
.setLayoutCount = static_cast<uint32_t>(descriptor_set_layout.size()),
.pSetLayouts = descriptor_set_layout.data(),
.pushConstantRangeCount = static_cast<uint32_t>(push_constant_ranges.size()),
.pPushConstantRanges = push_constant_ranges.data(),
};
@ -346,6 +428,21 @@ void Device::unmap_memory(VkDeviceMemory memory)
vkc(vk_create_buffer(m_device, &info, nullptr, &buffer));
return buffer;
}
[[nodiscard]] auto Device::create_desscriptor_pool(VkDescriptorPoolCreateInfo info) const
-> VkDescriptorPool
{
auto *pool = VkDescriptorPool {};
vkc(vk_create_descriptor_pool(m_device, &info, nullptr, &pool));
return pool;
}
[[nodiscard]] auto Device::create_descriptor_set_layout(VkDescriptorSetLayoutCreateInfo info) const
-> VkDescriptorSetLayout
{
auto *layout = VkDescriptorSetLayout {};
vkc(vk_create_descriptor_set_layout(m_device, &info, nullptr, &layout));
return layout;
}
[[nodiscard]] auto Device::allocate_command_buffers(VkCommandBufferAllocateInfo info) const
-> std::vector<VkCommandBuffer>
@ -362,11 +459,27 @@ void Device::unmap_memory(VkDeviceMemory memory)
return memory;
}
[[nodiscard]] auto Device::allocate_descriptor_set(VkDescriptorSetAllocateInfo info) const
-> VkDescriptorSet
{
auto *descriptor_set = VkDescriptorSet {};
vkc(vk_allocate_descriptor_sets(m_device, &info, &descriptor_set));
return descriptor_set;
}
void Device::free_memory(VkDeviceMemory memory) const
{
vk_free_memory(m_device, memory, nullptr);
}
void Device::free_descriptor_set(
VkDescriptorPool descriptor_pool,
VkDescriptorSet descriptor_set
) const
{
vkc(vk_free_descriptor_sets(m_device, descriptor_pool, 1, &descriptor_set));
}
void Device::destroy_swapchain(VkSwapchainKHR swapchain) const
{
vk_destroy_swapchain_khr(m_device, swapchain, m_allocator);
@ -454,4 +567,14 @@ void Device::destroy_buffer(VkBuffer buffer) const
vk_destroy_buffer(m_device, buffer, nullptr);
}
void Device::destroy_descriptor_set_layout(VkDescriptorSetLayout layout) const
{
vk_destroy_descriptor_set_layout(m_device, layout, nullptr);
}
void Device::destroy_descriptor_pool(VkDescriptorPool pool) const
{
vk_destroy_descriptor_pool(m_device, pool, nullptr);
}
} // namespace lt::renderer::vk

View file

@ -119,6 +119,7 @@ public:
[[nodiscard]] auto create_pass(VkRenderPassCreateInfo info) const -> VkRenderPass;
[[nodiscard]] auto create_pipeline_layout(
std::vector<VkDescriptorSetLayout> descriptor_set_layout,
std::vector<VkPushConstantRange> push_constant_ranges
) const -> VkPipelineLayout;
@ -133,15 +134,29 @@ public:
[[nodiscard]] auto create_buffer(VkBufferCreateInfo info) const -> VkBuffer;
[[nodiscard]] auto create_descriptor_set_layout(VkDescriptorSetLayoutCreateInfo info) const
-> VkDescriptorSetLayout;
[[nodiscard]] auto create_desscriptor_pool(VkDescriptorPoolCreateInfo info) const
-> VkDescriptorPool;
/** allocation functions */
[[nodiscard]] auto allocate_memory(VkMemoryAllocateInfo info) const -> VkDeviceMemory;
[[nodiscard]] auto allocate_command_buffers(VkCommandBufferAllocateInfo info) const
-> std::vector<VkCommandBuffer>;
[[nodiscard]] auto allocate_descriptor_set(VkDescriptorSetAllocateInfo info) const
-> VkDescriptorSet;
/** de-allocation functions */
void free_memory(VkDeviceMemory memory) const;
void free_descriptor_set(
VkDescriptorPool descriptor_pool,
VkDescriptorSet descriptor_set
) const;
/** destroy functions */
void destroy_swapchain(VkSwapchainKHR swapchain) const;
@ -173,6 +188,10 @@ public:
void destroy_buffer(VkBuffer buffer) const;
void destroy_descriptor_set_layout(VkDescriptorSetLayout layout) const;
void destroy_descriptor_pool(VkDescriptorPool pool) const;
private:
template<typename T>
static auto get_object_type(const T &object) -> VkObjectType

View file

@ -244,10 +244,10 @@ void Instance::initialize_instance()
memset(extensions.data(), 0, extensions.size() * sizeof(VkExtensionProperties));
vkc(vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data()));
// log_inf("Available vulkan instance extensions:");
// log::info("Available vulkan instance extensions:");
for (auto &ext : extensions)
{
// log_inf("\t{} @ {}", ext.extensionName, ext.specVersion);
// log::info("\t{} @ {}", ext.extensionName, ext.specVersion);
}
}
@ -293,7 +293,7 @@ void Instance::load_global_functions()
// 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);
// log::trace("Loaded global function: {}", fn_name);
};
load_fn(vk_create_instance, "vkCreateInstance");
@ -307,7 +307,7 @@ void Instance::load_instance_functions()
// 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);
// log::trace("Loaded instance function: {}", fn_name);
};
load_fn(vk_destroy_instance, "vkDestroyInstance");
@ -352,7 +352,7 @@ void Instance::load_device_functions_impl(VkDevice device)
// 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);
// log::trace("Loaded device function: {}", fn_name);
};
load_fn(vk_get_device_queue, "vkGetDeviceQueue");

View file

@ -1,3 +1,4 @@
#include <logger/logger.hpp>
#include <ranges>
#include <renderer/backend/vk/context/device.hpp>
#include <renderer/backend/vk/context/gpu.hpp>
@ -95,8 +96,8 @@ Swapchain::~Swapchain()
}
catch (const std::exception &exp)
{
log_err("Failed to destroy swapchain:");
log_err("\twhat: {}", exp.what());
log::error("Failed to destroy swapchain:");
log::error("\twhat: {}", exp.what());
}
}

View file

@ -0,0 +1,9 @@
#pragma once
namespace lt::renderer::vk {
class Descriptors
{
};
} // namespace lt::renderer::vk

View file

@ -1,3 +1,4 @@
#include <logger/logger.hpp>
#include <renderer/backend/vk/messenger.hpp>
namespace lt::renderer::vk {
@ -42,8 +43,8 @@ Messenger::Messenger(IInstance *instance, CreateInfo info)
}
catch (const std::exception &exp)
{
log_err("Uncaught exception in messenger callback:");
log_err("\twhat: {}", exp.what());
log::error("Uncaught exception in messenger callback:");
log::error("\twhat: {}", exp.what());
}
return VK_FALSE;

View file

@ -12,16 +12,85 @@ Pass::Pass(
const lt::assets::ShaderAsset &fragment_shader
)
: m_device(static_cast<Device *>(device))
, m_layout(m_device->create_pipeline_layout(
std::vector<VkPushConstantRange> {
VkPushConstantRange {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0u,
.size = sizeof(FrameConstants),
},
}
))
{
auto binding = VkDescriptorSetLayoutBinding {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1'000,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
};
const auto descriptor_binding_flags = VkDescriptorBindingFlagsEXT {
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT
| VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT
| VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT
| VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT,
};
constexpr auto descriptor_count = uint32_t { 1'000 };
auto descriptor_binding_flags_info = VkDescriptorSetLayoutBindingFlagsCreateInfoEXT {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT,
.bindingCount = 1,
.pBindingFlags = &descriptor_binding_flags,
};
m_vertices_descriptor_set_layout = m_device->create_descriptor_set_layout(
{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = &descriptor_binding_flags_info,
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT,
.bindingCount = 1u,
.pBindings = &binding,
}
);
auto pool_size = VkDescriptorPoolSize {
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = descriptor_count,
};
m_descriptor_pool = m_device->create_desscriptor_pool(
{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = 1u,
.pPoolSizes = &pool_size,
}
);
auto descriptor_set_variable_descriptor_count_info
= VkDescriptorSetVariableDescriptorCountAllocateInfo {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
.descriptorSetCount = 1u,
.pDescriptorCounts = &descriptor_count,
};
m_vertices_descriptor_set = m_device->allocate_descriptor_set(
VkDescriptorSetAllocateInfo {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pNext = &descriptor_set_variable_descriptor_count_info,
.descriptorPool = m_descriptor_pool,
.descriptorSetCount = 1u,
.pSetLayouts = &m_vertices_descriptor_set_layout,
}
);
m_layout = m_device->create_pipeline_layout(
std::vector<VkDescriptorSetLayout> {
m_vertices_descriptor_set_layout,
},
std::vector<VkPushConstantRange> {
VkPushConstantRange {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0u,
.size = sizeof(FrameConstants),
},
}
);
auto *vertex_module = create_module(
vertex_shader.unpack(lt::assets::ShaderAsset::BlobTag::code)
);
@ -188,6 +257,11 @@ Pass::~Pass()
}
m_device->wait_idle();
m_device->destroy_descriptor_set_layout(m_vertices_descriptor_set_layout);
m_device->free_descriptor_set(m_descriptor_pool, m_vertices_descriptor_set);
m_device->destroy_descriptor_pool(m_descriptor_pool);
m_device->destroy_framebuffers(m_framebuffers);
m_device->destroy_pipeline(m_pipeline);
// m_device->destroy_pass(m_pass);

View file

@ -54,6 +54,12 @@ private:
VkPipelineLayout m_layout = VK_NULL_HANDLE;
std::vector<VkFramebuffer> m_framebuffers;
VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE;
VkDescriptorSetLayout m_vertices_descriptor_set_layout;
VkDescriptorSet m_vertices_descriptor_set = VK_NULL_HANDLE;
};
} // namespace lt::renderer::vk

View file

@ -4,11 +4,30 @@
namespace lt::renderer::vk {
Renderer::Renderer(IDevice *device, ISwapchain *swapchain, uint32_t max_frames_in_flight)
Renderer::Renderer(IGpu *gpu, IDevice *device, ISwapchain *swapchain, uint32_t max_frames_in_flight)
: m_device(static_cast<Device *>(device))
, m_swapchain(static_cast<Swapchain *>(swapchain))
, m_resolution(m_swapchain->get_resolution())
, m_max_frames_in_flight(max_frames_in_flight)
, m_staging_offset()
, m_vertex_buffer(
device,
gpu,
IBuffer::CreateInfo {
.usage = IBuffer::Usage::vertex,
.size = 1'000'000,
.debug_name = "vertex buffer",
}
)
, m_staging_buffer(
device,
gpu,
IBuffer::CreateInfo {
.usage = IBuffer::Usage::staging,
.size = 1'000'000,
.debug_name = "staging buffer",
}
)
{
ensure(m_device, "Failed to initialize renderer: null device");
ensure(m_swapchain, "Failed to initialize renderer: null swapchain");
@ -17,7 +36,7 @@ Renderer::Renderer(IDevice *device, ISwapchain *swapchain, uint32_t max_frames_i
m_pass = memory::create_ref<vk::Pass>(
m_device,
m_swapchain,
assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
assets::ShaderAsset { "./data/test_assets/sprite.vert.asset" },
assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" }
);
@ -29,6 +48,15 @@ Renderer::Renderer(IDevice *device, ISwapchain *swapchain, uint32_t max_frames_i
}
);
m_transient_pool = m_device->create_command_pool(
VkCommandPoolCreateInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
.queueFamilyIndex = m_device->get_family_indices()[0],
}
);
m_cmds.resize(m_max_frames_in_flight);
m_cmds = m_device->allocate_command_buffers(
VkCommandBufferAllocateInfo {
@ -73,9 +101,10 @@ Renderer::~Renderer()
m_device->destroy_semaphores(m_submit_semaphores);
m_device->destroy_fences(m_frame_fences);
m_device->destroy_command_pool(m_pool);
m_device->destroy_command_pool(m_transient_pool);
}
[[nodiscard]] auto Renderer::draw(uint32_t frame_idx) -> DrawResult
[[nodiscard]] auto Renderer::frame(uint32_t frame_idx, std::function<void()> submit_scene) -> Result
{
ensure(
frame_idx < m_max_frames_in_flight,
@ -93,13 +122,14 @@ Renderer::~Renderer()
auto image_idx = m_device->acquire_image(m_swapchain->vk(), aquire_semaphore);
if (!image_idx.has_value())
{
return {};
return Result::invalid_swapchain;
}
m_device->reset_fence(frame_fence);
vk_reset_command_buffer(cmd, {});
record_cmd(cmd, *image_idx);
map_buffers(frame_idx);
submit_scene();
record_cmd(cmd, *image_idx);
auto wait_stage = VkPipelineStageFlags { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
auto &submit_semaphore = m_submit_semaphores[*image_idx];
@ -130,7 +160,8 @@ Renderer::~Renderer()
.pResults = &result,
}
);
return DrawResult::success;
return Result::success;
}
void Renderer::replace_swapchain(ISwapchain *swapchain)
@ -141,6 +172,39 @@ void Renderer::replace_swapchain(ISwapchain *swapchain)
m_pass->replace_swapchain(*swapchain);
}
void Renderer::map_buffers(uint32_t frame_idx)
{
using components::Sprite;
m_current_sprite_idx = 0;
m_staging_map = m_staging_buffer.map();
auto frame_segment_size = m_staging_map.size() / m_max_frames_in_flight;
m_staging_offset = frame_segment_size * frame_idx;
m_staging_map = m_staging_map.subspan(m_staging_offset, frame_segment_size);
m_sprite_vertex_map = std::span<Sprite::Vertex>(
std::bit_cast<Sprite::Vertex *>(m_staging_map.data()),
m_staging_map.size() / sizeof(Sprite::Vertex)
);
}
void Renderer::flush_buffers(VkCommandBuffer cmd)
{
m_staging_map = {};
m_sprite_vertex_map = {};
m_staging_buffer.unmap();
const auto buffer_copy_info = VkBufferCopy {
.srcOffset = m_staging_offset,
.dstOffset = m_staging_offset,
.size = m_current_sprite_idx * sizeof(components::Sprite::Vertex),
};
vk_cmd_copy_buffer(cmd, m_staging_buffer.vk(), m_vertex_buffer.vk(), 1u, &buffer_copy_info);
}
void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
{
const auto cmd_begin_info = VkCommandBufferBeginInfo {
@ -216,7 +280,10 @@ void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
};
vk_reset_command_buffer(cmd, {});
vkc(vk_begin_command_buffer(cmd, &cmd_begin_info));
flush_buffers(cmd);
vk_cmd_push_constants(
cmd,
m_pass->get_layout(),
@ -241,7 +308,7 @@ void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
vk_cmd_bind_pipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pass->get_pipeline());
vk_cmd_set_viewport(cmd, 0, 1, &viewport);
vk_cmd_set_scissors(cmd, 0, 1, &scissor);
vk_cmd_draw(cmd, 3, 1, 0, 0);
vk_cmd_draw(cmd, m_current_sprite_idx, 1, 0, 0);
vk_cmd_end_rendering(cmd);
vk_cmd_pipeline_barrier(
cmd,
@ -258,8 +325,45 @@ void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
vkc(vk_end_command_buffer(cmd));
}
void submit_sprite(const components::Sprite &sprite, const math::components::Transform &transform)
void Renderer::submit_sprite(
const components::Sprite &sprite,
const math::components::Transform &transform
)
{
using components::Sprite;
const auto &[x, y, z] = transform.translation;
const auto &[width, height, _] = transform.scale;
m_sprite_vertex_map[m_current_sprite_idx++] = components::Sprite::Vertex {
.position = { x, y + height, z },
.color = sprite.color,
};
m_sprite_vertex_map[m_current_sprite_idx++] = components::Sprite::Vertex {
.position = { x + width, y + height, z },
.color = sprite.color,
};
m_sprite_vertex_map[m_current_sprite_idx++] = components::Sprite::Vertex {
.position = { x + width, y, z },
.color = sprite.color,
};
m_sprite_vertex_map[m_current_sprite_idx++] = components::Sprite::Vertex {
.position = { x + width, y, z },
.color = sprite.color,
};
m_sprite_vertex_map[m_current_sprite_idx++] = components::Sprite::Vertex {
.position = { x, y, z },
.color = sprite.color,
};
m_sprite_vertex_map[m_current_sprite_idx++] = components::Sprite::Vertex {
.position = { x, y + height, z },
.color = sprite.color,
};
}
} // namespace lt::renderer::vk

View file

@ -15,7 +15,12 @@ namespace lt::renderer::vk {
class Renderer: public IRenderer
{
public:
Renderer(class IDevice *device, class ISwapchain *swapchain, uint32_t max_frames_in_flight);
Renderer(
class IGpu *gpu,
class IDevice *device,
class ISwapchain *swapchain,
uint32_t max_frames_in_flight
);
~Renderer() override;
@ -27,7 +32,8 @@ public:
auto operator=(const Renderer &) -> Renderer & = delete;
[[nodiscard]] DrawResult draw(uint32_t frame_idx) override;
[[nodiscard]] auto frame(uint32_t frame_idx, std::function<void()> submit_scene)
-> Result override;
void replace_swapchain(ISwapchain *swapchain) override;
@ -39,13 +45,15 @@ public:
void submit_sprite(
const components::Sprite &sprite,
const math::components::Transform &transform
) override
{
}
) override;
private:
void record_cmd(VkCommandBuffer cmd, uint32_t image_idx);
void map_buffers(uint32_t frame_idx);
void flush_buffers(VkCommandBuffer cmd);
memory::NullOnMove<class Device *> m_device {};
class Swapchain *m_swapchain {};
@ -54,6 +62,8 @@ private:
VkCommandPool m_pool = VK_NULL_HANDLE;
VkCommandPool m_transient_pool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> m_cmds;
std::vector<VkFence> m_frame_fences;
@ -67,6 +77,18 @@ private:
uint32_t m_max_frames_in_flight {};
FrameConstants m_frame_constants;
Buffer m_vertex_buffer;
Buffer m_staging_buffer;
size_t m_staging_offset;
std::span<std::byte> m_staging_map;
std::span<components::Sprite::Vertex> m_sprite_vertex_map;
size_t m_current_sprite_idx;
};
} // namespace lt::renderer::vk

View file

@ -1,3 +1,4 @@
#include <logger/logger.hpp>
#include <memory/reference.hpp>
#include <renderer/frontend/context/instance.hpp>
#include <renderer/frontend/context/surface.hpp>

View file

@ -9,6 +9,7 @@ class ShaderAsset;
namespace lt::renderer {
class IPass
{
public:

View file

@ -7,11 +7,13 @@ namespace lt::renderer {
[[nodiscard]] /* static */ auto IRenderer::create(
Api target_api,
IGpu *gpu,
IDevice *device,
ISwapchain *swapchain,
uint32_t max_frames_in_flight
) -> memory::Scope<IRenderer>
{
ensure(gpu, "Failed to create renderer::IRenderer: null gpu");
ensure(device, "Failed to create renderer::IRenderer: null device");
ensure(swapchain, "Failed to create renderer::IRenderer: null swapchain");
ensure(
@ -28,7 +30,7 @@ namespace lt::renderer {
switch (target_api)
{
case Api::vulkan:
return memory::create_scope<vk::Renderer>(device, swapchain, max_frames_in_flight);
return memory::create_scope<vk::Renderer>(gpu, device, swapchain, max_frames_in_flight);
case Api::none:
case Api::metal:
case Api::direct_x: throw std::runtime_error { "Invalid API" };

View file

@ -15,7 +15,7 @@ public:
static constexpr auto frames_in_flight_lower_limit = 1u;
enum class DrawResult : uint8_t
enum class Result : uint8_t
{
success = 0,
invalid_swapchain,
@ -24,6 +24,7 @@ public:
[[nodiscard]] static auto create(
Api target_api,
class IGpu *gpu,
class IDevice *device,
class ISwapchain *swapchain,
uint32_t max_frames_in_flight
@ -41,7 +42,7 @@ public:
auto operator=(const IRenderer &) -> IRenderer & = delete;
[[nodiscard]] virtual auto draw(uint32_t frame_idx) -> DrawResult = 0;
virtual auto frame(uint32_t frame_idx, std::function<void()> submit_scene) -> Result = 0;
virtual void replace_swapchain(class ISwapchain *swapchain) = 0;

View file

@ -7,6 +7,7 @@ Suite raii = "renderer_raii"_suite = [] {
auto fixture = FixtureDeviceSwapchain {};
ignore = lt::renderer::IRenderer::create(
constants::api,
fixture.gpu(),
fixture.device(),
fixture.swapchain(),
constants::frames_in_flight
@ -20,6 +21,7 @@ Suite raii = "renderer_raii"_suite = [] {
ignore = lt::renderer::IRenderer::create(
constants::api,
nullptr,
fixture.device(),
fixture.swapchain(),
constants::frames_in_flight
);
@ -28,6 +30,17 @@ Suite raii = "renderer_raii"_suite = [] {
expect_throw([&] {
ignore = lt::renderer::IRenderer::create(
constants::api,
fixture.gpu(),
nullptr,
fixture.swapchain(),
constants::frames_in_flight
);
});
expect_throw([&] {
ignore = lt::renderer::IRenderer::create(
constants::api,
fixture.gpu(),
fixture.device(),
nullptr,
constants::frames_in_flight
@ -37,6 +50,7 @@ Suite raii = "renderer_raii"_suite = [] {
expect_throw([&] {
ignore = lt::renderer::IRenderer::create(
constants::api,
fixture.gpu(),
fixture.device(),
nullptr,
lt::renderer::IRenderer::frames_in_flight_upper_limit + 1
@ -46,6 +60,7 @@ Suite raii = "renderer_raii"_suite = [] {
expect_throw([&] {
ignore = lt::renderer::IRenderer::create(
constants::api,
fixture.gpu(),
fixture.device(),
nullptr,
lt::renderer::IRenderer::frames_in_flight_lower_limit - 1
@ -55,12 +70,13 @@ Suite raii = "renderer_raii"_suite = [] {
};
Suite draw = "renderer_draw"_suite = [] {
using enum lt::renderer::IRenderer::DrawResult;
using enum lt::renderer::IRenderer::Result;
Case { "renderer draw" } = [] {
auto fixture = FixtureDeviceSwapchain {};
auto renderer = lt::renderer::IRenderer::create(
constants::api,
fixture.gpu(),
fixture.device(),
fixture.swapchain(),
constants::frames_in_flight
@ -68,7 +84,7 @@ Suite draw = "renderer_draw"_suite = [] {
for (auto frame_idx : std::views::iota(0u, 30u))
{
expect_eq(renderer->draw(frame_idx % constants::frames_in_flight), success);
expect_eq(renderer->frame(frame_idx % constants::frames_in_flight, [] {}), success);
}
};
@ -76,6 +92,7 @@ Suite draw = "renderer_draw"_suite = [] {
auto fixture = FixtureDeviceSwapchain {};
auto renderer = lt::renderer::IRenderer::create(
constants::api,
fixture.gpu(),
fixture.device(),
fixture.swapchain(),
constants::frames_in_flight
@ -83,14 +100,14 @@ Suite draw = "renderer_draw"_suite = [] {
for (auto frame_idx : std::views::iota(0u, 15u))
{
expect_eq(renderer->draw(frame_idx % constants::frames_in_flight), success);
expect_eq(renderer->frame(frame_idx % constants::frames_in_flight, [] {}), success);
}
fixture.recreate_swapchain();
renderer->replace_swapchain(fixture.swapchain());
for (auto frame_idx : std::views::iota(0u, 15u))
{
expect_eq(renderer->draw(frame_idx % constants::frames_in_flight), success);
expect_eq(renderer->frame(frame_idx % constants::frames_in_flight, [] {}), success);
}
};
};

View file

@ -44,6 +44,7 @@ System::System(CreateInfo info)
m_swapchain = ISwapchain::create(m_api, m_surface.get(), m_gpu.get(), m_device.get());
m_renderer = { IRenderer::create(
m_api,
m_gpu.get(),
m_device.get(),
m_swapchain.get(),
info.config.max_frames_in_flight
@ -64,6 +65,18 @@ void System::tick(app::TickInfo tick)
{
std::ignore = tick;
handle_surface_resized_events();
auto frame_result = m_renderer->frame(m_frame_idx, [this] { submit_scene(); });
if (frame_result == IRenderer::Result::invalid_swapchain)
{
recreate_swapchain();
}
m_frame_idx = (m_frame_idx + 1) % m_max_frames_in_flight;
}
void System::handle_surface_resized_events()
{
for (const auto &event : m_surface_entity.get<surface::SurfaceComponent>().peek_events())
{
if (std::holds_alternative<surface::ResizedEvent>(event))
@ -71,9 +84,15 @@ void System::tick(app::TickInfo tick)
m_swapchain.reset();
m_swapchain = ISwapchain::create(m_api, m_surface.get(), m_gpu.get(), m_device.get());
m_renderer->replace_swapchain(m_swapchain.get());
// No need to process multiple resize events
break;
}
}
}
void System::submit_scene()
{
auto perspective = math::mat4::identity();
for (auto [id, camera] : m_registry->view<lt::camera::components::PerspectiveCamera>())
{
@ -90,24 +109,20 @@ void System::tick(app::TickInfo tick)
}
}
// for each sprite, submit a new "model matrix" + "color" to go into the scene's SSBO
m_renderer->set_frame_constants({ .view_projection = perspective });
for (auto &[id, sprite, transform] :
m_registry->view<components::Sprite, math::components::Transform>())
{
m_renderer->submit_sprite(sprite, transform);
}
}
m_renderer->set_frame_constants({ .view_projection = perspective });
if (m_renderer->draw(m_frame_idx) != IRenderer::DrawResult::success)
{
m_swapchain.reset();
m_swapchain = ISwapchain::create(m_api, m_surface.get(), m_gpu.get(), m_device.get());
m_renderer->replace_swapchain(m_swapchain.get());
std::ignore = m_renderer->draw(m_frame_idx); // drop the frame if failed twice
}
m_frame_idx = (m_frame_idx + 1) % m_max_frames_in_flight;
void System::recreate_swapchain()
{
log::trace("Re-creating swapchaain");
m_swapchain.reset();
m_swapchain = ISwapchain::create(m_api, m_surface.get(), m_gpu.get(), m_device.get());
m_renderer->replace_swapchain(m_swapchain.get());
}
} // namespace lt::renderer

View file

@ -1,3 +1,4 @@
#include <logger/logger.hpp>
#include <memory/reference.hpp>
#include <memory/scope.hpp>
#include <renderer/frontend/renderer/renderer.hpp>

View file

@ -1,5 +1,6 @@
#pragma once
#include <logger/logger.hpp>
#include <memory/reference.hpp>
#include <renderer/components/messenger.hpp>
#include <renderer/frontend/context/device.hpp>
@ -170,7 +171,7 @@ private:
{
// I know this makes the tests too verbose...
// but makes it easier to figure out what the problem is when things fail on ci
log_trc("vulkan: {}", data.message);
lt::log::trace("vulkan: {}", data.message);
std::ignore = data;
std::ignore = type;
@ -240,7 +241,7 @@ private:
{
// I know this makes the tests too verbose...
// but makes it easier to figure out what the problem is when things fail on ci
log_trc("vulkan: {}", data.message);
lt::log::trace("vulkan: {}", data.message);
std::ignore = data;
std::ignore = type;

View file

@ -69,9 +69,7 @@ struct Sprite
}
};
memory::Ref<assets::ShaderAsset> vertex_shader;
memory::Ref<assets::ShaderAsset> fragment_shader;
math::vec3 color;
};
} // namespace lt::renderer::components

View file

@ -10,6 +10,16 @@
namespace lt::renderer {
/** The main rendering engine.
*
* Responsible for:
* - Creating a rendering backend context (vk/dx/mt)
* - Connecting the context to the physical devices (select gpu, create surface, logical device)
* - Rendering the scene represented in registry via lt::renderer::components.
*
* @todo(Light): Add DirectX12 support
* @todo(Light): Add Metal support
*/
class System: public app::ISystem
{
public:
@ -61,6 +71,12 @@ public:
}
private:
void handle_surface_resized_events();
void submit_scene();
void recreate_swapchain();
Api m_api;
memory::Ref<ecs::Registry> m_registry;

View file

@ -1,3 +0,0 @@
add_library(std INTERFACE)
target_precompile_headers(std INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/public/pch.hpp)

View file

@ -1,154 +0,0 @@
/** @file pch.hpp
*
* @brief Provides the entire standard library as a precompiled header
*/
#pragma once // NOLINTBEGIN
//============================================================
// C Standard Library Headers (from <c...> family)
//============================================================
#include <cassert>
#include <cctype>
#include <cerrno>
#include <cfenv>
#include <cfloat>
#include <cinttypes>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cwchar>
#include <cwctype>
//============================================================
// Input/Output and Formatting
//============================================================
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <istream>
#include <ostream>
#include <sstream>
//============================================================
// Containers
//============================================================
#include <array>
#include <deque>
#include <flat_map>
#include <forward_list>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <span>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
//============================================================
// Algorithms and Iterators
//============================================================
#include <algorithm>
#include <execution>
#include <iterator>
#include <numeric>
//============================================================
// Strings and Localization
//============================================================
#include <charconv>
#include <codecvt>
#include <locale>
#include <string>
#include <string_view>
//============================================================
// Memory Management and Smart Pointers
//============================================================
#include <memory>
#include <memory_resource>
#include <new>
#include <scoped_allocator>
//============================================================
// Utility and Miscellaneous
//============================================================
#include <any>
#include <bitset>
#include <chrono>
#include <functional>
#include <optional>
#include <ratio>
#include <source_location>
#include <tuple>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <utility>
#include <variant>
#include <version>
//============================================================
// Concurrency and Multithreading
//============================================================
#include <atomic>
#include <barrier>
#include <condition_variable>
#include <future>
#include <latch>
#include <mutex>
#include <semaphore>
#include <shared_mutex>
#include <stop_token>
#include <thread>
//============================================================
// Random Numbers and Numeric Limits
//============================================================
#include <complex>
#include <limits>
#include <numbers>
#include <random>
#include <valarray>
//============================================================
// Regular Expressions
//============================================================
#include <regex>
//============================================================
// Error Handling and Diagnostics
//============================================================
#include <cassert>
#include <cerrno>
#include <exception>
#include <source_location>
#include <stdexcept>
#include <system_error>
//============================================================
// C++20/23 Additions
//============================================================
#include <bit> // C++20 bit operations
#include <compare> // C++20 three-way comparison
#include <expected> // C++23 std::expected (if supported)
#include <format> // C++20 formatting
#include <print> // C++23 print functions
#include <ranges> // C++20 ranges library
#include <span> // C++20 span (repeated intentionally for clarity)
// #include <stacktrace> // C++23 stack tracing utilities // Not supported yet
#include <syncstream> // C++20 synchronized output streams
// NOLINTEND

View file

@ -1,9 +1,29 @@
if(NOT WIN32)
add_library_module(surface linux/system.cpp)
add_library_module(
NAME
surface
INTERFACES
system.cppm
requests.cppm
events.cppm
components.cppm
SOURCES
platform_linux.cpp)
target_link_libraries(surface PRIVATE X11)
else(WIN32)
add_library_module(surface windows/system.cpp)
add_library_module(
NAME
surface
INTERFACES
system.cppm
requests.cppm
system.cppm
requests.cppm
events.cppm
components.cppm
SOURCES
platform_windows.cpp)
endif()
@ -13,4 +33,5 @@ target_link_libraries(
PRIVATE logger lt_debug time)
add_test_module(surface system.test.cpp)
add_fuzz_module(surface system.fuzz.cpp)
# add_fuzz_module(surface system.fuzz.cpp)

View file

@ -1,40 +1,33 @@
#pragma once
#include <math/vec2.hpp>
#include <surface/events/keyboard.hpp>
#include <surface/events/mouse.hpp>
#include <surface/events/surface.hpp>
#include <surface/requests/surface.hpp>
#include <variant>
module;
#ifdef LIGHT_PLATFORM_LINUX
typedef struct _XDisplay Display;
#endif
export module surface.system:components;
import std;
import math.vec2;
import surface.events;
import surface.requests;
namespace lt::surface {
/** Represents a platform's surface (eg. a Window).
*
* @note This is a "system component"
*/
class SurfaceComponent
export class SurfaceComponent
{
public:
friend class System;
using Event = std::variant<
// surface events
ClosedEvent,
MovedEvent,
ResizedEvent,
LostFocusEvent,
GainFocusEvent,
// keyboard events
KeyPressedEvent,
KeyReleasedEvent,
// mouse events
MouseMovedEvent,
ButtonPressedEvent,
ButtonReleasedEvent>;
@ -49,7 +42,7 @@ public:
struct NativeData
{
Display *display;
uint32_t window;
std::uint32_t window;
unsigned long wm_delete_message;
};
#endif

260
modules/surface/events.cppm Normal file
View file

@ -0,0 +1,260 @@
export module surface.events;
import math.vec2;
import std;
export namespace lt::surface {
class KeyPressedEvent
{
public:
KeyPressedEvent(std::uint32_t key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> std::uint32_t
{
return m_key;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyPressed: {}", m_key);
}
private:
std::uint32_t m_key;
};
class KeyRepeatEvent
{
public:
KeyRepeatEvent(std::int32_t key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> std::uint32_t
{
return m_key;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyRepeated: {}", m_key);
}
private:
std::uint32_t m_key;
};
class KeyReleasedEvent
{
public:
KeyReleasedEvent(std::uint32_t key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> std::uint32_t
{
return m_key;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyReleased: {}", m_key);
}
private:
std::uint32_t m_key;
};
class KeySetCharEvent
{
public:
KeySetCharEvent(std::uint32_t character): m_character(character)
{
}
[[nodiscard]] auto get_character() const -> std::uint32_t
{
return m_character;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyCharSet: {}", m_character);
}
private:
std::uint32_t m_character;
};
class MouseMovedEvent
{
public:
MouseMovedEvent(float x, float y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const math::vec2 &
{
return m_position;
}
[[nodiscard]] auto get_x() const -> float
{
return m_position.x;
}
[[nodiscard]] auto get_y() const -> float
{
return m_position.y;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("MouseMoved: {}, {}", m_position.x, m_position.y);
}
private:
math::vec2 m_position;
};
class WheelScrolledEvent
{
public:
WheelScrolledEvent(float offset): m_offset(offset)
{
}
[[nodiscard]] auto get_offset() const -> float
{
return m_offset;
}
[[nodiscard]] auto to_string() const -> std::string
{
std::stringstream ss;
ss << "WheelScrolled: " << m_offset;
return ss.str();
}
private:
float m_offset;
};
class ButtonPressedEvent
{
public:
ButtonPressedEvent(std::int32_t button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> std::int32_t
{
return m_button;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("ButtonPressed: {}", m_button);
}
private:
std::int32_t m_button;
};
class ButtonReleasedEvent
{
public:
ButtonReleasedEvent(std::int32_t button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> std::int32_t
{
return m_button;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("ButtonReleased: {}", m_button);
}
private:
std::int32_t m_button;
};
class ClosedEvent
{
public:
[[nodiscard]] auto to_string() const -> std::string_view
{
return "SurfaceClosedEvent";
}
};
class MovedEvent
{
public:
MovedEvent(std::int32_t x, std::int32_t y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const math::ivec2 &
{
return m_position;
}
[[nodiscard]] auto to_string() const -> std::string
{
auto stream = std::stringstream {};
stream << "WindwoMoved: " << m_position.x << ", " << m_position.y;
return stream.str();
}
private:
math::ivec2 m_position;
};
class ResizedEvent
{
public:
ResizedEvent(std::uint32_t width, std::uint32_t height): m_size(width, height)
{
}
[[nodiscard]] auto get_size() const -> const math::uvec2 &
{
return m_size;
}
[[nodiscard]] auto to_string() const -> std::string
{
auto stream = std::stringstream {};
stream << "SurfaceResized: " << m_size.x << ", " << m_size.y;
return stream.str();
}
private:
math::uvec2 m_size;
};
class LostFocusEvent
{
public:
[[nodiscard]] auto to_string() const -> std::string_view
{
return "SurfaceLostFocus";
}
};
class GainFocusEvent
{
public:
[[nodiscard]] auto to_string() const -> std::string_view
{
return "SurfaceGainFocus";
}
};
} // namespace lt::surface

View file

@ -1,17 +1,18 @@
#include <memory/reference.hpp>
#include <surface/components.hpp>
#include <surface/events/mouse.hpp>
#include <surface/requests/surface.hpp>
#include <surface/system.hpp>
#include <time/timer.hpp>
//
module;
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/keysymdef.h>
//
module surface.system;
import debug.assertions;
import memory.reference;
import surface.requests;
import surface.events;
import logger;
import ecs.registry;
import time;
import std;
namespace lt::surface {
@ -51,9 +52,9 @@ constexpr auto all_events_mask = KeyPressMask | //
System::System(memory::Ref<ecs::Registry> registry): m_registry(std::move(registry))
{
ensure(m_registry, "Failed to initialize surface system: null registry");
debug::ensure(m_registry, "Failed to initialize surface system: null registry");
ensure(
debug::ensure(
m_registry->view<SurfaceComponent>().get_size() == 0,
"Failed to initialize surface system: registry has surface component(s)"
);
@ -116,15 +117,15 @@ try
// TODO(Light): refactor "environment" into standalone module
// NOLINTNEXTLINE(concurrency-mt-unsafe)
auto *display_env = std::getenv("DISPLAY");
ensure(display_env != nullptr, "DISPLAY env var not found!");
debug::ensure(display_env != nullptr, "DISPLAY env var not found!");
auto *display = XOpenDisplay(display_env);
ensure(display, "Failed to open XDisplay with DISPLAY: {}", display_env);
debug::ensure(display, "Failed to open XDisplay with DISPLAY: {}", display_env);
auto root_window = XDefaultRootWindow(display);
auto border_width = 0;
auto depth = int32_t { CopyFromParent };
auto depth = std::int32_t { CopyFromParent };
auto window_class = CopyFromParent;
auto *visual = (Visual *)CopyFromParent;
@ -226,14 +227,14 @@ void System::handle_events(SurfaceComponent &surface)
case KeyPress:
{
queue.emplace_back<KeyPressedEvent>(
static_cast<uint32_t>(XLookupKeysym(&event.xkey, 0))
static_cast<std::uint32_t>(XLookupKeysym(&event.xkey, 0))
);
break;
}
case KeyRelease:
{
queue.emplace_back<KeyReleasedEvent>(
static_cast<uint32_t>(XLookupKeysym(&event.xkey, 0))
static_cast<std::uint32_t>(XLookupKeysym(&event.xkey, 0))
);
break;
}
@ -284,8 +285,8 @@ void System::handle_events(SurfaceComponent &surface)
surface.m_resolution.x = new_width;
surface.m_resolution.y = new_height;
queue.emplace_back<ResizedEvent>(ResizedEvent {
static_cast<uint32_t>(new_width),
static_cast<uint32_t>(new_height),
static_cast<std::uint32_t>(new_width),
static_cast<std::uint32_t>(new_height),
});
}
@ -347,7 +348,12 @@ void System::modify_resolution(SurfaceComponent &surface, const ModifyResolution
auto serial = NextRequest(display);
// request a new window size from the X server
XResizeWindow(display, window, static_cast<uint32_t>(width), static_cast<uint32_t>(height));
XResizeWindow(
display,
window,
static_cast<std::uint32_t>(width),
static_cast<std::uint32_t>(height)
);
// flush output queue and wait for X server to processes the request
XSync(display, False);
@ -367,7 +373,7 @@ void System::modify_resolution(SurfaceComponent &surface, const ModifyResolution
// the size we asked for). We receive a ConfigureNotify event when
// our new size has been set.
constexpr auto lifespan = std::chrono::milliseconds { 10 };
auto timer = Timer {};
auto timer = time::Timer {};
auto event = XEvent {};
while (!XCheckIfEvent(
display,
@ -406,7 +412,7 @@ void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequ
// flush output queue and wait for X server to processes the request
XSync(display, False);
constexpr auto lifespan = std::chrono::milliseconds { 10 };
auto timer = Timer {};
auto timer = time::Timer {};
auto event = XEvent {};
while (!XCheckIfEvent(
display,
@ -466,25 +472,29 @@ void ensure_component_sanity(const SurfaceComponent &component)
{
auto [width, height] = component.get_resolution();
ensure(width != 0u, "Received bad values for surface component: width({}) == 0", width);
debug::ensure(width != 0u, "Received bad values for surface component: width({}) == 0", width);
ensure(height != 0u, "Received bad values for surface component: height({}) == 0", height);
debug::ensure(
height != 0u,
"Received bad values for surface component: height({}) == 0",
height
);
ensure(
debug::ensure(
width < SurfaceComponent::max_dimension,
"Received bad values for surface component: width({}) > max_dimension({})",
width,
SurfaceComponent::max_dimension
);
ensure(
debug::ensure(
height < SurfaceComponent::max_dimension,
"Received bad values for surface component: height({}) > max_dimension({})",
height,
SurfaceComponent::max_dimension
);
ensure(
debug::ensure(
component.get_title().size() < SurfaceComponent::max_title_length,
"Received bad values for surface component: title.size({}) > max_title_length({})",
component.get_title().size(),

View file

@ -1 +0,0 @@
1484824981238913982498139812098u24

View file

@ -1,91 +0,0 @@
#pragma once
#include <format>
namespace lt::surface {
class KeyPressedEvent
{
public:
KeyPressedEvent(uint32_t key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> uint32_t
{
return m_key;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyPressed: {}", m_key);
}
private:
uint32_t m_key;
};
class KeyRepeatEvent
{
public:
KeyRepeatEvent(int key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> uint32_t
{
return m_key;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyRepeated: {}", m_key);
}
private:
uint32_t m_key;
};
class KeyReleasedEvent
{
public:
KeyReleasedEvent(uint32_t key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> uint32_t
{
return m_key;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyReleased: {}", m_key);
}
private:
uint32_t m_key;
};
class KeySetCharEvent
{
public:
KeySetCharEvent(uint32_t character): m_character(character)
{
}
[[nodiscard]] auto get_character() const -> uint32_t
{
return m_character;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyCharSet: {}", m_character);
}
private:
uint32_t m_character;
};
} // namespace lt::surface

View file

@ -1,103 +0,0 @@
#pragma once
#include <math/vec2.hpp>
namespace lt::surface {
class MouseMovedEvent
{
public:
MouseMovedEvent(float x, float y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const math::vec2 &
{
return m_position;
}
[[nodiscard]] auto get_x() const -> float
{
return m_position.x;
}
[[nodiscard]] auto get_y() const -> float
{
return m_position.y;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("MouseMoved: {}, {}", m_position.x, m_position.y);
}
private:
math::vec2 m_position;
};
class WheelScrolledEvent
{
public:
WheelScrolledEvent(float offset): m_offset(offset)
{
}
[[nodiscard]] auto get_offset() const -> float
{
return m_offset;
}
[[nodiscard]] auto to_string() const -> std::string
{
std::stringstream ss;
ss << "WheelScrolled: " << m_offset;
return ss.str();
}
private:
float m_offset;
};
class ButtonPressedEvent
{
public:
ButtonPressedEvent(int button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> int
{
return m_button;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("ButtonPressed: {}", m_button);
}
private:
int m_button;
};
class ButtonReleasedEvent
{
public:
ButtonReleasedEvent(int button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> int
{
return m_button;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("ButtonReleased: {}", m_button);
}
private:
int m_button;
};
} // namespace lt::surface

View file

@ -1,81 +0,0 @@
#pragma once
#include <math/vec2.hpp>
#include <sstream>
namespace lt::surface {
class ClosedEvent
{
public:
[[nodiscard]] auto to_string() const -> std::string_view
{
return "SurfaceClosedEvent";
}
};
class MovedEvent
{
public:
MovedEvent(int x, int y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const math::ivec2 &
{
return m_position;
}
[[nodiscard]] auto to_string() const -> std::string
{
auto stream = std::stringstream {};
stream << "WindwoMoved: " << m_position.x << ", " << m_position.y;
return stream.str();
}
private:
math::ivec2 m_position;
};
class ResizedEvent
{
public:
ResizedEvent(unsigned int width, unsigned int height): m_size(width, height)
{
}
[[nodiscard]] auto get_size() const -> const math::uvec2 &
{
return m_size;
}
[[nodiscard]] auto to_string() const -> std::string
{
auto stream = std::stringstream {};
stream << "SurfaceResized: " << m_size.x << ", " << m_size.y;
return stream.str();
}
private:
math::uvec2 m_size;
};
class LostFocusEvent
{
public:
[[nodiscard]] auto to_string() const -> std::string_view
{
return "SurfaceLostFocus";
}
};
class GainFocusEvent
{
public:
[[nodiscard]] auto to_string() const -> std::string_view
{
return "SurfaceGainFocus";
}
};
} // namespace lt::surface

View file

@ -1,8 +1,8 @@
#pragma once
export module surface.requests;
import math.vec2;
import std;
#include <math/vec2.hpp>
namespace lt::surface {
export namespace lt::surface {
struct ModifyTitleRequest
{

View file

@ -1,14 +1,15 @@
#pragma once
#include <app/system.hpp>
#include <ecs/registry.hpp>
#include <math/vec2.hpp>
#include <memory/reference.hpp>
#include <surface/components.hpp>
export module surface.system;
export import :components;
import app.system;
import ecs.registry;
import math.vec2;
import surface.requests;
import memory.reference;
import std;
namespace lt::surface {
class System: public app::ISystem
export class System: public app::ISystem
{
public:
[[nodiscard]] System(memory::Ref<ecs::Registry> registry);
@ -39,26 +40,17 @@ public:
private:
void on_surface_destruct(ecs::Registry &registry, ecs::EntityId entity);
void handle_requests(struct SurfaceComponent &surface);
void handle_requests(SurfaceComponent &surface);
void handle_events(struct SurfaceComponent &surface);
void handle_events(SurfaceComponent &surface);
void modify_title(struct SurfaceComponent &surface, const struct ModifyTitleRequest &request);
void modify_title(SurfaceComponent &surface, const ModifyTitleRequest &request);
void modify_resolution(
struct SurfaceComponent &surface,
const struct ModifyResolutionRequest &request
);
void modify_resolution(SurfaceComponent &surface, const ModifyResolutionRequest &request);
void modify_position(
struct SurfaceComponent &surface,
const struct ModifyPositionRequest &request
);
void modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request);
void modify_visiblity(
struct SurfaceComponent &surface,
const struct ModifyVisibilityRequest &request
);
void modify_visiblity(SurfaceComponent &surface, const ModifyVisibilityRequest &request);
void modify_position(ecs::EntityId surface_entity, const math::ivec2 &new_size);

View file

@ -1,24 +1,29 @@
#include <ecs/entity.hpp>
#include <memory/reference.hpp>
#include <memory/scope.hpp>
#include <ranges>
#include <surface/components.hpp>
#include <surface/requests/surface.hpp>
#include <surface/system.hpp>
#include <test/test.hpp>
import test.test;
import test.expects;
import surface.system;
import surface.events;
import surface.requests;
import ecs.registry;
import memory.scope;
import memory.reference;
import logger;
import math.vec2;
import app.system;
import std;
using namespace lt;
using std::ignore;
using surface::SurfaceComponent;
using surface::System;
using test::Case;
using test::expect_eq;
using test::expect_ne;
using test::expect_not_nullptr;
using test::expect_throw;
using test::Suite;
[[nodiscard]] auto tick_info() -> app::TickInfo
using ::std::ignore;
using ::lt::surface::SurfaceComponent;
using ::lt::surface::System;
using ::lt::test::Case;
using ::lt::test::expect_eq;
using ::lt::test::expect_ne;
using ::lt::test::expect_not_nullptr;
using ::lt::test::expect_throw;
using ::lt::test::Suite;
using ::lt::test::operator""_suite;
[[nodiscard]] auto tick_info() -> lt::app::TickInfo
{
return {
.delta_time = std::chrono::milliseconds { 16 },
@ -43,7 +48,7 @@ struct overloads: Ts...
class Fixture
{
public:
[[nodiscard]] auto registry() -> memory::Ref<ecs::Registry>
[[nodiscard]] auto registry() -> lt::memory::Ref<lt::ecs::Registry>
{
return m_registry;
}
@ -78,7 +83,7 @@ public:
}
private:
memory::Ref<ecs::Registry> m_registry = memory::create_ref<ecs::Registry>();
lt::memory::Ref<lt::ecs::Registry> m_registry = lt::memory::create_ref<lt::ecs::Registry>();
System m_system { m_registry };
};
@ -110,7 +115,7 @@ Suite raii = "raii"_suite = [] {
Case { "post destruct has correct state" } = [] {
auto fixture = Fixture {};
auto system = memory::create_scope<System>(fixture.registry());
auto system = lt::memory::create_scope<System>(fixture.registry());
fixture.create_component();
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1);
@ -185,7 +190,7 @@ Suite registry_events = "registry_events"_suite = [] {
Case { "on_destrroy<SurfaceComponent> cleans up component" } = [] {
auto fixture = Fixture {};
auto system = memory::create_scope<System>(fixture.registry());
auto system = lt::memory::create_scope<System>(fixture.registry());
const auto &component = fixture.create_component();
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1);
@ -221,10 +226,10 @@ Suite tick_handles_events = "tick_handles_events"_suite = [] {
system.tick(tick_info());
expect_eq(surface.peek_events().size(), 0);
surface.push_event(surface::MovedEvent({}, {}));
surface.push_event(lt::surface::MovedEvent({}, {}));
expect_eq(surface.peek_events().size(), 1);
surface.push_event(surface::ButtonPressedEvent({}));
surface.push_event(lt::surface::ButtonPressedEvent({}));
expect_eq(surface.peek_events().size(), 2);
system.tick(tick_info());
@ -239,26 +244,26 @@ Suite tick_handles_requests = "tick_handles_requests"_suite = [] {
auto &surface = **fixture.create_component();
constexpr auto title = "ABC";
constexpr auto position = math::ivec2 { 50, 50 };
constexpr auto resolution = math::uvec2 { 50, 50 };
constexpr auto position = lt::math::ivec2 { 50, 50 };
constexpr auto resolution = lt::math::uvec2 { 50, 50 };
expect_eq(surface.peek_requests().size(), 0);
surface.push_request(surface::ModifyVisibilityRequest(true));
surface.push_request(lt::surface::ModifyVisibilityRequest(true));
expect_eq(surface.peek_requests().size(), 1);
system.tick(tick_info());
expect_eq(surface.peek_requests().size(), 0);
surface.push_request(surface::ModifyTitleRequest(title));
surface.push_request(lt::surface::ModifyTitleRequest(title));
expect_eq(surface.peek_requests().size(), 1);
surface.push_request(surface::ModifyResolutionRequest(resolution));
surface.push_request(surface::ModifyPositionRequest(position));
surface.push_request(lt::surface::ModifyResolutionRequest(resolution));
surface.push_request(lt::surface::ModifyPositionRequest(position));
expect_eq(surface.peek_requests().size(), 1 + 2);
surface.push_request(surface::ModifyVisibilityRequest(false));
surface.push_request(surface::ModifyVisibilityRequest(true));
surface.push_request(surface::ModifyVisibilityRequest(false));
surface.push_request(lt::surface::ModifyVisibilityRequest(false));
surface.push_request(lt::surface::ModifyVisibilityRequest(true));
surface.push_request(lt::surface::ModifyVisibilityRequest(false));
expect_eq(surface.peek_requests().size(), 1 + 2 + 3);
system.tick(tick_info());
@ -268,11 +273,11 @@ Suite tick_handles_requests = "tick_handles_requests"_suite = [] {
expect_eq(surface.get_position(), position);
expect_eq(surface.get_resolution(), resolution);
log::debug("EVENT COUNT: {}", surface.peek_events().size());
lt::log::debug("EVENT COUNT: {}", surface.peek_events().size());
for (const auto &event : surface.peek_events())
{
const auto visitor = overloads {
[&](auto event) { log::debug("event: {}", event.to_string()); },
[&](auto event) { lt::log::debug("event: {}", event.to_string()); },
};
std::visit(visitor, event);

View file

@ -1,7 +1,16 @@
add_library_module(test test.cpp entrypoint.cpp)
add_library_module(fuzz_test test.cpp fuzz.cpp)
add_library_module(
NAME
test
INTERFACES
test.cppm
expects.cppm
PRIVATE_INTERFACES
registry.cppm
SOURCES
entrypoint.cpp)
# add_library_module(fuzz_test test.cpp fuzz.cpp)
target_link_libraries(test PUBLIC logger)
target_link_libraries(fuzz_test PUBLIC logger)
# target_link_libraries(fuzz_test PUBLIC logger)
add_test_module(test test.test.cpp)

View file

@ -1,8 +1,10 @@
#include <logger/logger.hpp>
#include <test/test.hpp>
import logger;
import test.test;
import test.registry;
import std;
using namespace ::lt::test;
using namespace ::lt::test::details;
void parse_option(std::string_view argument, Registry::Options &options)
{
@ -49,7 +51,7 @@ void print_help()
std::println("--help | -h --> ~You just used it! :D");
}
auto main(int32_t argc, char **argv) -> int32_t
auto main(std::int32_t argc, char **argv) -> std::int32_t
try
{
auto raw_arguments = std::span<char *>(argv, argc);
@ -81,18 +83,18 @@ try
}
}
return static_cast<int32_t>(Registry::run_all(options));
return static_cast<std::int32_t>(Registry::run_all(options));
}
catch (const std::exception &exp)
{
lt::log::critical("Terminated due to uncaught exception:");
lt::log::critical("\twhat: {}", exp.what());
return EXIT_FAILURE;
return 1;
}
catch (...)
{
lt::log::critical("Terminated due to uncaught non-std exception!");
return EXIT_FAILURE;
return 1;
}

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