Compare commits

..

No commits in common. "abb9c1b1ec9b8e8661722eb18f537feccb8438ad" and "3265f25f0f239971a216f00a19dcc4a1eab2164d" have entirely different histories.

93 changed files with 2983 additions and 1861 deletions

View file

@ -1,47 +1,51 @@
add_module(NAME preliminary INTERFACES module.cppm fundumental_types.cppm assertions.cppm build_constants.cppm) add_module(NAME logger INTERFACES logger.cppm TESTS logger.test.cpp)
add_module(NAME logger INTERFACES logger.cppm TESTS logger.test.cpp DEPENDENCIES preliminary) add_module(NAME bitwise INTERFACES operations.cppm)
add_module(NAME tracer INTERFACES tracer.cppm DEPENDENCIES preliminary logger) add_module(NAME env INTERFACES constants.cppm)
add_module(NAME bitwise INTERFACES operations.cppm DEPENDENCIES preliminary)
add_module(NAME memory INTERFACES null_on_move.cppm reference.cppm scope.cppm add_module(NAME memory INTERFACES null_on_move.cppm reference.cppm scope.cppm
DEPENDENCIES DEPENDENCIES
preliminary
logger logger
) )
add_module(NAME time INTERFACES timer.cppm TESTS timer.test.cpp DEPENDENCIES preliminary) add_module(NAME time INTERFACES timer.cppm TESTS timer.test.cpp)
add_module( add_module(
NAME NAME
test test
INTERFACES INTERFACES
module.cppm
test.cppm test.cppm
expects.cppm expects.cppm
registry.cppm registry.cppm
SOURCES SOURCES
entrypoint.cpp entrypoint.cpp
DEPENDENCIES DEPENDENCIES
preliminary
logger logger
TESTS TESTS
test.test.cpp test.test.cpp
) )
add_module(
NAME
lt_debug
ROOT_DIR
${CMAKE_CURRENT_SOURCE_DIR}/debug
INTERFACES
instrumentor.cppm
assertions.cppm
DEPENDENCIES
logger
)
add_module( add_module(
NAME NAME
math math
INTERFACES INTERFACES
algebra.cppm algebra.cppm
mat4.cppm
trig.cppm trig.cppm
vec2.cppm vec2.cppm
vec3.cppm vec3.cppm
vec4.cppm vec4.cppm
mat4.cppm
components.cppm components.cppm
DEPENDENCIES
preliminary
TESTS
vec2.test.cpp
) )
add_module( add_module(
@ -51,8 +55,8 @@ add_module(
shader.cppm shader.cppm
metadata.cppm metadata.cppm
DEPENDENCIES DEPENDENCIES
preliminary
logger logger
lt_debug
TESTS TESTS
shader.test.cpp shader.test.cpp
) )
@ -67,15 +71,17 @@ add_module(
ENTRYPOINT ENTRYPOINT
entrypoint.cpp entrypoint.cpp
DEPENDENCIES DEPENDENCIES
preliminary
assets assets
logger logger
lt_debug
TESTS
bakers.test.cpp
) )
# add_executable(asset_baker entrypoint.cpp) target_link_libraries(asset_baker # add_executable(asset_baker entrypoint.cpp) target_link_libraries(asset_baker
# PRIVATE libasset_baker) # PRIVATE libasset_baker)
add_module(NAME camera INTERFACES components.cppm DEPENDENCIES preliminary math) add_module(NAME camera INTERFACES components.cppm DEPENDENCIES math)
add_module( add_module(
NAME NAME
@ -84,9 +90,9 @@ add_module(
application.cppm application.cppm
system.cppm system.cppm
DEPENDENCIES DEPENDENCIES
preliminary
memory memory
PRIVATE_DEPENDENCIES PRIVATE_DEPENDENCIES
lt_debug
) )
add_module( add_module(
@ -98,13 +104,14 @@ add_module(
entity.cppm entity.cppm
DEPENDENCIES DEPENDENCIES
logger logger
lt_debug
memory memory
TESTS TESTS
registry.test.cpp registry.test.cpp
sparse_set.test.cpp sparse_set.test.cpp
) )
add_module(NAME input_codes INTERFACES input_codes.cppm DEPENDENCIES preliminary) add_module(NAME input_codes INTERFACES input_codes.cppm)
if(WIN32) if(WIN32)
add_module( add_module(
@ -117,7 +124,6 @@ if(WIN32)
events.cppm events.cppm
components.cppm components.cppm
DEPENDENCIES DEPENDENCIES
preliminary
ecs ecs
app app
math math
@ -125,6 +131,7 @@ if(WIN32)
input_codes input_codes
PRIVATE_DEPENDENCIES PRIVATE_DEPENDENCIES
logger logger
lt_debug
time time
TESTS TESTS
system.test.cpp system.test.cpp
@ -141,7 +148,6 @@ elseif(UNIX)
events.cppm events.cppm
components.cppm components.cppm
DEPENDENCIES DEPENDENCIES
preliminary
ecs ecs
app app
math math
@ -151,6 +157,7 @@ elseif(UNIX)
PRIVATE_DEPENDENCIES PRIVATE_DEPENDENCIES
X11 X11
logger logger
lt_debug
time time
TESTS TESTS
system.test.cpp system.test.cpp
@ -181,7 +188,6 @@ add_module(
components.cppm components.cppm
events.cppm events.cppm
DEPENDENCIES DEPENDENCIES
preliminary
input_codes input_codes
surface surface
math math
@ -212,7 +218,6 @@ add_module(
vk/renderer.cppm vk/renderer.cppm
vk/debugger.cppm vk/debugger.cppm
DEPENDENCIES DEPENDENCIES
preliminary
app app
ecs ecs
memory memory
@ -225,12 +230,12 @@ add_module(
PRIVATE_DEPENDENCIES PRIVATE_DEPENDENCIES
surface surface
TESTS TESTS
_tests/buffer.cpp # _tests/buffer.cpp
_tests/debugger.cpp # _tests/debugger.cpp
_tests/device.cpp # _tests/device.cpp
_tests/pass.cpp # _tests/pass.cpp
_tests/renderer.cpp # _tests/renderer.cpp
_tests/surface.cpp # _tests/surface.cpp
_tests/system.cpp _tests/system.cpp
TEST_INTERFACES TEST_INTERFACES
_tests/utils.cppm _tests/utils.cppm
@ -251,8 +256,25 @@ add_module(
surface surface
renderer renderer
camera camera
# TESTS system.test.cpp
) )
add_executable(exectest ${CMAKE_CURRENT_SOURCE_DIR}/mirror/entrypoint.cpp)
target_link_libraries(
exectest
PRIVATE mirror
app
time
input
surface
renderer
camera
)
# add_executable_module(mirror entrypoint/mirror.cpp)
# target_link_libraries(mirror PRIVATE libmirror input)
if(ENABLE_SANDBOX) if(ENABLE_SANDBOX)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sandbox/) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sandbox/)
endif() endif()

View file

@ -1,17 +1,16 @@
export module app; export module app;
import preliminary;
import app.system; import app.system;
import memory.reference; import memory.reference;
import memory.scope; import memory.scope;
import std;
export namespace lt::app { namespace lt::app {
/** The main application class. /** The main application class.
* Think of this like an aggregate of systems, you register systems through this interface. * Think of this like an aggregate of systems, you register systems through this interface.
* Then they'll tick every "application frame". * Then they'll tick every "application frame".
*/ */
class Application export class Application
{ {
public: public:
Application(const Application &) = delete; Application(const Application &) = delete;
@ -55,13 +54,11 @@ void Application::game_loop()
const auto &last_tick = system->get_last_tick_result(); const auto &last_tick = system->get_last_tick_result();
const auto now = std::chrono::steady_clock::now(); const auto now = std::chrono::steady_clock::now();
system->tick( system->tick(TickInfo {
TickInfo { .delta_time = now - last_tick.end_time,
.delta_time = now - last_tick.end_time, .budget = std::chrono::milliseconds { 10 },
.budget = std::chrono::milliseconds { 10 }, .start_time = now,
.start_time = now, });
}
);
} }
for (auto &system : m_systems_to_be_registered) for (auto &system : m_systems_to_be_registered)

View file

@ -1,18 +1,17 @@
export module app.system; export module app.system;
import preliminary;
import logger; import logger;
import std;
export namespace lt::app { namespace lt::app {
/** Information required to tick a system. /** Information required to tick a system.
* @note May be used across an entire application-frame (consisting of multiple systems ticking) * @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>; using Timepoint_T = std::chrono::time_point<std::chrono::steady_clock>;
using Duration_T = std::chrono::duration<f64>; using Duration_T = std::chrono::duration<double>;
/** Duration since previous tick's end_time to current tick's start_time. */ /** Duration since previous tick's end_time to current tick's start_time. */
Duration_T delta_time {}; Duration_T delta_time {};
@ -31,11 +30,11 @@ struct TickInfo
}; };
/** Information about how a system's tick performed */ /** Information about how a system's tick performed */
struct TickResult export struct TickResult
{ {
using Timepoint_T = std::chrono::time_point<std::chrono::steady_clock>; using Timepoint_T = std::chrono::time_point<std::chrono::steady_clock>;
using Duration_T = std::chrono::duration<f64>; using Duration_T = std::chrono::duration<double>;
/** The info supplied to the system for ticking. */ /** The info supplied to the system for ticking. */
TickInfo info; TickInfo info;
@ -47,9 +46,9 @@ struct TickResult
Timepoint_T end_time; Timepoint_T end_time;
}; };
struct SystemDiagnosis export struct SystemDiagnosis
{ {
enum class Severity : u8 enum class Severity : std::uint8_t
{ {
verbose, verbose,
info, info,
@ -65,7 +64,7 @@ struct SystemDiagnosis
Severity severity; Severity severity;
}; };
class SystemStats export class SystemStats
{ {
public: public:
void push_diagnosis(SystemDiagnosis &&diagnosis) void push_diagnosis(SystemDiagnosis &&diagnosis)
@ -84,7 +83,7 @@ private:
std::vector<SystemDiagnosis> m_diagnosis; std::vector<SystemDiagnosis> m_diagnosis;
}; };
class ISystem export class ISystem
{ {
public: public:
ISystem() = default; ISystem() = default;

View file

@ -1,9 +1,10 @@
export module bakers; export module bakers;
import preliminary; import debug.assertions;
import assets.metadata; import assets.metadata;
import assets.shader; import assets.shader;
import logger; import logger;
import std;
export void bake_shader( export void bake_shader(
const std::filesystem::path &in_path, const std::filesystem::path &in_path,
@ -25,18 +26,16 @@ export void bake_shader(
// Don't bother linking to shaderc, just invoke the command with a system call. // Don't bother linking to shaderc, just invoke the command with a system call.
// NOLINTNEXTLINE(concurrency-mt-unsafe) // NOLINTNEXTLINE(concurrency-mt-unsafe)
std::system( std::system(std::format(
std::format( "glslc --target-env=vulkan1.4 -std=450core -fshader-stage={} {} -o {}",
"glslc --target-env=vulkan1.4 -std=450core -fshader-stage={} {} -o {}", type == vertex ? "vert" : "frag",
type == vertex ? "vert" : "frag", glsl_path,
glsl_path, spv_path
spv_path )
) .c_str());
.c_str()
);
auto stream = std::ifstream(spv_path, std::ios::binary); auto stream = std::ifstream(spv_path, std::ios::binary);
ensure( lt::debug::ensure(
stream.is_open(), stream.is_open(),
"Failed to open compiled {} shader at: {}", "Failed to open compiled {} shader at: {}",
type == vertex ? "vert" : "frag", type == vertex ? "vert" : "frag",
@ -46,9 +45,10 @@ export void bake_shader(
stream.seekg(0, std::ios::end); stream.seekg(0, std::ios::end);
const auto size = stream.tellg(); const auto size = stream.tellg();
auto bytes = std::vector<byte>(size); auto bytes = std::vector<std::byte>(size);
stream.seekg(0, std::ios::beg); stream.seekg(0, std::ios::beg);
stream.read((char *)bytes.data(), size); // NOLINT stream.read((char *)bytes.data(), size); // NOLINT
lt::log::debug("BYTES: {}", bytes.size());
stream.close(); stream.close();
std::filesystem::remove(spv_path); std::filesystem::remove(spv_path);

View file

View file

@ -1,9 +1,9 @@
import preliminary;
import assets.shader; import assets.shader;
import logger; import logger;
import bakers; import bakers;
import std;
auto main(i32 argc, char *argv[]) -> i32 auto main(int argc, char *argv[]) -> std::int32_t
try try
{ {
if (argc != 2) if (argc != 2)

View file

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

View file

@ -1,8 +1,8 @@
export module assets.shader; export module assets.shader;
import preliminary;
import assets.metadata; import assets.metadata;
import logger; import debug.assertions;
import std;
export namespace lt::assets { export namespace lt::assets {
@ -16,7 +16,7 @@ public:
code, code,
}; };
enum class Type : u8 enum class Type : std::uint8_t
{ {
vertex, vertex,
fragment, fragment,
@ -38,7 +38,7 @@ public:
ShaderAsset(const std::filesystem::path &path); ShaderAsset(const std::filesystem::path &path);
void unpack_to(BlobTag tag, std::span<byte> destination) const; void unpack_to(BlobTag tag, std::span<std::byte> destination) const;
[[nodiscard]] auto unpack(BlobTag tag) const -> Blob; [[nodiscard]] auto unpack(BlobTag tag) const -> Blob;
@ -54,7 +54,7 @@ public:
[[nodiscard]] auto get_blob_metadata(BlobTag tag) const -> const BlobMetadata & [[nodiscard]] auto get_blob_metadata(BlobTag tag) const -> const BlobMetadata &
{ {
ensure( debug::ensure(
tag == BlobTag::code, tag == BlobTag::code,
"Invalid blob tag for shader asset: {}", "Invalid blob tag for shader asset: {}",
std::to_underlying(tag) std::to_underlying(tag)
@ -88,17 +88,16 @@ constexpr auto total_metadata_size = //
+ sizeof(BlobMetadata::compressed_size) // + sizeof(BlobMetadata::compressed_size) //
+ sizeof(BlobMetadata::uncompressed_size); + sizeof(BlobMetadata::uncompressed_size);
ShaderAsset::ShaderAsset(const std::filesystem::path &path) ShaderAsset::ShaderAsset(const std::filesystem::path &path): m_stream(path)
: m_stream(path, std::ios::beg | std::ios::binary)
{ {
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) { const auto read = [this](auto &field) {
m_stream.read(std::bit_cast<char *>(&field), sizeof(field)); m_stream.read(std::bit_cast<char *>(&field), sizeof(field));
}; };
m_stream.seekg(0, std::ifstream::end); m_stream.seekg(0, std::ifstream::end);
const auto file_size = static_cast<size_t>(m_stream.tellg()); const auto file_size = static_cast<std::size_t>(m_stream.tellg());
ensure( debug::ensure(
file_size > total_metadata_size, file_size > total_metadata_size,
"Failed to open shader asset at: {}, file smaller than metadata: {} < {}", "Failed to open shader asset at: {}, file smaller than metadata: {} < {}",
path.string(), path.string(),
@ -110,14 +109,13 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path)
read(m_asset_metadata.type); read(m_asset_metadata.type);
read(m_asset_metadata.version); read(m_asset_metadata.version);
read(m_metadata.type); read(m_metadata.type);
read(m_code_blob_metadata.tag); read(m_code_blob_metadata.tag);
read(m_code_blob_metadata.offset); read(m_code_blob_metadata.offset);
read(m_code_blob_metadata.compression_type); read(m_code_blob_metadata.compression_type);
read(m_code_blob_metadata.compressed_size); read(m_code_blob_metadata.compressed_size);
read(m_code_blob_metadata.uncompressed_size); read(m_code_blob_metadata.uncompressed_size);
ensure( debug::ensure(
m_asset_metadata.type == asset_type_identifier, m_asset_metadata.type == asset_type_identifier,
"Failed to open shader asset at: {}, incorrect asset type: {} != {}", "Failed to open shader asset at: {}, incorrect asset type: {} != {}",
path.string(), path.string(),
@ -125,7 +123,7 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path)
asset_type_identifier asset_type_identifier
); );
ensure( debug::ensure(
m_asset_metadata.version == current_version, m_asset_metadata.version == current_version,
"Failed to open shader asset at: {}, version mismatch: {} != {}", "Failed to open shader asset at: {}, version mismatch: {} != {}",
path.string(), path.string(),
@ -133,21 +131,21 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path)
current_version current_version
); );
ensure( debug::ensure(
std::to_underlying(m_metadata.type) <= std::to_underlying(Type::compute), std::to_underlying(m_metadata.type) <= std::to_underlying(Type::compute),
"Failed to open shader asset at: {}, invalid shader type: {}", "Failed to open shader asset at: {}, invalid shader type: {}",
path.string(), path.string(),
std::to_underlying(m_metadata.type) std::to_underlying(m_metadata.type)
); );
ensure( debug::ensure(
m_code_blob_metadata.tag == std::to_underlying(BlobTag::code), m_code_blob_metadata.tag == std::to_underlying(BlobTag::code),
"Failed to open shader asset at: {}, invalid blob tag: {}", "Failed to open shader asset at: {}, invalid blob tag: {}",
path.string(), path.string(),
m_code_blob_metadata.tag m_code_blob_metadata.tag
); );
ensure( debug::ensure(
m_code_blob_metadata.offset + m_code_blob_metadata.compressed_size <= file_size, m_code_blob_metadata.offset + m_code_blob_metadata.compressed_size <= file_size,
"Failed to open shader asset at: {}, file smaller than blob: {} > {} + {}", "Failed to open shader asset at: {}, file smaller than blob: {} > {} + {}",
path.string(), path.string(),
@ -177,7 +175,7 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path)
.uncompressed_size = code_blob.size(), .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) { const auto write = [&stream](auto &field) {
stream.write(std::bit_cast<char *>(&field), sizeof(field)); stream.write(std::bit_cast<char *>(&field), sizeof(field));
}; };
@ -192,30 +190,38 @@ ShaderAsset::ShaderAsset(const std::filesystem::path &path)
stream.write(std::bit_cast<char *>(code_blob.data()), static_cast<long long>(code_blob.size())); stream.write(std::bit_cast<char *>(code_blob.data()), static_cast<long long>(code_blob.size()));
} }
void ShaderAsset::unpack_to(BlobTag tag, std::span<byte> destination) const 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, destination.size() >= m_code_blob_metadata.uncompressed_size,
"Failed to unpack shader blob {} to destination ({}) of size {} since it's smaller " "Failed to unpack shader blob {} to destination ({}) of size {} since it's smaller "
"than the blobl's uncompressed size: {}", "than the blobl's uncompressed size: {}",
std::to_underlying(tag), std::to_underlying(tag),
std::bit_cast<size_t>(destination.data()), std::bit_cast<std::size_t>(destination.data()),
destination.size(), destination.size(),
m_code_blob_metadata.uncompressed_size m_code_blob_metadata.uncompressed_size
); );
m_stream.seekg(static_cast<long long>(m_code_blob_metadata.offset), std::ifstream::beg); m_stream.seekg(static_cast<long long>(m_code_blob_metadata.offset));
m_stream.read( m_stream.read(
std::bit_cast<char *>(destination.data()), std::bit_cast<char *>(destination.data()),
m_code_blob_metadata.uncompressed_size static_cast<long long>(m_code_blob_metadata.uncompressed_size)
); );
} }
[[nodiscard]] auto ShaderAsset::unpack(BlobTag tag) const -> Blob [[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); auto blob = Blob(m_code_blob_metadata.uncompressed_size);
unpack_to(tag, blob); unpack_to(tag, blob);

View file

@ -1,65 +1,47 @@
import test;
import assets.metadata; import assets.metadata;
import assets.shader; import assets.shader;
import test.test;
import test.expects;
import std;
using ::lt::assets::AssetMetadata; using ::lt::assets::AssetMetadata;
using ::lt::assets::Blob;
using ::lt::assets::BlobMetadata; using ::lt::assets::BlobMetadata;
using ::lt::assets::ShaderAsset; using ::lt::assets::ShaderAsset;
using ::lt::test::Case;
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 test_data_path = std::filesystem::path { "./data/test_assets" };
const auto tmp_path = std::filesystem::path { "/tmp/lt_assets_tests/" }; const auto tmp_path = std::filesystem::path { "/tmp/lt_assets_tests/" };
[[nodiscard]] auto generate_blob(size_t size) -> Blob
{
auto blob = Blob {};
for (auto idx : std::views::iota(0u, size))
{
blob.emplace_back(static_cast<byte>(idx));
}
return blob;
}
Suite raii = "shader_raii"_suite = [] { Suite raii = "shader_raii"_suite = [] {
std::filesystem::current_path(test_data_path); std::filesystem::current_path(test_data_path);
std::filesystem::create_directories(tmp_path); std::filesystem::create_directories(tmp_path);
Case { "happy paths" } = [] { Case { "happy path won't throw" } = [] {
auto shader_asset = ShaderAsset { "triangle.frag.asset" };
}; };
Case { "unhappy paths" } = [] { Case { "many won't freeze/throw" } = [] {
// non-existent file
expect_throw([] { ShaderAsset { "path" }; });
// incompatible type
expect_throw([] { ShaderAsset { "dummytext" }; });
// some random stressing
expect_throw([] {
for (auto idx : std::views::iota(0u, 1'000u))
{
auto shader_asset = ShaderAsset { std::to_string(idx) };
}
});
}; };
Case { "many" } = [] { Case { "unhappy path throws" } = [] {
for (auto idx : std::views::iota(0u, 1'000u)) expect_throw([] { ShaderAsset { "random_path" }; });
{
ignore = idx;
auto shader_asset = ShaderAsset { "triangle.frag.asset" };
}
}; };
}; };
// NOLINTNEXTLINE(cppcoreguidelines-interfaces-global-init)
Suite packing = "shader_pack"_suite = [] { Suite packing = "shader_pack"_suite = [] {
Case { "Unpacking packed data returns the same data" } = [] { Case { "" } = [] {
const auto out_path = tmp_path / "shader_packing"; const auto out_path = tmp_path / "shader_packing";
constexpr auto blob_size = size_t { 255u }; auto dummy_blob = lt::assets::Blob {};
for (auto idx : std::views::iota(0, 255))
auto blob = generate_blob(blob_size); {
dummy_blob.emplace_back(static_cast<std::byte>(idx));
}
const auto expected_size = // const auto expected_size = //
sizeof(AssetMetadata::type) // sizeof(AssetMetadata::type) //
@ -70,7 +52,7 @@ Suite packing = "shader_pack"_suite = [] {
+ sizeof(BlobMetadata::compression_type) // + sizeof(BlobMetadata::compression_type) //
+ sizeof(BlobMetadata::compressed_size) // + sizeof(BlobMetadata::compressed_size) //
+ sizeof(BlobMetadata::uncompressed_size) // + sizeof(BlobMetadata::uncompressed_size) //
+ blob.size(); + dummy_blob.size();
ShaderAsset::pack( ShaderAsset::pack(
out_path, out_path,
@ -81,7 +63,7 @@ Suite packing = "shader_pack"_suite = [] {
ShaderAsset::Metadata { ShaderAsset::Metadata {
.type = ShaderAsset::Type::vertex, .type = ShaderAsset::Type::vertex,
}, },
std::move(blob) std::move(dummy_blob)
); );
auto stream = std::ifstream { auto stream = std::ifstream {
@ -90,8 +72,8 @@ Suite packing = "shader_pack"_suite = [] {
}; };
expect_true(stream.is_open()); expect_true(stream.is_open());
stream.seekg(0u, std::ios::end); 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); expect_eq(file_size, expected_size);
stream.close(); stream.close();
@ -104,12 +86,12 @@ Suite packing = "shader_pack"_suite = [] {
const auto &metadata = shader_asset.get_metadata(); const auto &metadata = shader_asset.get_metadata();
expect_eq(metadata.type, ShaderAsset::Type::vertex); expect_eq(metadata.type, ShaderAsset::Type::vertex);
auto unpakced_blob = shader_asset.unpack(ShaderAsset::BlobTag::code); auto blob = shader_asset.unpack(ShaderAsset::BlobTag::code);
expect_eq(unpakced_blob.size(), blob_size); expect_eq(blob.size(), 255);
for (auto idx : std::views::iota(0u, blob_size)) for (auto idx : std::views::iota(0, 255))
{ {
expect_eq(unpakced_blob[idx], static_cast<byte>(idx)); expect_eq(blob[idx], static_cast<std::byte>(idx));
} }
}; };
}; };

View file

@ -1,12 +1,12 @@
export module bitwise; export module bitwise;
import preliminary; import std;
namespace lt::bitwise { namespace lt::bitwise {
/* bit-wise */ /* bit-wise */
export constexpr auto bit(u32 x) -> u32 export constexpr auto bit(std::uint32_t x) -> std::uint32_t
{ {
return u32 { 1u } << x; return 1u << x;
} }
} // namespace lt::bitwise } // namespace lt::bitwise

View file

@ -1,19 +1,17 @@
export module camera.components; export module camera.components;
import preliminary;
import math.vec4; import math.vec4;
export namespace lt::camera::components { namespace lt::camera::components {
struct PerspectiveCamera export struct PerspectiveCamera
{ {
f32 vertical_fov {}; float vertical_fov {};
f32 near_plane {}; float near_plane {};
f32 far_plane {}; float far_plane {};
f32 aspect_ratio {}; float aspect_ratio {};
math::vec4 background_color; math::vec4 background_color;

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,25 +1,23 @@
// @todo(Light): Implement...
export module debug.instrumentor; export module debug.instrumentor;
import preliminary; import std;
import logger; import logger;
namespace lt::tracer { namespace lt::debug {
struct ScopeTraceResult struct ScopeProfileResult
{ {
std::string name; std::string name;
u64 start, duration; long long start, duration;
u32 threadID; std::uint32_t threadID;
}; };
class Tracer class Instrumentor
{ {
public: public:
static auto instance() -> Tracer & static auto instance() -> Instrumentor &
{ {
static auto instance = Tracer {}; static auto instance = Instrumentor {};
return instance; return instance;
} }
@ -32,7 +30,7 @@ public:
instance().end_session_impl(); instance().end_session_impl();
} }
static void submit_scope_profile(const ScopeTraceResult &profileResult) static void submit_scope_profile(const ScopeProfileResult &profileResult)
{ {
instance().submit_scope_profile_impl(profileResult); instance().submit_scope_profile_impl(profileResult);
} }
@ -42,46 +40,46 @@ private:
unsigned int m_current_session_count { 0u }; unsigned int m_current_session_count { 0u };
Tracer() = default; Instrumentor() = default;
void begin_session_impl(const std::string &outputPath); void begin_session_impl(const std::string &outputPath);
void end_session_impl(); void end_session_impl();
void submit_scope_profile_impl(const ScopeTraceResult &profileResult); void submit_scope_profile_impl(const ScopeProfileResult &profileResult);
}; };
class TracerTimer class InstrumentorTimer
{ {
public: public:
TracerTimer(const std::string &scopeName); InstrumentorTimer(const std::string &scopeName);
~TracerTimer(); ~InstrumentorTimer();
private: private:
ScopeTraceResult m_result; ScopeProfileResult m_result;
std::chrono::time_point<std::chrono::steady_clock> m_start; std::chrono::time_point<std::chrono::steady_clock> m_start;
}; };
} // namespace lt::tracer } // namespace lt::debug
/* scope */ /* scope */
#define lt_trace_scope(name) lt_profile_scope_no_redifinition(name, __LINE__) #define lt_profile_scope(name) lt_profile_scope_no_redifinition(name, __LINE__)
#define lt_trace_scope_no_redifinition(name, line) lt_profile_scope_no_redifinition2(name, line) #define lt_profile_scope_no_redifinition(name, line) lt_profile_scope_no_redifinition2(name, line)
#define lt_trace_scope_no_redifinition2(name, line) InstrumentorTimer timer##line(name) #define lt_profile_scope_no_redifinition2(name, line) InstrumentorTimer timer##line(name)
/* function */ /* function */
#define lt_trace_function lt_profile_scope(__FUNCSIG__) #define LT_PROFILE_FUNCTION lt_profile_scope(__FUNCSIG__)
/* session */ /* session */
#define lt_trace_begin_session(outputPath) ::lt::Instrumentor::begin_session(outputPath) #define lt_profile_begin_session(outputPath) ::lt::Instrumentor::begin_session(outputPath)
#define lt_trace_end_session() ::lt::Instrumentor::end_session() #define lt_profile_end_session() ::lt::Instrumentor::end_session()
module :private; module :private;
namespace lt::tracer { namespace lt::debug {
void Tracer::begin_session_impl(const std::string &outputPath) void Instrumentor::begin_session_impl(const std::string &outputPath)
{ {
std::filesystem::create_directory(outputPath.substr(0, outputPath.find_last_of('/') + 1)); std::filesystem::create_directory(outputPath.substr(0, outputPath.find_last_of('/') + 1));
@ -89,7 +87,7 @@ void Tracer::begin_session_impl(const std::string &outputPath)
m_output_file_stream << "{\"traceEvents\":["; m_output_file_stream << "{\"traceEvents\":[";
} }
void Tracer::end_session_impl() void Instrumentor::end_session_impl()
{ {
if (m_current_session_count == 0u) if (m_current_session_count == 0u)
{ {
@ -103,7 +101,7 @@ void Tracer::end_session_impl()
m_output_file_stream.close(); m_output_file_stream.close();
} }
void Tracer::submit_scope_profile_impl(const ScopeTraceResult &profileResult) void Instrumentor::submit_scope_profile_impl(const ScopeProfileResult &profileResult)
{ {
if (m_current_session_count++ == 0u) if (m_current_session_count++ == 0u)
{ {
@ -124,13 +122,13 @@ void Tracer::submit_scope_profile_impl(const ScopeTraceResult &profileResult)
m_output_file_stream << "}"; m_output_file_stream << "}";
} }
TracerTimer::TracerTimer(const std::string &scopeName) InstrumentorTimer::InstrumentorTimer(const std::string &scopeName)
: m_result({ .name = scopeName, .start = 0, .duration = 0, .threadID = 0 }) : m_result({ .name = scopeName, .start = 0, .duration = 0, .threadID = 0 })
, m_start(std::chrono::steady_clock::now()) , m_start(std::chrono::steady_clock::now())
{ {
} }
TracerTimer::~TracerTimer() InstrumentorTimer::~InstrumentorTimer()
{ {
auto end = std::chrono::steady_clock::now(); auto end = std::chrono::steady_clock::now();
@ -143,6 +141,6 @@ TracerTimer::~TracerTimer()
.count() .count()
- m_result.start; - m_result.start;
Tracer::submit_scope_profile(m_result); Instrumentor::submit_scope_profile(m_result);
} }
} // namespace lt::tracer } // namespace lt::debug

View file

@ -1,20 +1,20 @@
export module ecs.entity; export module ecs.entity;
import debug.assertions;
import preliminary;
import memory.reference; import memory.reference;
import ecs.registry; import ecs.registry;
import std;
export namespace lt::ecs { namespace lt::ecs {
/** High-level entity convenience wrapper */ /** High-level entity convenience wrapper */
class Entity export class Entity
{ {
public: public:
Entity(memory::Ref<Registry> registry, EntityId identifier) Entity(memory::Ref<Registry> registry, EntityId identifier)
: m_registry(std::move(registry)) : m_registry(std::move(registry))
, m_identifier(identifier) , 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> template<typename Component_T>
@ -51,4 +51,5 @@ private:
EntityId m_identifier; EntityId m_identifier;
}; };
} // namespace lt::ecs } // namespace lt::ecs

View file

@ -1,14 +1,14 @@
export module ecs.registry; export module ecs.registry;
import debug.assertions;
import preliminary;
import ecs.sparse_set; import ecs.sparse_set;
import memory.scope; import memory.scope;
import std;
export namespace lt::ecs { namespace lt::ecs {
using EntityId = u32; 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. /** A registry of components, the heart of an ECS architecture.
* *
@ -23,7 +23,7 @@ constexpr auto null_entity = std::numeric_limits<EntityId>::max();
* @ref https://github.com/skypjack/entt * @ref https://github.com/skypjack/entt
* @ref https://github.com/SanderMertens/flecs * @ref https://github.com/SanderMertens/flecs
*/ */
class Registry export class Registry
{ {
public: public:
using UnderlyingSparseSet_T = TypeErasedSparseSet<EntityId>; using UnderlyingSparseSet_T = TypeErasedSparseSet<EntityId>;
@ -190,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: private:
using TypeId = size_t; using TypeId = std::size_t;
static consteval auto hash_cstr(const char *str) -> TypeId static consteval auto hash_cstr(const char *str) -> TypeId
{ {
constexpr auto fnv_offset_basis = size_t { 14695981039346656037ull }; constexpr auto fnv_offset_basis = std::size_t { 14695981039346656037ull };
constexpr auto fnv_prime = size_t { 1099511628211ull }; constexpr auto fnv_prime = std::size_t { 1099511628211ull };
auto hash = fnv_offset_basis; auto hash = fnv_offset_basis;
for (const auto &ch : std::string_view { str }) for (const auto &ch : std::string_view { str })
{ {
hash *= fnv_prime; hash *= fnv_prime;
hash ^= static_cast<u8>(ch); hash ^= static_cast<std::uint8_t>(ch);
} }
return hash; return hash;
@ -242,7 +242,7 @@ private:
auto *base_set = m_sparsed_sets[type_id].get(); auto *base_set = m_sparsed_sets[type_id].get();
auto *derived_set = dynamic_cast<SparseSet<T, EntityId> *>(base_set); auto *derived_set = dynamic_cast<SparseSet<T, 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; return *derived_set;
} }

View file

@ -1,12 +1,21 @@
import test;
import ecs.registry; import ecs.registry;
import test.test;
import test.expects;
import std;
using ::lt::ecs::EntityId; using ::lt::ecs::EntityId;
using ::lt::ecs::Registry; 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 struct Component
{ {
i32 m_int {}; int m_int {};
std::string m_string; std::string m_string;
[[nodiscard]] friend auto operator==(const Component &lhs, const Component &rhs) -> bool [[nodiscard]] friend auto operator==(const Component &lhs, const Component &rhs) -> bool
@ -30,7 +39,7 @@ struct std::formatter<Component>
struct Component_B struct Component_B
{ {
f32 m_float {}; float m_float {};
[[nodiscard]] friend auto operator==(const Component_B lhs, const Component_B &rhs) -> bool [[nodiscard]] friend auto operator==(const Component_B lhs, const Component_B &rhs) -> bool
{ {
@ -52,21 +61,20 @@ struct std::formatter<Component_B>
}; };
Suite raii = "raii"_suite = [] { Suite raii = "raii"_suite = [] {
Case { "happy paths" } = [] { Case { "happy path won't throw" } = [] {
ignore = Registry {}; std::ignore = Registry {};
}; };
Case { "unhappy paths" } = [] { Case { "many won't freeze/throw" } = [] {
};
Case { "many" } = [] {
for (auto idx : std::views::iota(0, 100'000)) for (auto idx : std::views::iota(0, 100'000))
{ {
ignore = idx; std::ignore = Registry {};
ignore = Registry {};
} }
}; };
Case { "unhappy path throws" } = [] {
};
Case { "post construct has correct state" } = [] { Case { "post construct has correct state" } = [] {
auto registry = Registry {}; auto registry = Registry {};
expect_eq(registry.get_entity_count(), 0); expect_eq(registry.get_entity_count(), 0);
@ -150,7 +158,6 @@ Suite callbacks = "callbacks"_suite = [] {
Case { "on_construct/on_destruct won't get called on unrelated component" } = [] { Case { "on_construct/on_destruct won't get called on unrelated component" } = [] {
auto registry = Registry {}; auto registry = Registry {};
registry.connect_on_construct<Component>([&](Registry &, EntityId) { registry.connect_on_construct<Component>([&](Registry &, EntityId) {
expect_unreachable(); expect_unreachable();
}); });
@ -160,7 +167,6 @@ Suite callbacks = "callbacks"_suite = [] {
for (auto idx : std::views::iota(0, 100'000)) for (auto idx : std::views::iota(0, 100'000))
{ {
ignore = idx;
registry.add<Component_B>(registry.create_entity(), {}); registry.add<Component_B>(registry.create_entity(), {});
} }
}; };
@ -182,8 +188,6 @@ Suite callbacks = "callbacks"_suite = [] {
expect_true(on_destruct_called.empty()); expect_true(on_destruct_called.empty());
for (auto idx : std::views::iota(0, 100'000)) for (auto idx : std::views::iota(0, 100'000))
{ {
ignore = idx;
auto entity = all_entities.emplace_back(registry.create_entity()); auto entity = all_entities.emplace_back(registry.create_entity());
registry.add<Component>(entity, {}); registry.add<Component>(entity, {});
} }
@ -218,7 +222,7 @@ Suite each = "each"_suite = [] {
component_map_a[entity] = component; component_map_a[entity] = component;
} }
auto component_map_b = std::unordered_map<EntityId, Component_B> {}; auto component_map_b = std::unordered_map<lt::ecs::EntityId, Component_B> {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
auto entity = EntityId {}; auto entity = EntityId {};
@ -233,7 +237,7 @@ Suite each = "each"_suite = [] {
} }
auto &component = registry.add<Component_B>( auto &component = registry.add<Component_B>(
entity, entity,
{ .m_float = static_cast<f32>(idx) / 2.0f } { .m_float = static_cast<float>(idx) / 2.0f }
); );
component_map_b[entity] = component; component_map_b[entity] = component;
@ -304,7 +308,7 @@ Suite views = "views"_suite = [] {
} }
auto &component = registry.add<Component_B>( auto &component = registry.add<Component_B>(
entity, entity,
{ .m_float = static_cast<f32>(idx) / 2.0f } { .m_float = static_cast<float>(idx) / 2.0f }
); );
component_map_b[entity] = component; component_map_b[entity] = component;

View file

@ -1,13 +1,13 @@
export module ecs.sparse_set; export module ecs.sparse_set;
import debug.assertions;
import std;
import preliminary; namespace lt::ecs {
export namespace lt::ecs {
/** /**
* @ref https://programmingpraxis.com/2012/03/09/sparse-sets/ * @ref https://programmingpraxis.com/2012/03/09/sparse-sets/
*/ */
template<typename Identifier_T = u32> export template<typename Identifier_T = std::uint32_t>
class TypeErasedSparseSet class TypeErasedSparseSet
{ {
public: public:
@ -26,19 +26,19 @@ public:
virtual void remove(Identifier_T identifier) = 0; virtual void remove(Identifier_T identifier) = 0;
}; };
template<typename Value_T, typename Identifier_T = u32> export template<typename Value_T, typename Identifier_T = std::uint32_t>
class SparseSet: public TypeErasedSparseSet<Identifier_T> class SparseSet: public TypeErasedSparseSet<Identifier_T>
{ {
public: public:
using Dense_T = std::pair<Identifier_T, Value_T>; 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(); 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, initial_capacity <= max_capacity,
"Failed to create SparseSet: capacity too large ({} > {})", "Failed to create SparseSet: capacity too large ({} > {})",
initial_capacity, initial_capacity,
@ -51,18 +51,24 @@ public:
auto insert(Identifier_T identifier, Value_T value) -> Dense_T & auto insert(Identifier_T identifier, Value_T value) -> Dense_T &
{ {
ensure(identifier < max_capacity, "SparseSet::insert: identifier < max_capacity");
if (m_sparse.size() < identifier + 1) 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); new_capacity = std::min(new_capacity, max_capacity);
// log::debug("Increasing sparse vector size:", m_dead_count);
// log::debug("\tdead_count: {}", m_dead_count);
// log::debug("\talive_count: {}", m_alive_count);
// log::debug("\tsparse.size: {} -> {}", m_sparse.size(), new_capacity);
m_sparse.resize(new_capacity, null_identifier); m_sparse.resize(new_capacity, null_identifier);
} }
++m_alive_count; ++m_alive_count;
m_sparse[identifier] = static_cast<Identifier_T>(m_dense.size()); m_sparse[identifier] = m_dense.size();
return m_dense.emplace_back(identifier, std::move(value)); return m_dense.emplace_back(identifier, std::move(value));
} }
@ -72,27 +78,7 @@ public:
*/ */
void remove(Identifier_T identifier) override void remove(Identifier_T identifier) override
{ {
ensure(
identifier < m_sparse.size(),
"Failed to ensure: identifier < m_sparse.size() [{} < {}]",
identifier,
m_sparse.size()
);
auto &idx = m_sparse[identifier]; auto &idx = m_sparse[identifier];
ensure(
idx != null_identifier,
"Failed to ensure: idx != null_identifier [{} != {}]",
idx,
null_identifier
);
ensure(
idx < m_dense.size(),
"Failed to ensure: idx < m_dense.size() [{} < {}]",
idx,
m_dense.size()
);
auto &[entity, component] = m_dense[idx]; auto &[entity, component] = m_dense[idx];
auto &[last_entity, last_component] = m_dense.back(); auto &[last_entity, last_component] = m_dense.back();
@ -163,12 +149,12 @@ public:
return std::forward<Self_T>(self).m_dense[std::forward<Self_T>(self).m_sparse[identifier]]; 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; 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(); return m_sparse.capacity();
} }
@ -183,9 +169,9 @@ private:
std::vector<Identifier_T> m_sparse; 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 } // namespace lt::ecs

View file

@ -1,26 +1,28 @@
import test;
import ecs.sparse_set; import ecs.sparse_set;
import test.test;
import test.expects;
import std;
using Value_T = i32; using ::lt::test::Case;
using Set = lt::ecs::SparseSet<Value_T>; 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; constexpr auto capacity = 100;
Suite raii = "raii"_suite = [] { Suite raii = "raii"_suite = [] {
Case { "happy paths" } = [] { Case { "happy path won't throw" } = [] {
ignore = Set {}; std::ignore = Set {};
ignore = Set { Set::max_capacity }; std::ignore = Set { Set::max_capacity };
}; };
Case { "unhappy paths" } = [] { Case { "unhappy path throws" } = [] {
expect_throw([] { ignore = Set { Set::max_capacity + 1 }; }); expect_throw([] { std::ignore = Set { Set::max_capacity + 1 }; });
};
Case { "many" } = [] {
for (auto idx : std::views::iota(0, 1'000))
{
ignore = Set { static_cast<size_t>(idx) };
}
}; };
Case { "post construct has correct state" } = [&] { Case { "post construct has correct state" } = [&] {
@ -31,29 +33,7 @@ Suite raii = "raii"_suite = [] {
}; };
Suite element_raii = "element_raii"_suite = [] { Suite element_raii = "element_raii"_suite = [] {
Case { "happy paths" } = [] { Case { "many inserts/removes won't freeze/throw" } = [] {
auto set = Set { capacity };
set.insert(0, {});
set.remove(0);
};
Case { "unhappy paths" } = [] {
expect_throw([] {
auto set = Set { capacity };
set.insert(Set::max_capacity + 1, {});
});
expect_throw([] {
auto set = Set { capacity };
set.insert(0, {});
set.insert(1, {});
set.insert(2, {});
set.remove(3);
});
};
Case { "many" } = [] {
auto set = Set {}; auto set = Set {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
@ -97,7 +77,7 @@ Suite element_raii = "element_raii"_suite = [] {
expect_eq(set.get_size(), 10'000 - (idx + 1)); expect_eq(set.get_size(), 10'000 - (idx + 1));
expect_false(set.contains(idx)); expect_false(set.contains(idx));
expect_throw([&] { ignore = set.at(idx); }); expect_throw([&] { std::ignore = set.at(idx); });
} }
}; };
@ -117,7 +97,7 @@ Suite element_raii = "element_raii"_suite = [] {
for (auto &[identifier, value] : set) for (auto &[identifier, value] : set)
{ {
expect_eq(static_cast<Value_T>(identifier), value); expect_eq(identifier, value);
expect_ne(value, 0); expect_ne(value, 0);
expect_ne(value, 32); expect_ne(value, 32);
expect_ne(value, 69); expect_ne(value, 69);
@ -149,7 +129,7 @@ Suite getters = "getters"_suite = [] {
expect_eq(set.get_capacity(), 10'000); expect_eq(set.get_capacity(), 10'000);
set.insert(static_cast<Value_T>(set.get_size()), {}); set.insert(set.get_size(), {});
expect_ne(set.get_capacity(), 10'000); expect_ne(set.get_capacity(), 10'000);
}; };
@ -160,12 +140,12 @@ Suite getters = "getters"_suite = [] {
{ {
expect_throw([&] { expect_throw([&] {
set.insert(idx, {}); set.insert(idx, {});
ignore = set.at(50); std::ignore = set.at(50);
}); });
} }
set.insert(50, {}); set.insert(50, {});
ignore = set.at(50); // should not throw std::ignore = set.at(50); // should not throw
}; };
}; };
@ -179,10 +159,5 @@ Suite clear = "clear"_suite = [] {
set.clear(); set.clear();
expect_eq(set.get_size(), 0); expect_eq(set.get_size(), 0);
for (auto idx : std::views::iota(0, 10'000))
{
expect_throw([&] { ignore = set.at(idx); });
}
}; };
}; };

View file

@ -1,11 +1,10 @@
export module preliminary.build_constants; export module env;
import preliminary.fundumental_types;
import std; import std;
export namespace build_constants { namespace lt {
enum class Platform : u8 enum class Platform : std::uint8_t
{ {
/** The GNU/Linux platform. /** The GNU/Linux platform.
* Tested on the following distros: arch-x86_64 * Tested on the following distros: arch-x86_64
@ -27,7 +26,7 @@ enum class Platform : u8
}; };
/** The compiler that was used for compiling the project. */ /** The compiler that was used for compiling the project. */
enum class Compiler : u8 enum class Compiler : std::uint8_t
{ {
clang, clang,
gcc, gcc,
@ -35,51 +34,37 @@ enum class Compiler : u8
apple_clang, apple_clang,
}; };
enum class BuildType namespace constants {
{
debug,
release,
distribution
};
#if defined(LIGHT_PLATFORM_WINDOWS) #if defined(LIGHT_PLATFORM_WINDOWS)
#define lt_win(x) #define lt_win(x)
constexpr auto platform = Platform::windows; constexpr auto platform = Platform::windows;
constexpr auto platform_name = "windows"; constexpr auto platform_name = "windows";
constexpr auto platform_identifier = platform_name; // TODO(Light)
#undef LIGHT_PLATFORM_WINDOWS #undef LIGHT_PLATFORM_WINDOWS
#elif defined(LIGHT_PLATFORM_LINUX) #elif defined(LIGHT_PLATFORM_LINUX)
constexpr auto platform = Platform::gnu_linux; constexpr auto platform = Platform::gnu_linux;
constexpr auto platform_name = "gnu_linux"; constexpr auto platform_name = "gnu_linux";
constexpr auto platform_identifier = platform_name; // TODO(Light)
#elif defined(LIGHT_PLATFORM_MAC) #elif defined(LIGHT_PLATFORM_MAC)
#define lt_mac(x) x #define lt_mac(x) x
constexpr auto platform = Platform::mac; constexpr auto platform = Platform::mac;
constexpr auto platform_name = "mac"; constexpr auto platform_name = "mac";
constexpr auto platform_identifier = platform_name; // TODO(Light)
#else #else
#error "Unsupported platform: Unknown" #error "Unsupported platform: Unknown"
#endif #endif
/** @TODO(Light): Handle other compilers... */
#ifdef __clang__ #ifdef __clang__
constexpr auto compiler = Compiler::clang; constexpr auto compiler = Compiler::clang;
constexpr auto compiler_name = "clang"; constexpr auto compiler_name = "clang";
/** @TODO(Light): insert the full identifier, including version information and such */ /** @todo(Light): insert the full identifier, including version information and such */
constexpr auto compiler_identifier = "clang"; constexpr auto full_compiler_identifier = "clang";
#endif #endif
// @TODO(Light): inject build info through CMake using LIGHT_... constant macros } // namespace constants
#if defined(_DEBUG)
constexpr auto build_type = BuildType::debug;
#else
constexpr auto build_type = BuildType::release;
#endif
} // namespace build_constants } // namespace lt

View file

@ -1,18 +1,17 @@
export module input.system:components; export module input.system:components;
import preliminary;
import input.codes; import input.codes;
import std;
export namespace lt::input { namespace lt::input {
struct Trigger export struct Trigger
{ {
Key mapped_keycode; Key mapped_keycode;
}; };
struct InputAction export struct InputAction
{ {
enum class State : u8 enum class State : std::uint8_t
{ {
inactive, inactive,
active, active,
@ -27,18 +26,18 @@ struct InputAction
Trigger trigger; Trigger trigger;
}; };
class InputComponent export class InputComponent
{ {
public: public:
InputComponent() = default; InputComponent() = default;
auto add_action(InputAction action) -> size_t auto add_action(InputAction action) -> std::size_t
{ {
m_actions.emplace_back(std::move(action)); m_actions.emplace_back(std::move(action));
return m_actions.size() - 1; return m_actions.size() - 1;
} }
auto get_action(size_t idx) -> const InputAction & auto get_action(std::size_t idx) -> const InputAction &
{ {
return m_actions[idx]; return m_actions[idx];
} }

View file

@ -2,6 +2,7 @@ export module input.system;
export import :components; export import :components;
import logger; import logger;
import app.system; import app.system;
import debug.assertions;
import ecs.registry; import ecs.registry;
import memory.reference; import memory.reference;
import surface.system; import surface.system;
@ -68,7 +69,7 @@ struct overloads: Ts...
System::System(memory::Ref<ecs::Registry> registry): m_registry(std::move(registry)) System::System(memory::Ref<ecs::Registry> registry): m_registry(std::move(registry))
{ {
ensure(m_registry, "Failed to initialize input system: null registry"); debug::ensure(m_registry, "Failed to initialize input system: null registry");
} }
void System::tick(app::TickInfo tick) void System::tick(app::TickInfo tick)
@ -157,10 +158,8 @@ void System::on_key_press(const lt::surface::KeyPressedEvent &event)
{ {
if (std::to_underlying(event.get_key()) > m_keys.size()) if (std::to_underlying(event.get_key()) > m_keys.size())
{ {
log::warn( log::debug("Key code larger than key container size, implement platform-dependant "
"Key code larger than key container size, implement platform-dependant " "key-code-mapping!");
"key-code-mapping!"
);
return; return;
} }
@ -172,10 +171,8 @@ void System::on_key_release(const lt::surface::KeyReleasedEvent &event)
{ {
if (std::to_underlying(event.get_key()) > m_keys.size()) if (std::to_underlying(event.get_key()) > m_keys.size())
{ {
log::warn( log::debug("Key code larger than key container size, implement platform-dependant "
"Key code larger than key container size, implement platform-dependant " "key-code-mapping!");
"key-code-mapping!"
);
return; return;
} }

View file

@ -1,6 +1,9 @@
import test; import std;
import input.system; import input.system;
import input.codes; import input.codes;
import std;
import test.test;
import test.expects;
import surface.events; import surface.events;
import memory.scope; import memory.scope;
import memory.reference; import memory.reference;
@ -9,10 +12,23 @@ import ecs.entity;
import ecs.registry; import ecs.registry;
import surface.system; import surface.system;
using ::lt::input::InputComponent;
using ::lt::input::System;
[[nodiscard]] auto tick_info() -> lt::app::TickInfo // NOLINTBEGIN
using namespace lt;
using input::InputComponent;
using input::System;
using std::ignore;
using test::Case;
using test::expect_eq;
using test::expect_false;
using test::expect_ne;
using test::expect_not_nullptr;
using test::operator""_suite;
using test::expect_throw;
using test::Suite;
// NOLINTEND
[[nodiscard]] auto tick_info() -> app::TickInfo
{ {
return { return {
.delta_time = std::chrono::milliseconds { 16 }, .delta_time = std::chrono::milliseconds { 16 },
@ -24,12 +40,12 @@ using ::lt::input::System;
class Fixture class Fixture
{ {
public: public:
[[nodiscard]] auto registry() -> lt::memory::Ref<lt::ecs::Registry> [[nodiscard]] auto registry() -> memory::Ref<ecs::Registry>
{ {
return m_registry; return m_registry;
} }
auto add_input_component() -> lt::ecs::EntityId auto add_input_component() -> ecs::EntityId
{ {
auto entity = m_registry->create_entity(); auto entity = m_registry->create_entity();
m_registry->add<InputComponent>(entity, {}); m_registry->add<InputComponent>(entity, {});
@ -37,7 +53,7 @@ public:
return entity; return entity;
} }
auto add_surface_component() -> lt::ecs::EntityId auto add_surface_component() -> ecs::EntityId
{ {
auto entity = m_registry->create_entity(); auto entity = m_registry->create_entity();
m_surface_system.create_surface_component( m_surface_system.create_surface_component(
@ -49,28 +65,27 @@ public:
} }
private: private:
lt::memory::Ref<lt::ecs::Registry> m_registry = lt::memory::create_ref<lt::ecs::Registry>(); memory::Ref<ecs::Registry> m_registry = memory::create_ref<ecs::Registry>();
lt::surface::System m_surface_system = lt::surface::System { m_registry }; surface::System m_surface_system = surface::System { m_registry };
}; };
Suite raii = "raii"_suite = "raii"_suite = [] { Suite raii = "raii"_suite = "raii"_suite = [] {
Case { "happy paths" } = [&] { Case { "happy path won't throw" } = [&] {
System { Fixture {}.registry() }; System { Fixture {}.registry() };
}; };
Case { "unhappy paths" } = [] { Case { "many won't freeze/throw" } = [&] {
expect_throw([] { ignore = System { {} }; });
};
Case { "many" } = [&] {
auto fixture = Fixture {}; auto fixture = Fixture {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
ignore = idx;
ignore = System { fixture.registry() }; ignore = System { fixture.registry() };
} }
}; };
Case { "unhappy path throws" } = [] {
expect_throw([] { ignore = System { {} }; });
};
}; };
Suite system_events = "system_events"_suite = [] { Suite system_events = "system_events"_suite = [] {
@ -100,14 +115,14 @@ Suite registry_events = "registry_events"_suite = [] {
auto registry = fixture.registry(); auto registry = fixture.registry();
auto system = System { registry }; auto system = System { registry };
fixture.add_input_component(); const auto &entity = fixture.add_input_component();
expect_eq(registry->view<InputComponent>().get_size(), 1); expect_eq(registry->view<InputComponent>().get_size(), 1);
}; };
Case { "on_destrroy<InputComponent>" } = [] { Case { "on_destrroy<InputComponent>" } = [] {
auto fixture = Fixture {}; auto fixture = Fixture {};
auto registry = fixture.registry(); auto registry = fixture.registry();
auto system = lt::memory::create_scope<System>(registry); auto system = memory::create_scope<System>(registry);
auto entity_a = fixture.add_input_component(); auto entity_a = fixture.add_input_component();
auto entity_b = fixture.add_input_component(); auto entity_b = fixture.add_input_component();
@ -139,7 +154,7 @@ Suite tick = "tick"_suite = [] {
auto system = System { fixture.registry() }; auto system = System { fixture.registry() };
auto surface_entity = fixture.add_surface_component(); auto surface_entity = fixture.add_surface_component();
auto &surface = registry->get<lt::surface::SurfaceComponent>(surface_entity); auto &surface = registry->get<surface::SurfaceComponent>(surface_entity);
auto input_entity = fixture.add_input_component(); auto input_entity = fixture.add_input_component();
auto &input = registry->get<InputComponent>(input_entity); auto &input = registry->get<InputComponent>(input_entity);
@ -147,29 +162,48 @@ Suite tick = "tick"_suite = [] {
auto action_key = input.add_action( auto action_key = input.add_action(
{ {
.name { "test" }, .name { "test" },
.trigger = { .mapped_keycode = Key::a }, .trigger = { .mapped_keycode = Key::A },
} }
); );
using enum ::lt::input::InputAction::State; expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
expect_eq(input.get_action(action_key).state, inactive);
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, inactive); expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
surface.push_event(lt::surface::KeyPressedEvent(Key::a)); surface.push_event(surface::KeyPressedEvent(Key::A));
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, triggered); expect_eq(input.get_action(action_key).state, input::InputAction::State::triggered);
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, active); expect_eq(input.get_action(action_key).state, input::InputAction::State::active);
system.tick(tick_info()); system.tick(tick_info());
system.tick(tick_info()); system.tick(tick_info());
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, active); expect_eq(input.get_action(action_key).state, input::InputAction::State::active);
surface.push_event(lt::surface::KeyReleasedEvent(Key::a)); surface.push_event(surface::KeyReleasedEvent(Key::A));
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, inactive); expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
};
Case { "Tick triggers" } = [] {
auto fixture = Fixture {};
auto registry = fixture.registry();
auto system = System { fixture.registry() };
auto surface_entity = fixture.add_surface_component();
auto &surface = registry->get<surface::SurfaceComponent>(surface_entity);
auto input_entity = fixture.add_input_component();
auto &input = registry->get<InputComponent>(input_entity);
auto action_key = input.add_action(
{
.name { "test" },
.trigger = { .mapped_keycode = Key::A },
}
);
}; };
}; };

View file

@ -1,31 +1,20 @@
/**
* @note: The reason this is a separate module, rather than being in the `Input` module is that
* the input is received from the hardware through the `Surface` module, and it is further parsed
* inside the `Input` module, USING the `Surface` module's events.
*
* Hence, both `Surface` and `Input` needs to agree to the same input codes, while `Input` depends
* on `Surface`. The simplest solution is to keep the codes in a 3rd module and make both depend on
* it. (I did not want to give `Surface` the responsibility of defining input codes...)
*/
export module input.codes; export module input.codes;
import std;
import preliminary; export enum class Key: std::uint16_t {
export enum class Key: u16 {
none = 0, none = 0,
left_button, left_mouse_button,
l_button = left_button, right_mouse_button,
middle_mouse_button,
right_button, left_mouse = left_mouse_button,
r_button = right_button, right_mouse = right_mouse_button,
middle_mouse = middle_mouse_button,
middle_button, l_mouse = left_mouse_button,
m_button = middle_button, r_mouse = right_mouse_button,
m_mouse = middle_mouse_button,
// the buttons on the sidse of some mouses
x_button_1,
x_button_2,
backspace, backspace,
tab, tab,
@ -98,6 +87,7 @@ export enum class Key: u16 {
digit_8, digit_8,
digit_9, digit_9,
/* letters */
a, a,
b, b,
c, c,
@ -150,6 +140,7 @@ export enum class Key: u16 {
kp_enter, kp_enter,
kp_equal, kp_equal,
/* function */
f1, f1,
f2, f2,
f3, f3,
@ -163,7 +154,6 @@ export enum class Key: u16 {
f11, f11,
f12, f12,
/** Input was received but was none of the above. */
unknown, unknown,
}; };
@ -174,13 +164,12 @@ export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
{ {
case none: return "<none>"; case none: return "<none>";
case left_button: return "left_button"; /* mouse */
case right_button: return "right_button"; case left_mouse_button: return "left_mouse_button";
case middle_button: return "middle_button"; case right_mouse_button: return "right_mouse_button";
case middle_mouse_button: return "middle_mouse_button";
case x_button_1: return "x_button_1";
case x_button_2: return "x_button_2";
/* editing / control */
case backspace: return "backspace"; case backspace: return "backspace";
case tab: return "tab"; case tab: return "tab";
case capslock: return "capslock"; case capslock: return "capslock";
@ -188,12 +177,14 @@ export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
case space: return "space"; case space: return "space";
case delete_: return "delete"; case delete_: return "delete";
/* modifiers */
case shift: return "shift"; case shift: return "shift";
case control: return "control"; case control: return "control";
case right_control: return "right_control"; case right_control: return "right_control";
case alt: return "alt"; case alt: return "alt";
case right_alt: return "right_alt"; case right_alt: return "right_alt";
/* navigation */
case pageup: return "pageup"; case pageup: return "pageup";
case pagedown: return "pagedown"; case pagedown: return "pagedown";
case home: return "home"; case home: return "home";
@ -204,6 +195,7 @@ export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
case right_arrow: return "right_arrow"; case right_arrow: return "right_arrow";
case down_arrow: return "down_arrow"; case down_arrow: return "down_arrow";
/* system */
case cancel: return "cancel"; case cancel: return "cancel";
case pause: return "pause"; case pause: return "pause";
case select: return "select"; case select: return "select";
@ -213,6 +205,7 @@ export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
case help: return "help"; case help: return "help";
case sleep: return "sleep"; case sleep: return "sleep";
/* digits */
case digit_0: return "0"; case digit_0: return "0";
case digit_1: return "1"; case digit_1: return "1";
case digit_2: return "2"; case digit_2: return "2";
@ -224,6 +217,7 @@ export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
case digit_8: return "8"; case digit_8: return "8";
case digit_9: return "9"; case digit_9: return "9";
/* letters */
case a: return "a"; case a: return "a";
case b: return "b"; case b: return "b";
case c: return "c"; case c: return "c";
@ -251,9 +245,11 @@ export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
case y: return "y"; case y: return "y";
case z: return "z"; case z: return "z";
/* super / meta */
case super: return "super"; case super: return "super";
case right_super: return "right_super"; case right_super: return "right_super";
/* keypad */
case kp_0: return "kp_0"; case kp_0: return "kp_0";
case kp_1: return "kp_1"; case kp_1: return "kp_1";
case kp_2: return "kp_2"; case kp_2: return "kp_2";
@ -272,6 +268,7 @@ export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
case kp_enter: return "kp_enter"; case kp_enter: return "kp_enter";
case kp_equal: return "kp_equal"; case kp_equal: return "kp_equal";
/* function keys */
case f1: return "f1"; case f1: return "f1";
case f2: return "f2"; case f2: return "f2";
case f3: return "f3"; case f3: return "f3";

View file

@ -1,11 +1,11 @@
export module logger; export module logger;
import preliminary; import std;
namespace lt::log { namespace lt::log {
/** Severity of a log message. */ /** Severity of a log message. */
enum class Level : u8 enum class Level : std::uint8_t
{ {
/** Lowest and most vebose log level, for tracing execution paths and events */ /** Lowest and most vebose log level, for tracing execution paths and events */
trace = 0, trace = 0,
@ -31,9 +31,9 @@ enum class Level : u8
namespace details { namespace details {
inline auto thread_hash_id() noexcept -> u64 inline auto thread_hash_id() noexcept -> std::uint64_t
{ {
return static_cast<u64>(std::hash<std::thread::id> {}(std::this_thread::get_id())); return static_cast<std::uint64_t>(std::hash<std::thread::id> {}(std::this_thread::get_id()));
} }
} // namespace details } // namespace details
@ -48,7 +48,7 @@ struct [[maybe_unused]] print
Args &&...arguments Args &&...arguments
) noexcept ) noexcept
{ {
constexpr auto to_string = [](Level level) { constexpr auto to_string = [](Level level, auto location) {
// clang-format off // clang-format off
switch (level) switch (level)
{ {
@ -70,7 +70,7 @@ struct [[maybe_unused]] print
std::println( std::println(
"{} {} ==> {}", "{} {} ==> {}",
to_string(level), to_string(level, location),
std::format("{}:{}", path.filename().string(), location.line()), std::format("{}:{}", path.filename().string(), location.line()),
std::format(format, std::forward<Args>(arguments)...) std::format(format, std::forward<Args>(arguments)...)
); );

View file

@ -1,7 +1,11 @@
import test; import logger;
import test.test;
using ::lt::test::Case;
using ::lt::test::Suite;
Suite suite = [] { Suite suite = [] {
Case { "formatless" } = [] { Case { "no format" } = [] {
lt::log::trace("trace"); lt::log::trace("trace");
lt::log::debug("debug"); lt::log::debug("debug");
lt::log::info("info"); lt::log::info("info");

View file

@ -1,7 +1,6 @@
export module math.algebra; export module math.algebra;
import preliminary;
import math.mat4; import math.mat4;
import std;
export namespace lt::math { export namespace lt::math {
@ -36,7 +35,6 @@ export namespace lt::math {
* https://www.youtube.com/watch?v=EqNcqBdrNyI * https://www.youtube.com/watch?v=EqNcqBdrNyI
*/ */
template<typename T> template<typename T>
requires(std::is_arithmetic_v<T>)
constexpr auto perspective(T field_of_view, T aspect_ratio, T z_near, T z_far) constexpr auto perspective(T field_of_view, T aspect_ratio, T z_near, T z_far)
{ {
const T half_fov_tan = std::tan(field_of_view / static_cast<T>(2)); const T half_fov_tan = std::tan(field_of_view / static_cast<T>(2));

View file

@ -1,6 +1,5 @@
export module math.components; export module math.components;
import preliminary;
import math.vec3; import math.vec3;
namespace lt::math::components { namespace lt::math::components {

View file

@ -1,14 +1,11 @@
export module math.mat4; export module math.mat4;
import preliminary;
import math.vec2;
import math.vec3; import math.vec3;
import math.vec4; import math.vec4;
import std;
export namespace lt::math { namespace lt::math {
template<typename T = f32> export template<typename T = float>
requires(std::is_arithmetic_v<T>)
struct mat4_impl struct mat4_impl
{ {
using Column_T = vec4_impl<T>; using Column_T = vec4_impl<T>;
@ -57,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]; 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]; return values[idx];
} }
@ -77,41 +74,37 @@ struct mat4_impl
return vec4_impl<T> {}; return vec4_impl<T> {};
} }
std::array<Column_T, 4u> values; std::array<Column_T, 4> values; // NOLINT
}; };
/** @todo(Light): Implement */ export template<typename T>
template<typename T>
[[nodiscard]] auto translate(const vec3_impl<T> &value) -> mat4_impl<T> [[nodiscard]] auto translate(const vec3_impl<T> &value) -> mat4_impl<T>
{ {
return mat4_impl<T> {}; return mat4_impl<T> {};
} }
/** @todo(Light): Implement */ export template<typename T>
template<typename T> [[nodiscard]] auto rotate(float value, const vec3_impl<T> &xyz) -> mat4_impl<T>
[[nodiscard]] auto rotate(f32 value, const vec3_impl<T> &xyz) -> mat4_impl<T>
{ {
return mat4_impl<T> {}; return mat4_impl<T> {};
} }
/** @todo(Light): Implement */ export template<typename T>
template<typename T>
[[nodiscard]] auto scale(const vec3_impl<T> &value) -> mat4_impl<T> [[nodiscard]] auto scale(const vec3_impl<T> &value) -> mat4_impl<T>
{ {
return mat4_impl<T> {}; return mat4_impl<T> {};
} }
/** @todo(Light): Implement */ export template<typename T>
template<typename T>
[[nodiscard]] auto inverse(const mat4_impl<T> &value) -> mat4_impl<T> [[nodiscard]] auto inverse(const mat4_impl<T> &value) -> mat4_impl<T>
{ {
return mat4_impl<T> {}; return mat4_impl<T> {};
} }
using mat4 = mat4_impl<f32>; export using mat4 = mat4_impl<float>;
using imat4 = mat4_impl<i32>; export using imat4 = mat4_impl<std::int32_t>;
using umat4 = mat4_impl<u32>; export using umat4 = mat4_impl<std::uint32_t>;
} // namespace lt::math } // namespace lt::math

View file

@ -1,27 +1,26 @@
export module math.trig; export module math.trig;
import preliminary;
export namespace lt::math { export namespace lt::math {
[[nodiscard]] constexpr auto radians(f32 degrees) -> f32 [[nodiscard]] constexpr auto radians(float degrees) -> float
{ {
return degrees * 0.01745329251994329576923690768489f; return degrees * 0.01745329251994329576923690768489f;
} }
[[nodiscard]] constexpr auto radians(f64 degrees) -> f64 [[nodiscard]] constexpr auto radians(double degrees) -> double
{ {
return degrees * 0.01745329251994329576923690768489; return degrees * 0.01745329251994329576923690768489;
} }
[[nodiscard]] constexpr auto degrees(f32 radians) -> f32 [[nodiscard]] constexpr auto degrees(float radians) -> float
{ {
return radians * 57.295779513082320876798154814105f; return radians * 57.295779513082320876798154814105f;
} }
[[nodiscard]] constexpr auto degrees(f64 radians) -> f64 [[nodiscard]] constexpr auto degrees(double radians) -> double
{ {
return radians * 57.295779513082320876798154814105; return radians * 57.295779513082320876798154814105;
} }
} // namespace lt::math } // namespace lt::math

View file

@ -1,15 +1,12 @@
export module math.vec2; export module math.vec2;
import preliminary; import std;
export namespace lt::math { namespace lt::math {
template<typename T = f32> export template<typename T = float>
requires(std::is_arithmetic_v<T>)
struct vec2_impl struct vec2_impl
{ {
static constexpr auto num_elements = 2u;
constexpr vec2_impl(): x(), y() constexpr vec2_impl(): x(), y()
{ {
} }
@ -32,23 +29,7 @@ struct vec2_impl
return !(*this == other); return !(*this == other);
} }
[[nodiscard]] constexpr auto operator+(const vec2_impl<T> &other) const -> vec2_impl [[nodiscard]] auto operator*(const vec2_impl<T> &other) const -> vec2_impl
{
return {
x + other.x,
y + other.y,
};
}
[[nodiscard]] constexpr auto operator-(const vec2_impl<T> &other) const -> vec2_impl
{
return {
x - other.x,
y - other.y,
};
}
[[nodiscard]] constexpr auto operator*(const vec2_impl<T> &other) const -> vec2_impl
{ {
return { return {
x * other.x, x * other.x,
@ -56,43 +37,33 @@ struct vec2_impl
}; };
} }
[[nodiscard]] constexpr auto operator/(const vec2_impl<T> &other) const -> vec2_impl [[nodiscard]] auto operator-(const vec2_impl<T> &other) const -> vec2_impl
{ {
return { return {
x / other.x, x - other.x,
y / other.y, y - other.y,
}; };
} }
[[nodiscard]] constexpr auto operator[](u8 idx) -> T & [[nodiscard]] auto operator*(float scalar) const -> vec2_impl
{ {
debug_check(idx <= num_elements, "vec2 out of bound: {}", idx); return {
return ((T *)this)[idx]; x * scalar,
y * scalar,
};
} }
[[nodiscard]] constexpr auto operator[](u8 idx) const -> const T & T x; // NOLINT
{
debug_check(idx < num_elements, "vec2 out of bound: {}", idx);
return ((T *)this)[idx];
}
friend auto operator<<(std::ostream &stream, vec2_impl<T> value) -> std::ostream & T y; // NOLINT
{
stream << value.x << ", " << value.y;
return stream;
}
T x;
T y;
}; };
using vec2 = vec2_impl<f32>; export using vec2 = vec2_impl<float>;
using ivec2 = vec2_impl<i32>; export using ivec2 = vec2_impl<std::int32_t>;
using uvec2 = vec2_impl<u32>; export using uvec2 = vec2_impl<std::uint32_t>;
} // namespace lt::math } // namespace lt::math

View file

@ -1,7 +0,0 @@
import test;
import math.vec2;
Suite raii = "raii"_suite = [] {
Case { "happy path" } = [] {
};
};

View file

@ -1,16 +1,13 @@
export module math.vec3; export module math.vec3;
import preliminary;
import math.vec2; import math.vec2;
import std;
export namespace lt::math { namespace lt::math {
template<typename T = f32> export template<typename T = float>
requires(std::is_arithmetic_v<T>)
struct vec3_impl struct vec3_impl
{ {
static constexpr auto num_elements = 3u;
constexpr vec3_impl(): x(), y(), z() constexpr vec3_impl(): x(), y(), z()
{ {
} }
@ -23,14 +20,6 @@ struct vec3_impl
{ {
} }
constexpr vec3_impl(vec2_impl<T> xy, T z): x(xy.x), y(xy.y), z(z)
{
}
constexpr vec3_impl(T x, vec2_impl<T> yz): x(x), y(yz.y), z(yz.z)
{
}
[[nodiscard]] auto operator==(const vec3_impl<T> &other) const -> bool [[nodiscard]] auto operator==(const vec3_impl<T> &other) const -> bool
{ {
return x == other.x && y == other.y && z == other.z; return x == other.x && y == other.y && z == other.z;
@ -41,15 +30,6 @@ struct vec3_impl
return !(*this == other); return !(*this == other);
} }
[[nodiscard]] constexpr auto operator+(const vec3_impl<T> &other) const -> vec3_impl
{
return {
x + other.x,
y + other.y,
z + other.z,
};
}
[[nodiscard]] constexpr auto operator-(const vec3_impl<T> &other) const -> vec3_impl [[nodiscard]] constexpr auto operator-(const vec3_impl<T> &other) const -> vec3_impl
{ {
return { return {
@ -68,49 +48,28 @@ struct vec3_impl
}; };
} }
[[nodiscard]] constexpr auto operator/(const vec3_impl<T> &other) const -> vec3_impl
{
return {
x / other.x,
y / other.y,
z / other.z,
};
}
[[nodiscard]] constexpr auto operator[](u8 idx) -> T &
{
debug_check(idx <= num_elements, "vec3 out of bound: {}", idx);
return ((T *)this)[idx];
}
[[nodiscard]] constexpr auto operator[](u8 idx) const -> const T &
{
debug_check(idx < num_elements, "vec3 out of bound: {}", idx);
return ((T *)this)[idx];
}
friend auto operator<<(std::ostream &stream, vec3_impl<T> value) -> std::ostream & friend auto operator<<(std::ostream &stream, vec3_impl<T> value) -> std::ostream &
{ {
stream << value.x << ", " << value.y << ", " << value.z; stream << value.x << ", " << value.y << ", " << value.z;
return stream; return stream;
} }
T x; T x; // NOLINT
T y; T y; // NOLINT
T z; T z; // NOLINT
}; };
using vec3 = vec3_impl<f32>; export using vec3 = vec3_impl<float>;
using ivec3 = vec3_impl<i32>; export using ivec3 = vec3_impl<std::int32_t>;
using uvec3 = vec3_impl<u32>; export using uvec3 = vec3_impl<std::uint32_t>;
} // namespace lt::math } // namespace lt::math
export template<typename T> template<typename T>
struct std::formatter<lt::math::vec3_impl<T>> struct std::formatter<lt::math::vec3_impl<T>>
{ {
constexpr auto parse(std::format_parse_context &context) constexpr auto parse(std::format_parse_context &context)

View file

@ -1,17 +1,13 @@
export module math.vec4; export module math.vec4;
import preliminary;
import math.vec2; import math.vec2;
import math.vec3; import math.vec3;
import std;
export namespace lt::math { namespace lt::math {
template<typename T = f32> export template<typename T = float>
requires(std::is_arithmetic_v<T>)
struct vec4_impl struct vec4_impl
{ {
static constexpr auto num_elements = 4u;
constexpr vec4_impl(): x(), y(), z(), w() constexpr vec4_impl(): x(), y(), z(), w()
{ {
} }
@ -24,26 +20,6 @@ struct vec4_impl
{ {
} }
constexpr vec4_impl(vec2_impl<T> xy, T z, T w): x(xy.x), y(xy.y), z(z), w(w)
{
}
constexpr vec4_impl(T x, T y, vec2_impl<T> zw): x(x), y(y), z(zw.z), w(zw.w)
{
}
constexpr vec4_impl(vec2_impl<T> xy, vec2_impl<T> zw): x(xy.x), y(xy.y), z(zw.z), w(zw.w)
{
}
constexpr vec4_impl(vec3_impl<T> xyz, T w): x(xyz.x), y(xyz.y), z(xyz.z), w(w)
{
}
constexpr vec4_impl(T x, vec3_impl<T> yzw): x(x), y(yzw.y), z(yzw.z), w(yzw.w)
{
}
[[nodiscard]] auto operator==(const vec4_impl<T> &other) const -> bool [[nodiscard]] auto operator==(const vec4_impl<T> &other) const -> bool
{ {
return x == other.x && y == other.y && z == other.z && w == other.w; return x == other.x && y == other.y && z == other.z && w == other.w;
@ -54,16 +30,6 @@ struct vec4_impl
return !(*this == other); return !(*this == other);
} }
[[nodiscard]] constexpr auto operator+(const vec4_impl<T> &other) const -> vec4_impl
{
return {
x + other.x,
y + other.y,
z + other.z,
w + other.w,
};
}
[[nodiscard]] constexpr auto operator-(const vec4_impl<T> &other) const -> vec4_impl [[nodiscard]] constexpr auto operator-(const vec4_impl<T> &other) const -> vec4_impl
{ {
return { return {
@ -74,36 +40,14 @@ struct vec4_impl
}; };
} }
[[nodiscard]] constexpr auto operator*(const vec4_impl<T> &other) const -> vec4_impl [[nodiscard]] constexpr auto operator[](std::size_t idx) -> T &
{ {
return { return values[idx];
x * other.x,
y * other.y,
z * other.z,
w * other.w,
};
} }
[[nodiscard]] constexpr auto operator/(const vec4_impl<T> &other) const -> vec4_impl [[nodiscard]] constexpr auto operator[](std::size_t idx) const -> const T &
{ {
return { return values[idx];
x / other.x,
y / other.y,
z / other.z,
w / other.w,
};
}
[[nodiscard]] constexpr auto operator[](u8 idx) -> T &
{
debug_check(idx <= num_elements, "vec4 out of bound: {}", idx);
return ((T *)this)[idx];
}
[[nodiscard]] constexpr auto operator[](u8 idx) const -> const T &
{
debug_check(idx < num_elements, "vec4 out of bound: {}", idx);
return ((T *)this)[idx];
} }
friend auto operator<<(std::ostream &stream, vec4_impl<T> value) -> std::ostream & friend auto operator<<(std::ostream &stream, vec4_impl<T> value) -> std::ostream &
@ -112,20 +56,41 @@ struct vec4_impl
return stream; return stream;
} }
T x; // NOLINTNEXTLINE
union
{
struct
{
T x;
T y; T y;
T z; T z;
T w; T w;
};
struct
{
T r;
T g;
T b;
T a;
};
struct
{
std::array<T, 4> values;
};
};
}; };
using vec4 = vec4_impl<f32>; export using vec4 = vec4_impl<float>;
using ivec4 = vec4_impl<i32>; export using ivec4 = vec4_impl<std::int32_t>;
using uvec4 = vec4_impl<u32>; export using uvec4 = vec4_impl<std::uint32_t>;
} // namespace lt::math } // namespace lt::math

View file

@ -2,14 +2,14 @@ export module memory.null_on_move;
import logger; import logger;
import preliminary; import std;
namespace lt::memory { namespace lt::memory {
/** Holds an `Underlying_T`, assigns it to `null_value` when this object is moved. /** 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 * @note For avoiding the need to explicitly implement the move constructor for objects that hold
* non-raii-handles (eg. Vulkan, Wayland). * Vulkan handles. But may serve other purposes, hence why I kept the implementation generic.
*/ */
export template<typename Underlying_T, Underlying_T null_value = nullptr> export template<typename Underlying_T, Underlying_T null_value = nullptr>
class NullOnMove class NullOnMove
@ -81,6 +81,11 @@ public:
return m_value; return m_value;
} }
operator std::uint64_t() const
{
return (std::uint64_t)m_value;
}
[[nodiscard]] auto get() -> Underlying_T & [[nodiscard]] auto get() -> Underlying_T &
{ {
return m_value; return m_value;

View file

@ -1,6 +1,6 @@
export module memory.reference; export module memory.reference;
import preliminary; import std;
namespace lt::memory { namespace lt::memory {

View file

@ -1,6 +1,6 @@
export module memory.scope; export module memory.scope;
import preliminary; import std;
namespace lt::memory { namespace lt::memory {

View file

@ -7,12 +7,12 @@ import mirror.system;
import renderer.factory; import renderer.factory;
/** The ultimate entrypoint. */ /** The ultimate entrypoint. */
auto main(i32 argc, char *argv[]) -> i32 auto main(int argc, char *argv[]) -> std::int32_t
{ {
try try
{ {
ignore = argc; std::ignore = argc;
ignore = argv; std::ignore = argv;
auto application = lt::memory::create_scope<lt::Mirror>(); auto application = lt::memory::create_scope<lt::Mirror>();
if (!application) if (!application)

View file

@ -0,0 +1,227 @@
#pragma once
#include <app/layer.hpp>
#include <imgui.h>
#include <math/vec2.hpp>
#include <memory/reference.hpp>
#include <mirror/panels/asset_browser.hpp>
#include <mirror/panels/properties.hpp>
#include <mirror/panels/scene_hierarchy.hpp>
#include <renderer/texture.hpp>
namespace lt {
class Scene;
class EditorLayer: public Layer
{
public:
EditorLayer(const std::string &name);
~EditorLayer() override;
EditorLayer(EditorLayer &&) = delete;
EditorLayer(const EditorLayer &) = delete;
auto operator=(EditorLayer &&) const -> EditorLayer & = delete;
auto operator=(const EditorLayer &) const -> EditorLayer & = delete;
void on_update(float delta_time) override;
void on_render() override;
void on_user_interface_update() override;
private:
std::string m_scene_dir;
math::vec2 m_direction;
float m_speed = 1000.0f;
memory::Ref<Scene> m_scene;
memory::Ref<SceneHierarchyPanel> m_sceneHierarchyPanel;
memory::Ref<PropertiesPanel> m_properties_panel;
memory::Ref<AssetBrowserPanel> m_content_browser_panel;
memory::Ref<Framebuffer> m_framebuffer;
Entity m_camera_entity;
ImVec2 m_available_content_region_prev;
};
} // namespace lt
#include <app/application.hpp>
#include <asset_manager/asset_manager.hpp>
#include <camera/component.hpp>
#include <ecs/components.hpp>
#include <ecs/registry.hpp>
#include <ecs/serializer.hpp>
#include <input/input.hpp>
#include <input/key_codes.hpp>
#include <math/vec4.hpp>
#include <memory/reference.hpp>
#include <mirror/layers/editor_layer.hpp>
#include <renderer/framebuffer.hpp>
#include <renderer/texture.hpp>
#include <ui/ui.hpp>
namespace lt {
EditorLayer::EditorLayer(const std::string &name)
: Layer(name)
, m_scene_dir("")
, m_direction { 0.0, 0.0 }
{
m_scene = memory::create_ref<Scene>();
m_properties_panel = memory::create_ref<PropertiesPanel>();
m_sceneHierarchyPanel = memory::create_ref<SceneHierarchyPanel>(m_scene, m_properties_panel);
m_content_browser_panel = memory::create_ref<AssetBrowserPanel>(m_scene);
m_framebuffer = Framebuffer::create(
{
.width = 1,
.height = 1,
.samples = 1,
},
GraphicsContext::get_shared_context()
);
if (m_scene_dir.empty())
{
m_camera_entity = m_scene->create_entity("Camera");
m_camera_entity.add_component<CameraComponent>(SceneCamera(), true);
AssetManager::load_texture("Awesomeface", "data/assets/textures/awesomeface.asset");
auto entity = Entity { m_scene->create_entity("Awesomeface", {}) };
entity.add_component<SpriteRendererComponent>(
AssetManager::get_texture("Awesomeface"),
math::vec4 { 0.0f, 1.0f, 1.0f, 1.0f }
);
}
else
{
auto serializer = SceneSerializer { m_scene };
ensure(serializer.deserialize(m_scene_dir), "Failed to de-serialize: {}", m_scene_dir);
// m_camera_entity = m_scene->GetEntityByTag("Game Camera");
}
}
EditorLayer::~EditorLayer()
{
if (!m_scene_dir.empty())
{
auto serializer = SceneSerializer { m_scene };
serializer.serialize(m_scene_dir);
}
}
void EditorLayer::on_update(float delta_time)
{
m_scene->on_update(delta_time);
if (Input::get_keyboard_key(Key::A))
{
m_direction.x = -1.0;
}
else if (Input::get_keyboard_key(Key::D))
{
m_direction.x = 1.0f;
}
else
{
m_direction.x = 0.0;
}
if (Input::get_keyboard_key(Key::S))
{
m_direction.y = -1.0;
}
else if (Input::get_keyboard_key(Key::W))
{
m_direction.y = 1.0f;
}
else
{
m_direction.y = 0.0;
}
auto &translation = m_camera_entity.get_component<TransformComponent>().translation;
auto velocity = m_direction * m_speed * delta_time;
translation = translation * math::vec3 { velocity.x, velocity.y, 0.0f };
if (Input::get_keyboard_key(Key::Escape))
{
Application::quit();
}
}
void EditorLayer::on_render()
{
m_scene->on_render(m_framebuffer);
}
void EditorLayer::on_user_interface_update()
{
UserInterface::dockspace_begin();
ImGui::ShowDemoWindow();
if (ImGui::Begin("Game"))
{
Input::receive_game_events(ImGui::IsWindowFocused());
auto available_region = ImGui::GetContentRegionAvail();
if (m_available_content_region_prev.x != available_region.x
|| m_available_content_region_prev.y != available_region.y)
{
m_framebuffer->resize(
math::uvec2 {
static_cast<uint32_t>(available_region.x),
static_cast<uint32_t>(available_region.y),
}
);
auto &camera = m_camera_entity.get_component<CameraComponent>().camera;
camera.set_viewport_size(
static_cast<uint32_t>(available_region.x),
static_cast<uint32_t>(available_region.y)
);
m_available_content_region_prev = available_region;
}
if (GraphicsContext::get_graphics_api() == GraphicsAPI::DirectX)
{
ImGui::Image(m_framebuffer->get_color_attachment(), available_region);
}
else
{
ImGui::Image(
m_framebuffer->get_color_attachment(),
available_region,
ImVec2(0, 1),
ImVec2(1, 0)
);
}
}
ImGui::End();
// Panels
m_sceneHierarchyPanel->on_user_interface_update();
m_properties_panel->on_user_interface_update();
m_content_browser_panel->on_user_interface_update();
UserInterface::dockspace_end();
}
} // namespace lt

View file

@ -0,0 +1,200 @@
#pragma once
#include <filesystem>
#include <memory/reference.hpp>
#include <mirror/panels/panel.hpp>
#include <renderer/texture.hpp>
namespace lt {
class Scene;
class AssetBrowserPanel: public Panel
{
public:
AssetBrowserPanel(memory::Ref<Scene> active_scene);
void on_user_interface_update();
private:
enum class AssetType
{
none = 0,
scene,
directory,
text,
image,
};
std::filesystem::path m_current_directory;
const std::filesystem::path m_assets_path;
float m_file_size = 128.0f;
float m_file_padding = 8.0f;
memory::Ref<Scene> m_active_scene;
memory::Ref<Texture> m_directory_texture;
memory::Ref<Texture> m_scene_texture;
memory::Ref<Texture> m_image_texture;
memory::Ref<Texture> m_text_texture;
};
} // namespace lt
#include <asset_manager/asset_manager.hpp>
#include <ecs/registry.hpp>
#include <ecs/serializer.hpp>
#include <imgui.h>
#include <memory/reference.hpp>
#include <mirror/panels/asset_browser.hpp>
#include <renderer/texture.hpp>
namespace lt {
AssetBrowserPanel::AssetBrowserPanel(memory::Ref<Scene> active_scene)
: m_current_directory("./data/assets")
, m_assets_path("./data/assets")
, m_active_scene(std::move(active_scene))
{
AssetManager::load_texture("_Assets_Directory", "data/engine/icons/asset/dir.asset");
AssetManager::load_texture("_Assets_Scene", "data/engine/icons/asset/scene.asset");
AssetManager::load_texture("_Assets_Image", "data/engine/icons/asset/img.asset");
AssetManager::load_texture("_Assets_Text", "data/engine/icons/asset/txt.asset");
m_directory_texture = AssetManager::get_texture("_Assets_Directory");
m_scene_texture = AssetManager::get_texture("_Assets_Scene");
m_image_texture = AssetManager::get_texture("_Assets_Image");
m_text_texture = AssetManager::get_texture("_Assets_Text");
}
void AssetBrowserPanel::on_user_interface_update()
{
ImGui::Begin("Content Browser");
// Parent directory button
if (m_current_directory != std::filesystem::path("data/assets"))
{
if (ImGui::Button(" <-- "))
{
m_current_directory = m_current_directory.parent_path();
}
}
const auto available_region = ImGui::GetContentRegionAvail();
const auto cell_size = m_file_size + m_file_padding;
const auto column_count = std::clamp(
static_cast<uint32_t>(std::floor(available_region.x / cell_size)),
1u,
64u
);
if (ImGui::BeginTable("ContentBrowser", static_cast<int>(column_count)))
{
m_directory_texture->bind(0u);
for (const auto &directory_entry : std::filesystem::directory_iterator(m_current_directory))
{
const auto &path = directory_entry.path();
auto extension = directory_entry.path().extension().string();
auto asset_type = AssetType {};
if (extension.empty())
{
asset_type = AssetType::directory;
}
else if (extension == ".txt" || extension == ".glsl")
{
asset_type = AssetType::text;
}
else if (extension == ".png")
{
asset_type = AssetType::image;
}
else if (extension == ".scene")
{
asset_type = AssetType::scene;
}
else
{
asset_type = AssetType::none;
}
// Extension not supported
if (asset_type == AssetType::none)
{
continue;
}
// Button
const auto path_str = path.string();
ImGui::TableNextColumn();
ImGui::PushID(path_str.c_str());
switch (asset_type)
{
// Directory
case AssetType::directory:
if (ImGui::ImageButton(
path_str.c_str(),
m_directory_texture->get_texture(),
ImVec2(m_file_size, m_file_size)
))
{
m_current_directory /= path.filename();
}
break;
// Scene
case AssetType::scene:
if (ImGui::ImageButton(
path_str.c_str(),
m_scene_texture->get_texture(),
ImVec2(m_file_size, m_file_size)
))
{
auto serializer = SceneSerializer { m_active_scene };
log::info("Attempting to deserialize: {}", path.string());
serializer.deserialize(path.string());
}
break;
// Image
case AssetType::image:
if (ImGui::ImageButton(
path_str.c_str(),
m_image_texture->get_texture(),
ImVec2(m_file_size, m_file_size)
))
{
}
break;
// Text
case AssetType::text:
if (ImGui::ImageButton(
path_str.c_str(),
m_text_texture->get_texture(),
ImVec2(m_file_size, m_file_size)
))
{
}
break;
default: break;
}
// Label
ImGui::TextUnformatted(std::format("{}", path.filename().string()).c_str());
ImGui::PopID();
}
ImGui::EndTable();
}
ImGui::End();
}
} // namespace lt

View file

@ -0,0 +1,13 @@
#pragma once
namespace lt {
class Panel
{
public:
Panel() = default;
virtual ~Panel() = default;
};
} // namespace lt

View file

@ -0,0 +1,345 @@
#pragma once
#include <ecs/entity.hpp>
#include <math/vec3.hpp>
#include <mirror/panels/panel.hpp>
namespace lt {
class PropertiesPanel: public Panel
{
public:
PropertiesPanel() = default;
void on_user_interface_update();
void set_entity_context(const Entity &entity);
private:
void draw_vec3_control(
const std::string &label,
math::vec3 &values,
float reset_value = 0.0f,
float column_width = 100.0f
);
template<typename ComponentType, typename UIFunction>
void draw_component(const std::string &name, Entity entity, UIFunction function);
Entity m_entity_context;
};
} // namespace lt
#include <asset_manager/asset_manager.hpp>
#include <camera/component.hpp>
#include <ecs/components.hpp>
#include <imgui.h>
#include <imgui_internal.h>
#include <math/trig.hpp>
#include <mirror/panels/properties.hpp>
namespace lt {
void PropertiesPanel::on_user_interface_update()
{
ImGui::Begin("Properties");
if (m_entity_context.is_valid())
{
if (m_entity_context.has_component<TagComponent>())
{
auto &tagComponent = m_entity_context.get_component<TagComponent>();
auto buffer = std::array<char, 256> {};
memset(buffer.data(), 0, buffer.size());
strncpy(buffer.data(), tagComponent.tag.c_str(), buffer.size());
if (ImGui::InputText("##Tag", buffer.data(), buffer.size()))
{
tagComponent.tag = buffer.data();
}
}
ImGui::SameLine();
ImGui::PushItemWidth(-1);
if (ImGui::Button("Add component"))
{
ImGui::OpenPopup("Components");
}
if (ImGui::BeginPopup("Components"))
{
if (ImGui::Selectable(
"SpriteRenderer",
false,
m_entity_context.has_component<SpriteRendererComponent>() ?
ImGuiSelectableFlags_Disabled :
ImGuiSelectableFlags {}
))
{
m_entity_context.add_component<SpriteRendererComponent>(
lt::AssetManager::get_texture("awesomeface")
);
}
if (ImGui::Selectable(
"Camera",
false,
m_entity_context.has_component<CameraComponent>() ?
ImGuiSelectableFlags_Disabled :
ImGuiSelectableFlags {}
))
{
m_entity_context.add_component<CameraComponent>();
}
ImGui::EndPopup();
}
ImGui::PopItemWidth();
draw_component<TransformComponent>(
"Transform Component",
m_entity_context,
[&](auto &transformComponent) {
draw_vec3_control("Translation", transformComponent.translation);
}
);
draw_component<SpriteRendererComponent>(
"SpriteRenderer Component",
m_entity_context,
[&](auto &spriteRendererComponent) {
ImGui::ColorEdit4("Color", &spriteRendererComponent.tint[0]);
}
);
draw_component<CameraComponent>(
"Camera Component",
m_entity_context,
[&](auto &cameraComponent) {
auto &camera = cameraComponent.camera;
auto projection_type = camera.get_projection_type();
auto projection_types_str = std::array<const char *, 2> {
"Orthographic",
"Perspective",
};
if (ImGui::BeginCombo("ProjectionType", projection_types_str[(int)projection_type]))
{
for (auto idx = 0; idx < 2; idx++)
{
const auto is_selected = static_cast<int>(projection_type) == idx;
if (ImGui::Selectable(projection_types_str[idx], is_selected))
{
projection_type = static_cast<SceneCamera::ProjectionType>(idx);
camera.set_projection_type(projection_type);
}
if (is_selected)
{
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
if (projection_type == SceneCamera::ProjectionType::Orthographic)
{
auto ortho_size = float {};
auto near_plane = float {};
auto far_plane = float {};
ortho_size = camera.get_orthographic_size();
near_plane = camera.get_orthographic_near_plane();
far_plane = camera.get_orthographic_far_plane();
if (ImGui::DragFloat("Orthographic Size", &ortho_size))
{
camera.set_orthographic_size(ortho_size);
}
if (ImGui::DragFloat("Near Plane", &near_plane))
{
camera.set_orthographic_near_plane(near_plane);
}
if (ImGui::DragFloat("Far Plane", &far_plane))
{
camera.set_orthographic_far_plane(far_plane);
}
}
else // perspective
{
auto vertical_fov = float {};
auto near_plane = float {};
auto far_plane = float {};
vertical_fov = math::degrees(camera.get_perspective_vertical_fov());
near_plane = camera.get_perspective_near_plane();
far_plane = camera.get_perspective_far_plane();
if (ImGui::DragFloat("Vertical FOV", &vertical_fov))
{
camera.set_perspective_vertical_fov(math::radians(vertical_fov));
}
if (ImGui::DragFloat("Near Plane", &near_plane))
{
camera.set_perspective_near_plane(near_plane);
}
if (ImGui::DragFloat("Far Plane", &far_plane))
{
camera.set_perspective_far_plane(far_plane);
}
}
ImGui::Separator();
}
);
}
ImGui::End();
}
void PropertiesPanel::set_entity_context(const Entity &entity)
{
m_entity_context = entity;
}
void PropertiesPanel::draw_vec3_control(
const std::string &label,
math::vec3 &values,
float reset_value,
float column_width
)
{
auto &io = ImGui::GetIO();
auto *bold_font = io.Fonts->Fonts[0];
ImGui::Columns(2);
ImGui::SetColumnWidth(0, column_width);
ImGui::TextUnformatted(label.c_str());
ImGui::NextColumn();
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2 { 0, 0 });
auto line_height = GImGui->Font->LegacySize + GImGui->Style.FramePadding.y * 2.0f;
auto button_size = ImVec2 { line_height + 3.0f, line_height };
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.1f, 0.15f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.2f, 0.2f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.8f, 0.1f, 0.15f, 1.0f));
ImGui::PushFont(bold_font);
if (ImGui::Button("X", button_size))
{
values.x = reset_value;
}
ImGui::PopFont();
ImGui::PopStyleColor(3);
ImGui::SameLine();
ImGui::DragFloat("##X", &values.x, 0.1f);
ImGui::PopItemWidth();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.7f, 0.2f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.8f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.2f, 0.7f, 0.2f, 1.0f));
ImGui::PushFont(bold_font);
if (ImGui::Button("Y", button_size))
{
values.y = reset_value;
}
ImGui::PopFont();
ImGui::PopStyleColor(3);
ImGui::SameLine();
ImGui::DragFloat("##Y", &values.y, 0.1f);
ImGui::PopItemWidth();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.1f, 0.25f, 0.8f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.2f, 0.35f, 0.9f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.25f, 0.8f, 1.0f));
ImGui::PushFont(bold_font);
if (ImGui::Button("Z", button_size))
{
values.z = reset_value;
}
ImGui::PopFont();
ImGui::PopStyleColor(3);
ImGui::SameLine();
ImGui::DragFloat("##Z", &values.z, 0.1f);
ImGui::PopItemWidth();
ImGui::PopStyleVar();
ImGui::Columns(1);
}
template<typename ComponentType, typename UIFunction>
void PropertiesPanel::draw_component(
const std::string &name,
Entity entity,
UIFunction user_interface_function
)
{
if (!entity.has_component<ComponentType>())
{
return;
}
auto &component = entity.get_component<ComponentType>();
auto available_region = ImGui::GetContentRegionAvail();
// NOLINTNEXTLINE
auto flags = ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanAvailWidth
| ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_AllowItemOverlap
| ImGuiTreeNodeFlags_FramePadding;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 4, 4 });
auto lineHeight = GImGui->Font->LegacySize + GImGui->Style.FramePadding.y * 2.0f;
ImGui::Separator();
// NOLINTNEXTLINE
if (ImGui::TreeNodeEx((void *)typeid(ComponentType).hash_code(), flags, name.c_str()))
{
ImGui::PopStyleVar();
ImGui::SameLine(available_region.x - lineHeight * .5f);
if (ImGui::Button("+", { lineHeight, lineHeight }))
{
ImGui::OpenPopup("ComponentSettings");
}
if (ImGui::BeginPopup("ComponentSettings"))
{
if (ImGui::Selectable("Remove component"))
{
entity.remove_component<ComponentType>();
}
ImGui::EndPopup();
}
user_interface_function(component);
ImGui::TreePop();
}
else
{
ImGui::PopStyleVar();
}
}
} // namespace lt

View file

@ -0,0 +1,124 @@
#pragma once
#include <ecs/entity.hpp>
#include <ecs/registry.hpp>
#include <memory/reference.hpp>
#include <mirror/panels/panel.hpp>
namespace lt {
class PropertiesPanel;
class SceneHierarchyPanel: public Panel
{
public:
SceneHierarchyPanel();
SceneHierarchyPanel(
memory::Ref<Scene> context,
memory::Ref<PropertiesPanel> properties_panel = nullptr
);
void on_user_interface_update();
void set_context(
memory::Ref<Scene> context,
memory::Ref<PropertiesPanel> properties_panel = nullptr
);
private:
void draw_node(Entity entity, const std::string &label);
memory::Ref<Scene> m_context;
memory::Ref<PropertiesPanel> m_properties_panel_context;
Entity m_selection_context;
};
} // namespace lt
#include <ecs/components.hpp>
#include <imgui.h>
#include <memory/reference.hpp>
#include <mirror/panels/properties.hpp>
#include <mirror/panels/scene_hierarchy.hpp>
namespace lt {
SceneHierarchyPanel::SceneHierarchyPanel(): m_context(nullptr), m_properties_panel_context(nullptr)
{
}
SceneHierarchyPanel::SceneHierarchyPanel(
memory::Ref<Scene> context,
memory::Ref<PropertiesPanel> properties_panel
)
: m_context(std::move(context))
, m_properties_panel_context(std::move(properties_panel))
{
}
void SceneHierarchyPanel::on_user_interface_update()
{
if (m_context)
{
ImGui::Begin("Hierarchy");
for (auto entityID : m_context->m_registry.view<TagComponent>())
{
auto entity = Entity {
static_cast<entt::entity>(entityID),
m_context.get(),
};
const auto &tag = entity.get_component<TagComponent>();
draw_node(entity, tag);
};
}
ImGui::End();
}
void SceneHierarchyPanel::set_context(
memory::Ref<Scene> context,
memory::Ref<PropertiesPanel> properties_panel
)
{
if (properties_panel)
{
m_properties_panel_context = std::move(properties_panel);
}
m_context = std::move(context);
}
void SceneHierarchyPanel::draw_node(Entity entity, const std::string &label)
{
auto flags = ImGuiTreeNodeFlags {
// NOLINTNEXTLINE
(m_selection_context == entity ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags {})
| ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanFullWidth
};
// NOLINTNEXTLINE
const auto expanded = ImGui::TreeNodeEx(
std::bit_cast<void *>(static_cast<uint64_t>(entity)),
flags,
"%s",
label.c_str()
);
if (ImGui::IsItemClicked())
{
m_selection_context = entity;
m_properties_panel_context->set_entity_context(entity);
}
if (expanded)
{
ImGui::TextUnformatted("TEST_OPENED_TREE!");
ImGui::TreePop();
}
}
} // namespace lt

View file

@ -1,6 +1,4 @@
export module mirror.system; export module mirror.system;
import preliminary;
import math.vec3; import math.vec3;
import camera.components; import camera.components;
import surface.requests; import surface.requests;
@ -24,6 +22,7 @@ import app;
import app.system; import app.system;
import ecs.entity; import ecs.entity;
import ecs.registry; import ecs.registry;
import std;
namespace lt { namespace lt {
@ -34,11 +33,11 @@ void renderer_callback(
std::any &user_data std::any &user_data
) )
{ {
ignore = message_severity; std::ignore = message_severity;
ignore = message_type; std::ignore = message_type;
ignore = user_data; std::ignore = user_data;
log::trace("< Renderer > ==> {}", std::string { data.message }); log::debug("RENDERER CALLBACK: {}", std::string { data.message });
} }
class MirrorSystem: public lt::app::ISystem class MirrorSystem: public lt::app::ISystem
@ -46,8 +45,8 @@ class MirrorSystem: public lt::app::ISystem
public: public:
MirrorSystem( MirrorSystem(
memory::Ref<ecs::Registry> registry, memory::Ref<ecs::Registry> registry,
size_t quit_action_key, std::size_t quit_action_key,
std::array<size_t, 4ul> debug_action_keys std::array<std::size_t, 4ul> debug_action_keys
) )
: m_registry(std::move(registry)) : m_registry(std::move(registry))
, m_quit_action_key(quit_action_key) , m_quit_action_key(quit_action_key)
@ -85,7 +84,7 @@ public:
for (auto &[id, camera] : for (auto &[id, camera] :
m_registry->view<lt::camera::components::PerspectiveCamera>()) m_registry->view<lt::camera::components::PerspectiveCamera>())
{ {
camera.vertical_fov += (static_cast<f32>(tick.delta_time.count()) * 40.0f); camera.vertical_fov += (static_cast<float>(tick.delta_time.count()) * 40.0f);
} }
} }
@ -129,9 +128,9 @@ public:
private: private:
memory::Ref<ecs::Registry> m_registry; memory::Ref<ecs::Registry> m_registry;
size_t m_quit_action_key; std::size_t m_quit_action_key;
std::array<size_t, 4ul> m_debug_action_keys {}; std::array<std::size_t, 4ul> m_debug_action_keys {};
app::TickResult m_last_tick_result {}; app::TickResult m_last_tick_result {};
}; };
@ -184,7 +183,7 @@ public:
} }
); );
auto debug_action_keys = std::array<size_t, 4ul> {}; auto debug_action_keys = std::array<std::size_t, 4ul> {};
debug_action_keys[0] = input.add_action( debug_action_keys[0] = input.add_action(
input::InputAction { input::InputAction {
.name = "debug_1", .name = "debug_1",

View file

View file

@ -1,85 +0,0 @@
export module preliminary.assertions;
import preliminary.build_constants;
import std;
///////////////////////////////////////
// ----------* INTERFACE *--------- //
/////////////////////////////////////
/** To be used for ensuring a condition holds true, throws otherwise. */
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()
);
};
/** To be used for costly checks that should be stripped in release builds. */
export template<typename Expression_T, typename... Args_T>
struct debug_check
{
debug_check(
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...>;
export template<typename Expression_T, typename... Args_T>
debug_check(Expression_T, std::format_string<Args_T...>, Args_T &&...)
-> debug_check<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()
) };
}
}
template<typename Expression_T, typename... Args_T>
debug_check<Expression_T, Args_T...>::debug_check(
const Expression_T &expression,
std::format_string<Args_T...> fmt,
Args_T &&...args,
const std::source_location &location
)
{
if constexpr (build_constants::build_type != build_constants::BuildType::debug)
{
return;
}
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()
) };
}
}

View file

@ -1,22 +0,0 @@
export module preliminary.fundumental_types;
import std;
export using byte = ::std::byte;
export using u8 = ::std::uint8_t;
export using u16 = ::std::uint16_t;
export using u32 = ::std::uint32_t;
export using u64 = ::std::uint64_t;
export using i8 = ::std::int8_t;
export using i16 = ::std::int16_t;
export using i32 = ::std::int32_t;
export using i64 = ::std::int64_t;
export using f32 = float;
export using f64 = double;
export using size_t = ::std::size_t;
export using ::std::ignore;

View file

@ -1,7 +0,0 @@
export module preliminary;
export import preliminary.fundumental_types;
export import preliminary.assertions;
export import preliminary.build_constants;
// std should always be available...
export import std;

View file

@ -3,22 +3,25 @@ import renderer.test_utils;
using enum ::lt::renderer::IDebugger::MessageSeverity; using enum ::lt::renderer::IDebugger::MessageSeverity;
using enum ::lt::renderer::IBuffer::Usage; using enum ::lt::renderer::IBuffer::Usage;
using ::std::this_thread::sleep_for;
// TODO(Light): finish these (and many other) tests...
Suite raii = "buffer_raii"_suite = [] { Suite raii = "buffer_raii"_suite = [] {
Case { "happy paths" } = [] { Case { "happy path won't throw" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
}; };
sleep_for(std::chrono::milliseconds { 500u }); std::this_thread::sleep_for(std::chrono::milliseconds(500));
Case { "unhappy paths" } = [] { Case { "unhappy path throws" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
}; };
sleep_for(std::chrono::milliseconds { 500u }); std::this_thread::sleep_for(std::chrono::milliseconds(500));
Case { "tapping" } = [] {
auto fixture = FixtureDeviceSwapchain {};
};
std::this_thread::sleep_for(std::chrono::milliseconds(500));
Case { "mapping" } = [] { Case { "mapping" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
}; };
sleep_for(std::chrono::milliseconds { 500u }); std::this_thread::sleep_for(std::chrono::milliseconds(500));
}; };

View file

@ -1,9 +1,18 @@
import renderer.frontend; import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
void noop_callback(
lt::renderer::IDebugger::MessageSeverity message_severity,
lt::renderer::IDebugger::MessageType message_type,
const lt::renderer::IDebugger::MessageData &data,
std::any &user_data
)
{
}
Suite raii = "debugger_raii"_suite = [] { Suite raii = "debugger_raii"_suite = [] {
Case { "happy paths" } = [] { Case { "happy path won't throw" } = [] {
ignore = lt::renderer::create_debugger( std::ignore = lt::renderer::create_debugger(
lt::renderer::Api::vulkan, lt::renderer::Api::vulkan,
lt::renderer::get_instance(lt::renderer::Api::vulkan), lt::renderer::get_instance(lt::renderer::Api::vulkan),
lt::renderer::IDebugger::CreateInfo { lt::renderer::IDebugger::CreateInfo {
@ -14,9 +23,9 @@ Suite raii = "debugger_raii"_suite = [] {
); );
}; };
Case { "unhappy paths" } = [] { Case { "unhappy path throws" } = [] {
expect_throw([] { expect_throw([] {
ignore = lt::renderer::create_debugger( std::ignore = lt::renderer::create_debugger(
lt::renderer::Api::vulkan, lt::renderer::Api::vulkan,
lt::renderer::get_instance(lt::renderer::Api::vulkan), lt::renderer::get_instance(lt::renderer::Api::vulkan),
lt::renderer::IDebugger::CreateInfo { lt::renderer::IDebugger::CreateInfo {
@ -28,7 +37,7 @@ Suite raii = "debugger_raii"_suite = [] {
}); });
expect_throw([] { expect_throw([] {
ignore = lt::renderer::create_debugger( std::ignore = lt::renderer::create_debugger(
lt::renderer::Api::vulkan, lt::renderer::Api::vulkan,
lt::renderer::get_instance(lt::renderer::Api::vulkan), lt::renderer::get_instance(lt::renderer::Api::vulkan),
lt::renderer::IDebugger::CreateInfo { lt::renderer::IDebugger::CreateInfo {
@ -40,7 +49,7 @@ Suite raii = "debugger_raii"_suite = [] {
}); });
expect_throw([] { expect_throw([] {
ignore = lt::renderer::create_debugger( std::ignore = lt::renderer::create_debugger(
lt::renderer::Api::vulkan, lt::renderer::Api::vulkan,
lt::renderer::get_instance(lt::renderer::Api::vulkan), lt::renderer::get_instance(lt::renderer::Api::vulkan),
lt::renderer::IDebugger::CreateInfo { lt::renderer::IDebugger::CreateInfo {

View file

@ -2,12 +2,12 @@ import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
Suite raii = "device_raii"_suite = [] { Suite raii = "device_raii"_suite = [] {
Case { "happy paths" } = [] { Case { "happy path won't throw" } = [] {
auto fixture = Fixture_SurfaceGpu {}; auto fixture = Fixture_SurfaceGpu {};
ignore = lt::renderer::create_device(constants::api, fixture.gpu(), fixture.surface()); std::ignore = lt::renderer::create_device(constants::api, fixture.gpu(), fixture.surface());
}; };
Case { "unhappy paths" } = [] { Case { "unhappy path throws" } = [] {
auto fixture = Fixture_SurfaceGpu {}; auto fixture = Fixture_SurfaceGpu {};
expect_throw([&] { expect_throw([&] {

View file

@ -2,9 +2,9 @@ import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
Suite raii = "pass_raii"_suite = [] { Suite raii = "pass_raii"_suite = [] {
Case { "happy paths" } = [] { Case { "happy path won't throw" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
ignore = lt::renderer::create_pass( std::ignore = lt::renderer::create_pass(
constants::api, constants::api,
fixture.device(), fixture.device(),
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" }, lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
@ -17,10 +17,10 @@ Suite raii = "pass_raii"_suite = [] {
); );
}; };
Case { "unhappy paths" } = [] { Case { "unhappy path throws" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_pass( std::ignore = lt::renderer::create_pass(
constants::api, constants::api,
nullptr, nullptr,
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" }, lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
@ -29,7 +29,7 @@ Suite raii = "pass_raii"_suite = [] {
}); });
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_pass( std::ignore = lt::renderer::create_pass(
lt::renderer::Api::none, lt::renderer::Api::none,
fixture.device(), fixture.device(),
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" }, lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
@ -38,7 +38,7 @@ Suite raii = "pass_raii"_suite = [] {
}); });
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_pass( std::ignore = lt::renderer::create_pass(
lt::renderer::Api::direct_x, lt::renderer::Api::direct_x,
fixture.device(), fixture.device(),
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" }, lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
@ -47,7 +47,7 @@ Suite raii = "pass_raii"_suite = [] {
}); });
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_pass( std::ignore = lt::renderer::create_pass(
lt::renderer::Api::metal, lt::renderer::Api::metal,
fixture.device(), fixture.device(),
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" }, lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },

View file

@ -2,7 +2,7 @@ import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
Suite raii = "renderer_raii"_suite = [] { Suite raii = "renderer_raii"_suite = [] {
Case { "happy paths" } = [] { Case { "happy path won't throw" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
ignore = lt::renderer::create_renderer( ignore = lt::renderer::create_renderer(
constants::api, constants::api,
@ -18,7 +18,7 @@ Suite raii = "renderer_raii"_suite = [] {
); );
}; };
Case { "unhappy paths" } = [] { Case { "unhappy path throws" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
expect_throw([&] { expect_throw([&] {

View file

@ -2,7 +2,7 @@ import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
Suite raii = "surface"_suite = [] { Suite raii = "surface"_suite = [] {
Case { "happy paths" } = [&] { Case { "happy path won't throw" } = [&] {
auto fixture = Fixture_SurfaceSystem {}; auto fixture = Fixture_SurfaceSystem {};
const auto surface = lt::renderer::create_surface( const auto surface = lt::renderer::create_surface(
@ -16,13 +16,13 @@ Suite raii = "surface"_suite = [] {
expect_eq(y, constants::resolution.y); expect_eq(y, constants::resolution.y);
}; };
Case { "unhappy paths" } = [&] { Case { "unhappy path throws" } = [&] {
auto registry = lt::memory::create_ref<lt::ecs::Registry>(); auto registry = lt::memory::create_ref<lt::ecs::Registry>();
auto entity = lt::ecs::Entity { registry, registry->create_entity() }; auto entity = lt::ecs::Entity { registry, registry->create_entity() };
auto system = lt::surface::System(registry); auto system = lt::surface::System(registry);
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_surface( std::ignore = lt::renderer::create_surface(
constants::api, constants::api,
lt::renderer::get_instance(constants::api), lt::renderer::get_instance(constants::api),
entity entity
@ -38,11 +38,11 @@ Suite raii = "surface"_suite = [] {
); );
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_surface(constants::api, nullptr, entity); std::ignore = lt::renderer::create_surface(constants::api, nullptr, entity);
}); });
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_surface( std::ignore = lt::renderer::create_surface(
lt::renderer::Api::none, lt::renderer::Api::none,
lt::renderer::get_instance(constants::api), lt::renderer::get_instance(constants::api),
entity entity
@ -50,7 +50,7 @@ Suite raii = "surface"_suite = [] {
}); });
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_surface( std::ignore = lt::renderer::create_surface(
lt::renderer::Api::direct_x, lt::renderer::Api::direct_x,
lt::renderer::get_instance(constants::api), lt::renderer::get_instance(constants::api),
entity entity
@ -58,7 +58,7 @@ Suite raii = "surface"_suite = [] {
}); });
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_surface( std::ignore = lt::renderer::create_surface(
lt::renderer::Api::metal, lt::renderer::Api::metal,
lt::renderer::get_instance(constants::api), lt::renderer::get_instance(constants::api),
entity entity
@ -66,7 +66,7 @@ Suite raii = "surface"_suite = [] {
}); });
// Ensure base creation info is non-throwing // Ensure base creation info is non-throwing
ignore = lt::renderer::create_surface( std::ignore = lt::renderer::create_surface(
constants::api, constants::api,
lt::renderer::get_instance(constants::api), lt::renderer::get_instance(constants::api),
entity entity

View file

@ -1,4 +1,3 @@
import preliminary;
import time; import time;
import renderer.frontend; import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
@ -18,64 +17,95 @@ struct RendererContext
}; };
Suite raii = "system_raii"_suite = [] { Suite raii = "system_raii"_suite = [] {
Case { "happy paths" } = [] { Case { "sandbox" } = [] {
auto fixture = Fixture_RendererSystem {}; auto fixture = Fixture_RendererSystem {};
expect_false(fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::error)); auto &surface_system = fixture.surface_system();
expect_false( auto &renderer_system = fixture.renderer_system();
fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::warning)
); auto timer = lt::time::Timer {};
lt::log::trace("Ticking for 3 seconds...");
while (timer.elapsed_time() < std::chrono::seconds { 3 })
{
surface_system.tick({});
renderer_system.tick({});
}
lt::log::trace("Three seconds passed, quitting...");
}; };
Case { "unhappy paths" } = [] { // Case { "happy path won't throw" } = [] {
auto fixture = Fixture_SurfaceSystem {}; // ignore = Fixture_RendererSystem {};
auto empty_entity = lt::ecs::Entity { fixture.registry(), //
fixture.registry()->create_entity() }; //
auto info = fixture.renderer_system_create_info(); // auto timer = lt::time::Timer {};
// lt::log::trace("Ticking for 3 seconds...");
expect_throw([=] mutable { // while (timer.elapsed_time() < std::chrono::seconds { 3 })
info.registry = nullptr; // {
ignore = lt::renderer::System { info }; // system.tick({});
}); // }
//
expect_throw([=] mutable { // lt::log::trace("Three seconds passed, quitting...");
info.surface_entity = lt::ecs::Entity({}, {}); // };
ignore = lt::renderer::System { info }; //
}); // Case { "happy path has no errors" } = [] {
// auto fixture = Fixture_RendererSystem {};
expect_throw([=] mutable { // expect_false(fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::error));
info.config.target_api = lt::renderer::Api::none; // expect_false(
ignore = lt::renderer::System { info }; // fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::warning)
}); // );
// };
// unsupported Apis //
expect_throw([=] mutable { // Case { "unhappy path throws" } = [] {
info.config.target_api = lt::renderer::Api::direct_x; // auto fixture = Fixture_SurfaceSystem {};
ignore = lt::renderer::System { info }; // auto empty_entity = lt::ecs::Entity { fixture.registry(),
}); // fixture.registry()->create_entity() };
// auto info = fixture.renderer_system_create_info();
expect_throw([=] mutable { //
info.config.target_api = lt::renderer::Api::metal; // expect_throw([=] mutable {
ignore = lt::renderer::System { info }; // info.registry = nullptr;
}); // ignore = lt::renderer::System { info };
// });
expect_throw([=] mutable { //
constexpr auto limit = lt::renderer::System::frames_in_flight_upper_limit; // expect_throw([=] mutable {
info.config.max_frames_in_flight = limit + 1u; // info.surface_entity = lt::ecs::Entity({}, {});
ignore = lt::renderer::System { info }; // ignore = lt::renderer::System { info };
}); // });
//
expect_throw([=] mutable { // expect_throw([=] mutable {
constexpr auto limit = lt::renderer::System::frames_in_flight_lower_limit; // info.config.target_api = lt::renderer::Api::none;
info.config.max_frames_in_flight = limit - 1u; // ignore = lt::renderer::System { info };
ignore = lt::renderer::System { info }; // });
}); //
// // unsupported Apis
expect_throw([=] mutable { // expect_throw([=] mutable {
info.debug_callback_info = lt::renderer::IDebugger::CreateInfo {}; // info.config.target_api = lt::renderer::Api::direct_x;
ignore = lt::renderer::System { info }; // ignore = lt::renderer::System { info };
}); // });
//
// Make sure the base info is not at fault for unhappiness. // expect_throw([=] mutable {
ignore = lt::renderer::System { info }; // info.config.target_api = lt::renderer::Api::metal;
}; // ignore = lt::renderer::System { info };
// });
//
// expect_throw([=] mutable {
// constexpr auto limit = lt::renderer::System::frames_in_flight_upper_limit;
// info.config.max_frames_in_flight = limit + 1u;
// ignore = lt::renderer::System { info };
// });
//
// expect_throw([=] mutable {
// constexpr auto limit = lt::renderer::System::frames_in_flight_lower_limit;
// info.config.max_frames_in_flight = limit - 1u;
// ignore = lt::renderer::System { info };
// });
//
// expect_throw([=] mutable {
// info.debug_callback_info = lt::renderer::IDebugger::CreateInfo {};
// ignore = lt::renderer::System { info };
// });
//
// // Make sure the base info is not at fault for unhappiness.
// ignore = lt::renderer::System { info };
// };
}; };

View file

@ -1,9 +1,11 @@
export module renderer.test_utils; export module renderer.test_utils;
export import test; export import logger;
export import surface.system; export import surface.system;
export import ecs.registry; export import ecs.registry;
export import renderer.factory; export import renderer.factory;
export import test.test;
export import test.expects;
export import memory.reference; export import memory.reference;
export import renderer.frontend; export import renderer.frontend;
export import renderer.system; export import renderer.system;
@ -11,21 +13,32 @@ export import math.vec2;
export import math.vec3; export import math.vec3;
export import math.vec4; export import math.vec4;
export import math.mat4; export import math.mat4;
export import std;
export using ::lt::test::Case;
export using ::lt::test::expect_eq;
export using ::lt::test::expect_false;
export using ::lt::test::expect_not_nullptr;
export using ::lt::test::expect_throw;
export using ::lt::test::operator""_suite;
export using ::lt::test::expect_true;
export using ::lt::test::Suite;
export using ::std::ignore;
export namespace constants { export namespace constants {
constexpr auto api = lt::renderer::Api::vulkan; constexpr auto api = lt::renderer::Api::vulkan;
constexpr auto resolution = lt::math::uvec2 { 800u, 600u }; constexpr auto resolution = lt::math::uvec2 { 800u, 600u };
constexpr auto frames_in_flight = u32 { 3u }; constexpr auto frames_in_flight = std::uint32_t { 3u };
} // namespace constants } // namespace constants
export void noop_callback( void noop_messenger_callback(
lt::renderer::IDebugger::MessageSeverity, lt::renderer::IDebugger::MessageSeverity severity,
lt::renderer::IDebugger::MessageType, lt::renderer::IDebugger::MessageType type,
const lt::renderer::IDebugger::MessageData &, const lt::renderer::IDebugger::MessageData &data,
std::any & std::any &user_data
) )
{ {
} }
@ -56,7 +69,7 @@ public:
.debug_callback_info = { .debug_callback_info = {
.severities = lt::renderer::IDebugger::MessageSeverity::all, .severities = lt::renderer::IDebugger::MessageSeverity::all,
.types= lt::renderer::IDebugger::MessageType::all, .types= lt::renderer::IDebugger::MessageType::all,
.callback = noop_callback, .callback = noop_messenger_callback,
.user_data = {}, .user_data = {},
} }
} ; } ;
@ -144,7 +157,7 @@ public:
} }
[[nodiscard]] auto has_any_messages_of(lt::renderer::IDebugger ::MessageSeverity severity) const [[nodiscard]] auto has_any_messages_of(lt::renderer::IDebugger ::MessageSeverity severity) const
-> u32 -> std::uint32_t
{ {
return m_user_data->m_severity_counter.contains(severity); return m_user_data->m_severity_counter.contains(severity);
} }
@ -160,8 +173,8 @@ private:
// I know this makes the tests too verbose... // I know this makes the tests too verbose...
// but makes it easier to figure out what the problem is when things fail on ci // but makes it easier to figure out what the problem is when things fail on ci
lt::log::trace("vulkan: {}", std::string { data.message }); lt::log::trace("vulkan: {}", std::string { data.message });
ignore = data; std::ignore = data;
ignore = type; std::ignore = type;
auto *fixture = std::any_cast<UserData *>(user_data); auto *fixture = std::any_cast<UserData *>(user_data);
fixture->m_has_any_messages = true; fixture->m_has_any_messages = true;
@ -170,7 +183,8 @@ private:
struct UserData struct UserData
{ {
std::unordered_map<lt::renderer::IDebugger::MessageSeverity, u32> m_severity_counter; std::unordered_map<lt::renderer::IDebugger::MessageSeverity, std::uint32_t>
m_severity_counter;
bool m_has_any_messages {}; bool m_has_any_messages {};
}; };
@ -213,7 +227,7 @@ public:
} }
[[nodiscard]] auto has_any_messages_of(lt::renderer::IDebugger ::MessageSeverity severity) const [[nodiscard]] auto has_any_messages_of(lt::renderer::IDebugger ::MessageSeverity severity) const
-> u32 -> std::uint32_t
{ {
return m_user_data->m_severity_counter.contains(severity); return m_user_data->m_severity_counter.contains(severity);
} }
@ -230,8 +244,8 @@ private:
// but makes it easier to figure out what the problem is when things fail on ci // but makes it easier to figure out what the problem is when things fail on ci
lt::log::trace("vulkan: {}", std::string { data.message }); lt::log::trace("vulkan: {}", std::string { data.message });
ignore = data; std::ignore = data;
ignore = type; std::ignore = type;
auto *fixture = std::any_cast<UserData *>(user_data); auto *fixture = std::any_cast<UserData *>(user_data);
fixture->m_has_any_messages = true; fixture->m_has_any_messages = true;
@ -240,7 +254,8 @@ private:
struct UserData struct UserData
{ {
std::unordered_map<lt::renderer::IDebugger::MessageSeverity, u32> m_severity_counter; std::unordered_map<lt::renderer::IDebugger::MessageSeverity, std::uint32_t>
m_severity_counter;
bool m_has_any_messages {}; bool m_has_any_messages {};
}; };

View file

@ -1,20 +1,19 @@
export module renderer.components; export module renderer.components;
import preliminary;
import assets.shader; import assets.shader;
import math.vec3; import math.vec3;
import memory.reference; import memory.reference;
import std;
export namespace lt::renderer::components { export namespace lt::renderer::components {
enum class VertexFormat : u8 enum class VertexFormat : std::uint8_t
{ {
r32_g32_b32_sfloat, r32_g32_b32_sfloat,
r32_g32_sfloat, r32_g32_sfloat,
}; };
enum class VertexInputRate : u8 enum class VertexInputRate : std::uint8_t
{ {
per_vertex, per_vertex,
@ -23,20 +22,20 @@ enum class VertexInputRate : u8
struct VertexInputAttributeDescriptipn struct VertexInputAttributeDescriptipn
{ {
u32 location; std::uint32_t location;
u32 binding; std::uint32_t binding;
u32 offset; std::uint32_t offset;
VertexFormat format; VertexFormat format;
}; };
struct VertexInputBindingDescription struct VertexInputBindingDescription
{ {
u32 binding; std::uint32_t binding;
u32 stride; std::uint32_t stride;
}; };
/** Requires a math::components::Transform component on the same entity to be functional. */ /** Requires a math::components::Transform component on the same entity to be functional. */

View file

@ -2,9 +2,10 @@ export module renderer.data;
import math.mat4; import math.mat4;
export namespace lt::renderer {
struct FrameConstants namespace lt::renderer {
export struct FrameConstants
{ {
math::mat4 view_projection; math::mat4 view_projection;
}; };

View file

@ -11,9 +11,9 @@ export import renderer.vk.gpu;
export import renderer.vk.debugger; export import renderer.vk.debugger;
export import renderer.vk.surface; export import renderer.vk.surface;
export import memory.scope; export import memory.scope;
export import debug.assertions;
export import ecs.entity; export import ecs.entity;
export import std;
import preliminary;
export namespace lt::renderer { export namespace lt::renderer {
@ -48,7 +48,7 @@ export namespace lt::renderer {
IGpu *gpu, IGpu *gpu,
IDevice *device, IDevice *device,
ISwapchain *swapchain, ISwapchain *swapchain,
u32 max_frames_in_flight std::uint32_t max_frames_in_flight
) -> memory::Scope<IRenderer>; ) -> memory::Scope<IRenderer>;
[[nodiscard]] auto create_buffer( [[nodiscard]] auto create_buffer(
@ -82,7 +82,7 @@ namespace lt::renderer {
const lt::ecs::Entity &surface_entity const lt::ecs::Entity &surface_entity
) -> memory::Scope<ISurface> ) -> memory::Scope<ISurface>
{ {
ensure(instance, "Failed to create renderer::ISurface: null instance"); debug::ensure(instance, "Failed to create renderer::ISurface: null instance");
switch (target_api) switch (target_api)
{ {
@ -111,8 +111,8 @@ namespace lt::renderer {
[[nodiscard]] auto create_device(Api target_api, IGpu *gpu, ISurface *surface) [[nodiscard]] auto create_device(Api target_api, IGpu *gpu, ISurface *surface)
-> memory::Scope<IDevice> -> memory::Scope<IDevice>
{ {
ensure(gpu, "Failed to create renderer::IDevice: null gpu"); debug::ensure(gpu, "Failed to create renderer::IDevice: null gpu");
ensure(surface, "Failed to create renderer::IDevice: null surface"); debug::ensure(surface, "Failed to create renderer::IDevice: null surface");
switch (target_api) switch (target_api)
{ {
@ -146,9 +146,9 @@ namespace lt::renderer {
const IBuffer::CreateInfo &info const IBuffer::CreateInfo &info
) -> memory::Scope<IBuffer> ) -> memory::Scope<IBuffer>
{ {
ensure(device, "Failed to create renderer::IBuffer: null device"); debug::ensure(device, "Failed to create renderer::IBuffer: null device");
ensure(gpu, "Failed to create renderer::IBuffer: null gpu"); debug::ensure(gpu, "Failed to create renderer::IBuffer: null gpu");
ensure(info.size > 0, "Failed to create renderer::IBuffer: null size"); debug::ensure(info.size > 0, "Failed to create renderer::IBuffer: null size");
switch (target_api) switch (target_api)
{ {
@ -169,7 +169,7 @@ namespace lt::renderer {
const lt::assets::ShaderAsset &fragment_shader const lt::assets::ShaderAsset &fragment_shader
) -> memory::Scope<IPass> ) -> memory::Scope<IPass>
{ {
ensure(device, "Failed to create renderer::IPass: null device"); debug::ensure(device, "Failed to create renderer::IPass: null device");
switch (target_api) switch (target_api)
{ {
@ -188,13 +188,13 @@ namespace lt::renderer {
IGpu *gpu, IGpu *gpu,
IDevice *device, IDevice *device,
ISwapchain *swapchain, ISwapchain *swapchain,
u32 max_frames_in_flight std::uint32_t max_frames_in_flight
) -> memory::Scope<IRenderer> ) -> memory::Scope<IRenderer>
{ {
ensure(gpu, "Failed to create renderer::IRenderer: null gpu"); debug::ensure(gpu, "Failed to create renderer::IRenderer: null gpu");
ensure(device, "Failed to create renderer::IRenderer: null device"); debug::ensure(device, "Failed to create renderer::IRenderer: null device");
ensure(swapchain, "Failed to create renderer::IRenderer: null swapchain"); debug::ensure(swapchain, "Failed to create renderer::IRenderer: null swapchain");
ensure( debug::ensure(
std::clamp( std::clamp(
max_frames_in_flight, max_frames_in_flight,
IRenderer::frames_in_flight_lower_limit, IRenderer::frames_in_flight_lower_limit,
@ -223,17 +223,17 @@ namespace lt::renderer {
[[nodiscard]] auto create_debugger(Api target_api, IInstance *instance, IDebugger::CreateInfo info) [[nodiscard]] auto create_debugger(Api target_api, IInstance *instance, IDebugger::CreateInfo info)
-> memory::Scope<IDebugger> -> memory::Scope<IDebugger>
{ {
ensure( debug::ensure(
info.severities != IDebugger::MessageSeverity::none, info.severities != IDebugger::MessageSeverity::none,
"Failed to create renderer::IDebugger: severities == none" "Failed to create renderer::IDebugger: severities == none"
); );
ensure( debug::ensure(
info.types != IDebugger::MessageType::none, info.types != IDebugger::MessageType::none,
"Failed to create renderer::IDebugger: types == none" "Failed to create renderer::IDebugger: types == none"
); );
ensure(info.callback, "Failed to create vk::Messenger: null callback"); debug::ensure(info.callback, "Failed to create vk::Messenger: null callback");
switch (target_api) switch (target_api)
{ {

View file

@ -1,6 +1,4 @@
export module renderer.frontend; export module renderer.frontend;
import preliminary;
import renderer.data; import renderer.data;
import renderer.components; import renderer.components;
import bitwise; import bitwise;
@ -9,10 +7,11 @@ import assets.shader;
import ecs.entity; import ecs.entity;
import math.vec2; import math.vec2;
import memory.scope; import memory.scope;
import std;
export namespace lt::renderer { export namespace lt::renderer {
enum class Api : u8 enum class Api : std::uint8_t
{ {
none = 0u, none = 0u,
@ -66,7 +65,7 @@ public:
class IBuffer class IBuffer
{ {
public: public:
enum class Usage : u8 enum class Usage : std::uint8_t
{ {
vertex, vertex,
@ -81,27 +80,27 @@ public:
{ {
Usage usage; Usage usage;
size_t size; std::size_t size;
std::string debug_name; std::string debug_name;
}; };
struct CopyInfo struct CopyInfo
{ {
size_t offset; std::size_t offset;
size_t size; std::size_t size;
}; };
IBuffer() = default; IBuffer() = default;
virtual ~IBuffer() = default; virtual ~IBuffer() = default;
[[nodiscard]] virtual auto map() -> std::span<byte> = 0; [[nodiscard]] virtual auto map() -> std::span<std::byte> = 0;
virtual void unmap() = 0; virtual void unmap() = 0;
[[nodiscard]] virtual auto get_size() const -> size_t = 0; [[nodiscard]] virtual auto get_size() const -> std::size_t = 0;
private: private:
}; };
@ -121,7 +120,7 @@ public:
static constexpr auto frames_in_flight_lower_limit = 1u; static constexpr auto frames_in_flight_lower_limit = 1u;
enum class Result : u8 enum class Result : std::uint8_t
{ {
success = 0, success = 0,
invalid_swapchain, invalid_swapchain,
@ -132,7 +131,7 @@ public:
virtual ~IRenderer() = default; virtual ~IRenderer() = default;
virtual auto frame(u32 frame_idx, std::function<void()> submit_scene) -> Result = 0; virtual auto frame(std::uint32_t frame_idx, std::function<void()> submit_scene) -> Result = 0;
virtual void replace_swapchain(class ISwapchain *swapchain) = 0; virtual void replace_swapchain(class ISwapchain *swapchain) = 0;
@ -147,7 +146,7 @@ public:
class IDebugger class IDebugger
{ {
public: public:
enum class MessageSeverity : u8 enum class MessageSeverity : std::uint8_t
{ {
none = 0u, none = 0u,
@ -159,7 +158,7 @@ public:
all = verbose | info | warning | error, all = verbose | info | warning | error,
}; };
enum class MessageType : u8 enum class MessageType : std::uint8_t
{ {
none = 0u, none = 0u,
general = bitwise::bit(0u), general = bitwise::bit(0u),

View file

@ -1,7 +1,6 @@
export module renderer.system; export module renderer.system;
import preliminary;
import logger; import logger;
import debug.assertions;
import math.mat4; import math.mat4;
import renderer.factory; import renderer.factory;
import app.system; import app.system;
@ -17,8 +16,9 @@ import renderer.components;
import math.components; import math.components;
import math.algebra; import math.algebra;
import math.trig; import math.trig;
import std;
export namespace lt::renderer { namespace lt::renderer {
/** The main rendering engine. /** The main rendering engine.
* *
@ -27,11 +27,9 @@ export namespace lt::renderer {
* - Connecting the context to the physical devices (select gpu, create surface, logical device) * - Connecting the context to the physical devices (select gpu, create surface, logical device)
* - Rendering the scene represented in registry via lt::renderer::components. * - Rendering the scene represented in registry via lt::renderer::components.
*/ */
class System: public app::ISystem export class System: public app::ISystem
{ {
public: public:
// TODO(Light): this is some horrible design... fix it :(
/** config.max_frames_in_flight should not be higher than this value. */ /** config.max_frames_in_flight should not be higher than this value. */
static constexpr auto frames_in_flight_upper_limit = 5u; static constexpr auto frames_in_flight_upper_limit = 5u;
@ -42,7 +40,7 @@ public:
{ {
Api target_api; Api target_api;
u32 max_frames_in_flight; std::uint32_t max_frames_in_flight;
}; };
struct CreateInfo struct CreateInfo
@ -108,9 +106,9 @@ private:
app::TickResult m_last_tick_result {}; app::TickResult m_last_tick_result {};
u32 m_frame_idx {}; std::uint32_t m_frame_idx {};
u32 m_max_frames_in_flight {}; std::uint32_t m_max_frames_in_flight {};
}; };
} // namespace lt::renderer } // namespace lt::renderer
@ -125,8 +123,8 @@ System::System(CreateInfo info)
, m_instance(get_instance(m_api)) , m_instance(get_instance(m_api))
, m_max_frames_in_flight(info.config.max_frames_in_flight) , m_max_frames_in_flight(info.config.max_frames_in_flight)
{ {
ensure(m_registry, "Failed to initialize renderer::System: null registry"); debug::ensure(m_registry, "Failed to initialize renderer::System: null registry");
ensure( debug::ensure(
std::clamp( std::clamp(
info.config.max_frames_in_flight, info.config.max_frames_in_flight,
frames_in_flight_lower_limit, frames_in_flight_lower_limit,
@ -164,7 +162,7 @@ void System::on_unregister()
void System::tick(app::TickInfo tick) void System::tick(app::TickInfo tick)
{ {
ignore = tick; std::ignore = tick;
handle_surface_resized_events(); handle_surface_resized_events();
auto frame_result = m_renderer->frame(m_frame_idx, [this] { submit_scene(); }); auto frame_result = m_renderer->frame(m_frame_idx, [this] { submit_scene(); });

View file

@ -40,10 +40,11 @@ struct wl_surface;
#endif #endif
export module renderer.vk.api_wrapper; export module renderer.vk.api_wrapper;
import preliminary;
import memory.null_on_move; import memory.null_on_move;
import math.vec3; import math.vec3;
import math.vec2; import math.vec2;
import debug.assertions;
import std;
import logger; import logger;
template<class... Ts> template<class... Ts>
@ -489,57 +490,57 @@ enum class Format : std::underlying_type_t<VkFormat>
r16_sscaled = VK_FORMAT_R16_SSCALED, r16_sscaled = VK_FORMAT_R16_SSCALED,
r16_uint = VK_FORMAT_R16_UINT, r16_uint = VK_FORMAT_R16_UINT,
r16_sint = VK_FORMAT_R16_SINT, r16_sint = VK_FORMAT_R16_SINT,
r16_sf32 = VK_FORMAT_R16_SFLOAT, r16_sfloat = VK_FORMAT_R16_SFLOAT,
r16g16_unorm = VK_FORMAT_R16G16_UNORM, r16g16_unorm = VK_FORMAT_R16G16_UNORM,
r16g16_snorm = VK_FORMAT_R16G16_SNORM, r16g16_snorm = VK_FORMAT_R16G16_SNORM,
r16g16_uscaled = VK_FORMAT_R16G16_USCALED, r16g16_uscaled = VK_FORMAT_R16G16_USCALED,
r16g16_sscaled = VK_FORMAT_R16G16_SSCALED, r16g16_sscaled = VK_FORMAT_R16G16_SSCALED,
r16g16_uint = VK_FORMAT_R16G16_UINT, r16g16_uint = VK_FORMAT_R16G16_UINT,
r16g16_sint = VK_FORMAT_R16G16_SINT, r16g16_sint = VK_FORMAT_R16G16_SINT,
r16g16_sf32 = VK_FORMAT_R16G16_SFLOAT, r16g16_sfloat = VK_FORMAT_R16G16_SFLOAT,
r16g16b16_unorm = VK_FORMAT_R16G16B16_UNORM, r16g16b16_unorm = VK_FORMAT_R16G16B16_UNORM,
r16g16b16_snorm = VK_FORMAT_R16G16B16_SNORM, r16g16b16_snorm = VK_FORMAT_R16G16B16_SNORM,
r16g16b16_uscaled = VK_FORMAT_R16G16B16_USCALED, r16g16b16_uscaled = VK_FORMAT_R16G16B16_USCALED,
r16g16b16_sscaled = VK_FORMAT_R16G16B16_SSCALED, r16g16b16_sscaled = VK_FORMAT_R16G16B16_SSCALED,
r16g16b16_uint = VK_FORMAT_R16G16B16_UINT, r16g16b16_uint = VK_FORMAT_R16G16B16_UINT,
r16g16b16_sint = VK_FORMAT_R16G16B16_SINT, r16g16b16_sint = VK_FORMAT_R16G16B16_SINT,
r16g16b16_sf32 = VK_FORMAT_R16G16B16_SFLOAT, r16g16b16_sfloat = VK_FORMAT_R16G16B16_SFLOAT,
r16g16b16a16_unorm = VK_FORMAT_R16G16B16A16_UNORM, r16g16b16a16_unorm = VK_FORMAT_R16G16B16A16_UNORM,
r16g16b16a16_snorm = VK_FORMAT_R16G16B16A16_SNORM, r16g16b16a16_snorm = VK_FORMAT_R16G16B16A16_SNORM,
r16g16b16a16_uscaled = VK_FORMAT_R16G16B16A16_USCALED, r16g16b16a16_uscaled = VK_FORMAT_R16G16B16A16_USCALED,
r16g16b16a16_sscaled = VK_FORMAT_R16G16B16A16_SSCALED, r16g16b16a16_sscaled = VK_FORMAT_R16G16B16A16_SSCALED,
r16g16b16a16_uint = VK_FORMAT_R16G16B16A16_UINT, r16g16b16a16_uint = VK_FORMAT_R16G16B16A16_UINT,
r16g16b16a16_sint = VK_FORMAT_R16G16B16A16_SINT, r16g16b16a16_sint = VK_FORMAT_R16G16B16A16_SINT,
r16g16b16a16_sf32 = VK_FORMAT_R16G16B16A16_SFLOAT, r16g16b16a16_sfloat = VK_FORMAT_R16G16B16A16_SFLOAT,
r32_uint = VK_FORMAT_R32_UINT, r32_uint = VK_FORMAT_R32_UINT,
r32_sint = VK_FORMAT_R32_SINT, r32_sint = VK_FORMAT_R32_SINT,
r32_sf32 = VK_FORMAT_R32_SFLOAT, r32_sfloat = VK_FORMAT_R32_SFLOAT,
r32g32_uint = VK_FORMAT_R32G32_UINT, r32g32_uint = VK_FORMAT_R32G32_UINT,
r32g32_sint = VK_FORMAT_R32G32_SINT, r32g32_sint = VK_FORMAT_R32G32_SINT,
r32g32_sf32 = VK_FORMAT_R32G32_SFLOAT, r32g32_sfloat = VK_FORMAT_R32G32_SFLOAT,
r32g32b32_uint = VK_FORMAT_R32G32B32_UINT, r32g32b32_uint = VK_FORMAT_R32G32B32_UINT,
r32g32b32_sint = VK_FORMAT_R32G32B32_SINT, r32g32b32_sint = VK_FORMAT_R32G32B32_SINT,
r32g32b32_sf32 = VK_FORMAT_R32G32B32_SFLOAT, r32g32b32_sfloat = VK_FORMAT_R32G32B32_SFLOAT,
r32g32b32a32_uint = VK_FORMAT_R32G32B32A32_UINT, r32g32b32a32_uint = VK_FORMAT_R32G32B32A32_UINT,
r32g32b32a32_sint = VK_FORMAT_R32G32B32A32_SINT, r32g32b32a32_sint = VK_FORMAT_R32G32B32A32_SINT,
r32g32b32a32_sf32 = VK_FORMAT_R32G32B32A32_SFLOAT, r32g32b32a32_sfloat = VK_FORMAT_R32G32B32A32_SFLOAT,
r64_uint = VK_FORMAT_R64_UINT, r64_uint = VK_FORMAT_R64_UINT,
r64_sint = VK_FORMAT_R64_SINT, r64_sint = VK_FORMAT_R64_SINT,
r64_sf32 = VK_FORMAT_R64_SFLOAT, r64_sfloat = VK_FORMAT_R64_SFLOAT,
r64g64_uint = VK_FORMAT_R64G64_UINT, r64g64_uint = VK_FORMAT_R64G64_UINT,
r64g64_sint = VK_FORMAT_R64G64_SINT, r64g64_sint = VK_FORMAT_R64G64_SINT,
r64g64_sf32 = VK_FORMAT_R64G64_SFLOAT, r64g64_sfloat = VK_FORMAT_R64G64_SFLOAT,
r64g64b64_uint = VK_FORMAT_R64G64B64_UINT, r64g64b64_uint = VK_FORMAT_R64G64B64_UINT,
r64g64b64_sint = VK_FORMAT_R64G64B64_SINT, r64g64b64_sint = VK_FORMAT_R64G64B64_SINT,
r64g64b64_sf32 = VK_FORMAT_R64G64B64_SFLOAT, r64g64b64_sfloat = VK_FORMAT_R64G64B64_SFLOAT,
r64g64b64a64_uint = VK_FORMAT_R64G64B64A64_UINT, r64g64b64a64_uint = VK_FORMAT_R64G64B64A64_UINT,
r64g64b64a64_sint = VK_FORMAT_R64G64B64A64_SINT, r64g64b64a64_sint = VK_FORMAT_R64G64B64A64_SINT,
r64g64b64a64_sf32 = VK_FORMAT_R64G64B64A64_SFLOAT, r64g64b64a64_sfloat = VK_FORMAT_R64G64B64A64_SFLOAT,
b10g11r11_ufloat_pack32 = VK_FORMAT_B10G11R11_UFLOAT_PACK32, b10g11r11_ufloat_pack32 = VK_FORMAT_B10G11R11_UFLOAT_PACK32,
e5b9g9r9_ufloat_pack32 = VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, e5b9g9r9_ufloat_pack32 = VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
d16_unorm = VK_FORMAT_D16_UNORM, d16_unorm = VK_FORMAT_D16_UNORM,
x8_d24_unorm_pack32 = VK_FORMAT_X8_D24_UNORM_PACK32, x8_d24_unorm_pack32 = VK_FORMAT_X8_D24_UNORM_PACK32,
d32_sf32 = VK_FORMAT_D32_SFLOAT, d32_sfloat = VK_FORMAT_D32_SFLOAT,
s8_uint = VK_FORMAT_S8_UINT, s8_uint = VK_FORMAT_S8_UINT,
d16_unorm_s8_uint = VK_FORMAT_D16_UNORM_S8_UINT, d16_unorm_s8_uint = VK_FORMAT_D16_UNORM_S8_UINT,
d24_unorm_s8_uint = VK_FORMAT_D24_UNORM_S8_UINT, d24_unorm_s8_uint = VK_FORMAT_D24_UNORM_S8_UINT,
@ -695,9 +696,9 @@ struct Viewport
math::vec2 extent; math::vec2 extent;
f32 min_depth {}; float min_depth {};
f32 max_depth {}; float max_depth {};
}; };
struct Rect2d struct Rect2d
@ -723,7 +724,7 @@ public:
struct Setting struct Setting
{ {
std::string name; std::string name;
std::variant<std::vector<const char *>, u32, bool> values; std::variant<std::vector<const char *>, std::uint32_t, bool> values;
}; };
std::string name; std::string name;
@ -805,9 +806,9 @@ public:
struct Capabilities struct Capabilities
{ {
u32 min_image_count; std::uint32_t min_image_count;
u32 max_image_count; std::uint32_t max_image_count;
math::uvec2 current_extent; math::uvec2 current_extent;
@ -815,7 +816,7 @@ public:
math::uvec2 max_image_extent; math::uvec2 max_image_extent;
u32 max_image_array_layers; std::uint32_t max_image_array_layers;
std::underlying_type_t<Transform> supported_transforms; std::underlying_type_t<Transform> supported_transforms;
@ -973,112 +974,112 @@ public:
struct Limits struct Limits
{ {
u32 max_image_dimension_1d; std::uint32_t max_image_dimension_1d;
u32 max_image_dimension_2d; std::uint32_t max_image_dimension_2d;
u32 max_image_dimension_3d; std::uint32_t max_image_dimension_3d;
u32 max_image_dimension_cube; std::uint32_t max_image_dimension_cube;
u32 max_image_array_layers; std::uint32_t max_image_array_layers;
u32 max_texel_buffer_elements; std::uint32_t max_texel_buffer_elements;
u32 max_uniform_buffer_range; std::uint32_t max_uniform_buffer_range;
u32 max_storage_buffer_range; std::uint32_t max_storage_buffer_range;
u32 max_push_constants_size; std::uint32_t max_push_constants_size;
u32 max_memory_allocation_count; std::uint32_t max_memory_allocation_count;
u32 max_sampler_allocation_count; std::uint32_t max_sampler_allocation_count;
size_t buffer_image_granularity; std::size_t buffer_image_granularity;
size_t sparse_address_space_size; std::size_t sparse_address_space_size;
u32 max_bound_descriptor_sets; std::uint32_t max_bound_descriptor_sets;
u32 max_per_stage_descriptor_samplers; std::uint32_t max_per_stage_descriptor_samplers;
u32 max_per_stage_descriptor_uniform_buffers; std::uint32_t max_per_stage_descriptor_uniform_buffers;
u32 max_per_stage_descriptor_storage_buffers; std::uint32_t max_per_stage_descriptor_storage_buffers;
u32 max_per_stage_descriptor_sampled_images; std::uint32_t max_per_stage_descriptor_sampled_images;
u32 max_per_stage_descriptor_storage_images; std::uint32_t max_per_stage_descriptor_storage_images;
u32 max_per_stage_descriptor_input_attachments; std::uint32_t max_per_stage_descriptor_input_attachments;
u32 max_per_stage_resources; std::uint32_t max_per_stage_resources;
u32 max_descriptor_set_samplers; std::uint32_t max_descriptor_set_samplers;
u32 max_descriptor_set_uniform_buffers; std::uint32_t max_descriptor_set_uniform_buffers;
u32 max_descriptor_set_uniform_buffers_dynamic; std::uint32_t max_descriptor_set_uniform_buffers_dynamic;
u32 max_descriptor_set_storage_buffers; std::uint32_t max_descriptor_set_storage_buffers;
u32 max_descriptor_set_storage_buffers_dynamic; std::uint32_t max_descriptor_set_storage_buffers_dynamic;
u32 max_descriptor_set_sampled_images; std::uint32_t max_descriptor_set_sampled_images;
u32 max_descriptor_set_storage_images; std::uint32_t max_descriptor_set_storage_images;
u32 max_descriptor_set_input_attachments; std::uint32_t max_descriptor_set_input_attachments;
u32 max_vertex_input_attributes; std::uint32_t max_vertex_input_attributes;
u32 max_vertex_input_bindings; std::uint32_t max_vertex_input_bindings;
u32 max_vertex_input_attribute_offset; std::uint32_t max_vertex_input_attribute_offset;
u32 max_vertex_input_binding_stride; std::uint32_t max_vertex_input_binding_stride;
u32 max_vertex_output_components; std::uint32_t max_vertex_output_components;
u32 max_tessellation_generation_level; std::uint32_t max_tessellation_generation_level;
u32 max_tessellation_patch_size; std::uint32_t max_tessellation_patch_size;
u32 max_tessellation_control_per_vertex_input_components; std::uint32_t max_tessellation_control_per_vertex_input_components;
u32 max_tessellation_control_per_vertex_output_components; std::uint32_t max_tessellation_control_per_vertex_output_components;
u32 max_tessellation_control_per_patch_output_components; std::uint32_t max_tessellation_control_per_patch_output_components;
u32 max_tessellation_control_total_output_components; std::uint32_t max_tessellation_control_total_output_components;
u32 max_tessellation_evaluation_input_components; std::uint32_t max_tessellation_evaluation_input_components;
u32 max_tessellation_evaluation_output_components; std::uint32_t max_tessellation_evaluation_output_components;
u32 max_geometry_shader_invocations; std::uint32_t max_geometry_shader_invocations;
u32 max_geometry_input_components; std::uint32_t max_geometry_input_components;
u32 max_geometry_output_components; std::uint32_t max_geometry_output_components;
u32 max_geometry_output_vertices; std::uint32_t max_geometry_output_vertices;
u32 max_geometry_total_output_components; std::uint32_t max_geometry_total_output_components;
u32 max_fragment_input_components; std::uint32_t max_fragment_input_components;
u32 max_fragment_output_attachments; std::uint32_t max_fragment_output_attachments;
u32 max_fragment_dual_src_attachments; std::uint32_t max_fragment_dual_src_attachments;
u32 max_fragment_combined_output_resources; std::uint32_t max_fragment_combined_output_resources;
u32 max_compute_shared_memory_size; std::uint32_t max_compute_shared_memory_size;
std::array<u32, 3> max_compute_work_group_count; std::array<std::uint32_t, 3> max_compute_work_group_count;
u32 max_compute_work_group_invocations; std::uint32_t max_compute_work_group_invocations;
std::array<u32, 3> max_compute_work_group_size; std::array<std::uint32_t, 3> max_compute_work_group_size;
u32 sub_pixel_precision_bits; std::uint32_t sub_pixel_precision_bits;
u32 sub_texel_precision_bits; std::uint32_t sub_texel_precision_bits;
u32 mipmap_precision_bits; std::uint32_t mipmap_precision_bits;
u32 max_draw_indexed_index_value; std::uint32_t max_draw_indexed_index_value;
u32 max_draw_indirect_count; std::uint32_t max_draw_indirect_count;
f32 max_sampler_lod_bias; float max_sampler_lod_bias;
f32 max_sampler_anisotropy; float max_sampler_anisotropy;
u32 max_viewports; std::uint32_t max_viewports;
std::array<u32, 2> max_viewport_dimensions; std::array<std::uint32_t, 2> max_viewport_dimensions;
std::array<f32, 2> viewport_bounds_range; std::array<float, 2> viewport_bounds_range;
u32 viewport_sub_pixel_bits; std::uint32_t viewport_sub_pixel_bits;
size_t min_memory_map_alignment; std::size_t min_memory_map_alignment;
VkDeviceSize min_texel_buffer_offset_alignment; VkDeviceSize min_texel_buffer_offset_alignment;
VkDeviceSize min_uniform_buffer_offset_alignment; VkDeviceSize min_uniform_buffer_offset_alignment;
VkDeviceSize min_storage_buffer_offset_alignment; VkDeviceSize min_storage_buffer_offset_alignment;
i32 min_texel_offset; std::int32_t min_texel_offset;
u32 max_texel_offset; std::uint32_t max_texel_offset;
i32 min_texel_gather_offset; std::int32_t min_texel_gather_offset;
u32 max_texel_gather_offset; std::uint32_t max_texel_gather_offset;
f32 min_interpolation_offset; float min_interpolation_offset;
f32 max_interpolation_offset; float max_interpolation_offset;
u32 sub_pixel_interpolation_offset_bits; std::uint32_t sub_pixel_interpolation_offset_bits;
u32 max_framebuffer_width; std::uint32_t max_framebuffer_width;
u32 max_framebuffer_height; std::uint32_t max_framebuffer_height;
u32 max_framebuffer_layers; std::uint32_t max_framebuffer_layers;
VkSampleCountFlags framebuffer_color_sample_counts; VkSampleCountFlags framebuffer_color_sample_counts;
VkSampleCountFlags framebuffer_depth_sample_counts; VkSampleCountFlags framebuffer_depth_sample_counts;
VkSampleCountFlags framebuffer_stencil_sample_counts; VkSampleCountFlags framebuffer_stencil_sample_counts;
VkSampleCountFlags framebuffer_no_attachments_sample_counts; VkSampleCountFlags framebuffer_no_attachments_sample_counts;
u32 max_color_attachments; std::uint32_t max_color_attachments;
VkSampleCountFlags sampled_image_color_sample_counts; VkSampleCountFlags sampled_image_color_sample_counts;
VkSampleCountFlags sampled_image_integer_sample_counts; VkSampleCountFlags sampled_image_integer_sample_counts;
VkSampleCountFlags sampled_image_depth_sample_counts; VkSampleCountFlags sampled_image_depth_sample_counts;
VkSampleCountFlags sampled_image_stencil_sample_counts; VkSampleCountFlags sampled_image_stencil_sample_counts;
VkSampleCountFlags storage_image_sample_counts; VkSampleCountFlags storage_image_sample_counts;
u32 max_sample_mask_words; std::uint32_t max_sample_mask_words;
Bool32 timestamp_compute_and_graphics; Bool32 timestamp_compute_and_graphics;
f32 timestamp_period; float timestamp_period;
u32 max_clip_distances; std::uint32_t max_clip_distances;
u32 max_cull_distances; std::uint32_t max_cull_distances;
u32 max_combined_clip_and_cull_distances; std::uint32_t max_combined_clip_and_cull_distances;
u32 discrete_queue_priorities; std::uint32_t discrete_queue_priorities;
std::array<f32, 2> point_size_range; std::array<float, 2> point_size_range;
std::array<f32, 2> line_width_range; std::array<float, 2> line_width_range;
f32 point_size_granularity; float point_size_granularity;
f32 line_width_granularity; float line_width_granularity;
Bool32 strict_lines; Bool32 strict_lines;
Bool32 standard_sample_locations; Bool32 standard_sample_locations;
size_t optimal_buffer_copy_offset_alignment; std::size_t optimal_buffer_copy_offset_alignment;
size_t optimal_buffer_copy_row_pitch_alignment; std::size_t optimal_buffer_copy_row_pitch_alignment;
size_t non_coherent_atom_size; std::size_t non_coherent_atom_size;
}; };
struct SparseProperties struct SparseProperties
@ -1092,13 +1093,13 @@ public:
struct Properties struct Properties
{ {
u32 api_version; std::uint32_t api_version;
u32 driver_version; std::uint32_t driver_version;
u32 vendor_id; std::uint32_t vendor_id;
u32 device_id; std::uint32_t device_id;
Type device_type; Type device_type;
std::array<char, constants::max_physical_device_name> device_name; std::array<char, constants::max_physical_device_name> device_name;
std::array<u8, constants::uuid_size> pipeline_cache_uuid; std::array<std::uint8_t, constants::uuid_size> pipeline_cache_uuid;
Limits limits; Limits limits;
SparseProperties sparse_properties; SparseProperties sparse_properties;
}; };
@ -1106,12 +1107,12 @@ public:
struct MemoryType struct MemoryType
{ {
MemoryPropertyFlags::T property_flags; MemoryPropertyFlags::T property_flags;
u32 heap_idx; std::uint32_t heap_idx;
}; };
struct MemoryHeap struct MemoryHeap
{ {
size_t size; std::size_t size;
MemoryHeapFlags::T flags; MemoryHeapFlags::T flags;
}; };
@ -1124,8 +1125,8 @@ public:
struct QueueFamilyProperties struct QueueFamilyProperties
{ {
QueueFlags::T queue_flags {}; QueueFlags::T queue_flags {};
u32 queue_count {}; std::uint32_t queue_count {};
u32 timestamp_valid_bits {}; std::uint32_t timestamp_valid_bits {};
math::uvec3 min_image_transfer_granularity; math::uvec3 min_image_transfer_granularity;
}; };
@ -1162,7 +1163,7 @@ public:
[[nodiscard]] auto queue_family_supports_surface( [[nodiscard]] auto queue_family_supports_surface(
const Surface &surface, const Surface &surface,
u32 queue_family_idx std::uint32_t queue_family_idx
) const -> bool; ) const -> bool;
[[nodiscard]] auto get_surface_capabilities(Surface &surface) const -> Surface::Capabilities; [[nodiscard]] auto get_surface_capabilities(Surface &surface) const -> Surface::Capabilities;
@ -1200,7 +1201,7 @@ public:
struct CreateInfo struct CreateInfo
{ {
std::set<u32> queue_indices; std::set<std::uint32_t> queue_indices;
std::vector<std::string> extensions; std::vector<std::string> extensions;
@ -1257,7 +1258,7 @@ public:
void bind_memory(VkBuffer buffer, VkDeviceMemory memory, size_t offset = 0u) const; void bind_memory(VkBuffer buffer, VkDeviceMemory memory, size_t offset = 0u) const;
[[nodiscard]] auto map_memory(VkDeviceMemory memory, size_t size, size_t offset) const [[nodiscard]] auto map_memory(VkDeviceMemory memory, size_t size, size_t offset) const
-> std::span<byte>; -> std::span<std::byte>;
void unmap_memory(VkDeviceMemory memory); void unmap_memory(VkDeviceMemory memory);
@ -1497,14 +1498,14 @@ public:
struct MemoryRequirements struct MemoryRequirements
{ {
size_t size; std::size_t size;
size_t alignment; std::size_t alignment;
u32 memory_type_bits; std::uint32_t memory_type_bits;
}; };
struct CreateInfo struct CreateInfo
{ {
size_t size; std::size_t size;
UsageFlags usage; UsageFlags usage;
@ -1639,10 +1640,10 @@ public:
struct Range struct Range
{ {
Image::AspectFlags aspect_flags; Image::AspectFlags aspect_flags;
u32 base_mip_level; std::uint32_t base_mip_level;
u32 level_count; std::uint32_t level_count;
u32 base_array_layer; std::uint32_t base_array_layer;
u32 layer_count; std::uint32_t layer_count;
}; };
static constexpr auto full_color_range = Range { static constexpr auto full_color_range = Range {
@ -1763,7 +1764,7 @@ public:
struct CreateInfo struct CreateInfo
{ {
std::vector<byte> code; std::vector<std::byte> code;
std::string_view name; std::string_view name;
}; };
@ -1873,9 +1874,9 @@ public:
Flags flags; Flags flags;
u32 idx; std::uint32_t idx;
u32 count; std::uint32_t count;
DescriptorSet::Type type; DescriptorSet::Type type;
@ -1944,14 +1945,14 @@ public:
{ {
DescriptorSet::Type type; DescriptorSet::Type type;
u32 count; std::uint32_t count;
}; };
struct CreateInfo struct CreateInfo
{ {
std::vector<Size> sizes; std::vector<Size> sizes;
u32 max_sets; std::uint32_t max_sets;
std::string_view name; std::string_view name;
}; };
@ -2008,9 +2009,9 @@ public:
struct ViewportState struct ViewportState
{ {
u32 viewport_count; std::uint32_t viewport_count;
u32 scissor_count; std::uint32_t scissor_count;
}; };
struct RasterizationState struct RasterizationState
@ -2027,13 +2028,13 @@ public:
bool depth_bias_enabled; bool depth_bias_enabled;
f32 depth_bias_constant_factor; float depth_bias_constant_factor;
f32 depth_bias_clamp; float depth_bias_clamp;
f32 depth_bias_slope_factor; float depth_bias_slope_factor;
f32 line_width; float line_width;
}; };
struct MultisamplingState struct MultisamplingState
@ -2042,7 +2043,7 @@ public:
bool sample_shading_enabled; bool sample_shading_enabled;
f32 min_sample_shading; float min_sample_shading;
bool alpha_to_coverage_enabled; bool alpha_to_coverage_enabled;
@ -2133,8 +2134,8 @@ public:
struct PushConstantRange struct PushConstantRange
{ {
ShaderStageFlags::T shader_stages; ShaderStageFlags::T shader_stages;
u32 offset; std::uint32_t offset;
u32 size; std::uint32_t size;
}; };
struct CreateInfo struct CreateInfo
@ -2188,11 +2189,11 @@ public:
Buffer *dst_buffer; Buffer *dst_buffer;
size_t src_offset; std::size_t src_offset;
size_t dst_offset; std::size_t dst_offset;
size_t size; std::size_t size;
}; };
struct PushConstantsInfo struct PushConstantsInfo
@ -2201,9 +2202,9 @@ public:
vk::ShaderStageFlags::T shader_stages; vk::ShaderStageFlags::T shader_stages;
u32 offset; std::uint32_t offset;
u32 size; std::uint32_t size;
void *data; void *data;
}; };
@ -2297,11 +2298,11 @@ public:
StoreOperation store_operation; StoreOperation store_operation;
std::array<f32, 4> color_clear_values; std::array<float, 4> color_clear_values;
f32 depth_clear_value; float depth_clear_value;
u32 stencil_clear_value; std::uint32_t stencil_clear_value;
Flags resolve_mode_flags; Flags resolve_mode_flags;
}; };
@ -2315,13 +2316,13 @@ public:
struct DrawInfo struct DrawInfo
{ {
u32 vertex_count; std::uint32_t vertex_count;
u32 instance_count; std::uint32_t instance_count;
u32 first_vertex; std::uint32_t first_vertex;
u32 first_instance; std::uint32_t first_instance;
}; };
CommandBuffer() = default; CommandBuffer() = default;
@ -2461,7 +2462,7 @@ public:
math::uvec2 extent; math::uvec2 extent;
u32 min_image_count; std::uint32_t min_image_count;
std::vector<uint32_t> queue_family_indices; std::vector<uint32_t> queue_family_indices;
@ -2490,7 +2491,8 @@ public:
[[nodiscard]] auto get_images() -> std::vector<Image>; [[nodiscard]] auto get_images() -> std::vector<Image>;
[[nodiscard]] auto acquire_image(Semaphore &semaphore, u64 timeout = 100'000'000) -> u32; [[nodiscard]] auto acquire_image(Semaphore &semaphore, std::uint64_t timeout = 100'000'000)
-> std::uint32_t;
private: private:
[[nodiscard]] auto get_vk_handle() -> VkSwapchainKHR [[nodiscard]] auto get_vk_handle() -> VkSwapchainKHR
@ -2588,9 +2590,9 @@ public:
struct AllocateInfo struct AllocateInfo
{ {
size_t size; std::size_t size;
u32 memory_type_idx; std::uint32_t memory_type_idx;
std::string_view name; std::string_view name;
}; };
@ -2607,7 +2609,7 @@ public:
auto operator=(const Memory &) -> Memory & = delete; auto operator=(const Memory &) -> Memory & = delete;
[[nodiscard]] auto map(size_t size, size_t offset) -> std::span<byte>; [[nodiscard]] auto map(std::size_t size, std::size_t offset) -> std::span<std::byte>;
void unmap(); void unmap();
@ -2753,9 +2755,11 @@ constexpr auto to_string(VkResult result) noexcept -> std::string_view
case VK_PIPELINE_BINARY_MISSING_KHR: return "VK_PIPELINE_BINARY_MISSING_KHR"; case VK_PIPELINE_BINARY_MISSING_KHR: return "VK_PIPELINE_BINARY_MISSING_KHR";
case VK_ERROR_NOT_ENOUGH_SPACE_KHR: return "VK_ERROR_NOT_ENOUGH_SPACE_KHR"; case VK_ERROR_NOT_ENOUGH_SPACE_KHR: return "VK_ERROR_NOT_ENOUGH_SPACE_KHR";
case VK_RESULT_MAX_ENUM: return "VK_RESULT_MAX_ENUM"; case VK_RESULT_MAX_ENUM: return "VK_RESULT_MAX_ENUM";
default: return "<unknown>"; default: return"<unknown>";
// clang-format on // clang-format on
} }
std::unreachable();
} }
void vkc(VkResult result) void vkc(VkResult result)
@ -2929,21 +2933,24 @@ void load_library()
{ {
library = dlopen("libvulkan.so", runtime_loader_flags); library = dlopen("libvulkan.so", runtime_loader_flags);
} }
ensure(library, "Failed to dlopen the libvulkan.so"); lt::debug::ensure(library, "Failed to dlopen the libvulkan.so");
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
api::get_instance_proc_address = reinterpret_cast<PFN_vkGetInstanceProcAddr>( api::get_instance_proc_address = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
dlsym(library, "vkGetInstanceProcAddr") dlsym(library, "vkGetInstanceProcAddr")
); );
ensure(api::get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr"); lt::debug::ensure(
api::get_instance_proc_address,
"Failed to load vulkan function: vkGetInstanceProcAddr"
);
#elif defined(LIGHT_PLATFORM_WINDOWS) #elif defined(LIGHT_PLATFORM_WINDOWS)
library = LoadLibraryA("vulkan-1.dll"); auto library = LoadLibraryA("vulkan-1.dll");
ensure(library, "Failed to LoadLibraryA the vulkan-1.dll"); lt::debug::ensure(library, "Failed to LoadLibraryA the vulkan-1.dll");
api::get_instance_proc_address = std::bit_cast<PFN_vkGetInstanceProcAddr>( api::get_instance_proc_address = std::bit_cast<PFN_vkGetInstanceProcAddr>(
GetProcAddress(std::bit_cast<HMODULE>(library), "vkGetInstanceProcAddr") GetProcAddress(library, "vkGetInstanceProcAddr")
); );
ensure( lt::debug::ensure(
api::get_instance_proc_address, api::get_instance_proc_address,
"Failed to get vkGetInstanceProcAddr function pointer from vulkan-1.dll" "Failed to get vkGetInstanceProcAddr function pointer from vulkan-1.dll"
); );
@ -2973,7 +2980,7 @@ void load_global_functions()
{ {
constexpr auto load_fn = []<typename T>(T &pfn, const char *fn_name) { constexpr auto load_fn = []<typename T>(T &pfn, const char *fn_name) {
pfn = std::bit_cast<T>(api::get_instance_proc_address(nullptr, fn_name)); pfn = std::bit_cast<T>(api::get_instance_proc_address(nullptr, fn_name));
ensure(pfn, "Failed to load vulkan global function: {}", fn_name); lt::debug::ensure(pfn, "Failed to load vulkan global function: {}", fn_name);
}; };
load_fn(api::create_instance, "vkCreateInstance"); load_fn(api::create_instance, "vkCreateInstance");
@ -2985,7 +2992,7 @@ void Instance::load_functions()
{ {
const auto load_fn = [this]<typename T>(T &pfn, const char *fn_name) { const auto load_fn = [this]<typename T>(T &pfn, const char *fn_name) {
pfn = std::bit_cast<T>(api::get_instance_proc_address(m_instance, fn_name)); pfn = std::bit_cast<T>(api::get_instance_proc_address(m_instance, fn_name));
ensure(pfn, "Failed to load vulkan instance function: {}", fn_name); lt::debug::ensure(pfn, "Failed to load vulkan instance function: {}", fn_name);
}; };
load_fn(api::destroy_instance, "vkDestroyInstance"); load_fn(api::destroy_instance, "vkDestroyInstance");
@ -3035,7 +3042,7 @@ void Device::load_functions()
{ {
const auto load_fn = [this]<typename T>(T &pfn, const char *fn_name) { const auto load_fn = [this]<typename T>(T &pfn, const char *fn_name) {
pfn = std::bit_cast<T>(api::get_device_proc_address(m_device, fn_name)); pfn = std::bit_cast<T>(api::get_device_proc_address(m_device, fn_name));
ensure(pfn, "Failed to load vulkan device function: {}", fn_name); lt::debug::ensure(pfn, "Failed to load vulkan device function: {}", fn_name);
}; };
load_fn(api::get_device_queue, "vkGetDeviceQueue"); load_fn(api::get_device_queue, "vkGetDeviceQueue");
@ -3107,13 +3114,13 @@ Instance::Instance(CreateInfo info)
{ {
const auto layer_setting_type_visitor = overloads { const auto layer_setting_type_visitor = overloads {
[](const std::vector<const char *> &) { return VK_LAYER_SETTING_TYPE_STRING_EXT; }, [](const std::vector<const char *> &) { return VK_LAYER_SETTING_TYPE_STRING_EXT; },
[](u32) { return VK_LAYER_SETTING_TYPE_UINT32_EXT; }, [](std::uint32_t) { return VK_LAYER_SETTING_TYPE_UINT32_EXT; },
[](bool) { return VK_LAYER_SETTING_TYPE_BOOL32_EXT; }, [](bool) { return VK_LAYER_SETTING_TYPE_BOOL32_EXT; },
}; };
const auto layer_setting_value_visitor = overloads { const auto layer_setting_value_visitor = overloads {
[](std::vector<const char *> values) { return std::bit_cast<void *>(values.data()); }, [](std::vector<const char *> values) { return std::bit_cast<void *>(values.data()); },
[](u32 value) { return std::bit_cast<void *>(&value); }, [](std::uint32_t value) { return std::bit_cast<void *>(&value); },
[](bool value) { return std::bit_cast<void *>(&value); }, [](bool value) { return std::bit_cast<void *>(&value); },
}; };
@ -3145,7 +3152,7 @@ Instance::Instance(CreateInfo info)
values = std::bit_cast<const void *>(&std::get<2>(setting.values)); values = std::bit_cast<const void *>(&std::get<2>(setting.values));
} }
ensure(values, "Failed to get variant from setting.values"); debug::ensure(values, "Failed to get variant from setting.values");
layer_settings.emplace_back( layer_settings.emplace_back(
VkLayerSettingEXT { VkLayerSettingEXT {
@ -3185,7 +3192,7 @@ Instance::Instance(CreateInfo info)
}; };
vkc(api::create_instance(&vk_info, nullptr, &m_instance)); vkc(api::create_instance(&vk_info, nullptr, &m_instance));
ensure(m_instance, "Failed to create vulkan instance"); debug::ensure(m_instance, "Failed to create vulkan instance");
} }
Surface::Surface(const Instance &instance, const CreateInfo &info) Surface::Surface(const Instance &instance, const CreateInfo &info)
@ -3231,7 +3238,7 @@ Surface::~Surface()
{ {
auto count = 0u; auto count = 0u;
vkc(api::enumerate_physical_devices(instance.m_instance, &count, nullptr)); vkc(api::enumerate_physical_devices(instance.m_instance, &count, nullptr));
ensure(count != 0u, "Failed to find any gpus with Vulkan support"); debug::ensure(count != 0u, "Failed to find any gpus with Vulkan support");
auto vk_gpus = std::vector<VkPhysicalDevice>(count); auto vk_gpus = std::vector<VkPhysicalDevice>(count);
vkc(api::enumerate_physical_devices(instance.m_instance, &count, vk_gpus.data())); vkc(api::enumerate_physical_devices(instance.m_instance, &count, vk_gpus.data()));
@ -3396,7 +3403,7 @@ Surface::~Surface()
std::memcpy( std::memcpy(
properties.pipeline_cache_uuid.data(), properties.pipeline_cache_uuid.data(),
static_cast<u8 *>(vk_properties.pipelineCacheUUID), static_cast<std::uint8_t *>(vk_properties.pipelineCacheUUID),
constants::uuid_size constants::uuid_size
); );
@ -3552,7 +3559,7 @@ Surface::~Surface()
[[nodiscard]] auto Gpu::get_queue_family_properties() const -> std::vector<QueueFamilyProperties> [[nodiscard]] auto Gpu::get_queue_family_properties() const -> std::vector<QueueFamilyProperties>
{ {
auto count = u32 {}; auto count = std::uint32_t {};
api::get_physical_device_queue_family_properties(m_physical_device, &count, {}); api::get_physical_device_queue_family_properties(m_physical_device, &count, {});
@ -3584,7 +3591,7 @@ Surface::~Surface()
[[nodiscard]] auto Gpu::queue_family_supports_surface( [[nodiscard]] auto Gpu::queue_family_supports_surface(
const Surface &surface, const Surface &surface,
u32 queue_family_idx std::uint32_t queue_family_idx
) const -> bool ) const -> bool
{ {
auto supported = VkBool32 { false }; auto supported = VkBool32 { false };
@ -3876,7 +3883,7 @@ void Device::reset_fence(VkFence fence) const
void Device::reset_fences(std::span<VkFence> fences) const void Device::reset_fences(std::span<VkFence> fences) const
{ {
vkc(api::reset_fences(m_device, static_cast<uint32_t>(fences.size()), fences.data())); vkc(api::reset_fences(m_device, fences.size(), fences.data()));
} }
auto Device::acquire_image(VkSwapchainKHR swapchain, VkSemaphore semaphore, uint64_t timeout) auto Device::acquire_image(VkSwapchainKHR swapchain, VkSemaphore semaphore, uint64_t timeout)
@ -3910,7 +3917,7 @@ void Device::wait_for_fences(std::span<VkFence> fences) const
{ {
vkc(api::wait_for_fences( vkc(api::wait_for_fences(
m_device, m_device,
static_cast<uint32_t>(fences.size()), fences.size(),
fences.data(), fences.data(),
true, true,
std::numeric_limits<uint64_t>::max() std::numeric_limits<uint64_t>::max()
@ -3922,7 +3929,7 @@ void Device::wait_for_fences(std::span<VkFence> fences) const
{ {
auto count = uint32_t { 0u }; auto count = uint32_t { 0u };
vkc(api::get_swapchain_images_khr(m_device, swapchain, &count, nullptr)); vkc(api::get_swapchain_images_khr(m_device, swapchain, &count, nullptr));
ensure(count != 0u, "Failed to get swapchain images"); debug::ensure(count != 0u, "Failed to get swapchain images");
auto images = std::vector<VkImage>(count); auto images = std::vector<VkImage>(count);
vkc(api::get_swapchain_images_khr(m_device, swapchain, &count, images.data())); vkc(api::get_swapchain_images_khr(m_device, swapchain, &count, images.data()));
@ -3942,11 +3949,11 @@ void Device::bind_memory(VkBuffer buffer, VkDeviceMemory memory, size_t offset /
} }
[[nodiscard]] auto Device::map_memory(VkDeviceMemory memory, size_t size, size_t offset) const [[nodiscard]] auto Device::map_memory(VkDeviceMemory memory, size_t size, size_t offset) const
-> std::span<byte> -> std::span<std::byte>
{ {
void *data = {}; void *data = {};
vkc(api::map_memory(m_device, memory, offset, size, {}, &data)); vkc(api::map_memory(m_device, memory, offset, size, {}, &data));
return { std::bit_cast<byte *>(data), size }; return { std::bit_cast<std::byte *>(data), size };
} }
void Device::unmap_memory(VkDeviceMemory memory) void Device::unmap_memory(VkDeviceMemory memory)
@ -4267,9 +4274,6 @@ void Queue::present(PresentInfo info) const
Image::Image(Device &device, CreateInfo info): m_device(device.get_vk_handle()), m_image() Image::Image(Device &device, CreateInfo info): m_device(device.get_vk_handle()), m_image()
{ {
// WIP(Light): use image create info's info
ignore = info;
auto vk_info = VkImageCreateInfo {}; auto vk_info = VkImageCreateInfo {};
vkc(api::create_image(m_device, &vk_info, nullptr, &m_image)); vkc(api::create_image(m_device, &vk_info, nullptr, &m_image));
} }
@ -4333,9 +4337,6 @@ CommandBuffer::CommandBuffer(VkCommandBuffer buffer): m_buffer(buffer)
void CommandBuffer::begin(BeginInfo info /* = {} */) void CommandBuffer::begin(BeginInfo info /* = {} */)
{ {
// WIP(Light): Use info
ignore = info;
auto vk_info = VkCommandBufferBeginInfo { auto vk_info = VkCommandBufferBeginInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = {}, .pNext = {},
@ -4442,7 +4443,7 @@ void CommandBuffer::begin_rendering(RenderingInfo info)
.extent = {info.area_extent.x, info.area_extent.y}, .extent = {info.area_extent.x, info.area_extent.y},
}, },
.layerCount = 1u, .layerCount = 1u,
.colorAttachmentCount = static_cast<u32>(vk_color_attachments.size()), .colorAttachmentCount = static_cast<std::uint32_t>(vk_color_attachments.size()),
.pColorAttachments= vk_color_attachments.data(), .pColorAttachments= vk_color_attachments.data(),
}; };
@ -4580,7 +4581,7 @@ Swapchain::Swapchain(Device &device, Surface &surface, CreateInfo info)
.imageArrayLayers = 1u, .imageArrayLayers = 1u,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = static_cast<u32>(info.queue_family_indices.size()), .queueFamilyIndexCount = static_cast<std::uint32_t>(info.queue_family_indices.size()),
.pQueueFamilyIndices = info.queue_family_indices.data(), .pQueueFamilyIndices = info.queue_family_indices.data(),
.preTransform = static_cast<VkSurfaceTransformFlagBitsKHR>(info.pre_transform), .preTransform = static_cast<VkSurfaceTransformFlagBitsKHR>(info.pre_transform),
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
@ -4622,9 +4623,10 @@ Swapchain::~Swapchain()
return images; return images;
} }
[[nodiscard]] auto Swapchain::acquire_image(Semaphore &semaphore, u64 timeout) -> u32 [[nodiscard]] auto Swapchain::acquire_image(Semaphore &semaphore, std::uint64_t timeout)
-> std::uint32_t
{ {
auto idx = u32 {}; auto idx = std::uint32_t {};
vkc(api::acquire_next_image_khr( vkc(api::acquire_next_image_khr(
m_device, m_device,
m_swapchain, m_swapchain,
@ -4698,11 +4700,11 @@ Memory::~Memory()
api::free_memory(m_device, m_memory, nullptr); api::free_memory(m_device, m_memory, nullptr);
} }
[[nodiscard]] auto Memory::map(size_t size, size_t offset) -> std::span<byte> [[nodiscard]] auto Memory::map(std::size_t size, std::size_t offset) -> std::span<std::byte>
{ {
void *data = {}; void *data = {};
vkc(api::map_memory(m_device, m_memory, offset, size, {}, &data)); vkc(api::map_memory(m_device, m_memory, offset, size, {}, &data));
return { std::bit_cast<byte *>(data), size }; return { std::bit_cast<std::byte *>(data), size };
} }
void Memory::unmap() void Memory::unmap()
@ -4717,7 +4719,7 @@ ShaderModule::ShaderModule(Device &device, CreateInfo info): m_device(device.get
.pNext = {}, .pNext = {},
.flags = {}, .flags = {},
.codeSize = info.code.size(), .codeSize = info.code.size(),
.pCode = std::bit_cast<u32 *>(info.code.data()), .pCode = std::bit_cast<std::uint32_t *>(info.code.data()),
}; };
vkc(api::create_shader_module(m_device, &vk_info, nullptr, &m_shader_module)); vkc(api::create_shader_module(m_device, &vk_info, nullptr, &m_shader_module));
@ -4762,7 +4764,7 @@ DescriptorSetLayout::DescriptorSetLayout(Device &device, CreateInfo info)
auto vk_binding_flags_info = VkDescriptorSetLayoutBindingFlagsCreateInfoEXT { auto vk_binding_flags_info = VkDescriptorSetLayoutBindingFlagsCreateInfoEXT {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.pNext = {}, .pNext = {},
.bindingCount = static_cast<u32>(vk_binding_flag_values.size()), .bindingCount = static_cast<std::uint32_t>(vk_binding_flag_values.size()),
.pBindingFlags = vk_binding_flag_values.data(), .pBindingFlags = vk_binding_flag_values.data(),
}; };
@ -4770,7 +4772,7 @@ DescriptorSetLayout::DescriptorSetLayout(Device &device, CreateInfo info)
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = &vk_binding_flags_info, .pNext = &vk_binding_flags_info,
.flags = info.flags, .flags = info.flags,
.bindingCount = static_cast<u32>(vk_bindings.size()), .bindingCount = static_cast<std::uint32_t>(vk_bindings.size()),
.pBindings = vk_bindings.data(), .pBindings = vk_bindings.data(),
}; };
vkc(api::create_descriptor_set_layout(m_device, &vk_info, nullptr, &m_descriptor_set_layout)); vkc(api::create_descriptor_set_layout(m_device, &vk_info, nullptr, &m_descriptor_set_layout));
@ -4809,7 +4811,7 @@ DescriptorPool::DescriptorPool(Device &device, CreateInfo info): m_device(device
.pNext = {}, .pNext = {},
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
.maxSets = info.max_sets, .maxSets = info.max_sets,
.poolSizeCount = static_cast<u32>(vk_sizes.size()), .poolSizeCount = static_cast<std::uint32_t>(vk_sizes.size()),
.pPoolSizes = vk_sizes.data(), .pPoolSizes = vk_sizes.data(),
}; };
@ -4935,12 +4937,11 @@ Pipeline::Pipeline(Device &device, PipelineLayout &layout, CreateInfo info)
auto color_attachment_formats = std::vector<vk::Format> {}; auto color_attachment_formats = std::vector<vk::Format> {};
// WIP(Light): use color attachments
for (auto &color_attachment : info.attachment_state.color_attachments) for (auto &color_attachment : info.attachment_state.color_attachments)
{ {
ignore = color_attachment;
} }
auto rendering_info = VkPipelineRenderingCreateInfoKHR { auto rendering_info = VkPipelineRenderingCreateInfoKHR {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = static_cast<uint32_t>(color_attachment_formats.size()), .colorAttachmentCount = static_cast<uint32_t>(color_attachment_formats.size()),
@ -5017,9 +5018,9 @@ PipelineLayout::PipelineLayout(Device &device, CreateInfo info): m_device(device
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = {}, .pNext = {},
.flags = {}, .flags = {},
.setLayoutCount = static_cast<u32>(vk_set_layouts.size()), .setLayoutCount = static_cast<std::uint32_t>(vk_set_layouts.size()),
.pSetLayouts = vk_set_layouts.data(), .pSetLayouts = vk_set_layouts.data(),
.pushConstantRangeCount = static_cast<u32>(vk_push_constant_ranges.size()), .pushConstantRangeCount = static_cast<std::uint32_t>(vk_push_constant_ranges.size()),
.pPushConstantRanges = vk_push_constant_ranges.data() .pPushConstantRanges = vk_push_constant_ranges.data()
}; };
vkc(api::create_pipeline_layout(m_device, &vk_info, nullptr, &m_pipeline_layout)); vkc(api::create_pipeline_layout(m_device, &vk_info, nullptr, &m_pipeline_layout));

View file

@ -1,14 +1,13 @@
export module renderer.vk.buffer; export module renderer.vk.buffer;
import preliminary;
import renderer.vk.device; import renderer.vk.device;
import renderer.vk.gpu; import renderer.vk.gpu;
import renderer.vk.api_wrapper; import renderer.vk.api_wrapper;
import renderer.frontend; import renderer.frontend;
import std;
export namespace lt::renderer::vkb { namespace lt::renderer::vkb {
class Buffer: public IBuffer export class Buffer: public IBuffer
{ {
public: public:
Buffer(class IDevice *device, class IGpu *gpu, const CreateInfo &info); Buffer(class IDevice *device, class IGpu *gpu, const CreateInfo &info);
@ -17,7 +16,7 @@ public:
void unmap() override; void unmap() override;
[[nodiscard]] auto get_size() const -> size_t override [[nodiscard]] auto get_size() const -> std::size_t override
{ {
return m_size; return m_size;
} }
@ -35,11 +34,12 @@ private:
[[nodiscard]] auto to_native_memory_properties(Usage usage) const -> vk::Memory::PropertyFlags; [[nodiscard]] auto to_native_memory_properties(Usage usage) const -> vk::Memory::PropertyFlags;
[[nodiscard]] auto has_correct_memory_type_bit(u32 type_bits, u32 type_idx) const -> bool; [[nodiscard]] auto has_correct_memory_type_bit(std::uint32_t type_bits, std::uint32_t type_idx)
const -> bool;
[[nodiscard]] auto has_required_memory_properties( [[nodiscard]] auto has_required_memory_properties(
u32 required_properties, std::uint32_t required_properties,
u32 property_flags std::uint32_t property_flags
) const -> bool; ) const -> bool;
Device *m_device {}; Device *m_device {};
@ -51,13 +51,14 @@ private:
vk::Memory m_memory; vk::Memory m_memory;
// TODO(Light): should this reflect the allocation size instead? // TODO(Light): should this reflect the allocation size instead?
size_t m_size {}; std::size_t m_size {};
}; };
} // namespace lt::renderer::vkb } // namespace lt::renderer::vkb
module :private; module :private;
namespace lt::renderer::vkb { using namespace ::lt::renderer;
using namespace ::lt::renderer::vkb;
Buffer::Buffer(IDevice *device, IGpu *gpu, const CreateInfo &info) Buffer::Buffer(IDevice *device, IGpu *gpu, const CreateInfo &info)
: m_device(static_cast<Device *>(device)) : m_device(static_cast<Device *>(device))
@ -144,17 +145,18 @@ void Buffer::unmap() /* override */
std::unreachable(); std::unreachable();
} }
[[nodiscard]] auto Buffer::has_correct_memory_type_bit(u32 type_bits, u32 type_idx) const -> bool [[nodiscard]] auto Buffer::has_correct_memory_type_bit(
std::uint32_t type_bits,
std::uint32_t type_idx
) const -> bool
{ {
return type_bits & (1 << type_idx); return type_bits & (1 << type_idx);
} }
[[nodiscard]] auto Buffer::has_required_memory_properties( [[nodiscard]] auto Buffer::has_required_memory_properties(
u32 required_properties, std::uint32_t required_properties,
u32 property_flags std::uint32_t property_flags
) const -> bool ) const -> bool
{ {
return (property_flags & required_properties) == required_properties; return (property_flags & required_properties) == required_properties;
} }
} // namespace lt::renderer::vkb

View file

@ -1,15 +1,15 @@
export module renderer.vk.debugger; export module renderer.vk.debugger;
import preliminary;
import renderer.vk.instance; import renderer.vk.instance;
import renderer.frontend; import renderer.frontend;
import renderer.vk.api_wrapper; import renderer.vk.api_wrapper;
import memory.null_on_move; import memory.null_on_move;
import debug.assertions;
import logger; import logger;
import std;
export namespace lt::renderer::vkb { namespace lt::renderer::vkb {
class Debugger: public IDebugger export class Debugger: public IDebugger
{ {
public: public:
Debugger(IInstance *instance, CreateInfo info); Debugger(IInstance *instance, CreateInfo info);
@ -155,7 +155,7 @@ void Debugger::native_callback(
{ {
try try
{ {
ensure(user_data, "Null vulkan_user_data received in messenger callback"); debug::ensure(user_data, "Null vulkan_user_data received in messenger callback");
auto *messenger = std::bit_cast<Debugger *>(user_data); auto *messenger = std::bit_cast<Debugger *>(user_data);
messenger->m_user_callback( messenger->m_user_callback(

View file

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

View file

@ -1,17 +1,17 @@
export module renderer.vk.device; export module renderer.vk.device;
import preliminary;
import memory.null_on_move; import memory.null_on_move;
import logger; import logger;
import debug.assertions;
import renderer.vk.instance; import renderer.vk.instance;
import renderer.frontend; import renderer.frontend;
import renderer.vk.api_wrapper; import renderer.vk.api_wrapper;
import renderer.vk.gpu; import renderer.vk.gpu;
import renderer.vk.surface; import renderer.vk.surface;
import std;
export namespace lt::renderer::vkb { namespace lt::renderer::vkb {
class Device: public IDevice export class Device: public IDevice
{ {
public: public:
Device(IGpu *gpu, ISurface *surface); Device(IGpu *gpu, ISurface *surface);
@ -21,7 +21,7 @@ public:
return m_device; return m_device;
} }
[[nodiscard]] auto get_family_indices() const -> std::vector<u32> [[nodiscard]] auto get_family_indices() const -> std::vector<std::uint32_t>
{ {
return { m_graphics_queue_family_index, m_present_queue_family_index }; return { m_graphics_queue_family_index, m_present_queue_family_index };
} }
@ -43,7 +43,7 @@ private:
void initialize_queue_indices(); void initialize_queue_indices();
[[nodiscard]] auto find_suitable_queue_family() const -> u32; [[nodiscard]] auto find_suitable_queue_family() const -> std::uint32_t;
vkb::Gpu *m_gpu {}; vkb::Gpu *m_gpu {};
@ -55,9 +55,9 @@ private:
vk::Queue m_present_queue {}; vk::Queue m_present_queue {};
u32 m_graphics_queue_family_index = vk::constants::queue_family_ignored; std::uint32_t m_graphics_queue_family_index = vk::constants::queue_family_ignored;
u32 m_present_queue_family_index = vk::constants::queue_family_ignored; std::uint32_t m_present_queue_family_index = vk::constants::queue_family_ignored;
}; };
} // namespace lt::renderer::vkb } // namespace lt::renderer::vkb
@ -70,7 +70,7 @@ Device::Device(IGpu *gpu, ISurface *surface)
: m_gpu(static_cast<Gpu *>(gpu)) : m_gpu(static_cast<Gpu *>(gpu))
, m_surface(static_cast<Surface *>(surface)) , m_surface(static_cast<Surface *>(surface))
{ {
ensure(m_surface->vk(), "Failed to initialize vk::Device: null vulkan surface"); debug::ensure(m_surface->vk(), "Failed to initialize vk::Device: null vulkan surface");
initialize_queue_indices(); initialize_queue_indices();
initialize_logical_device(); initialize_logical_device();
@ -124,7 +124,7 @@ void Device::initialize_logical_device()
void Device::initialize_queue_indices() void Device::initialize_queue_indices()
{ {
auto properties = m_gpu->vk().get_queue_family_properties(); auto properties = m_gpu->vk().get_queue_family_properties();
for (auto idx = u32 { 0u }; const auto &property : properties) for (auto idx = std::uint32_t { 0u }; const auto &property : properties)
{ {
if (property.queue_flags & vk::QueueFlags::graphics_bit) if (property.queue_flags & vk::QueueFlags::graphics_bit)
{ {
@ -145,12 +145,12 @@ void Device::initialize_queue_indices()
++idx; ++idx;
} }
ensure( debug::ensure(
m_graphics_queue_family_index != vk::constants::queue_family_ignored, m_graphics_queue_family_index != vk::constants::queue_family_ignored,
"Failed to find graphics queue family" "Failed to find graphics queue family"
); );
ensure( debug::ensure(
m_present_queue_family_index != vk::constants::queue_family_ignored, m_present_queue_family_index != vk::constants::queue_family_ignored,
"Failed to find presentation queue family" "Failed to find presentation queue family"
); );

View file

@ -1,19 +1,52 @@
export module renderer.vk.gpu; export module renderer.vk.gpu;
import preliminary;
import renderer.vk.api_wrapper; import renderer.vk.api_wrapper;
import logger; import logger;
import debug.assertions;
import renderer.frontend; import renderer.frontend;
import renderer.vk.instance; import renderer.vk.instance;
import memory.null_on_move; import memory.null_on_move;
import std;
export namespace lt::renderer::vkb { namespace lt::renderer::vkb {
class Gpu: public IGpu export class Gpu: public IGpu
{ {
public: public:
Gpu(IInstance *instance); Gpu(IInstance *instance);
// [[nodiscard]] auto queue_family_supports_presentation(
// VkSurfaceKHR surface,
// uint32_t queue_family_idx
// ) -> bool;
//
// [[nodiscard]] auto get_surface_capabilities(VkSurfaceKHR surface) const
// -> VkSurfaceCapabilitiesKHR;
//
// [[nodiscard]] auto get_surface_formats(VkSurfaceKHR surface) const
// -> std::vector<VkSurfaceFormatKHR>;
//
// [[nodiscard]] auto get_properties() const -> VkPhysicalDeviceProperties
// {
// return m_properties;
// }
//
// [[nodiscard]] auto get_descriptor_indexing_features() const
// -> VkPhysicalDeviceDescriptorIndexingFeatures
// {
// return m_descriptor_indexing_features;
// }
//
// [[nodiscard]] auto get_memory_properties() const -> VkPhysicalDeviceMemoryProperties
// {
// return m_memory_properties;
// }
//
// [[nodiscard]] auto get_queue_family_properties() const ->
// std::vector<VkQueueFamilyProperties>
// {
// return m_queue_family_properties;
// }
[[nodiscard]] auto vk() -> vk::Gpu &; [[nodiscard]] auto vk() -> vk::Gpu &;
private: private:
@ -21,6 +54,8 @@ private:
vk::Gpu::Properties m_properties {}; vk::Gpu::Properties m_properties {};
// VkPhysicalDeviceDescriptorIndexingFeatures m_descriptor_indexing_features;
vk::Gpu::MemoryProperties m_memory_properties {}; vk::Gpu::MemoryProperties m_memory_properties {};
std::vector<vk::Gpu::QueueFamilyProperties> m_queue_family_properties; std::vector<vk::Gpu::QueueFamilyProperties> m_queue_family_properties;
@ -65,7 +100,7 @@ Gpu::Gpu(IInstance *instance)
} }
// No suitable GPU is fonud... // No suitable GPU is fonud...
ensure(m_gpu, "Failed to find any suitable Vulkan physical device"); debug::ensure(m_gpu, "Failed to find any suitable Vulkan physical device");
m_memory_properties = m_gpu.get_memory_properties(); m_memory_properties = m_gpu.get_memory_properties();
m_queue_family_properties = m_gpu.get_queue_family_properties(); m_queue_family_properties = m_gpu.get_queue_family_properties();
@ -76,4 +111,42 @@ Gpu::Gpu(IInstance *instance)
return m_gpu; return m_gpu;
} }
// [[nodiscard]] auto Gpu::queue_family_supports_presentation(
// VkSurfaceKHR surface,
// uint32_t queue_family_idx
// ) -> bool
// {
// auto supported = VkBool32 { false };
// vkc(vk_get_physical_device_surface_support(m_gpu, queue_family_idx, surface, &supported));
//
// return supported;
// }
//
// [[nodiscard]] auto Gpu::get_surface_capabilities(VkSurfaceKHR surface) const
// -> VkSurfaceCapabilitiesKHR
// {
// auto capabilities = VkSurfaceCapabilitiesKHR {};
// vkc(vk_get_physical_device_surface_capabilities(m_gpu, surface, &capabilities));
// return capabilities;
// }
//
// [[nodiscard]] auto Gpu::get_surface_formats(VkSurfaceKHR surface) const
// -> std::vector<VkSurfaceFormatKHR>
// {
// auto count = uint32_t { 0u };
// vkc(vk_get_physical_device_surface_formats(m_gpu, surface, &count, nullptr));
//
// auto formats = std::vector<VkSurfaceFormatKHR>(count);
// vkc(vk_get_physical_device_surface_formats(m_gpu, surface, &count, formats.data()));
//
// return formats;
// }
// [[nodiscard]] auto Gpu::create_device(VkDeviceCreateInfo info) const -> VkDevice
// {
// auto *device = VkDevice {};
// vkc(vk_create_device(m_gpu, &info, nullptr, &device));
// return device;
// }
} // namespace lt::renderer::vkb } // namespace lt::renderer::vkb

View file

@ -1,10 +1,10 @@
export module renderer.vk.instance; export module renderer.vk.instance;
import debug.assertions;
import preliminary;
import renderer.frontend; import renderer.frontend;
import renderer.vk.api_wrapper; import renderer.vk.api_wrapper;
import std;
export namespace lt::renderer::vkb { namespace lt::renderer::vkb {
/** /**
* Responsible for dynamically loading Vulkan library/functions. * Responsible for dynamically loading Vulkan library/functions.
@ -15,7 +15,7 @@ export namespace lt::renderer::vkb {
* https://www.xfree86.org/4.7.0/DRI11.html * https://www.xfree86.org/4.7.0/DRI11.html
* https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/1894 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/1894
*/ */
class Instance: public IInstance export class Instance: public IInstance
{ {
public: public:
static auto get() -> IInstance * static auto get() -> IInstance *
@ -28,6 +28,20 @@ public:
return m_instance; return m_instance;
} }
// /* create functions */
// [[nodiscard]] auto create_xlib_surface(VkXlibSurfaceCreateInfoKHR info) const ->
// VkSurfaceKHR;
//
// [[nodiscard]] auto create_messenger(VkDebugUtilsMessengerCreateInfoEXT info) const
// -> VkDebugUtilsMessengerEXT;
//
// /* destroy functions */
// void destroy_surface(VkSurfaceKHR surface) const;
//
// void destroy_messenger(VkDebugUtilsMessengerEXT messenger) const;
//
// [[nodiscard]] auto enumerate_gpus() const -> std::vector<VkPhysicalDevice>;
private: private:
static auto instance() -> IInstance & static auto instance() -> IInstance &
{ {
@ -67,7 +81,7 @@ Instance::Instance()
Setting { .name = "enable_message_limit", .values = true }, Setting { .name = "enable_message_limit", .values = true },
Setting { Setting {
.name = "duplicate_message_limit", .name = "duplicate_message_limit",
.values = std::numeric_limits<u32>::max(), .values = std::numeric_limits<std::uint32_t>::max(),
}, },
Setting { Setting {
.name = "report_flags", .name = "report_flags",
@ -75,6 +89,7 @@ Instance::Instance()
}, },
}; };
using Layer = vk::Instance::Layer; using Layer = vk::Instance::Layer;
m_instance = vk::Instance( m_instance = vk::Instance(
vk::Instance::CreateInfo { vk::Instance::CreateInfo {
@ -95,4 +110,41 @@ Instance::Instance()
m_instance.load_functions(); m_instance.load_functions();
} }
// auto Instance::enumerate_gpus() const -> std::vector<VkPhysicalDevice>
// {
// auto count = 0u;
// vkc(vk_enumerate_physical_devices(m_instance, &count, nullptr));
// debug::ensure(count != 0u, "Failed to find any gpus with Vulkan support");
//
// auto gpus = std::vector<VkPhysicalDevice>(count);
// vkc(vk_enumerate_physical_devices(m_instance, &count, gpus.data()));
// return gpus;
// }
//
// auto Instance::create_xlib_surface(VkXlibSurfaceCreateInfoKHR info) const -> VkSurfaceKHR
// {
// auto *value = VkSurfaceKHR {};
// vk_create_xlib_surface_khr(m_instance, &info, m_allocator, &value);
//
// return value;
// }
//
// [[nodiscard]] auto Instance::create_messenger(VkDebugUtilsMessengerCreateInfoEXT info) const
// -> VkDebugUtilsMessengerEXT
// {
// auto *messenger = VkDebugUtilsMessengerEXT {};
// vkc(vk_create_debug_messenger(m_instance, &info, m_allocator, &messenger));
// return messenger;
// }
//
// void Instance::destroy_surface(VkSurfaceKHR surface) const
// {
// vk_destroy_surface_khr(m_instance, surface, m_allocator);
// }
//
// void Instance::destroy_messenger(VkDebugUtilsMessengerEXT messenger) const
// {
// vk_destroy_debug_messenger(m_instance, messenger, m_allocator);
// }
} // namespace lt::renderer::vkb } // namespace lt::renderer::vkb

View file

@ -1,6 +1,4 @@
export module renderer.vk.pass; export module renderer.vk.pass;
import preliminary;
import renderer.data; import renderer.data;
import renderer.vk.api_wrapper; import renderer.vk.api_wrapper;
import renderer.vk.device; import renderer.vk.device;
@ -9,10 +7,11 @@ import assets.shader;
import assets.metadata; import assets.metadata;
import memory.null_on_move; import memory.null_on_move;
import renderer.frontend; import renderer.frontend;
import std;
export namespace lt::renderer::vkb { namespace lt::renderer::vkb {
class Pass: public IPass export class Pass: public IPass
{ {
public: public:
Pass( Pass(
@ -52,8 +51,10 @@ private:
} // namespace lt::renderer::vkb } // namespace lt::renderer::vkb
module :private; module :private;
namespace lt::renderer::vkb { using namespace ::lt::renderer::vkb;
using namespace ::lt::renderer;
using enum vk::DescriptorSetLayout::Binding::FlagBits; using enum vk::DescriptorSetLayout::Binding::FlagBits;
@ -79,6 +80,36 @@ Pass::Pass(
.push_constant_ranges = { { vk::ShaderStageFlags::vertex_bit, 0u, sizeof(FrameConstants) } } .push_constant_ranges = { { vk::ShaderStageFlags::vertex_bit, 0u, sizeof(FrameConstants) } }
})) }))
{ {
// 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,
// }
// );
auto shaders = std::vector<std::pair<vk::ShaderModule, vk::ShaderStageFlags::T>> {}; auto shaders = std::vector<std::pair<vk::ShaderModule, vk::ShaderStageFlags::T>> {};
shaders.emplace_back( shaders.emplace_back(
vk::ShaderModule( vk::ShaderModule(
@ -121,5 +152,3 @@ Pass::Pass(
} }
); );
} }
} // namespace lt::renderer::vkb

View file

@ -1,8 +1,7 @@
export module renderer.vk.renderer; export module renderer.vk.renderer;
import preliminary;
import logger; import logger;
import assets.shader; import assets.shader;
import debug.assertions;
import renderer.vk.api_wrapper; import renderer.vk.api_wrapper;
import memory.reference; import memory.reference;
import memory.null_on_move; import memory.null_on_move;
@ -15,17 +14,19 @@ import renderer.vk.buffer;
import renderer.vk.pass; import renderer.vk.pass;
import renderer.data; import renderer.data;
import renderer.frontend; import renderer.frontend;
import std;
export namespace lt::renderer::vkb { namespace lt::renderer::vkb {
class Renderer: public IRenderer // NOLINTNEXTLINE
export class Renderer: public IRenderer
{ {
public: public:
Renderer( Renderer(
class IGpu *gpu, class IGpu *gpu,
class IDevice *device, class IDevice *device,
class ISwapchain *swapchain, class ISwapchain *swapchain,
u32 max_frames_in_flight std::uint32_t max_frames_in_flight
); );
~Renderer() override ~Renderer() override
@ -34,14 +35,14 @@ public:
{ {
m_device->vk().wait_idle(); m_device->vk().wait_idle();
} }
catch (const std::exception &exp) catch (std::exception &exp)
{ {
log::error("Failed to wait idle on device in renderer destructor:"); log::error("Failed to wait idle on device in renderer destructor");
log::error("\twhat: {}", exp.what());
} }
} }
[[nodiscard]] auto frame(u32 frame_idx, std::function<void()> submit_scene) -> Result override; [[nodiscard]] auto frame(std::uint32_t frame_idx, std::function<void()> submit_scene)
-> Result override;
void replace_swapchain(ISwapchain *swapchain) override; void replace_swapchain(ISwapchain *swapchain) override;
@ -56,11 +57,11 @@ public:
) override; ) override;
private: private:
void record_cmd(vk::CommandBuffer &cmd, u32 image_idx); void record_cmd(vk::CommandBuffer &cmd, std::uint32_t image_idx);
void map_buffers(u32 frame_idx); void map_buffers(std::uint32_t frame_idx);
u32 m_max_frames_in_flight {}; std::uint32_t m_max_frames_in_flight {};
Device *m_device {}; Device *m_device {};
@ -86,13 +87,13 @@ private:
Buffer m_staging_buffer; Buffer m_staging_buffer;
size_t m_staging_offset; std::size_t m_staging_offset;
std::span<byte> m_staging_map; std::span<std::byte> m_staging_map;
std::span<components::Sprite::Vertex> m_sprite_vertex_map; std::span<components::Sprite::Vertex> m_sprite_vertex_map;
size_t m_current_sprite_idx; std::size_t m_current_sprite_idx;
vk::DescriptorPool m_global_set_pool; vk::DescriptorPool m_global_set_pool;
@ -104,7 +105,12 @@ private:
module :private; module :private;
namespace lt::renderer::vkb { namespace lt::renderer::vkb {
Renderer::Renderer(IGpu *gpu, IDevice *device, ISwapchain *swapchain, u32 max_frames_in_flight) Renderer::Renderer(
IGpu *gpu,
IDevice *device,
ISwapchain *swapchain,
std::uint32_t max_frames_in_flight
)
: m_device(static_cast<Device *>(device)) : m_device(static_cast<Device *>(device))
, m_swapchain(static_cast<Swapchain *>(swapchain)) , m_swapchain(static_cast<Swapchain *>(swapchain))
, m_resolution(m_swapchain->get_resolution()) , m_resolution(m_swapchain->get_resolution())
@ -168,9 +174,10 @@ Renderer::Renderer(IGpu *gpu, IDevice *device, ISwapchain *swapchain, u32 max_fr
} }
}; };
[[nodiscard]] auto Renderer::frame(u32 frame_idx, std::function<void()> submit_scene) -> Result [[nodiscard]] auto Renderer::frame(std::uint32_t frame_idx, std::function<void()> submit_scene)
-> Result
{ {
ensure( debug::ensure(
frame_idx < m_max_frames_in_flight, frame_idx < m_max_frames_in_flight,
"Failed to draw: frame_idx >= max_frames_in_flight ({} >= {})", "Failed to draw: frame_idx >= max_frames_in_flight ({} >= {})",
frame_idx, frame_idx,
@ -186,9 +193,6 @@ Renderer::Renderer(IGpu *gpu, IDevice *device, ISwapchain *swapchain, u32 max_fr
frame_fence.reset(); frame_fence.reset();
map_buffers(frame_idx); map_buffers(frame_idx);
// WIP(Light): submit the scene!
ignore = submit_scene;
// submit_scene(); // submit_scene();
record_cmd(cmd, image_idx); record_cmd(cmd, image_idx);
@ -221,7 +225,7 @@ void Renderer::replace_swapchain(ISwapchain *swapchain)
m_resolution = m_swapchain->get_resolution(); m_resolution = m_swapchain->get_resolution();
} }
void Renderer::map_buffers(u32 frame_idx) void Renderer::map_buffers(std::uint32_t frame_idx)
{ {
using components::Sprite; using components::Sprite;
@ -238,7 +242,7 @@ void Renderer::map_buffers(u32 frame_idx)
); );
} }
void Renderer::record_cmd(vk::CommandBuffer &cmd, u32 image_idx) void Renderer::record_cmd(vk::CommandBuffer &cmd, std::uint32_t image_idx)
{ {
m_staging_map = {}; m_staging_map = {};
m_sprite_vertex_map = {}; m_sprite_vertex_map = {};
@ -310,7 +314,7 @@ void Renderer::record_cmd(vk::CommandBuffer &cmd, u32 image_idx)
// cmd.set_viewport( // cmd.set_viewport(
// { // {
// .origin = {}, // .origin = {},
// .extent = { static_cast<f32>(m_resolution.x), static_cast<f32>(m_resolution.y) }, // .extent = { static_cast<float>(m_resolution.x), static_cast<float>(m_resolution.y) },
// .min_depth = 0.0f, // .min_depth = 0.0f,
// .max_depth = 1.0f, // .max_depth = 1.0f,
// } // }
@ -318,7 +322,7 @@ void Renderer::record_cmd(vk::CommandBuffer &cmd, u32 image_idx)
// cmd.set_scissor({ .offset = {}, .extent = m_resolution }); // cmd.set_scissor({ .offset = {}, .extent = m_resolution });
// cmd.draw( // cmd.draw(
// { // {
// .vertex_count = static_cast<u32>(m_current_sprite_idx), // .vertex_count = static_cast<std::uint32_t>(m_current_sprite_idx),
// .instance_count = 1u, // .instance_count = 1u,
// .first_vertex = 0u, // .first_vertex = 0u,
// .first_instance = 0u, // .first_instance = 0u,

View file

@ -1,6 +1,5 @@
export module renderer.vk.surface; export module renderer.vk.surface;
import debug.assertions;
import preliminary;
import ecs.entity; import ecs.entity;
import ecs.registry; import ecs.registry;
import memory.null_on_move; import memory.null_on_move;
@ -10,9 +9,9 @@ import renderer.frontend;
import renderer.vk.instance; import renderer.vk.instance;
import renderer.vk.api_wrapper; import renderer.vk.api_wrapper;
export namespace lt::renderer::vkb { namespace lt::renderer::vkb {
class Surface: public ISurface export class Surface: public ISurface
{ {
public: public:
Surface(IInstance *instance, const ecs::Entity &surface_entity); Surface(IInstance *instance, const ecs::Entity &surface_entity);
@ -42,11 +41,11 @@ Surface::Surface(IInstance *instance, const ecs::Entity &surface_entity)
#if defined(LIGHT_PLATFORM_LINUX) #if defined(LIGHT_PLATFORM_LINUX)
ensure( debug::ensure(
component.get_native_data().display, component.get_native_data().display,
"Failed to initialize vk::Surface: null Wayland display" "Failed to initialize vk::Surface: null Wayland display"
); );
ensure( debug::ensure(
component.get_native_data().surface, component.get_native_data().surface,
"Failed to initialize vk::Surface: null Wayland surface" "Failed to initialize vk::Surface: null Wayland surface"
); );
@ -60,7 +59,7 @@ Surface::Surface(IInstance *instance, const ecs::Entity &surface_entity)
); );
#elif defined(LIGHT_PLATFORM_WINDOWS) #elif defined(LIGHT_PLATFORM_WINDOWS)
ensure( debug::ensure(
component.get_native_data().window, component.get_native_data().window,
"Failed to initialize vk::Surface: null win32 window handle" "Failed to initialize vk::Surface: null win32 window handle"
); );

View file

@ -1,6 +1,4 @@
export module renderer.vk.swapchain; export module renderer.vk.swapchain;
import preliminary;
import renderer.vk.api_wrapper; import renderer.vk.api_wrapper;
import renderer.vk.surface; import renderer.vk.surface;
import renderer.vk.device; import renderer.vk.device;
@ -10,10 +8,11 @@ import renderer.frontend;
import math.vec2; import math.vec2;
import memory.null_on_move; import memory.null_on_move;
import logger; import logger;
import std;
export namespace lt::renderer::vkb { namespace lt::renderer::vkb {
class Swapchain: public ISwapchain export class Swapchain: public ISwapchain
{ {
public: public:
Swapchain(ISurface *surface, IGpu *gpu, IDevice *device); Swapchain(ISurface *surface, IGpu *gpu, IDevice *device);
@ -33,17 +32,17 @@ public:
return m_format; return m_format;
} }
[[nodiscard]] auto get_image_count() const -> size_t [[nodiscard]] auto get_image_count() const -> std::size_t
{ {
return m_images.size(); return m_images.size();
} }
[[nodiscard]] auto get_image_view(u32 idx) -> vk::ImageView & [[nodiscard]] auto get_image_view(std::uint32_t idx) -> vk::ImageView &
{ {
return m_image_views[idx]; return m_image_views[idx];
} }
[[nodiscard]] auto get_image(u32 idx) -> vk::Image & [[nodiscard]] auto get_image(std::uint32_t idx) -> vk::Image &
{ {
return m_images[idx]; return m_images[idx];
} }
@ -51,8 +50,8 @@ public:
private: private:
[[nodiscard]] auto get_optimal_image_count( [[nodiscard]] auto get_optimal_image_count(
vk::Surface::Capabilities capabilities, vk::Surface::Capabilities capabilities,
u32 desired_image_count std::uint32_t desired_image_count
) const -> u32; ) const -> std::uint32_t;
Gpu *m_gpu; Gpu *m_gpu;
@ -73,6 +72,7 @@ private:
} // namespace lt::renderer::vkb } // namespace lt::renderer::vkb
module :private; module :private;
namespace lt::renderer::vkb { namespace lt::renderer::vkb {
@ -81,15 +81,17 @@ Swapchain::Swapchain(ISurface *surface, IGpu *gpu, IDevice *device)
, m_gpu(static_cast<Gpu *>(gpu)) , m_gpu(static_cast<Gpu *>(gpu))
, m_device(static_cast<Device *>(device)) , m_device(static_cast<Device *>(device))
{ {
static auto idx = 0u;
auto capabilities = m_gpu->vk().get_surface_capabilities(m_surface->vk()); auto capabilities = m_gpu->vk().get_surface_capabilities(m_surface->vk());
const auto formats = m_gpu->vk().get_surface_formats(m_surface->vk()); const auto formats = m_gpu->vk().get_surface_formats(m_surface->vk());
// TODO(Light): parameterize // TODO(Light): parameterize
constexpr auto desired_image_count = u32 { 3u }; constexpr auto desired_image_count = std::uint32_t { 3u };
const auto surface_format = formats.front(); const auto surface_format = formats.front();
m_format = surface_format.format; m_format = surface_format.format;
if (capabilities.current_extent.x == std::numeric_limits<u32>::max()) if (capabilities.current_extent.x == std::numeric_limits<std::uint32_t>::max())
{ {
log::info( log::info(
"Vulkan surface capabilities current extent is uint32 max... This indicates that the " "Vulkan surface capabilities current extent is uint32 max... This indicates that the "
@ -102,7 +104,6 @@ Swapchain::Swapchain(ISurface *surface, IGpu *gpu, IDevice *device)
capabilities.current_extent.y = 600u; capabilities.current_extent.y = 600u;
} }
static auto swapchain_idx = 0u;
m_swapchain = vk::Swapchain( m_swapchain = vk::Swapchain(
m_device->vk(), m_device->vk(),
m_surface->vk(), m_surface->vk(),
@ -115,7 +116,7 @@ Swapchain::Swapchain(ISurface *surface, IGpu *gpu, IDevice *device)
.queue_family_indices = m_device->get_family_indices(), .queue_family_indices = m_device->get_family_indices(),
.present_mode = vk::Swapchain::PresentMode::mailbox, .present_mode = vk::Swapchain::PresentMode::mailbox,
.pre_transform = capabilities.current_transform, .pre_transform = capabilities.current_transform,
.name = std::format("swapchain {}", swapchain_idx++), .name = std::format("swapchain {}", idx++),
} }
); );
m_resolution = capabilities.current_extent; m_resolution = capabilities.current_extent;
@ -155,8 +156,8 @@ Swapchain::Swapchain(ISurface *surface, IGpu *gpu, IDevice *device)
[[nodiscard]] auto Swapchain::get_optimal_image_count( [[nodiscard]] auto Swapchain::get_optimal_image_count(
vk::Surface::Capabilities capabilities, vk::Surface::Capabilities capabilities,
u32 desired_image_count std::uint32_t desired_image_count
) const -> u32 ) const -> std::uint32_t
{ {
const auto min_image_count = capabilities.min_image_count; const auto min_image_count = capabilities.min_image_count;
const auto max_image_count = capabilities.max_image_count; const auto max_image_count = capabilities.max_image_count;

View file

@ -1,3 +1,3 @@
add_executable(sandbox sandbox.cpp) add_executable(sandbox sandbox.cpp)
target_link_libraries(sandbox PRIVATE preliminary logger bitwise memory time test math assets app ecs surface renderer input mirror) target_link_libraries(sandbox PRIVATE logger bitwise env memory time test lt_debug math assets app ecs surface renderer input mirror)

View file

@ -1,4 +1,4 @@
import preliminary; import test.test;
import time; import time;
import test.expects; import test.expects;
import surface.system; import surface.system;
@ -10,6 +10,7 @@ import memory.reference;
import logger; import logger;
import math.vec2; import math.vec2;
import app.system; import app.system;
import std;
constexpr auto title = "TestWindow"; constexpr auto title = "TestWindow";
constexpr auto width = 800u; constexpr auto width = 800u;
@ -17,8 +18,7 @@ constexpr auto height = 600u;
constexpr auto vsync = true; constexpr auto vsync = true;
constexpr auto visible = false; constexpr auto visible = false;
auto main() -> i32 int main()
try
{ {
auto registry = lt::memory::create_ref<lt::ecs::Registry>(); auto registry = lt::memory::create_ref<lt::ecs::Registry>();
auto system = lt::surface::System { registry }; auto system = lt::surface::System { registry };
@ -37,18 +37,9 @@ try
auto timer = lt::time::Timer {}; auto timer = lt::time::Timer {};
lt::log::trace("Ticking for 3 seconds..."); lt::log::trace("Ticking for 3 seconds...");
while (timer.elapsed_time() < std::chrono::seconds { 30 }) while (timer.elapsed_time() < std::chrono::seconds { 3 })
{ {
system.tick({}); system.tick({});
} }
lt::log::trace("Three seconds passed, quitting..."); lt::log::trace("Three seconds passed, quitting...");
} }
catch (const std::exception &exp)
{
lt::log::critical("Aborting due to uncaught std::exception:");
lt::log::critical("\twhat: {}", exp.what());
}
catch (...)
{
lt::log::critical("Aborting due to uncaught non std::exception!");
}

View file

@ -7,19 +7,18 @@ struct wl_surface;
#endif #endif
export module surface.system:components; export module surface.system:components;
import std;
import preliminary;
import math.vec2; import math.vec2;
import surface.events; import surface.events;
import surface.requests; import surface.requests;
export namespace lt::surface { namespace lt::surface {
/** Represents a platform's surface (eg. a Window). /** Represents a platform's surface (eg. a Window).
* *
* @note This is a "system component" * @note This is a "system component"
*/ */
class SurfaceComponent export class SurfaceComponent
{ {
public: public:
friend class System; friend class System;
@ -60,8 +59,6 @@ public:
static constexpr auto max_title_length = 256; static constexpr auto max_title_length = 256;
// TODO(Light): add `center_to_screen` flag
// TODO(Light): add `screen_mode` flag (windowed/full_screen/windowed_full_screen)
struct CreateInfo struct CreateInfo
{ {
std::string_view title; std::string_view title;

View file

@ -1,8 +1,7 @@
export module surface.events; export module surface.events;
import preliminary;
import input.codes; import input.codes;
import math.vec2; import math.vec2;
import std;
export namespace lt::surface { export namespace lt::surface {
@ -93,7 +92,7 @@ private:
class MouseMovedEvent class MouseMovedEvent
{ {
public: public:
MouseMovedEvent(f32 x, f32 y): m_position(x, y) MouseMovedEvent(float x, float y): m_position(x, y)
{ {
} }
@ -102,12 +101,12 @@ public:
return m_position; return m_position;
} }
[[nodiscard]] auto get_x() const -> f32 [[nodiscard]] auto get_x() const -> float
{ {
return m_position.x; return m_position.x;
} }
[[nodiscard]] auto get_y() const -> f32 [[nodiscard]] auto get_y() const -> float
{ {
return m_position.y; return m_position.y;
} }
@ -124,11 +123,11 @@ private:
class WheelScrolledEvent class WheelScrolledEvent
{ {
public: public:
WheelScrolledEvent(f32 offset): m_offset(offset) WheelScrolledEvent(float offset): m_offset(offset)
{ {
} }
[[nodiscard]] auto get_offset() const -> f32 [[nodiscard]] auto get_offset() const -> float
{ {
return m_offset; return m_offset;
} }
@ -141,7 +140,7 @@ public:
} }
private: private:
f32 m_offset; float m_offset;
}; };
class ButtonPressedEvent class ButtonPressedEvent
@ -198,7 +197,7 @@ public:
class MovedEvent class MovedEvent
{ {
public: public:
MovedEvent(i32 x, i32 y): m_position(x, y) MovedEvent(std::int32_t x, std::int32_t y): m_position(x, y)
{ {
} }
@ -219,7 +218,7 @@ private:
class ResizedEvent class ResizedEvent
{ {
public: public:
ResizedEvent(u32 width, u32 height): m_size(width, height) ResizedEvent(std::uint32_t width, std::uint32_t height): m_size(width, height)
{ {
} }

View file

@ -1,7 +1,6 @@
export module surface.requests; export module surface.requests;
import preliminary;
import math.vec2; import math.vec2;
import std;
export namespace lt::surface { export namespace lt::surface {

View file

@ -10,8 +10,7 @@ module;
export module surface.system; export module surface.system;
export import :components; export import :components;
import debug.assertions;
import preliminary;
import app.system; import app.system;
import ecs.registry; import ecs.registry;
import math.vec2; import math.vec2;
@ -20,8 +19,9 @@ import surface.requests;
import memory.reference; import memory.reference;
import memory.null_on_move; import memory.null_on_move;
import logger; import logger;
import preliminary; import std;
import surface.constants; import surface.constants;
import debug.assertions;
import memory.reference; import memory.reference;
import surface.requests; import surface.requests;
import surface.events; import surface.events;
@ -29,6 +29,7 @@ import logger;
import ecs.registry; import ecs.registry;
import ecs.entity; import ecs.entity;
import time; import time;
import std;
export namespace lt::surface { export namespace lt::surface {
@ -65,24 +66,28 @@ private:
static void wayland_registry_listener( static void wayland_registry_listener(
void *data, void *data,
wl_registry *registry, wl_registry *registry,
u32 name, std::uint32_t name,
const char *interface, const char *interface,
u32 version std::uint32_t version
); );
static void wayland_seat_capabilities_listener(void *data, wl_seat *seat, u32 capabilities); static void wayland_seat_capabilities_listener(
void *data,
wl_seat *seat,
std::uint32_t capabilities
);
static void wayland_pointer_leave_listener( static void wayland_pointer_leave_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 serial, std::uint32_t serial,
wl_surface *surface wl_surface *surface
); );
static void wayland_pointer_enter_listener( static void wayland_pointer_enter_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 serial, std::uint32_t serial,
wl_surface *surface, wl_surface *surface,
wl_fixed_t surface_x, wl_fixed_t surface_x,
wl_fixed_t surface_y wl_fixed_t surface_y
@ -91,7 +96,7 @@ private:
static void wayland_pointer_motion_listener( static void wayland_pointer_motion_listener(
void *data, void *data,
wl_pointer *listener, wl_pointer *listener,
u32 time, std::uint32_t time,
wl_fixed_t surface_x, wl_fixed_t surface_x,
wl_fixed_t surface_y wl_fixed_t surface_y
); );
@ -99,38 +104,38 @@ private:
static void wayland_pointer_button_listener( static void wayland_pointer_button_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 serial, std::uint32_t serial,
u32 time, std::uint32_t time,
u32 button, std::uint32_t button,
u32 state std::uint32_t state
); );
static void wayland_pointer_axis_listener( static void wayland_pointer_axis_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 time, std::uint32_t time,
u32 axis, std::uint32_t axis,
wl_fixed_t value wl_fixed_t value
); );
static void wayland_pointer_axis_source_listener( static void wayland_pointer_axis_source_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 axis_source std::uint32_t axis_source
); );
static void wayland_pointer_axis_stop_listener( static void wayland_pointer_axis_stop_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 time, std::uint32_t time,
u32 axis_source std::uint32_t axis_source
); );
static void wayland_pointer_axis_discrete_listener( static void wayland_pointer_axis_discrete_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 axis, std::uint32_t axis,
i32 discrete std::int32_t discrete
); );
static void wayland_pointer_frame_listener(void *data, wl_pointer *pointer); static void wayland_pointer_frame_listener(void *data, wl_pointer *pointer);
@ -173,7 +178,7 @@ private:
wl_compositor *m_wl_compositor {}; wl_compositor *m_wl_compositor {};
xdg_wm_base *m_shell {}; xdg_wm_base *m_shell = {};
wl_seat *m_wl_seat {}; wl_seat *m_wl_seat {};
@ -194,18 +199,18 @@ namespace lt::surface {
#if defined(LIGHT_PLATFORM_LINUX) #if defined(LIGHT_PLATFORM_LINUX)
void handle_shell_ping(void *data, xdg_wm_base *shell, u32 serial) void handle_shell_ping(void *data, xdg_wm_base *shell, std::uint32_t serial)
{ {
ignore = data; std::ignore = data;
xdg_wm_base_pong(shell, serial); xdg_wm_base_pong(shell, serial);
} }
const auto shell_listener = xdg_wm_base_listener { const auto shell_listener = xdg_wm_base_listener {
.ping = &handle_shell_ping, .ping = &handle_shell_ping,
}; };
void handle_shell_surface_configure(void *data, xdg_surface *shell_surface, u32 serial) void handle_shell_surface_configure(void *data, xdg_surface *shell_surface, std::uint32_t serial)
{ {
ignore = data; std::ignore = data;
xdg_surface_ack_configure(shell_surface, serial); xdg_surface_ack_configure(shell_surface, serial);
} }
@ -216,8 +221,8 @@ const auto shell_surface_listener = xdg_surface_listener {
void handle_toplevel_configure( void handle_toplevel_configure(
void *data, void *data,
xdg_toplevel *toplevel, xdg_toplevel *toplevel,
i32 width, std::int32_t width,
i32 height, std::int32_t height,
wl_array *states wl_array *states
) )
{ {
@ -235,7 +240,7 @@ const auto toplevel_listener = xdg_toplevel_listener {
void wayland_pointer_leave_listener( void wayland_pointer_leave_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 serial, std::uint32_t serial,
wl_surface *surface wl_surface *surface
) )
{ {
@ -245,10 +250,10 @@ void wayland_pointer_leave_listener(
/* static */ void System::wayland_seat_capabilities_listener( /* static */ void System::wayland_seat_capabilities_listener(
void *data, void *data,
wl_seat *seat, wl_seat *seat,
u32 capabilities std::uint32_t capabilities
) )
{ {
ignore = seat; std::ignore = seat;
auto *system = std::bit_cast<System *>(data); auto *system = std::bit_cast<System *>(data);
const auto have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER; const auto have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
@ -257,21 +262,27 @@ void wayland_pointer_leave_listener(
{ {
system->m_wl_pointer = wl_seat_get_pointer(system->m_wl_seat); system->m_wl_pointer = wl_seat_get_pointer(system->m_wl_seat);
wl_pointer_add_listener(system->m_wl_pointer, &system->m_wl_pointer_listener, system); wl_pointer_add_listener(system->m_wl_pointer, &system->m_wl_pointer_listener, system);
log::info("Added Wayland pointer (0x{:x})", std::bit_cast<size_t>(system->m_wl_pointer)); log::info(
"Added Wayland pointer (0x{:x})",
std::bit_cast<std::size_t>(system->m_wl_pointer)
);
} }
else if (!have_pointer && system->m_wl_pointer) else if (!have_pointer && system->m_wl_pointer)
{ {
wl_pointer_release(system->m_wl_pointer); wl_pointer_release(system->m_wl_pointer);
system->m_wl_pointer = nullptr; system->m_wl_pointer = nullptr;
log::info("Released Wayland pointer (0x{:x})", std::bit_cast<size_t>(system->m_wl_pointer)); log::info(
"Released Wayland pointer (0x{:x})",
std::bit_cast<std::size_t>(system->m_wl_pointer)
);
} }
} }
/* static */ void System::wayland_pointer_leave_listener( /* static */ void System::wayland_pointer_leave_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 serial, std::uint32_t serial,
wl_surface *surface wl_surface *surface
) )
{ {
@ -281,7 +292,7 @@ void wayland_pointer_leave_listener(
/* static */ void System::wayland_pointer_enter_listener( /* static */ void System::wayland_pointer_enter_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 serial, std::uint32_t serial,
wl_surface *surface, wl_surface *surface,
wl_fixed_t surface_x, wl_fixed_t surface_x,
wl_fixed_t surface_y wl_fixed_t surface_y
@ -293,7 +304,7 @@ void wayland_pointer_leave_listener(
/* static */ void System::wayland_pointer_motion_listener( /* static */ void System::wayland_pointer_motion_listener(
void *data, void *data,
wl_pointer *listener, wl_pointer *listener,
u32 time, std::uint32_t time,
wl_fixed_t surface_x, wl_fixed_t surface_x,
wl_fixed_t surface_y wl_fixed_t surface_y
) )
@ -304,10 +315,10 @@ void wayland_pointer_leave_listener(
/* static */ void System::wayland_pointer_button_listener( /* static */ void System::wayland_pointer_button_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 serial, std::uint32_t serial,
u32 time, std::uint32_t time,
u32 button, std::uint32_t button,
u32 state std::uint32_t state
) )
{ {
} }
@ -315,8 +326,8 @@ void wayland_pointer_leave_listener(
/* static */ void System::wayland_pointer_axis_listener( /* static */ void System::wayland_pointer_axis_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 time, std::uint32_t time,
u32 axis, std::uint32_t axis,
wl_fixed_t value wl_fixed_t value
) )
{ {
@ -325,7 +336,7 @@ void wayland_pointer_leave_listener(
/* static */ void System::wayland_pointer_axis_source_listener( /* static */ void System::wayland_pointer_axis_source_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 axis_source std::uint32_t axis_source
) )
{ {
} }
@ -333,8 +344,8 @@ void wayland_pointer_leave_listener(
/* static */ void System::wayland_pointer_axis_stop_listener( /* static */ void System::wayland_pointer_axis_stop_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 time, std::uint32_t time,
u32 axis_source std::uint32_t axis_source
) )
{ {
} }
@ -342,8 +353,8 @@ void wayland_pointer_leave_listener(
/* static */ void System::wayland_pointer_axis_discrete_listener( /* static */ void System::wayland_pointer_axis_discrete_listener(
void *data, void *data,
wl_pointer *pointer, wl_pointer *pointer,
u32 axis, std::uint32_t axis,
i32 discrete std::int32_t discrete
) )
{ {
} }
@ -355,23 +366,23 @@ void wayland_pointer_leave_listener(
void seat_name_listener(void *data, wl_seat *seat, const char *name) void seat_name_listener(void *data, wl_seat *seat, const char *name)
{ {
ignore = data; std::ignore = data;
log::info("Wayland seat:"); log::info("Wayland seat:");
log::info("\tname: {}", name); log::info("\tname: {}", name);
log::info("\taddr: 0x{:x}", std::bit_cast<size_t>(seat)); log::info("\taddr: 0x{:x}", std::bit_cast<std::size_t>(seat));
} }
void System::wayland_registry_listener( void System::wayland_registry_listener(
void *data, void *data,
wl_registry *registry, wl_registry *registry,
u32 name, std::uint32_t name,
const char *interface, const char *interface,
u32 version std::uint32_t version
) )
{ {
ignore = version; std::ignore = version;
auto *system = std::bit_cast<System *>(data); auto *system = std::bit_cast<System *>(data);
@ -402,7 +413,7 @@ void System::wayland_registry_listener(
} }
} }
void registry_handle_global_remove(void *data, wl_registry *registry, u32 name) void registry_handle_global_remove(void *data, wl_registry *registry, std::uint32_t name)
{ {
log::trace("Registry global remove:"); log::trace("Registry global remove:");
log::trace("\tname: {}", name); log::trace("\tname: {}", name);
@ -438,11 +449,11 @@ System::System(memory::Ref<ecs::Registry> registry)
{ {
// NOLINTNEXTLINE // NOLINTNEXTLINE
m_wl_display = wl_display_connect({}); m_wl_display = wl_display_connect({});
ensure(m_wl_display, "Failed to connect to Wayland display"); debug::ensure(m_wl_display, "Failed to connect to Wayland display");
// NOLINTNEXTLINE // NOLINTNEXTLINE
m_wl_registry = wl_display_get_registry(m_wl_display); m_wl_registry = wl_display_get_registry(m_wl_display);
ensure(m_wl_registry, "Failed to get Wayland display's registry"); debug::ensure(m_wl_registry, "Failed to get Wayland display's registry");
// TODO(Light): "this" could be moved around... replace with a pointer to some heap allocation // TODO(Light): "this" could be moved around... replace with a pointer to some heap allocation
wl_registry_add_listener(m_wl_registry, &m_wl_registry_listener, this); wl_registry_add_listener(m_wl_registry, &m_wl_registry_listener, this);
@ -452,8 +463,8 @@ System::System(memory::Ref<ecs::Registry> registry)
// For reasons beyond my fragile comprehension :( // For reasons beyond my fragile comprehension :(
wl_display_roundtrip(m_wl_display); wl_display_roundtrip(m_wl_display);
ensure(m_wl_compositor, "Failed to bind to the Wayland's compositor global"); debug::ensure(m_wl_compositor, "Failed to bind to the Wayland's compositor global");
ensure(m_shell, "Failed to bind to the Wayland's XDG-shell global"); debug::ensure(m_shell, "Failed to bind to the Wayland's XDG-shell global");
} }
System::~System() System::~System()
@ -486,14 +497,14 @@ void System::create_surface_component(ecs::EntityId entity, SurfaceComponent::Cr
auto *shell_toplevel = (xdg_toplevel *)nullptr; auto *shell_toplevel = (xdg_toplevel *)nullptr;
wayland_surface = wl_compositor_create_surface(m_wl_compositor); wayland_surface = wl_compositor_create_surface(m_wl_compositor);
ensure(wayland_surface, "Failed to create Wayland surface"); debug::ensure(wayland_surface, "Failed to create Wayland surface");
shell_surface = xdg_wm_base_get_xdg_surface(m_shell, wayland_surface); shell_surface = xdg_wm_base_get_xdg_surface(m_shell, wayland_surface);
ensure(shell_surface, "Failed to get XDG-shell surface"); debug::ensure(shell_surface, "Failed to get XDG-shell surface");
xdg_surface_add_listener(shell_surface, &shell_surface_listener, {}); xdg_surface_add_listener(shell_surface, &shell_surface_listener, {});
shell_toplevel = xdg_surface_get_toplevel(shell_surface); shell_toplevel = xdg_surface_get_toplevel(shell_surface);
ensure(shell_toplevel, "Failed to get XDG-shell toplevel"); debug::ensure(shell_toplevel, "Failed to get XDG-shell toplevel");
xdg_toplevel_add_listener(shell_toplevel, &toplevel_listener, {}); xdg_toplevel_add_listener(shell_toplevel, &toplevel_listener, {});
xdg_toplevel_set_title(shell_toplevel, "Wayland Vulkan Example"); xdg_toplevel_set_title(shell_toplevel, "Wayland Vulkan Example");
@ -516,125 +527,6 @@ void System::tick(app::TickInfo tick)
#ifdef LIGHT_PLATFORM_WINDOWS #ifdef LIGHT_PLATFORM_WINDOWS
constexpr auto translate_key(auto code) -> Key
{
using enum Key;
switch (code)
{
case VK_LBUTTON: return left_button;
case VK_RBUTTON: return right_button;
case VK_MBUTTON: return middle_button;
case VK_BACK: return backspace;
case VK_TAB: return tab;
case VK_CAPITAL: return capslock;
case VK_RETURN: return enter;
case VK_SPACE: return space;
case VK_DELETE: return delete_;
case VK_SHIFT: return shift;
case VK_RSHIFT: return right_shift;
case VK_CONTROL: return control;
case VK_RCONTROL: return right_control;
case VK_MENU: return alt;
case VK_RMENU: return right_alt;
case VK_PRIOR: return pageup;
case VK_NEXT: return pagedown;
case VK_END: return end;
case VK_HOME: return home;
case VK_LEFT: return left_arrow;
case VK_RIGHT: return right_arrow;
case VK_DOWN: return down_arrow;
case VK_UP: return up_arrow;
case VK_CANCEL: return cancel;
case VK_PAUSE: return pause;
case VK_SELECT: return select;
case VK_PRINT: return print;
case VK_SNAPSHOT: return snapshot;
case VK_INSERT: return insert;
case VK_HELP: return help;
case VK_SLEEP: return sleep;
case '0': return digit_0;
case '1': return digit_1;
case '2': return digit_2;
case '3': return digit_3;
case '4': return digit_4;
case '5': return digit_5;
case '6': return digit_6;
case '7': return digit_7;
case '8': return digit_8;
case '9': return digit_9;
case 'A': return a;
case 'B': return b;
case 'C': return c;
case 'D': return d;
case 'E': return e;
case 'F': return f;
case 'G': return g;
case 'H': return h;
case 'I': return i;
case 'J': return j;
case 'K': return k;
case 'L': return l;
case 'M': return m;
case 'N': return n;
case 'O': return o;
case 'P': return p;
case 'Q': return q;
case 'R': return r;
case 'S': return s;
case 'T': return t;
case 'U': return u;
case 'V': return v;
case 'W': return w;
case 'X': return x;
case 'Y': return y;
case 'Z': return z;
case VK_LWIN: return super;
case VK_RWIN: return right_super;
case VK_NUMPAD0: return kp_0;
case VK_NUMPAD1: return kp_1;
case VK_NUMPAD2: return kp_2;
case VK_NUMPAD3: return kp_3;
case VK_NUMPAD4: return kp_4;
case VK_NUMPAD5: return kp_5;
case VK_NUMPAD6: return kp_6;
case VK_NUMPAD7: return kp_7;
case VK_NUMPAD8: return kp_8;
case VK_NUMPAD9: return kp_9;
case VK_MULTIPLY: return kp_multiply;
case VK_ADD: return kp_add;
case VK_SUBTRACT: return kp_subtract;
case VK_DECIMAL: return kp_decimal;
case VK_F1: return f1;
case VK_F2: return f2;
case VK_F3: return f3;
case VK_F4: return f4;
case VK_F5: return f5;
case VK_F6: return f6;
case VK_F7: return f7;
case VK_F8: return f8;
case VK_F9: return f9;
case VK_F10: return f10;
case VK_F11: return f11;
case VK_F12: return f12;
default: return unknown;
}
};
template<class... Ts> template<class... Ts>
struct overloads: Ts... struct overloads: Ts...
{ {
@ -643,13 +535,13 @@ struct overloads: Ts...
void ensure_component_sanity(const SurfaceComponent &component); void ensure_component_sanity(const SurfaceComponent &component);
auto CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT; auto CALLBACK native_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT;
System::System(memory::Ref<ecs::Registry> registry): m_registry(std::move(registry)) 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, m_registry->view<SurfaceComponent>().get_size() == 0,
"Failed to initialize surface system: registry has surface component(s)" "Failed to initialize surface system: registry has surface component(s)"
); );
@ -661,7 +553,7 @@ System::System(memory::Ref<ecs::Registry> registry): m_registry(std::move(regist
); );
auto window_class = WNDCLASS { auto window_class = WNDCLASS {
.lpfnWndProc = window_proc, .lpfnWndProc = native_window_proc,
.hInstance = GetModuleHandle(nullptr), .hInstance = GetModuleHandle(nullptr),
.lpszClassName = constants::class_name, .lpszClassName = constants::class_name,
}; };
@ -710,17 +602,11 @@ void System::on_unregister()
void System::create_surface_component(ecs::EntityId entity, SurfaceComponent::CreateInfo info) void System::create_surface_component(ecs::EntityId entity, SurfaceComponent::CreateInfo info)
try try
{ {
// WIP(Light): use ignored local variables...
auto &component = m_registry->add<SurfaceComponent>(entity, info); auto &component = m_registry->add<SurfaceComponent>(entity, info);
ignore = component;
auto &surface = m_registry->get<SurfaceComponent>(entity); auto &surface = m_registry->get<SurfaceComponent>(entity);
ensure_component_sanity(surface);
const auto &resolution = surface.get_resolution(); const auto &resolution = surface.get_resolution();
const auto &position = surface.get_position(); const auto &position = surface.get_position();
ignore = resolution; ensure_component_sanity(surface);
ignore = position;
surface.m_native_data.window = CreateWindowEx( surface.m_native_data.window = CreateWindowEx(
0, 0,
@ -736,25 +622,22 @@ try
GetModuleHandle(nullptr), GetModuleHandle(nullptr),
nullptr nullptr
); );
ensure(surface.m_native_data.window, "Failed to create Windows surface component"); debug::ensure(surface.m_native_data.window, "Failed to create Windows surface component");
ShowWindow(surface.m_native_data.window, SW_NORMAL); ShowWindow(surface.m_native_data.window, SW_NORMAL);
// SetWindowLongPtrA(surface.m_native_data.window, 0, this);
// SetWindowLongPtrA(surface.m_native_data.window, 1, entity);
// TODO(Light): refactor "environment" into standalone module // TODO(Light): refactor "environment" into standalone module
// NOLINTNEXTLINE(concurrency-mt-unsafe) // NOLINTNEXTLINE(concurrency-mt-unsafe)
// auto *display_env = std::getenv("DISPLAY"); // 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); // 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 root_window = XDefaultRootWindow(display);
// //
// auto border_width = 0; // auto border_width = 0;
// auto depth = i32 { CopyFromParent }; // auto depth = std::int32_t { CopyFromParent };
// auto window_class = CopyFromParent; // auto window_class = CopyFromParent;
// auto *visual = (Visual *)CopyFromParent; // auto *visual = (Visual *)CopyFromParent;
// //
@ -820,7 +703,7 @@ try
catch (const std::exception &exp) catch (const std::exception &exp)
{ {
log::error("Exception thrown when on_constructing surface component"); log::error("Exception thrown when on_constructing surface component");
log::error("\tentity: {}", u32 { entity }); log::error("\tentity: {}", std::uint32_t { entity });
log::error("\twhat: {}", exp.what()); log::error("\twhat: {}", exp.what());
m_registry->remove<SurfaceComponent>(entity); m_registry->remove<SurfaceComponent>(entity);
} }
@ -843,65 +726,15 @@ void System::handle_events(SurfaceComponent &surface)
queue.clear(); queue.clear();
auto message = MSG {}; auto message = MSG {};
while (PeekMessage(&message, {}, {}, {}, PM_REMOVE)) while (PeekMessage(&message, surface.m_native_data.window, {}, {}, PM_REMOVE))
{ {
TranslateMessage(&message);
const auto wParam = message.wParam;
switch (message.message) switch (message.message)
{ {
case WM_SETFOCUS: log::debug("Window setfocus"); break;
case WM_KILLFOCUS: log::debug("Window killfocus"); break;
case WM_ACTIVATE:
log::debug("Window activate: {}", static_cast<size_t>(LOWORD(wParam)));
break;
case WM_MOUSEWHEEL:
{
const auto delta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
log::debug("wheel delta: {}", static_cast<i64>(delta));
break;
}
case WM_LBUTTONDOWN:
log::debug("Left button down: {}", to_string(translate_key(wParam)));
break;
case WM_LBUTTONUP: log::debug("Left button up {}", to_string(translate_key(wParam))); break;
case WM_RBUTTONDOWN:
log::debug("Right button down {}", to_string(translate_key(wParam)));
break;
case WM_RBUTTONUP:
log::debug("Right button up {}", to_string(translate_key(wParam)));
break;
case WM_MBUTTONDOWN:
log::debug("Middle button down {}", to_string(translate_key(wParam)));
break;
case WM_MBUTTONUP:
log::debug("Middle button up {}", to_string(translate_key(wParam)));
break;
case WM_XBUTTONDOWN:
{
const auto key = static_cast<Key>(
std::to_underlying(Key::x_button_1) + GET_XBUTTON_WPARAM(wParam) - 1
);
log::debug("xbutn: {}", std::to_underlying(key));
break;
}
case WM_XBUTTONUP:
{
const auto key = static_cast<Key>(
std::to_underlying(Key::x_button_1) + GET_XBUTTON_WPARAM(wParam) - 1
);
log::debug("xbutn: {}", std::to_underlying(key));
break;
}
case WM_KEYDOWN: log::debug("Keydown: {}", to_string(translate_key(wParam))); break;
case WM_KEYUP: log::debug("Keyup__: {}", to_string(translate_key(wParam))); break;
} }
TranslateMessage(&message);
DispatchMessage(&message); DispatchMessage(&message);
// log::debug("Window message type: {}", std::uint32_t { message.message });
} }
// auto event = XEvent {}; // auto event = XEvent {};
@ -917,14 +750,14 @@ void System::handle_events(SurfaceComponent &surface)
// case KeyPress: // case KeyPress:
// { // {
// queue.emplace_back<KeyPressedEvent>( // queue.emplace_back<KeyPressedEvent>(
// static_cast<u32>(XLookupKeysym(&event.xkey, 0)) // static_cast<std::uint32_t>(XLookupKeysym(&event.xkey, 0))
// ); // );
// break; // break;
// } // }
// case KeyRelease: // case KeyRelease:
// { // {
// queue.emplace_back<KeyReleasedEvent>( // queue.emplace_back<KeyReleasedEvent>(
// static_cast<u32>(XLookupKeysym(&event.xkey, 0)) // static_cast<std::uint32_t>(XLookupKeysym(&event.xkey, 0))
// ); // );
// break; // break;
// } // }
@ -960,8 +793,8 @@ void System::handle_events(SurfaceComponent &surface)
// case MotionNotify: // case MotionNotify:
// { // {
// queue.emplace_back<MouseMovedEvent>(MouseMovedEvent { // queue.emplace_back<MouseMovedEvent>(MouseMovedEvent {
// static_cast<f32>(event.xmotion.x), // static_cast<float>(event.xmotion.x),
// static_cast<f32>(event.xmotion.y), // static_cast<float>(event.xmotion.y),
// }); // });
// break; // break;
// } // }
@ -975,8 +808,8 @@ void System::handle_events(SurfaceComponent &surface)
// surface.m_resolution.x = new_width; // surface.m_resolution.x = new_width;
// surface.m_resolution.y = new_height; // surface.m_resolution.y = new_height;
// queue.emplace_back<ResizedEvent>(ResizedEvent { // queue.emplace_back<ResizedEvent>(ResizedEvent {
// static_cast<u32>(new_width), // static_cast<std::uint32_t>(new_width),
// static_cast<u32>(new_height), // static_cast<std::uint32_t>(new_height),
// }); // });
// } // }
// //
@ -1015,16 +848,10 @@ void System::handle_requests(SurfaceComponent &surface)
{ {
std::visit(visitor, request); std::visit(visitor, request);
} }
surface.m_requests.clear();
} }
void System::modify_title(SurfaceComponent &surface, const ModifyTitleRequest &request) void System::modify_title(SurfaceComponent &surface, const ModifyTitleRequest &request)
{ {
// WIP(Light):
ignore = surface;
ignore = request;
surface.m_title = request.title; surface.m_title = request.title;
// const auto &[display, window, _] = surface.get_native_data(); // const auto &[display, window, _] = surface.get_native_data();
@ -1033,10 +860,6 @@ void System::modify_title(SurfaceComponent &surface, const ModifyTitleRequest &r
void System::modify_resolution(SurfaceComponent &surface, const ModifyResolutionRequest &request) void System::modify_resolution(SurfaceComponent &surface, const ModifyResolutionRequest &request)
{ {
// WIP(Light):
ignore = surface;
ignore = request;
// surface.m_resolution = request.resolution; // surface.m_resolution = request.resolution;
// auto &[display, window, _] = surface.m_native_data; // auto &[display, window, _] = surface.m_native_data;
@ -1050,8 +873,8 @@ void System::modify_resolution(SurfaceComponent &surface, const ModifyResolution
// XResizeWindow( // XResizeWindow(
// display, // display,
// window, // window,
// static_cast<u32>(width), // static_cast<std::uint32_t>(width),
// static_cast<u32>(height) // static_cast<std::uint32_t>(height)
// ); // );
// //
// // flush output queue and wait for X server to processes the request // // flush output queue and wait for X server to processes the request
@ -1099,24 +922,45 @@ void System::modify_resolution(SurfaceComponent &surface, const ModifyResolution
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request) void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
{ {
log::debug("Setting window position to: {}, {}", request.position.x, request.position.y); // surface.m_position = request.position;
SetWindowPos(
surface.m_native_data.window, // auto &[display, window, _] = surface.m_native_data;
{}, // const auto &[x, y] = request.position;
request.position.x, //
request.position.y, // // get baseline serial number for X requests generated from XResizeWindow
{}, // auto serial = NextRequest(display);
{}, // XMoveWindow(display, window, static_cast<int>(x), static_cast<int>(y));
{} //
); // // flush output queue and wait for X server to processes the request
// XSync(display, False);
// constexpr auto lifespan = std::chrono::milliseconds { 10 };
// auto timer = time::Timer {};
// auto event = XEvent {};
// while (!XCheckIfEvent(
// display,
// &event,
// XEventTypeEquals<ConfigureNotify>,
// reinterpret_cast<XPointer>(&window) // NOLINT
// )
// || event.xconfigure.serial < serial)
// {
// std::this_thread::sleep_for(std::chrono::microseconds { 100 });
// if (timer.elapsed_time() > lifespan)
// {
// log::error("Timed out waiting for XMoveWindow's event");
// return;
// }
// }
// // We don't need to update the component's state and handle the event in this funcion.
// // Since handle_requests is called before handle_events.
// // So we just put the event back into the queue and move on.
// XPutBackEvent(display, &event);
// XSync(display, False);
// XFlush(display);
} }
void System::modify_visiblity(SurfaceComponent &surface, const ModifyVisibilityRequest &request) void System::modify_visiblity(SurfaceComponent &surface, const ModifyVisibilityRequest &request)
{ {
// WIP(Light): Use ignored local-variables
ignore = surface;
ignore = request;
// const auto &[display, window, _] = surface.get_native_data(); // const auto &[display, window, _] = surface.get_native_data();
// surface.m_visible = request.visible; // surface.m_visible = request.visible;
@ -1150,25 +994,29 @@ void ensure_component_sanity(const SurfaceComponent &component)
{ {
auto [width, height] = component.get_resolution(); 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, width < SurfaceComponent::max_dimension,
"Received bad values for surface component: width({}) > max_dimension({})", "Received bad values for surface component: width({}) > max_dimension({})",
width, width,
SurfaceComponent::max_dimension SurfaceComponent::max_dimension
); );
ensure( debug::ensure(
height < SurfaceComponent::max_dimension, height < SurfaceComponent::max_dimension,
"Received bad values for surface component: height({}) > max_dimension({})", "Received bad values for surface component: height({}) > max_dimension({})",
height, height,
SurfaceComponent::max_dimension SurfaceComponent::max_dimension
); );
ensure( debug::ensure(
component.get_title().size() < SurfaceComponent::max_title_length, component.get_title().size() < SurfaceComponent::max_title_length,
"Received bad values for surface component: title.size({}) > max_title_length({})", "Received bad values for surface component: title.size({}) > max_title_length({})",
component.get_title().size(), component.get_title().size(),
@ -1176,28 +1024,139 @@ void ensure_component_sanity(const SurfaceComponent &component)
); );
} }
auto CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT auto CALLBACK native_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT
{ {
constexpr auto translate_key = [](auto code) -> Key {
switch (code)
{
using enum Key;
case VK_LBUTTON: return left_mouse_button;
case VK_RBUTTON: return right_mouse_button;
case VK_MBUTTON: return middle_mouse_button;
case VK_BACK: return backspace;
case VK_TAB: return tab;
case VK_CAPITAL: return capslock;
case VK_RETURN: enter;
case VK_SPACE: space;
case VK_DELETE: return delete_;
case VK_SHIFT: shift;
case VK_RSHIFT: right_shift;
case VK_CONTROL: control;
case VK_RCONTROL: right_control;
case VK_MENU: alt;
case VK_RMENU: right_alt;
case VK_PRIOR: return pageup;
case VK_NEXT: return pagedown;
case VK_END: return end;
case VK_HOME: return home;
case VK_LEFT: return left_arrow;
case VK_RIGHT: return right_arrow;
case VK_DOWN: return down_arrow;
case VK_UP: return up_arrow;
case VK_CANCEL: return cancel;
case VK_PAUSE: return pause;
case VK_SELECT: return select;
case VK_PRINT: return print;
case VK_SNAPSHOT: return snapshot;
case VK_INSERT: return insert;
case VK_HELP: return help;
case VK_SLEEP: return sleep;
case '0': return digit_0;
case '1': return digit_1;
case '2': return digit_2;
case '3': return digit_3;
case '4': return digit_4;
case '5': return digit_5;
case '6': return digit_6;
case '7': return digit_7;
case '8': return digit_8;
case '9': return digit_9;
case 'A': return a;
case 'B': return b;
case 'C': return c;
case 'D': return d;
case 'E': return e;
case 'F': return f;
case 'G': return g;
case 'H': return h;
case 'I': return i;
case 'J': return j;
case 'K': return k;
case 'L': return l;
case 'M': return m;
case 'N': return n;
case 'O': return o;
case 'P': return p;
case 'Q': return q;
case 'R': return r;
case 'S': return s;
case 'T': return t;
case 'U': return u;
case 'V': return v;
case 'W': return w;
case 'X': return x;
case 'Y': return y;
case 'Z': return z;
case VK_LWIN: return super;
case VK_RWIN: return right_super;
case VK_NUMPAD0: return kp_0;
case VK_NUMPAD1: return kp_1;
case VK_NUMPAD2: return kp_2;
case VK_NUMPAD3: return kp_3;
case VK_NUMPAD4: return kp_4;
case VK_NUMPAD5: return kp_5;
case VK_NUMPAD6: return kp_6;
case VK_NUMPAD7: return kp_7;
case VK_NUMPAD8: return kp_8;
case VK_NUMPAD9: return kp_9;
case VK_MULTIPLY: return kp_multiply;
case VK_ADD: return kp_add;
case VK_SUBTRACT: return kp_subtract;
case VK_DECIMAL: return kp_decimal;
case VK_F1: return f1;
case VK_F2: return f2;
case VK_F3: return f3;
case VK_F4: return f4;
case VK_F5: return f5;
case VK_F6: return f6;
case VK_F7: return f7;
case VK_F8: return f8;
case VK_F9: return f9;
case VK_F10: return f10;
case VK_F11: return f11;
case VK_F12: return f12;
default: return unknown;
}
};
switch (uMsg) switch (uMsg)
{ {
case WM_KILLFOCUS: case WM_KEYDOWN: log::debug("Keydown: {}", to_string(translate_key(wParam)));
case WM_MOVE: case WM_KEYUP: log::debug("Keyup__: {}", to_string(translate_key(wParam)));
case WM_SETFOCUS: case WM_DESTROY:
case WM_ACTIVATE: {
case WM_MOUSEWHEEL: PostQuitMessage(0);
case WM_LBUTTONDOWN: return 0;
case WM_LBUTTONUP: }
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
case WM_KEYDOWN:
case WM_KEYUP: return 0;
case WM_DESTROY: PostQuitMessage(0); return 0;
} }
// log::debug("Window message type (window proc): {}", std::uint32_t { uMsg
// });
return DefWindowProcA(hwnd, uMsg, wParam, lParam); return DefWindowProcA(hwnd, uMsg, wParam, lParam);
} }

View file

@ -0,0 +1,155 @@
#include <ecs/entity.hpp>
#include <ecs/registry.hpp>
#include <memory/reference.hpp>
#include <surface/components.hpp>
#include <surface/system.hpp>
#include <test/fuzz.hpp>
#include <test/test.hpp>
namespace lt::surface {
enum class FuzzAction : uint8_t
{
create_entity,
create_surface_component,
destroy_surface_component,
push_request,
push_event,
tick_system,
count,
};
enum class EventType : uint8_t
{
Closed,
Moved,
Resized,
LostFocus,
GainFocus,
};
void create_surface_component(test::FuzzDataProvider &provider, ecs::Registry &registry)
{
const auto length = std::min(provider.consume<uint32_t>().value_or(16), 255u);
const auto title = provider.consume_string(length).value_or("");
const auto resolution = math::uvec2 {
provider.consume<uint32_t>().value_or(32u),
provider.consume<uint32_t>().value_or(64u),
};
const auto visible = provider.consume<bool>().value_or(false);
const auto vsync = provider.consume<bool>().value_or(false);
try
{
auto entity = registry.create_entity();
registry.add<surface::SurfaceComponent>(
entity,
surface::SurfaceComponent::CreateInfo {
.title = std::move(title),
.resolution = resolution,
.vsync = vsync,
.visible = visible,
}
);
}
catch (const std::exception &exp)
{
std::ignore = exp;
}
}
void remove_surface_component(ecs::Registry &registry)
{
const auto view = registry.view<SurfaceComponent>();
if (!view.is_empty())
{
registry.remove<SurfaceComponent>(view[0].first);
}
}
void push_request(ecs::Registry &registry)
{
}
void push_event(ecs::Registry &registry)
{
}
void check_invariants()
{
}
test::FuzzHarness harness = [](const uint8_t *data, size_t size) {
auto provider = test::FuzzDataProvider { data, size };
auto registry = memory::create_ref<ecs::Registry>();
auto system = surface::System { registry };
while (auto action = provider.consume<uint8_t>())
{
if (*action > std::to_underlying(FuzzAction::count))
{
*action = *action % std::to_underlying(FuzzAction::count);
}
switch (static_cast<FuzzAction>(action.value()))
{
case FuzzAction::create_entity:
{
const auto length = std::min(provider.consume<uint32_t>().value_or(16), 255u);
registry->create_entity();
break;
}
case FuzzAction::create_surface_component:
{
create_surface_component(provider, *registry);
break;
}
case FuzzAction::destroy_surface_component:
{
remove_surface_component(*registry);
break;
}
case FuzzAction::push_event:
{
auto view = registry->view<SurfaceComponent>();
if (!view.is_empty())
{
for (auto &[entity, component] : view)
{
provider.consume<uint8_t>().value_or(0);
// @TODO(Light): push some event
}
}
break;
}
case FuzzAction::push_request:
{
break;
}
case FuzzAction::count:
case FuzzAction::tick_system:
{
system.tick();
break;
}
}
check_invariants();
}
return 0;
};
} // namespace lt::surface

View file

@ -1,16 +1,27 @@
import test; import test.test;
import time; import time;
import test.expects;
import surface.system; import surface.system;
import surface.events; import surface.events;
import surface.requests; import surface.requests;
import ecs.registry; import ecs.registry;
import memory.scope; import memory.scope;
import memory.reference; import memory.reference;
import logger;
import math.vec2; import math.vec2;
import app.system; import app.system;
import std;
using ::lt::surface::SurfaceComponent; using ::lt::surface::SurfaceComponent;
using ::lt::surface::System; 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 ::std::ignore;
using ::lt::test::operator""_suite;
[[nodiscard]] auto tick_info() -> lt::app::TickInfo [[nodiscard]] auto tick_info() -> lt::app::TickInfo
{ {
@ -77,187 +88,206 @@ private:
}; };
Suite raii = "raii"_suite = [] { Suite raii = "raii"_suite = [] {
Case { "happy paths" } = [] { Case { "happy path won't throw" } = [] {
auto fixture = Fixture {}; auto fixture = Fixture {};
auto system = System { fixture.registry() }; auto system = System { fixture.registry() };
};
Case { "unhappy paths" } = [] { auto timer = lt::time::Timer {};
expect_throw([] { ignore = System { {} }; }); lt::log::trace("Ticking for 3 seconds...");
}; while (timer.elapsed_time() < std::chrono::seconds { 3 })
Case { "many" } = [] {
auto fixture = Fixture {};
for (auto idx : std::views::iota(0, 250))
{ {
ignore = idx; system.tick({});
ignore = System { fixture.registry() };
} }
lt::log::trace("Three seconds passed, quitting...");
}; };
Case { "post construct has correct state" } = [] {
auto fixture = Fixture {};
auto system = System { fixture.registry() };
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
};
Case { "post destruct has correct state" } = [] { // Case { "many won't freeze/throw" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
auto system = lt::memory::create_scope<System>(fixture.registry()); // for (auto idx : std::views::iota(0, 250))
// {
fixture.create_component(); // ignore = System { fixture.registry() };
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1); // }
// };
system.reset(); //
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0); // Case { "unhappy path throws" } = [] {
}; // expect_throw([] { ignore = System { {} }; });
// };
//
// Case { "post construct has correct state" } = [] {
// auto fixture = Fixture {};
// auto system = System { fixture.registry() };
// expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
// };
//
// Case { "post destruct has correct state" } = [] {
// auto fixture = Fixture {};
// auto system = lt::memory::create_scope<System>(fixture.registry());
//
// fixture.create_component();
// expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1);
//
// system.reset();
// expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
// };
}; };
Suite system_events = "system_events"_suite = [] { // Suite system_events = "system_events"_suite = [] {
Case { "on_register won't throw" } = [] { // Case { "on_register won't throw" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
auto system = System { fixture.registry() }; // auto system = System { fixture.registry() };
//
system.on_register(); // system.on_register();
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0); // expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
}; // };
//
Case { "on_unregister won't throw" } = [] { // Case { "on_unregister won't throw" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
auto system = System { fixture.registry() }; // auto system = System { fixture.registry() };
//
system.on_register(); // system.on_register();
system.on_unregister(); // system.on_unregister();
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0); // expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
}; // };
}; // };
//
Suite registry_events = "registry_events"_suite = [] { // Suite registry_events = "registry_events"_suite = [] {
Case { "on_construct<SurfaceComponent> initializes component" } = [] { // Case { "on_construct<SurfaceComponent> initializes component" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
//
const auto &component = fixture.create_component(); // const auto &component = fixture.create_component();
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1); // expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1);
fixture.check_values(*component); // fixture.check_values(*component);
}; // };
//
Case { "unhappy on_construct<SurfaceComponent> throws" } = [] { // Case { "unhappy on_construct<SurfaceComponent> throws" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
auto system = System { fixture.registry() }; // auto system = System { fixture.registry() };
//
expect_throw([&] { fixture.create_component({ .resolution = { width, 0 } }); }); // expect_throw([&] { fixture.create_component({ .resolution = { width, 0 } }); });
//
expect_throw([&] { fixture.create_component({ .resolution = { 0, height } }); }); // expect_throw([&] { fixture.create_component({ .resolution = { 0, height } }); });
//
expect_throw([&] { // expect_throw([&] {
fixture.create_component( // fixture.create_component(
{ .title = "", .resolution = { SurfaceComponent::max_dimension + 1, height } } // { .title = "", .resolution = { SurfaceComponent::max_dimension + 1, height } }
); // );
}); // });
//
expect_throw([&] { // expect_throw([&] {
fixture.create_component( // fixture.create_component(
{ .title = "", .resolution = { width, SurfaceComponent::max_dimension + 1 } } // { .title = "", .resolution = { width, SurfaceComponent::max_dimension + 1 } }
); // );
}); // });
//
auto big_str = std::string {}; // auto big_str = std::string {};
big_str.resize(SurfaceComponent::max_title_length + 1); // big_str.resize(SurfaceComponent::max_title_length + 1);
expect_throw([&] { // expect_throw([&] {
fixture.create_component({ .title = big_str, .resolution = { width, height } }); // fixture.create_component({ .title = big_str, .resolution = { width, height } });
}); // });
}; // };
//
Case { "unhappy on_construct<SurfaceComponent> removes component" } = [] { // Case { "unhappy on_construct<SurfaceComponent> removes component" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
auto system = System { fixture.registry() }; // auto system = System { fixture.registry() };
//
expect_throw([&] { fixture.create_component({ .resolution = { width, 0 } }); }); // expect_throw([&] { fixture.create_component({ .resolution = { width, 0 } }); });
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0); // expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
}; // };
//
Case { "on_destrroy<SurfaceComponent> cleans up component" } = [] { // Case { "on_destrroy<SurfaceComponent> cleans up component" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
auto system = lt::memory::create_scope<System>(fixture.registry()); // auto system = lt::memory::create_scope<System>(fixture.registry());
//
const auto &component = fixture.create_component(); // const auto &component = fixture.create_component();
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1); // expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1);
fixture.check_values(*component); // fixture.check_values(*component);
//
system.reset(); // system.reset();
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0); // expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
}; // };
}; // };
//
Suite tick = "tick"_suite = [] { // Suite tick = "tick"_suite = [] {
Case { "ticking on empty registry won't throw" } = [] { // Case { "ticking on empty registry won't throw" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
System { fixture.registry() }.tick(tick_info()); // System { fixture.registry() }.tick(tick_info());
}; // };
//
Case { "ticking on non-empty registry won't throw" } = [] { // Case { "ticking on non-empty registry won't throw" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
auto system = System { fixture.registry() }; // auto system = System { fixture.registry() };
//
fixture.create_component(); // fixture.create_component();
system.tick(tick_info()); // system.tick(tick_info());
}; // };
}; // };
//
Suite tick_handles_events = "tick_handles_events"_suite = [] { // Suite tick_handles_events = "tick_handles_events"_suite = [] {
Case { "ticking clears previous tick's events" } = [] { // Case { "ticking clears previous tick's events" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
auto system = System { fixture.registry() }; // auto system = System { fixture.registry() };
auto &surface = **fixture.create_component(); // auto &surface = **fixture.create_component();
//
// flush window-creation events // // flush window-creation events
system.tick(tick_info()); // system.tick(tick_info());
expect_eq(surface.peek_events().size(), 0); // expect_eq(surface.peek_events().size(), 0);
//
surface.push_event(lt::surface::MovedEvent({}, {})); // surface.push_event(lt::surface::MovedEvent({}, {}));
expect_eq(surface.peek_events().size(), 1); // expect_eq(surface.peek_events().size(), 1);
//
surface.push_event(lt::surface::ButtonPressedEvent({})); // surface.push_event(lt::surface::ButtonPressedEvent({}));
expect_eq(surface.peek_events().size(), 2); // expect_eq(surface.peek_events().size(), 2);
//
system.tick(tick_info()); // system.tick(tick_info());
expect_eq(surface.peek_events().size(), 0); // expect_eq(surface.peek_events().size(), 0);
}; // };
}; // };
//
Suite tick_handles_requests = "tick_handles_requests"_suite = [] { // Suite tick_handles_requests = "tick_handles_requests"_suite = [] {
Case { "ticking clears requests" } = [] { // Case { "ticking clears requests" } = [] {
auto fixture = Fixture {}; // auto fixture = Fixture {};
auto system = System { fixture.registry() }; // auto system = System { fixture.registry() };
auto &surface = **fixture.create_component(); // auto &surface = **fixture.create_component();
//
constexpr auto position = lt::math::ivec2 { 50, 50 }; // constexpr auto title = "ABC";
constexpr auto resolution = lt::math::uvec2 { width, height }; // constexpr auto position = lt::math::ivec2 { 50, 50 };
// constexpr auto resolution = lt::math::uvec2 { 50, 50 };
expect_eq(surface.peek_requests().size(), 0); //
// expect_eq(surface.peek_requests().size(), 0);
surface.push_request(lt::surface::ModifyVisibilityRequest(true)); //
expect_eq(surface.peek_requests().size(), 1); // surface.push_request(lt::surface::ModifyVisibilityRequest(true));
system.tick(tick_info()); // expect_eq(surface.peek_requests().size(), 1);
expect_eq(surface.peek_requests().size(), 0); // system.tick(tick_info());
// expect_eq(surface.peek_requests().size(), 0);
surface.push_request(lt::surface::ModifyTitleRequest(title)); //
expect_eq(surface.peek_requests().size(), 1); // surface.push_request(lt::surface::ModifyTitleRequest(title));
// expect_eq(surface.peek_requests().size(), 1);
surface.push_request(lt::surface::ModifyResolutionRequest(resolution)); //
surface.push_request(lt::surface::ModifyPositionRequest(position)); // surface.push_request(lt::surface::ModifyResolutionRequest(resolution));
expect_eq(surface.peek_requests().size(), 1 + 2); // surface.push_request(lt::surface::ModifyPositionRequest(position));
// expect_eq(surface.peek_requests().size(), 1 + 2);
surface.push_request(lt::surface::ModifyVisibilityRequest(false)); //
surface.push_request(lt::surface::ModifyVisibilityRequest(true)); // surface.push_request(lt::surface::ModifyVisibilityRequest(false));
surface.push_request(lt::surface::ModifyVisibilityRequest(false)); // surface.push_request(lt::surface::ModifyVisibilityRequest(true));
expect_eq(surface.peek_requests().size(), 1 + 2 + 3); // surface.push_request(lt::surface::ModifyVisibilityRequest(false));
// expect_eq(surface.peek_requests().size(), 1 + 2 + 3);
system.tick(tick_info()); //
expect_eq(surface.peek_requests().size(), 0); // system.tick(tick_info());
// expect_eq(surface.peek_requests().size(), 0);
expect_eq(surface.get_title(), title); //
expect_eq(surface.get_position(), position); // expect_eq(surface.get_title(), title);
expect_eq(surface.get_resolution(), resolution); // expect_eq(surface.get_position(), position);
}; // expect_eq(surface.get_resolution(), resolution);
}; //
// lt::log::debug("EVENT COUNT: {}", surface.peek_events().size());
// for (const auto &event : surface.peek_events())
// {
// const auto visitor = overloads {
// [&](auto event) { lt::log::debug("event: {}", event.to_string()); },
// };
//
// std::visit(visitor, event);
// }
// };
// };

View file

@ -1,7 +1,12 @@
import test; import logger;
import test.test;
import test.registry; import test.registry;
void parse_option(std::string_view argument, lt::test::Registry::Options &options) import std;
using namespace ::lt::test;
void parse_option(std::string_view argument, Registry::Options &options)
{ {
constexpr auto case_str = std::string_view { "--case=" }; constexpr auto case_str = std::string_view { "--case=" };
constexpr auto suite_str = std::string_view { "--suite=" }; constexpr auto suite_str = std::string_view { "--suite=" };
@ -14,7 +19,7 @@ void parse_option(std::string_view argument, lt::test::Registry::Options &option
if (argument.starts_with("--mode=") && argument.substr(7ul) == "stats") if (argument.starts_with("--mode=") && argument.substr(7ul) == "stats")
{ {
options.execution_policy = lt::test::Registry::ExecutionPolicy::stats; options.execution_policy = Registry::ExecutionPolicy::stats;
return; return;
} }
@ -46,12 +51,12 @@ void print_help()
std::println("--help | -h --> ~You just used it! :D"); std::println("--help | -h --> ~You just used it! :D");
} }
auto main(i32 argc, char **argv) -> i32 auto main(std::int32_t argc, char **argv) -> std::int32_t
try try
{ {
auto raw_arguments = std::span<char *>(argv, argc); auto raw_arguments = std::span<char *>(argv, argc);
auto options = lt::test::Registry::Options {}; auto options = Registry::Options {};
for (auto idx = 0; auto &raw_argument : raw_arguments) for (auto idx = 0; auto &raw_argument : raw_arguments)
{ {
// First argument is the "cwd' // First argument is the "cwd'
@ -78,7 +83,7 @@ try
} }
} }
return static_cast<i32>(lt::test::Registry::run_all(options)); return static_cast<std::int32_t>(Registry::run_all(options));
} }
catch (const std::exception &exp) catch (const std::exception &exp)
{ {

View file

@ -1,7 +1,6 @@
export module test.expects; export module test.expects;
import std;
import preliminary;
namespace lt::test { namespace lt::test {
@ -32,7 +31,6 @@ export void expect_unreachable(
}; };
}; };
/** @todo(Light7734): Check exception type. */
export constexpr void expect_throw( export constexpr void expect_throw(
std::invocable auto invocable, std::invocable auto invocable,
std::source_location source_location = std::source_location::current() std::source_location source_location = std::source_location::current()
@ -42,7 +40,7 @@ export constexpr void expect_throw(
{ {
invocable(); invocable();
} }
catch (const std::exception &) catch (const std::exception &exp)
{ {
return; return;
} }

86
modules/test/fuzz.cppm Normal file
View file

@ -0,0 +1,86 @@
#include <cstring>
#include <test/test.hpp>
namespace lt::test {
class FuzzDataProvider
{
public:
FuzzDataProvider(const uint8_t *data, size_t size): m_data(data, size)
{
}
template<typename T>
requires(
std::is_trivially_constructible_v<T> //
&& std::is_trivially_copy_constructible_v<T> //
&& std::is_trivially_copy_assignable_v<T>
)
auto consume() -> std::optional<T>
{
if (m_data.size() < sizeof(T))
{
return std::nullopt;
}
T value;
std::memcpy(&value, m_data.data(), sizeof(T));
m_data = m_data.subspan(sizeof(T));
return value;
}
auto consume_string(size_t size) -> std::optional<std::string>
{
if (m_data.size() < size)
{
return std::nullopt;
}
// NOLINTNEXTLINE
auto value = std::string { (const char *)m_data.data(), size };
m_data = m_data.subspan(size);
return value;
}
auto consume_remaining_as_string() -> std::string
{
if (m_data.empty())
{
return std::string {};
}
return { m_data.begin(), m_data.end() };
};
private:
std::span<const uint8_t> m_data;
};
} // namespace lt::test
namespace lt::test {
auto process_fuzz_input(const uint8_t *data, size_t size) -> int32_t
try
{
return details::Registry::process_fuzz_input(data, size);
}
catch (const std::exception &exp)
{
std::println("Fuzz input resulted in uncaught exception:");
std::println("\twhat: {}", exp.what());
std::println("\tinput size: {}", size);
return EXIT_FAILURE;
}
}; // namespace lt::test
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
return lt::test::process_fuzz_input(data, size);
}

View file

@ -1,23 +0,0 @@
export module test;
export import preliminary;
export import test.test;
export import test.expects;
export import test.expects;
export import logger;
export using ::lt::test::Suite;
export using ::lt::test::Case;
export using ::lt::test::expect_eq;
export using ::lt::test::expect_ne;
export using ::lt::test::expect_le;
export using ::lt::test::expect_true;
export using ::lt::test::expect_false;
export using ::lt::test::expect_throw;
export using ::lt::test::expect_not_nullptr;
export using ::lt::test::expect_unreachable;
export using ::lt::test::operator""_suite;

View file

@ -1,6 +1,6 @@
export module test.registry; export module test.registry;
import preliminary; import std;
import test.expects; import test.expects;
/////////////////////////////////////// ///////////////////////////////////////
@ -11,7 +11,7 @@ namespace lt::test {
export class Registry export class Registry
{ {
public: public:
enum class ExecutionPolicy : u8 enum class ExecutionPolicy : std::uint8_t
{ {
normal, normal,
stats, stats,
@ -28,7 +28,7 @@ public:
std::string case_regex; std::string case_regex;
}; };
using FuzzFunction = i32 (*)(const u8 *, size_t); using FuzzFunction = std::int32_t (*)(const std::uint8_t *, std::size_t);
using SuiteFunction = void (*)(); using SuiteFunction = void (*)();
@ -36,9 +36,9 @@ public:
static void register_fuzz_harness(FuzzFunction suite); static void register_fuzz_harness(FuzzFunction suite);
static auto run_all(Options options) -> i32; static auto run_all(Options options) -> std::int32_t;
static auto process_fuzz_input(const u8 *data, size_t size) -> i32; static auto process_fuzz_input(const std::uint8_t *data, std::size_t size) -> std::int32_t;
static void set_last_suite_name(const char *name); static void set_last_suite_name(const char *name);
@ -73,7 +73,7 @@ private:
[[nodiscard]] static auto instance() -> Registry &; [[nodiscard]] static auto instance() -> Registry &;
auto run_all_impl() -> i32; auto run_all_impl() -> std::int32_t;
void print_options(); void print_options();
@ -83,25 +83,25 @@ private:
FuzzFunction m_fuzz_harness {}; FuzzFunction m_fuzz_harness {};
i32 m_total_case_count {}; std::int32_t m_total_case_count {};
i32 m_passed_case_count {}; std::int32_t m_passed_case_count {};
i32 m_failed_case_count {}; std::int32_t m_failed_case_count {};
i32 m_matched_case_count {}; std::int32_t m_matched_case_count {};
i32 m_skipped_case_count {}; std::int32_t m_skipped_case_count {};
i32 m_total_suite_count {}; std::int32_t m_total_suite_count {};
i32 m_passed_suite_count {}; std::int32_t m_passed_suite_count {};
i32 m_failed_suite_count {}; std::int32_t m_failed_suite_count {};
i32 m_matched_suite_count {}; std::int32_t m_matched_suite_count {};
i32 m_skipped_suite_count {}; std::int32_t m_skipped_suite_count {};
std::regex m_case_regex; std::regex m_case_regex;
}; };
@ -131,13 +131,14 @@ namespace lt::test {
instance().m_fuzz_harness = suite; instance().m_fuzz_harness = suite;
} }
/* static */ auto Registry::run_all(Options options) -> i32 /* static */ auto Registry::run_all(Options options) -> std::int32_t
{ {
instance().m_options = std::move(options); instance().m_options = std::move(options);
return instance().run_all_impl(); return instance().run_all_impl();
} }
/* static */ auto Registry::process_fuzz_input(const u8 *data, size_t size) -> i32 /* static */ auto Registry::process_fuzz_input(const std::uint8_t *data, std::size_t size)
-> std::int32_t
{ {
if (!instance().m_fuzz_harness) if (!instance().m_fuzz_harness)
{ {
@ -219,7 +220,7 @@ namespace lt::test {
return instance().m_case_regex; return instance().m_case_regex;
} }
auto Registry::run_all_impl() -> i32 auto Registry::run_all_impl() -> std::int32_t
{ {
print_options(); print_options();
m_case_regex = std::regex(m_options.case_regex); m_case_regex = std::regex(m_options.case_regex);

View file

@ -1,8 +1,8 @@
export module test.test; export module test.test;
import std;
import test.expects; import test.expects;
import test.registry; import test.registry;
import preliminary;
/////////////////////////////////////// ///////////////////////////////////////
// ----------* INTERFACE *--------- // // ----------* INTERFACE *--------- //
@ -38,7 +38,7 @@ struct TestFuzzHarness
export using Case = const TestCase; export using Case = const TestCase;
export using Suite = const TestSuite; export using Suite = const TestSuite;
export using FuzzHarness = const TestFuzzHarness; export using FuzzHarness = const TestFuzzHarness;
export auto operator""_suite(const char *name, size_t size) -> TestSuite; export auto operator""_suite(const char *name, std::size_t size) -> TestSuite;
/////////////////////////////////////// ///////////////////////////////////////
// * IMPLEMENTATION -- TEMPLATES * // // * IMPLEMENTATION -- TEMPLATES * //
@ -106,11 +106,8 @@ constexpr TestFuzzHarness::TestFuzzHarness(auto body)
#endif #endif
}; };
auto operator""_suite(const char *name, size_t size) -> TestSuite auto operator""_suite(const char *name, std::size_t size) -> TestSuite
{ {
// TODO(Light): do we need the size parameter?
ignore = size;
Registry::set_last_suite_name(name); Registry::set_last_suite_name(name);
return {}; return {};
} }

View file

@ -1,6 +1,21 @@
import test; import test.test;
import test.expects;
import std;
using lt::test::Case;
using lt::test::Suite;
using lt::test::operator""_suite;
Suite expects = "expects"_suite = []() { Suite expects = "expects"_suite = []() {
using lt::test::expect_unreachable;
using lt::test::expect_true;
using lt::test::expect_false;
using lt::test::expect_eq;
using lt::test::expect_ne;
using lt::test::expect_le;
using lt::test::expect_throw;
Case { "" } = [] { Case { "" } = [] {
}; };
@ -9,7 +24,7 @@ Suite expects = "expects"_suite = []() {
// clang-format off // clang-format off
try { expect_unreachable(); } try { expect_unreachable(); }
catch (const std::exception &) { unhappy = true; } catch (const std::exception &exp) { unhappy = true; }
// clang-format on // clang-format on
if (!unhappy) if (!unhappy)
@ -33,16 +48,16 @@ Suite expects = "expects"_suite = []() {
// clang-format off // clang-format off
try { expect_true(where_oongaboonga_ptr); } try { expect_true(where_oongaboonga_ptr); }
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
try { expect_true(!true); } try { expect_true(!true); }
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
try { expect_true(false); } try { expect_true(false); }
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
try { expect_true(0); } // NOLINT try { expect_true(0); } // NOLINT
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
// clang-format on // clang-format on
}; };
@ -51,31 +66,27 @@ Suite expects = "expects"_suite = []() {
expect_false(oongaboonga_is_slacking); expect_false(oongaboonga_is_slacking);
expect_false(false); expect_false(false);
expect_false(0); // NOLINT
}; };
Case { "expect_false - unhappy" } = [] { Case { "expect_false - unhappy" } = [] {
auto *oonga_oonga_can_rest_now = (u32 *)nullptr; auto oongaboonga = int {};
auto *oonga_oonga_can_rest_now = (int *)nullptr;
auto unhappy_counter = 0u; auto unhappy_counter = 0u;
oonga_oonga_can_rest_now = &unhappy_counter;
// clang-format off // clang-format off
try { expect_false(oonga_oonga_can_rest_now); } try { expect_false(oonga_oonga_can_rest_now); }
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
try { expect_false(true); } try { expect_false(true); }
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
try { expect_false(!false); } try { expect_false(!false); }
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
try { expect_false(!!1); } try { expect_false(1); } // NOLINT
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
// clang-format on // clang-format on
if (unhappy_counter != 4)
{
throw std::runtime_error { "expect_false - unhappy" };
}
}; };
Case { "expect_true - unhappy" } = [] { Case { "expect_true - unhappy" } = [] {
@ -84,29 +95,23 @@ Suite expects = "expects"_suite = []() {
// clang-format off // clang-format off
try { expect_true(where_oongaboonga_ptr); } try { expect_true(where_oongaboonga_ptr); }
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
try { expect_true(!true); } try { expect_true(!true); }
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
try { expect_true(false); } try { expect_true(false); }
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
try { expect_true(!!0); } try { expect_true(0); } // NOLINT
catch (const std::exception&) { ++unhappy_counter; } catch (const std::exception& exp) { ++unhappy_counter; }
// clang-format on // clang-format on
if (unhappy_counter != 4)
{
throw std::runtime_error { "expect_true - unhappy" };
}
}; };
Case { "expect_eq - happy" } = [] { Case { "expect_eq - happy" } = [] {
expect_eq(5, 5); expect_eq(5, 5);
expect_eq(20.0, 20.0); expect_eq(20.0, 20.0);
expect_eq(true, true); expect_eq(true, 1);
expect_eq(false, false);
}; };
Case { "expect_eq - unhappy" } = [] { Case { "expect_eq - unhappy" } = [] {
@ -114,7 +119,7 @@ Suite expects = "expects"_suite = []() {
// clang-format off // clang-format off
try { expect_eq(true, false); } try { expect_eq(true, false); }
catch (const std::exception &) { unhappy = true; } catch (const std::exception &exp) { unhappy = true; }
// clang-format on // clang-format on
if (!unhappy) if (!unhappy)
@ -126,8 +131,7 @@ Suite expects = "expects"_suite = []() {
Case { "expect_ne - happy " } = [] { Case { "expect_ne - happy " } = [] {
expect_ne(5, 5.0000001); expect_ne(5, 5.0000001);
expect_ne(20.0, 69.0); expect_ne(20.0, 69.0);
expect_ne(true, false); expect_ne(true, 0);
expect_ne(false, true);
}; };
Case { "expect_ne - unhappy" } = [] { Case { "expect_ne - unhappy" } = [] {
@ -135,19 +139,16 @@ Suite expects = "expects"_suite = []() {
// clang-format off // clang-format off
try { expect_ne(5, 5); } try { expect_ne(5, 5); }
catch (const std::exception &) { ++unhappy_counter; } catch (const std::exception &exp) { ++unhappy_counter; }
try { expect_ne(20.0, 20.0); } try { expect_ne(20.0, 20.0); }
catch (const std::exception &) { ++unhappy_counter; } catch (const std::exception &exp) { ++unhappy_counter; }
try { expect_ne(true, true); } try { expect_ne(true, 1); }
catch (const std::exception &) { ++unhappy_counter; } catch (const std::exception &exp) { ++unhappy_counter; }
try { expect_ne(false, false); }
catch (const std::exception &) { ++unhappy_counter; }
// clang-format on // clang-format on
if (unhappy_counter != 4) if (unhappy_counter != 3)
{ {
throw std::runtime_error { "expect_ne unhappy" }; throw std::runtime_error { "expect_ne unhappy" };
} }
@ -162,7 +163,7 @@ Suite expects = "expects"_suite = []() {
// clang-format off // clang-format off
try { expect_throw([] {}); } try { expect_throw([] {}); }
catch (const std::exception &) { unhappy = true; } catch (const std::exception &exp) { unhappy = true; }
// clang-format on // clang-format on
if (!unhappy) if (!unhappy)
@ -174,7 +175,7 @@ Suite expects = "expects"_suite = []() {
Case { "expect_le - happy" } = [] { Case { "expect_le - happy" } = [] {
expect_le(69, 420); expect_le(69, 420);
expect_le(19.694206942069420, 20.0); expect_le(19.694206942069420, 20.0);
expect_le(false, !!1); expect_le(false, 1);
}; };
Case { "expect_le - unhappy" } = [] { Case { "expect_le - unhappy" } = [] {
@ -182,16 +183,16 @@ Suite expects = "expects"_suite = []() {
// clang-format off // clang-format off
try { expect_le(20020619 + 23, 20020619 ); } try { expect_le(20020619 + 23, 20020619 ); }
catch (const std::exception &) { ++unhappy_counter; } catch (const std::exception &exp) { ++unhappy_counter; }
try { expect_le(420, 69); } try { expect_le(420, 69); }
catch (const std::exception &) { ++unhappy_counter; } catch (const std::exception &exp) { ++unhappy_counter; }
try { expect_le(20.0, 19.694206942069420); } try { expect_le(20.0, 19.694206942069420); }
catch (const std::exception &) { ++unhappy_counter; } catch (const std::exception &exp) { ++unhappy_counter; }
try { expect_le(true, false); } try { expect_le(1, false); }
catch (const std::exception &) { ++unhappy_counter; } catch (const std::exception &exp) { ++unhappy_counter; }
// clang-format on // clang-format on
if (unhappy_counter != 4) if (unhappy_counter != 4)

View file

@ -1,16 +1,15 @@
export module time; export module time;
import std;
import preliminary; namespace lt::time {
export namespace lt::time {
/** Simple timer class to keep track of the elapsed time. */ /** Simple timer class to keep track of the elapsed time. */
class Timer export class Timer
{ {
public: public:
using Clock = std::chrono::steady_clock; using Clock = std::chrono::steady_clock;
using Duration = std::chrono::duration<f64>; using Duration = std::chrono::duration<double>;
using Timepoint = std::chrono::time_point<std::chrono::steady_clock>; using Timepoint = std::chrono::time_point<std::chrono::steady_clock>;

View file

@ -1,25 +1,33 @@
import test;
import time; import time;
import test.test;
import test.expects;
import std;
using ::lt::test::Case;
using ::lt::test::expect_le;
using ::lt::test::operator""_suite;
using ::lt::test::Suite;
using ::lt::time::Timer; using ::lt::time::Timer;
/* @note: error margin is high since run-time may slow down extremely due to // error margin is high since run-time may slow down extremely due to
* sanitization/debugging or execution through valgrind... // sanitization/debugging or execution through valgrind...
* <1us error margin is tested manually in release builds and it works fine. //
**/ // <1us error margin is tested manually in release builds and it works fine.
constexpr auto max_error_margin = std::chrono::milliseconds { 1 }; constexpr auto max_error_margin = std::chrono::milliseconds { 1 };
Suite raii = "raii"_suite = [] { Suite raii = "raii"_suite = [] {
using std::chrono::microseconds; using std::chrono::microseconds;
Case { "happy paths" } = [] { Case { "default" } = [] {
Timer {}; Timer {};
}; };
Case { "many" } = [] { Case { "unhappy path throws" } = [] {
};
Case { "plenty" } = [] {
for (auto idx : std::views::iota(0, 100'001)) for (auto idx : std::views::iota(0, 100'001))
{ {
ignore = idx;
Timer {}; Timer {};
} }
}; };
@ -29,9 +37,9 @@ Suite reset_and_elapsed_time = "reset_and_elapsed_time"_suite = [] {
using std::chrono::hours; using std::chrono::hours;
using std::chrono::microseconds; using std::chrono::microseconds;
Case { "happy path" } = [] { Case { "won't throw" } = [] {
Timer {}.reset(); Timer {}.reset();
ignore = Timer {}.elapsed_time(); std::ignore = Timer {}.elapsed_time();
}; };
Case { "elapsed time is sane" } = [] { Case { "elapsed time is sane" } = [] {

View file

@ -1,15 +1,5 @@
# @ref https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category conan profile detect
conan build . -s compiler.cppstd=20 -s build_type=Release
rm -Force -Recurse ./build
cmake `
-S . `
-B build `
-G Ninja `
-D ENABLE_UNIT_TESTS=ON `
-D CMAKE_BUILD_TYPE=Release `
-D CMAKE_CXX_FLAGS="/std:c++latest /EHsc /Zi /Oy- /WX /W4"
cmake --build ./build
$tests = Get-ChildItem -Path "./build" -Recurse -File | Where-Object { $tests = Get-ChildItem -Path "./build" -Recurse -File | Where-Object {
$_.Name -like "*_tests.exe" $_.Name -like "*_tests.exe"