Compare commits

..

1 commit

Author SHA1 Message Date
4ce413a1d7
wip
Some checks are pending
continuous-integration/drone/pr Build is running
2025-10-21 15:37:33 +03:30
53 changed files with 854 additions and 1217 deletions

View file

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

View file

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

128
CODE_OF_CONDUCT.md Normal file
View file

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

Binary file not shown.

View file

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

View file

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

View file

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

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

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

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

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

View file

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

View file

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

View file

@ -2,11 +2,9 @@
#include <app/application.hpp>
#include <app/entrypoint.hpp>
#include <app/system.hpp>
#include <camera/components.hpp>
#include <ecs/entity.hpp>
#include <input/components.hpp>
#include <input/system.hpp>
#include <math/trig.hpp>
#include <math/vec2.hpp>
#include <memory/reference.hpp>
#include <memory/scope.hpp>
@ -68,18 +66,13 @@ public:
const auto &[x, y] = surface.get_position();
const auto &[width, height] = surface.get_resolution();
if (input.get_action(m_quit_action_key).state == State::active)
{
should_quit = true;
}
if (input.get_action(m_debug_action_keys[0]).state == State::active)
{
for (auto &[id, camera] :
m_registry->view<lt::camera::components::PerspectiveCamera>())
{
camera.vertical_fov += (static_cast<float>(tick.delta_time.count()) * 40.0f);
}
surface.push_request(surface::ModifyPositionRequest({ x + 5, y + 5 }));
}
if (input.get_action(m_debug_action_keys[1]).state == State::active)
@ -226,20 +219,6 @@ public:
.callback = &renderer_callback,
.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()
@ -266,8 +245,6 @@ private:
memory::Ref<MirrorSystem> m_mirror_system;
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>

View file

