Compare commits

..

13 commits

Author SHA1 Message Date
d029c0e473 build: add /EHsc and DriveMode: cl to .clangd file
Some checks are pending
continuous-integration/drone/push Build is running
2025-10-30 18:09:40 +03:30
604ee5e6a1 fix(renderer/vk/raii): special member function issues
Some checks are pending
continuous-integration/drone/push Build is running
2025-10-30 13:45:48 +03:30
7ee4381bbf feat(renderer/vk): add some vulkan functions 2025-10-30 13:45:48 +03:30
8730d31e2f refactor(renderer/vk/gpu): quality of life modifications 2025-10-30 13:45:48 +03:30
f50208653e build: add .clangd to fix clangd lsp issues on Windows
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-10-29 23:17:37 +03:30
5422792705
feat(renderer): storage & staging buffer types
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-10-27 23:29:08 +03:30
2ddb90faff
chore: remove code of conduct
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-26 16:44:12 +03:30
736c37d2f1
feat(renderer/vk): dynamic rendering
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-26 16:43:56 +03:30
97ca429d38 feat: frame constants & camera component (#62)
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #62
Co-authored-by: light7734 <light7734@tuta.io>
Co-committed-by: light7734 <light7734@tuta.io>
2025-10-26 06:56:18 +00:00
5a404d5269 feat(renderer): buffer (#61)
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #61
Co-authored-by: light7734 <light7734@tuta.io>
Co-committed-by: light7734 <light7734@tuta.io>
2025-10-25 12:56:14 +00:00
a9e27d6935
fix: old baked shaders
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-22 06:08:10 +03:30
80662983a3
ci: revert commenting the entire .drone.yml
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-10-22 05:58:48 +03:30
c39ce89a9b
chore(ci): bump c++ standard version to 26
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-10-22 05:55:20 +03:30
53 changed files with 1216 additions and 853 deletions

5
.clangd Normal file
View file

@ -0,0 +1,5 @@
CompileFlags:
DriverMode: cl
Add:
- /EHsc
- /std:c++latest

View file

@ -1,42 +1,42 @@
# --- ---
# kind: pipeline kind: pipeline
# type: exec type: exec
# name: amd64 — msvc name: amd64 — msvc
# trigger: trigger:
# branch: branch:
# - main - main
# platform: platform:
# os: windows os: windows
# arch: amd64 arch: amd64
#
# steps: steps:
# - name: unit tests - name: unit tests
# shell: powershell shell: powershell
# commands: commands:
# - ./tools/ci/amd64/msvc/unit_tests.ps1 - ./tools/ci/amd64/msvc/unit_tests.ps1
#
# --- ---
# kind: pipeline kind: pipeline
# type: docker type: docker
# name: amd64 — gcc name: amd64 — gcc
# trigger: trigger:
# branch: branch:
# - main - main
#
# steps: steps:
# - name: unit tests - name: unit tests
# image: ci:latest image: ci:latest
# pull: if-not-exists pull: if-not-exists
# commands: commands:
# - ./tools/ci/amd64/gcc/unit_tests.sh - ./tools/ci/amd64/gcc/unit_tests.sh
#
# - name: valgrind - name: valgrind
# image: ci:latest image: ci:latest
# pull: if-not-exists pull: if-not-exists
# commands: commands:
# - ./tools/ci/amd64/gcc/valgrind.sh - ./tools/ci/amd64/gcc/valgrind.sh
#
# --- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: amd64 — clang name: amd64 — clang
@ -45,114 +45,114 @@ trigger:
- main - main
steps: steps:
# - name: code coverage - name: code coverage
# image: ci:latest image: ci:latest
# pull: if-not-exists pull: if-not-exists
# environment: environment:
# CODECOV_TOKEN: CODECOV_TOKEN:
# from_secret: CODECOV_TOKEN from_secret: CODECOV_TOKEN
# commands: commands:
# - ./tools/ci/amd64/clang/coverage.sh - ./tools/ci/amd64/clang/coverage.sh
#
# - name: leak sanitizer - name: leak sanitizer
# image: ci:latest image: ci:latest
# pull: if-not-exists pull: if-not-exists
# commands: commands:
# - ./tools/ci/amd64/clang/lsan.sh - ./tools/ci/amd64/clang/lsan.sh
#
- name: memory sanitizer - name: memory sanitizer
image: ci:latest image: ci:latest
pull: if-not-exists pull: if-not-exists
commands: commands:
- ./tools/ci/amd64/clang/msan.sh - ./tools/ci/amd64/clang/msan.sh
#
# --- ---
# kind: pipeline kind: pipeline
# type: docker type: docker
# name: static analysis name: static analysis
# trigger: trigger:
# branch: branch:
# - main - main
#
# steps: steps:
# - name: clang tidy - name: clang tidy
# image: ci:latest image: ci:latest
# pull: if-not-exists pull: if-not-exists
# privileged: true privileged: true
# commands: commands:
# - ./tools/ci/static_analysis/clang_tidy.sh - ./tools/ci/static_analysis/clang_tidy.sh
#
# - name: shell check - name: shell check
# image: ci:latest image: ci:latest
# pull: if-not-exists pull: if-not-exists
# commands: commands:
# - ./tools/ci/static_analysis/shell_check.sh - ./tools/ci/static_analysis/shell_check.sh
#
# - name: clang format - name: clang format
# image: ci:latest image: ci:latest
# pull: if-not-exists pull: if-not-exists
# commands: commands:
# - ./tools/ci/static_analysis/clang_format.sh - ./tools/ci/static_analysis/clang_format.sh
#
# - name: cmake format - name: cmake format
# image: ci:latest image: ci:latest
# pull: if-not-exists pull: if-not-exists
# commands: commands:
# - ./tools/ci/static_analysis/cmake_format.sh - ./tools/ci/static_analysis/cmake_format.sh
#
# - name: shell format - name: shell format
# image: ci:latest image: ci:latest
# pull: if-not-exists pull: if-not-exists
# commands: commands:
# - ./tools/ci/static_analysis/shell_format.sh - ./tools/ci/static_analysis/shell_format.sh
#
# --- ---
# kind: pipeline kind: pipeline
# type: docker type: docker
# name: documentation — development name: documentation — development
# node: node:
# environment: ryali environment: ryali
# trigger: trigger:
# branch: branch:
# - main - main
#
# steps: steps:
# - name: build and deploy - name: build and deploy
# image: documentation:latest image: documentation:latest
# pull: if-not-exists pull: if-not-exists
# commands: commands:
# - pwd - pwd
# - cd docs - cd docs
# - mkdir generated - mkdir generated
# - touch generated/changelogs.rst - touch generated/changelogs.rst
# - touch generated/api.rst - touch generated/api.rst
# - sphinx-build -M html . . - sphinx-build -M html . .
#
# - rm -rf /light_docs_dev/* - rm -rf /light_docs_dev/*
# - mv ./html/* /light_docs_dev/ - mv ./html/* /light_docs_dev/
#
# --- ---
#
# kind: pipeline kind: pipeline
# type: docker type: docker
# name: documentation — production name: documentation — production
# node: node:
# environment: ryali environment: ryali
# trigger: trigger:
# event: event:
# - tag - tag
#
# steps: steps:
# - name: build and deploy - name: build and deploy
# image: documentation:latest image: documentation:latest
# pull: if-not-exists pull: if-not-exists
# commands: commands:
# - cd docs - cd docs
# - mkdir generated - mkdir generated
# - touch generated/changelogs.rst - touch generated/changelogs.rst
# - touch generated/api.rst - touch generated/api.rst
# - sphinx-build -M html . . - sphinx-build -M html . .
#
# - rm -rf /light_docs/* - rm -rf /light_docs/*
# - mv ./html/* /light_docs/ - mv ./html/* /light_docs/
#

View file

@ -1,128 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
Discord: Light7734#4652.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

Binary file not shown.

View file

@ -1,21 +1,26 @@
#version 450 core #version 450 core
vec2 positions[3] = vec2[]( layout(push_constant ) uniform pc {
vec2(0.0, -0.5), mat4 view_projection;
vec2(0.5, 0.5), };
vec2(-0.5, 0.5)
vec3 positions[3] = vec3[](
vec3(0.0, -0.5, 0.5),
vec3(0.5, 0.5, 0.5),
vec3(-0.5, 0.5, 0.5)
); );
vec3 colors[3] = vec3[]( vec3 colors[3] = vec3[](
vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 0.0),
vec3(0.0, 0.0, 1.0) vec3(0.0, 0.0, 0.0)
); );
layout(location = 0) out vec3 out_frag_color; layout(location = 0) out vec3 out_frag_color;
void main() void main()
{ {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); gl_Position = view_projection * vec4(positions[gl_VertexIndex], 1.0);
out_frag_color = colors[gl_VertexIndex]; out_frag_color = colors[gl_VertexIndex];
} }

Binary file not shown.

View file

@ -1,3 +1,3 @@
add_library_module(camera camera.cpp scene.cpp) add_library_module(camera)
target_link_libraries(camera PUBLIC math) target_link_libraries(camera INTERFACE math)

View file

@ -1,6 +0,0 @@
#include <camera/camera.hpp>
namespace lt {
}

View file

