test(renderer): overhaul tests & fix many bugs
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
e7c61b2faf
commit
61473c2758
50 changed files with 1030 additions and 832 deletions
|
@ -3,7 +3,6 @@ add_library_module(renderer
|
|||
|
||||
# Vulkan - backend
|
||||
backend/vk/messenger.cpp
|
||||
backend/vk/context/context.cpp
|
||||
backend/vk/context/device.cpp
|
||||
backend/vk/context/gpu.cpp
|
||||
backend/vk/context/instance.cpp
|
||||
|
@ -11,11 +10,15 @@ add_library_module(renderer
|
|||
backend/vk/context/swapchain.cpp
|
||||
backend/vk/renderer/pass.cpp
|
||||
backend/vk/renderer/renderer.cpp
|
||||
|
||||
# Vulkan - frontend
|
||||
frontend/messenger.cpp
|
||||
frontend/context/context.cpp
|
||||
frontend/context/device.cpp
|
||||
frontend/context/gpu.cpp
|
||||
frontend/context/instance.cpp
|
||||
frontend/context/surface.cpp
|
||||
frontend/context/swapchain.cpp
|
||||
frontend/renderer/renderer.cpp
|
||||
frontend/renderer/pass.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(renderer
|
||||
|
@ -31,21 +34,29 @@ PRIVATE
|
|||
pthread
|
||||
)
|
||||
|
||||
# add_test_module(renderer
|
||||
# system.test.cpp
|
||||
# vk/test_utils.cpp
|
||||
# vk/debug/messenger.test.cpp
|
||||
# vk/context/instance.test.cpp
|
||||
# vk/context/surface.test.cpp
|
||||
# vk/context/device.test.cpp
|
||||
# vk/context/swapchain.test.cpp
|
||||
# vk/context/context.test.cpp
|
||||
# vk/renderer/pass.test.cpp
|
||||
# vk/renderer/renderer.test.cpp
|
||||
# vk/pipeline.test.cpp
|
||||
# )
|
||||
# target_link_libraries(renderer_tests
|
||||
# PRIVATE
|
||||
# surface
|
||||
# pthread
|
||||
# )
|
||||
add_test_module(renderer
|
||||
test/utils.cpp
|
||||
|
||||
system.test.cpp
|
||||
|
||||
# general backend tests through the frontend
|
||||
frontend/messenger.test.cpp
|
||||
frontend/context/surface.test.cpp
|
||||
frontend/context/device.test.cpp
|
||||
frontend/context/swapchain.test.cpp
|
||||
frontend/renderer/pass.test.cpp
|
||||
frontend/renderer/renderer.test.cpp
|
||||
|
||||
# backend specific tests -- vk
|
||||
backend/vk/context/instance.test.cpp
|
||||
|
||||
# backend specific tests -- dx
|
||||
|
||||
# backend specific tests -- mt
|
||||
)
|
||||
|
||||
target_link_libraries(renderer_tests
|
||||
PRIVATE
|
||||
surface
|
||||
pthread
|
||||
)
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
#include <memory/scope.hpp>
|
||||
#include <renderer/backend/vk/context/context.hpp>
|
||||
#include <renderer/backend/vk/context/device.hpp>
|
||||
#include <renderer/backend/vk/context/gpu.hpp>
|
||||
#include <renderer/backend/vk/context/instance.hpp>
|
||||
#include <renderer/backend/vk/context/surface.hpp>
|
||||
#include <renderer/backend/vk/context/swapchain.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
Context::Context(const ecs::Entity &surface_entity)
|
||||
: m_instance(Instance::get())
|
||||
, m_surface(memory::create_scope<Surface>(m_instance, surface_entity))
|
||||
, m_gpu(memory::create_scope<Gpu>(m_instance))
|
||||
, m_device(memory::create_scope<Device>(m_gpu.get(), m_surface.get()))
|
||||
, m_swapchain(memory::create_scope<Swapchain>(m_surface.get(), m_gpu.get(), m_device.get()))
|
||||
{
|
||||
ensure(
|
||||
static_cast<Instance *>(m_instance)->vk(),
|
||||
"Failed to create vulkan context: null instance"
|
||||
);
|
||||
|
||||
ensure(
|
||||
static_cast<Surface *>(m_surface.get())->vk(),
|
||||
"Failed to create vulkan context: null surface"
|
||||
);
|
||||
|
||||
ensure(static_cast<Gpu *>(m_gpu.get())->vk(), "Failed to create vulkan context: null gpu");
|
||||
|
||||
ensure(
|
||||
static_cast<Device *>(m_device.get())->vk(),
|
||||
"Failed to create vulkan context: null device"
|
||||
);
|
||||
|
||||
ensure(
|
||||
static_cast<Swapchain *>(m_swapchain.get())->vk(),
|
||||
"Failed to create vulkan context: null swapchain"
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace lt::renderer::vk
|
|
@ -1,66 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <ecs/entity.hpp>
|
||||
#include <memory/pointer_types/null_on_move.hpp>
|
||||
#include <memory/scope.hpp>
|
||||
#include <renderer/backend/vk/context/swapchain.hpp>
|
||||
#include <renderer/frontend/context/context.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
using memory::NullOnMove;
|
||||
|
||||
/** A layer of glue between main graphics objects. */
|
||||
class Context: public IContext
|
||||
{
|
||||
public:
|
||||
Context(const ecs::Entity &surface_entity);
|
||||
|
||||
[[nodiscard]] auto instance() const -> IInstance * override
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto gpu() const -> IGpu * override
|
||||
{
|
||||
return m_gpu.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto device() const -> IDevice * override
|
||||
{
|
||||
return m_device.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto swapchain() const -> ISwapchain * override
|
||||
{
|
||||
return m_swapchain.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto surface() const -> ISurface * override
|
||||
{
|
||||
return m_surface.get();
|
||||
}
|
||||
|
||||
void recreate_swapchain() override
|
||||
{
|
||||
m_swapchain.reset();
|
||||
m_swapchain = memory::create_scope<vk::Swapchain>(
|
||||
m_surface.get(),
|
||||
m_gpu.get(),
|
||||
m_device.get()
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
IInstance *m_instance;
|
||||
|
||||
memory::Scope<ISurface> m_surface;
|
||||
|
||||
memory::Scope<IGpu> m_gpu;
|
||||
|
||||
memory::Scope<IDevice> m_device;
|
||||
|
||||
memory::Scope<ISwapchain> m_swapchain;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
|
@ -91,7 +91,7 @@ public:
|
|||
[[nodiscard]] auto acquire_image(
|
||||
VkSwapchainKHR swapchain,
|
||||
VkSemaphore semaphore,
|
||||
uint64_t timeout = 1'000'000
|
||||
uint64_t timeout = 100'000'000
|
||||
) -> std::optional<uint32_t>;
|
||||
|
||||
[[nodiscard]] auto get_swapchain_images(VkSwapchainKHR swapchain) const -> std::vector<VkImage>;
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
#include <renderer/backend/vk/context/instance.hpp>
|
||||
#include <renderer/backend/vk/vulkan.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using namespace lt;
|
||||
using renderer::vk::Instance;
|
||||
using test::Case;
|
||||
using test::expect_not_nullptr;
|
||||
using test::Suite;
|
||||
// NOLINTNEXTLINE
|
||||
Suite raii = "raii"_suite = [] {
|
||||
Case { "post singleton insantiation state is correct" } = [] {
|
||||
expect_not_nullptr(Instance::get());
|
||||
|
||||
using namespace renderer::vk;
|
||||
expect_not_nullptr(vk_get_physical_device_properties);
|
||||
expect_not_nullptr(vk_get_physical_device_queue_family_properties);
|
||||
expect_not_nullptr(vk_create_device);
|
||||
expect_not_nullptr(vk_get_device_proc_address);
|
||||
expect_not_nullptr(vk_destroy_device);
|
||||
expect_not_nullptr(vk_get_physical_device_features);
|
||||
expect_not_nullptr(vk_enumerate_device_extension_properties);
|
||||
|
||||
expect_not_nullptr(vk_cmd_begin_debug_label);
|
||||
expect_not_nullptr(vk_cmd_end_debug_label);
|
||||
expect_not_nullptr(vk_cmd_insert_debug_label);
|
||||
expect_not_nullptr(vk_create_debug_messenger);
|
||||
expect_not_nullptr(vk_destroy_debug_messenger);
|
||||
expect_not_nullptr(vk_queue_begin_debug_label);
|
||||
expect_not_nullptr(vk_queue_end_debug_label);
|
||||
expect_not_nullptr(vk_queue_insert_debug_label);
|
||||
expect_not_nullptr(vk_set_debug_object_name);
|
||||
expect_not_nullptr(vk_set_debug_object_tag);
|
||||
expect_not_nullptr(vk_submit_debug_message);
|
||||
|
||||
expect_not_nullptr(vk_get_physical_device_surface_support);
|
||||
expect_not_nullptr(vk_get_physical_device_surface_capabilities);
|
||||
expect_not_nullptr(vk_get_physical_device_surface_formats);
|
||||
|
||||
// TODO(Light): add test for platform-dependant functions
|
||||
// expect_not_nullptr(vk_create_xlib_surface_khr);
|
||||
// expect_not_nullptr(vk_destroy_surface_khr);
|
||||
};
|
||||
|
||||
Case { "post load device functions state is correct" } = [] {
|
||||
using namespace renderer::vk;
|
||||
expect_not_nullptr(Instance::get());
|
||||
|
||||
expect_not_nullptr(vk_get_device_queue);
|
||||
expect_not_nullptr(vk_create_command_pool);
|
||||
expect_not_nullptr(vk_destroy_command_pool);
|
||||
expect_not_nullptr(vk_allocate_command_buffers);
|
||||
expect_not_nullptr(vk_free_command_buffers);
|
||||
expect_not_nullptr(vk_begin_command_buffer);
|
||||
expect_not_nullptr(vk_end_command_buffer);
|
||||
expect_not_nullptr(vk_cmd_pipeline_barrier);
|
||||
expect_not_nullptr(vk_queue_submit);
|
||||
expect_not_nullptr(vk_queue_wait_idle);
|
||||
expect_not_nullptr(vk_device_wait_idle);
|
||||
expect_not_nullptr(vk_create_fence);
|
||||
expect_not_nullptr(vk_destroy_fence);
|
||||
expect_not_nullptr(vk_wait_for_fences);
|
||||
expect_not_nullptr(vk_reset_fences);
|
||||
expect_not_nullptr(vk_create_semaphore);
|
||||
expect_not_nullptr(vk_destroy_semaphore);
|
||||
expect_not_nullptr(vk_create_swapchain_khr);
|
||||
expect_not_nullptr(vk_destroy_swapchain_khr);
|
||||
expect_not_nullptr(vk_get_swapchain_images_khr);
|
||||
expect_not_nullptr(vk_acquire_next_image_khr);
|
||||
expect_not_nullptr(vk_queue_present_khr);
|
||||
expect_not_nullptr(vk_create_image_view);
|
||||
expect_not_nullptr(vk_destroy_image_view);
|
||||
expect_not_nullptr(vk_create_render_pass);
|
||||
expect_not_nullptr(vk_destroy_render_pass);
|
||||
expect_not_nullptr(vk_create_frame_buffer);
|
||||
expect_not_nullptr(vk_destroy_frame_buffer);
|
||||
expect_not_nullptr(vk_create_shader_module);
|
||||
expect_not_nullptr(vk_destroy_shader_module);
|
||||
expect_not_nullptr(vk_create_pipeline_layout);
|
||||
expect_not_nullptr(vk_destroy_pipeline_layout);
|
||||
expect_not_nullptr(vk_create_graphics_pipelines);
|
||||
expect_not_nullptr(vk_destroy_pipeline);
|
||||
expect_not_nullptr(vk_cmd_begin_render_pass);
|
||||
expect_not_nullptr(vk_cmd_end_render_pass);
|
||||
expect_not_nullptr(vk_cmd_bind_pipeline);
|
||||
expect_not_nullptr(vk_cmd_draw);
|
||||
expect_not_nullptr(vk_cmd_set_viewport);
|
||||
expect_not_nullptr(vk_cmd_set_scissors);
|
||||
};
|
||||
};
|
|
@ -32,15 +32,9 @@ Surface::~Surface()
|
|||
m_instance->destroy_surface(m_surface);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Surface::get_framebuffer_size() const -> VkExtent2D
|
||||
[[nodiscard]] auto Surface::get_framebuffer_size() const -> math::uvec2
|
||||
{
|
||||
const auto &[width, height] = //
|
||||
m_surface_entity.get<surface::SurfaceComponent>().get_resolution();
|
||||
|
||||
return {
|
||||
.width = width,
|
||||
.height = height,
|
||||
};
|
||||
return m_surface_entity.get<surface::SurfaceComponent>().get_resolution();
|
||||
}
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
return m_surface;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_framebuffer_size() const -> VkExtent2D;
|
||||
[[nodiscard]] auto get_framebuffer_size() const -> math::uvec2 override;
|
||||
|
||||
private:
|
||||
class Instance *m_instance {};
|
||||
|
|
|
@ -4,10 +4,24 @@ namespace lt::renderer::vk {
|
|||
|
||||
Messenger::Messenger(IInstance *instance, ecs::Entity entity)
|
||||
: m_instance(static_cast<Instance *>(instance))
|
||||
, m_entity(std::move(entity))
|
||||
|
||||
// Move this to heap for pointer-stability of .pUserData
|
||||
, m_entity(memory::create_scope<ecs::Entity>(std::move(entity)))
|
||||
|
||||
{
|
||||
const auto &component = m_entity.get<MessengerComponent>();
|
||||
const auto &component = m_entity->get<MessengerComponent>();
|
||||
|
||||
ensure(
|
||||
component.get_severities() != MessageSeverity::none,
|
||||
"Failed to create vk::Messenger: severities == none"
|
||||
);
|
||||
|
||||
ensure(
|
||||
component.get_types() != MessageType::none,
|
||||
"Failed to create vk::Messenger: types == none"
|
||||
);
|
||||
|
||||
ensure(component.get_callback(), "Failed to create vk::Messenger: null callback");
|
||||
|
||||
m_debug_messenger = m_instance->create_messenger(
|
||||
VkDebugUtilsMessengerCreateInfoEXT {
|
||||
|
@ -15,7 +29,7 @@ Messenger::Messenger(IInstance *instance, ecs::Entity entity)
|
|||
.messageSeverity = to_native_severity(component.get_severities()),
|
||||
.messageType = to_native_type(component.get_types()),
|
||||
.pfnUserCallback = &native_callback,
|
||||
.pUserData = this,
|
||||
.pUserData = m_entity.get(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -41,8 +55,8 @@ Messenger::~Messenger()
|
|||
{
|
||||
ensure(vulkan_user_data, "Null vulkan_user_data received in messenger callback");
|
||||
|
||||
auto *messenger = (Messenger *)vulkan_user_data; // NOLINT
|
||||
auto &component = messenger->m_entity.get<MessengerComponent>();
|
||||
auto *messenger = std::bit_cast<ecs::Entity *>(vulkan_user_data);
|
||||
auto &component = messenger->get<MessengerComponent>();
|
||||
component.get_callback()(
|
||||
from_native_severity(severity),
|
||||
from_native_type(type),
|
||||
|
@ -102,22 +116,22 @@ Messenger::~Messenger()
|
|||
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
|
||||
{
|
||||
flags &= std::to_underlying(error);
|
||||
flags |= std::to_underlying(error);
|
||||
}
|
||||
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
|
||||
{
|
||||
flags &= std::to_underlying(warning);
|
||||
flags |= std::to_underlying(warning);
|
||||
}
|
||||
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
|
||||
{
|
||||
flags &= std::to_underlying(info);
|
||||
flags |= std::to_underlying(info);
|
||||
}
|
||||
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
|
||||
{
|
||||
flags &= std::to_underlying(verbose);
|
||||
flags |= std::to_underlying(verbose);
|
||||
}
|
||||
|
||||
return static_cast<MessageSeverity>(flags);
|
||||
|
|
|
@ -24,7 +24,6 @@ public:
|
|||
|
||||
auto operator=(const Messenger &) const -> Messenger & = delete;
|
||||
|
||||
|
||||
private:
|
||||
static auto native_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
|
@ -45,7 +44,7 @@ private:
|
|||
|
||||
memory::NullOnMove<class Instance *> m_instance {};
|
||||
|
||||
ecs::Entity m_entity;
|
||||
memory::Scope<ecs::Entity> m_entity;
|
||||
|
||||
VkDebugUtilsMessengerEXT m_debug_messenger = VK_NULL_HANDLE;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include <renderer/backend/vk/context/surface.hpp>
|
||||
#include <renderer/backend/vk/debug/messenger.hpp>
|
||||
#include <renderer/backend/vk/test_utils.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/expects.hpp>
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
namespace lt::renderer::vk {
|
||||
|
||||
Pass::Pass(
|
||||
IContext &context,
|
||||
lt::assets::ShaderAsset vertex_shader,
|
||||
lt::assets::ShaderAsset fragment_shader
|
||||
IDevice *device,
|
||||
ISwapchain *swapchain,
|
||||
const lt::assets::ShaderAsset &vertex_shader,
|
||||
const lt::assets::ShaderAsset &fragment_shader
|
||||
)
|
||||
: m_device(static_cast<Device *>(context.device()))
|
||||
: m_device(static_cast<Device *>(device))
|
||||
{
|
||||
auto *vertex_module = create_module(
|
||||
vertex_shader.unpack(lt::assets::ShaderAsset::BlobTag::code)
|
||||
|
@ -113,7 +114,7 @@ Pass::Pass(
|
|||
);
|
||||
|
||||
auto attachment_description = VkAttachmentDescription {
|
||||
.format = static_cast<Swapchain *>(context.swapchain())->get_format(),
|
||||
.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,
|
||||
|
@ -176,8 +177,7 @@ Pass::Pass(
|
|||
}
|
||||
);
|
||||
|
||||
m_framebuffers = static_cast<Swapchain *>(context.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);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <assets/shader.hpp>
|
||||
#include <memory/pointer_types/null_on_move.hpp>
|
||||
#include <renderer/backend/vk/utils.hpp>
|
||||
#include <renderer/frontend/context/context.hpp>
|
||||
#include <renderer/frontend/renderer/pass.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
@ -12,9 +11,10 @@ class Pass: public IPass
|
|||
{
|
||||
public:
|
||||
Pass(
|
||||
IContext &context,
|
||||
lt::assets::ShaderAsset vertex_shader,
|
||||
lt::assets::ShaderAsset fragment_shader
|
||||
class IDevice *device,
|
||||
class ISwapchain *swapchain,
|
||||
const lt::assets::ShaderAsset &vertex_shader,
|
||||
const lt::assets::ShaderAsset &fragment_shader
|
||||
);
|
||||
|
||||
~Pass() override;
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
Renderer::Renderer(IContext &context, uint32_t max_frames_in_flight)
|
||||
: m_device(static_cast<Device *>(context.device()))
|
||||
, m_swapchain(static_cast<Swapchain *>(context.swapchain()))
|
||||
Renderer::Renderer(IDevice *device, ISwapchain *swapchain, uint32_t max_frames_in_flight)
|
||||
: m_device(static_cast<Device *>(device))
|
||||
, m_swapchain(static_cast<Swapchain *>(swapchain))
|
||||
, m_resolution(m_swapchain->get_resolution())
|
||||
, m_max_frames_in_flight(max_frames_in_flight)
|
||||
{
|
||||
|
@ -15,7 +15,8 @@ Renderer::Renderer(IContext &context, uint32_t max_frames_in_flight)
|
|||
|
||||
// TODO(Light): HARDCODED PASS!!!
|
||||
m_pass = memory::create_ref<vk::Pass>(
|
||||
context,
|
||||
m_device,
|
||||
m_swapchain,
|
||||
assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
);
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
#include <memory/reference.hpp>
|
||||
#include <ranges>
|
||||
#include <renderer/backend/vk/context/context.hpp>
|
||||
#include <renderer/backend/vk/context/device.hpp>
|
||||
#include <renderer/backend/vk/renderer/pass.hpp>
|
||||
#include <renderer/backend/vk/utils.hpp>
|
||||
#include <renderer/frontend/context/context.hpp>
|
||||
#include <renderer/frontend/renderer/pass.hpp>
|
||||
#include <renderer/frontend/renderer/renderer.hpp>
|
||||
|
||||
|
@ -15,7 +13,7 @@ namespace lt::renderer::vk {
|
|||
class Renderer: public IRenderer
|
||||
{
|
||||
public:
|
||||
Renderer(IContext &context, uint32_t max_frames_in_flight);
|
||||
Renderer(class IDevice *device, class ISwapchain *swapchain, uint32_t max_frames_in_flight);
|
||||
|
||||
~Renderer() override;
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#include <renderer/vk/test_utils.hpp>
|
|
@ -1,115 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory/reference.hpp>
|
||||
#include <ranges>
|
||||
#include <renderer/backend/vk/context/context.hpp>
|
||||
#include <renderer/backend/vk/context/surface.hpp>
|
||||
#include <renderer/backend/vk/debug/messenger.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using ::lt::test::Case;
|
||||
using ::lt::test::expect_eq;
|
||||
using ::lt::test::expect_false;
|
||||
using ::lt::test::expect_not_nullptr;
|
||||
using ::lt::test::expect_throw;
|
||||
using ::lt::test::expect_true;
|
||||
using ::lt::test::Suite;
|
||||
using ::std::ignore;
|
||||
|
||||
namespace constants {
|
||||
|
||||
constexpr auto resolution = lt::math::uvec2 { 800u, 600u };
|
||||
|
||||
}
|
||||
|
||||
class ValidationObserver
|
||||
{
|
||||
using Messenger = lt::renderer::vk::Messenger;
|
||||
using enum Messenger::Type;
|
||||
using enum Messenger::Severity;
|
||||
|
||||
public:
|
||||
ValidationObserver()
|
||||
: m_messenger(
|
||||
Messenger::CreateInfo {
|
||||
.severity = static_cast<Messenger::Severity>(warning | error),
|
||||
.type = lt::renderer::vk::Messenger::all_type,
|
||||
.callback = &callback,
|
||||
.user_data = &m_had_any_messages,
|
||||
}
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] auto had_any_messages() const -> bool
|
||||
{
|
||||
return m_had_any_messages;
|
||||
}
|
||||
|
||||
private:
|
||||
static void callback(
|
||||
Messenger::Severity message_severity,
|
||||
Messenger::Type message_type,
|
||||
Messenger::CallbackData_T vulkan_data,
|
||||
void *user_data
|
||||
)
|
||||
{
|
||||
std::ignore = message_severity;
|
||||
std::ignore = message_type;
|
||||
for (auto idx = 0; idx < vulkan_data->objectCount; ++idx)
|
||||
{
|
||||
auto object = vulkan_data->pObjects[idx];
|
||||
std::println(
|
||||
"0x{:x}({}) = {}",
|
||||
object.objectHandle,
|
||||
string_VkObjectType(object.objectType),
|
||||
object.pObjectName ? object.pObjectName : "unnamed"
|
||||
);
|
||||
}
|
||||
|
||||
std::println("Validation message: {}", vulkan_data->pMessage);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
*(bool *)user_data = true;
|
||||
}
|
||||
|
||||
Messenger m_messenger;
|
||||
bool m_had_any_messages = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline auto create_context()
|
||||
-> std::pair<lt::renderer::vk::Context, lt::surface::System>
|
||||
{
|
||||
using lt::surface::SurfaceComponent;
|
||||
|
||||
auto registry = lt::memory::create_ref<lt::ecs::Registry>();
|
||||
auto entity = lt::ecs::Entity { registry, registry->create_entity() };
|
||||
auto surface_system = lt::surface::System(registry);
|
||||
entity.add<SurfaceComponent>(SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = constants::resolution,
|
||||
});
|
||||
|
||||
return { lt::renderer::vk::Context { entity }, std::move(surface_system) };
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
// }
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
#include <memory/scope.hpp>
|
||||
#include <renderer/api.hpp>
|
||||
#include <renderer/backend/vk/context/context.hpp>
|
||||
#include <renderer/frontend/context/context.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
auto IContext::create(API target_api, const ecs::Entity &surface_entity)
|
||||
-> memory::Scope<IContext>
|
||||
{
|
||||
switch (target_api)
|
||||
{
|
||||
case API::Vulkan: return memory::create_scope<vk::Context>(surface_entity);
|
||||
default: throw std::runtime_error { "Invalid API" };
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt::renderer
|
|
@ -1,46 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <ecs/entity.hpp>
|
||||
#include <memory/scope.hpp>
|
||||
#include <renderer/api.hpp>
|
||||
#include <renderer/frontend/context/device.hpp>
|
||||
#include <renderer/frontend/context/gpu.hpp>
|
||||
#include <renderer/frontend/context/instance.hpp>
|
||||
#include <renderer/frontend/context/surface.hpp>
|
||||
#include <renderer/frontend/context/swapchain.hpp>
|
||||
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class IContext
|
||||
{
|
||||
public:
|
||||
static auto create(API target_api, const ecs::Entity &surface_entity)
|
||||
-> memory::Scope<IContext>;
|
||||
IContext() = default;
|
||||
|
||||
virtual ~IContext() = default;
|
||||
|
||||
IContext(IContext &&) = default;
|
||||
|
||||
IContext(const IContext &) = delete;
|
||||
|
||||
auto operator=(IContext &&) -> IContext & = default;
|
||||
|
||||
auto operator=(const IContext &) -> IContext & = delete;
|
||||
|
||||
virtual void recreate_swapchain() = 0;
|
||||
|
||||
[[nodiscard]] virtual auto instance() const -> IInstance * = 0;
|
||||
|
||||
[[nodiscard]] virtual auto surface() const -> ISurface * = 0;
|
||||
|
||||
[[nodiscard]] virtual auto gpu() const -> IGpu * = 0;
|
||||
|
||||
[[nodiscard]] virtual auto device() const -> IDevice * = 0;
|
||||
|
||||
[[nodiscard]] virtual auto swapchain() const -> ISwapchain * = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace lt::renderer
|
|
@ -1,56 +0,0 @@
|
|||
#include <memory/reference.hpp>
|
||||
#include <memory/scope.hpp>
|
||||
#include <ranges>
|
||||
#include <renderer/interface/context.hpp>
|
||||
#include <renderer/vk/context/context.hpp>
|
||||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <renderer/vk/debug/messenger.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using lt::renderer::API;
|
||||
using lt::renderer::IContext;
|
||||
using ::lt::test::Case;
|
||||
using ::lt::test::expect_eq;
|
||||
using ::lt::test::expect_false;
|
||||
using ::lt::test::expect_not_nullptr;
|
||||
using ::lt::test::expect_throw;
|
||||
using ::lt::test::expect_true;
|
||||
using ::lt::test::Suite;
|
||||
using ::std::ignore;
|
||||
|
||||
namespace constants {
|
||||
|
||||
constexpr auto resolution = lt::math::uvec2 { 800u, 600u };
|
||||
|
||||
}
|
||||
|
||||
|
||||
class Fixture
|
||||
{
|
||||
public:
|
||||
Fixture()
|
||||
: m_registry(lt::memory::create_ref<lt::ecs::Registry>())
|
||||
, m_surface_system(lt::memory::create_scope<lt::surface::System>(m_registry))
|
||||
, m_surface_entity(m_registry, m_registry->create_entity())
|
||||
{
|
||||
}
|
||||
|
||||
auto get_surface_entity() -> lt::ecs::Entity
|
||||
{
|
||||
return m_surface_entity;
|
||||
}
|
||||
|
||||
private:
|
||||
lt::memory::Ref<lt::ecs::Registry> m_registry;
|
||||
lt::memory::Scope<lt::surface::System> m_surface_system;
|
||||
lt::ecs::Entity m_surface_entity;
|
||||
};
|
||||
|
||||
Suite raii = "context_raii"_suite = [] {
|
||||
Case { "Happy path won't throw" } = [] {
|
||||
auto fixture = Fixture {};
|
||||
IContext::create(API::Vulkan, fixture.get_surface_entity());
|
||||
};
|
||||
};
|
21
modules/renderer/private/frontend/context/device.cpp
Normal file
21
modules/renderer/private/frontend/context/device.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <renderer/backend/vk/context/device.hpp>
|
||||
#include <renderer/frontend/context/device.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
[[nodiscard]] /* static */ auto IDevice::create(Api target_api, IGpu *gpu, ISurface *surface)
|
||||
-> memory::Scope<IDevice>
|
||||
{
|
||||
ensure(gpu, "Failed to create renderer::IDevice: null gpu");
|
||||
ensure(surface, "Failed to create renderer::IDevice: null surface");
|
||||
|
||||
switch (target_api)
|
||||
{
|
||||
case Api::vulkan: return memory::create_scope<vk::Device>(gpu, surface);
|
||||
case Api::none:
|
||||
case Api::metal:
|
||||
case Api::direct_x: throw std::runtime_error { "Invalid API" };
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt::renderer
|
|
@ -1,10 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory/scope.hpp>
|
||||
#include <renderer/api.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class IDevice
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static auto create(Api target_api, class IGpu *gpu, class ISurface *surface)
|
||||
-> memory::Scope<IDevice>;
|
||||
|
||||
IDevice() = default;
|
||||
|
||||
virtual ~IDevice() = default;
|
||||
|
|
|
@ -1,100 +1,55 @@
|
|||
#include <memory/reference.hpp>
|
||||
#include <ranges>
|
||||
#include <renderer/vk/context/device.hpp>
|
||||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <renderer/frontend/context/device.hpp>
|
||||
#include <renderer/frontend/context/surface.hpp>
|
||||
#include <renderer/test/utils.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using namespace lt;
|
||||
using renderer::vk::Device;
|
||||
using renderer::vk::Surface;
|
||||
using test::Case;
|
||||
using test::expect_ne;
|
||||
using test::expect_throw;
|
||||
using test::Suite;
|
||||
|
||||
constexpr auto resolution = math::uvec2 { 800u, 600u };
|
||||
|
||||
Suite raii = "device_raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
auto registry = memory::create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
});
|
||||
|
||||
auto surface = Surface { entity };
|
||||
auto device = Device { surface };
|
||||
};
|
||||
|
||||
Case { "many won't freeze/throw" } = [] {
|
||||
auto registry = memory::create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
});
|
||||
auto surface = Surface { entity };
|
||||
|
||||
// it takes a loong time to initialize vulkan + setup device
|
||||
for (auto idx : std::views::iota(0, 10))
|
||||
{
|
||||
Device { surface };
|
||||
}
|
||||
auto fixture = Fixture_SurfaceGpu {};
|
||||
std::ignore = lt::renderer::IDevice::create(
|
||||
constants::api,
|
||||
fixture.gpu(),
|
||||
fixture.surface()
|
||||
);
|
||||
};
|
||||
|
||||
Case { "unhappy path throws" } = [] {
|
||||
auto registry = memory::create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
auto fixture = Fixture_SurfaceGpu {};
|
||||
|
||||
expect_throw([&] {
|
||||
ignore = lt::renderer::IDevice::create(constants::api, nullptr, fixture.surface());
|
||||
});
|
||||
|
||||
auto moved_out_surface = Surface { entity };
|
||||
auto surface = std::move(moved_out_surface);
|
||||
|
||||
expect_throw([&] { Device { moved_out_surface }; });
|
||||
};
|
||||
|
||||
Case { "post construct has correct state" } = [] {
|
||||
auto registry = memory::create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
expect_throw([&] {
|
||||
ignore = lt::renderer::IDevice::create(constants::api, fixture.gpu(), nullptr);
|
||||
});
|
||||
|
||||
auto surface = Surface { entity };
|
||||
auto device = Device { surface };
|
||||
|
||||
for (auto &index : device.get_family_indices())
|
||||
{
|
||||
expect_ne(index, VK_QUEUE_FAMILY_IGNORED);
|
||||
}
|
||||
test::expect_true(device.physical());
|
||||
test::expect_true(device.vk());
|
||||
};
|
||||
|
||||
Case { "post destruct has correct state" } = [] {
|
||||
auto registry = memory::create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
expect_throw([&] {
|
||||
ignore = lt::renderer::IDevice::create(
|
||||
lt::renderer::Api::none,
|
||||
fixture.gpu(),
|
||||
fixture.surface()
|
||||
);
|
||||
});
|
||||
|
||||
auto surface = Surface { entity };
|
||||
expect_throw([&] {
|
||||
ignore = lt::renderer::IDevice::create(
|
||||
lt::renderer::Api::direct_x,
|
||||
fixture.gpu(),
|
||||
fixture.surface()
|
||||
);
|
||||
});
|
||||
|
||||
{
|
||||
auto device = Device { surface };
|
||||
}
|
||||
expect_throw([&] {
|
||||
ignore = lt::renderer::IDevice::create(
|
||||
lt::renderer::Api::metal,
|
||||
fixture.gpu(),
|
||||
fixture.surface()
|
||||
);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
18
modules/renderer/private/frontend/context/gpu.cpp
Normal file
18
modules/renderer/private/frontend/context/gpu.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include <renderer/backend/vk/context/gpu.hpp>
|
||||
#include <renderer/frontend/context/gpu.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
[[nodiscard]] /* static */ auto IGpu::create(Api target_api, IInstance *instance)
|
||||
-> memory::Scope<IGpu>
|
||||
{
|
||||
switch (target_api)
|
||||
{
|
||||
case Api::vulkan: return memory::create_scope<vk::Gpu>(instance);
|
||||
case Api::none:
|
||||
case Api::metal:
|
||||
case Api::direct_x: throw std::runtime_error { "Invalid API" };
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt::renderer
|
|
@ -1,10 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory/scope.hpp>
|
||||
#include <renderer/api.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class IGpu
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static auto create(Api target_api, class IInstance *instance)
|
||||
-> memory::Scope<IGpu>;
|
||||
|
||||
IGpu() = default;
|
||||
|
||||
virtual ~IGpu() = default;
|
||||
|
|
17
modules/renderer/private/frontend/context/instance.cpp
Normal file
17
modules/renderer/private/frontend/context/instance.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <renderer/backend/vk/context/instance.hpp>
|
||||
#include <renderer/frontend/context/instance.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
[[nodiscard]] /* static */ auto IInstance::get(Api target_api) -> IInstance *
|
||||
{
|
||||
switch (target_api)
|
||||
{
|
||||
case Api::vulkan: return vk::Instance::get();
|
||||
case Api::none:
|
||||
case Api::metal:
|
||||
case Api::direct_x: throw std::runtime_error { "Invalid API" };
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt::renderer
|
|
@ -1,10 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <renderer/api.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class IInstance
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static auto get(Api target_api) -> IInstance *;
|
||||
|
||||
IInstance() = default;
|
||||
|
||||
virtual ~IInstance() = default;
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
#include <renderer/vk/context/instance.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using namespace lt;
|
||||
using renderer::vk::Instance;
|
||||
using test::Case;
|
||||
using test::expect_not_nullptr;
|
||||
using test::Suite;
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
Suite raii = "raii"_suite = [] {
|
||||
Case { "post singleton insantiation state is correct" } = [] {
|
||||
expect_not_nullptr(Instance::get());
|
||||
|
||||
using namespace renderer::vk;
|
||||
expect_not_nullptr(vk_get_instance_proc_address);
|
||||
expect_not_nullptr(vk_create_instance);
|
||||
expect_not_nullptr(vk_enumerate_instance_extension_properties);
|
||||
expect_not_nullptr(vk_enumerate_instance_layer_properties);
|
||||
|
||||
expect_not_nullptr(vk_destroy_instance);
|
||||
expect_not_nullptr(vk_enumerate_physical_devices);
|
||||
expect_not_nullptr(vk_get_physical_device_properties);
|
||||
expect_not_nullptr(vk_get_physical_device_queue_family_properties);
|
||||
expect_not_nullptr(vk_create_device);
|
||||
expect_not_nullptr(vk_get_device_proc_address);
|
||||
expect_not_nullptr(vk_destroy_device);
|
||||
expect_not_nullptr(vk_get_physical_device_features);
|
||||
expect_not_nullptr(vk_enumerate_device_extension_properties);
|
||||
|
||||
expect_not_nullptr(vk_cmd_begin_debug_label);
|
||||
expect_not_nullptr(vk_cmd_end_debug_label);
|
||||
expect_not_nullptr(vk_cmd_insert_debug_label);
|
||||
expect_not_nullptr(vk_create_debug_messenger);
|
||||
expect_not_nullptr(vk_destroy_debug_messenger);
|
||||
expect_not_nullptr(vk_queue_begin_debug_label);
|
||||
expect_not_nullptr(vk_queue_end_debug_label);
|
||||
expect_not_nullptr(vk_queue_insert_debug_label);
|
||||
expect_not_nullptr(vk_set_debug_object_name);
|
||||
expect_not_nullptr(vk_set_debug_object_tag);
|
||||
expect_not_nullptr(vk_submit_debug_message);
|
||||
|
||||
expect_not_nullptr(vk_get_physical_device_surface_support);
|
||||
expect_not_nullptr(vk_get_physical_device_surface_capabilities);
|
||||
expect_not_nullptr(vk_get_physical_device_surface_formats);
|
||||
expect_not_nullptr(vk_create_xlib_surface_khr);
|
||||
expect_not_nullptr(vk_destroy_surface_khr);
|
||||
};
|
||||
|
||||
Case { "post load device functions state is correct" } = [] {
|
||||
using namespace renderer::vk;
|
||||
expect_not_nullptr(Instance::get());
|
||||
|
||||
expect_not_nullptr(vk_get_device_queue);
|
||||
expect_not_nullptr(vk_create_command_pool);
|
||||
expect_not_nullptr(vk_destroy_command_pool);
|
||||
expect_not_nullptr(vk_allocate_command_buffers);
|
||||
expect_not_nullptr(vk_free_command_buffers);
|
||||
expect_not_nullptr(vk_begin_command_buffer);
|
||||
expect_not_nullptr(vk_end_command_buffer);
|
||||
expect_not_nullptr(vk_cmd_pipeline_barrier);
|
||||
expect_not_nullptr(vk_queue_submit);
|
||||
expect_not_nullptr(vk_queue_wait_idle);
|
||||
expect_not_nullptr(vk_device_wait_idle);
|
||||
expect_not_nullptr(vk_create_fence);
|
||||
expect_not_nullptr(vk_destroy_fence);
|
||||
expect_not_nullptr(vk_wait_for_fences);
|
||||
expect_not_nullptr(vk_reset_fences);
|
||||
expect_not_nullptr(vk_create_semaphore);
|
||||
expect_not_nullptr(vk_destroy_semaphore);
|
||||
expect_not_nullptr(vk_create_swapchain_khr);
|
||||
expect_not_nullptr(vk_destroy_swapchain_khr);
|
||||
expect_not_nullptr(vk_get_swapchain_images_khr);
|
||||
expect_not_nullptr(vk_acquire_next_image_khr);
|
||||
expect_not_nullptr(vk_queue_present_khr);
|
||||
expect_not_nullptr(vk_create_image_view);
|
||||
expect_not_nullptr(vk_destroy_image_view);
|
||||
expect_not_nullptr(vk_create_render_pass);
|
||||
expect_not_nullptr(vk_destroy_render_pass);
|
||||
expect_not_nullptr(vk_create_frame_buffer);
|
||||
expect_not_nullptr(vk_destroy_frame_buffer);
|
||||
expect_not_nullptr(vk_create_shader_module);
|
||||
expect_not_nullptr(vk_destroy_shader_module);
|
||||
expect_not_nullptr(vk_create_pipeline_layout);
|
||||
expect_not_nullptr(vk_destroy_pipeline_layout);
|
||||
expect_not_nullptr(vk_create_graphics_pipelines);
|
||||
expect_not_nullptr(vk_destroy_pipeline);
|
||||
expect_not_nullptr(vk_cmd_begin_render_pass);
|
||||
expect_not_nullptr(vk_cmd_end_render_pass);
|
||||
expect_not_nullptr(vk_cmd_bind_pipeline);
|
||||
expect_not_nullptr(vk_cmd_draw);
|
||||
expect_not_nullptr(vk_cmd_set_viewport);
|
||||
expect_not_nullptr(vk_cmd_set_scissors);
|
||||
};
|
||||
};
|
24
modules/renderer/private/frontend/context/surface.cpp
Normal file
24
modules/renderer/private/frontend/context/surface.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
|
||||
#include <renderer/backend/vk/context/surface.hpp>
|
||||
#include <renderer/frontend/context/surface.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
[[nodiscard]] /* static */ auto ISurface::create(
|
||||
Api target_api,
|
||||
IInstance *instance,
|
||||
const ecs::Entity &surface_entity
|
||||
) -> memory::Scope<ISurface>
|
||||
{
|
||||
ensure(instance, "Failed to create renderer::ISurface: null instance");
|
||||
|
||||
switch (target_api)
|
||||
{
|
||||
case Api::vulkan: return memory::create_scope<vk::Surface>(instance, surface_entity);
|
||||
case Api::none:
|
||||
case Api::metal:
|
||||
case Api::direct_x: throw std::runtime_error { "Invalid API" };
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt::renderer
|
|
@ -1,10 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <ecs/entity.hpp>
|
||||
#include <math/vec2.hpp>
|
||||
#include <memory/scope.hpp>
|
||||
#include <renderer/api.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class ISurface
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static auto create(
|
||||
Api target_api,
|
||||
class IInstance *instance,
|
||||
const ecs::Entity &surface_entity
|
||||
) -> memory::Scope<ISurface>;
|
||||
|
||||
ISurface() = default;
|
||||
|
||||
virtual ~ISurface() = default;
|
||||
|
@ -16,6 +27,8 @@ public:
|
|||
auto operator=(ISurface &&) -> ISurface & = default;
|
||||
|
||||
auto operator=(const ISurface &) -> ISurface & = delete;
|
||||
|
||||
[[nodiscard]] virtual auto get_framebuffer_size() const -> math::uvec2 = 0;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer
|
||||
|
|
|
@ -1,52 +1,88 @@
|
|||
#include <memory/reference.hpp>
|
||||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <renderer/vk/debug/messenger.hpp>
|
||||
#include <renderer/vk/test_utils.hpp>
|
||||
#include <renderer/frontend/context/instance.hpp>
|
||||
#include <renderer/frontend/context/surface.hpp>
|
||||
#include <renderer/test/utils.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using ::lt::ecs::Entity;
|
||||
using ::lt::ecs::EntityId;
|
||||
using ::lt::ecs::Registry;
|
||||
using ::lt::renderer::vk::Surface;
|
||||
using ::lt::surface::SurfaceComponent;
|
||||
using ::lt::surface::System;
|
||||
|
||||
Suite raii = "surface"_suite = [] {
|
||||
Case { "happy path won't throw" } = [&] {
|
||||
auto observer = ValidationObserver {};
|
||||
auto fixture = Fixture_SurfaceSystem {};
|
||||
|
||||
auto registry = lt::memory::create_ref<Registry>();
|
||||
auto entity = Entity { registry, registry->create_entity() };
|
||||
auto surface_system = System(registry);
|
||||
|
||||
entity.add<SurfaceComponent>(SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = constants::resolution,
|
||||
.visible = true,
|
||||
});
|
||||
|
||||
const auto surface = Surface { entity };
|
||||
const auto &[x, y] = surface.get_framebuffer_size();
|
||||
const auto surface = lt::renderer::ISurface::create(
|
||||
constants::api,
|
||||
lt::renderer::IInstance::get(constants::api),
|
||||
fixture.surface_entity()
|
||||
);
|
||||
|
||||
const auto &[x, y] = surface->get_framebuffer_size();
|
||||
expect_eq(x, constants::resolution.x);
|
||||
expect_eq(y, constants::resolution.y);
|
||||
expect_not_nullptr(surface.vk());
|
||||
expect_false(observer.had_any_messages());
|
||||
};
|
||||
|
||||
Case { "unhappy path throws" } = [&] {
|
||||
auto observer = ValidationObserver {};
|
||||
auto registry = lt::memory::create_ref<Registry>();
|
||||
auto entity = Entity { registry, registry->create_entity() };
|
||||
auto registry = lt::memory::create_ref<lt::ecs::Registry>();
|
||||
auto entity = lt::ecs::Entity { registry, registry->create_entity() };
|
||||
auto system = lt::surface::System(registry);
|
||||
|
||||
entity.add<SurfaceComponent>(SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = constants::resolution,
|
||||
.visible = true,
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::ISurface::create(
|
||||
constants::api,
|
||||
lt::renderer::IInstance::get(constants::api),
|
||||
entity
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([&] { Surface { entity }; });
|
||||
expect_false(observer.had_any_messages());
|
||||
system.create_surface_component(
|
||||
entity.id(),
|
||||
lt::surface::SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = constants::resolution,
|
||||
}
|
||||
);
|
||||
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::ISurface::create(constants::api, nullptr, entity);
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::ISurface::create(
|
||||
lt::renderer::Api::none,
|
||||
lt::renderer::IInstance::get(constants::api),
|
||||
entity
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::ISurface::create(
|
||||
lt::renderer::Api::direct_x,
|
||||
lt::renderer::IInstance::get(constants::api),
|
||||
entity
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::ISurface::create(
|
||||
lt::renderer::Api::metal,
|
||||
lt::renderer::IInstance::get(constants::api),
|
||||
entity
|
||||
);
|
||||
});
|
||||
|
||||
// Ensure base creation info is non-throwing
|
||||
std::ignore = lt::renderer::ISurface::create(
|
||||
constants::api,
|
||||
lt::renderer::IInstance::get(constants::api),
|
||||
entity
|
||||
);
|
||||
};
|
||||
|
||||
// TODO(Light): add torture tests
|
||||
Case { "torture tests" } = [] {
|
||||
};
|
||||
};
|
||||
|
|
23
modules/renderer/private/frontend/context/swapchain.cpp
Normal file
23
modules/renderer/private/frontend/context/swapchain.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include <renderer/backend/vk/context/swapchain.hpp>
|
||||
#include <renderer/frontend/context/swapchain.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
[[nodiscard]] /* static */ auto ISwapchain::create(
|
||||
Api target_api,
|
||||
ISurface *surface,
|
||||
IGpu *gpu,
|
||||
IDevice *device
|
||||
) -> memory::Scope<ISwapchain>
|
||||
{
|
||||
switch (target_api)
|
||||
{
|
||||
case Api::vulkan: return memory::create_scope<vk::Swapchain>(surface, gpu, device);
|
||||
case Api::none:
|
||||
case Api::metal:
|
||||
case Api::direct_x: throw std::runtime_error { "Invalid API" };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace lt::renderer
|
|
@ -1,10 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory/scope.hpp>
|
||||
#include <renderer/api.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class ISwapchain
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static auto create(
|
||||
Api target_api,
|
||||
class ISurface *surface,
|
||||
class IGpu *gpu,
|
||||
class IDevice *device
|
||||
) -> memory::Scope<ISwapchain>;
|
||||
|
||||
ISwapchain() = default;
|
||||
|
||||
virtual ~ISwapchain() = default;
|
||||
|
|
|
@ -5,16 +5,17 @@
|
|||
namespace lt::renderer {
|
||||
|
||||
[[nodiscard]] /* static */ auto IMessenger::create(
|
||||
API target_api,
|
||||
Api target_api,
|
||||
IInstance *instance,
|
||||
ecs::Entity entity
|
||||
) -> memory::Scope<IMessenger>
|
||||
{
|
||||
switch (target_api)
|
||||
{
|
||||
case API::Vulkan: return memory::create_scope<vk::Messenger>(instance, std::move(entity));
|
||||
case API::Metal:
|
||||
case API::DirectX: throw std::runtime_error { "Invalid API" };
|
||||
case Api::vulkan: return memory::create_scope<vk::Messenger>(instance, std::move(entity));
|
||||
case Api::none:
|
||||
case Api::metal:
|
||||
case Api::direct_x: throw std::runtime_error { "Invalid API" };
|
||||
}
|
||||
}
|
||||
} // namespace lt::renderer
|
||||
|
|
0
modules/renderer/private/frontend/messenger.test.cpp
Normal file
0
modules/renderer/private/frontend/messenger.test.cpp
Normal file
29
modules/renderer/private/frontend/renderer/pass.cpp
Normal file
29
modules/renderer/private/frontend/renderer/pass.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include <assets/shader.hpp>
|
||||
#include <renderer/backend/vk/renderer/pass.hpp>
|
||||
#include <renderer/frontend/context/gpu.hpp>
|
||||
#include <renderer/frontend/renderer/pass.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
[[nodiscard]] /* static */ auto IPass::create(
|
||||
lt::renderer::Api target_api,
|
||||
IDevice *device,
|
||||
ISwapchain *swapchain,
|
||||
const lt::assets::ShaderAsset &vertex_shader,
|
||||
const lt::assets::ShaderAsset &fragment_shader
|
||||
) -> memory::Scope<IPass>
|
||||
{
|
||||
ensure(device, "Failed to create renderer::IPass: null device");
|
||||
ensure(swapchain, "Failed to create renderer::IPass: null swapchain");
|
||||
|
||||
switch (target_api)
|
||||
{
|
||||
case Api::vulkan:
|
||||
return memory::create_scope<vk::Pass>(device, swapchain, vertex_shader, fragment_shader);
|
||||
case Api::none:
|
||||
case Api::metal:
|
||||
case Api::direct_x: throw std::runtime_error { "Invalid API" };
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt::renderer
|
|
@ -1,12 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <renderer/api.hpp>
|
||||
#include <renderer/frontend/context/swapchain.hpp>
|
||||
|
||||
namespace lt::assets {
|
||||
class ShaderAsset;
|
||||
}
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class IPass
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static auto create(
|
||||
lt::renderer::Api target_api,
|
||||
class IDevice *device,
|
||||
class ISwapchain *swapchain,
|
||||
const class lt::assets::ShaderAsset &vertex_shader,
|
||||
const class lt::assets::ShaderAsset &fragment_shader
|
||||
) -> memory::Scope<IPass>;
|
||||
|
||||
IPass() = default;
|
||||
|
||||
virtual ~IPass() = default;
|
||||
|
|
|
@ -1,20 +1,74 @@
|
|||
#include <renderer/vk/renderer/pass.hpp>
|
||||
#include <renderer/vk/test_utils.hpp>
|
||||
|
||||
using ::lt::assets::ShaderAsset;
|
||||
using ::lt::renderer::vk::Pass;
|
||||
#include <assets/shader.hpp>
|
||||
#include <renderer/frontend/renderer/pass.hpp>
|
||||
#include <renderer/test/utils.hpp>
|
||||
|
||||
Suite raii = "pass_raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
auto observer = ValidationObserver {};
|
||||
auto [context, _] = create_context();
|
||||
auto fixture = Fixture_RendererSystem {};
|
||||
auto &system = fixture.renderer_system();
|
||||
|
||||
std::ignore = Pass {
|
||||
context,
|
||||
ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
ShaderAsset { "./data/test_assets/triangle.frag.asset" },
|
||||
};
|
||||
std::ignore = lt::renderer::IPass::create(
|
||||
constants::api,
|
||||
system.get_device(),
|
||||
system.get_swapchain(),
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
);
|
||||
};
|
||||
|
||||
expect_false(observer.had_any_messages());
|
||||
Case { "unhappy path throws" } = [] {
|
||||
auto fixture = Fixture_RendererSystem {};
|
||||
auto &system = fixture.renderer_system();
|
||||
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::IPass::create(
|
||||
constants::api,
|
||||
nullptr,
|
||||
system.get_swapchain(),
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::IPass::create(
|
||||
constants::api,
|
||||
system.get_device(),
|
||||
nullptr,
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::IPass::create(
|
||||
lt::renderer::Api::none,
|
||||
system.get_device(),
|
||||
system.get_swapchain(),
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::IPass::create(
|
||||
lt::renderer::Api::direct_x,
|
||||
system.get_device(),
|
||||
system.get_swapchain(),
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
std::ignore = lt::renderer::IPass::create(
|
||||
lt::renderer::Api::metal,
|
||||
system.get_device(),
|
||||
system.get_swapchain(),
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
lt::assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,13 +5,33 @@
|
|||
|
||||
namespace lt::renderer {
|
||||
|
||||
auto IRenderer::create(API target_api, IContext &context, uint32_t max_frames_in_flight)
|
||||
-> memory::Scope<IRenderer>
|
||||
[[nodiscard]] /* static */ auto IRenderer::create(
|
||||
Api target_api,
|
||||
IDevice *device,
|
||||
ISwapchain *swapchain,
|
||||
uint32_t max_frames_in_flight
|
||||
) -> memory::Scope<IRenderer>
|
||||
{
|
||||
ensure(device, "Failed to create renderer::IRenderer: null device");
|
||||
ensure(swapchain, "Failed to create renderer::IRenderer: null swapchain");
|
||||
ensure(
|
||||
std::clamp(max_frames_in_flight, frames_in_flight_lower_limit, frames_in_flight_upper_limit)
|
||||
== max_frames_in_flight,
|
||||
"Failed to initialize renderer::System: max_frames_in_flight ({}) not within bounds ({} -> "
|
||||
"{}) ",
|
||||
max_frames_in_flight,
|
||||
frames_in_flight_lower_limit,
|
||||
frames_in_flight_upper_limit
|
||||
);
|
||||
|
||||
|
||||
switch (target_api)
|
||||
{
|
||||
case API::Vulkan: return memory::create_scope<vk::Renderer>(context, max_frames_in_flight);
|
||||
default: throw std::runtime_error { "Invalid API" };
|
||||
case Api::vulkan:
|
||||
return memory::create_scope<vk::Renderer>(device, swapchain, max_frames_in_flight);
|
||||
case Api::none:
|
||||
case Api::metal:
|
||||
case Api::direct_x: throw std::runtime_error { "Invalid API" };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@ namespace lt::renderer {
|
|||
class IRenderer
|
||||
{
|
||||
public:
|
||||
static constexpr auto frames_in_flight_upper_limit = 5u;
|
||||
|
||||
static constexpr auto frames_in_flight_lower_limit = 1u;
|
||||
|
||||
enum class DrawResult : uint8_t
|
||||
{
|
||||
success = 0,
|
||||
|
@ -15,8 +19,12 @@ public:
|
|||
error,
|
||||
};
|
||||
|
||||
static auto create(API target_api, class IContext &context, uint32_t max_frames_in_flight)
|
||||
-> memory::Scope<IRenderer>;
|
||||
[[nodiscard]] static auto create(
|
||||
Api target_api,
|
||||
class IDevice *device,
|
||||
class ISwapchain *swapchain,
|
||||
uint32_t max_frames_in_flight
|
||||
) -> memory::Scope<IRenderer>;
|
||||
|
||||
IRenderer() = default;
|
||||
|
||||
|
|
|
@ -1,74 +1,96 @@
|
|||
#include <memory/reference.hpp>
|
||||
#include <renderer/vk/renderer/renderer.hpp>
|
||||
#include <renderer/vk/test_utils.hpp>
|
||||
|
||||
using ::lt::assets::ShaderAsset;
|
||||
using ::lt::renderer::vk::Pass;
|
||||
using ::lt::renderer::vk::Renderer;
|
||||
#include <renderer/frontend/renderer/renderer.hpp>
|
||||
#include <renderer/test/utils.hpp>
|
||||
|
||||
Suite raii = "renderer_raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
auto observer = ValidationObserver {};
|
||||
auto [context, _] = create_context();
|
||||
|
||||
std::ignore = Renderer(
|
||||
context,
|
||||
lt::memory::create_ref<Pass>(
|
||||
context,
|
||||
ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
)
|
||||
auto fixture = FixtureDeviceSwapchain {};
|
||||
ignore = lt::renderer::IRenderer::create(
|
||||
constants::api,
|
||||
fixture.device(),
|
||||
fixture.swapchain(),
|
||||
constants::frames_in_flight
|
||||
);
|
||||
};
|
||||
|
||||
expect_false(observer.had_any_messages());
|
||||
Case { "unhappy path throws" } = [] {
|
||||
auto fixture = FixtureDeviceSwapchain {};
|
||||
|
||||
expect_throw([&] {
|
||||
ignore = lt::renderer::IRenderer::create(
|
||||
constants::api,
|
||||
nullptr,
|
||||
fixture.swapchain(),
|
||||
constants::frames_in_flight
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
ignore = lt::renderer::IRenderer::create(
|
||||
constants::api,
|
||||
fixture.device(),
|
||||
nullptr,
|
||||
constants::frames_in_flight
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
ignore = lt::renderer::IRenderer::create(
|
||||
constants::api,
|
||||
fixture.device(),
|
||||
nullptr,
|
||||
lt::renderer::IRenderer::frames_in_flight_upper_limit + 1
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
ignore = lt::renderer::IRenderer::create(
|
||||
constants::api,
|
||||
fixture.device(),
|
||||
nullptr,
|
||||
lt::renderer::IRenderer::frames_in_flight_lower_limit - 1
|
||||
);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Suite draw = "renderer_draw"_suite = [] {
|
||||
Case { "renderer draw" } = [] {
|
||||
auto observer = ValidationObserver {};
|
||||
auto [context, _] = create_context();
|
||||
using enum lt::renderer::IRenderer::DrawResult;
|
||||
|
||||
auto renderer = Renderer(
|
||||
context,
|
||||
lt::memory::create_ref<Pass>(
|
||||
context,
|
||||
ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
)
|
||||
Case { "renderer draw" } = [] {
|
||||
auto fixture = FixtureDeviceSwapchain {};
|
||||
auto renderer = lt::renderer::IRenderer::create(
|
||||
constants::api,
|
||||
fixture.device(),
|
||||
fixture.swapchain(),
|
||||
constants::frames_in_flight
|
||||
);
|
||||
|
||||
for (auto frame_idx : std::views::iota(0u, 30u))
|
||||
{
|
||||
expect_true(renderer.draw(frame_idx % Renderer::max_frames_in_flight));
|
||||
expect_eq(renderer->draw(frame_idx % constants::frames_in_flight), success);
|
||||
}
|
||||
expect_false(observer.had_any_messages());
|
||||
};
|
||||
|
||||
Case { "post swapchain replacement renderer draw" } = [] {
|
||||
auto observer = ValidationObserver {};
|
||||
auto [context, _] = create_context();
|
||||
auto pass = lt::memory::create_ref<Pass>(
|
||||
context,
|
||||
ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
auto fixture = FixtureDeviceSwapchain {};
|
||||
auto renderer = lt::renderer::IRenderer::create(
|
||||
constants::api,
|
||||
fixture.device(),
|
||||
fixture.swapchain(),
|
||||
constants::frames_in_flight
|
||||
);
|
||||
|
||||
auto renderer = Renderer { context, pass };
|
||||
|
||||
for (auto frame_idx : std::views::iota(0u, 15u))
|
||||
{
|
||||
expect_true(renderer.draw(frame_idx % Renderer::max_frames_in_flight));
|
||||
expect_eq(renderer->draw(frame_idx % constants::frames_in_flight), success);
|
||||
}
|
||||
|
||||
context.recreate_swapchain();
|
||||
renderer.replace_swapchain(context.swapchain());
|
||||
pass->replace_swapchain(context.swapchain());
|
||||
fixture.recreate_swapchain();
|
||||
renderer->replace_swapchain(fixture.swapchain());
|
||||
for (auto frame_idx : std::views::iota(0u, 15u))
|
||||
{
|
||||
expect_true(renderer.draw(frame_idx % Renderer::max_frames_in_flight));
|
||||
expect_eq(renderer->draw(frame_idx % constants::frames_in_flight), success);
|
||||
}
|
||||
|
||||
expect_false(observer.had_any_messages());
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#include <renderer/components/messenger.hpp>
|
||||
#include <renderer/frontend/context/context.hpp>
|
||||
#include <renderer/frontend/context/device.hpp>
|
||||
#include <renderer/frontend/context/gpu.hpp>
|
||||
#include <renderer/frontend/context/instance.hpp>
|
||||
#include <renderer/frontend/context/surface.hpp>
|
||||
#include <renderer/frontend/context/swapchain.hpp>
|
||||
#include <renderer/frontend/messenger.hpp>
|
||||
#include <renderer/frontend/renderer/pass.hpp>
|
||||
#include <renderer/frontend/renderer/renderer.hpp>
|
||||
|
@ -9,20 +13,65 @@
|
|||
namespace lt::renderer {
|
||||
|
||||
System::System(CreateInfo info)
|
||||
: m_api(info.config.target_api)
|
||||
: m_surface_entity(info.surface_entity)
|
||||
, m_api(info.config.target_api)
|
||||
, m_registry(std::move(info.registry))
|
||||
, m_context(IContext::create(m_api, info.surface_entity))
|
||||
, m_surface_entity(info.surface_entity)
|
||||
, m_instance(IInstance::get(m_api))
|
||||
, m_max_frames_in_flight(info.config.max_frames_in_flight)
|
||||
{
|
||||
// ensure(m_stats, "Failed to initialize system: null stats");
|
||||
ensure(m_registry, "Failed to initialize renderer system: null registry");
|
||||
ensure(m_registry, "Failed to initialize renderer::System: null registry");
|
||||
ensure(
|
||||
std::clamp(
|
||||
info.config.max_frames_in_flight,
|
||||
frames_in_flight_lower_limit,
|
||||
frames_in_flight_upper_limit
|
||||
) == info.config.max_frames_in_flight,
|
||||
"Failed to initialize renderer::System: max_frames_in_flight ({}) not within bounds ({} -> "
|
||||
"{}) ",
|
||||
info.config.max_frames_in_flight,
|
||||
frames_in_flight_lower_limit,
|
||||
frames_in_flight_upper_limit
|
||||
);
|
||||
|
||||
m_renderer = IRenderer::create(m_api, *m_context, info.config.max_frames_in_flight);
|
||||
if (info.messenger_info.has_value())
|
||||
{
|
||||
ensure(
|
||||
create_messenger_component(m_registry->create_entity(), info.messenger_info.value()),
|
||||
"Failed to initialize renderer::System: failed to create messenger component"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_wrn(
|
||||
"Creating renderer::System without a default messenger component, this is not "
|
||||
"recommended"
|
||||
);
|
||||
}
|
||||
|
||||
m_surface = ISurface::create(m_api, m_instance, m_surface_entity);
|
||||
m_gpu = IGpu::create(m_api, m_instance);
|
||||
m_device = IDevice::create(m_api, m_gpu.get(), m_surface.get());
|
||||
m_swapchain = ISwapchain::create(m_api, m_surface.get(), m_gpu.get(), m_device.get());
|
||||
m_renderer = { IRenderer::create(
|
||||
m_api,
|
||||
m_device.get(),
|
||||
m_swapchain.get(),
|
||||
info.config.max_frames_in_flight
|
||||
) };
|
||||
}
|
||||
|
||||
System::~System()
|
||||
{
|
||||
auto entities_to_remove = std::vector<ecs::EntityId> {};
|
||||
for (auto &[entity, surface] : m_registry->view<MessengerComponent>())
|
||||
{
|
||||
entities_to_remove.emplace_back(entity);
|
||||
}
|
||||
|
||||
for (auto entity : entities_to_remove)
|
||||
{
|
||||
m_registry->remove<MessengerComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void System::on_register()
|
||||
|
@ -41,37 +90,41 @@ void System::tick(app::TickInfo tick)
|
|||
{
|
||||
if (std::holds_alternative<surface::ResizedEvent>(event))
|
||||
{
|
||||
m_context->recreate_swapchain();
|
||||
m_renderer->replace_swapchain(m_context->swapchain());
|
||||
// m_pass->replace_swapchain(m_context->swapchain());
|
||||
m_swapchain.reset();
|
||||
m_swapchain = ISwapchain::create(m_api, m_surface.get(), m_gpu.get(), m_device.get());
|
||||
m_renderer->replace_swapchain(m_swapchain.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_renderer->draw(m_frame_idx) != IRenderer::DrawResult::success)
|
||||
{
|
||||
m_context->recreate_swapchain();
|
||||
m_renderer->replace_swapchain(m_context->swapchain());
|
||||
// m_pass->replace_swapchain(m_context->swapchain());
|
||||
m_swapchain.reset();
|
||||
m_swapchain = ISwapchain::create(m_api, m_surface.get(), m_gpu.get(), m_device.get());
|
||||
m_renderer->replace_swapchain(m_swapchain.get());
|
||||
|
||||
std::ignore = m_renderer->draw(m_frame_idx); // drop the frame if failed twice
|
||||
}
|
||||
|
||||
m_frame_idx = (m_frame_idx + 1) % m_max_frames_in_flight;
|
||||
}
|
||||
|
||||
void System::create_messenger_component(ecs::EntityId entity, MessengerComponent::CreateInfo info)
|
||||
[[nodiscard]] auto System::create_messenger_component(
|
||||
ecs::EntityId entity,
|
||||
MessengerComponent::CreateInfo info
|
||||
) -> bool
|
||||
try
|
||||
{
|
||||
auto &component = m_registry->add<MessengerComponent>(entity, std::move(info));
|
||||
component.m_implementation = IMessenger::create(
|
||||
m_api,
|
||||
m_context->instance(),
|
||||
{ m_registry, entity }
|
||||
);
|
||||
component.m_implementation = IMessenger::create(m_api, m_instance, { m_registry, entity });
|
||||
// component.m_user_data = info.user_data;
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception &exp)
|
||||
{
|
||||
log_err("Failed to create renderer::MessengerComponent:");
|
||||
log_err("\twhat: {}", exp.what());
|
||||
m_registry->remove<MessengerComponent>(entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#include <memory/reference.hpp>
|
||||
#include <memory/scope.hpp>
|
||||
#include <ranges>
|
||||
#include <renderer/frontend/renderer/renderer.hpp>
|
||||
#include <renderer/system.hpp>
|
||||
#include <renderer/vk/context/context.hpp>
|
||||
#include <renderer/vk/renderer/renderer.hpp>
|
||||
#include <renderer/test/utils.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
@ -15,10 +14,9 @@ using test::expect_throw;
|
|||
using test::expect_true;
|
||||
using test::Suite;
|
||||
|
||||
using lt::renderer::MessageSeverity;
|
||||
using renderer::System;
|
||||
|
||||
constexpr auto resolution = math::uvec2 { 800, 600 };
|
||||
|
||||
struct SurfaceContext
|
||||
{
|
||||
surface::System system;
|
||||
|
@ -31,140 +29,67 @@ struct RendererContext
|
|||
System system;
|
||||
};
|
||||
|
||||
[[nodiscard]] auto create_surface() -> SurfaceContext
|
||||
{
|
||||
using surface::SurfaceComponent;
|
||||
|
||||
auto surface_registry = memory::create_ref<ecs::Registry>();
|
||||
auto surface_entity = surface_registry->create_entity();
|
||||
auto surface_system = surface::System(surface_registry);
|
||||
surface_registry->add<SurfaceComponent>(
|
||||
surface_entity,
|
||||
SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = resolution,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
.system = std::move(surface_system),
|
||||
.entity = ecs::Entity { surface_registry, surface_entity },
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto create_system() -> std::pair<SurfaceContext, RendererContext>
|
||||
{
|
||||
auto surface_context = create_surface();
|
||||
auto &[surface_system, surface_entity] = surface_context;
|
||||
auto registry = memory::create_ref<ecs::Registry>();
|
||||
auto stats = memory::create_ref<app::SystemStats>();
|
||||
|
||||
return {
|
||||
std::move(surface_context),
|
||||
RendererContext {
|
||||
.registry = registry,
|
||||
.system = System(
|
||||
{
|
||||
.registry = registry,
|
||||
.surface_entity = surface_entity,
|
||||
.system_stats = stats,
|
||||
}
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class SystemTest
|
||||
{
|
||||
public:
|
||||
SystemTest()
|
||||
{
|
||||
m_surface_entity->add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = resolution,
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] auto registry() const -> memory::Ref<ecs::Registry>
|
||||
{
|
||||
return m_registry;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto surface_entity() const -> const ecs::Entity &
|
||||
{
|
||||
return *m_surface_entity;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto stats() const -> memory::Ref<app::SystemStats>
|
||||
{
|
||||
return m_stats;
|
||||
}
|
||||
|
||||
private:
|
||||
memory::Ref<app::SystemStats> m_stats = memory::create_ref<app::SystemStats>();
|
||||
|
||||
memory::Ref<ecs::Registry> m_registry = memory::create_ref<ecs::Registry>();
|
||||
|
||||
memory::Ref<surface::System> m_surface_system = memory::create_ref<surface::System>(
|
||||
m_registry
|
||||
);
|
||||
|
||||
memory::Scope<ecs::Entity> m_surface_entity = memory::create_scope<ecs::Entity>(
|
||||
m_registry,
|
||||
m_registry->create_entity()
|
||||
);
|
||||
};
|
||||
|
||||
Suite raii = "raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [&] {
|
||||
ignore = create_system();
|
||||
Case { "happy path won't throw" } = [] {
|
||||
ignore = Fixture_RendererSystem {};
|
||||
};
|
||||
|
||||
Case { "happy path has no validation errors" } = [&] {
|
||||
auto fixture = SystemTest {};
|
||||
std::ignore = System(
|
||||
{
|
||||
.registry = fixture.registry(),
|
||||
.surface_entity = fixture.surface_entity(),
|
||||
.system_stats = fixture.stats(),
|
||||
}
|
||||
);
|
||||
|
||||
expect_true(fixture.stats()->empty_diagnosis());
|
||||
Case { "happy path has no errors" } = [] {
|
||||
auto fixture = Fixture_RendererSystem {};
|
||||
expect_false(fixture.has_any_messages_of(MessageSeverity::error));
|
||||
expect_false(fixture.has_any_messages_of(MessageSeverity::warning));
|
||||
};
|
||||
|
||||
Case { "unhappy path throws" } = [] {
|
||||
auto fixture = SystemTest {};
|
||||
auto fixture = Fixture_SurfaceSystem {};
|
||||
auto empty_entity = ecs::Entity { fixture.registry(), fixture.registry()->create_entity() };
|
||||
auto info = fixture.renderer_system_create_info();
|
||||
|
||||
expect_throw([&] {
|
||||
ignore = System(
|
||||
{
|
||||
.registry = {},
|
||||
.surface_entity = fixture.surface_entity(),
|
||||
.system_stats = fixture.stats(),
|
||||
}
|
||||
);
|
||||
expect_throw([=] mutable {
|
||||
info.registry = nullptr;
|
||||
ignore = System { info };
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
ignore = System(
|
||||
System::CreateInfo {
|
||||
.registry = fixture.registry(),
|
||||
.surface_entity = empty_entity,
|
||||
.system_stats = fixture.stats(),
|
||||
}
|
||||
);
|
||||
expect_throw([=] mutable {
|
||||
info.surface_entity = ecs::Entity({}, {});
|
||||
ignore = System { info };
|
||||
});
|
||||
|
||||
expect_throw([&] {
|
||||
ignore = System(
|
||||
System::CreateInfo {
|
||||
.registry = fixture.registry(),
|
||||
.surface_entity = fixture.surface_entity(),
|
||||
.system_stats = {},
|
||||
}
|
||||
);
|
||||
expect_throw([=] mutable {
|
||||
info.config.target_api = lt::renderer::Api::none;
|
||||
ignore = System { info };
|
||||
});
|
||||
|
||||
// unsupported Apis
|
||||
expect_throw([=] mutable {
|
||||
info.config.target_api = lt::renderer::Api::direct_x;
|
||||
ignore = System { info };
|
||||
});
|
||||
|
||||
expect_throw([=] mutable {
|
||||
info.config.target_api = lt::renderer::Api::metal;
|
||||
ignore = System { info };
|
||||
});
|
||||
|
||||
expect_throw([=] mutable {
|
||||
constexpr auto limit = lt::renderer::System::frames_in_flight_upper_limit;
|
||||
info.config.max_frames_in_flight = limit + 1u;
|
||||
ignore = System { info };
|
||||
});
|
||||
|
||||
expect_throw([=] mutable {
|
||||
constexpr auto limit = lt::renderer::System::frames_in_flight_lower_limit;
|
||||
info.config.max_frames_in_flight = limit - 1u;
|
||||
ignore = System { info };
|
||||
});
|
||||
|
||||
expect_throw([=] mutable {
|
||||
info.messenger_info = lt::renderer::MessengerComponent::CreateInfo {};
|
||||
ignore = System { info };
|
||||
});
|
||||
|
||||
// Make sure the base info is not at fault for unhappiness.
|
||||
ignore = System { info };
|
||||
};
|
||||
};
|
||||
|
|
5
modules/renderer/private/test/constants.hpp
Normal file
5
modules/renderer/private/test/constants.hpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
namespace constants {
|
||||
|
||||
};
|
0
modules/renderer/private/test/formatters.hpp
Normal file
0
modules/renderer/private/test/formatters.hpp
Normal file
1
modules/renderer/private/test/utils.cpp
Normal file
1
modules/renderer/private/test/utils.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
|
199
modules/renderer/private/test/utils.hpp
Normal file
199
modules/renderer/private/test/utils.hpp
Normal file
|
@ -0,0 +1,199 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory/reference.hpp>
|
||||
#include <renderer/components/messenger.hpp>
|
||||
#include <renderer/frontend/context/device.hpp>
|
||||
#include <renderer/frontend/context/gpu.hpp>
|
||||
#include <renderer/frontend/context/instance.hpp>
|
||||
#include <renderer/frontend/context/surface.hpp>
|
||||
#include <renderer/frontend/context/swapchain.hpp>
|
||||
#include <renderer/frontend/messenger.hpp>
|
||||
#include <renderer/system.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using ::lt::test::Case;
|
||||
using ::lt::test::expect_eq;
|
||||
using ::lt::test::expect_false;
|
||||
using ::lt::test::expect_not_nullptr;
|
||||
using ::lt::test::expect_throw;
|
||||
using ::lt::test::expect_true;
|
||||
using ::lt::test::Suite;
|
||||
using ::std::ignore;
|
||||
|
||||
namespace constants {
|
||||
|
||||
constexpr auto api = lt::renderer::Api::vulkan;
|
||||
constexpr auto resolution = lt::math::uvec2 { 800u, 600u };
|
||||
constexpr auto frames_in_flight = uint32_t { 3u };
|
||||
|
||||
} // namespace constants
|
||||
|
||||
class Fixture_SurfaceSystem
|
||||
{
|
||||
public:
|
||||
Fixture_SurfaceSystem()
|
||||
{
|
||||
m_system.create_surface_component(
|
||||
m_entity.id(),
|
||||
lt::surface::SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = constants::resolution,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto renderer_system_create_info() -> lt::renderer::System::CreateInfo
|
||||
{
|
||||
return lt::renderer::System::CreateInfo{
|
||||
.config = lt::renderer::System::Configuration{
|
||||
.target_api = constants::api,
|
||||
.max_frames_in_flight = constants::frames_in_flight,
|
||||
},
|
||||
.registry = registry(),
|
||||
.surface_entity = surface_entity(),
|
||||
} ;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto registry() -> lt::memory::Ref<lt::ecs::Registry> &
|
||||
{
|
||||
return m_registry;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto surface_entity() -> lt::ecs::Entity &
|
||||
{
|
||||
return m_entity;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto surface_system() -> lt::surface::System &
|
||||
{
|
||||
return m_system;
|
||||
}
|
||||
|
||||
private:
|
||||
lt::memory::Ref<lt::ecs::Registry> m_registry = lt::memory::create_ref<lt::ecs::Registry>();
|
||||
|
||||
lt::ecs::Entity m_entity { m_registry, m_registry->create_entity() };
|
||||
|
||||
lt::surface::System m_system = lt::surface::System(m_registry);
|
||||
};
|
||||
|
||||
class Fixture_SurfaceGpu: public Fixture_SurfaceSystem
|
||||
{
|
||||
public:
|
||||
Fixture_SurfaceGpu() = default;
|
||||
|
||||
[[nodiscard]] auto surface() -> lt::renderer::ISurface *
|
||||
{
|
||||
return m_surface.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto gpu() -> lt::renderer::IGpu *
|
||||
{
|
||||
return m_gpu.get();
|
||||
}
|
||||
|
||||
private:
|
||||
lt::memory::Scope<lt::renderer::ISurface> m_surface { lt::renderer::ISurface::create(
|
||||
constants::api,
|
||||
lt::renderer::IInstance::get(constants::api),
|
||||
surface_entity()
|
||||
) };
|
||||
|
||||
lt::memory::Scope<lt::renderer::IGpu> m_gpu {
|
||||
lt::renderer::IGpu::create(constants::api, lt::renderer::IInstance::get(constants::api))
|
||||
};
|
||||
};
|
||||
|
||||
class FixtureDeviceSwapchain: public Fixture_SurfaceGpu
|
||||
{
|
||||
public:
|
||||
FixtureDeviceSwapchain() = default;
|
||||
|
||||
[[nodiscard]] auto device() -> lt::renderer::IDevice *
|
||||
{
|
||||
return m_device.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto swapchain() -> lt::renderer::ISwapchain *
|
||||
{
|
||||
return m_swapchain.get();
|
||||
}
|
||||
|
||||
void recreate_swapchain()
|
||||
{
|
||||
m_swapchain.reset();
|
||||
m_swapchain = lt::renderer::ISwapchain::create(
|
||||
constants::api,
|
||||
surface(),
|
||||
gpu(),
|
||||
m_device.get()
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
lt::memory::Scope<lt::renderer::IDevice> m_device {
|
||||
lt::renderer::IDevice::create(constants::api, gpu(), surface())
|
||||
};
|
||||
|
||||
lt::memory::Scope<lt::renderer::ISwapchain> m_swapchain {
|
||||
lt::renderer::ISwapchain::create(constants::api, surface(), gpu(), m_device.get())
|
||||
};
|
||||
};
|
||||
|
||||
class Fixture_RendererSystem: public Fixture_SurfaceSystem
|
||||
{
|
||||
public:
|
||||
Fixture_RendererSystem() = default;
|
||||
|
||||
[[nodiscard]] auto renderer_system() -> lt::renderer::System &
|
||||
{
|
||||
return m_system;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto has_any_messages() const -> bool
|
||||
{
|
||||
return m_has_any_messages;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto has_any_messages_of(lt::renderer::MessageSeverity severity) const -> uint32_t
|
||||
{
|
||||
return m_severity_counter.contains(severity);
|
||||
}
|
||||
|
||||
private:
|
||||
static void messenger_callback(
|
||||
lt::renderer::MessageSeverity severity,
|
||||
lt::renderer::MessageType type,
|
||||
lt::renderer::MessageData data,
|
||||
std::any &user_data
|
||||
)
|
||||
{
|
||||
std::ignore = type;
|
||||
std::ignore = data;
|
||||
|
||||
auto *fixture = std::any_cast<Fixture_RendererSystem *>(user_data);
|
||||
fixture->m_has_any_messages = true;
|
||||
++fixture->m_severity_counter[severity];
|
||||
}
|
||||
|
||||
std::unordered_map<lt::renderer::MessageSeverity, uint32_t> m_severity_counter;
|
||||
|
||||
bool m_has_any_messages {};
|
||||
|
||||
lt::renderer::System m_system = lt::renderer::System::CreateInfo {
|
||||
.config = {
|
||||
.target_api = constants::api,
|
||||
.max_frames_in_flight = constants::frames_in_flight,
|
||||
},
|
||||
.registry = registry(),
|
||||
.surface_entity = surface_entity(),
|
||||
.messenger_info = lt::renderer::MessengerComponent::CreateInfo {
|
||||
.severities = lt::renderer::MessageSeverity::all,
|
||||
.types = lt::renderer::MessageType::all,
|
||||
.callback = &messenger_callback,
|
||||
.user_data = this,
|
||||
}
|
||||
};
|
||||
};
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
namespace lt::renderer {
|
||||
|
||||
enum class API : uint8_t
|
||||
enum class Api : uint8_t
|
||||
{
|
||||
Vulkan,
|
||||
DirectX,
|
||||
Metal,
|
||||
none = 0,
|
||||
|
||||
vulkan,
|
||||
direct_x,
|
||||
metal,
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ enum class MessageType : uint8_t
|
|||
all = general | validation | performance,
|
||||
};
|
||||
|
||||
struct MessengerCallbackData
|
||||
struct MessageData
|
||||
{
|
||||
std::string message;
|
||||
};
|
||||
|
@ -37,7 +37,7 @@ struct MessengerCallbackData
|
|||
using Callback_T = std::function<void(
|
||||
MessageSeverity message_severity,
|
||||
MessageType message_type,
|
||||
MessengerCallbackData data,
|
||||
MessageData data,
|
||||
std::any &user_data
|
||||
)>;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace lt::renderer {
|
|||
class IMessenger
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static auto create(API target_api, class IInstance *instance, ecs::Entity entity)
|
||||
[[nodiscard]] static auto create(Api target_api, class IInstance *instance, ecs::Entity entity)
|
||||
-> memory::Scope<IMessenger>;
|
||||
|
||||
IMessenger() = default;
|
||||
|
|
|
@ -7,15 +7,22 @@
|
|||
#include <memory/scope.hpp>
|
||||
#include <renderer/api.hpp>
|
||||
#include <renderer/components/messenger.hpp>
|
||||
#include <renderer/frontend/renderer/renderer.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class System: public app::ISystem
|
||||
{
|
||||
public:
|
||||
/** config.max_frames_in_flight should not be higher than this value. */
|
||||
static constexpr auto frames_in_flight_upper_limit = IRenderer::frames_in_flight_upper_limit;
|
||||
|
||||
/** config.max_frames_in_flight should not be lower than this value. */
|
||||
static constexpr auto frames_in_flight_lower_limit = IRenderer::frames_in_flight_lower_limit;
|
||||
|
||||
struct Configuration
|
||||
{
|
||||
API target_api;
|
||||
Api target_api;
|
||||
|
||||
uint32_t max_frames_in_flight;
|
||||
};
|
||||
|
@ -27,6 +34,8 @@ public:
|
|||
memory::Ref<ecs::Registry> registry;
|
||||
|
||||
ecs::Entity surface_entity;
|
||||
|
||||
std::optional<MessengerComponent::CreateInfo> messenger_info;
|
||||
};
|
||||
|
||||
System(CreateInfo info);
|
||||
|
@ -47,7 +56,35 @@ public:
|
|||
|
||||
void tick(app::TickInfo tick) override;
|
||||
|
||||
void create_messenger_component(ecs::EntityId entity, MessengerComponent::CreateInfo info);
|
||||
[[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 create_messenger_component(
|
||||
ecs::EntityId entity,
|
||||
MessengerComponent::CreateInfo info
|
||||
) -> bool;
|
||||
|
||||
[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override
|
||||
{
|
||||
|
@ -55,13 +92,23 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
API m_api;
|
||||
Api m_api;
|
||||
|
||||
memory::Ref<ecs::Registry> m_registry;
|
||||
|
||||
ecs::Entity m_surface_entity;
|
||||
|
||||
memory::Scope<class IContext> m_context;
|
||||
memory::Scope<class IMessenger> m_messenger;
|
||||
|
||||
class IInstance *m_instance;
|
||||
|
||||
memory::Scope<class ISurface> m_surface;
|
||||
|
||||
memory::Scope<class IGpu> m_gpu;
|
||||
|
||||
memory::Scope<class IDevice> m_device;
|
||||
|
||||
memory::Scope<class ISwapchain> m_swapchain;
|
||||
|
||||
memory::Scope<class IRenderer> m_renderer;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue