Compare commits

...

3 commits

Author SHA1 Message Date
1df42cf30d
wip: convert from include style to module import style :D
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-11-09 17:16:57 +03:30
b4fe163f15
wip: convert from include style to module import style :D 2025-11-09 12:31:58 +03:30
a7be2fa370
wip: convert from include style to module import style :D 2025-11-09 12:05:50 +03:30
83 changed files with 3025 additions and 2752 deletions

View file

@ -1,37 +1,175 @@
# engine add_subdirectory(./std)
add_library_module(
NAME
test
INTERFACES
test.cppm
expects.cppm
PRIVATE_INTERFACES
registry.cppm
SOURCES
entrypoint.cpp
DEPENDENCIES
logger
)
add_subdirectory(test)
add_library_module(NAME logger INTERFACES logger.cppm)
add_subdirectory(./logger)
add_library_module(NAME bitwise INTERFACES operations.cppm)
add_subdirectory(./bitwise)
add_library_module(NAME env INTERFACES constants.cppm)
add_subdirectory(./env)
add_library_module(
NAME memory INTERFACES null_on_move.cppm reference.cppm scope.cppm
)
add_subdirectory(./memory)
add_library_module(NAME time INTERFACES timer.cppm)
add_subdirectory(./time)
add_library_module(
NAME
lt_debug
ROOT_DIR
${CMAKE_CURRENT_SOURCE_DIR}/debug
INTERFACES
instrumentor.cppm
assertions.cppm
DEPENDENCIES
logger
)
add_subdirectory(./debug)
add_library_module(
NAME
math
INTERFACES
algebra.cppm
mat4.cppm
trig.cppm
vec2.cppm
vec3.cppm
vec4.cppm
components.cppm
)
add_subdirectory(./math)
add_library_module(
NAME
assets
INTERFACES
shader.cppm
metadata.cppm
DEPENDENCIES
logger
lt_debug
)
add_subdirectory(./assets)
add_library_module(
NAME
libasset_baker
ROOT_DIR
${CMAKE_CURRENT_SOURCE_DIR}/asset_baker
INTERFACES
bakers.cppm
DEPENDENCIES
assets
logger
lt_debug
)
add_subdirectory(./asset_baker)
# add_executable(asset_baker entrypoint.cpp) target_link_libraries(asset_baker
# PRIVATE libasset_baker)
add_subdirectory(./camera)
add_library_module(NAME camera INTERFACES components.cppm DEPENDENCIES math)
add_subdirectory(./app)
add_subdirectory(./ecs)
add_library_module(
NAME
app
INTERFACES
application.cppm
system.cppm
SOURCES
entrypoint.cpp
DEPENDENCIES
memory
PRIVATE_DEPENDENCIES
lt_debug
)
add_subdirectory(./surface)
add_library_module(
NAME
ecs
INTERFACES
sparse_set.cppm
registry.cppm
entity.cppm
DEPENDENCIES
logger
lt_debug
memory
)
add_subdirectory(./input)
if(NOT WIN32)
add_library_module(
NAME
surface
INTERFACES
system.cppm
requests.cppm
events.cppm
components.cppm
SOURCES
platform_linux.cpp
DEPENDENCIES
ecs
app
math
memory
tbb
PRIVATE_DEPENDENCIES
X11
logger
lt_debug
time
)
else()
# add_subdirectory(./ui)
endif()
# add_subdirectory(./renderer)
add_library_module(
NAME
input
INTERFACES
system.cppm
codes.cppm
components.cppm
events.cppm
DEPENDENCIES
surface
math
logger
)
add_library_module(
NAME
renderer
INTERFACES
frontends.cppm
factory.cppm
vk/api_wrapper.cppm
vk/device.cppm
vk/gpu.cppm
vk/instance.cppm
vk/surface.cppm
vk/swapchain.cppm
vk/buffer.cppm
vk/pass.cppm
DEPENDENCIES
app
ecs
memory
assets
time
bitwise
camera
PRIVATE_DEPENDENCIES
surface
)
#
# add_subdirectory(./mirror)

View file

@ -1,13 +0,0 @@
add_library_module(
NAME
app
INTERFACES
application.cppm
system.cppm
SOURCES
entrypoint.cpp)
target_link_libraries(
app
PUBLIC memory
PRIVATE lt_debug)

View file

@ -1,5 +0,0 @@
add_library_module(NAME libasset_baker INTERFACES bakers.cppm)
target_link_libraries(libasset_baker PUBLIC assets logger lt_debug)
add_executable(asset_baker entrypoint.cpp)
target_link_libraries(asset_baker PRIVATE libasset_baker)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,2 +0,0 @@
add_library_module(NAME lt_debug INTERFACES instrumentor.cppm assertions.cppm)
target_link_libraries(lt_debug PUBLIC logger)

View file

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

View file

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

View file

@ -1,11 +0,0 @@
add_library_module(
NAME
input
INTERFACES
system.cppm
codes.cppm
components.cppm
events.cppm)
target_link_libraries(input PUBLIC surface math logger)
# add_test_module(input system.test.cpp)

View file

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

View file

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

View file

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

View file