@ -1,84 +0,0 @@
#include <camera/camera.hpp>
#include <camera/component.hpp>
#include <math/algebra.hpp>
#include <math/trig.hpp>
namespace lt {
SceneCamera::SceneCamera()
: m_orthographic_specification { .size = 1000.0f, .near_plane = -1.0f, .far_plane = 10000.0f }
, m_perspective_specification { .vertical_fov = math::radians(45.0f),
.near_plane = 0.01f,
.far_plane = 10000.0f }
, m_aspect_ratio(16.0f / 9.0f)
{
calculate_projection();
}
void SceneCamera::set_viewport_size(unsigned int width, unsigned int height)
{
m_aspect_ratio = static_cast<float>(width) / static_cast<float>(height);
calculate_projection();
}
void SceneCamera::set_projection_type(ProjectionType projection_type)
{
m_projection_type = projection_type;
calculate_projection();
}
void SceneCamera::set_orthographic_size(float size)
{
m_orthographic_specification.size = size;
calculate_projection();
}
void SceneCamera::set_orthographic_far_plane(float far_plane)
{
m_orthographic_specification.far_plane = far_plane;
calculate_projection();
}
void SceneCamera::set_orthographic_near_plane(float near_plane)
{
m_orthographic_specification.near_plane = near_plane;
calculate_projection();
}
void SceneCamera::set_perspective_vertical_fov(float vertical_fov)
{
m_perspective_specification.vertical_fov = vertical_fov;
calculate_projection();
}
void SceneCamera::set_perspective_far_plane(float far_plane)
{
m_perspective_specification.far_plane = far_plane;
calculate_projection();
}
void SceneCamera::set_perspective_near_plane(float near_plane)
{
m_perspective_specification.near_plane = near_plane;
calculate_projection();
}
void SceneCamera::calculate_projection()
{
// TODO(Light): implement ortho perspective
if (m_projection_type == ProjectionType::Orthographic)
{
// throw std::runtime_error { "ortho perspective not supported yet" };
}
// defaults to perspective for now...
m_projection = math::perspective(
m_perspective_specification.vertical_fov,
m_aspect_ratio,
m_perspective_specification.near_plane,
m_perspective_specification.far_plane
);
}
} // namespace lt

View file

@ -1,35 +0,0 @@
#pragma once
#include <math/mat4.hpp>
#include <math/vec4.hpp>
namespace lt {
class Camera
{
public:
Camera() = default;
[[nodiscard]] auto get_projection() const -> const math::mat4 &
{
return m_projection;
}
[[nodiscard]] auto get_background_color() const -> const math::vec4 &
{
return m_background_color;
}
void set_background_color(const math::vec4 &color)
{
m_background_color = color;
}
protected:
math::mat4 m_projection;
private:
math::vec4 m_background_color = math::vec4(1.0f, 0.0f, 0.0f, 1.0f);
};
} // namespace lt

View file

@ -1,29 +0,0 @@
#pragma once
#include <camera/scene.hpp>
namespace lt {
struct CameraComponent
{
CameraComponent() = default;
CameraComponent(const CameraComponent &) = default;
CameraComponent(SceneCamera _camera, bool _isPrimary = false)
: camera(_camera)
, isPrimary(_isPrimary)
{
}
operator SceneCamera() const
{
return camera;
}
SceneCamera camera;
bool isPrimary {};
};
} // namespace lt

View file

@ -0,0 +1,22 @@
#pragma once
#include <math/mat4.hpp>
namespace lt::camera::components {
struct PerspectiveCamera
{
float vertical_fov {};
float near_plane {};
float far_plane {};
float aspect_ratio {};
math::vec4 background_color;
bool is_primary {};
};
} // namespace lt::camera::components

View file

@ -1,100 +0,0 @@
#pragma once
#include <camera/camera.hpp>
namespace lt {
class SceneCamera: public Camera
{
public:
enum class ProjectionType
{
Orthographic = 0,
Perspetcive = 1
};
struct OrthographicSpecification
{
float size;
float near_plane;
float far_plane;
};
struct PerspectiveSpecification
{
float vertical_fov;
float near_plane;
float far_plane;
};
SceneCamera();
void set_viewport_size(unsigned int width, unsigned int height);
void set_projection_type(ProjectionType projection_type);
void set_orthographic_size(float size);
void set_orthographic_far_plane(float far_plane);
void set_orthographic_near_plane(float near_plane);
void set_perspective_vertical_fov(float vertical_fov);
void set_perspective_far_plane(float far_plane);
void set_perspective_near_plane(float near_plane);
[[nodiscard]] auto get_orthographic_size() const -> float
{
return m_orthographic_specification.size;
}
[[nodiscard]] auto get_orthographic_far_plane() const -> float
{
return m_orthographic_specification.far_plane;
}
[[nodiscard]] auto get_orthographic_near_plane() const -> float
{
return m_orthographic_specification.near_plane;
}
[[nodiscard]] auto get_perspective_vertical_fov() const -> float
{
return m_perspective_specification.vertical_fov;
}
[[nodiscard]] auto get_perspective_far_plane() const -> float
{
return m_perspective_specification.far_plane;
}
[[nodiscard]] auto get_perspective_near_plane() const -> float
{
return m_perspective_specification.near_plane;
}
[[nodiscard]] auto get_projection_type() const -> ProjectionType
{
return m_projection_type;
}
private:
OrthographicSpecification m_orthographic_specification;
PerspectiveSpecification m_perspective_specification;
float m_aspect_ratio;
ProjectionType m_projection_type { ProjectionType::Orthographic };
void calculate_projection();
};
} // namespace lt

View file

@ -31,25 +31,29 @@ namespace lt::math {
* *
* the 1 at [z][3] is to save the Z axis into the resulting W for perspective division. * the 1 at [z][3] is to save the Z axis into the resulting W for perspective division.
* *
* thanks to pikuma: https://www.youtube.com/watch?v=EqNcqBdrNyI * @ref Thanks to pikuma for explaining the math behind this:
* https://www.youtube.com/watch?v=EqNcqBdrNyI
*/ */
template<typename T> template<typename T>
constexpr auto perspective(T field_of_view, T aspect_ratio, T z_near, T z_far) constexpr auto perspective(T field_of_view, T aspect_ratio, T z_near, T z_far)
{ {
const T half_fov_tan = std::tan(field_of_view / static_cast<T>(2)); const T half_fov_tan = std::tan(field_of_view / static_cast<T>(2));
auto result = mat4_impl<T> { T { 0 } }; auto result = mat4_impl<T>::identity();
result[0][0] = T { 1 } / (aspect_ratio * half_fov_tan); result[0][0] = T { 1 } / (aspect_ratio * half_fov_tan);
//
result[1][1] = T { 1 } / (half_fov_tan); result[1][1] = T { 1 } / (half_fov_tan);
//
result[2][2] = -(z_far + z_near) / (z_far - z_near); // result[2][2] = -(z_far + z_near) / (z_far - z_near);
//
result[2][2] = z_far / (z_far - z_near);
//
result[2][3] = -T { 1 }; result[2][3] = -T { 1 };
//
result[3][2] = -(T { 2 } * z_far * z_near) / (z_far - z_near); // result[3][2] = -(T { 2 } * z_far * z_near) / (z_far - z_near);
result[3][2] = -(z_far * z_near) / (z_far - z_near);
//
return result; return result;
} }

View file

@ -9,6 +9,7 @@ template<typename T = float>
struct mat4_impl struct mat4_impl
{ {
using Column_T = vec4_impl<T>; using Column_T = vec4_impl<T>;
constexpr explicit mat4_impl(T scalar = 0) constexpr explicit mat4_impl(T scalar = 0)
: values( : values(
{ {
@ -43,7 +44,7 @@ struct mat4_impl
{ {
} }
[[nodiscard]] constexpr auto identity() -> mat4_impl<T> [[nodiscard]] static constexpr auto identity() -> mat4_impl<T>
{ {
return mat4_impl<T> { return mat4_impl<T> {
{ 1 }, {}, {}, {}, // { 1 }, {}, {}, {}, //

View file

@ -1,5 +1,6 @@
add_library_module(libmirror) add_library_module(libmirror)
target_link_libraries(libmirror INTERFACE app time input surface renderer) target_link_libraries(libmirror INTERFACE app time input surface renderer
camera)
add_test_module( add_test_module(
libmirror layers/editor_layer.test.cpp panels/asset_browser.test.cpp libmirror layers/editor_layer.test.cpp panels/asset_browser.test.cpp

View file

@ -2,9 +2,11 @@
#include <app/application.hpp> #include <app/application.hpp>
#include <app/entrypoint.hpp> #include <app/entrypoint.hpp>
#include <app/system.hpp> #include <app/system.hpp>
#include <camera/components.hpp>
#include <ecs/entity.hpp> #include <ecs/entity.hpp>
#include <input/components.hpp> #include <input/components.hpp>
#include <input/system.hpp> #include <input/system.hpp>
#include <math/trig.hpp>
#include <math/vec2.hpp> #include <math/vec2.hpp>
#include <memory/reference.hpp> #include <memory/reference.hpp>
#include <memory/scope.hpp> #include <memory/scope.hpp>
@ -66,13 +68,18 @@ public:
const auto &[x, y] = surface.get_position(); const auto &[x, y] = surface.get_position();
const auto &[width, height] = surface.get_resolution(); const auto &[width, height] = surface.get_resolution();
if (input.get_action(m_quit_action_key).state == State::active) if (input.get_action(m_quit_action_key).state == State::active)
{ {
should_quit = true; should_quit = true;
} }
if (input.get_action(m_debug_action_keys[0]).state == State::active) if (input.get_action(m_debug_action_keys[0]).state == State::active)
{ {
surface.push_request(surface::ModifyPositionRequest({ x + 5, y + 5 })); for (auto &[id, camera] :
m_registry->view<lt::camera::components::PerspectiveCamera>())
{
camera.vertical_fov += (static_cast<float>(tick.delta_time.count()) * 40.0f);
}
} }
if (input.get_action(m_debug_action_keys[1]).state == State::active) if (input.get_action(m_debug_action_keys[1]).state == State::active)
@ -219,6 +226,20 @@ public:
.callback = &renderer_callback, .callback = &renderer_callback,
.user_data = this, .user_data = this,
} }); } });
m_camera_id = m_editor_registry->create_entity();
m_editor_registry->add(
m_camera_id,
camera::components::PerspectiveCamera {
.vertical_fov = math::radians(90.0f),
.near_plane = 0.1f,
.far_plane = 30.0,
.aspect_ratio = 1.0f,
.background_color = math::vec4(1.0, 0.0, 0.0, 1.0),
.is_primary = true,
}
);
} }
void setup_input_system() void setup_input_system()
@ -245,6 +266,8 @@ private:
memory::Ref<MirrorSystem> m_mirror_system; memory::Ref<MirrorSystem> m_mirror_system;
lt::ecs::EntityId m_window = lt::ecs::null_entity; lt::ecs::EntityId m_window = lt::ecs::null_entity;
lt::ecs::EntityId m_camera_id = lt::ecs::null_entity;
}; };
auto app::create_application() -> memory::Scope<app::Application> auto app::create_application() -> memory::Scope<app::Application>

View file

@ -8,21 +8,29 @@ add_library_module(
backend/vk/context/instance.cpp backend/vk/context/instance.cpp
backend/vk/context/surface.cpp backend/vk/context/surface.cpp
backend/vk/context/swapchain.cpp backend/vk/context/swapchain.cpp
backend/vk/data/buffer.cpp
backend/vk/renderer/pass.cpp backend/vk/renderer/pass.cpp
backend/vk/renderer/renderer.cpp backend/vk/renderer/renderer.cpp
# Vulkan - frontend # frontend
frontend/messenger.cpp frontend/messenger.cpp
frontend/context/device.cpp frontend/context/device.cpp
frontend/context/gpu.cpp frontend/context/gpu.cpp
frontend/context/instance.cpp frontend/context/instance.cpp
frontend/context/surface.cpp frontend/context/surface.cpp
frontend/context/swapchain.cpp frontend/context/swapchain.cpp
frontend/data/buffer.cpp
frontend/renderer/renderer.cpp frontend/renderer/renderer.cpp
frontend/renderer/pass.cpp) frontend/renderer/pass.cpp)
target_link_libraries( target_link_libraries(
renderer renderer
PUBLIC app ecs memory assets time bitwise PUBLIC app
ecs
memory
assets
time
bitwise
camera
PRIVATE surface pthread) PRIVATE surface pthread)
add_test_module( add_test_module(
@ -34,11 +42,10 @@ add_test_module(
frontend/context/surface.test.cpp frontend/context/surface.test.cpp
frontend/context/device.test.cpp frontend/context/device.test.cpp
frontend/context/swapchain.test.cpp frontend/context/swapchain.test.cpp
frontend/renderer/pass.test.cpp frontend/data/buffer.test.cpp
# frontend/renderer/pass.test.cpp
frontend/renderer/renderer.test.cpp frontend/renderer/renderer.test.cpp
# backend specific tests -- vk # backend specific tests -- vk
backend/vk/context/instance.test.cpp backend/vk/context/instance.test.cpp)
# backend specific tests -- dx backend specific tests -- mt
)
target_link_libraries(renderer_tests PRIVATE surface pthread) target_link_libraries(renderer_tests PRIVATE surface pthread)

View file

@ -75,10 +75,17 @@ void Device::initialize_logical_device()
auto extensions = std::vector<const char *> { auto extensions = std::vector<const char *> {
VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
};
const auto dynamic_rendering_features = VkPhysicalDeviceDynamicRenderingFeatures {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES,
.dynamicRendering = true,
}; };
auto device_info = VkDeviceCreateInfo { auto device_info = VkDeviceCreateInfo {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &dynamic_rendering_features,
.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size()), .queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size()),
.pQueueCreateInfos = queue_infos.data(), .pQueueCreateInfos = queue_infos.data(),
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()), .enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
@ -207,6 +214,31 @@ void Device::wait_for_fences(std::span<VkFence> fences) const
return images; return images;
} }
[[nodiscard]] auto Device::get_memory_requirements(VkBuffer buffer) const -> VkMemoryRequirements
{
auto requirements = VkMemoryRequirements {};
vk_get_buffer_memory_requirements(m_device, buffer, &requirements);
return requirements;
}
void Device::bind_memory(VkBuffer buffer, VkDeviceMemory memory, size_t offset /* = 0u */) const
{
vkc(vk_bind_buffer_memory(m_device, buffer, memory, offset));
}
[[nodiscard]] auto Device::map_memory(VkDeviceMemory memory, size_t size, size_t offset) const
-> std::span<std::byte>
{
void *data = {};
vkc(vk_map_memory(m_device, memory, offset, size, {}, &data));
return { std::bit_cast<std::byte *>(data), size };
}
void Device::unmap_memory(VkDeviceMemory memory)
{
vk_unmap_memory(m_device, memory);
}
[[nodiscard]] auto Device::create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR [[nodiscard]] auto Device::create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR
{ {
auto *swapchain = VkSwapchainKHR {}; auto *swapchain = VkSwapchainKHR {};
@ -251,9 +283,18 @@ void Device::wait_for_fences(std::span<VkFence> fences) const
return pass; return pass;
} }
[[nodiscard]] auto Device::create_pipeline_layout(VkPipelineLayoutCreateInfo info) const [[nodiscard]] auto Device::create_pipeline_layout(
-> VkPipelineLayout std::vector<VkPushConstantRange> push_constant_ranges
) const -> VkPipelineLayout
{ {
auto info = VkPipelineLayoutCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0u,
.pSetLayouts = nullptr,
.pushConstantRangeCount = static_cast<uint32_t>(push_constant_ranges.size()),
.pPushConstantRanges = push_constant_ranges.data(),
};
auto *pipeline_layout = VkPipelineLayout {}; auto *pipeline_layout = VkPipelineLayout {};
vkc(vk_create_pipeline_layout(m_device, &info, nullptr, &pipeline_layout)); vkc(vk_create_pipeline_layout(m_device, &info, nullptr, &pipeline_layout));
return pipeline_layout; return pipeline_layout;
@ -299,6 +340,13 @@ void Device::wait_for_fences(std::span<VkFence> fences) const
return fences; return fences;
} }
[[nodiscard]] auto Device::create_buffer(VkBufferCreateInfo info) const -> VkBuffer
{
auto *buffer = VkBuffer {};
vkc(vk_create_buffer(m_device, &info, nullptr, &buffer));
return buffer;
}
[[nodiscard]] auto Device::allocate_command_buffers(VkCommandBufferAllocateInfo info) const [[nodiscard]] auto Device::allocate_command_buffers(VkCommandBufferAllocateInfo info) const
-> std::vector<VkCommandBuffer> -> std::vector<VkCommandBuffer>
{ {
@ -307,6 +355,18 @@ void Device::wait_for_fences(std::span<VkFence> fences) const
return command_buffers; return command_buffers;
} }
[[nodiscard]] auto Device::allocate_memory(VkMemoryAllocateInfo info) const -> VkDeviceMemory
{
auto *memory = VkDeviceMemory {};
vkc(vk_allocate_memory(m_device, &info, nullptr, &memory));
return memory;
}
void Device::free_memory(VkDeviceMemory memory) const
{
vk_free_memory(m_device, memory, nullptr);
}
void Device::destroy_swapchain(VkSwapchainKHR swapchain) const void Device::destroy_swapchain(VkSwapchainKHR swapchain) const
{ {
vk_destroy_swapchain_khr(m_device, swapchain, m_allocator); vk_destroy_swapchain_khr(m_device, swapchain, m_allocator);
@ -389,4 +449,9 @@ void Device::destroy_fences(std::span<VkFence> fences) const
} }
} }
void Device::destroy_buffer(VkBuffer buffer) const
{
vk_destroy_buffer(m_device, buffer, nullptr);
}
} // namespace lt::renderer::vk } // namespace lt::renderer::vk

View file

@ -96,6 +96,16 @@ public:
[[nodiscard]] auto get_swapchain_images(VkSwapchainKHR swapchain) const -> std::vector<VkImage>; [[nodiscard]] auto get_swapchain_images(VkSwapchainKHR swapchain) const -> std::vector<VkImage>;
[[nodiscard]] auto get_memory_requirements(VkBuffer buffer) const -> VkMemoryRequirements;
/** binders / mappers */
void bind_memory(VkBuffer buffer, VkDeviceMemory memory, size_t offset = 0u) const;
[[nodiscard]] auto map_memory(VkDeviceMemory memory, size_t size, size_t offset) const
-> std::span<std::byte>;
void unmap_memory(VkDeviceMemory memory);
/** create functions */ /** create functions */
[[nodiscard]] auto create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR; [[nodiscard]] auto create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR;
@ -108,8 +118,9 @@ public:
[[nodiscard]] auto create_pass(VkRenderPassCreateInfo info) const -> VkRenderPass; [[nodiscard]] auto create_pass(VkRenderPassCreateInfo info) const -> VkRenderPass;
[[nodiscard]] auto create_pipeline_layout(VkPipelineLayoutCreateInfo info) const [[nodiscard]] auto create_pipeline_layout(
-> VkPipelineLayout; std::vector<VkPushConstantRange> push_constant_ranges
) const -> VkPipelineLayout;
[[nodiscard]] auto create_shader_module(VkShaderModuleCreateInfo info) const -> VkShaderModule; [[nodiscard]] auto create_shader_module(VkShaderModuleCreateInfo info) const -> VkShaderModule;
@ -120,10 +131,17 @@ public:
[[nodiscard]] auto create_fences(VkFenceCreateInfo info, uint32_t count) const [[nodiscard]] auto create_fences(VkFenceCreateInfo info, uint32_t count) const
-> std::vector<VkFence>; -> std::vector<VkFence>;
[[nodiscard]] auto create_buffer(VkBufferCreateInfo info) const -> VkBuffer;
/** allocation functions */ /** allocation functions */
[[nodiscard]] auto allocate_memory(VkMemoryAllocateInfo info) const -> VkDeviceMemory;
[[nodiscard]] auto allocate_command_buffers(VkCommandBufferAllocateInfo info) const [[nodiscard]] auto allocate_command_buffers(VkCommandBufferAllocateInfo info) const
-> std::vector<VkCommandBuffer>; -> std::vector<VkCommandBuffer>;
/** de-allocation functions */
void free_memory(VkDeviceMemory memory) const;
/** destroy functions */ /** destroy functions */
void destroy_swapchain(VkSwapchainKHR swapchain) const; void destroy_swapchain(VkSwapchainKHR swapchain) const;
@ -153,6 +171,8 @@ public:
void destroy_fences(std::span<VkFence> fences) const; void destroy_fences(std::span<VkFence> fences) const;
void destroy_buffer(VkBuffer buffer) const;
private: private:
template<typename T> template<typename T>
static auto get_object_type(const T &object) -> VkObjectType static auto get_object_type(const T &object) -> VkObjectType