@ -8,29 +8,21 @@ add_library_module(
backend/vk/context/instance.cpp
backend/vk/context/surface.cpp
backend/vk/context/swapchain.cpp
backend/vk/data/buffer.cpp
backend/vk/renderer/pass.cpp
backend/vk/renderer/renderer.cpp
# frontend
# Vulkan - frontend
frontend/messenger.cpp
frontend/context/device.cpp
frontend/context/gpu.cpp
frontend/context/instance.cpp
frontend/context/surface.cpp
frontend/context/swapchain.cpp
frontend/data/buffer.cpp
frontend/renderer/renderer.cpp
frontend/renderer/pass.cpp)
target_link_libraries(
renderer
PUBLIC app
ecs
memory
assets
time
bitwise
camera
PUBLIC app ecs memory assets time bitwise
PRIVATE surface pthread)
add_test_module(
@ -42,10 +34,11 @@ add_test_module(
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/pass.test.cpp
frontend/renderer/renderer.test.cpp
# 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)

View file

@ -75,17 +75,10 @@ void Device::initialize_logical_device()
auto extensions = std::vector<const char *> {
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 {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &dynamic_rendering_features,
.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size()),
.pQueueCreateInfos = queue_infos.data(),
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
@ -214,31 +207,6 @@ void Device::wait_for_fences(std::span<VkFence> fences) const
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
{
auto *swapchain = VkSwapchainKHR {};
@ -283,18 +251,9 @@ void Device::unmap_memory(VkDeviceMemory memory)
return pass;
}
[[nodiscard]] auto Device::create_pipeline_layout(
std::vector<VkPushConstantRange> push_constant_ranges
) const -> VkPipelineLayout
[[nodiscard]] auto Device::create_pipeline_layout(VkPipelineLayoutCreateInfo info) 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 {};
vkc(vk_create_pipeline_layout(m_device, &info, nullptr, &pipeline_layout));
return pipeline_layout;
@ -340,13 +299,6 @@ void Device::unmap_memory(VkDeviceMemory memory)
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
-> std::vector<VkCommandBuffer>
{
@ -355,18 +307,6 @@ void Device::unmap_memory(VkDeviceMemory memory)
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
{
vk_destroy_swapchain_khr(m_device, swapchain, m_allocator);
@ -449,9 +389,4 @@ 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

View file

@ -96,16 +96,6 @@ public:
[[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 */
[[nodiscard]] auto create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR;
@ -118,9 +108,8 @@ public:
[[nodiscard]] auto create_pass(VkRenderPassCreateInfo info) const -> VkRenderPass;
[[nodiscard]] auto create_pipeline_layout(
std::vector<VkPushConstantRange> push_constant_ranges
) const -> VkPipelineLayout;
[[nodiscard]] auto create_pipeline_layout(VkPipelineLayoutCreateInfo info) const
-> VkPipelineLayout;
[[nodiscard]] auto create_shader_module(VkShaderModuleCreateInfo info) const -> VkShaderModule;
@ -131,17 +120,10 @@ public:
[[nodiscard]] auto create_fences(VkFenceCreateInfo info, uint32_t count) const
-> std::vector<VkFence>;
[[nodiscard]] auto create_buffer(VkBufferCreateInfo info) const -> VkBuffer;
/** allocation functions */
[[nodiscard]] auto allocate_memory(VkMemoryAllocateInfo info) const -> VkDeviceMemory;
[[nodiscard]] auto allocate_command_buffers(VkCommandBufferAllocateInfo info) const
-> std::vector<VkCommandBuffer>;
/** de-allocation functions */
void free_memory(VkDeviceMemory memory) const;
/** destroy functions */
void destroy_swapchain(VkSwapchainKHR swapchain) const;
@ -171,8 +153,6 @@ public:
void destroy_fences(std::span<VkFence> fences) const;
void destroy_buffer(VkBuffer buffer) const;
private:
template<typename T>
static auto get_object_type(const T &object) -> VkObjectType

View file

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

View file

@ -12,6 +12,13 @@ class Gpu: public IGpu
public:
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(
VkSurfaceKHR surface,
uint32_t queue_family_idx
@ -23,39 +30,8 @@ public:
[[nodiscard]] auto get_surface_formats(VkSurfaceKHR surface) const
-> 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:
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

View file

@ -29,9 +29,8 @@ PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue_family
PFN_vkCreateDevice vk_create_device {};
PFN_vkGetDeviceProcAddr vk_get_device_proc_address {};
PFN_vkDestroyDevice vk_destroy_device {};
PFN_vkGetPhysicalDeviceFeatures2 vk_get_physical_device_features {};
PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features {};
PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties {};
PFN_vkGetPhysicalDeviceMemoryProperties vk_get_physical_device_memory_properties {};
// extension instance functions
PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label {};
@ -87,37 +86,15 @@ PFN_vkCmdBindPipeline vk_cmd_bind_pipeline {};
PFN_vkCmdDraw vk_cmd_draw {};
PFN_vkCmdSetViewport vk_cmd_set_viewport {};
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_vkCmdBeginRendering vk_cmd_begin_rendering {};
PFN_vkCmdEndRendering vk_cmd_end_rendering {};
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {};
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {};
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {};
auto vk_create_xlib_surface_khr = PFN_vkCreateXlibSurfaceKHR {};
auto vk_destroy_surface_khr = PFN_vkDestroySurfaceKHR {};
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
Instance::Instance()
@ -150,7 +127,6 @@ void Instance::initialize_instance()
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
VK_KHR_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";
@ -322,7 +298,6 @@ void Instance::load_instance_functions()
load_fn(vk_destroy_device, "vkDestroyDevice");
load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures");
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_end_debug_label, "vkCmdEndDebugUtilsLabelEXT");
@ -395,26 +370,7 @@ void Instance::load_device_functions_impl(VkDevice device)
load_fn(vk_cmd_draw, "vkCmdDraw");
load_fn(vk_cmd_set_viewport, "vkCmdSetViewport");
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_cmd_begin_rendering, "vkCmdBeginRendering");
load_fn(vk_cmd_end_rendering, "vkCmdEndRendering");
}
auto Instance::enumerate_gpus() const -> std::vector<VkPhysicalDevice>

View file

@ -49,17 +49,6 @@ public:
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>;

View file

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

View file

@ -1,11 +1,10 @@
#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/vulkan.hpp>
namespace lt::renderer::vk::raii {
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
class DebugMessenger
{
public:
@ -17,108 +16,18 @@ public:
~DebugMessenger()
{
if (m_instance)
if (!m_instance)
{
m_instance->destroy_messenger(m_object);
return;
}
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:
memory::NullOnMove<Instance *> m_instance {};
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

View file

@ -1,7 +1,6 @@
#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 {
@ -13,12 +12,12 @@ Pass::Pass(
)
: m_device(static_cast<Device *>(device))
, m_layout(m_device->create_pipeline_layout(
std::vector<VkPushConstantRange> {
VkPushConstantRange {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0u,
.size = sizeof(FrameConstants),
},
VkPipelineLayoutCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0u,
.pSetLayouts = nullptr,
.pushConstantRangeCount = 0u,
.pPushConstantRanges = nullptr,
}
))
{
@ -113,16 +112,17 @@ Pass::Pass(
.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 attachment_description = VkAttachmentDescription {
.format = static_cast<Swapchain *>(swapchain)->get_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,
@ -144,18 +144,21 @@ Pass::Pass(
.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_pass = m_device->create_pass(
VkRenderPassCreateInfo {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1u,
.pAttachments = &attachment_description,
.subpassCount = 1u,
.pSubpasses = &subpass_description,
.dependencyCount = 1u,
.pDependencies = &pass_dependency,
}
);
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,
@ -167,14 +170,15 @@ Pass::Pass(
.pColorBlendState = &color_blend,
.pDynamicState = &dynamic_state,
.layout = m_layout,
.renderPass = VK_NULL_HANDLE,
.renderPass = m_pass,
.subpass = 0u,
.basePipelineHandle = VK_NULL_HANDLE,
.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(fragment_module);
@ -190,7 +194,7 @@ Pass::~Pass()
m_device->wait_idle();
m_device->destroy_framebuffers(m_framebuffers);
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);
}
@ -203,8 +207,7 @@ void Pass::replace_swapchain(const ISwapchain &swapchain)
m_device->wait_idle();
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

View file

@ -29,16 +29,16 @@ public:
void replace_swapchain(const ISwapchain &swapchain);
[[nodiscard]] auto get_pass() -> VkRenderPass
{
return m_pass;
}
[[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;
@ -49,6 +49,8 @@ private:
memory::NullOnMove<class Device *> m_device {};
VkRenderPass m_pass = VK_NULL_HANDLE;
VkPipeline m_pipeline = VK_NULL_HANDLE;
VkPipelineLayout m_layout = VK_NULL_HANDLE;

View file

@ -100,7 +100,6 @@ Renderer::~Renderer()
vk_reset_command_buffer(cmd, {});
record_cmd(cmd, *image_idx);
auto wait_stage = VkPipelineStageFlags { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
auto &submit_semaphore = m_submit_semaphores[*image_idx];
m_device->submit(
@ -143,52 +142,35 @@ void Renderer::replace_swapchain(ISwapchain *swapchain)
void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
{
const auto cmd_begin_info = VkCommandBufferBeginInfo {
auto cmd_begin_info = VkCommandBufferBeginInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = {},
.pInheritanceInfo = nullptr,
};
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,
},
vkc(vk_begin_command_buffer(cmd, &cmd_begin_info));
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());
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 {
auto viewport = VkViewport {
.x = 0.0f,
.y = 0.0f,
.width = static_cast<float>(m_resolution.width),
@ -196,70 +178,17 @@ void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
const auto color_attachment_info = VkRenderingAttachmentInfoKHR {
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.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 } },
};
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);
auto scissor = VkRect2D {
.offset = { 0u, 0u },
.extent = m_resolution,
};
vk_cmd_set_scissors(cmd, 0, 1, &scissor);
vk_cmd_draw(cmd, 3, 1, 0, 0);
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
);
vk_cmd_end_render_pass(cmd);
vkc(vk_end_command_buffer(cmd));
}
void submit_sprite(const components::Sprite &sprite, const math::components::Transform &transform)
{
}
} // namespace lt::renderer::vk

View file

@ -3,10 +3,8 @@
#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>
@ -31,18 +29,6 @@ public:
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);
@ -65,8 +51,6 @@ private:
VkExtent2D m_resolution;
uint32_t m_max_frames_in_flight {};
FrameConstants m_frame_constants;
};
} // namespace lt::renderer::vk

View file

@ -0,0 +1,18 @@
// 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,9 +13,8 @@ extern PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue
extern PFN_vkCreateDevice vk_create_device;
extern PFN_vkGetDeviceProcAddr vk_get_device_proc_address;
extern PFN_vkDestroyDevice vk_destroy_device;
extern PFN_vkGetPhysicalDeviceFeatures2 vk_get_physical_device_features;
extern PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features;
extern PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties;
extern PFN_vkGetPhysicalDeviceMemoryProperties vk_get_physical_device_memory_properties;
// extension instance functions
extern PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label;
@ -76,29 +75,8 @@ extern PFN_vkCmdBindPipeline vk_cmd_bind_pipeline;
extern PFN_vkCmdDraw vk_cmd_draw;
extern PFN_vkCmdSetViewport vk_cmd_set_viewport;
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_vkCmdBeginRendering vk_cmd_begin_rendering;
extern PFN_vkCmdEndRendering vk_cmd_end_rendering;
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace lt::renderer::vk

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,115 +0,0 @@
#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 = [] {
Case { "happy path won't throw" } = [] {
Fixture_ auto fixture = Fixture_RendererSystem {};
auto fixture = Fixture_RendererSystem {};
auto &system = fixture.renderer_system();
std::ignore = lt::renderer::IPass::create(

View file

@ -1,10 +1,7 @@
#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 {
@ -44,13 +41,6 @@ public:
[[nodiscard]] virtual auto draw(uint32_t frame_idx) -> DrawResult = 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,8 +1,4 @@
#include <camera/components.hpp>
#include <math/algebra.hpp>
#include <math/components/transform.hpp>
#include <renderer/components/messenger.hpp>
#include <renderer/components/sprite.hpp>
#include <renderer/frontend/context/device.hpp>
#include <renderer/frontend/context/gpu.hpp>
#include <renderer/frontend/context/instance.hpp>
@ -74,30 +70,6 @@ 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)
{
m_swapchain.reset();

View file

@ -0,0 +1 @@

View file

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

View file

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

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

View file

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

View file

@ -55,6 +55,31 @@ public:
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
{
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(fuzz_test PUBLIC tbb logger)
add_test_module(test test.test.cpp)
add_test_module(test test.test.cpp mock.test.cpp)

View file

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

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

View file

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

View file

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

View file

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

View file

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