@ -9,7 +9,7 @@ namespace lt::memory {
* @note For avoiding the need to explicitly implement the move constructor for objects that hold
* Vulkan objects. But may serve other purposes, hence why I kept the implementation generic.
*/
template<typename Underlying_T, Underlying_T null_value = nullptr>
export template<typename Underlying_T, Underlying_T null_value = nullptr>
class NullOnMove
{
public:
@ -84,7 +84,12 @@ public:
return (std::uint64_t)m_value;
}
[[nodiscard]] auto get() -> Underlying_T
[[nodiscard]] auto get() -> Underlying_T &
{
return m_value;
}
[[nodiscard]] auto get() const -> const Underlying_T &
{
return m_value;
}

View file

@ -1,10 +0,0 @@
add_library_module(libmirror)
target_link_libraries(libmirror INTERFACE app time input surface renderer
camera)
add_test_module(
libmirror layers/editor_layer.test.cpp panels/asset_browser.test.cpp
panels/properties.test.cpp panels/scene_hierarchy.test.cpp)
add_executable_module(mirror entrypoint/mirror.cpp)
target_link_libraries(mirror PRIVATE libmirror input)

View file

@ -1,3 +1,64 @@
#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>

View file

@ -1,3 +1,51 @@
#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>

View file

@ -1,3 +1,36 @@
#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>

View file

@ -1,3 +1,42 @@
#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>

View file

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

View file

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

View file

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

View file

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

View file

@ -1,248 +0,0 @@
---
Checks: "-*,
performance-unnecessary-value-param,
performance-unnecessary-copy-initialization,
performance-type-promotion-in-math-fn,
performance-trivially-destructible,
performance-noexcept-swap,
performance-noexcept-move-constructor,
performance-noexcept-destructor,
performance-no-int-to-ptr,
performance-no-automatic-move,
performance-move-constructor-init,
performance-move-const-arg,
performance-inefficient-vector-operation,
performance-inefficient-string-concatenation,
performance-inefficient-algorithm,
performance-implicit-conversion-in-loop,
performance-for-range-copy,
performance-faster-string-find,
performance-enum-size,
performance-avoid-endl,
readability-avoid-const-params-in-decls,
readability-avoid-nested-conditional-operator,
readability-avoid-return-with-void-value,
readability-avoid-unconditional-preprocessor-if,
readability-braces-around-statements,
readability-const-return-type,
readability-container-contains,
readability-container-data-pointdr,
readability-container-size-empty,
readability-delete-null-pointer,
readability-duplicate-include,
readability-else-after-return,
readability-inconsistent-declaration-parameter-name,
readability-isolate-declaration,
readability-make-member-function-const,
readability-misleading-indentation,
readability-misplaced-array-index,
readability-named-parameter,
readability-non-const-parameter,
readability-qualified-auto,
readability-redundant-access-specifiers,
readability-redundant-casting,
readability-redundant-control-flow,
readability-redundant-declaration,
readability-redundant-function-ptr-dereference,
readability-redundant-inline-specifier,
readability-redundant-member-init,
readability-redundant-preprocessor,
readability-redundant-smartptr-get,
readability-redundant-string-cstr,
readability-reference-to-constructed-temporary,
readability-simplify-boolean-expr,
readability-simplify-subscript-expr,
readability-static-accessed-through-instance,
readability-static-definition-in-anonymous-namespace,
readability-string-compare,
readability-suspicious-call-argument,
readability-uniqueptr-delete-release,
readability-use-anyofallof
readability-use-std-min-max,
readability-function-cognitive-complexity
readability-function-size
readability-identifier-naming
readability-identifier-length
readability-magic-numbers
modernize-avoid-bind,
modernize-avoid-c-arrays,
modernize-concat-nested-namespaces,
modernize-deprecated-headers,
modernize-deprecated-ios-base-aliases,
modernize-loop-convert,
modernize-macro-to-enum,
modernize-make-shared,
modernize-make-unique,
modernize-pass-by-value,
modernize-raw-string-literal,
modernize-redundant-void-arg,
modernize-replace-auto-ptr,
modernize-replace-disallow-copy-and-assign-macro,
modernize-replace-random-shuffle,
modernize-return-braced-init-list,
modernize-shrink-to-fit,
modernize-type-traits,
modernize-unary-static-assert,
modernize-use-auto,
modernize-use-bool-literals,
modernize-use-constraints,
modernize-use-default-member-init,
modernize-use-designated-initializers,
modernize-use-emplace,
modernize-use-equals-default,
modernize-use-equals-delete,
modernize-use-nodiscard,
modernize-use-noexcept,
modernize-use-nullptr,
modernize-use-override,
modernize-use-starts-ends-with,
modernize-use-std-numbers,
modernize-use-std-print,
modernize-use-transparent-functors,
modernize-use-uncaught-exceptions,
modernize-use-using
modernize-min-max-use-initializer-list,
cppcoreguidelines-avoid-capturing-lambda-coroutines,
cppcoreguidelines-avoid-const-or-ref-data-members,
cppcoreguidelines-avoid-do-while,
cppcoreguidelines-avoid-goto,
cppcoreguidelines-avoid-non-const-global-variables,
cppcoreguidelines-avoid-reference-coroutine-parameters,
cppcoreguidelines-init-variables,
cppcoreguidelines-interfaces-global-init,
cppcoreguidelines-macro-usage,
cppcoreguidelines-misleading-capture-default-by-value,
cppcoreguidelines-missing-std-forward,
cppcoreguidelines-narrowing-conversions,
cppcoreguidelines-no-malloc,
cppcoreguidelines-no-suspend-with-lock,
cppcoreguidelines-owning-memory,
cppcoreguidelines-prefer-member-initializer,
cppcoreguidelines-pro-bounds-array-to-pointer-decay,
cppcoreguidelines-pro-bounds-pointer-arithmetic,
cppcoreguidelines-pro-type-const-cast,
cppcoreguidelines-pro-type-cstyle-cast,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-pro-type-vararg,
cppcoreguidelines-rvalue-reference-param-not-moved,
cppcoreguidelines-slicing,
cppcoreguidelines-special-member-functions,
cppcoreguidelines-virtual-class-destructor,
bugprone-argument-comment,
bugprone-assert-side-effect,
bugprone-assignment-in-if-condition,
bugprone-bad-signal-to-kill-thread,
bugprone-bool-pointer-implicit-conversion,
bugprone-branch-clone,
bugprone-casting-through-void,
bugprone-chained-comparison,
bugprone-compare-pointer-to-member-virtual-function,
bugprone-copy-constructor-init,
bugprone-crtp-constructor-accessibility,
bugprone-dangling-handle,
bugprone-empty-catch,
bugprone-exception-escape,
bugprone-fold-init-type,
bugprone-forward-declaration-namespace,
bugprone-forwarding-reference-overload,
bugprone-implicit-widening-of-multiplication-result,
bugprone-inaccurate-erase,
bugprone-inc-dec-in-conditions,
bugprone-incorrect-enable-if,
bugprone-incorrect-roundings,
bugprone-infinite-loop,
bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-arithmetic-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multi-level-implicit-pointer-conversion,
bugprone-multiple-new-in-one-expression,
bugprone-multiple-statement-macro,
bugprone-no-escape,
bugprone-non-zero-enum-to-bool-conversion,
bugprone-not-null-terminated-result,
bugprone-optional-value-conversion,
bugprone-parent-virtual-call,
bugprone-posix-return,
bugprone-redundant-branch-condition,
bugprone-reserved-identifier,
bugprone-return-const-ref-from-parameter,
bugprone-shared-ptr-array-mismatch,
bugprone-signal-handler,
bugprone-signed-char-misuse,
bugprone-sizeof-container,
bugprone-sizeof-expression,
bugprone-spuriously-wake-up-functions,
bugprone-standalone-empty,
bugprone-string-constructor,
bugprone-string-integer-assignment,
bugprone-string-literal-with-embedded-nul,
bugprone-stringview-nullptr,
bugprone-suspicious-enum-usage,
bugprone-suspicious-include,
bugprone-suspicious-memory-comparison,
bugprone-suspicious-memset-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-realloc-usage,
bugprone-suspicious-semicolon,
bugprone-suspicious-string-compare,
bugprone-suspicious-stringview-data-usage,
bugprone-swapped-arguments,
bugprone-switch-missing-default-case,
bugprone-terminating-continue,
bugprone-throw-keyword-missing,
bugprone-too-small-loop-variable,
bugprone-unchecked-optional-access,
bugprone-undefined-memory-manipulation,
bugprone-undelegated-constructor,
bugprone-unhandled-exception-at-new,
bugprone-unhandled-self-assignment,
bugprone-unique-ptr-array-mismatch,
bugprone-unsafe-functions,
bugprone-unused-local-non-trivial-variable,
bugprone-unused-raii,
bugprone-unused-return-value,
bugprone-use-after-move,
bugprone-virtual-near-miss,
concurrency-mt-unsafe,
concurrency-thread-canceltype-asynchronous,
misc-use-anonymous-namespace,
misc-unused-using-decls,
misc-unused-parameters,
misc-unused-alias-decls,
misc-uniqueptr-reset-release,
misc-unconventional-assign-operator,
misc-throw-by-value-catch-by-reference,
misc-static-assert,
misc-redundant-expression,
misc-non-private-member-variables-in-classes,
misc-non-copyable-objects,
misc-no-recursion,
misc-new-delete-overloads,
misc-misplaced-const,
misc-misleading-identifier,
misc-misleading-bidirectional,
misc-header-include-cycle,
misc-definitions-in-headers,
misc-coroutine-hostile-raii,
misc-const-correctness,
-hicpp-signed-bitwise,
hicpp-no-assembler,
hicpp-multiway-paths-covered,
hicpp-ignored-remove-result,
hicpp-exception-baseclass,
"

View file

@ -1,52 +0,0 @@
add_library_module(
NAME
renderer
INTERFACES
# system.cppm Vulkan - backend
api.cppm
frontends.cppm
factory.cppm
# backends/vk/messenger.cppm backends/vk/library_loader.cppm
backends/vk/library_wrapper.cppm
backends/vk/context/device.cppm
backends/vk/context/gpu.cppm
backends/vk/context/instance.cppm
backends/vk/context/surface.cppm
# backends/vk/context/swapchain.cppm backends/vk/data/buffer.cppm
# backends/vk/renderer/pass.cppm backends/vk/renderer/renderer.cppm frontend
# frontend/messenger.cppm frontend/context/device.cppm
# frontend/context/gpu.cppm frontend/context/instance.cppm
# frontend/context/surface.cppm frontend/context/swapchain.cppm
# frontend/data/buffer.cppm frontend/renderer/renderer.cppm
# frontend/renderer/pass.cppm
)
target_link_libraries(
renderer
PUBLIC app
ecs
memory
assets
time
bitwise
camera
PRIVATE surface pthread)
return()
add_test_module(
renderer
test/utils.cpp
system.test.cpp
# general backend tests through the frontend
frontend/messenger.test.cpp
frontend/context/surface.test.cpp
frontend/context/device.test.cpp
frontend/context/swapchain.test.cpp
frontend/data/buffer.test.cpp
# frontend/renderer/pass.test.cpp
frontend/renderer/renderer.test.cpp
# backend specific tests -- vk
backend/vk/context/instance.test.cpp)
target_link_libraries(renderer_tests PRIVATE surface pthread)

View file

@ -1,14 +0,0 @@
export module renderer.api;
import std;
namespace lt::renderer {
export enum class Api: std::uint8_t {
none = 0u,
vulkan,
direct_x,
metal,
};
}

View file

@ -1,246 +0,0 @@
#pragma once
#include <memory/pointer_types/null_on_move.hpp>
#include <renderer/backend/vk/utils.hpp>
#include <renderer/backend/vk/vulkan.hpp>
#include <renderer/frontend/context/device.hpp>
#include <renderer/frontend/context/gpu.hpp>
#include <renderer/frontend/context/surface.hpp>
#include <renderer/frontend/context/swapchain.hpp>
namespace lt::renderer::vk {
class Swapchain: public ISwapchain
{
public:
Swapchain(ISurface *surface, IGpu *gpu, IDevice *device);
~Swapchain() override;
Swapchain(Swapchain &&) = default;
Swapchain(const Swapchain &) = delete;
auto operator=(Swapchain &&) -> Swapchain & = default;
auto operator=(const Swapchain &) const -> Swapchain & = delete;
[[nodiscard]] auto vk() const -> VkSwapchainKHR
{
return m_swapchain;
}
[[nodiscard]] auto vk_ptr() -> VkSwapchainKHR *
{
return &m_swapchain;
}
[[nodiscard]] auto get_resolution() const -> VkExtent2D
{
return m_resolution;
}
[[nodiscard]] auto get_format() const -> VkFormat
{
return m_format;
}
[[nodiscard]] auto get_image_count() const -> size_t
{
return m_images.size();
}
[[nodiscard]] auto get_image_view(uint32_t idx) -> VkImageView
{
return m_image_views[idx];
}
[[nodiscard]] auto get_image(uint32_t idx) -> VkImage
{
return m_images[idx];
}
[[nodiscard]] auto create_framebuffers_for_pass(VkRenderPass pass) const
-> std::vector<VkFramebuffer>;
private:
[[nodiscard]] auto get_optimal_image_count(
VkSurfaceCapabilitiesKHR capabilities,
uint32_t desired_image_count
) const -> uint32_t;
memory::NullOnMove<class Surface *> m_surface {};
class Gpu *m_gpu {};
class Device *m_device {};
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
std::vector<VkImage> m_images;
std::vector<VkImageView> m_image_views;
VkExtent2D m_resolution {};
VkFormat m_format {};
};
} // namespace lt::renderer::vk
#include <logger/logger.hpp>
#include <ranges>
#include <renderer/backend/vk/context/device.hpp>
#include <renderer/backend/vk/context/gpu.hpp>
#include <renderer/backend/vk/context/instance.hpp>
#include <renderer/backend/vk/context/surface.hpp>
#include <renderer/backend/vk/context/swapchain.hpp>
#include <renderer/backend/vk/utils.hpp>
namespace lt::renderer::vk {
Swapchain::Swapchain(ISurface *surface, IGpu *gpu, IDevice *device)
: m_surface(static_cast<Surface *>(surface))
, m_gpu(static_cast<Gpu *>(gpu))
, m_device(static_cast<Device *>(device))
{
static auto idx = 0u;
const auto capabilities = m_gpu->get_surface_capabilities(m_surface->vk());
const auto formats = m_gpu->get_surface_formats(m_surface->vk());
// TODO(Light): parameterize
constexpr auto desired_image_count = uint32_t { 3 };
const auto surface_format = formats.front();
const auto queue_indices = m_device->get_family_indices();
m_format = surface_format.format;
m_swapchain = m_device->create_swapchain(
VkSwapchainCreateInfoKHR {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = m_surface->vk(),
.minImageCount = get_optimal_image_count(capabilities, desired_image_count),
.imageFormat = surface_format.format,
.imageColorSpace = surface_format.colorSpace,
.imageExtent = capabilities.currentExtent,
.imageArrayLayers = 1u,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = queue_indices.size(),
.pQueueFamilyIndices = queue_indices.data(),
.preTransform = capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = VK_PRESENT_MODE_FIFO_KHR, // TODO(Light): parameterize
.clipped = VK_TRUE,
.oldSwapchain = nullptr,
}
);
m_resolution = capabilities.currentExtent;
m_device->name(m_swapchain, "swapchain {}", idx++);
m_device->wait_idle();
m_images = m_device->get_swapchain_images(m_swapchain);
m_image_views.resize(m_images.size());
for (auto idx = 0u; auto [image, view] : std::views::zip(m_images, m_image_views))
{
view = m_device->create_image_view(VkImageViewCreateInfo {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = surface_format.format,
.components = VkComponentMapping {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = VkImageSubresourceRange {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0u,
.levelCount = 1u,
.baseArrayLayer = 0u,
.layerCount = 1u,
}
});
m_device->name(image, "swapchain image {}", idx++);
m_device->name(view, "swapchain image view {}", idx++);
}
m_device->wait_idle();
}
Swapchain::~Swapchain()
{
if (!m_surface)
{
return;
}
try
{
m_device->wait_idle();
m_device->destroy_image_views(m_image_views);
m_device->destroy_swapchain(m_swapchain);
m_device->wait_idle();
}
catch (const std::exception &exp)
{
log::error("Failed to destroy swapchain:");
log::error("\twhat: {}", exp.what());
}
}
[[nodiscard]] auto Swapchain::create_framebuffers_for_pass(VkRenderPass pass) const
-> std::vector<VkFramebuffer>
{
auto framebuffers = std::vector<VkFramebuffer>(m_image_views.size());
for (auto idx = 0u; auto &framebuffer : framebuffers)
{
framebuffer = m_device->create_framebuffer(
VkFramebufferCreateInfo {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = pass,
.attachmentCount = 1u,
.pAttachments = &m_image_views[idx++],
.width = m_resolution.width,
.height = m_resolution.height,
.layers = 1u,
}
);
}
return framebuffers;
}
[[nodiscard]] auto Swapchain::get_optimal_image_count(
VkSurfaceCapabilitiesKHR capabilities,
uint32_t desired_image_count
) const -> uint32_t
{
const auto min_image_count = capabilities.minImageCount;
const auto max_image_count = capabilities.maxImageCount;
const auto has_max_limit = max_image_count != 0;
// Desired image count is in range
if ((!has_max_limit || max_image_count >= desired_image_count)
&& min_image_count <= desired_image_count)
{
return desired_image_count;
}
// Fall-back to 2 if in ange
if (min_image_count <= 2 && max_image_count >= 2)
{
return 2;
}
// Fall-back to min_image_count
return min_image_count;
}
} // namespace lt::renderer::vk

View file

@ -1,168 +0,0 @@
#pragma once
#include <renderer/backend/vk/raii/raii.hpp>
#include <renderer/frontend/data/buffer.hpp>
namespace lt::renderer::vk {
class Buffer: public IBuffer
{
public:
Buffer(class IDevice *device, class IGpu *gpu, const CreateInfo &info);
[[nodiscard]] auto map() -> std::span<std::byte> override;
void unmap() override;
// TODO(Light): this is to make copying possible.
// But it should be removed in the future,
// Right now it's not possible because: buffers can't understand CommandBuffers.
// And I'm not sure how to properly abstract over command buffers,
// before using other APIs...
[[nodiscard]] auto vk()
{
return *m_buffer;
}
[[nodiscard]] auto get_size() const -> size_t override
{
return m_size;
}
private:
[[nodiscard]] auto determine_allocation_info(Usage usage) const -> VkMemoryAllocateInfo;
[[nodiscard]] auto to_native_usage_flags(Usage usage) const -> VkBufferUsageFlags;
[[nodiscard]] auto to_native_memory_properties(Usage usage) const -> VkMemoryPropertyFlags;
[[nodiscard]] auto has_correct_memory_type_bit(uint32_t type_bits, uint32_t type_idx) const
-> bool;
[[nodiscard]] auto has_required_memory_properties(
uint32_t required_properties,
uint32_t property_flags
) const -> bool;
Device *m_device {};
Gpu *m_gpu {};
raii::Buffer m_buffer;
raii::Memory m_memory;
// TODO(Light): should this reflect the allocation size instead?
size_t m_size {};
};
} // namespace lt::renderer::vk
#include <renderer/backend/vk/context/device.hpp>
#include <renderer/backend/vk/context/gpu.hpp>
#include <renderer/backend/vk/data/buffer.hpp>
namespace lt::renderer::vk {
Buffer::Buffer(IDevice *device, IGpu *gpu, const CreateInfo &info)
: m_device(static_cast<Device *>(device))
, m_gpu(static_cast<Gpu *>(gpu))
, m_buffer(
m_device,
VkBufferCreateInfo {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = info.size,
.usage = to_native_usage_flags(info.usage),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
}
)
, m_memory(m_device, m_buffer, determine_allocation_info(info.usage))
, m_size(info.size)
{
}
[[nodiscard]] auto Buffer::map() -> std::span<std::byte> /* override */
{
return m_device->map_memory(m_memory, m_size, 0ul);
}
void Buffer::unmap() /* override */
{
m_device->unmap_memory(m_memory);
}
[[nodiscard]] auto Buffer::determine_allocation_info(Usage usage) const -> VkMemoryAllocateInfo
{
const auto requirements = m_device->get_memory_requirements(m_buffer);
auto memory_properties = m_gpu->get_memory_properties();
const auto required_properties = to_native_memory_properties(usage);
auto type = 0u;
for (auto idx = 0; idx < memory_properties.memoryTypeCount; ++idx)
{
const auto property_flags = memory_properties.memoryTypes[idx].propertyFlags;
if (has_correct_memory_type_bit(requirements.memoryTypeBits, idx)
&& has_required_memory_properties(required_properties, property_flags))
{
type = idx;
break;
}
}
return VkMemoryAllocateInfo {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = requirements.size,
.memoryTypeIndex = type,
};
}
[[nodiscard]] auto Buffer::to_native_usage_flags(Usage usage) const -> VkBufferUsageFlags
{
switch (usage)
{
case Usage::vertex: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
case Usage::index: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
case Usage::storage:
return VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
case Usage::staging: return VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
}
std::unreachable();
}
[[nodiscard]] auto Buffer::to_native_memory_properties(Usage usage) const -> VkMemoryPropertyFlags
{
switch (usage)
{
case Usage::vertex:
case Usage::index:
case Usage::storage: return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
case Usage::staging:
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
std::unreachable();
}
[[nodiscard]] auto Buffer::has_correct_memory_type_bit(uint32_t type_bits, uint32_t type_idx) const
-> bool
{
return type_bits & (1 << type_idx);
}
[[nodiscard]] auto Buffer::has_required_memory_properties(
uint32_t required_properties,
uint32_t property_flags
) const -> bool
{
return (property_flags & required_properties) == required_properties;
}
} // namespace lt::renderer::vk

View file

@ -1,102 +0,0 @@
module;
#define VK_NO_PROTOTYPES
#define VK_USE_PLATFORM_XLIB_KHR
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_core.h>
#include <vulkan/vulkan_xlib.h>
export module renderer.backend.vk.library_wrapper;
import std;
// import renderer.backend.vk.library_loader;
namespace lt::renderer::vk {
class Device
{
public:
struct CreateInfo
{
std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
std::vector<std::string> layers;
std::vector<std::string> extensions;
VkPhysicalDeviceFeatures2 features;
};
Device() = default;
Device(CreateInfo info)
{
}
~Device()
{
if (m_device)
{
vk_destroy_device(m_device, nullptr);
}
}
private:
VkDevice m_device {};
};
}; // namespace lt::renderer::vk
export namespace lt::renderer::vk {
using Version_T = uint32_t;
struct ApplicationInfo
{
std::string_view name;
Version_T version;
std::string_view *engine_name;
Version_T engine_version;
Version_T api_version;
};
[[nodiscard]]
auto enumerate_instance_extension_properties() -> std::vector<VkExtensionProperties>
{
auto count = 0u;
vkc(vk_enumerate_instance_extension_properties(nullptr, &count, nullptr));
auto extensions = std::vector<VkExtensionProperties>(count);
std::memset(extensions.data(), 0, extensions.size() * sizeof(VkExtensionProperties));
vkc(vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data()));
return extensions;
}
struct VkInstanceCreateInfo
{
VkInstanceCreateFlags flags;
ApplicationInfo application_info;
std::vector<const char **> layers;
std::vector<const char **> extensions;
const void *next;
};
class Instance
{
};
[[nodiscard]] auto create_instance() -> VkInstance
{
return VkInstanceCreateInfo
{
}
}
} // namespace lt::renderer::vk

View file

@ -1,296 +0,0 @@
#include <renderer/backend/vk/context/device.hpp>
#include <renderer/backend/vk/context/swapchain.hpp>
#include <renderer/backend/vk/renderer/pass.hpp>
#include <renderer/data/frame_constants.hpp>
namespace lt::renderer::vk {
Pass::Pass(
IDevice *device,
ISwapchain *swapchain,
const lt::assets::ShaderAsset &vertex_shader,
const lt::assets::ShaderAsset &fragment_shader
)
: m_device(static_cast<Device *>(device))
{
auto binding = VkDescriptorSetLayoutBinding {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1'000,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
};
const auto descriptor_binding_flags = VkDescriptorBindingFlagsEXT {
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT
| VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT
| VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT
| VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT,
};
constexpr auto descriptor_count = uint32_t { 1'000 };
auto descriptor_binding_flags_info = VkDescriptorSetLayoutBindingFlagsCreateInfoEXT {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT,
.bindingCount = 1,
.pBindingFlags = &descriptor_binding_flags,
};
m_vertices_descriptor_set_layout = m_device->create_descriptor_set_layout(
{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = &descriptor_binding_flags_info,
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT,
.bindingCount = 1u,
.pBindings = &binding,
}
);
auto pool_size = VkDescriptorPoolSize {
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = descriptor_count,
};
m_descriptor_pool = m_device->create_desscriptor_pool(
{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = 1u,
.pPoolSizes = &pool_size,
}
);
auto descriptor_set_variable_descriptor_count_info
= VkDescriptorSetVariableDescriptorCountAllocateInfo {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
.descriptorSetCount = 1u,
.pDescriptorCounts = &descriptor_count,
};
m_vertices_descriptor_set = m_device->allocate_descriptor_set(
VkDescriptorSetAllocateInfo {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pNext = &descriptor_set_variable_descriptor_count_info,
.descriptorPool = m_descriptor_pool,
.descriptorSetCount = 1u,
.pSetLayouts = &m_vertices_descriptor_set_layout,
}
);
m_layout = m_device->create_pipeline_layout(
std::vector<VkDescriptorSetLayout> {
m_vertices_descriptor_set_layout,
},
std::vector<VkPushConstantRange> {
VkPushConstantRange {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0u,
.size = sizeof(FrameConstants),
},
}
);
auto *vertex_module = create_module(
vertex_shader.unpack(lt::assets::ShaderAsset::BlobTag::code)
);
auto *fragment_module = create_module(
fragment_shader.unpack(lt::assets::ShaderAsset::BlobTag::code)
);
auto shader_stages = std::array<VkPipelineShaderStageCreateInfo, 2> {
VkPipelineShaderStageCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vertex_module,
.pName = "main",
},
VkPipelineShaderStageCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = fragment_module,
.pName = "main",
},
};
auto dynamic_states = std::array<VkDynamicState, 2> {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
auto dynamic_state = VkPipelineDynamicStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size()),
.pDynamicStates = dynamic_states.data(),
};
auto vertex_input = VkPipelineVertexInputStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
};
auto input_assembly = VkPipelineInputAssemblyStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
auto viewport_state = VkPipelineViewportStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1u,
.scissorCount = 1u,
};
auto rasterization = VkPipelineRasterizationStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
.lineWidth = 1.0,
};
auto multisampling = VkPipelineMultisampleStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.sampleShadingEnable = VK_FALSE,
.minSampleShading = 1.0,
.pSampleMask = nullptr,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
auto color_blend_attachment = VkPipelineColorBlendAttachmentState {
.blendEnable = VK_FALSE,
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
};
auto color_blend = VkPipelineColorBlendStateCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &color_blend_attachment,
.blendConstants = { 0.0f, 0.0, 0.0, 0.0 },
};
// auto attachment_description = VkAttachmentDescription {
// .format =,
// .samples = VK_SAMPLE_COUNT_1_BIT,
// .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
// .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
// .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
// .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
// .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
// .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
// };
auto color_attachment_ref = VkAttachmentReference {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
auto subpass_description = VkSubpassDescription {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1u,
.pColorAttachments = &color_attachment_ref,
};
auto pass_dependency = VkSubpassDependency {
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0u,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0u,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
};
auto color_format = static_cast<Swapchain *>(swapchain)->get_format();
auto rendering_info = VkPipelineRenderingCreateInfoKHR {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = 1u,
.pColorAttachmentFormats = &color_format,
};
m_pipeline = m_device->create_graphics_pipeline(
VkGraphicsPipelineCreateInfo {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &rendering_info,
.stageCount = static_cast<uint32_t>(shader_stages.size()),
.pStages = shader_stages.data(),
.pVertexInputState = &vertex_input,
.pInputAssemblyState = &input_assembly,
.pViewportState = &viewport_state,
.pRasterizationState = &rasterization,
.pMultisampleState = &multisampling,
.pDepthStencilState = nullptr,
.pColorBlendState = &color_blend,
.pDynamicState = &dynamic_state,
.layout = m_layout,
.renderPass = VK_NULL_HANDLE,
.subpass = 0u,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1,
}
);
// m_framebuffers = static_cast<Swapchain *>(swapchain)->create_framebuffers_for_pass(m_pass);
m_device->destroy_shader_module(vertex_module);
m_device->destroy_shader_module(fragment_module);
}
Pass::~Pass()
{
if (!m_device)
{
return;
}
m_device->wait_idle();
m_device->destroy_descriptor_set_layout(m_vertices_descriptor_set_layout);
m_device->free_descriptor_set(m_descriptor_pool, m_vertices_descriptor_set);
m_device->destroy_descriptor_pool(m_descriptor_pool);
m_device->destroy_framebuffers(m_framebuffers);
m_device->destroy_pipeline(m_pipeline);
// m_device->destroy_pass(m_pass);
m_device->destroy_pipeline_layout(m_layout);
}
void Pass::replace_swapchain(const ISwapchain &swapchain)
{
if (!m_device)
{
return;
}
m_device->wait_idle();
m_device->destroy_framebuffers(m_framebuffers);
// m_framebuffers = static_cast<const Swapchain
// &>(swapchain).create_framebuffers_for_pass(m_pass);
}
auto Pass::create_module(lt::assets::Blob blob) -> VkShaderModule
{
return m_device->create_shader_module(
VkShaderModuleCreateInfo {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = blob.size(),
.pCode = reinterpret_cast<const uint32_t *>(blob.data()) // NOLINT
}
);
}
} // namespace lt::renderer::vk