View file

@ -5,35 +5,38 @@
namespace lt::renderer::vk { namespace lt::renderer::vk {
Gpu::Gpu(IInstance *instance) Gpu::Gpu(IInstance *instance)
: m_descriptor_indexing_features(
{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES }
)
{ {
auto gpus = static_cast<Instance *>(instance)->enumerate_gpus(); auto gpus = static_cast<Instance *>(instance)->enumerate_gpus();
for (auto &gpu : gpus) for (auto &gpu : gpus)
{ {
auto properties = VkPhysicalDeviceProperties {}; auto properties = VkPhysicalDeviceProperties {};
auto features = VkPhysicalDeviceFeatures {}; auto features = VkPhysicalDeviceFeatures2 {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &m_descriptor_indexing_features
};
vk_get_physical_device_properties(gpu, &properties); vk_get_physical_device_properties(gpu, &properties);
vk_get_physical_device_features(gpu, &features); vk_get_physical_device_features(gpu, &features);
if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
&& features.geometryShader) && features.features.geometryShader)
{ {
m_gpu = gpu; m_gpu = gpu;
} }
} }
ensure(m_gpu, "Failed to find any suitable Vulkan physical device"); ensure(m_gpu, "Failed to find any suitable Vulkan physical device");
}
[[nodiscard]] auto Gpu::get_queue_family_properties() const -> std::vector<VkQueueFamilyProperties>
{ vk_get_physical_device_memory_properties(m_gpu, &m_memory_properties);
auto count = uint32_t { 0u }; auto count = uint32_t { 0u };
vk_get_physical_device_queue_family_properties(m_gpu, &count, nullptr); vk_get_physical_device_queue_family_properties(m_gpu, &count, nullptr);
m_queue_family_properties.resize(count);
auto properties = std::vector<VkQueueFamilyProperties>(count); vk_get_physical_device_queue_family_properties(m_gpu, &count, m_queue_family_properties.data());
vk_get_physical_device_queue_family_properties(m_gpu, &count, properties.data());
return properties;
} }
[[nodiscard]] auto Gpu::queue_family_supports_presentation( [[nodiscard]] auto Gpu::queue_family_supports_presentation(
@ -67,5 +70,11 @@ Gpu::Gpu(IInstance *instance)
return formats; return formats;
} }
[[nodiscard]] auto Gpu::create_device(VkDeviceCreateInfo info) const -> VkDevice
{
auto *device = VkDevice {};
vkc(vk_create_device(m_gpu, &info, nullptr, &device));
return device;
}
} // namespace lt::renderer::vk } // namespace lt::renderer::vk

View file

@ -12,13 +12,6 @@ class Gpu: public IGpu
public: public:
Gpu(IInstance *instance); Gpu(IInstance *instance);
[[nodiscard]] auto vk() const -> VkPhysicalDevice
{
return m_gpu;
}
[[nodiscard]] auto get_queue_family_properties() const -> std::vector<VkQueueFamilyProperties>;
[[nodiscard]] auto queue_family_supports_presentation( [[nodiscard]] auto queue_family_supports_presentation(
VkSurfaceKHR surface, VkSurfaceKHR surface,
uint32_t queue_family_idx uint32_t queue_family_idx
@ -30,8 +23,39 @@ public:
[[nodiscard]] auto get_surface_formats(VkSurfaceKHR surface) const [[nodiscard]] auto get_surface_formats(VkSurfaceKHR surface) const
-> std::vector<VkSurfaceFormatKHR>; -> std::vector<VkSurfaceFormatKHR>;
[[nodiscard]] auto create_device(VkDeviceCreateInfo info) const -> VkDevice;
[[nodiscard]] auto get_properties() const -> VkPhysicalDeviceProperties
{
return m_properties;
}
[[nodiscard]] auto get_descriptor_indexing_features() const
-> VkPhysicalDeviceDescriptorIndexingFeatures
{
return m_descriptor_indexing_features;
}
[[nodiscard]] auto get_memory_properties() const -> VkPhysicalDeviceMemoryProperties
{
return m_memory_properties;
}
[[nodiscard]] auto get_queue_family_properties() const -> std::vector<VkQueueFamilyProperties>
{
return m_queue_family_properties;
}
private: private:
memory::NullOnMove<VkPhysicalDevice> m_gpu = VK_NULL_HANDLE; memory::NullOnMove<VkPhysicalDevice> m_gpu = VK_NULL_HANDLE;
VkPhysicalDeviceProperties m_properties {};
VkPhysicalDeviceDescriptorIndexingFeatures m_descriptor_indexing_features;
VkPhysicalDeviceMemoryProperties m_memory_properties {};
std::vector<VkQueueFamilyProperties> m_queue_family_properties;
}; };
} // namespace lt::renderer::vk } // namespace lt::renderer::vk

View file

@ -29,8 +29,9 @@ PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue_family
PFN_vkCreateDevice vk_create_device {}; PFN_vkCreateDevice vk_create_device {};
PFN_vkGetDeviceProcAddr vk_get_device_proc_address {}; PFN_vkGetDeviceProcAddr vk_get_device_proc_address {};
PFN_vkDestroyDevice vk_destroy_device {}; PFN_vkDestroyDevice vk_destroy_device {};
PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features {}; PFN_vkGetPhysicalDeviceFeatures2 vk_get_physical_device_features {};
PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties {}; PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties {};
PFN_vkGetPhysicalDeviceMemoryProperties vk_get_physical_device_memory_properties {};
// extension instance functions // extension instance functions
PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label {}; PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label {};
@ -86,15 +87,37 @@ PFN_vkCmdBindPipeline vk_cmd_bind_pipeline {};
PFN_vkCmdDraw vk_cmd_draw {}; PFN_vkCmdDraw vk_cmd_draw {};
PFN_vkCmdSetViewport vk_cmd_set_viewport {}; PFN_vkCmdSetViewport vk_cmd_set_viewport {};
PFN_vkCmdSetScissor vk_cmd_set_scissors {}; PFN_vkCmdSetScissor vk_cmd_set_scissors {};
PFN_vkCmdPushConstants vk_cmd_push_constants {};
PFN_vkCmdCopyBuffer vk_cmd_copy_buffer {};
PFN_vkCreateDescriptorSetLayout vk_create_descriptor_set_layout {};
PFN_vkDestroyDescriptorSetLayout vk_destroy_descriptor_set_layout {};
PFN_vkCreateDescriptorPool vk_create_descriptor_pool {};
PFN_vkDestroyDescriptorPool vk_destroy_descriptor_pool {};
PFN_vkAllocateDescriptorSets vk_allocate_descriptor_sets {};
PFN_vkFreeDescriptorSets vk_free_descriptor_sets {};
PFN_vkCreateBuffer vk_create_buffer {};
PFN_vkDestroyBuffer vk_destroy_buffer {};
PFN_vkGetBufferMemoryRequirements vk_get_buffer_memory_requirements {};
PFN_vkAllocateMemory vk_allocate_memory {};
PFN_vkBindBufferMemory vk_bind_buffer_memory {};
PFN_vkMapMemory vk_map_memory {};
PFN_vkUnmapMemory vk_unmap_memory {};
PFN_vkFreeMemory vk_free_memory {};
PFN_vkResetCommandBuffer vk_reset_command_buffer {}; PFN_vkResetCommandBuffer vk_reset_command_buffer {};
PFN_vkCmdBeginRendering vk_cmd_begin_rendering {};
PFN_vkCmdEndRendering vk_cmd_end_rendering {};
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {}; PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {};
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {}; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {};
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {}; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {};
auto vk_create_xlib_surface_khr = PFN_vkCreateXlibSurfaceKHR {}; auto vk_create_xlib_surface_khr = PFN_vkCreateXlibSurfaceKHR {};
auto vk_destroy_surface_khr = PFN_vkDestroySurfaceKHR {}; auto vk_destroy_surface_khr = PFN_vkDestroySurfaceKHR {};
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
Instance::Instance() Instance::Instance()
@ -127,6 +150,7 @@ void Instance::initialize_instance()
VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_XLIB_SURFACE_EXTENSION_NAME, VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
}; };
const char *layer_name = "VK_LAYER_KHRONOS_validation"; const char *layer_name = "VK_LAYER_KHRONOS_validation";
@ -298,6 +322,7 @@ void Instance::load_instance_functions()
load_fn(vk_destroy_device, "vkDestroyDevice"); load_fn(vk_destroy_device, "vkDestroyDevice");
load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures"); load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures");
load_fn(vk_enumerate_device_extension_properties, "vkEnumerateDeviceExtensionProperties"); load_fn(vk_enumerate_device_extension_properties, "vkEnumerateDeviceExtensionProperties");
load_fn(vk_get_physical_device_memory_properties, "vkGetPhysicalDeviceMemoryProperties");
load_fn(vk_cmd_begin_debug_label, "vkCmdBeginDebugUtilsLabelEXT"); load_fn(vk_cmd_begin_debug_label, "vkCmdBeginDebugUtilsLabelEXT");
load_fn(vk_cmd_end_debug_label, "vkCmdEndDebugUtilsLabelEXT"); load_fn(vk_cmd_end_debug_label, "vkCmdEndDebugUtilsLabelEXT");
@ -370,7 +395,26 @@ void Instance::load_device_functions_impl(VkDevice device)
load_fn(vk_cmd_draw, "vkCmdDraw"); load_fn(vk_cmd_draw, "vkCmdDraw");
load_fn(vk_cmd_set_viewport, "vkCmdSetViewport"); load_fn(vk_cmd_set_viewport, "vkCmdSetViewport");
load_fn(vk_cmd_set_scissors, "vkCmdSetScissor"); load_fn(vk_cmd_set_scissors, "vkCmdSetScissor");
load_fn(vk_cmd_push_constants, "vkCmdPushConstants");
load_fn(vk_cmd_copy_buffer, "vkCmdCopyBuffer");
load_fn(vk_create_descriptor_set_layout, "vkCreateDescriptorSetLayout");
load_fn(vk_destroy_descriptor_set_layout, "vkDestroyDescriptorSetLayout");
load_fn(vk_create_descriptor_pool, "vkCreateDescriptorPool");
load_fn(vk_destroy_descriptor_pool, "vkDestroyDescriptorPool");
load_fn(vk_allocate_descriptor_sets, "vkAllocateDescriptorSets");
load_fn(vk_free_descriptor_sets, "vkFreeDescriptorSets");
load_fn(vk_create_buffer, "vkCreateBuffer");
load_fn(vk_destroy_buffer, "vkDestroyBuffer");
load_fn(vk_allocate_memory, "vkAllocateMemory");
load_fn(vk_bind_buffer_memory, "vkBindBufferMemory");
load_fn(vk_map_memory, "vkMapMemory");
load_fn(vk_unmap_memory, "vkUnmapMemory");
load_fn(vk_free_memory, "vkFreeMemory");
load_fn(vk_get_buffer_memory_requirements, "vkGetBufferMemoryRequirements");
load_fn(vk_reset_command_buffer, "vkResetCommandBuffer"); load_fn(vk_reset_command_buffer, "vkResetCommandBuffer");
load_fn(vk_cmd_begin_rendering, "vkCmdBeginRendering");
load_fn(vk_cmd_end_rendering, "vkCmdEndRendering");
} }
auto Instance::enumerate_gpus() const -> std::vector<VkPhysicalDevice> auto Instance::enumerate_gpus() const -> std::vector<VkPhysicalDevice>

