From 44aa60f81680b42d663381c815d07d9183634fda Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Tue, 4 Aug 2026 18:14:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=96=E9=83=A8=E5=86=85?= =?UTF-8?q?=E5=AD=98=E6=95=B0=E6=8D=AE=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Kernel/CMakeLists.txt | 85 ++++++++-------- Kernel/readme.md | 74 ++++++++++++++ .../renderive/base/memory/Memory_Resource.hpp | 66 +++++++++++++ .../src/renderive/base/node/Multiway_Node.hpp | 6 +- .../base/property/Property_Builder.hpp | 13 +++ .../strategy/flow/Flow_Refresh_Strategy.hpp | 11 ++- .../strategy/flow/Flow_Refresh_Strategy.inl | 8 +- .../real_time_data/History_Real_Time_Data.hpp | 5 +- .../real_time_data/History_Real_Time_Data.inl | 19 +++- .../renderable/Renderable_Task_Graph.cpp | 6 +- .../renderable/Renderable_Task_Graph.hpp | 13 ++- .../renderable/base/Renderable_Base.cpp | 5 +- .../renderable/base/Renderable_Base.hpp | 3 + .../renderable/color/Color_Cache.hpp | 4 +- .../src/renderive/scene/Scene2D_Context.hpp | 67 ++++++++++--- .../src/renderive/scene/Scene3D_Context.hpp | 28 +++++- .../src/renderive/scene/base/Scene_Base.cpp | 30 ++++-- .../src/renderive/scene/base/Scene_Base.hpp | 31 +++++- Kernel/{src => tests}/main.cpp | 24 ++--- .../renderable/color/Color_Cache_Test.cpp | 4 +- .../scene/Scene2D_Render_Order_Test.cpp | 4 +- .../scene/Scene_Memory_Resource_Test.cpp | 98 +++++++++++++++++++ main.cmake | 1 + 23 files changed, 500 insertions(+), 105 deletions(-) create mode 100644 Kernel/src/renderive/base/memory/Memory_Resource.hpp rename Kernel/{src => tests}/main.cpp (77%) create mode 100644 Kernel/tests/renderive/scene/Scene_Memory_Resource_Test.cpp diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 57e2253..fa25e58 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -2,72 +2,73 @@ cmake_minimum_required(VERSION 3.20) project(Renderive_Scene_Taskflow LANGUAGES CXX) set(RENDERIVE_TASKFLOW_ROOT "" CACHE PATH "Taskflow 4.1.0 root") find_path(RENDERIVE_TASKFLOW_INCLUDE_DIR - NAMES taskflow/taskflow.hpp - PATHS + NAMES taskflow/taskflow.hpp + PATHS "${RENDERIVE_TASKFLOW_ROOT}" "${RENDERIVE_TASKFLOW_ROOT}/include" "${CMAKE_CURRENT_LIST_DIR}/third_party/taskflow" "${CMAKE_CURRENT_LIST_DIR}/third_party/taskflow/include" ) -if(NOT RENDERIVE_TASKFLOW_INCLUDE_DIR) +if (NOT RENDERIVE_TASKFLOW_INCLUDE_DIR) set(RENDERIVE_TASKFLOW_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/tests/compat") message(STATUS "Taskflow 4.1.0 not found; using the private dependency-graph compatibility layer") -endif() +endif () add_library(renderive_scene STATIC - "${CMAKE_CURRENT_LIST_DIR}/src/renderive/renderable/Renderable_Task_Graph.cpp" - "${CMAKE_CURRENT_LIST_DIR}/src/renderive/renderable/base/Renderable_Base.cpp" - "${CMAKE_CURRENT_LIST_DIR}/src/renderive/scene/base/Scene_Base.cpp" + "${CMAKE_CURRENT_LIST_DIR}/src/renderive/renderable/Renderable_Task_Graph.cpp" + "${CMAKE_CURRENT_LIST_DIR}/src/renderive/renderable/base/Renderable_Base.cpp" + "${CMAKE_CURRENT_LIST_DIR}/src/renderive/scene/base/Scene_Base.cpp" ) target_include_directories(renderive_scene - PUBLIC "$" - PRIVATE "${RENDERIVE_TASKFLOW_INCLUDE_DIR}" + PUBLIC "$" + PRIVATE "${RENDERIVE_TASKFLOW_INCLUDE_DIR}" ) target_compile_features(renderive_scene PUBLIC cxx_std_20) find_package(Threads REQUIRED) target_link_libraries(renderive_scene PRIVATE Threads::Threads) set(renderive_test_sources - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/Concepts_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/node/Multiway_Node_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/observer/Observer_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/observer/time_source/Steady_Time_Source_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/property/Concepts_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/property/Property_Builder_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/property/Validated_Value_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/base/Frame_Control_Strategy_Base_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/concept/Concepts_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/real_time_data/base/Real_Time_Data_Base_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/real_time_data/Real_Time_Data_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/renderable/base/Renderable_Base_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/renderable/color/Color_Cache_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/renderable/concept/Renderable_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/renderable/Renderable_Task_Graph_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/base/Scene_Base_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/concept/Scene_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/Scene2D_Context_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/Scene2D_Render_Order_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/Scene3D_Context_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/Scene_State_Observer_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/state/base/State_Strategy_Base_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/state/Concepts_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/state/Double_State_Strategy_Test.cpp" - "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/state/Triple_State_Strategy_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/Concepts_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/node/Multiway_Node_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/observer/Observer_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/observer/time_source/Steady_Time_Source_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/property/Concepts_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/property/Property_Builder_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/base/property/Validated_Value_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/base/Frame_Control_Strategy_Base_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/concept/Concepts_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/real_time_data/base/Real_Time_Data_Base_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/real_time_data/Real_Time_Data_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/renderable/base/Renderable_Base_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/renderable/color/Color_Cache_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/renderable/concept/Renderable_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/renderable/Renderable_Task_Graph_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/base/Scene_Base_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/Scene_Memory_Resource_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/concept/Scene_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/Scene2D_Context_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/Scene2D_Render_Order_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/Scene3D_Context_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/scene/Scene_State_Observer_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/state/base/State_Strategy_Base_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/state/Concepts_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/state/Double_State_Strategy_Test.cpp" + "${CMAKE_CURRENT_LIST_DIR}/tests/renderive/state/Triple_State_Strategy_Test.cpp" ) -add_executable(renderive_scene_app "${CMAKE_CURRENT_LIST_DIR}/src/main.cpp") +add_executable(renderive_scene_app "tests/main.cpp") target_link_libraries(renderive_scene_app PRIVATE renderive_scene) include(CTest) -if(BUILD_TESTING) +if (BUILD_TESTING) find_package(GTest CONFIG QUIET) - if(NOT GTest_FOUND) + if (NOT GTest_FOUND) add_library(renderive_gtest_compat INTERFACE) target_include_directories(renderive_gtest_compat INTERFACE "${CMAKE_CURRENT_LIST_DIR}/tests/compat") add_library(GTest::gtest ALIAS renderive_gtest_compat) message(STATUS "GTest not found; using the test-only compatibility runner") - endif() + endif () target_sources(renderive_scene_app PRIVATE ${renderive_test_sources}) target_compile_definitions(renderive_scene_app PRIVATE RENDERIVE_WITH_GTEST=1) target_link_libraries(renderive_scene_app PRIVATE GTest::gtest) add_test(NAME renderive_scene_tests COMMAND renderive_scene_app --gtest) -endif() +endif () diff --git a/Kernel/readme.md b/Kernel/readme.md index fbfdd43..8aea676 100644 --- a/Kernel/readme.md +++ b/Kernel/readme.md @@ -98,3 +98,77 @@ renderive_scene_app --gtest ## Taskflow 查找 CMake 通过 `RENDERIVE_TASKFLOW_ROOT` 或系统 include 路径查找 `taskflow/taskflow.hpp`。Taskflow 不存在时,仅测试和沙盒构建使用 `tests/compat/taskflow/taskflow.hpp` 验证图生成与依赖语义;正式集成时把 `RENDERIVE_TASKFLOW_ROOT` 指向 Taskflow 4.1.0 根目录或 include 目录。 + +## Scene 内存域 + +`Scene_Base` 接受外部 `std::pmr::memory_resource` 作为上游资源。Scene 内部建立 `std::pmr::synchronized_pool_resource`,所有可能跨线程访问的持久容器都使用该线程安全内存域: + +```cpp +std::pmr::unsynchronized_pool_resource application_resource; +Scene2D_Context<> scene(application_resource); +``` + +外部资源决定最终的上游分配机制。Scene 内部的同步池负责将来自调用线程、后台 Scene 线程和 Renderable 构图路径的分配串行化。外部资源必须比 Scene 以及由 Scene 工厂创建后仍存活的 Renderable 更长寿。 + +可通过以下接口访问两个层级: + +```cpp +scene.upstream_memory_resource(); +scene.memory_resource(); +``` + +`upstream_memory_resource()` 返回构造时传入的资源;`memory_resource()` 返回 Scene 实际使用的线程安全同步池。 + +单次渲染构图不长期占用同步池。`Scene_Base::execute_taskflow()` 和 Scene2D 树顺序生成使用局部 `std::pmr::monotonic_buffer_resource`,返回后整体释放临时索引、标记和任务句柄容器。 + +## Renderable 两种构造模式 + +Scene 工厂模式同时控制 Renderable 对象、`shared_ptr` 控制块以及 Renderable 内部树和任务图分配: + +```cpp +Scene2D_Context<> scene(memory_resource); +auto renderable = scene.make_renderable(configuration); +scene.attach_renderable(renderable); +``` + +工厂只构造对象,不自动挂载,`attach_renderable()` 的原有语义不变。工厂分配器持有 Scene 内存域的共享所有权,因此 Renderable 析构时内存域仍然有效。 + +独立构造模式继续保留: + +```cpp +auto renderable = std::make_shared(scene, configuration); +Spectrum_Renderable stack_renderable(scene, configuration); +``` + +独立模式下,Renderable 对象本身和 `shared_ptr` 控制块由调用方选择的机制分配;`Multiway_Node`、`Renderable_Task_Graph`、任务名称和依赖边仍使用所属 Scene 的内存域。 + +## 独立 allocator-aware 组件 + +以下组件可脱离 Scene 独立注入资源: + +- `Multiway_Node` +- `Renderable_Task_Graph` +- `Recording_Color_Cache` +- `Flow_Refresh_Strategy` +- `History_Real_Time_Data` +- `Property_Builder::build_with_resource()` +- `Property_Builder::build_unique_with_resource()` + +`History_Real_Time_Data` 的时间戳容器始终使用传入资源。历史值容器只有在 `Container` 本身支持 PMR allocator 时才使用该资源: + +```cpp +History_Real_Time_Data> history(memory_resource); +``` + +## 不受 Scene PMR 控制的分配 + +以下内存不属于 Renderive 自己的 allocator-aware 场景域: + +- Taskflow 4.1.0 内部任务节点、执行队列和 executor 工作资源 +- `std::function` 超出小对象优化后的内部存储 +- `std::thread` 的系统线程对象和线程栈 +- 异常运行时对象 +- 用户自定义 State、Frame、Observer、Renderable 成员内部自行使用的非 PMR 容器 +- 第三方渲染后端内部资源 + +Scene PMR 的语义是控制 Renderive 明确实现为 allocator-aware 的核心对象,不承诺接管第三方库和用户类型的全部动态分配。 diff --git a/Kernel/src/renderive/base/memory/Memory_Resource.hpp b/Kernel/src/renderive/base/memory/Memory_Resource.hpp new file mode 100644 index 0000000..0bf47c1 --- /dev/null +++ b/Kernel/src/renderive/base/memory/Memory_Resource.hpp @@ -0,0 +1,66 @@ +#pragma once +#include +#include +#include +#include +class Scene_Memory_Domain { +public: + explicit Scene_Memory_Domain(std::pmr::memory_resource& upstream) : upstream_(&upstream), resource_(&upstream) {} + std::pmr::memory_resource& resource() noexcept { + return resource_; + } + std::pmr::memory_resource& upstream() noexcept { + return *upstream_; + } +private: + std::pmr::memory_resource* const upstream_; + std::pmr::synchronized_pool_resource resource_; +}; +template +class Scene_Memory_Allocator { +public: + using value_type = Value; + template + struct rebind { + using other = Scene_Memory_Allocator; + }; + Scene_Memory_Allocator() = delete; + explicit Scene_Memory_Allocator(std::shared_ptr domain) noexcept : domain_(std::move(domain)) {} + template + Scene_Memory_Allocator(const Scene_Memory_Allocator& other) noexcept : domain_(other.domain_) {} + Value* allocate(std::size_t count) { + return static_cast(domain_->resource().allocate(count * sizeof(Value), alignof(Value))); + } + void deallocate(Value* pointer, std::size_t count) noexcept { + domain_->resource().deallocate(pointer, count * sizeof(Value), alignof(Value)); + } + template + bool operator==(const Scene_Memory_Allocator& other) const noexcept { + return domain_ == other.domain_; + } +private: + template + friend class Scene_Memory_Allocator; + std::shared_ptr domain_; +}; +template +struct Memory_Resource_Deleter { + std::pmr::memory_resource* resource{std::pmr::get_default_resource()}; + void operator()(Value* value) const noexcept { + std::destroy_at(value); + resource->deallocate(value, sizeof(Value), alignof(Value)); + } +}; +template +using Memory_Resource_Unique_Ptr = std::unique_ptr>; +template +Memory_Resource_Unique_Ptr make_memory_resource_unique(std::pmr::memory_resource& resource, Args&&... args) { + void* storage = resource.allocate(sizeof(Value), alignof(Value)); + try { + Value* value = std::construct_at(static_cast(storage), std::forward(args)...); + return Memory_Resource_Unique_Ptr(value, Memory_Resource_Deleter{&resource}); + } catch (...) { + resource.deallocate(storage, sizeof(Value), alignof(Value)); + throw; + } +} diff --git a/Kernel/src/renderive/base/node/Multiway_Node.hpp b/Kernel/src/renderive/base/node/Multiway_Node.hpp index 5151000..5105b5e 100644 --- a/Kernel/src/renderive/base/node/Multiway_Node.hpp +++ b/Kernel/src/renderive/base/node/Multiway_Node.hpp @@ -1,12 +1,14 @@ #pragma once #include #include +#include #include #include template class Multiway_Node { public: - explicit Multiway_Node(Owner* owner = nullptr) noexcept : owner(owner) {} + explicit Multiway_Node(Owner* owner = nullptr, std::pmr::memory_resource& memory_resource = *std::pmr::get_default_resource()) noexcept + : owner(owner), children(&memory_resource) {} Multiway_Node(const Multiway_Node&) = delete; Multiway_Node& operator=(const Multiway_Node&) = delete; Multiway_Node(Multiway_Node&&) = delete; @@ -45,5 +47,5 @@ public: } Owner* owner{}; Multiway_Node* parent{}; - std::vector children; + std::pmr::vector children; }; diff --git a/Kernel/src/renderive/base/property/Property_Builder.hpp b/Kernel/src/renderive/base/property/Property_Builder.hpp index 392c0f3..09775bd 100644 --- a/Kernel/src/renderive/base/property/Property_Builder.hpp +++ b/Kernel/src/renderive/base/property/Property_Builder.hpp @@ -1,6 +1,7 @@ #pragma once #include "Concepts.hpp" #include "Validators.hpp" +#include "renderive/base/memory/Memory_Resource.hpp" #include #include #include @@ -35,10 +36,22 @@ public: } template requires Property_Constructible + std::shared_ptr build_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const { + validator(properties); + return std::allocate_shared(std::pmr::polymorphic_allocator(&memory_resource), properties, std::forward(args)...); + } + template + requires Property_Constructible std::unique_ptr build_unique(Args&&... args) const { validator(properties); return std::make_unique(properties, std::forward(args)...); } + template + requires Property_Constructible + Memory_Resource_Unique_Ptr build_unique_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const { + validator(properties); + return make_memory_resource_unique(memory_resource, properties, std::forward(args)...); + } private: Properties properties; [[no_unique_address]] Validator validator; diff --git a/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp b/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp index c61257e..4bcb693 100644 --- a/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp +++ b/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp @@ -3,9 +3,12 @@ #include #include #include +#include #include #include +#include #include "renderive/base/Concepts.hpp" +#include "renderive/base/memory/Memory_Resource.hpp" #include "renderive/base/observer/Observer.hpp" #include "renderive/frame_control/base/Frame_Control_Strategy_Base.hpp" #include "renderive/real_time_data/Observation.hpp" @@ -67,7 +70,7 @@ public: const Frame* get() const; private: Flow_Refresh_Strategy* strategy_{}; - std::unique_ptr frame_; + Memory_Resource_Unique_Ptr frame_; }; class Render_Lease { public: @@ -86,10 +89,11 @@ public: const Frame* get() const; private: Flow_Refresh_Strategy* strategy_{}; - std::unique_ptr frame_; + Memory_Resource_Unique_Ptr frame_; std::unique_lock lease_lock_; }; explicit Flow_Refresh_Strategy(Observer observer = {}); + Flow_Refresh_Strategy(std::pmr::memory_resource& memory_resource, Observer observer = {}); Painter_Lease acquire_painter(); Render_Lease acquire_renderer(); void swap() override; @@ -102,8 +106,9 @@ public: private: std::uint64_t now_ns() const noexcept; void observe(const Observation& observation) noexcept; + std::pmr::memory_resource* const memory_resource_; Observer observer_; - std::deque> frames_; + std::pmr::deque> frames_; std::uint64_t next_sequence_{}; std::uint64_t real_time_data_update_sequence_{}; Real_Time_Data_Update_State last_real_time_data_update_; diff --git a/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.inl b/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.inl index 4780313..4a925ce 100644 --- a/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.inl +++ b/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.inl @@ -1,7 +1,7 @@ #pragma once template Flow_Refresh_Strategy::Painter_Lease::Painter_Lease(Flow_Refresh_Strategy& strategy) - : strategy_(&strategy), frame_(std::make_unique()) { + : strategy_(&strategy), frame_(make_memory_resource_unique(*strategy.memory_resource_)) { std::lock_guard lock(strategy.state_mutex_); frame_->statistics.sequence = ++strategy.next_sequence_; frame_->statistics.real_time_data_update_sequence = strategy.real_time_data_update_sequence_; @@ -136,7 +136,11 @@ auto Flow_Refresh_Strategy::Render_Lease::get() co return frame_.get(); } template -Flow_Refresh_Strategy::Flow_Refresh_Strategy(Observer observer) : Frame_Control_Strategy_Base(), observer_(std::move(observer)) {} +Flow_Refresh_Strategy::Flow_Refresh_Strategy(Observer observer) + : Flow_Refresh_Strategy(*std::pmr::get_default_resource(), std::move(observer)) {} +template +Flow_Refresh_Strategy::Flow_Refresh_Strategy(std::pmr::memory_resource& memory_resource, Observer observer) + : Frame_Control_Strategy_Base(), memory_resource_(&memory_resource), observer_(std::move(observer)), frames_(&memory_resource) {} template auto Flow_Refresh_Strategy::acquire_painter() -> Painter_Lease { return Painter_Lease(*this); diff --git a/Kernel/src/renderive/real_time_data/History_Real_Time_Data.hpp b/Kernel/src/renderive/real_time_data/History_Real_Time_Data.hpp index 5698b81..2c0c090 100644 --- a/Kernel/src/renderive/real_time_data/History_Real_Time_Data.hpp +++ b/Kernel/src/renderive/real_time_data/History_Real_Time_Data.hpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ public: static_assert(Real_Time_Data_History_Container); static_assert(Timed_Struct_Observer); explicit History_Real_Time_Data(Observer observer = {}); + History_Real_Time_Data(std::pmr::memory_resource& memory_resource, Observer observer = {}); History_Real_Time_Data(const History_Real_Time_Data&) = delete; History_Real_Time_Data& operator=(const History_Real_Time_Data&) = delete; void update(Value value); @@ -37,10 +39,11 @@ public: observer_.bind(target); } private: + static Container make_container(std::pmr::memory_resource& memory_resource); mutable Mutex mutex_; Observer observer_; Container values_; - std::vector update_times_; + std::pmr::vector update_times_; std::uint64_t revision_{}; std::uint64_t total_update_count_{}; std::uint64_t last_update_time_ns_{}; diff --git a/Kernel/src/renderive/real_time_data/History_Real_Time_Data.inl b/Kernel/src/renderive/real_time_data/History_Real_Time_Data.inl index d321f0a..c7ffd06 100644 --- a/Kernel/src/renderive/real_time_data/History_Real_Time_Data.inl +++ b/Kernel/src/renderive/real_time_data/History_Real_Time_Data.inl @@ -1,6 +1,23 @@ #pragma once template -History_Real_Time_Data::History_Real_Time_Data(Observer observer) : observer_(std::move(observer)) {} +History_Real_Time_Data::History_Real_Time_Data(Observer observer) + : History_Real_Time_Data(*std::pmr::get_default_resource(), std::move(observer)) {} +template +History_Real_Time_Data::History_Real_Time_Data(std::pmr::memory_resource& memory_resource, Observer observer) + : observer_(std::move(observer)), values_(make_container(memory_resource)), update_times_(&memory_resource) {} +template +auto History_Real_Time_Data::make_container(std::pmr::memory_resource& memory_resource) -> Container { + if constexpr (requires { typename Container::allocator_type; }) { + using Allocator = typename Container::allocator_type; + if constexpr (std::constructible_from && std::constructible_from) { + return Container(Allocator(&memory_resource)); + } else { + return Container(); + } + } else { + return Container(); + } +} template void History_Real_Time_Data::update(Value value) { Real_Time_Data_Observation observation; diff --git a/Kernel/src/renderive/renderable/Renderable_Task_Graph.cpp b/Kernel/src/renderive/renderable/Renderable_Task_Graph.cpp index f55a5ce..671756f 100644 --- a/Kernel/src/renderive/renderable/Renderable_Task_Graph.cpp +++ b/Kernel/src/renderive/renderable/Renderable_Task_Graph.cpp @@ -1,8 +1,10 @@ #include "Renderable_Task_Graph.hpp" #include #include +Renderable_Task_Graph::Renderable_Task_Graph(std::pmr::memory_resource& memory_resource) noexcept + : memory_resource_(&memory_resource), nodes_(&memory_resource) {} Renderable_Task_Graph::Task Renderable_Task_Graph::emplace(Task_Function function, std::string name) { - nodes_.push_back({std::move(function), std::move(name), {}}); + nodes_.emplace_back(std::move(function), std::move(name), *memory_resource_); return {nodes_.size() - 1}; } void Renderable_Task_Graph::precede(Task from, Task to) { @@ -17,6 +19,6 @@ void Renderable_Task_Graph::clear() { bool Renderable_Task_Graph::empty() const noexcept { return nodes_.empty(); } -const std::vector& Renderable_Task_Graph::nodes() const noexcept { +const std::pmr::vector& Renderable_Task_Graph::nodes() const noexcept { return nodes_; } diff --git a/Kernel/src/renderive/renderable/Renderable_Task_Graph.hpp b/Kernel/src/renderive/renderable/Renderable_Task_Graph.hpp index ce97397..850c67c 100644 --- a/Kernel/src/renderive/renderable/Renderable_Task_Graph.hpp +++ b/Kernel/src/renderive/renderable/Renderable_Task_Graph.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include #include "renderive/scene/base/Scene_Render_Context.hpp" @@ -12,15 +13,19 @@ public: friend bool operator==(Task, Task) = default; }; struct Node { + Node(Task_Function function, std::string name, std::pmr::memory_resource& memory_resource) + : function(std::move(function)), name(name.data(), name.size(), &memory_resource), successors(&memory_resource) {} Task_Function function; - std::string name; - std::vector successors; + std::pmr::string name; + std::pmr::vector successors; }; + explicit Renderable_Task_Graph(std::pmr::memory_resource& memory_resource = *std::pmr::get_default_resource()) noexcept; Task emplace(Task_Function function, std::string name = {}); void precede(Task from, Task to); void clear(); bool empty() const noexcept; - const std::vector& nodes() const noexcept; + const std::pmr::vector& nodes() const noexcept; private: - std::vector nodes_; + std::pmr::memory_resource* const memory_resource_; + std::pmr::vector nodes_; }; diff --git a/Kernel/src/renderive/renderable/base/Renderable_Base.cpp b/Kernel/src/renderive/renderable/base/Renderable_Base.cpp index ac2f517..847b9ce 100644 --- a/Kernel/src/renderive/renderable/base/Renderable_Base.cpp +++ b/Kernel/src/renderive/renderable/base/Renderable_Base.cpp @@ -1,7 +1,7 @@ #include "Renderable_Base.hpp" #include "renderive/scene/base/Scene_Base.hpp" Renderable_Base::Renderable_Base(Scene_Base& scene, Renderable_Configuration configuration) noexcept - : scene(&scene), layer_node(this), dependency_node(this), configuration(configuration) {} + : scene(&scene), layer_node(this, scene.memory_resource()), dependency_node(this, scene.memory_resource()), configuration(configuration), memory_resource_(&scene.memory_resource()), task_graph_(*memory_resource_) {} Renderable_Base::~Renderable_Base() = default; void Renderable_Base::render(const Scene_Render_Context&) {} void Renderable_Base::invalidate_cache() noexcept { @@ -28,6 +28,9 @@ const Renderable_Task_Graph& Renderable_Base::task_graph() { } return task_graph_; } +std::pmr::memory_resource& Renderable_Base::memory_resource() const noexcept { + return *memory_resource_; +} void Renderable_Base::build_task_graph(Renderable_Task_Graph& graph) { graph.emplace([this](const Scene_Render_Context& context) { render(context); diff --git a/Kernel/src/renderive/renderable/base/Renderable_Base.hpp b/Kernel/src/renderive/renderable/base/Renderable_Base.hpp index d03d49d..49061cd 100644 --- a/Kernel/src/renderive/renderable/base/Renderable_Base.hpp +++ b/Kernel/src/renderive/renderable/base/Renderable_Base.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include "renderive/base/node/Multiway_Node.hpp" #include "renderive/renderable/Renderable_Task_Graph.hpp" class Scene_Base; @@ -22,6 +23,7 @@ public: bool cache_valid() const noexcept; void rebuild_task_graph() noexcept; const Renderable_Task_Graph& task_graph(); + std::pmr::memory_resource& memory_resource() const noexcept; Scene_Base* scene; Layer_Node layer_node; Dependency_Node dependency_node; @@ -33,6 +35,7 @@ private: friend class Scene_Base; bool requires_render() const noexcept; void mark_rendered() noexcept; + std::pmr::memory_resource* const memory_resource_; Renderable_Task_Graph task_graph_; std::atomic cache_revision_{1}; std::atomic rendered_cache_revision_{}; diff --git a/Kernel/src/renderive/renderable/color/Color_Cache.hpp b/Kernel/src/renderive/renderable/color/Color_Cache.hpp index f517576..2cc8197 100644 --- a/Kernel/src/renderive/renderable/color/Color_Cache.hpp +++ b/Kernel/src/renderive/renderable/color/Color_Cache.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include class Color_Cache { public: @@ -9,6 +10,7 @@ public: }; class Recording_Color_Cache : public Color_Cache { public: + explicit Recording_Color_Cache(std::pmr::memory_resource& memory_resource = *std::pmr::get_default_resource()) : values(&memory_resource) {} void clear() override { values.clear(); ++revision; @@ -22,6 +24,6 @@ public: values.push_back(value); ++revision; } - std::vector values; + std::pmr::vector values; std::uint64_t revision{}; }; diff --git a/Kernel/src/renderive/scene/Scene2D_Context.hpp b/Kernel/src/renderive/scene/Scene2D_Context.hpp index ea43438..249f125 100644 --- a/Kernel/src/renderive/scene/Scene2D_Context.hpp +++ b/Kernel/src/renderive/scene/Scene2D_Context.hpp @@ -1,10 +1,13 @@ #pragma once #include +#include #include -#include +#include #include #include #include +#include +#include "renderive/base/memory/Memory_Resource.hpp" #include "renderive/base/observer/Observer.hpp" #include "renderive/frame_control/Frame_Control.hpp" #include "renderive/renderable/color/Concepts.hpp" @@ -14,6 +17,8 @@ struct Scene2D_Frame_Data {}; struct Scene2D_State { std::uint64_t revision{}; }; +template +concept Scene2D_Frame_Control_Constructible = std::constructible_from || std::constructible_from; template , Color_Cache_Type Cache = Recording_Color_Cache, class State = Scene2D_State, class State_Observer = Observer_State<>, class Scene_Observer = Observer_State<>> requires Frame_Control_Strategy_For && State_Value class Scene2D_Context : public Triple_State_Strategy { @@ -27,14 +32,22 @@ public: using Painter_Lease = typename Frame_Control::Painter_Lease; using Render_Lease = typename Frame_Control::Render_Lease; template - requires std::constructible_from - explicit Scene2D_Context(Args&&... args) : Scene_State_Strategy(), frame_control(std::forward(args)...), display_root(nullptr), dependency_root(nullptr) { + requires Scene2D_Frame_Control_Constructible + explicit Scene2D_Context(Args&&... args) : Scene2D_Context(*std::pmr::get_default_resource(), std::forward(args)...) {} + template + requires Scene2D_Frame_Control_Constructible + explicit Scene2D_Context(std::pmr::memory_resource& memory_resource, Args&&... args) + : Scene_State_Strategy(State{}, memory_resource), frame_control(make_frame_control(this->memory_resource(), std::forward(args)...)), display_root(nullptr, this->memory_resource()), dependency_root(nullptr, this->memory_resource()), final_color_cache(make_cache(this->memory_resource())), color_caches_(&this->memory_resource()) { this->frame_control_strategy = &frame_control; } template - requires std::constructible_from + requires Scene2D_Frame_Control_Constructible Scene2D_Context(With_Observer state_observer, With_Observer scene_observer, Args&&... args) - : Scene_State_Strategy(std::move(state_observer)), frame_control(std::forward(args)...), display_root(nullptr), dependency_root(nullptr), scene_observer_(std::move(scene_observer.observer)) { + : Scene2D_Context(*std::pmr::get_default_resource(), std::move(state_observer), std::move(scene_observer), std::forward(args)...) {} + template + requires Scene2D_Frame_Control_Constructible + Scene2D_Context(std::pmr::memory_resource& memory_resource, With_Observer state_observer, With_Observer scene_observer, Args&&... args) + : Scene_State_Strategy(State{}, std::move(state_observer), memory_resource), frame_control(make_frame_control(this->memory_resource(), std::forward(args)...)), display_root(nullptr, this->memory_resource()), dependency_root(nullptr, this->memory_resource()), final_color_cache(make_cache(this->memory_resource())), scene_observer_(std::move(scene_observer.observer)), color_caches_(&this->memory_resource()) { this->frame_control_strategy = &frame_control; } ~Scene2D_Context() override { @@ -53,7 +66,7 @@ protected: if (!renderable.dependency_node.parent) { dependency_root.append_child(renderable.dependency_node); } - color_caches_.try_emplace(&renderable, std::make_unique()); + color_caches_.try_emplace(&renderable, make_cache_pointer(this->memory_resource())); } void on_renderable_detached(Renderable_Base& renderable) override { promote_children(renderable.layer_node); @@ -87,22 +100,45 @@ protected: return scene_observer_.now_ns(); } private: + using Cache_Pointer = Memory_Resource_Unique_Ptr; + template + static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) { + if constexpr (std::constructible_from) { + return Frame_Control(memory_resource, std::forward(args)...); + } else { + return Frame_Control(std::forward(args)...); + } + } + static Cache make_cache(std::pmr::memory_resource& memory_resource) { + if constexpr (std::constructible_from) { + return Cache(memory_resource); + } else { + return Cache(); + } + } + static Cache_Pointer make_cache_pointer(std::pmr::memory_resource& memory_resource) { + if constexpr (std::constructible_from) { + return make_memory_resource_unique(memory_resource, memory_resource); + } else { + return make_memory_resource_unique(memory_resource); + } + } template - static void promote_children(Node& node) { + void promote_children(Node& node) { if (!node.parent) { return; } Node* parent = node.parent; const auto iterator = std::find(parent->children.begin(), parent->children.end(), &node); std::size_t index = static_cast(iterator - parent->children.begin()); - const auto children = node.children; + std::pmr::vector children(node.children.begin(), node.children.end(), &this->memory_resource()); node.detach(); for (Node* child : children) { parent->insert_child(index++, *child); } } - template - static void append_tree_order(Node& node, const std::unordered_map& attached, std::unordered_set& visited, Renderable_List& order) { + template + static void append_tree_order(Node& node, const Attached& attached, Visited& visited, Renderable_List& order) { if (node.owner) { auto iterator = attached.find(node.owner); if (iterator != attached.end() && visited.insert(node.owner).second) { @@ -114,14 +150,15 @@ private: } } template - static Renderable_List tree_order(Node& root, const Renderable_List& renderables) { - std::unordered_map attached; + Renderable_List tree_order(Node& root, const Renderable_List& renderables) { + std::pmr::monotonic_buffer_resource scratch_resource(&this->memory_resource()); + std::pmr::unordered_map attached(&scratch_resource); attached.reserve(renderables.size()); for (const Renderable& renderable : renderables) { attached.emplace(renderable.get(), renderable); } - std::unordered_set visited; - Renderable_List order; + std::pmr::unordered_set visited(&scratch_resource); + Renderable_List order(&this->memory_resource()); order.reserve(renderables.size()); append_tree_order(root, attached, visited, order); for (const Renderable& renderable : renderables) { @@ -132,5 +169,5 @@ private: return order; } Scene_Observer scene_observer_; - std::unordered_map> color_caches_; + std::pmr::unordered_map color_caches_; }; diff --git a/Kernel/src/renderive/scene/Scene3D_Context.hpp b/Kernel/src/renderive/scene/Scene3D_Context.hpp index 1ae2201..a878a09 100644 --- a/Kernel/src/renderive/scene/Scene3D_Context.hpp +++ b/Kernel/src/renderive/scene/Scene3D_Context.hpp @@ -1,4 +1,6 @@ #pragma once +#include +#include #include #include "renderive/base/observer/Observer.hpp" #include "renderive/frame_control/Frame_Control.hpp" @@ -8,6 +10,8 @@ struct Scene3D_Frame_Data {}; struct Scene3D_State { std::uint64_t revision{}; }; +template +concept Scene3D_Frame_Control_Constructible = std::constructible_from || std::constructible_from; template , class State = Scene3D_State, class State_Observer = Observer_State<>, class Scene_Observer = Observer_State<>> requires Frame_Control_Strategy_For && State_Value class Scene3D_Context : public Triple_State_Strategy { @@ -18,14 +22,22 @@ public: using Painter_Lease = typename Frame_Control::Painter_Lease; using Render_Lease = typename Frame_Control::Render_Lease; template - requires std::constructible_from - explicit Scene3D_Context(Args&&... args) : Scene_State_Strategy(), frame_control(std::forward(args)...) { + requires Scene3D_Frame_Control_Constructible + explicit Scene3D_Context(Args&&... args) : Scene3D_Context(*std::pmr::get_default_resource(), std::forward(args)...) {} + template + requires Scene3D_Frame_Control_Constructible + explicit Scene3D_Context(std::pmr::memory_resource& memory_resource, Args&&... args) + : Scene_State_Strategy(State{}, memory_resource), frame_control(make_frame_control(this->memory_resource(), std::forward(args)...)) { this->frame_control_strategy = &frame_control; } template - requires std::constructible_from + requires Scene3D_Frame_Control_Constructible Scene3D_Context(With_Observer state_observer, With_Observer scene_observer, Args&&... args) - : Scene_State_Strategy(std::move(state_observer)), frame_control(std::forward(args)...), scene_observer_(std::move(scene_observer.observer)) { + : Scene3D_Context(*std::pmr::get_default_resource(), std::move(state_observer), std::move(scene_observer), std::forward(args)...) {} + template + requires Scene3D_Frame_Control_Constructible + Scene3D_Context(std::pmr::memory_resource& memory_resource, With_Observer state_observer, With_Observer scene_observer, Args&&... args) + : Scene_State_Strategy(State{}, std::move(state_observer), memory_resource), frame_control(make_frame_control(this->memory_resource(), std::forward(args)...)), scene_observer_(std::move(scene_observer.observer)) { this->frame_control_strategy = &frame_control; } ~Scene3D_Context() override { @@ -44,5 +56,13 @@ protected: return scene_observer_.now_ns(); } private: + template + static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) { + if constexpr (std::constructible_from) { + return Frame_Control(memory_resource, std::forward(args)...); + } else { + return Frame_Control(std::forward(args)...); + } + } Scene_Observer scene_observer_; }; diff --git a/Kernel/src/renderive/scene/base/Scene_Base.cpp b/Kernel/src/renderive/scene/base/Scene_Base.cpp index 96bd052..3cf02ff 100644 --- a/Kernel/src/renderive/scene/base/Scene_Base.cpp +++ b/Kernel/src/renderive/scene/base/Scene_Base.cpp @@ -1,12 +1,16 @@ #include "Scene_Base.hpp" #include +#include #include +#include #include #include #include #include "renderive/renderable/color/Color_Cache.hpp" static_assert(TF_VERSION == 400100, "Renderive requires Taskflow 4.1.0"); -Scene_Base::Scene_Base() : render_renderables_(&renderable_states_[0]), cache_renderables_(&renderable_states_[1]) { +Scene_Base::Scene_Base() : Scene_Base(*std::pmr::get_default_resource()) {} +Scene_Base::Scene_Base(std::pmr::memory_resource& upstream_memory_resource) + : memory_domain_(std::allocate_shared(std::pmr::polymorphic_allocator(&upstream_memory_resource), upstream_memory_resource)), renderable_states_{Renderable_List(&memory_domain_->resource()), Renderable_List(&memory_domain_->resource())}, render_renderables_(&renderable_states_[0]), cache_renderables_(&renderable_states_[1]), task_(memory_domain_->resource()) { worker_ = std::thread([this] { render_loop(); }); @@ -37,7 +41,7 @@ void Scene_Base::render() { if (!frame_control_strategy) { throw std::logic_error("frame control strategy is not bound"); } - Render_Task task; + Render_Task task(memory_resource()); { std::lock_guard renderable_lock(renderable_mutex_); std::swap(render_renderables_, cache_renderables_); @@ -89,6 +93,12 @@ std::size_t Scene_Base::renderable_count() const { std::lock_guard lock(renderable_mutex_); return cache_renderables_->size(); } +std::pmr::memory_resource& Scene_Base::memory_resource() const noexcept { + return memory_domain_->resource(); +} +std::pmr::memory_resource& Scene_Base::upstream_memory_resource() const noexcept { + return memory_domain_->upstream(); +} void Scene_Base::on_renderable_attached(Renderable_Base&) {} void Scene_Base::on_renderable_detached(Renderable_Base&) {} void Scene_Base::prepare_render_task(Render_Task& task, const Renderable_List& renderables) { @@ -108,7 +118,7 @@ std::uint64_t Scene_Base::observer_now_ns() const noexcept { } void Scene_Base::render_loop() { for (;;) { - Render_Task task; + Render_Task task(memory_resource()); { std::unique_lock lock(task_mutex_); task_ready_.wait(lock, [this] { @@ -145,11 +155,12 @@ void Scene_Base::execute_taskflow(Render_Task& task) { tf::Task module_task; bool render{}; }; + std::pmr::monotonic_buffer_resource scratch_resource(&memory_resource()); tf::Executor executor; tf::Taskflow scene_graph; - std::unordered_map indices; + std::pmr::unordered_map indices(&scratch_resource); indices.reserve(task.render_order.size()); - std::vector render_flags(task.render_order.size()); + std::pmr::vector render_flags(task.render_order.size(), false, &scratch_resource); for (std::size_t index = 0; index < task.render_order.size(); ++index) { indices.emplace(task.render_order[index].get(), index); render_flags[index] = task.render_order[index]->requires_render(); @@ -161,7 +172,8 @@ void Scene_Base::execute_taskflow(Render_Task& task) { render_flags[index] = true; } } - std::vector modules(task.render_order.size()); + std::pmr::vector modules(&scratch_resource); + modules.resize(task.render_order.size()); for (std::size_t index = 0; index < task.render_order.size(); ++index) { Renderable_Base& renderable = *task.render_order[index]; Module& module = modules[index]; @@ -170,12 +182,12 @@ void Scene_Base::execute_taskflow(Render_Task& task) { Color_Cache* color_cache = prepare_renderable_cache(renderable, true); const Scene_Render_Context context{this, task.frame_control_state, task.scene_state_revision, task.render_sequence, &renderable, color_cache}; const auto& nodes = renderable.task_graph().nodes(); - std::vector internal_tasks; + std::pmr::vector internal_tasks(&scratch_resource); internal_tasks.reserve(nodes.size()); for (const auto& node : nodes) { internal_tasks.push_back(module.graph.emplace([function = node.function, context] { function(context); - }).name(node.name)); + }).name(std::string(node.name))); } for (std::size_t node_index = 0; node_index < nodes.size(); ++node_index) { for (std::size_t successor : nodes[node_index].successors) { @@ -188,7 +200,7 @@ void Scene_Base::execute_taskflow(Render_Task& task) { if (internal_tasks.empty()) { module.graph.emplace([] {}).precede(mark_rendered); } else { - std::vector has_successor(nodes.size()); + std::pmr::vector has_successor(nodes.size(), false, &scratch_resource); for (std::size_t node_index = 0; node_index < nodes.size(); ++node_index) { has_successor[node_index] = !nodes[node_index].successors.empty(); if (!has_successor[node_index]) { diff --git a/Kernel/src/renderive/scene/base/Scene_Base.hpp b/Kernel/src/renderive/scene/base/Scene_Base.hpp index afd9b63..b788054 100644 --- a/Kernel/src/renderive/scene/base/Scene_Base.hpp +++ b/Kernel/src/renderive/scene/base/Scene_Base.hpp @@ -1,11 +1,16 @@ #pragma once +#include #include +#include #include #include #include +#include #include #include #include +#include +#include "renderive/base/memory/Memory_Resource.hpp" #include "renderive/frame_control/base/Frame_Control_Strategy_Base.hpp" #include "renderive/renderable/base/Renderable_Base.hpp" #include "Scene_Render_Context.hpp" @@ -13,7 +18,7 @@ class Color_Cache; class Scene_Base { public: using Renderable = std::shared_ptr; - using Renderable_List = std::vector; + using Renderable_List = std::pmr::vector; enum class Observation_Event { render_submitted, render_started, @@ -28,6 +33,7 @@ public: std::size_t renderable_count{}; }; Scene_Base(); + explicit Scene_Base(std::pmr::memory_resource& upstream_memory_resource); Scene_Base(const Scene_Base&) = delete; Scene_Base& operator=(const Scene_Base&) = delete; Scene_Base(Scene_Base&&) = delete; @@ -38,9 +44,17 @@ public: void attach_renderable(Renderable renderable); void detach_renderable(const Renderable_Base& renderable); std::size_t renderable_count() const; + std::pmr::memory_resource& memory_resource() const noexcept; + std::pmr::memory_resource& upstream_memory_resource() const noexcept; + template + requires std::derived_from && std::constructible_from + std::shared_ptr make_renderable(Args&&... args) { + return std::allocate_shared(Scene_Memory_Allocator{memory_domain_}, *this, std::forward(args)...); + } Frame_Control_Strategy_Base* frame_control_strategy{}; protected: struct Render_Task { + explicit Render_Task(std::pmr::memory_resource& memory_resource) : render_order(&memory_resource), display_order(&memory_resource) {} Frame_Control_Strategy_Base::State frame_control_state; std::uint64_t scene_state_revision{}; std::uint64_t render_sequence{}; @@ -58,7 +72,8 @@ protected: private: void render_loop(); void execute_taskflow(Render_Task& task); - Renderable_List renderable_states_[2]; + std::shared_ptr memory_domain_; + std::array renderable_states_; Renderable_List* render_renderables_; Renderable_List* cache_renderables_; mutable std::mutex renderable_mutex_; @@ -73,5 +88,13 @@ private: bool rendering_{}; bool stop_{}; }; -class Scene_2D_Base : public Scene_Base {}; -class Scene_3D_Base : public Scene_Base {}; +class Scene_2D_Base : public Scene_Base { +public: + Scene_2D_Base() = default; + explicit Scene_2D_Base(std::pmr::memory_resource& memory_resource) : Scene_Base(memory_resource) {} +}; +class Scene_3D_Base : public Scene_Base { +public: + Scene_3D_Base() = default; + explicit Scene_3D_Base(std::pmr::memory_resource& memory_resource) : Scene_Base(memory_resource) {} +}; diff --git a/Kernel/src/main.cpp b/Kernel/tests/main.cpp similarity index 77% rename from Kernel/src/main.cpp rename to Kernel/tests/main.cpp index 02a844b..7bb10c5 100644 --- a/Kernel/src/main.cpp +++ b/Kernel/tests/main.cpp @@ -6,10 +6,10 @@ #ifdef RENDERIVE_WITH_GTEST #include #endif -#include "renderive/base/property/Property.hpp" -#include "renderive/renderable/Renderable.hpp" -#include "renderive/scene/Scene.hpp" -#include "renderive/state/State_Strategy.hpp" +#include "../src/renderive/base/property/Property.hpp" +#include "../src/renderive/renderable/Renderable.hpp" +#include "../src/renderive/scene/Scene.hpp" +#include "../src/renderive/state/State_Strategy.hpp" struct Radio_State { struct Even_Validator { void operator()(const int& value) const { @@ -44,14 +44,14 @@ using Radio_Strategy = Double_State_Strategy; using Radio = Attach_State_Builder; int run_main_flow() { auto radio = Radio::Builder{} - .set<&Radio_State::port>(9000) - .set<&Radio_State::buffer_count>(8) - .configure([](Radio_State& state) { - state.low_watermark = 16; - state.high_watermark = 64; - }) - .build(1, "primary"); - radio->set<&Radio_State::port>(9100); + .set < &Radio_State::port > (9000) + .set<&Radio_State::buffer_count>(8) + .configure([](Radio_State& state) { + state.low_watermark = 16; + state.high_watermark = 64; + }) + .build(1, "primary"); + radio->set < &Radio_State::port > (9100); radio->publish(); Scene2D_Context<> scene; auto renderable = std::make_shared(scene); diff --git a/Kernel/tests/renderive/renderable/color/Color_Cache_Test.cpp b/Kernel/tests/renderive/renderable/color/Color_Cache_Test.cpp index 41c8c23..8580d8b 100644 --- a/Kernel/tests/renderive/renderable/color/Color_Cache_Test.cpp +++ b/Kernel/tests/renderive/renderable/color/Color_Cache_Test.cpp @@ -9,5 +9,7 @@ TEST(color_cache_test, composites_source_values_in_order) { second.append(2); result.composite(first); result.composite(second); - EXPECT_EQ(result.values, (std::vector{1, 2})); + EXPECT_EQ(result.values.size(), 2); + EXPECT_EQ(result.values.at(0), 1); + EXPECT_EQ(result.values.at(1), 2); } diff --git a/Kernel/tests/renderive/scene/Scene2D_Render_Order_Test.cpp b/Kernel/tests/renderive/scene/Scene2D_Render_Order_Test.cpp index 77cf0c1..332797c 100644 --- a/Kernel/tests/renderive/scene/Scene2D_Render_Order_Test.cpp +++ b/Kernel/tests/renderive/scene/Scene2D_Render_Order_Test.cpp @@ -29,5 +29,7 @@ TEST(scene2d_render_order_test, separates_dependency_order_from_display_order) { scene.render(); scene.wait_for_render(); EXPECT_EQ(state.render_order, (std::vector{1, 2})); - EXPECT_EQ(scene.final_color_cache.values, (std::vector{2, 1})); + EXPECT_EQ(scene.final_color_cache.values.size(), 2); + EXPECT_EQ(scene.final_color_cache.values.at(0), 2); + EXPECT_EQ(scene.final_color_cache.values.at(1), 1); } diff --git a/Kernel/tests/renderive/scene/Scene_Memory_Resource_Test.cpp b/Kernel/tests/renderive/scene/Scene_Memory_Resource_Test.cpp new file mode 100644 index 0000000..f4df722 --- /dev/null +++ b/Kernel/tests/renderive/scene/Scene_Memory_Resource_Test.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#include "renderive/base/property/Property.hpp" +#include "renderive/real_time_data/Real_Time_Data.hpp" +#include "renderive/renderable/Renderable.hpp" +#include "renderive/scene/Scene.hpp" +class Scene_Memory_Resource_Test_Resource : public std::pmr::memory_resource { +public: + std::size_t allocation_count() const noexcept { + return allocation_count_.load(std::memory_order_acquire); + } + std::size_t deallocation_count() const noexcept { + return deallocation_count_.load(std::memory_order_acquire); + } +private: + void* do_allocate(std::size_t bytes, std::size_t alignment) override { + allocation_count_.fetch_add(1, std::memory_order_release); + return std::pmr::new_delete_resource()->allocate(bytes, alignment); + } + void do_deallocate(void* pointer, std::size_t bytes, std::size_t alignment) override { + deallocation_count_.fetch_add(1, std::memory_order_release); + std::pmr::new_delete_resource()->deallocate(pointer, bytes, alignment); + } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { + return this == &other; + } + std::atomic allocation_count_{}; + std::atomic deallocation_count_{}; +}; +struct Scene_Memory_Resource_Test_Renderable : Renderable_Base { + explicit Scene_Memory_Resource_Test_Renderable(Scene_Base& scene) : Renderable_Base(scene, {.cache_enabled = false}) {} + void build_task_graph(Renderable_Task_Graph& graph) override { + auto first = graph.emplace([](const Scene_Render_Context&) {}, "prepare_memory_resource_test"); + auto second = graph.emplace([](const Scene_Render_Context&) {}, "render_memory_resource_test"); + graph.precede(first, second); + } +}; +struct Scene_Memory_Resource_Test_Product { + explicit Scene_Memory_Resource_Test_Product(int value) : value(value) {} + int value; +}; +struct Scene_Memory_Resource_Test_Properties { + int value{}; +}; +struct Scene_Memory_Resource_Test_Property_Product { + Scene_Memory_Resource_Test_Property_Product(const Scene_Memory_Resource_Test_Properties& properties, int offset) : value(properties.value + offset) {} + int value; +}; +TEST(scene_memory_resource_test, propagates_thread_safe_scene_memory_domain) { + Scene_Memory_Resource_Test_Resource upstream; + Scene2D_Context<> scene(upstream); + EXPECT_EQ(&scene.upstream_memory_resource(), &upstream); + EXPECT_NE(&scene.memory_resource(), &upstream); + auto factory_renderable = scene.make_renderable(); + auto independent_renderable = std::make_shared(scene); + EXPECT_EQ(&factory_renderable->memory_resource(), &scene.memory_resource()); + EXPECT_EQ(&independent_renderable->memory_resource(), &scene.memory_resource()); + scene.attach_renderable(factory_renderable); + scene.attach_renderable(independent_renderable); + scene.render(); + scene.wait_for_render(); + EXPECT_GT(upstream.allocation_count(), 0); +} +TEST(scene_memory_resource_test, keeps_factory_memory_domain_alive_until_renderable_release) { + Scene_Memory_Resource_Test_Resource upstream; + std::shared_ptr renderable; + { + Scene2D_Context<> scene(upstream); + renderable = scene.make_renderable(); + renderable->task_graph(); + } + renderable.reset(); + EXPECT_GT(upstream.deallocation_count(), 0); +} +TEST(scene_memory_resource_test, supports_independent_resource_aware_components) { + Scene_Memory_Resource_Test_Resource memory_resource; + Flow_Refresh_Strategy flow(memory_resource); + { + auto painter = flow.acquire_painter(); + EXPECT_TRUE(painter); + } + auto renderer = flow.acquire_renderer(); + EXPECT_TRUE(renderer); + History_Real_Time_Data> history(memory_resource); + history.update(1); + history.update(2); + EXPECT_EQ(history.size(), 2); + Property_Builder builder({3}); + auto product = builder.build_with_resource(memory_resource, 4); + auto unique_product = builder.build_unique_with_resource(memory_resource, 5); + EXPECT_EQ(product->value, 7); + EXPECT_EQ(unique_product->value, 8); + EXPECT_GT(memory_resource.allocation_count(), 0); +} diff --git a/main.cmake b/main.cmake index 5fe5840..be59b92 100644 --- a/main.cmake +++ b/main.cmake @@ -42,6 +42,7 @@ add_executable(Renderive_Kernel target_include_directories(Renderive_Kernel PUBLIC ${root_dir}/Kernel/src ) +target_compile_definitions(Renderive_Kernel PUBLIC RENDERIVE_WITH_GTEST) target_link_libraries(Renderive_Kernel PUBLIC ${renderive_private_libraries}) append_glob_source(srcs ${root_dir}/Core) add_library(Renderive_Core STATIC ${srcs})