View file

@ -1,65 +0,0 @@
#pragma once
#include <assets/shader.hpp>
#include <memory/pointer_types/null_on_move.hpp>
#include <renderer/backend/vk/utils.hpp>
#include <renderer/frontend/renderer/pass.hpp>
namespace lt::renderer::vk {
class Pass: public IPass
{
public:
Pass(
class IDevice *device,
class ISwapchain *swapchain,
const lt::assets::ShaderAsset &vertex_shader,
const lt::assets::ShaderAsset &fragment_shader
);
~Pass() override;
Pass(Pass &&) = default;
Pass(const Pass &) = delete;
auto operator=(Pass &&) -> Pass & = default;
auto operator=(const Pass &) -> Pass & = delete;
void replace_swapchain(const ISwapchain &swapchain);
[[nodiscard]] auto get_pipeline() -> VkPipeline
{
return m_pipeline;
}
[[nodiscard]] auto get_layout() -> VkPipelineLayout
{
return m_layout;
}
[[nodiscard]] auto get_framebuffers() -> std::vector<VkFramebuffer> &
{
return m_framebuffers;
}
private:
auto create_module(lt::assets::Blob blob) -> VkShaderModule;
memory::NullOnMove<class Device *> m_device {};
VkPipeline m_pipeline = VK_NULL_HANDLE;
VkPipelineLayout m_layout = VK_NULL_HANDLE;
std::vector<VkFramebuffer> m_framebuffers;
VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE;
VkDescriptorSetLayout m_vertices_descriptor_set_layout;
VkDescriptorSet m_vertices_descriptor_set = VK_NULL_HANDLE;
};
} // namespace lt::renderer::vk