View file

@ -49,6 +49,17 @@ public:
return m_images.size(); 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 [[nodiscard]] auto create_framebuffers_for_pass(VkRenderPass pass) const
-> std::vector<VkFramebuffer>; -> std::vector<VkFramebuffer>;

View file

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

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

View file

@ -1,10 +1,11 @@
#include <memory/pointer_types/null_on_move.hpp> #include <memory/pointer_types/null_on_move.hpp>
#include <renderer/backend/vk/context/device.hpp>
#include <renderer/backend/vk/context/instance.hpp> #include <renderer/backend/vk/context/instance.hpp>
#include <renderer/backend/vk/vulkan.hpp> #include <renderer/backend/vk/vulkan.hpp>
namespace lt::renderer::vk::raii { namespace lt::renderer::vk::raii {
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
class DebugMessenger class DebugMessenger
{ {
public: public:
@ -16,18 +17,108 @@ public:
~DebugMessenger() ~DebugMessenger()
{ {
if (!m_instance) if (m_instance)
{ {
return; m_instance->destroy_messenger(m_object);
} }
m_instance->destroy_messenger(m_object);
} }
DebugMessenger(DebugMessenger &&) = default;
DebugMessenger(const DebugMessenger &) = delete;
auto operator=(DebugMessenger &&) -> DebugMessenger & = default;
auto operator=(const DebugMessenger &) -> DebugMessenger & = delete;
private: private:
memory::NullOnMove<Instance *> m_instance {}; memory::NullOnMove<Instance *> m_instance {};
VkDebugUtilsMessengerEXT m_object; 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
{
public:
Memory(Device *device, VkBuffer buffer, VkMemoryAllocateInfo info)
: m_device(device)
, m_object(m_device->allocate_memory(info))
{
m_device->bind_memory(buffer, m_object);
}
~Memory()
{
if (m_device)
{
m_device->free_memory(m_object);
}
}
Memory(Memory &&) = default;
Memory(const Memory &) = delete;
auto operator=(Memory &&) -> Memory & = default;
auto operator=(const Memory &) -> Memory & = delete;
[[nodiscard]] auto operator*() const -> VkDeviceMemory
{
return m_object;
}
[[nodiscard]] operator VkDeviceMemory() const
{
return m_object;
}
private:
memory::NullOnMove<Device *> m_device {};
VkDeviceMemory m_object = VK_NULL_HANDLE;
};
} // namespace lt::renderer::vk::raii } // namespace lt::renderer::vk::raii

View file

@ -1,6 +1,7 @@
#include <renderer/backend/vk/context/device.hpp> #include <renderer/backend/vk/context/device.hpp>
#include <renderer/backend/vk/context/swapchain.hpp> #include <renderer/backend/vk/context/swapchain.hpp>
#include <renderer/backend/vk/renderer/pass.hpp> #include <renderer/backend/vk/renderer/pass.hpp>
#include <renderer/data/frame_constants.hpp>
namespace lt::renderer::vk { namespace lt::renderer::vk {
@ -12,12 +13,12 @@ Pass::Pass(
) )
: m_device(static_cast<Device *>(device)) : m_device(static_cast<Device *>(device))
, m_layout(m_device->create_pipeline_layout( , m_layout(m_device->create_pipeline_layout(
VkPipelineLayoutCreateInfo { std::vector<VkPushConstantRange> {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, VkPushConstantRange {
.setLayoutCount = 0u, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.pSetLayouts = nullptr, .offset = 0u,
.pushConstantRangeCount = 0u, .size = sizeof(FrameConstants),
.pPushConstantRanges = nullptr, },
} }
)) ))
{ {
@ -112,17 +113,16 @@ Pass::Pass(
.blendConstants = { 0.0f, 0.0, 0.0, 0.0 }, .blendConstants = { 0.0f, 0.0, 0.0, 0.0 },
}; };
// auto attachment_description = VkAttachmentDescription {
auto attachment_description = VkAttachmentDescription { // .format =,
.format = static_cast<Swapchain *>(swapchain)->get_format(), // .samples = VK_SAMPLE_COUNT_1_BIT,
.samples = VK_SAMPLE_COUNT_1_BIT, // .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, // .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE, // .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, // .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, // .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, // .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // };
};
auto color_attachment_ref = VkAttachmentReference { auto color_attachment_ref = VkAttachmentReference {
.attachment = 0, .attachment = 0,
@ -144,21 +144,18 @@ Pass::Pass(
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
}; };
m_pass = m_device->create_pass( auto color_format = static_cast<Swapchain *>(swapchain)->get_format();
VkRenderPassCreateInfo { auto rendering_info = VkPipelineRenderingCreateInfoKHR {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.attachmentCount = 1u, .colorAttachmentCount = 1u,
.pAttachments = &attachment_description, .pColorAttachmentFormats = &color_format,
.subpassCount = 1u,
.pSubpasses = &subpass_description, };
.dependencyCount = 1u,
.pDependencies = &pass_dependency,
}
);
m_pipeline = m_device->create_graphics_pipeline( m_pipeline = m_device->create_graphics_pipeline(
VkGraphicsPipelineCreateInfo { VkGraphicsPipelineCreateInfo {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &rendering_info,
.stageCount = static_cast<uint32_t>(shader_stages.size()), .stageCount = static_cast<uint32_t>(shader_stages.size()),
.pStages = shader_stages.data(), .pStages = shader_stages.data(),
.pVertexInputState = &vertex_input, .pVertexInputState = &vertex_input,
@ -170,15 +167,14 @@ Pass::Pass(
.pColorBlendState = &color_blend, .pColorBlendState = &color_blend,
.pDynamicState = &dynamic_state, .pDynamicState = &dynamic_state,
.layout = m_layout, .layout = m_layout,
.renderPass = m_pass, .renderPass = VK_NULL_HANDLE,
.subpass = 0u, .subpass = 0u,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1, .basePipelineIndex = -1,
} }
); );
m_framebuffers = static_cast<Swapchain *>(swapchain)->create_framebuffers_for_pass(m_pass); // m_framebuffers = static_cast<Swapchain *>(swapchain)->create_framebuffers_for_pass(m_pass);
m_device->destroy_shader_module(vertex_module); m_device->destroy_shader_module(vertex_module);
m_device->destroy_shader_module(fragment_module); m_device->destroy_shader_module(fragment_module);
@ -194,7 +190,7 @@ Pass::~Pass()
m_device->wait_idle(); m_device->wait_idle();
m_device->destroy_framebuffers(m_framebuffers); m_device->destroy_framebuffers(m_framebuffers);
m_device->destroy_pipeline(m_pipeline); m_device->destroy_pipeline(m_pipeline);
m_device->destroy_pass(m_pass); // m_device->destroy_pass(m_pass);
m_device->destroy_pipeline_layout(m_layout); m_device->destroy_pipeline_layout(m_layout);
} }
@ -207,7 +203,8 @@ void Pass::replace_swapchain(const ISwapchain &swapchain)
m_device->wait_idle(); m_device->wait_idle();
m_device->destroy_framebuffers(m_framebuffers); m_device->destroy_framebuffers(m_framebuffers);
m_framebuffers = static_cast<const Swapchain &>(swapchain).create_framebuffers_for_pass(m_pass); // m_framebuffers = static_cast<const Swapchain
// &>(swapchain).create_framebuffers_for_pass(m_pass);
} }
auto Pass::create_module(lt::assets::Blob blob) -> VkShaderModule auto Pass::create_module(lt::assets::Blob blob) -> VkShaderModule

View file

@ -29,16 +29,16 @@ public:
void replace_swapchain(const ISwapchain &swapchain); void replace_swapchain(const ISwapchain &swapchain);
[[nodiscard]] auto get_pass() -> VkRenderPass
{
return m_pass;
}
[[nodiscard]] auto get_pipeline() -> VkPipeline [[nodiscard]] auto get_pipeline() -> VkPipeline
{ {
return m_pipeline; return m_pipeline;
} }
[[nodiscard]] auto get_layout() -> VkPipelineLayout
{
return m_layout;
}
[[nodiscard]] auto get_framebuffers() -> std::vector<VkFramebuffer> & [[nodiscard]] auto get_framebuffers() -> std::vector<VkFramebuffer> &
{ {
return m_framebuffers; return m_framebuffers;
@ -49,8 +49,6 @@ private:
memory::NullOnMove<class Device *> m_device {}; memory::NullOnMove<class Device *> m_device {};
VkRenderPass m_pass = VK_NULL_HANDLE;
VkPipeline m_pipeline = VK_NULL_HANDLE; VkPipeline m_pipeline = VK_NULL_HANDLE;
VkPipelineLayout m_layout = VK_NULL_HANDLE; VkPipelineLayout m_layout = VK_NULL_HANDLE;

View file

@ -100,6 +100,7 @@ Renderer::~Renderer()
vk_reset_command_buffer(cmd, {}); vk_reset_command_buffer(cmd, {});
record_cmd(cmd, *image_idx); record_cmd(cmd, *image_idx);
auto wait_stage = VkPipelineStageFlags { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; auto wait_stage = VkPipelineStageFlags { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
auto &submit_semaphore = m_submit_semaphores[*image_idx]; auto &submit_semaphore = m_submit_semaphores[*image_idx];
m_device->submit( m_device->submit(
@ -142,35 +143,52 @@ void Renderer::replace_swapchain(ISwapchain *swapchain)
void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx) void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
{ {
auto cmd_begin_info = VkCommandBufferBeginInfo { const auto cmd_begin_info = VkCommandBufferBeginInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = {}, .flags = {},
.pInheritanceInfo = nullptr, .pInheritanceInfo = nullptr,
}; };
vkc(vk_begin_command_buffer(cmd, &cmd_begin_info)); const auto begin_frame_barrier = VkImageMemoryBarrier {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = {},
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.image = m_swapchain->get_image(image_idx),
.subresourceRange = VkImageSubresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0u,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0u,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
auto clear_value = VkClearValue {
.color = {
0.93,
0.93,
0.93,
1.0,
},
};
auto pass_begin_info = VkRenderPassBeginInfo {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = m_pass->get_pass(),
.framebuffer = m_pass->get_framebuffers()[image_idx],
.renderArea = { .offset = {}, .extent = m_resolution },
.clearValueCount = 1u,
.pClearValues = &clear_value
}; };
vk_cmd_begin_render_pass(cmd, &pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
vk_cmd_bind_pipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pass->get_pipeline());
auto viewport = VkViewport { const auto end_frame_barrier = VkImageMemoryBarrier {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstAccessMask = {},
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.image = m_swapchain->get_image(image_idx),
.subresourceRange = VkImageSubresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0u,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0u,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
const auto scissor = VkRect2D {
.offset = { .x = 0u, .y = 0u },
.extent = m_resolution,
};
const auto viewport = VkViewport {
.x = 0.0f, .x = 0.0f,
.y = 0.0f, .y = 0.0f,
.width = static_cast<float>(m_resolution.width), .width = static_cast<float>(m_resolution.width),
@ -178,17 +196,70 @@ void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
.minDepth = 0.0f, .minDepth = 0.0f,
.maxDepth = 1.0f, .maxDepth = 1.0f,
}; };
vk_cmd_set_viewport(cmd, 0, 1, &viewport);
auto scissor = VkRect2D { const auto color_attachment_info = VkRenderingAttachmentInfoKHR {
.offset = { 0u, 0u }, .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.extent = m_resolution, .imageView = m_swapchain->get_image_view(image_idx),
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.resolveMode = VK_RESOLVE_MODE_NONE,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = VkClearValue { .color = { 0.93, 0.93, 0.93, 1.0 } },
}; };
vk_cmd_set_scissors(cmd, 0, 1, &scissor);
const auto rendering_info = VkRenderingInfoKHR {
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR,
.renderArea = scissor,
.layerCount = 1,
.colorAttachmentCount = 1,
.pColorAttachments = &color_attachment_info,
};
vkc(vk_begin_command_buffer(cmd, &cmd_begin_info));
vk_cmd_push_constants(
cmd,
m_pass->get_layout(),
VK_SHADER_STAGE_VERTEX_BIT,
0u,
sizeof(FrameConstants),
&m_frame_constants
);
vk_cmd_pipeline_barrier(
cmd,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0,
0,
nullptr,
0,
nullptr,
1,
&begin_frame_barrier
);
vk_cmd_begin_rendering(cmd, &rendering_info);
vk_cmd_bind_pipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pass->get_pipeline());
vk_cmd_set_viewport(cmd, 0, 1, &viewport);
vk_cmd_set_scissors(cmd, 0, 1, &scissor);
vk_cmd_draw(cmd, 3, 1, 0, 0); vk_cmd_draw(cmd, 3, 1, 0, 0);
vk_cmd_end_render_pass(cmd); vk_cmd_end_rendering(cmd);
vk_cmd_pipeline_barrier(
cmd,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
0,
nullptr,
0,
nullptr,
1,
&end_frame_barrier
);
vkc(vk_end_command_buffer(cmd)); vkc(vk_end_command_buffer(cmd));
} }
void submit_sprite(const components::Sprite &sprite, const math::components::Transform &transform)
{
}
} // namespace lt::renderer::vk } // namespace lt::renderer::vk

View file

@ -3,8 +3,10 @@
#include <memory/reference.hpp> #include <memory/reference.hpp>
#include <ranges> #include <ranges>
#include <renderer/backend/vk/context/device.hpp> #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/renderer/pass.hpp>
#include <renderer/backend/vk/utils.hpp> #include <renderer/backend/vk/utils.hpp>
#include <renderer/frontend/data/buffer.hpp>
#include <renderer/frontend/renderer/pass.hpp> #include <renderer/frontend/renderer/pass.hpp>
#include <renderer/frontend/renderer/renderer.hpp> #include <renderer/frontend/renderer/renderer.hpp>
@ -29,6 +31,18 @@ public:
void replace_swapchain(ISwapchain *swapchain) 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: private:
void record_cmd(VkCommandBuffer cmd, uint32_t image_idx); void record_cmd(VkCommandBuffer cmd, uint32_t image_idx);
@ -51,6 +65,8 @@ private:
VkExtent2D m_resolution; VkExtent2D m_resolution;
uint32_t m_max_frames_in_flight {}; uint32_t m_max_frames_in_flight {};
FrameConstants m_frame_constants;
}; };
} // namespace lt::renderer::vk } // namespace lt::renderer::vk

View file

@ -1,18 +0,0 @@
// template<>
// struct std::formatter<VkExtent2D>
// {
// constexpr auto parse(std::format_parse_context &context)
// {
// return context.begin();
// }
//
// auto format(const VkExtent2D &val, std::format_context &context) const
// {
// return std::format_to(context.out(), "{}, {}", val.width, val.height);
// }
// };
//
// inline auto operator==(VkExtent2D lhs, VkExtent2D rhs) -> bool
// {
// return lhs.width == rhs.width && lhs.height == rhs.height;
// }

View file

@ -13,8 +13,9 @@ extern PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue
extern PFN_vkCreateDevice vk_create_device; extern PFN_vkCreateDevice vk_create_device;
extern PFN_vkGetDeviceProcAddr vk_get_device_proc_address; extern PFN_vkGetDeviceProcAddr vk_get_device_proc_address;
extern PFN_vkDestroyDevice vk_destroy_device; extern PFN_vkDestroyDevice vk_destroy_device;
extern PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features; extern PFN_vkGetPhysicalDeviceFeatures2 vk_get_physical_device_features;
extern PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties; extern PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties;
extern PFN_vkGetPhysicalDeviceMemoryProperties vk_get_physical_device_memory_properties;
// extension instance functions // extension instance functions
extern PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label; extern PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label;
@ -75,8 +76,29 @@ extern PFN_vkCmdBindPipeline vk_cmd_bind_pipeline;
extern PFN_vkCmdDraw vk_cmd_draw; extern PFN_vkCmdDraw vk_cmd_draw;
extern PFN_vkCmdSetViewport vk_cmd_set_viewport; extern PFN_vkCmdSetViewport vk_cmd_set_viewport;
extern PFN_vkCmdSetScissor vk_cmd_set_scissors; extern PFN_vkCmdSetScissor vk_cmd_set_scissors;
extern PFN_vkCmdPushConstants vk_cmd_push_constants;
extern PFN_vkCmdCopyBuffer vk_cmd_copy_buffer;
extern PFN_vkCreateDescriptorSetLayout vk_create_descriptor_set_layout;
extern PFN_vkDestroyDescriptorSetLayout vk_destroy_descriptor_set_layout;
extern PFN_vkCreateDescriptorPool vk_create_descriptor_pool;
extern PFN_vkDestroyDescriptorPool vk_destroy_descriptor_pool;
extern PFN_vkAllocateDescriptorSets vk_allocate_descriptor_sets;
extern PFN_vkFreeDescriptorSets vk_free_descriptor_sets;
extern PFN_vkCreateBuffer vk_create_buffer;
extern PFN_vkDestroyBuffer vk_destroy_buffer;
extern PFN_vkGetBufferMemoryRequirements vk_get_buffer_memory_requirements;
extern PFN_vkAllocateMemory vk_allocate_memory;
extern PFN_vkBindBufferMemory vk_bind_buffer_memory;
extern PFN_vkMapMemory vk_map_memory;
extern PFN_vkUnmapMemory vk_unmap_memory;
extern PFN_vkFreeMemory vk_free_memory;
extern PFN_vkResetCommandBuffer vk_reset_command_buffer; extern PFN_vkResetCommandBuffer vk_reset_command_buffer;
extern PFN_vkCmdBeginRendering vk_cmd_begin_rendering;
extern PFN_vkCmdEndRendering vk_cmd_end_rendering;
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace lt::renderer::vk } // namespace lt::renderer::vk

View file

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

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

@ -0,0 +1,115 @@
#include <renderer/frontend/data/buffer.hpp>
#include <renderer/test/utils.hpp>
using ::lt::renderer::IBuffer;
using enum ::lt::renderer::IMessenger::MessageSeverity;
Suite raii = "buffer_raii"_suite = [] {
Case { "happy path won't throw" } = [] {
auto fixture = FixtureDeviceSwapchain {};
for (auto idx = 0; idx <= std::to_underlying(IBuffer::Usage::staging); ++idx)
{
ignore = IBuffer::create(
lt::renderer::Api::vulkan,
fixture.device(),
fixture.gpu(),
IBuffer::CreateInfo {
.usage = static_cast<IBuffer::Usage>(idx),
.size = 1000u,
.debug_name = "",
}
);
}
expect_false(fixture.has_any_messages_of(error));
expect_false(fixture.has_any_messages_of(warning));
};
Case { "unhappy path throws" } = [] {
auto fixture = FixtureDeviceSwapchain {};
auto info = IBuffer::CreateInfo {
.usage = IBuffer::Usage::vertex,
.size = 10000u,
.debug_name = "",
};
expect_throw([&] {
ignore = IBuffer::create(lt::renderer::Api::vulkan, nullptr, fixture.gpu(), info);
});
expect_throw([&] {
ignore = IBuffer::create(lt::renderer::Api::vulkan, fixture.device(), nullptr, info);
});
expect_throw([&, info] mutable {
info.size = 0;
ignore = IBuffer::create(
lt::renderer::Api::vulkan,
fixture.device(),
fixture.gpu(),
info
);
});
expect_throw([&] {
ignore = IBuffer::create(
lt::renderer::Api::direct_x,
fixture.device(),
fixture.gpu(),
info
);
});
expect_throw([&] {
ignore = IBuffer::create(
lt::renderer::Api::metal,
fixture.device(),
fixture.gpu(),
info
);
});
expect_throw([&] {
ignore = IBuffer::create(
lt::renderer::Api::none,
fixture.device(),
fixture.gpu(),
info
);
});
/** Make sure the default-case was OK */
ignore = IBuffer::create(lt::renderer::Api::vulkan, fixture.device(), fixture.gpu(), info);
expect_false(fixture.has_any_messages_of(error));
expect_false(fixture.has_any_messages_of(warning));
};
};
Suite mapping = "buffer_mapping"_suite = [] {
Case { "mapping" } = [] {
auto fixture = FixtureDeviceSwapchain {};
constexpr auto size = 1000u;
auto buffer = IBuffer::create(
lt::renderer::Api::vulkan,
fixture.device(),
fixture.gpu(),
IBuffer::CreateInfo {
.usage = IBuffer::Usage::staging,
.size = size,
.debug_name = "",
}
);
auto map = buffer->map();
expect_eq(map.size(), size);
expect_not_nullptr(map.data());
expect_false(fixture.has_any_messages_of(error));
expect_false(fixture.has_any_messages_of(warning));
};
};

View file

@ -7,7 +7,7 @@ using ::lt::renderer::IMessenger;
Suite raii = "pass_raii"_suite = [] { Suite raii = "pass_raii"_suite = [] {
Case { "happy path won't throw" } = [] { Case { "happy path won't throw" } = [] {
auto fixture = Fixture_RendererSystem {}; Fixture_ auto fixture = Fixture_RendererSystem {};
auto &system = fixture.renderer_system(); auto &system = fixture.renderer_system();
std::ignore = lt::renderer::IPass::create( std::ignore = lt::renderer::IPass::create(

View file

@ -1,7 +1,10 @@
#pragma once #pragma once
#include <math/components/transform.hpp>
#include <memory/scope.hpp> #include <memory/scope.hpp>
#include <renderer/api.hpp> #include <renderer/api.hpp>
#include <renderer/components/sprite.hpp>
#include <renderer/data/frame_constants.hpp>
namespace lt::renderer { namespace lt::renderer {
@ -41,6 +44,13 @@ public:
[[nodiscard]] virtual auto draw(uint32_t frame_idx) -> DrawResult = 0; [[nodiscard]] virtual auto draw(uint32_t frame_idx) -> DrawResult = 0;
virtual void replace_swapchain(class ISwapchain *swapchain) = 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 } // namespace lt::renderer

View file

@ -1,4 +1,8 @@
#include <camera/components.hpp>
#include <math/algebra.hpp>
#include <math/components/transform.hpp>
#include <renderer/components/messenger.hpp> #include <renderer/components/messenger.hpp>
#include <renderer/components/sprite.hpp>
#include <renderer/frontend/context/device.hpp> #include <renderer/frontend/context/device.hpp>
#include <renderer/frontend/context/gpu.hpp> #include <renderer/frontend/context/gpu.hpp>
#include <renderer/frontend/context/instance.hpp> #include <renderer/frontend/context/instance.hpp>
@ -70,6 +74,30 @@ void System::tick(app::TickInfo tick)
} }
} }
auto perspective = math::mat4::identity();
for (auto [id, camera] : m_registry->view<lt::camera::components::PerspectiveCamera>())
{
if (camera.is_primary)
{
perspective = math::perspective(
camera.vertical_fov,
camera.aspect_ratio,
camera.near_plane,
camera.far_plane
);
break;
}
}
// for each sprite, submit a new "model matrix" + "color" to go into the scene's SSBO
for (auto &[id, sprite, transform] :
m_registry->view<components::Sprite, math::components::Transform>())
{
m_renderer->submit_sprite(sprite, transform);
}
m_renderer->set_frame_constants({ .view_projection = perspective });
if (m_renderer->draw(m_frame_idx) != IRenderer::DrawResult::success) if (m_renderer->draw(m_frame_idx) != IRenderer::DrawResult::success)
{ {
m_swapchain.reset(); m_swapchain.reset();

View file

@ -1 +0,0 @@

View file

@ -148,7 +148,6 @@ public:
); );
} }
[[nodiscard]] auto has_any_messages() const -> bool [[nodiscard]] auto has_any_messages() const -> bool
{ {
return m_user_data->m_has_any_messages; return m_user_data->m_has_any_messages;

View file

@ -0,0 +1,77 @@
#pragma once
#include <assets/shader.hpp>
#include <math/vec3.hpp>
#include <memory/reference.hpp>
namespace lt::renderer::components {
enum class VertexFormat : uint8_t
{
r32_g32_b32_sfloat,
r32_g32_sfloat,
};
enum class VertexInputRate : uint8_t
{
per_vertex,
per_instance,
};
struct VertexInputAttributeDescriptipn
{
uint32_t location;
uint32_t binding;
uint32_t offset;
VertexFormat format;
};
struct VertexInputBindingDescription
{
uint32_t binding;
uint32_t stride;
};
/** Requires a math::components::Transform component on the same entity to be functional. */
struct Sprite
{
struct Vertex
{
math::vec3 position;
math::vec3 color;
[[nodiscard]] constexpr static auto get_attributes()
-> std::array<VertexInputAttributeDescriptipn, 2>
{
return {
VertexInputAttributeDescriptipn {
.location = 0u,
.binding = 0u,
.offset = offsetof(Sprite::Vertex, position),
.format = VertexFormat::r32_g32_b32_sfloat,
},
VertexInputAttributeDescriptipn {
.location = 1u,
.binding = 0u,
.offset = offsetof(Sprite::Vertex, color),
.format = VertexFormat::r32_g32_b32_sfloat,
},
};
}
};
memory::Ref<assets::ShaderAsset> vertex_shader;
memory::Ref<assets::ShaderAsset> fragment_shader;
};
} // namespace lt::renderer::components

View file

@ -0,0 +1,12 @@
#pragma once
#include <math/mat4.hpp>
namespace lt::renderer {
struct FrameConstants
{
math::mat4 view_projection;
};
} // namespace lt::renderer

View file

@ -7,7 +7,6 @@
namespace lt::renderer { namespace lt::renderer {
class IMessenger class IMessenger
{ {
public: public:

View file

@ -55,31 +55,6 @@ public:
void tick(app::TickInfo tick) override; void tick(app::TickInfo tick) override;
[[nodiscard]] auto get_surface() -> class ISurface *
{
return m_surface.get();
}
[[nodiscard]] auto get_gpu() -> class IGpu *
{
return m_gpu.get();
}
[[nodiscard]] auto get_device() -> class IDevice *
{
return m_device.get();
}
[[nodiscard]] auto get_swapchain() -> class ISwapchain *
{
return m_swapchain.get();
}
[[nodiscard]] auto get_renderer() -> class IRenderer *
{
return m_renderer.get();
}
[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override [[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override
{ {
return m_last_tick_result; return m_last_tick_result;

View file

@ -4,4 +4,4 @@ add_library_module(fuzz_test test.cpp fuzz.cpp)
target_link_libraries(test PUBLIC tbb logger) target_link_libraries(test PUBLIC tbb logger)
target_link_libraries(fuzz_test PUBLIC tbb logger) target_link_libraries(fuzz_test PUBLIC tbb logger)
add_test_module(test test.test.cpp mock.test.cpp) add_test_module(test test.test.cpp)

View file

@ -1,52 +0,0 @@
#include <test/mock.hpp>
#include <test/test.hpp>
using namespace lt::test;
using namespace lt::test::mock;
class ExpensiveClass
{
private:
public:
virtual int expensive(std::string str, std::optional<int> opt)
{
return 0;
}
};
class MockClass: public ExpensiveClass
{
public:
int expensive(std::string str, std::optional<int> opt) override
{
return expensive_mock(str, opt);
};
Mock<int(std::string, std::optional<int>)> expensive_mock {};
};
class ExpensiveUser
{
public:
ExpensiveUser(ExpensiveClass &dependency)
{
dependency.expensive("", 10);
}
};
// problem #1: matcher functions should construct an invokable object to test against the indexed
// argument.
Suite raii = "mock_raii"_suite = [] {
Case { "happy path won't throw" } = [] {
auto a = std::function<int(int)> {};
auto expensive = MockClass {};
auto side_effect = false;
expensive.expensive_mock.expect("test", std::nullopt)
.apply([&](auto str, auto opt) { side_effect = true; })
.returns(69);
auto user = ExpensiveUser { expensive };
};
};

View file

@ -1,94 +0,0 @@
#pragma once
namespace lt::test {
template<typename _Signature>
class Mock;
template<typename Return_Type, typename... Arg_Types>
class Mock<Return_Type(Arg_Types...)>
{
public:
auto at_least() -> Mock &
{
return *this;
}
auto operator&&(Mock &mock) -> Mock &
{
return mock;
}
auto operator()(Arg_Types... arguments) -> Return_Type
{
++m_call_index;
for (auto &side_effect : m_side_effects)
{
side_effect(std::forward<Arg_Types>(arguments)...);
}
if (m_return_func)
{
return m_return_func(std::forward<Arg_Types>(arguments)...);
}
return m_return_value;
}
/** With any arguments. */
template<uint32_t counter = 1>
auto expect() -> Mock &
{
m_expected_counter = counter;
return *this;
}
auto apply(std::function<void(Arg_Types...)> side_effect) -> Mock &
{
m_side_effects.emplace_back(std::move(side_effect));
return *this;
}
/** Returns a fixed value. */
auto returns(Return_Type value) -> Mock &
{
m_return_value = value;
return *this;
}
/** Returns a value based on input. */
auto returns(std::function<Return_Type(Arg_Types...)> func) -> Mock &
{
m_return_func = std::move(func);
return *this;
}
private:
Return_Type m_return_value {};
std::function<Return_Type(Arg_Types...)> m_return_func {};
std::vector<std::function<void(Arg_Types...)>> m_side_effects {};
uint32_t m_call_index = 0;
std::vector<std::pair<std::tuple<Arg_Types...>, uint32_t>> m_expected_args;
uint32_t m_expected_counter {};
};
namespace mock::range {
[[nodiscard]] auto is_empty() -> bool
{
return false;
}
}; // namespace mock::range
[[nodiscard]] auto eq(auto rhs) -> bool
{
return false;
}
} // namespace lt::test

View file

@ -220,7 +220,7 @@ tar xf 'vulkansdk-linux-x86_64-1.4.328.1.tar.xz' \
-fsanitize-ignorelist=/msan/ignorelist_all_sources \ -fsanitize-ignorelist=/msan/ignorelist_all_sources \
-fno-omit-frame-pointer \ -fno-omit-frame-pointer \
-g \ -g \
-std=c++23 \ -std=c++26 \
-nostdinc++ \ -nostdinc++ \
-isystem /libcxx_msan/include/c++/v1/"\ -isystem /libcxx_msan/include/c++/v1/"\
&& export CFLAGS="\ && export CFLAGS="\
@ -233,7 +233,7 @@ tar xf 'vulkansdk-linux-x86_64-1.4.328.1.tar.xz' \
-fsanitize-memory-track-origins \ -fsanitize-memory-track-origins \
-fsanitize-ignorelist=/msan/ignorelist_all_sources \ -fsanitize-ignorelist=/msan/ignorelist_all_sources \
-g \ -g \
-std=c++23 \ -std=c++26 \
-L/msan/lib -Wl,-rpath,/msan/lib \ -L/msan/lib -Wl,-rpath,/msan/lib \
-L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \
-lc++ \ -lc++ \
@ -272,7 +272,7 @@ export CXXFLAGS="\
-fsanitize-ignorelist=/msan/ignorelist_all_sources \ -fsanitize-ignorelist=/msan/ignorelist_all_sources \
-fno-omit-frame-pointer \ -fno-omit-frame-pointer \
-g \ -g \
-std=c++23 \ -std=c++26 \
-nostdinc++ \ -nostdinc++ \
-isystem /libcxx_msan/include/c++/v1/"\ -isystem /libcxx_msan/include/c++/v1/"\
&& export CFLAGS="\ && export CFLAGS="\
@ -285,7 +285,7 @@ export CXXFLAGS="\
-fsanitize-memory-track-origins \ -fsanitize-memory-track-origins \
-fsanitize-ignorelist=/msan/ignorelist_all_sources \ -fsanitize-ignorelist=/msan/ignorelist_all_sources \
-g \ -g \
-std=c++23 \ -std=c++26 \
-L/msan/lib -Wl,-rpath,/msan/lib \ -L/msan/lib -Wl,-rpath,/msan/lib \
-L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \
-lc++ \ -lc++ \

View file

@ -34,14 +34,14 @@ cmake \
-fsanitize-memory-track-origins \ -fsanitize-memory-track-origins \
-g \ -g \
-fno-omit-frame-pointer \ -fno-omit-frame-pointer \
-std=c++23 \ -std=c++26 \
-nostdinc++ \ -nostdinc++ \
-isystem /libcxx_msan/include/c++/v1/" \ -isystem /libcxx_msan/include/c++/v1/" \
-D CMAKE_EXE_LINKER_FLAGS=" \ -D CMAKE_EXE_LINKER_FLAGS=" \
-fsanitize=memory \ -fsanitize=memory \
-fsanitize-memory-track-origins \ -fsanitize-memory-track-origins \
-g \ -g \
-std=c++23 \ -std=c++26 \
-L/msan/lib -Wl,-rpath,/msan/lib \ -L/msan/lib -Wl,-rpath,/msan/lib \
-L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \
-lc++ \ -lc++ \

View file

@ -22,7 +22,7 @@ cmake \
-D CMAKE_LINKER_TYPE=MOLD \ -D CMAKE_LINKER_TYPE=MOLD \
-D ENABLE_UNIT_TESTS=ON \ -D ENABLE_UNIT_TESTS=ON \
-D CMAKE_BUILD_TYPE=Release \ -D CMAKE_BUILD_TYPE=Release \
-D CMAKE_CXX_FLAGS="-std=c++23 -g -fno-omit-frame-pointer" -D CMAKE_CXX_FLAGS="-std=c++26 -g -fno-omit-frame-pointer"
cmake --build ./build -j"$(nproc)" cmake --build ./build -j"$(nproc)"

View file

@ -25,7 +25,7 @@ cmake \
-D CMAKE_LINKER_TYPE=MOLD \ -D CMAKE_LINKER_TYPE=MOLD \
-D ENABLE_UNIT_TESTS=ON \ -D ENABLE_UNIT_TESTS=ON \
-D CMAKE_BUILD_TYPE=Release \ -D CMAKE_BUILD_TYPE=Release \
-D CMAKE_CXX_FLAGS="-std=c++23 -fno-omit-frame-pointer -fno-common -g" -D CMAKE_CXX_FLAGS="-std=c++26 -fno-omit-frame-pointer -fno-common -g"
cmake --build ./build -j"$(nproc)" cmake --build ./build -j"$(nproc)"

View file

@ -17,6 +17,6 @@ cmake \
-D ENABLE_UNIT_TESTS=ON \ -D ENABLE_UNIT_TESTS=ON \
-D ENABLE_STATIC_ANALYSIS=ON \ -D ENABLE_STATIC_ANALYSIS=ON \
-D CMAKE_BUILD_TYPE=Release \ -D CMAKE_BUILD_TYPE=Release \
-D CMAKE_CXX_FLAGS="-std=c++23 -stdlib=libc++" -D CMAKE_CXX_FLAGS="-std=c++26 -stdlib=libc++"
cmake --build . -j"$(nproc)" cmake --build . -j"$(nproc)"