添加外部内存数据源
This commit is contained in:
+43
-42
@@ -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 "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/src>"
|
||||
PRIVATE "${RENDERIVE_TASKFLOW_INCLUDE_DIR}"
|
||||
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/src>"
|
||||
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 ()
|
||||
|
||||
@@ -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<Spectrum_Renderable>(configuration);
|
||||
scene.attach_renderable(renderable);
|
||||
```
|
||||
|
||||
工厂只构造对象,不自动挂载,`attach_renderable()` 的原有语义不变。工厂分配器持有 Scene 内存域的共享所有权,因此 Renderable 析构时内存域仍然有效。
|
||||
|
||||
独立构造模式继续保留:
|
||||
|
||||
```cpp
|
||||
auto renderable = std::make_shared<Spectrum_Renderable>(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<int, std::pmr::vector<int>> 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 的核心对象,不承诺接管第三方库和用户类型的全部动态分配。
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <utility>
|
||||
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 Value>
|
||||
class Scene_Memory_Allocator {
|
||||
public:
|
||||
using value_type = Value;
|
||||
template <class Other>
|
||||
struct rebind {
|
||||
using other = Scene_Memory_Allocator<Other>;
|
||||
};
|
||||
Scene_Memory_Allocator() = delete;
|
||||
explicit Scene_Memory_Allocator(std::shared_ptr<Scene_Memory_Domain> domain) noexcept : domain_(std::move(domain)) {}
|
||||
template <class Other>
|
||||
Scene_Memory_Allocator(const Scene_Memory_Allocator<Other>& other) noexcept : domain_(other.domain_) {}
|
||||
Value* allocate(std::size_t count) {
|
||||
return static_cast<Value*>(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 <class Other>
|
||||
bool operator==(const Scene_Memory_Allocator<Other>& other) const noexcept {
|
||||
return domain_ == other.domain_;
|
||||
}
|
||||
private:
|
||||
template <class>
|
||||
friend class Scene_Memory_Allocator;
|
||||
std::shared_ptr<Scene_Memory_Domain> domain_;
|
||||
};
|
||||
template <class Value>
|
||||
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 <class Value>
|
||||
using Memory_Resource_Unique_Ptr = std::unique_ptr<Value, Memory_Resource_Deleter<Value>>;
|
||||
template <class Value, class... Args>
|
||||
Memory_Resource_Unique_Ptr<Value> 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<Value*>(storage), std::forward<Args>(args)...);
|
||||
return Memory_Resource_Unique_Ptr<Value>(value, Memory_Resource_Deleter<Value>{&resource});
|
||||
} catch (...) {
|
||||
resource.deallocate(storage, sizeof(Value), alignof(Value));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <memory_resource>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
template <class Owner, class Tag = void>
|
||||
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<Multiway_Node*> children;
|
||||
std::pmr::vector<Multiway_Node*> children;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "Concepts.hpp"
|
||||
#include "Validators.hpp"
|
||||
#include "renderive/base/memory/Memory_Resource.hpp"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
@@ -35,10 +36,22 @@ public:
|
||||
}
|
||||
template <class... Args>
|
||||
requires Property_Constructible<Product, Properties, Args&&...>
|
||||
std::shared_ptr<Product> build_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const {
|
||||
validator(properties);
|
||||
return std::allocate_shared<Product>(std::pmr::polymorphic_allocator<Product>(&memory_resource), properties, std::forward<Args>(args)...);
|
||||
}
|
||||
template <class... Args>
|
||||
requires Property_Constructible<Product, Properties, Args&&...>
|
||||
std::unique_ptr<Product> build_unique(Args&&... args) const {
|
||||
validator(properties);
|
||||
return std::make_unique<Product>(properties, std::forward<Args>(args)...);
|
||||
}
|
||||
template <class... Args>
|
||||
requires Property_Constructible<Product, Properties, Args&&...>
|
||||
Memory_Resource_Unique_Ptr<Product> build_unique_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const {
|
||||
validator(properties);
|
||||
return make_memory_resource_unique<Product>(memory_resource, properties, std::forward<Args>(args)...);
|
||||
}
|
||||
private:
|
||||
Properties properties;
|
||||
[[no_unique_address]] Validator validator;
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <deque>
|
||||
#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> frame_;
|
||||
Memory_Resource_Unique_Ptr<Frame> frame_;
|
||||
};
|
||||
class Render_Lease {
|
||||
public:
|
||||
@@ -86,10 +89,11 @@ public:
|
||||
const Frame* get() const;
|
||||
private:
|
||||
Flow_Refresh_Strategy* strategy_{};
|
||||
std::unique_ptr<Frame> frame_;
|
||||
Memory_Resource_Unique_Ptr<Frame> frame_;
|
||||
std::unique_lock<Mutex> 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<std::unique_ptr<Frame>> frames_;
|
||||
std::pmr::deque<Memory_Resource_Unique_Ptr<Frame>> frames_;
|
||||
std::uint64_t next_sequence_{};
|
||||
std::uint64_t real_time_data_update_sequence_{};
|
||||
Real_Time_Data_Update_State last_real_time_data_update_;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::Painter_Lease(Flow_Refresh_Strategy& strategy)
|
||||
: strategy_(&strategy), frame_(std::make_unique<Frame>()) {
|
||||
: strategy_(&strategy), frame_(make_memory_resource_unique<Frame>(*strategy.memory_resource_)) {
|
||||
std::lock_guard<Mutex> 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<Scene_Frame, Mutex, Observer>::Render_Lease::get() co
|
||||
return frame_.get();
|
||||
}
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Flow_Refresh_Strategy(Observer observer) : Frame_Control_Strategy_Base(), observer_(std::move(observer)) {}
|
||||
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Flow_Refresh_Strategy(Observer observer)
|
||||
: Flow_Refresh_Strategy(*std::pmr::get_default_resource(), std::move(observer)) {}
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::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 <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::acquire_painter() -> Painter_Lease {
|
||||
return Painter_Lease(*this);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <memory_resource>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -19,6 +20,7 @@ public:
|
||||
static_assert(Real_Time_Data_History_Container<Container, Value>);
|
||||
static_assert(Timed_Struct_Observer<Observer, Real_Time_Data_Observation>);
|
||||
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<std::uint64_t> update_times_;
|
||||
std::pmr::vector<std::uint64_t> update_times_;
|
||||
std::uint64_t revision_{};
|
||||
std::uint64_t total_update_count_{};
|
||||
std::uint64_t last_update_time_ns_{};
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
#pragma once
|
||||
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
|
||||
History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::History_Real_Time_Data(Observer observer) : observer_(std::move(observer)) {}
|
||||
History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::History_Real_Time_Data(Observer observer)
|
||||
: History_Real_Time_Data(*std::pmr::get_default_resource(), std::move(observer)) {}
|
||||
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
|
||||
History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::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 <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
|
||||
auto History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::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<Allocator, std::pmr::memory_resource*> && std::constructible_from<Container, Allocator>) {
|
||||
return Container(Allocator(&memory_resource));
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
template <class Value_Type, class Container, Mutex_Type Mutex, class Observer>
|
||||
void History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::update(Value value) {
|
||||
Real_Time_Data_Observation observation;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "Renderable_Task_Graph.hpp"
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
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::Node>& Renderable_Task_Graph::nodes() const noexcept {
|
||||
const std::pmr::vector<Renderable_Task_Graph::Node>& Renderable_Task_Graph::nodes() const noexcept {
|
||||
return nodes_;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory_resource>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<std::size_t> successors;
|
||||
std::pmr::string name;
|
||||
std::pmr::vector<std::size_t> 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<Node>& nodes() const noexcept;
|
||||
const std::pmr::vector<Node>& nodes() const noexcept;
|
||||
private:
|
||||
std::vector<Node> nodes_;
|
||||
std::pmr::memory_resource* const memory_resource_;
|
||||
std::pmr::vector<Node> nodes_;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <memory_resource>
|
||||
#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<std::uint64_t> cache_revision_{1};
|
||||
std::atomic<std::uint64_t> rendered_cache_revision_{};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <memory_resource>
|
||||
#include <vector>
|
||||
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<std::uint64_t> values;
|
||||
std::pmr::vector<std::uint64_t> values;
|
||||
std::uint64_t revision{};
|
||||
};
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#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 <class Frame_Control, class... Args>
|
||||
concept Scene2D_Frame_Control_Constructible = std::constructible_from<Frame_Control, Args...> || std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args...>;
|
||||
template <class Strategy = Low_Latency_Strategy<Scene2D_Frame_Data>, 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<Strategy, Scene2D_Frame_Data> && State_Value<State>
|
||||
class Scene2D_Context : public Triple_State_Strategy<Scene_2D_Base, State, Atomic_Spin_Mutex, State_Observer> {
|
||||
@@ -27,14 +32,22 @@ public:
|
||||
using Painter_Lease = typename Frame_Control::Painter_Lease;
|
||||
using Render_Lease = typename Frame_Control::Render_Lease;
|
||||
template <class... Args>
|
||||
requires std::constructible_from<Frame_Control, Args&&...>
|
||||
explicit Scene2D_Context(Args&&... args) : Scene_State_Strategy(), frame_control(std::forward<Args>(args)...), display_root(nullptr), dependency_root(nullptr) {
|
||||
requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
explicit Scene2D_Context(Args&&... args) : Scene2D_Context(*std::pmr::get_default_resource(), std::forward<Args>(args)...) {}
|
||||
template <class... Args>
|
||||
requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
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>(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 <class... Args>
|
||||
requires std::constructible_from<Frame_Control, Args&&...>
|
||||
requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
Scene2D_Context(With_Observer<State_Observer> state_observer, With_Observer<Scene_Observer> scene_observer, Args&&... args)
|
||||
: Scene_State_Strategy(std::move(state_observer)), frame_control(std::forward<Args>(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>(args)...) {}
|
||||
template <class... Args>
|
||||
requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
Scene2D_Context(std::pmr::memory_resource& memory_resource, With_Observer<State_Observer> state_observer, With_Observer<Scene_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>(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<Cache>());
|
||||
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<Cache>;
|
||||
template <class... Args>
|
||||
static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) {
|
||||
if constexpr (std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args&&...>) {
|
||||
return Frame_Control(memory_resource, std::forward<Args>(args)...);
|
||||
} else {
|
||||
return Frame_Control(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
static Cache make_cache(std::pmr::memory_resource& memory_resource) {
|
||||
if constexpr (std::constructible_from<Cache, std::pmr::memory_resource&>) {
|
||||
return Cache(memory_resource);
|
||||
} else {
|
||||
return Cache();
|
||||
}
|
||||
}
|
||||
static Cache_Pointer make_cache_pointer(std::pmr::memory_resource& memory_resource) {
|
||||
if constexpr (std::constructible_from<Cache, std::pmr::memory_resource&>) {
|
||||
return make_memory_resource_unique<Cache>(memory_resource, memory_resource);
|
||||
} else {
|
||||
return make_memory_resource_unique<Cache>(memory_resource);
|
||||
}
|
||||
}
|
||||
template <class Node>
|
||||
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<std::size_t>(iterator - parent->children.begin());
|
||||
const auto children = node.children;
|
||||
std::pmr::vector<Node*> children(node.children.begin(), node.children.end(), &this->memory_resource());
|
||||
node.detach();
|
||||
for (Node* child : children) {
|
||||
parent->insert_child(index++, *child);
|
||||
}
|
||||
}
|
||||
template <class Node>
|
||||
static void append_tree_order(Node& node, const std::unordered_map<Renderable_Base*, Renderable>& attached, std::unordered_set<Renderable_Base*>& visited, Renderable_List& order) {
|
||||
template <class Node, class Attached, class Visited>
|
||||
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 <class Node>
|
||||
static Renderable_List tree_order(Node& root, const Renderable_List& renderables) {
|
||||
std::unordered_map<Renderable_Base*, Renderable> 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<Renderable_Base*, Renderable> attached(&scratch_resource);
|
||||
attached.reserve(renderables.size());
|
||||
for (const Renderable& renderable : renderables) {
|
||||
attached.emplace(renderable.get(), renderable);
|
||||
}
|
||||
std::unordered_set<Renderable_Base*> visited;
|
||||
Renderable_List order;
|
||||
std::pmr::unordered_set<Renderable_Base*> 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<Renderable_Base*, std::unique_ptr<Cache>> color_caches_;
|
||||
std::pmr::unordered_map<Renderable_Base*, Cache_Pointer> color_caches_;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#include <concepts>
|
||||
#include <memory_resource>
|
||||
#include <utility>
|
||||
#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 <class Frame_Control, class... Args>
|
||||
concept Scene3D_Frame_Control_Constructible = std::constructible_from<Frame_Control, Args...> || std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args...>;
|
||||
template <class Strategy = Low_Latency_Strategy<Scene3D_Frame_Data>, class State = Scene3D_State, class State_Observer = Observer_State<>, class Scene_Observer = Observer_State<>>
|
||||
requires Frame_Control_Strategy_For<Strategy, Scene3D_Frame_Data> && State_Value<State>
|
||||
class Scene3D_Context : public Triple_State_Strategy<Scene_3D_Base, State, Atomic_Spin_Mutex, State_Observer> {
|
||||
@@ -18,14 +22,22 @@ public:
|
||||
using Painter_Lease = typename Frame_Control::Painter_Lease;
|
||||
using Render_Lease = typename Frame_Control::Render_Lease;
|
||||
template <class... Args>
|
||||
requires std::constructible_from<Frame_Control, Args&&...>
|
||||
explicit Scene3D_Context(Args&&... args) : Scene_State_Strategy(), frame_control(std::forward<Args>(args)...) {
|
||||
requires Scene3D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
explicit Scene3D_Context(Args&&... args) : Scene3D_Context(*std::pmr::get_default_resource(), std::forward<Args>(args)...) {}
|
||||
template <class... Args>
|
||||
requires Scene3D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
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>(args)...)) {
|
||||
this->frame_control_strategy = &frame_control;
|
||||
}
|
||||
template <class... Args>
|
||||
requires std::constructible_from<Frame_Control, Args&&...>
|
||||
requires Scene3D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
Scene3D_Context(With_Observer<State_Observer> state_observer, With_Observer<Scene_Observer> scene_observer, Args&&... args)
|
||||
: Scene_State_Strategy(std::move(state_observer)), frame_control(std::forward<Args>(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>(args)...) {}
|
||||
template <class... Args>
|
||||
requires Scene3D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
Scene3D_Context(std::pmr::memory_resource& memory_resource, With_Observer<State_Observer> state_observer, With_Observer<Scene_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>(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 <class... Args>
|
||||
static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) {
|
||||
if constexpr (std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args&&...>) {
|
||||
return Frame_Control(memory_resource, std::forward<Args>(args)...);
|
||||
} else {
|
||||
return Frame_Control(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
Scene_Observer scene_observer_;
|
||||
};
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#include "Scene_Base.hpp"
|
||||
#include <algorithm>
|
||||
#include <memory_resource>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <taskflow/taskflow.hpp>
|
||||
#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<Scene_Memory_Domain>(std::pmr::polymorphic_allocator<Scene_Memory_Domain>(&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<std::mutex> 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<std::mutex> 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<std::mutex> 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<Renderable_Base*, std::size_t> indices;
|
||||
std::pmr::unordered_map<Renderable_Base*, std::size_t> indices(&scratch_resource);
|
||||
indices.reserve(task.render_order.size());
|
||||
std::vector<bool> render_flags(task.render_order.size());
|
||||
std::pmr::vector<bool> 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<Module> modules(task.render_order.size());
|
||||
std::pmr::vector<Module> 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<tf::Task> internal_tasks;
|
||||
std::pmr::vector<tf::Task> 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<bool> has_successor(nodes.size());
|
||||
std::pmr::vector<bool> 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]) {
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#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<Renderable_Base>;
|
||||
using Renderable_List = std::vector<Renderable>;
|
||||
using Renderable_List = std::pmr::vector<Renderable>;
|
||||
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 <class Renderable_Type, class... Args>
|
||||
requires std::derived_from<Renderable_Type, Renderable_Base> && std::constructible_from<Renderable_Type, Scene_Base&, Args&&...>
|
||||
std::shared_ptr<Renderable_Type> make_renderable(Args&&... args) {
|
||||
return std::allocate_shared<Renderable_Type>(Scene_Memory_Allocator<Renderable_Type>{memory_domain_}, *this, std::forward<Args>(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<Scene_Memory_Domain> memory_domain_;
|
||||
std::array<Renderable_List, 2> 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) {}
|
||||
};
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
#ifdef RENDERIVE_WITH_GTEST
|
||||
#include <gtest/gtest.h>
|
||||
#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<Radio_Base, Radio_State>;
|
||||
using Radio = Attach_State_Builder<Radio_Strategy, Radio_State>;
|
||||
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<Demo_Renderable>(scene);
|
||||
@@ -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<std::uint64_t>{1, 2}));
|
||||
EXPECT_EQ(result.values.size(), 2);
|
||||
EXPECT_EQ(result.values.at(0), 1);
|
||||
EXPECT_EQ(result.values.at(1), 2);
|
||||
}
|
||||
|
||||
@@ -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<std::uint64_t>{1, 2}));
|
||||
EXPECT_EQ(scene.final_color_cache.values, (std::vector<std::uint64_t>{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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <vector>
|
||||
#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<std::size_t> allocation_count_{};
|
||||
std::atomic<std::size_t> 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<Scene_Memory_Resource_Test_Renderable>();
|
||||
auto independent_renderable = std::make_shared<Scene_Memory_Resource_Test_Renderable>(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<Scene_Memory_Resource_Test_Renderable> renderable;
|
||||
{
|
||||
Scene2D_Context<> scene(upstream);
|
||||
renderable = scene.make_renderable<Scene_Memory_Resource_Test_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<Scene2D_Frame_Data> flow(memory_resource);
|
||||
{
|
||||
auto painter = flow.acquire_painter();
|
||||
EXPECT_TRUE(painter);
|
||||
}
|
||||
auto renderer = flow.acquire_renderer();
|
||||
EXPECT_TRUE(renderer);
|
||||
History_Real_Time_Data<int, std::pmr::vector<int>> history(memory_resource);
|
||||
history.update(1);
|
||||
history.update(2);
|
||||
EXPECT_EQ(history.size(), 2);
|
||||
Property_Builder<Scene_Memory_Resource_Test_Property_Product, Scene_Memory_Resource_Test_Properties> 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);
|
||||
}
|
||||
@@ -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})
|
||||
|
||||
Reference in New Issue
Block a user