View file

@ -1,94 +0,0 @@
#pragma once
#include <memory/reference.hpp>
#include <ranges>
#include <renderer/backend/vk/context/device.hpp>
#include <renderer/backend/vk/data/buffer.hpp>
#include <renderer/backend/vk/renderer/pass.hpp>
#include <renderer/backend/vk/utils.hpp>
#include <renderer/frontend/data/buffer.hpp>
#include <renderer/frontend/renderer/pass.hpp>
#include <renderer/frontend/renderer/renderer.hpp>
namespace lt::renderer::vk {
class Renderer: public IRenderer
{
public:
Renderer(
class IGpu *gpu,
class IDevice *device,
class ISwapchain *swapchain,
uint32_t max_frames_in_flight
);
~Renderer() override;
Renderer(Renderer &&) = default;
Renderer(const Renderer &) = delete;
auto operator=(Renderer &&) -> Renderer & = default;
auto operator=(const Renderer &) -> Renderer & = delete;
[[nodiscard]] auto frame(uint32_t frame_idx, std::function<void()> submit_scene)
-> Result override;
void replace_swapchain(ISwapchain *swapchain) override;
void set_frame_constants(FrameConstants constants) override
{
m_frame_constants = constants;
}
void submit_sprite(
const components::Sprite &sprite,
const math::components::Transform &transform
) override;
private:
void record_cmd(VkCommandBuffer cmd, uint32_t image_idx);
void map_buffers(uint32_t frame_idx);
void flush_buffers(VkCommandBuffer cmd);
memory::NullOnMove<class Device *> m_device {};
class Swapchain *m_swapchain {};
memory::Ref<class Pass> m_pass;
VkCommandPool m_pool = VK_NULL_HANDLE;
VkCommandPool m_transient_pool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> m_cmds;
std::vector<VkFence> m_frame_fences;
std::vector<VkSemaphore> m_aquire_image_semaphores;
std::vector<VkSemaphore> m_submit_semaphores;
VkExtent2D m_resolution;
uint32_t m_max_frames_in_flight {};
FrameConstants m_frame_constants;
Buffer m_vertex_buffer;
Buffer m_staging_buffer;
size_t m_staging_offset;
std::span<std::byte> m_staging_map;
std::span<components::Sprite::Vertex> m_sprite_vertex_map;
size_t m_current_sprite_idx;
};
} // namespace lt::renderer::vk

View file

@ -1,8 +0,0 @@
#pragma once
#include <renderer/backend/vk/vulkan.hpp>
namespace lt::renderer::vk {
} // namespace lt::renderer::vk

View file

@ -1,10 +1,13 @@
export module renderer.factory;
import renderer.frontend;
import assets.shader;
import renderer.backend.vk.device;
import renderer.vk.pass;
import renderer.backend.vk.instance;
import renderer.backend.vk.swapchain;
import renderer.backend.vk.buffer;
import renderer.backend.vk.gpu;
import renderer.backend.vk.surface;
import renderer.api;
import memory.scope;
import debug.assertions;
import ecs.entity;
@ -22,6 +25,16 @@ export namespace lt::renderer {
[[nodiscard]] auto create_gpu(Api target_api, IInstance *instance) -> memory::Scope<IGpu>;
[[nodiscard]] auto create_swapchain(Api target_api, ISurface *surface, IGpu *gpu, IDevice *device)
-> memory::Scope<ISwapchain>;
[[nodiscard]] auto create_pass(
lt::renderer::Api target_api,
IDevice *device,
const lt::assets::ShaderAsset &vertex_shader,
const lt::assets::ShaderAsset &fragment_shader
) -> memory::Scope<IPass>;
} // namespace lt::renderer
module :private;
@ -81,3 +94,112 @@ using namespace lt::renderer;
case Api::direct_x: throw std::runtime_error { "Invalid API" };
}
}
[[nodiscard]] auto create_swapchain(Api target_api, ISurface *surface, IGpu *gpu, IDevice *device)
-> memory::Scope<ISwapchain>
{
switch (target_api)
{
case Api::vulkan: return memory::create_scope<vkb::Swapchain>(surface, gpu, device);
case Api::none:
case Api::metal:
case Api::direct_x: throw std::runtime_error { "Invalid API" };
}
}
[[nodiscard]] auto create_buffer(
Api target_api,
class IDevice *device,
class IGpu *gpu,
const IBuffer::CreateInfo &info
) -> memory::Scope<IBuffer>
{
debug::ensure(device, "Failed to create renderer::IBuffer: null device");
debug::ensure(gpu, "Failed to create renderer::IBuffer: null gpu");
debug::ensure(info.size > 0, "Failed to create renderer::IBuffer: null size");
switch (target_api)
{
case Api::vulkan: return memory::create_scope<vkb::Buffer>(device, gpu, info);
case Api::none:
case Api::metal:
case Api::direct_x: throw std::runtime_error { "Invalid API" };
}
}
[[nodiscard]] auto create_pass(
lt::renderer::Api target_api,
IDevice *device,
const lt::assets::ShaderAsset &vertex_shader,
const lt::assets::ShaderAsset &fragment_shader
) -> memory::Scope<IPass>
{
debug::ensure(device, "Failed to create renderer::IPass: null device");
switch (target_api)
{
case Api::vulkan:
return memory::create_scope<vkb::Pass>(device, vertex_shader, fragment_shader);
case Api::none:
case Api::metal:
case Api::direct_x: throw std::runtime_error { "Invalid API" };
}
}
// [[nodiscard]] /* static */ auto IRenderer::create(
// Api target_api,
// IGpu *gpu,
// IDevice *device,
// ISwapchain *swapchain,
// uint32_t max_frames_in_flight
// ) -> memory::Scope<IRenderer>
// {
// ensure(gpu, "Failed to create renderer::IRenderer: null gpu");
// ensure(device, "Failed to create renderer::IRenderer: null device");
// ensure(swapchain, "Failed to create renderer::IRenderer: null swapchain");
// ensure(
// std::clamp(max_frames_in_flight, frames_in_flight_lower_limit, frames_in_flight_upper_limit)
// == max_frames_in_flight,
// "Failed to initialize renderer::System: max_frames_in_flight ({}) not within bounds ({} -> "
// "{}) ",
// max_frames_in_flight,
// frames_in_flight_lower_limit,
// frames_in_flight_upper_limit
// );
//
//
// switch (target_api)
// {
// case Api::vulkan:
// return memory::create_scope<vk::Renderer>(gpu, device, swapchain, max_frames_in_flight);
// case Api::none:
// case Api::metal:
// case Api::direct_x: throw std::runtime_error { "Invalid API" };
// }
// }
// [[nodiscard]] /* static */ auto IDebugger::create(
// Api target_api,
// IInstance *instance,
// CreateInfo info
// ) -> memory::Scope<IDebugger>
// {
// debug::ensure(
// info.severities != MessageSeverity::none,
// "Failed to create vk::Messenger: severities == none"
// );
//
// debug::ensure(info.types != MessageType::none, "Failed to create vk::Messenger: types == none");
//
// debug::ensure(info.callback, "Failed to create vk::Messenger: null callback");
//
// switch (target_api)
// {
// case Api::vulkan: return memory::create_scope<vk::Messenger>(instance, std::move(info));
// case Api::none:
// case Api::metal:
// case Api::direct_x: throw std::runtime_error { "Invalid API" };
// }
// }

