ai自主改之前
This commit is contained in:
@@ -244,8 +244,8 @@ private:
|
||||
data().buffer_storage.advance();
|
||||
data().dependency_graph_storage.advance();
|
||||
after_advance();
|
||||
// after_advance 只修改已提交的 current;由状态缓冲统一刷新下一次编辑基线,具体机制不得自行同步两份状态。
|
||||
*data().state.pending = *data().state.current;
|
||||
// Commit_Double_Buffer::advance() 已为 pending 建立 current 基线;after_advance
|
||||
// 只向 pending 写本轮派生状态,禁止回写正在供外部查询的 current。
|
||||
}
|
||||
public:
|
||||
template <detail::Buffer_Tag_In<Buffers> Tag>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <taskflow/observer/interface.hpp>
|
||||
namespace aethera {
|
||||
namespace {
|
||||
@@ -45,11 +46,9 @@ private:
|
||||
std::atomic_uint64_t worker_busy_time_ns{};
|
||||
std::atomic_uint64_t first_task_time_ns{};
|
||||
std::atomic_uint64_t last_task_time_ns{};
|
||||
mutable std::mutex longest_mutex;
|
||||
std::uint64_t longest_task_time_ns{};
|
||||
std::size_t longest_task_hash{};
|
||||
std::string longest_task_name;
|
||||
tf::TaskType longest_task_type{tf::TaskType::UNDEFINED};
|
||||
std::atomic_uint64_t longest_task_time_ns{};
|
||||
std::atomic_size_t longest_task_hash{};
|
||||
std::atomic<tf::TaskType> longest_task_type{tf::TaskType::UNDEFINED};
|
||||
static std::uint64_t clock_ns(Clock::time_point value) noexcept {
|
||||
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(value.time_since_epoch()).count());
|
||||
}
|
||||
@@ -79,7 +78,6 @@ public:
|
||||
}
|
||||
void on_entry(tf::WorkerView worker, tf::TaskView task) override {
|
||||
auto now = Clock::now();
|
||||
detail::observe_stage_entry(task.hash_value(), clock_ns(now));
|
||||
auto& worker_starts = starts[worker.id()];
|
||||
if (worker_starts.empty()) {
|
||||
worker_busy_starts[worker.id()] = now;
|
||||
@@ -103,7 +101,6 @@ public:
|
||||
}
|
||||
void on_exit(tf::WorkerView worker, tf::TaskView task) override {
|
||||
auto now = Clock::now();
|
||||
detail::observe_stage_exit(task.hash_value(), clock_ns(now));
|
||||
auto& worker_starts = starts[worker.id()];
|
||||
auto start = worker_starts.back();
|
||||
worker_starts.pop_back();
|
||||
@@ -123,14 +120,11 @@ public:
|
||||
update_min(type.min_time_ns, elapsed);
|
||||
update_max(type.max_time_ns, elapsed);
|
||||
}
|
||||
{
|
||||
std::lock_guard guard(longest_mutex);
|
||||
if (longest_task_time_ns < elapsed) {
|
||||
longest_task_time_ns = elapsed;
|
||||
longest_task_hash = task.hash_value();
|
||||
longest_task_name = task.name();
|
||||
longest_task_type = task.type();
|
||||
}
|
||||
auto longest = longest_task_time_ns.load(std::memory_order_relaxed);
|
||||
if (longest < elapsed && longest_task_time_ns.compare_exchange_strong(
|
||||
longest, elapsed, std::memory_order_relaxed)) {
|
||||
longest_task_hash.store(task.hash_value(), std::memory_order_relaxed);
|
||||
longest_task_type.store(task.type(), std::memory_order_relaxed);
|
||||
}
|
||||
active_tasks.fetch_sub(1, std::memory_order_relaxed);
|
||||
if (worker_starts.empty()) {
|
||||
@@ -187,11 +181,10 @@ public:
|
||||
target.max_task_time_ns = source.max_task_time_ns.load(std::memory_order_relaxed);
|
||||
target.utilization = state.observed_wall_time_ns ? static_cast<double>(target.busy_time_ns) * 100.0 / static_cast<double>(state.observed_wall_time_ns) : 0.0;
|
||||
}
|
||||
std::lock_guard guard(longest_mutex);
|
||||
state.longest_task_time_ns = longest_task_time_ns;
|
||||
state.longest_task_hash = longest_task_hash;
|
||||
state.longest_task_name = longest_task_name;
|
||||
state.longest_task_type = longest_task_type;
|
||||
state.longest_task_time_ns = longest_task_time_ns.load(std::memory_order_relaxed);
|
||||
state.longest_task_hash = longest_task_hash.load(std::memory_order_relaxed);
|
||||
state.longest_task_name.clear();
|
||||
state.longest_task_type = longest_task_type.load(std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
class Task_Resource : Pinned {
|
||||
@@ -206,6 +199,7 @@ private:
|
||||
Task_Runtime_State state;
|
||||
double_buffer::detail::State_Callback_Storage<Task_Runtime_State> state_callbacks;
|
||||
std::recursive_mutex state_mutex;
|
||||
std::atomic_bool state_callback_enabled{};
|
||||
void create_executor(std::size_t workers, std::shared_ptr<tf::WorkerInterface> worker_interface) {
|
||||
executor = std::make_unique<tf::Executor>(workers, std::move(worker_interface));
|
||||
observer = executor->make_observer<Task_Observer>();
|
||||
@@ -218,6 +212,7 @@ private:
|
||||
observer = executor->make_observer<Task_Observer>();
|
||||
}
|
||||
void publish_state() {
|
||||
if (!state_callback_enabled.load(std::memory_order_acquire)) return;
|
||||
std::lock_guard guard(state_mutex);
|
||||
observer->write_state(state, executor->num_workers(), executor->num_topologies());
|
||||
state.active_taskflow_count = active_taskflows.load(std::memory_order_relaxed);
|
||||
@@ -248,7 +243,10 @@ public:
|
||||
while (peak < active && !peak_active_taskflows.compare_exchange_weak(peak, active, std::memory_order_relaxed)) {}
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
try {
|
||||
executor->run(taskflow).get();
|
||||
if (executor->this_worker())
|
||||
executor->corun(taskflow);
|
||||
else
|
||||
executor->run(taskflow).get();
|
||||
}
|
||||
catch (...) {
|
||||
active_taskflows.fetch_sub(1, std::memory_order_relaxed);
|
||||
@@ -262,22 +260,46 @@ public:
|
||||
publish_state();
|
||||
return elapsed;
|
||||
}
|
||||
void run(tf::Taskflow& taskflow, std::function<void()> completion) {
|
||||
if (!completion) throw std::invalid_argument("Taskflow completion is empty");
|
||||
ensure_executor();
|
||||
auto active = active_taskflows.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
auto peak = peak_active_taskflows.load(std::memory_order_relaxed);
|
||||
while (peak < active && !peak_active_taskflows.compare_exchange_weak(
|
||||
peak, active, std::memory_order_relaxed)) {}
|
||||
executor->run(taskflow, [this, completion = std::move(completion)]() mutable {
|
||||
active_taskflows.fetch_sub(1, std::memory_order_relaxed);
|
||||
completed_taskflows.fetch_add(1, std::memory_order_relaxed);
|
||||
publish_state();
|
||||
completion();
|
||||
});
|
||||
}
|
||||
void schedule(std::function<void()> task) {
|
||||
if (!task) throw std::invalid_argument("Taskflow scheduled task is empty");
|
||||
ensure_executor();
|
||||
executor->silent_async(std::move(task));
|
||||
}
|
||||
std::pmr::memory_resource* memory_resource() const noexcept {
|
||||
return memory;
|
||||
}
|
||||
void set_state_callback(std::function<void(const Task_Runtime_State&)> callback) {
|
||||
std::lock_guard guard(state_mutex);
|
||||
state_callbacks.template set<Task_Runtime_State_Tag>(std::move(callback));
|
||||
state_callback_enabled.store(true, std::memory_order_release);
|
||||
}
|
||||
void clear_state_callback() {
|
||||
std::lock_guard guard(state_mutex);
|
||||
state_callbacks.template clear<Task_Runtime_State_Tag>();
|
||||
state_callback_enabled.store(false, std::memory_order_release);
|
||||
}
|
||||
};
|
||||
}
|
||||
void initialize_runtime(std::size_t workers, std::shared_ptr<tf::WorkerInterface> worker_interface, Pmr pmr) {
|
||||
Task_Resource::instance().initialize(workers, std::move(worker_interface), pmr);
|
||||
}
|
||||
void schedule_task(std::function<void()> task) {
|
||||
Task_Resource::instance().schedule(std::move(task));
|
||||
}
|
||||
namespace detail {
|
||||
void set_runtime_state_callback_impl(std::function<void(const Task_Runtime_State&)> callback) {
|
||||
Task_Resource::instance().set_state_callback(std::move(callback));
|
||||
@@ -288,6 +310,9 @@ void clear_runtime_state_callback_impl() {
|
||||
std::uint64_t run_taskflow(tf::Taskflow& taskflow) {
|
||||
return Task_Resource::instance().run(taskflow);
|
||||
}
|
||||
void run_taskflow(tf::Taskflow& taskflow, std::function<void()> completion) {
|
||||
Task_Resource::instance().run(taskflow, std::move(completion));
|
||||
}
|
||||
std::pmr::memory_resource* task_memory_resource() noexcept {
|
||||
return Task_Resource::instance().memory_resource();
|
||||
}
|
||||
|
||||
@@ -95,6 +95,8 @@ struct Task_Runtime_State : State_Type<Task_Runtime_State_Tag> {
|
||||
void initialize_runtime(std::size_t workers = std::thread::hardware_concurrency(),
|
||||
std::shared_ptr<tf::WorkerInterface> worker_interface = nullptr,
|
||||
Pmr pmr = {});
|
||||
/* 把独立业务任务提交给全局 Taskflow worker;任务不得执行阻塞式设备等待。 */
|
||||
void schedule_task(std::function<void()> task);
|
||||
/* 为全局 Taskflow 运行时状态注册回调;Tag 目前只接受 Task_Runtime_State_Tag。 */
|
||||
template <std::same_as<Task_Runtime_State_Tag> Tag, std::invocable<const Task_Runtime_State&> Callback>
|
||||
void set_runtime_state_callback(Callback&& callback);
|
||||
|
||||
@@ -3,9 +3,8 @@ namespace aethera::detail {
|
||||
void set_runtime_state_callback_impl(std::function<void(const Task_Runtime_State&)> callback);
|
||||
void clear_runtime_state_callback_impl();
|
||||
std::uint64_t run_taskflow(tf::Taskflow& taskflow);
|
||||
void run_taskflow(tf::Taskflow& taskflow, std::function<void()> completion);
|
||||
std::pmr::memory_resource* task_memory_resource() noexcept;
|
||||
void observe_stage_entry(std::size_t task_hash, std::uint64_t now);
|
||||
void observe_stage_exit(std::size_t task_hash, std::uint64_t now);
|
||||
}
|
||||
namespace aethera {
|
||||
template <std::same_as<Task_Runtime_State_Tag> Tag, std::invocable<const Task_Runtime_State&> Callback>
|
||||
|
||||
@@ -1,81 +1,13 @@
|
||||
#include "renderable.hpp"
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
namespace aethera {
|
||||
std::optional<double> Renderable::event_routing_distance(const Event& event) const {
|
||||
const auto& data = static_cast<const Private&>(*d);
|
||||
return data.event_routing_distance_run ? data.event_routing_distance_run(this, event) : std::nullopt;
|
||||
}
|
||||
namespace {
|
||||
class Stage_Registry : Pinned {
|
||||
private:
|
||||
struct Binding {
|
||||
const tf::Taskflow* owner;
|
||||
Root* object;
|
||||
Renderable::Private* data;
|
||||
detail::Renderable_Stage stage;
|
||||
detail::Stage_Observer_Point point;
|
||||
};
|
||||
std::unordered_map<std::size_t, Binding> bindings;
|
||||
std::mutex mutex;
|
||||
public:
|
||||
void bind(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, detail::Renderable_Stage stage, detail::Stage_Observer_Point point) {
|
||||
std::lock_guard guard(mutex);
|
||||
bindings.insert_or_assign(task_hash, Binding{owner, object, data, stage, point});
|
||||
}
|
||||
void clear(const tf::Taskflow* owner) {
|
||||
std::lock_guard guard(mutex);
|
||||
std::erase_if(
|
||||
bindings,
|
||||
[owner](const auto& value) {
|
||||
return value.second.owner == owner;
|
||||
}
|
||||
);
|
||||
}
|
||||
void on_entry(std::size_t task_hash, std::uint64_t now) {
|
||||
std::lock_guard guard(mutex);
|
||||
auto current = bindings.find(task_hash);
|
||||
if (current == bindings.end() || current->second.point != detail::Stage_Observer_Point::End) return;
|
||||
auto& binding = current->second;
|
||||
auto& state = *binding.data->dispatch->state.get(binding.object);
|
||||
if (binding.stage == detail::Renderable_Stage::Prepare) {
|
||||
if (state.prepare_executed) state.prepare_execution_time_ns = now - state.prepare_execution_time_ns;
|
||||
}
|
||||
else if (state.paint_executed) {
|
||||
state.paint_execution_time_ns = now - state.paint_execution_time_ns;
|
||||
}
|
||||
}
|
||||
void on_exit(std::size_t task_hash, std::uint64_t now) {
|
||||
std::lock_guard guard(mutex);
|
||||
auto current = bindings.find(task_hash);
|
||||
if (current == bindings.end() || current->second.point != detail::Stage_Observer_Point::Begin) return;
|
||||
auto& binding = current->second;
|
||||
auto& state = *binding.data->dispatch->state.get(binding.object);
|
||||
if (binding.stage == detail::Renderable_Stage::Prepare) {
|
||||
state.prepare_execution_time_ns = state.prepare_executed ? now : 0;
|
||||
}
|
||||
else {
|
||||
state.paint_execution_time_ns = state.paint_executed ? now : 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
Stage_Registry& stage_registry() {
|
||||
static Stage_Registry value;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
namespace detail {
|
||||
void observe_stage_entry(std::size_t task_hash, std::uint64_t now) {
|
||||
stage_registry().on_entry(task_hash, now);
|
||||
}
|
||||
void observe_stage_exit(std::size_t task_hash, std::uint64_t now) {
|
||||
stage_registry().on_exit(task_hash, now);
|
||||
}
|
||||
void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, Renderable_Stage stage, Stage_Observer_Point point) {
|
||||
stage_registry().bind(owner, task_hash, object, data, stage, point);
|
||||
}
|
||||
void clear_stage_observers(const tf::Taskflow* owner) {
|
||||
stage_registry().clear(owner);
|
||||
}
|
||||
tf::Taskflow& Renderable::prepare_taskflow() {
|
||||
return static_cast<Private&>(*d).prepare_extension;
|
||||
}
|
||||
tf::Taskflow& Renderable::paint_taskflow() {
|
||||
return static_cast<Private&>(*d).paint_extension;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,16 @@ struct Renderable : Def<Renderable, Root> {
|
||||
struct Private;
|
||||
/* 返回该对象参与当前事件竞争时的几何距离;无值表示沿用普通绘制层级路由。 */
|
||||
[[nodiscard]] std::optional<double> event_routing_distance(const Event& event) const;
|
||||
/*
|
||||
* 返回 Prepare 阶段完成后执行的直接 Taskflow 扩展端口。
|
||||
* tf::Taskflow 只能在该 Renderable 所属 Scene 没有运行时修改;禁止在图执行期间 emplace/erase/clear。
|
||||
*/
|
||||
[[nodiscard]] tf::Taskflow& prepare_taskflow();
|
||||
/*
|
||||
* 返回 Paint 阶段完成后执行的直接 Taskflow 扩展端口。
|
||||
* tf::Taskflow 只能在该 Renderable 所属 Scene 没有运行时修改;禁止在图执行期间 emplace/erase/clear。
|
||||
*/
|
||||
[[nodiscard]] tf::Taskflow& paint_taskflow();
|
||||
private:
|
||||
/* 数据模式的内部调度入口:执行最终对象 prepare_data(...),再触发各 CRTP 层 after_prepare_data(...)。 */
|
||||
template <Prepare_Data_Renderable Object>
|
||||
|
||||
@@ -25,8 +25,9 @@ struct Renderable::Private : Prev_Private {
|
||||
Graph_Builder builder; /* 子图模式构建入口;数据模式为空。 */
|
||||
};
|
||||
struct State_Dispatch {
|
||||
State_Get get; /* 获取最终对象的 Renderable 状态层。 */
|
||||
State_Notify notify; /* 发布最终对象的 Renderable 状态。 */
|
||||
State_Get current; /* 读取已经发布的 Renderable 状态。 */
|
||||
State_Get pending; /* 写入本阶段诊断,由完成节点统一交换发布。 */
|
||||
State_Notify publish; /* 阶段完成后交换 State 双缓冲并发布稳定 current。 */
|
||||
};
|
||||
struct Dispatch {
|
||||
Stage_Dispatch prepare; /* Prepare 阶段分派。 */
|
||||
@@ -39,6 +40,8 @@ struct Renderable::Private : Prev_Private {
|
||||
Color_Cache_Visit color_cache_visit{}; /* 最终对象存在 Color_Cache Buffer 时访问本轮写入结果。 */
|
||||
std::unique_ptr<tf::Taskflow> prepare_graph; /* Prepare 子图模式的当前构建产物。 */
|
||||
std::unique_ptr<tf::Taskflow> paint_graph; /* Paint 子图模式的当前构建产物。 */
|
||||
tf::Taskflow prepare_extension{}; /* 外部直接续写的 Prepare 完成图;不参与内部子图重建。 */
|
||||
tf::Taskflow paint_extension{}; /* 外部直接续写的 Paint 完成图;不参与内部子图重建。 */
|
||||
bool prepare_graph_built{}; /* Prepare 子图是否至少成功构建过一次。 */
|
||||
bool paint_graph_built{}; /* Paint 子图是否至少成功构建过一次。 */
|
||||
/* CRTP 可覆盖:决定已选中子图模式的 Prepare 子图是否重建;object 为最终对象,state 为当前发布状态;默认返回 false。 */
|
||||
@@ -190,6 +193,13 @@ inline void Renderable::bind_dependency_graph_object(Attached auto* object) {
|
||||
},
|
||||
[](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(*value->d);
|
||||
return static_cast<State*>(private_data.state.pending);
|
||||
},
|
||||
[](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(*value->d);
|
||||
private_data.state.advance();
|
||||
value->template notify_state<Renderable::Base_Tag>();
|
||||
}
|
||||
}
|
||||
@@ -197,16 +207,4 @@ inline void Renderable::bind_dependency_graph_object(Attached auto* object) {
|
||||
data.dispatch = &dispatch;
|
||||
object->template mark_dirty<Prepare_Data_Tag>();
|
||||
}
|
||||
namespace detail {
|
||||
enum class Renderable_Stage {
|
||||
Prepare,
|
||||
Paint
|
||||
};
|
||||
enum class Stage_Observer_Point {
|
||||
Begin,
|
||||
End
|
||||
};
|
||||
void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, Renderable_Stage stage, Stage_Observer_Point point);
|
||||
void clear_stage_observers(const tf::Taskflow* owner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "scene.hpp" /* 后端共有 Prepare Scene 实现。 */
|
||||
namespace aethera {
|
||||
Scene::Private::Private() = default;
|
||||
Scene::Private::~Private() {
|
||||
if (runtime && runtime->taskflow) detail::clear_stage_observers(runtime->taskflow.get());
|
||||
}
|
||||
Scene::Private::~Private() = default;
|
||||
}
|
||||
|
||||
+27
-12
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@@ -63,11 +64,12 @@ void Scene::Private::process(Object* object, Callback&& callback) requires std::
|
||||
template <Attached Object, typename Callback>
|
||||
void Scene::Private::process(Object* object, Render_Frame* frame, Callback&& callback) requires std::invocable<Callback, const Result&> {
|
||||
auto& private_data = static_cast<typename Object::Private&>(*this);
|
||||
auto& state = static_cast<State&>(*private_data.state.current);
|
||||
auto& state = static_cast<State&>(*private_data.state.pending);
|
||||
state.taskflow_execution_time_ns = 0;
|
||||
if (frame) frame->mark(Frame_Trace_Marker::prepare_started);
|
||||
if (runtime->taskflow && !runtime->taskflow->empty()) state.taskflow_execution_time_ns = detail::run_taskflow(*runtime->taskflow);
|
||||
if (frame) frame->mark(Frame_Trace_Marker::prepare_finished);
|
||||
private_data.state.advance();
|
||||
object->template notify_state<Scene::Base_Tag>();
|
||||
Result result;
|
||||
std::invoke(std::forward<Callback>(callback), std::as_const(result));
|
||||
@@ -79,7 +81,7 @@ void Scene::Private::after_advance(Object* object,
|
||||
const Prop* current_prop,
|
||||
State_Access<State> current_states) {
|
||||
auto* resource = detail::task_memory_resource();
|
||||
auto& scene_state = current_states.get<Scene::Base_Tag>();
|
||||
auto& scene_state = pending_states.get<Scene::Base_Tag>();
|
||||
scene_state.taskflow_rebuilt = false;
|
||||
std::pmr::unordered_set<Root*> advanced_objects{resource};
|
||||
advanced_objects.insert(object);
|
||||
@@ -87,18 +89,20 @@ void Scene::Private::after_advance(Object* object,
|
||||
[&](const Dependency_Graph& dependency_graph) {
|
||||
dependency_graph.for_each(
|
||||
[&](const Dependency_Graph::Node& node) {
|
||||
if (advanced_objects.insert(node.object).second) node.object->advance_object();
|
||||
if (advanced_objects.insert(node.object).second) {
|
||||
node.object->advance_object();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
auto prepare_dependencies = object->template current_dependency_graph<Prepare_Data_Tag>();
|
||||
bool taskflow_dirty = !runtime->taskflow;
|
||||
object->template access_pending_dependency_graph<Prepare_Data_Tag>(
|
||||
[&](auto& prepare_state) { taskflow_dirty = taskflow_dirty || prepare_state.dirty(); });
|
||||
if (!taskflow_dirty) return;
|
||||
if (!runtime->taskflow) runtime->taskflow = std::make_unique<tf::Taskflow>();
|
||||
auto& taskflow = *runtime->taskflow;
|
||||
auto prepare_dependencies = object->template current_dependency_graph<Prepare_Data_Tag>();
|
||||
std::pmr::unordered_set<Renderable*> renderables{resource};
|
||||
prepare_dependencies.for_each_bound(
|
||||
[&](Renderable* renderable, Renderable::Private&) {
|
||||
@@ -106,7 +110,6 @@ void Scene::Private::after_advance(Object* object,
|
||||
}
|
||||
);
|
||||
scene_state.renderable_count = renderables.size();
|
||||
detail::clear_stage_observers(&taskflow);
|
||||
taskflow.clear();
|
||||
struct Stage_Tasks {
|
||||
tf::Task prepare_entry; /* Prepare 条件任务,作为该阶段依赖入口。 */
|
||||
@@ -119,7 +122,7 @@ void Scene::Private::after_advance(Object* object,
|
||||
if (!data) continue;
|
||||
auto* dispatch = data->dispatch;
|
||||
auto prepare_if = taskflow.emplace([data, dispatch, root] {
|
||||
auto& state = *dispatch->state.get(root);
|
||||
auto& state = *dispatch->state.pending(root);
|
||||
state.prepare_graph_rebuilt = false;
|
||||
state.prepare_execution_time_ns = 0;
|
||||
if (dispatch->prepare.builder) {
|
||||
@@ -138,6 +141,11 @@ void Scene::Private::after_advance(Object* object,
|
||||
bool dirty = root->template dirty<Prepare_Data_Tag>();
|
||||
state.prepare_dirty = dirty;
|
||||
state.prepare_executed = dispatch->prepare.predicate(root, dirty);
|
||||
if (state.prepare_executed) {
|
||||
state.prepare_execution_time_ns = static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
return state.prepare_executed ? 0 : 1;
|
||||
}).name("renderable.prepare.condition");
|
||||
tf::Task prepare_run;
|
||||
@@ -150,15 +158,22 @@ void Scene::Private::after_advance(Object* object,
|
||||
if (dispatch->prepare.run) dispatch->prepare.run(root);
|
||||
}).name("renderable.prepare.data");
|
||||
}
|
||||
auto prepare_extension = taskflow.composed_of(data->prepare_extension)
|
||||
.name("renderable.prepare.extension");
|
||||
auto prepare_done = taskflow.emplace([dispatch, root] {
|
||||
auto& state = *dispatch->state.get(root);
|
||||
if (state.prepare_executed) root->template take_dirty<Prepare_Data_Tag>();
|
||||
dispatch->state.notify(root);
|
||||
auto& state = *dispatch->state.pending(root);
|
||||
if (state.prepare_executed) {
|
||||
root->template take_dirty<Prepare_Data_Tag>();
|
||||
const auto finished = static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
state.prepare_execution_time_ns = finished - state.prepare_execution_time_ns;
|
||||
}
|
||||
dispatch->state.publish(root);
|
||||
}).name("renderable.prepare.complete");
|
||||
prepare_if.precede(prepare_run, prepare_done);
|
||||
prepare_run.precede(prepare_done);
|
||||
detail::bind_stage_observer(&taskflow, prepare_if.hash_value(), root, data, detail::Renderable_Stage::Prepare, detail::Stage_Observer_Point::Begin);
|
||||
detail::bind_stage_observer(&taskflow, prepare_done.hash_value(), root, data, detail::Renderable_Stage::Prepare, detail::Stage_Observer_Point::End);
|
||||
prepare_run.precede(prepare_extension);
|
||||
prepare_extension.precede(prepare_done);
|
||||
stage_tasks.emplace(root, Stage_Tasks{prepare_if, prepare_done});
|
||||
}
|
||||
auto connect_dependencies = [&](const auto& dependency_graph) {
|
||||
|
||||
@@ -93,16 +93,21 @@ TEST(renderable_capability, direct_stages_do_not_allocate_subgraphs) {
|
||||
auto renderable = build_object<Direct>();
|
||||
auto scene = build_object<Scene>();
|
||||
add_renderable(*scene, renderable.get());
|
||||
int prepare_extension_calls{};
|
||||
renderable->prepare_taskflow().emplace(
|
||||
[&] { ++prepare_extension_calls; }).name("test.prepare.extension");
|
||||
scene->process([](const auto&) {});
|
||||
auto& data = renderable->data_for_test();
|
||||
auto& base = static_cast<aethera::Renderable::Private&>(data);
|
||||
EXPECT_EQ(data.prepare_calls, 1);
|
||||
EXPECT_EQ(data.paint_calls, 0);
|
||||
EXPECT_EQ(prepare_extension_calls, 1);
|
||||
EXPECT_EQ(base.prepare_graph, nullptr);
|
||||
EXPECT_EQ(base.paint_graph, nullptr);
|
||||
scene->process([](const auto&) {});
|
||||
EXPECT_EQ(data.prepare_calls, 1);
|
||||
EXPECT_EQ(data.paint_calls, 0);
|
||||
EXPECT_EQ(prepare_extension_calls, 1);
|
||||
}
|
||||
TEST(renderable_capability, graph_stage_builds_lazily_and_rebuilds_inside_condition) {
|
||||
aethera::initialize_runtime(2);
|
||||
|
||||
Reference in New Issue
Block a user