This commit is contained in:
2026-08-15 18:59:20 +08:00
parent d48f6608ad
commit 8bfaeeb265
15 changed files with 304 additions and 242 deletions
@@ -104,26 +104,20 @@ bool Render_Graph_Builder::reaches(std::size_t from, std::size_t target) const {
std::shared_ptr<const Render_Plan> Render_Plan_History::publish(Render_Graph graph) {
normalize(graph);
std::lock_guard lock(mutex_);
const auto existing = std::find_if(plans_.begin(), plans_.end(), [&graph](const auto& plan) {
return same_topology(plan->graph, graph);
});
if (existing != plans_.end()) {
current_ = *existing;
return current_;
}
if (!plans_.empty() && same_topology(plans_.back()->graph, graph))
return plans_.back();
if (next_version_ == 0 || next_version_ == std::numeric_limits<Render_Plan_Version>::max())
throw std::overflow_error("render plan version exhausted");
auto plan = std::make_shared<Render_Plan>();
plan->version = next_version_++;
plan->graph = std::move(graph);
plans_.push_back(plan);
current_ = plan;
return plan;
}
std::shared_ptr<const Render_Plan> Render_Plan_History::current() const {
std::lock_guard lock(mutex_);
return current_;
return plans_.empty() ? nullptr : plans_.back();
}
std::shared_ptr<const Render_Plan> Render_Plan_History::find(Render_Plan_Version version) const {
@@ -68,7 +68,6 @@ public:
private:
mutable std::mutex mutex_;
std::vector<std::shared_ptr<const Render_Plan>> plans_;
std::shared_ptr<const Render_Plan> current_;
Render_Plan_Version next_version_{1};
};
+14 -34
View File
@@ -9,7 +9,6 @@
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -89,8 +88,6 @@ struct Scene_Base::Compiled_Render_Plan {
Renderable_Id id{};
std::shared_ptr<const Renderable_Graph> graph;
std::vector<Renderable_Id> dependency_parent_ids;
bool prepare_required{};
bool paint_required{};
};
std::vector<Renderable_Key> renderables;
std::vector<Renderable_Id> display_order;
@@ -978,9 +975,7 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
const auto& state = snapshot.renderables[index];
if (key.id != state.renderable_id ||
key.graph != state.render_graph ||
key.dependency_parent_ids != state.dependency_parent_ids ||
key.prepare_required != state.prepare_required ||
key.paint_required != state.paint_required)
key.dependency_parent_ids != state.dependency_parent_ids)
return false;
}
return true;
@@ -992,8 +987,7 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
compiled.renderables.reserve(snapshot.renderables.size());
for (const auto& state : snapshot.renderables) {
compiled.renderables.push_back({
state.renderable_id, state.render_graph, state.dependency_parent_ids,
state.prepare_required, state.paint_required});
state.renderable_id, state.render_graph, state.dependency_parent_ids});
}
compiled.display_order = snapshot.display_order;
};
@@ -1027,16 +1021,10 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
const auto& state = snapshot.renderables[item.snapshot_index];
if (!state.render_graph)
throw std::logic_error("frame renderable has no graph snapshot");
std::unordered_set<Render_Node_Id> selected;
for (const auto& node : state.render_graph->graph.nodes) {
if (node.kind == Render_Node_Kind::composite)
throw std::logic_error(
"renderable graph must not define composite nodes");
const bool include = node.kind == Render_Node_Kind::prepare
? state.prepare_required
: state.paint_required;
if (!include)
continue;
graph.nodes.push_back(node);
const auto& render_function =
state.render_graph->functions.at(node.execution_index);
@@ -1044,30 +1032,22 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
functions.emplace(node.node_id,
Execution_Binding{item.snapshot_index, Execution_Function{function}});
}, render_function);
selected.insert(node.node_id);
}
for (const auto& edge : state.render_graph->graph.edges) {
if (selected.contains(edge.from) && selected.contains(edge.to))
append_edge(edge.from, edge.to);
}
for (const auto& edge : state.render_graph->graph.edges)
append_edge(edge.from, edge.to);
const auto stage_boundary = [&](Render_Node_Kind kind, bool roots) {
std::vector<Render_Node_Id> result;
if ((kind == Render_Node_Kind::prepare &&
!state.prepare_required) ||
(kind == Render_Node_Kind::paint && !state.paint_required))
return result;
for (const auto& node : state.render_graph->graph.nodes) {
if (node.kind != kind || !selected.contains(node.node_id))
if (node.kind != kind)
continue;
const bool connected = std::any_of(
state.render_graph->graph.edges.begin(),
state.render_graph->graph.edges.end(),
[&](const Render_Edge& edge) {
const Render_Node_Id adjacent =
roots ? edge.from : edge.to;
const Render_Node_Id adjacent = roots ? edge.from : edge.to;
const bool matches = roots ? edge.to == node.node_id
: edge.from == node.node_id;
if (!matches || !selected.contains(adjacent))
if (!matches)
return false;
return std::any_of(
state.render_graph->graph.nodes.begin(),
@@ -1169,12 +1149,9 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
throw std::logic_error("render plan node has no execution binding");
execution_bindings[node.execution_index] = std::move(function->second);
}
if (const auto existing = compiled_render_plans_.find(plan->version);
existing != compiled_render_plans_.end()) {
auto& compiled = *existing->second;
update_key(compiled);
compiled.execution_bindings = std::move(execution_bindings);
compiled_render_plan_ = existing->second;
if (compiled_render_plan_ && compiled_render_plan_->plan == plan) {
update_key(*compiled_render_plan_);
compiled_render_plan_->execution_bindings = std::move(execution_bindings);
return compiled_render_plan_;
}
auto compiled = std::make_shared<Compiled_Render_Plan>();
@@ -1183,7 +1160,6 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
compiled->execution_bindings = std::move(execution_bindings);
compiled->runtime = std::make_unique<
renderive::render_graph::detail::Render_Graph_Runtime>(*compiled->plan);
compiled_render_plans_.emplace(compiled->plan->version, compiled);
compiled_render_plan_ = compiled;
return compiled;
}
@@ -1234,6 +1210,8 @@ void Scene_Base::execute_render_graph(Render_Task& task) {
case Render_Node_Kind::prepare: {
const auto& state = snapshot.renderables.at(
binding.renderable_index.value());
if (!state.prepare_required)
return Node_Execution_Result::completed();
std::get<Prepare_Render_Node_Function>(binding.function)(
Prepare_Render_Context{snapshot, state, metrics,
diagnostics});
@@ -1242,6 +1220,8 @@ void Scene_Base::execute_render_graph(Render_Task& task) {
case Render_Node_Kind::paint: {
const auto& state = snapshot.renderables.at(
binding.renderable_index.value());
if (!state.paint_required)
return Node_Execution_Result::completed();
if (!state.paint_buffer_)
throw std::logic_error(
"paint node has no frame color cache");
@@ -296,8 +296,6 @@ private:
const Render_Node_Id composite_begin_node_id_;
const Render_Node_Id scene_render_node_id_;
Render_Plan_History render_plan_history_;
std::unordered_map<Render_Plan_Version, std::shared_ptr<Compiled_Render_Plan>>
compiled_render_plans_;
std::shared_ptr<Compiled_Render_Plan> compiled_render_plan_;
Capture_Controller capture_controller_;
Capture_Repository capture_repository_;
@@ -54,11 +54,12 @@ TEST(render_dag_test, one_graph_assigns_dense_execution_slots_and_versions_only_
changed_graph.edges.push_back({100, 101});
const auto changed = history.publish(std::move(changed_graph));
EXPECT_GT(changed->version, first->version);
const auto reused = history.publish(make_parallel_graph());
EXPECT_EQ(reused, first);
EXPECT_EQ(history.current(), first);
const auto restored = history.publish(make_parallel_graph());
EXPECT_GT(restored->version, changed->version);
EXPECT_EQ(history.current(), restored);
EXPECT_EQ(history.find(first->version), first);
EXPECT_EQ(history.find(changed->version), changed);
EXPECT_EQ(history.find(restored->version), restored);
}
TEST(render_dag_test, analysis_derives_wait_critical_path_and_parallel_overlap_from_frame_slots) {
@@ -191,7 +191,7 @@ TEST(render_plan_execution_test, dependent_prepare_does_not_wait_for_dependency_
}
#endif
TEST(render_plan_execution_test, prepare_and_paint_cache_validity_are_independent_and_prune_nodes) {
TEST(render_plan_execution_test, prepare_and_paint_cache_validity_do_not_recompile_render_plan) {
Scene2D_Context<> scene;
auto renderable = renderive_Owner<Pipeline_Renderable>::make(true);
attach_initial(scene, renderable);
@@ -199,6 +199,8 @@ TEST(render_plan_execution_test, prepare_and_paint_cache_validity_are_independen
scene.wait_for_render();
EXPECT_TRUE(renderable->prepare_cache_valid_for_test());
EXPECT_TRUE(renderable->paint_cache_valid_for_test());
EXPECT_EQ(renderable->prepare_count.load(), 1);
EXPECT_EQ(renderable->paint_count.load(), 1);
const auto first_plan = scene.render_plan_snapshot();
const Render_Node_Id prepare_id = node(*first_plan, renderable->renderable_id(),
Render_Node_Kind::prepare).node_id;
@@ -208,12 +210,13 @@ TEST(render_plan_execution_test, prepare_and_paint_cache_validity_are_independen
scene.render();
scene.wait_for_render();
const auto cached_plan = scene.render_plan_snapshot();
EXPECT_GT(cached_plan->version, first_plan->version);
EXPECT_EQ(std::count_if(cached_plan->graph.nodes.begin(), cached_plan->graph.nodes.end(),
[&](const Render_Node& value) {
return value.owner_id == renderable->renderable_id() &&
value.kind != Render_Node_Kind::composite;
}), 0);
EXPECT_EQ(cached_plan, first_plan);
EXPECT_EQ(renderable->prepare_count.load(), 1);
EXPECT_EQ(renderable->paint_count.load(), 1);
EXPECT_EQ(node(*cached_plan, renderable->renderable_id(), Render_Node_Kind::prepare).node_id,
prepare_id);
EXPECT_EQ(node(*cached_plan, renderable->renderable_id(), Render_Node_Kind::paint).node_id,
paint_id);
renderable->invalidate_paint_for_test();
EXPECT_TRUE(renderable->prepare_cache_valid_for_test());
@@ -221,14 +224,13 @@ TEST(render_plan_execution_test, prepare_and_paint_cache_validity_are_independen
scene.render();
scene.wait_for_render();
const auto paint_plan = scene.render_plan_snapshot();
EXPECT_GT(paint_plan->version, cached_plan->version);
EXPECT_EQ(paint_plan, first_plan);
EXPECT_EQ(renderable->prepare_count.load(), 1);
EXPECT_EQ(renderable->paint_count.load(), 2);
EXPECT_EQ(node(*paint_plan, renderable->renderable_id(), Render_Node_Kind::prepare).node_id,
prepare_id);
EXPECT_EQ(node(*paint_plan, renderable->renderable_id(), Render_Node_Kind::paint).node_id,
paint_id);
EXPECT_EQ(std::count_if(paint_plan->graph.nodes.begin(), paint_plan->graph.nodes.end(),
[&](const Render_Node& value) {
return value.owner_id == renderable->renderable_id() &&
value.kind == Render_Node_Kind::prepare;
}), 0);
renderable->invalidate_prepare_for_test();
EXPECT_FALSE(renderable->prepare_cache_valid_for_test());
@@ -236,7 +238,9 @@ TEST(render_plan_execution_test, prepare_and_paint_cache_validity_are_independen
scene.render();
scene.wait_for_render();
const auto rebuilt_plan = scene.render_plan_snapshot();
EXPECT_GT(rebuilt_plan->version, paint_plan->version);
EXPECT_EQ(rebuilt_plan, first_plan);
EXPECT_EQ(renderable->prepare_count.load(), 2);
EXPECT_EQ(renderable->paint_count.load(), 3);
EXPECT_EQ(node(*rebuilt_plan, renderable->renderable_id(), Render_Node_Kind::prepare).node_id,
prepare_id);
EXPECT_EQ(node(*rebuilt_plan, renderable->renderable_id(), Render_Node_Kind::paint).node_id,