View file

@ -1,56 +0,0 @@
#pragma once
#include <memory/scope.hpp>
#include <renderer/api.hpp>
namespace lt::renderer {
class ISwapchain
{
public:
[[nodiscard]] static auto create(
Api target_api,
class ISurface *surface,
class IGpu *gpu,
class IDevice *device
) -> memory::Scope<ISwapchain>;
ISwapchain() = default;
virtual ~ISwapchain() = default;
ISwapchain(ISwapchain &&) = default;
ISwapchain(const ISwapchain &) = delete;
auto operator=(ISwapchain &&) -> ISwapchain & = default;
auto operator=(const ISwapchain &) -> ISwapchain & = delete;
};
} // namespace lt::renderer
#include <renderer/backend/vk/context/swapchain.hpp>
#include <renderer/frontend/context/swapchain.hpp>
namespace lt::renderer {
[[nodiscard]] /* static */ auto ISwapchain::create(
Api target_api,
ISurface *surface,
IGpu *gpu,
IDevice *device
) -> memory::Scope<ISwapchain>
{
switch (target_api)
{
case Api::vulkan: return memory::create_scope<vk::Swapchain>(surface, gpu, device);
case Api::none:
case Api::metal:
case Api::direct_x: throw std::runtime_error { "Invalid API" };
}
}
} // namespace lt::renderer

View file

@ -1,27 +0,0 @@
#include <renderer/backend/vk/data/buffer.hpp>
#include <renderer/frontend/data/buffer.hpp>
namespace lt::renderer {
[[nodiscard]] /* static */ auto IBuffer::create(
Api target_api,
class IDevice *device,
class IGpu *gpu,
const CreateInfo &info
) -> memory::Scope<IBuffer>
{
ensure(device, "Failed to create renderer::IBuffer: null device");
ensure(gpu, "Failed to create renderer::IBuffer: null gpu");
ensure(info.size > 0, "Failed to create renderer::IBuffer: null size");
switch (target_api)
{
case Api::vulkan: return memory::create_scope<vk::Buffer>(device, gpu, info);
case Api::none:
case Api::metal:
case Api::direct_x: throw std::runtime_error { "Invalid API" };
}
}
} // namespace lt::renderer

View file

@ -1,66 +0,0 @@
#pragma once
#include <memory/scope.hpp>
#include <renderer/api.hpp>
namespace lt::renderer {
class IBuffer
{
public:
enum class Usage : uint8_t
{
vertex,
index,
storage,
staging,
};
struct CreateInfo
{
Usage usage;
size_t size;
std::string debug_name;
};
struct CopyInfo
{
size_t offset;
size_t size;
};
[[nodiscard]] static auto create(
Api target_api,
class IDevice *device,
class IGpu *gpu,
const CreateInfo &info
) -> memory::Scope<IBuffer>;
IBuffer() = default;
virtual ~IBuffer() = default;
IBuffer(IBuffer &&) = default;
IBuffer(const IBuffer &) = delete;
auto operator=(IBuffer &&) -> IBuffer & = default;
auto operator=(const IBuffer &) -> IBuffer & = delete;
[[nodiscard]] virtual auto map() -> std::span<std::byte> = 0;
virtual void unmap() = 0;
[[nodiscard]] virtual auto get_size() const -> size_t = 0;
private:
};
} // namespace lt::renderer

View file

@ -1,103 +0,0 @@
export module renderer.frontend;
import debug.assertions;
import memory.scope;
import renderer.api;
import bitwise;
import std;
namespace lt::renderer {
export class IMessenger
{
public:
enum class MessageSeverity : std::uint8_t
{
none = 0u,
verbose = bitwise::bit(0u),
info = bitwise::bit(1u),
warning = bitwise::bit(2u),
error = bitwise::bit(3u),
all = verbose | info | warning | error,
};
enum class MessageType : std::uint8_t
{
none = 0u,
general = bitwise::bit(0u),
validation = bitwise::bit(1u),
performance = bitwise::bit(2u),
all = general | validation | performance,
};
struct MessageData
{
std::string message;
};
using Callback_T = std::function<void(
MessageSeverity message_severity,
MessageType message_type,
const MessageData &data,
std::any &user_data
)>;
struct CreateInfo
{
MessageSeverity severities;
MessageType types;
Callback_T callback;
std::any user_data;
};
[[nodiscard]] static auto create(Api target_api, class IInstance *instance, CreateInfo info)
-> memory::Scope<IMessenger>;
IMessenger() = default;
virtual ~IMessenger() = default;
IMessenger(IMessenger &&) = default;
IMessenger(const IMessenger &) = delete;
auto operator=(IMessenger &&) -> IMessenger & = default;
auto operator=(const IMessenger &) -> IMessenger & = delete;
};
} // namespace lt::renderer
module :private;
using namespace lt::renderer;
import renderer.backends.vk.messenger;
[[nodiscard]] /* static */ auto IMessenger::create(
Api target_api,
IInstance *instance,
CreateInfo info
) -> memory::Scope<IMessenger>
{
debug::ensure(
info.severities != MessageSeverity::none,
"Failed to create vk::Messenger: severities == none"
);
debug::ensure(info.types != MessageType::none, "Failed to create vk::Messenger: types == none");
debug::ensure(info.callback, "Failed to create vk::Messenger: null callback");
switch (target_api)
{
case Api::vulkan: return memory::create_scope<vk::Messenger>(instance, std::move(info));
case Api::none:
case Api::metal:
case Api::direct_x: throw std::runtime_error { "Invalid API" };
}
}

View file

@ -1,29 +0,0 @@
#include <assets/shader.hpp>
#include <renderer/backend/vk/renderer/pass.hpp>
#include <renderer/frontend/context/gpu.hpp>
#include <renderer/frontend/renderer/pass.hpp>
namespace lt::renderer {
[[nodiscard]] /* static */ auto IPass::create(
lt::renderer::Api target_api,
IDevice *device,
ISwapchain *swapchain,
const lt::assets::ShaderAsset &vertex_shader,
const lt::assets::ShaderAsset &fragment_shader
) -> memory::Scope<IPass>
{
ensure(device, "Failed to create renderer::IPass: null device");
ensure(swapchain, "Failed to create renderer::IPass: null swapchain");
switch (target_api)
{
case Api::vulkan:
return memory::create_scope<vk::Pass>(device, swapchain, vertex_shader, fragment_shader);
case Api::none:
case Api::metal:
case Api::direct_x: throw std::runtime_error { "Invalid API" };
}
}
} // namespace lt::renderer

View file

@ -1,39 +0,0 @@
#pragma once
#include <renderer/api.hpp>
#include <renderer/frontend/context/swapchain.hpp>
namespace lt::assets {
class ShaderAsset;
}
namespace lt::renderer {
class IPass
{
public:
[[nodiscard]] static auto create(
lt::renderer::Api target_api,
class IDevice *device,
class ISwapchain *swapchain,
const class lt::assets::ShaderAsset &vertex_shader,
const class lt::assets::ShaderAsset &fragment_shader
) -> memory::Scope<IPass>;
IPass() = default;
virtual ~IPass() = default;
IPass(IPass &&) = default;
IPass(const IPass &) = delete;
auto operator=(IPass &&) -> IPass & = default;
auto operator=(const IPass &) -> IPass & = delete;
void replace_swapchain(const ISwapchain &swapchain);
};
} // namespace lt::renderer

View file

@ -1,40 +0,0 @@
#include <memory/scope.hpp>
#include <renderer/api.hpp>
#include <renderer/backend/vk/renderer/renderer.hpp>
#include <renderer/frontend/renderer/renderer.hpp>
namespace lt::renderer {
[[nodiscard]] /* static */ auto IRenderer::create(
Api target_api,
IGpu *gpu,
IDevice *device,
ISwapchain *swapchain,
uint32_t max_frames_in_flight
) -> memory::Scope<IRenderer>
{
ensure(gpu, "Failed to create renderer::IRenderer: null gpu");
ensure(device, "Failed to create renderer::IRenderer: null device");
ensure(swapchain, "Failed to create renderer::IRenderer: null swapchain");
ensure(
std::clamp(max_frames_in_flight, frames_in_flight_lower_limit, frames_in_flight_upper_limit)
== max_frames_in_flight,
"Failed to initialize renderer::System: max_frames_in_flight ({}) not within bounds ({} -> "
"{}) ",
max_frames_in_flight,
frames_in_flight_lower_limit,
frames_in_flight_upper_limit
);
switch (target_api)
{
case Api::vulkan:
return memory::create_scope<vk::Renderer>(gpu, device, swapchain, max_frames_in_flight);
case Api::none:
case Api::metal:
case Api::direct_x: throw std::runtime_error { "Invalid API" };
}
}
} // namespace lt::renderer

View file

@ -1,57 +0,0 @@
#pragma once
#include <math/components/transform.hpp>
#include <memory/scope.hpp>
#include <renderer/api.hpp>
#include <renderer/components/sprite.hpp>
#include <renderer/data/frame_constants.hpp>
namespace lt::renderer {
class IRenderer
{
public:
static constexpr auto frames_in_flight_upper_limit = 5u;
static constexpr auto frames_in_flight_lower_limit = 1u;
enum class Result : uint8_t
{
success = 0,
invalid_swapchain,
error,
};
[[nodiscard]] static auto create(
Api target_api,
class IGpu *gpu,
class IDevice *device,
class ISwapchain *swapchain,
uint32_t max_frames_in_flight
) -> memory::Scope<IRenderer>;
IRenderer() = default;
virtual ~IRenderer() = default;
IRenderer(IRenderer &&) = default;
IRenderer(const IRenderer &) = delete;
auto operator=(IRenderer &&) -> IRenderer & = default;
auto operator=(const IRenderer &) -> IRenderer & = delete;
virtual auto frame(uint32_t frame_idx, std::function<void()> submit_scene) -> Result = 0;
virtual void replace_swapchain(class ISwapchain *swapchain) = 0;
virtual void set_frame_constants(FrameConstants constants) = 0;
virtual void submit_sprite(
const components::Sprite &sprite,
const math::components::Transform &transform
) = 0;
};
} // namespace lt::renderer

View file

@ -1,26 +1,36 @@
export module renderer.frontend;
import assets.shader;
import ecs.entity;
import math.vec2;
import renderer.api;
import memory.scope;
import std;
export namespace lt::renderer {
class IDevice
enum class Api : std::uint8_t
{
none = 0u,
vulkan,
direct_x,
metal,
};
class IInstance
{
public:
IDevice() = default;
// [[nodiscard]] static auto get(Api target_api) -> IInstance *;
IInstance() = default;
virtual ~IDevice() = default;
virtual ~IInstance() = default;
IDevice(IDevice &&) = default;
IInstance(IInstance &&) = default;
IDevice(const IDevice &) = delete;
IInstance(const IInstance &) = delete;
auto operator=(IDevice &&) -> IDevice & = default;
auto operator=(IInstance &&) -> IInstance & = default;
auto operator=(const IDevice &) -> IDevice & = delete;
auto operator=(const IInstance &) -> IInstance & = delete;
};
class IGpu
@ -39,21 +49,20 @@ public:
auto operator=(const IGpu &) -> IGpu & = delete;
};
class IInstance
class IDevice
{
public:
// [[nodiscard]] static auto get(Api target_api) -> IInstance *;
IInstance() = default;
IDevice() = default;
virtual ~IInstance() = default;
virtual ~IDevice() = default;
IInstance(IInstance &&) = default;
IDevice(IDevice &&) = default;
IInstance(const IInstance &) = delete;
IDevice(const IDevice &) = delete;
auto operator=(IInstance &&) -> IInstance & = default;
auto operator=(IDevice &&) -> IDevice & = default;
auto operator=(const IInstance &) -> IInstance & = delete;
auto operator=(const IDevice &) -> IDevice & = delete;
};
class ISurface
@ -74,4 +83,212 @@ public:
[[nodiscard]] virtual auto get_framebuffer_size() const -> math::uvec2 = 0;
};
class ISwapchain
{
public:
ISwapchain() = default;
virtual ~ISwapchain() = default;
ISwapchain(ISwapchain &&) = default;
ISwapchain(const ISwapchain &) = delete;
auto operator=(ISwapchain &&) -> ISwapchain & = default;
auto operator=(const ISwapchain &) -> ISwapchain & = delete;
};
class IBuffer
{
public:
enum class Usage : std::uint8_t
{
vertex,
index,
storage,
staging,
};
struct CreateInfo
{
Usage usage;
std::size_t size;
std::string debug_name;
};
struct CopyInfo
{
std::size_t offset;
std::size_t size;
};
[[nodiscard]] static auto create(
Api target_api,
class IDevice *device,
class IGpu *gpu,
const CreateInfo &info
) -> memory::Scope<IBuffer>;
IBuffer() = default;
virtual ~IBuffer() = default;
IBuffer(IBuffer &&) = default;
IBuffer(const IBuffer &) = delete;
auto operator=(IBuffer &&) -> IBuffer & = default;
auto operator=(const IBuffer &) -> IBuffer & = delete;
[[nodiscard]] virtual auto map() -> std::span<std::byte> = 0;
virtual void unmap() = 0;
[[nodiscard]] virtual auto get_size() const -> std::size_t = 0;
private:
};
class IPass
{
public:
[[nodiscard]] static auto create(
lt::renderer::Api target_api,
class IDevice *device,
const class lt::assets::ShaderAsset &vertex_shader,
const class lt::assets::ShaderAsset &fragment_shader
) -> memory::Scope<IPass>;
IPass() = default;
virtual ~IPass() = default;
IPass(IPass &&) = default;
IPass(const IPass &) = delete;
auto operator=(IPass &&) -> IPass & = default;
auto operator=(const IPass &) -> IPass & = delete;
};
// class IRenderer
// {
// public:
// static constexpr auto frames_in_flight_upper_limit = 5u;
//
// static constexpr auto frames_in_flight_lower_limit = 1u;
//
// enum class Result : uint8_t
// {
// success = 0,
// invalid_swapchain,
// error,
// };
//
// [[nodiscard]] static auto create(
// Api target_api,
// class IGpu *gpu,
// class IDevice *device,
// class ISwapchain *swapchain,
// uint32_t max_frames_in_flight
// ) -> memory::Scope<IRenderer>;
//
// IRenderer() = default;
//
// virtual ~IRenderer() = default;
//
// IRenderer(IRenderer &&) = default;
//
// IRenderer(const IRenderer &) = delete;
//
// auto operator=(IRenderer &&) -> IRenderer & = default;
//
// auto operator=(const IRenderer &) -> IRenderer & = delete;
//
// virtual auto frame(uint32_t frame_idx, std::function<void()> submit_scene) -> Result = 0;
//
// virtual void replace_swapchain(class ISwapchain *swapchain) = 0;
//
// virtual void set_frame_constants(FrameConstants constants) = 0;
//
// virtual void submit_sprite(
// const components::Sprite &sprite,
// const math::components::Transform &transform
// ) = 0;
// };
// class IDebugger
// {
// public:
// enum class MessageSeverity : std::uint8_t
// {
// none = 0u,
//
// verbose = bitwise::bit(0u),
// info = bitwise::bit(1u),
// warning = bitwise::bit(2u),
// error = bitwise::bit(3u),
//
// all = verbose | info | warning | error,
// };
//
// enum class MessageType : std::uint8_t
// {
// none = 0u,
// general = bitwise::bit(0u),
// validation = bitwise::bit(1u),
// performance = bitwise::bit(2u),
//
// all = general | validation | performance,
// };
//
// struct MessageData
// {
// std::string message;
// };
//
// using Callback_T = std::function<void(
// MessageSeverity message_severity,
// MessageType message_type,
// const MessageData &data,
// std::any &user_data
// )>;
//
// struct CreateInfo
// {
// MessageSeverity severities;
//
// MessageType types;
//
// Callback_T callback;
//
// std::any user_data;
// };
//
// [[nodiscard]] static auto create(Api target_api, class IInstance *instance, CreateInfo info)
// -> memory::Scope<IDebugger>;
//
// IDebugger() = default;
//
// virtual ~IDebugger() = default;
//
// IDebugger(IDebugger &&) = default;
//
// IDebugger(const IDebugger &) = delete;
//
// auto operator=(IDebugger &&) -> IDebugger & = default;
//
// auto operator=(const IDebugger &) -> IDebugger & = delete;
// };
} // namespace lt::renderer

View file

@ -0,0 +1,161 @@
export module renderer.backend.vk.buffer;
import renderer.backend.vk.device;
import renderer.backend.vk.gpu;
import renderer.backend.vk.library_wrapper;
import renderer.frontend;
import std;
namespace lt::renderer::vkb {
export class Buffer: public IBuffer
{
public:
Buffer(class IDevice *device, class IGpu *gpu, const CreateInfo &info);
[[nodiscard]] auto map() -> std::span<std::byte> override;
void unmap() override;
[[nodiscard]] auto get_size() const -> std::size_t override
{
return m_size;
}
private:
[[nodiscard]] auto determine_allocation_info(Usage usage) const -> vk::Memory::AllocateInfo;
[[nodiscard]] auto to_native_usage_flags(Usage usage) const -> vk::Buffer::UsageFlags;
[[nodiscard]] auto to_native_memory_properties(Usage usage) const -> vk::Memory::PropertyFlags;
[[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(
std::uint32_t required_properties,
std::uint32_t property_flags
) const -> bool;
Device *m_device {};
Gpu *m_gpu {};
vk::Buffer m_buffer;
vk::Memory m_memory;
// TODO(Light): should this reflect the allocation size instead?
std::size_t m_size {};
};
} // namespace lt::renderer::vkb
module :private;
using namespace ::lt::renderer;
using namespace ::lt::renderer::vkb;
Buffer::Buffer(IDevice *device, IGpu *gpu, const CreateInfo &info)
: m_device(static_cast<Device *>(device))
, m_gpu(static_cast<Gpu *>(gpu))
, m_buffer(
m_device->vk(),
vk::Buffer::CreateInfo {
.size = info.size,
.usage = to_native_usage_flags(info.usage),
.sharing_mode = vk::SharingMode::exclusive,
}
)
, m_memory(m_device->vk(), m_buffer, determine_allocation_info(info.usage))
, m_size(info.size)
{
}
[[nodiscard]] auto Buffer::map() -> std::span<std::byte> /* override */
{
return m_memory.map(m_size, 0ul);
}
void Buffer::unmap() /* override */
{
m_memory.unmap();
}
[[nodiscard]] auto Buffer::determine_allocation_info(Usage usage) const -> vk::Memory::AllocateInfo
{
const auto requirements = m_buffer.get_memory_requirements();
auto memory_properties = m_gpu->vk().get_memory_properties();
const auto required_properties = to_native_memory_properties(usage);
auto type = 0u;
for (auto idx = 0; const auto &memory_type : memory_properties.memory_types)
{
const auto property_flags = memory_type.property_flags;
if (has_correct_memory_type_bit(requirements.memory_type_bits, idx)
&& has_required_memory_properties(required_properties, property_flags))
{
type = idx;
break;
}
++idx;
}
return vk::Memory::AllocateInfo {
.size = requirements.size,
.memory_type_idx = type,
};
}
[[nodiscard]] auto Buffer::to_native_usage_flags(Usage usage) const -> vk::Buffer::UsageFlags
{
using Flags = vk::Buffer::UsageFlags;
using enum vk::Buffer::UsageFlags;
switch (usage)
{
case Usage::vertex: return static_cast<Flags>(vertex_buffer_bit | transfer_dst_bit);
case Usage::index: return static_cast<Flags>(index_buffer_bit | transfer_dst_bit);
case Usage::storage: return static_cast<Flags>(transfer_dst_bit | storage_buffer_bit);
case Usage::staging: return transfer_src_bit;
}
}
[[nodiscard]] auto Buffer::to_native_memory_properties(Usage usage) const
-> vk::Memory::PropertyFlags
{
using Flags = vk::Memory::PropertyFlags;
using enum vk::Memory::PropertyFlags;
switch (usage)
{
case Usage::vertex:
case Usage::index:
case Usage::storage: return device_local_bit;
case Usage::staging: return static_cast<Flags>(host_visible_bit | host_coherent_bit);
}
std::unreachable();
}
[[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);
}
[[nodiscard]] auto Buffer::has_required_memory_properties(
std::uint32_t required_properties,
std::uint32_t property_flags
) const -> bool
{
return (property_flags & required_properties) == required_properties;
}

View file

@ -22,7 +22,7 @@ public:
return m_device;
}
[[nodiscard]] auto get_family_indices() const -> std::array<std::uint32_t, 2>
[[nodiscard]] auto get_family_indices() const -> std::vector<std::uint32_t>
{
return { m_graphics_queue_family_index, m_present_queue_family_index };
}

View file

@ -0,0 +1,183 @@
export module renderer.vk.pass;
import renderer.backend.vk.library_wrapper;
import renderer.backend.vk.device;
import renderer.backend.vk.swapchain;
import assets.shader;
import assets.metadata;
import memory.null_on_move;
import renderer.frontend;
import std;
namespace lt::renderer::vkb {
export class Pass: public IPass
{
public:
Pass(
class IDevice *device,
const lt::assets::ShaderAsset &vertex_shader,
const lt::assets::ShaderAsset &fragment_shader
);
[[nodiscard]] auto get_pipeline() -> vk::Pipeline &
{
return m_pipeline;
}
[[nodiscard]] auto get_layout() -> vk::PipelineLayout &
{
return m_layout;
}
private:
Device *m_device {};
vk::PipelineLayout m_layout;
vk::Pipeline m_pipeline;
vk::DescriptorPool m_descriptor_pool;
vk::DescriptorSetLayout m_vertices_descriptor_set_layout;
vk::DescriptorSet m_vertices_descriptor_set;
};
} // namespace lt::renderer::vkb
module :private;
using namespace ::lt::renderer::vkb;
using namespace ::lt::renderer;
Pass::Pass(
IDevice *device,
const lt::assets::ShaderAsset &vertex_shader,
const lt::assets::ShaderAsset &fragment_shader
)
: m_device(static_cast<Device *>(device))
{
// auto binding = VkDescriptorSetLayoutBinding {
// .binding = 0,
// .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
// .descriptorCount = 1'000,
// .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
// };
//
// const auto descriptor_binding_flags = VkDescriptorBindingFlagsEXT {
// VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT
// | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT
// | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT
// | VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT,
// };
//
// constexpr auto descriptor_count = uint32_t { 1'000 };
//
// auto descriptor_binding_flags_info = VkDescriptorSetLayoutBindingFlagsCreateInfoEXT {
// .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT,
// .bindingCount = 1,
// .pBindingFlags = &descriptor_binding_flags,
// };
//
//
// m_vertices_descriptor_set_layout = m_device->create_descriptor_set_layout(
// {
// .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
// .pNext = &descriptor_binding_flags_info,
// .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT,
// .bindingCount = 1u,
// .pBindings = &binding,
//
// }
// );
//
// auto pool_size = VkDescriptorPoolSize {
// .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
// .descriptorCount = descriptor_count,
// };
//
// m_descriptor_pool = m_device->create_desscriptor_pool(
// {
// .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
// .poolSizeCount = 1u,
// .pPoolSizes = &pool_size,
// }
// );
//
// auto descriptor_set_variable_descriptor_count_info
// = VkDescriptorSetVariableDescriptorCountAllocateInfo {
// .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
// .descriptorSetCount = 1u,
// .pDescriptorCounts = &descriptor_count,
// };
//
// m_vertices_descriptor_set = m_device->allocate_descriptor_set(
// VkDescriptorSetAllocateInfo {
// .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
// .pNext = &descriptor_set_variable_descriptor_count_info,
// .descriptorPool = m_descriptor_pool,
// .descriptorSetCount = 1u,
// .pSetLayouts = &m_vertices_descriptor_set_layout,
// }
// );
m_layout = vk::PipelineLayout(
m_device->vk(),
vk::PipelineLayout::CreateInfo {
// std::vector<VkDescriptorSetLayout> {
// m_vertices_descriptor_set_layout,
// },
//
// std::vector<VkPushConstantRange> {
// VkPushConstantRange {
// .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
// .offset = 0u,
// .size = sizeof(FrameConstants),
// },
// },
}
);
auto shaders = std::vector<std::pair<vk::ShaderModule, vk::ShaderStageFlags::T>> {};
shaders.emplace_back(
vk::ShaderModule(
m_device->vk(),
vk::ShaderModule::CreateInfo {
.code = vertex_shader.unpack(lt::assets::ShaderAsset::BlobTag::code),
}
),
vk::ShaderStageFlags::vertex_bit
);
shaders.emplace_back(
vk::ShaderModule(
m_device->vk(),
vk::ShaderModule::CreateInfo {
.code = fragment_shader.unpack(lt::assets::ShaderAsset::BlobTag::code),
}
),
vk::ShaderStageFlags::fragment_bit
);
m_pipeline = vk::Pipeline(
m_device->vk(),
m_layout,
vk::Pipeline::CreateInfo {
.shaders = std::move(shaders),
.input_assembly_state = {
.topology = vk::PrimitiveTopology::triangle_list,
.primitive_restart_enabled = true,
},
.viewport_state = {
.viewport_count = 1u,
.scissor_count = 1u,
},
.rasterization_state = vk::Pipeline::RasterizationState {},
.multisampling_state = vk::Pipeline::MultisamplingState {},
.attachment_state = vk::
Pipeline::AttachmentState {},
.debug_name = "abcdefgh"
}
);
}

View file

@ -37,46 +37,6 @@ private:
VkDebugUtilsMessengerEXT m_object;
};
class Buffer
{
public:
Buffer(Device *device, VkBufferCreateInfo info)
: m_device(device)
, m_object(m_device->create_buffer(info))
{
}
~Buffer()
{
if (m_device)
{
m_device->destroy_buffer(m_object);
}
}
Buffer(Buffer &&) = default;
Buffer(const Buffer &) = delete;
auto operator=(Buffer &&) -> Buffer & = default;
auto operator=(const Buffer &) -> Buffer & = delete;
[[nodiscard]] auto operator*() const -> VkBuffer
{
return m_object;
}
[[nodiscard]] operator VkBuffer() const
{
return m_object;
}
private:
memory::NullOnMove<Device *> m_device {};
VkBuffer m_object;
};
class Memory
{

View file

@ -1,3 +1,100 @@
#pragma once
#include <memory/reference.hpp>
#include <ranges>
#include <renderer/backend/vk/context/device.hpp>
#include <renderer/backend/vk/data/buffer.hpp>
#include <renderer/backend/vk/renderer/pass.hpp>
#include <renderer/backend/vk/utils.hpp>
#include <renderer/frontend/data/buffer.hpp>
#include <renderer/frontend/renderer/pass.hpp>
#include <renderer/frontend/renderer/renderer.hpp>
namespace lt::renderer::vk {
class Renderer: public IRenderer
{
public:
Renderer(
class IGpu *gpu,
class IDevice *device,
class ISwapchain *swapchain,
uint32_t max_frames_in_flight
);
~Renderer() override;
Renderer(Renderer &&) = default;
Renderer(const Renderer &) = delete;
auto operator=(Renderer &&) -> Renderer & = default;
auto operator=(const Renderer &) -> Renderer & = delete;
[[nodiscard]] auto frame(uint32_t frame_idx, std::function<void()> submit_scene)
-> Result override;
void replace_swapchain(ISwapchain *swapchain) override;
void set_frame_constants(FrameConstants constants) override
{
m_frame_constants = constants;
}
void submit_sprite(
const components::Sprite &sprite,
const math::components::Transform &transform
) override;
private:
void record_cmd(VkCommandBuffer cmd, uint32_t image_idx);
void map_buffers(uint32_t frame_idx);
void flush_buffers(VkCommandBuffer cmd);
memory::NullOnMove<class Device *> m_device {};
class Swapchain *m_swapchain {};
memory::Ref<class Pass> m_pass;
VkCommandPool m_pool = VK_NULL_HANDLE;
VkCommandPool m_transient_pool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> m_cmds;
std::vector<VkFence> m_frame_fences;
std::vector<VkSemaphore> m_aquire_image_semaphores;
std::vector<VkSemaphore> m_submit_semaphores;
VkExtent2D m_resolution;
uint32_t m_max_frames_in_flight {};
FrameConstants m_frame_constants;
Buffer m_vertex_buffer;
Buffer m_staging_buffer;
size_t m_staging_offset;
std::span<std::byte> m_staging_map;
std::span<components::Sprite::Vertex> m_sprite_vertex_map;
size_t m_current_sprite_idx;
};
} // namespace lt::renderer::vk
module :private;
#include <memory/reference.hpp>
#include <renderer/backend/vk/context/swapchain.hpp>
#include <renderer/backend/vk/renderer/renderer.hpp>

View file

@ -0,0 +1,170 @@
export module renderer.backend.vk.swapchain;
import renderer.backend.vk.library_wrapper;
import renderer.backend.vk.surface;
import renderer.backend.vk.device;
import renderer.backend.vk.instance;
import renderer.backend.vk.gpu;
import renderer.frontend;
import math.vec2;
import memory.null_on_move;
import logger;
import std;
namespace lt::renderer::vkb {
export class Swapchain: public ISwapchain
{
public:
Swapchain(ISurface *surface, IGpu *gpu, IDevice *device);
[[nodiscard]] auto vk() -> vk::Swapchain &
{
return m_swapchain;
}
[[nodiscard]] auto get_resolution() const -> math::uvec2
{
return m_resolution;
}
[[nodiscard]] auto get_format() const -> vk::Format
{
return m_format;
}
[[nodiscard]] auto get_image_count() const -> std::size_t
{
return m_images.size();
}
[[nodiscard]] auto get_image_view(std::uint32_t idx) -> vk::ImageView &
{
return m_image_views[idx];
}
[[nodiscard]] auto get_image(std::uint32_t idx) -> vk::Image &
{
return m_images[idx];
}
private:
[[nodiscard]] auto get_optimal_image_count(
vk::Surface::Capabilities capabilities,
std::uint32_t desired_image_count
) const -> std::uint32_t;
Gpu *m_gpu;
Surface *m_surface {};
Device *m_device;
vk::Swapchain m_swapchain;
std::vector<vk::Image> m_images;
std::vector<vk::ImageView> m_image_views;
math::uvec2 m_resolution {};
vk::Format m_format {};
};
} // namespace lt::renderer::vkb
module :private;
using namespace lt::renderer;
using namespace lt::renderer::vkb;
Swapchain::Swapchain(ISurface *surface, IGpu *gpu, IDevice *device)
: m_surface(static_cast<Surface *>(surface))
, m_gpu(static_cast<Gpu *>(gpu))
, m_device(static_cast<Device *>(device))
{
static auto idx = 0u;
const auto capabilities = m_gpu->vk().get_surface_capabilities(m_surface->vk());
const auto formats = m_gpu->vk().get_surface_formats(m_surface->vk());
// TODO(Light): parameterize
constexpr auto desired_image_count = std::uint32_t { 3u };
const auto surface_format = formats.front();
m_format = surface_format.format;
m_swapchain = vk::Swapchain(
m_device->vk(),
m_surface->vk(),
vk::Swapchain::CreateInfo {
.format = surface_format.format,
.color_space = surface_format.color_space,
.extent = capabilities.current_extent,
.min_image_count = get_optimal_image_count(capabilities, desired_image_count),
.queue_family_indices = m_device->get_family_indices(),
.present_mode = vk::Swapchain::PresentMode::immediate,
.pre_transform = capabilities.current_transform,
}
);
m_resolution = capabilities.current_extent;
m_device->vk().name(m_swapchain, "swapchain {}", idx++);
m_device->vk().wait_idle();
m_images = m_swapchain.get_images();
for (auto idx = 0u; auto &image : m_images)
{
m_image_views.emplace_back(
vk::ImageView {
m_device->vk(),
image,
vk::ImageView::CreateInfo {
.type = vk::ImageView::Type::_2d,
.format = surface_format.format,
.components = {
vk::ImageView::Swizzle::identity,
vk::ImageView::Swizzle::identity,
vk::ImageView::Swizzle::identity,
vk::ImageView::Swizzle::identity,
},
.range = {
.aspect_flags = vk::Image::AspectFlags::color_bit,
.base_mip_level = 0u,
.level_count = 1u,
.base_array_layer = 0u,
.layer_count = 1u,
},
.debug_name = std::format("swapchain image {}", idx++),
},
}
);
}
m_device->vk().wait_idle();
}
[[nodiscard]] auto Swapchain::get_optimal_image_count(
vk::Surface::Capabilities capabilities,
std::uint32_t desired_image_count
) const -> std::uint32_t
{
const auto min_image_count = capabilities.min_image_count;
const auto max_image_count = capabilities.max_image_count;
const auto has_max_limit = max_image_count != 0;
// Desired image count is in range
if ((!has_max_limit || max_image_count >= desired_image_count)
&& min_image_count <= desired_image_count)
{
return desired_image_count;
}
// Fall-back to 2 if in range
if (min_image_count <= 2 && max_image_count >= 2)
{
return 2;
}
// Fall-back to min_image_count
return min_image_count;
}

View file

@ -1,37 +0,0 @@
if(NOT WIN32)
add_library_module(
NAME
surface
INTERFACES
system.cppm
requests.cppm
events.cppm
components.cppm
SOURCES
platform_linux.cpp)
target_link_libraries(surface PRIVATE X11)
else(WIN32)
add_library_module(
NAME
surface
INTERFACES
system.cppm
requests.cppm
system.cppm
requests.cppm
events.cppm
components.cppm
SOURCES
platform_windows.cpp)
endif()
target_link_libraries(
surface
PUBLIC ecs app math memory tbb
PRIVATE logger lt_debug time)
add_test_module(surface system.test.cpp)
# add_fuzz_module(surface system.fuzz.cpp)

View file

@ -12,7 +12,6 @@ import app.system;
import std;
using ::std::ignore;
using ::lt::surface::SurfaceComponent;
using ::lt::surface::System;
using ::lt::test::Case;
@ -21,6 +20,7 @@ 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
@ -44,7 +44,6 @@ struct overloads: Ts...
using Ts::operator()...;
};
class Fixture
{
public:

View file

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

View file

@ -1,3 +0,0 @@
add_library_module(NAME time INTERFACES timer.cppm)
add_test_module(time timer.test.cpp)

View file

@ -1,2 +0,0 @@
add_library_module(ui ui.cpp)
target_link_libraries(ui PUBLIC imgui renderer logger lt_debug)

View file

@ -1,223 +0,0 @@
#include <memory/reference.hpp>
#include <memory/scope.hpp>
#include <ui/gl/ui.hpp>
#include <ui/ui.hpp>
#ifdef LIGHT_PLATFORM_WINDOWS
#include <renderer/dx/user_interface.hpp>
#endif
#include <imgui.h>
#include <input/events/char.hpp>
#include <input/events/event.hpp>
#include <input/events/keyboard.hpp>
#include <input/events/mouse.hpp>
#include <input/key_codes.hpp>
#include <utility>
inline ImGuiDockNodeFlags_ operator|(ImGuiDockNodeFlags_ a, ImGuiDockNodeFlags_ b)
{
return static_cast<ImGuiDockNodeFlags_>(std::to_underlying(a) | std::to_underlying(b));
}
namespace lt {
UserInterface *UserInterface::s_context = nullptr;
auto UserInterface::create(memory::Ref<SharedContext> sharedContext) -> memory::Scope<UserInterface>
{
auto scopeUserInterface = memory::Scope<UserInterface> { nullptr };
switch (GraphicsContext::get_graphics_api())
{
case GraphicsAPI::OpenGL: scopeUserInterface = memory::create_scope<glUserInterface>(); break;
case GraphicsAPI::DirectX:
lt_win(scopeUserInterface = memory::create_scope<dxUserInterface>();) break;
default:
ensure(
false,
"UserInterface::create: invalid/unsupported 'GraphicsAPI' {}",
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
);
return nullptr;
}
scopeUserInterface->init(windowHandle, std::move(sharedContext));
return std::move(scopeUserInterface);
}
UserInterface::UserInterface()
// NOLINTBEGIN
: m_dockspace_flags(
ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus
)
// NOLINTEND
{
ensure(
!s_context,
"UserInterface::UserInterface: an instance of 'UserInterface' already exists, do not "
"construct this class!"
);
s_context = this;
}
void UserInterface::init(memory::Ref<SharedContext> sharedContext)
{
// create context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
// configure io
ImGuiIO &io = ImGui::GetIO();
// NOLINTBEGIN
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
io.ConfigFlags |= ImGuiBackendFlags_PlatformHasViewports;
io.ConfigFlags |= ImGuiBackendFlags_RendererHasViewports;
// NOLINTEND
// #todo: handle this in a better way
if (std::filesystem::exists("user_gui_layout.ini"))
{
io.IniFilename = "user_gui_layout.ini";
}
else
{
io.IniFilename = "default_gui_layout.ini";
}
// style
ImGui::StyleColorsDark();
platform_implementation(windowHandle, std::move(sharedContext));
io.Fonts->AddFontFromFileTTF("data/assets/fonts/open_sans/OpenSans-Bold.ttf", 18.0f);
io.FontDefault = io.Fonts->AddFontFromFileTTF(
"data/assets/fonts/open_sans/OpenSans-Regular.ttf",
18.0f
);
set_dark_theme_colors();
}
void UserInterface::dockspace_begin()
{
ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("Dockspace", (bool *)nullptr, s_context->m_dockspace_flags);
ImGui::PopStyleVar(3);
ImGuiStyle &style = ImGui::GetStyle();
float const minWinSizeX = style.WindowMinSize.x;
style.WindowMinSize.x = 370.0f;
ImGui::DockSpace(
ImGui::GetID("MyDockSpace"),
ImVec2(0.0f, 0.0f),
ImGuiDockNodeFlags_None | ImGuiWindowFlags_NoBackground // NOLINT
);
style.WindowMinSize.x = minWinSizeX;
}
void UserInterface::dockspace_end()
{
ImGui::End();
}
void UserInterface::set_dark_theme_colors()
{
ImGuiStyle &style = ImGui::GetStyle();
ImVec4(&colors)[60] = style.Colors;
style.WindowPadding = ImVec2(0.0f, 0.0f);
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.11f, 1.00f);
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.20f, 0.21f, 1.00f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.30f, 0.31f, 0.31f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.20f, 0.20f, 0.21f, 1.00f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.30f, 0.31f, 0.31f, 1.00f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
colors[ImGuiCol_Header] = ImVec4(0.20f, 0.20f, 0.21f, 1.00f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.30f, 0.31f, 0.31f, 1.00f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.10f, 0.40f, 0.75f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.20f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_Tab] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
colors[ImGuiCol_TabHovered] = ImVec4(0.38f, 0.38f, 0.38f, 1.00f);
colors[ImGuiCol_TabActive] = ImVec4(0.28f, 0.28f, 0.28f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.20f, 0.20f, 0.21f, 1.00f);
colors[ImGuiCol_DockingPreview] = ImVec4(0.26f, 0.59f, 0.98f, 0.70f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
}
} // namespace lt

View file

@ -1,43 +0,0 @@
#pragma once
#include <app/system.hpp>
#include <ecs/registry.hpp>
#include <input/components.hpp>
#include <input/system.hpp>
#include <memory/reference.hpp>
namespace lt::ui {
struct ButtonComponent
{
std::function<void()> on_activate;
std::function<void(bool)> on_hover;
};
struct TransformComponent
{
float x, y;
float w, h;
};
class System: public app::ISystem
{
public:
System(memory::Ref<ecs::Registry> registry): m_registry(std::move(registry))
{
m_registry->each<ButtonComponent, TransformComponent>(
[](auto id, auto &button, auto &transform) {
// trigger `button`'s callbacksbased on input's state...
}
);
}
private:
memory::Ref<ecs::Registry> m_registry;
input::InputAction button_press_action;
};
} // namespace lt::ui

View file

@ -1,45 +0,0 @@
#pragma once
#include <memory/reference.hpp>
#include <memory/scope.hpp>
namespace lt {
class Event;
class SharedContext;
class UserInterface
{
public:
static auto create(memory::Ref<SharedContext> sharedContext) -> memory::Scope<UserInterface>;
static void dockspace_begin();
static void dockspace_end();
UserInterface(const UserInterface &) = delete;
auto operator=(const UserInterface &) -> UserInterface & = delete;
virtual ~UserInterface() = default;
void init(memory::Ref<SharedContext> sharedContext);
virtual void platform_implementation(memory::Ref<SharedContext> sharedContext) = 0;
virtual void begin() = 0;
virtual void end() = 0;
virtual void log_debug_data() = 0;
protected:
UserInterface();
private:
static UserInterface *s_context;
void set_dark_theme_colors();
};
} // namespace lt