#include "render_3D/Scene_Context.hpp" #include #include #include #include #include #include "renderive/renderable/Renderable.hpp" #include "renderive/renderable/Renderable_Test_Harness.hpp" #include "renderive/scene/Scene.hpp" #include "Scene_Test_Helpers.hpp" struct Scene3D_Context_Test_Renderable : Renderable_Test_Harness { explicit Scene3D_Context_Test_Renderable() = default; void prepare_for_test(const Prepare_Render_Context&) override { scene_is_3d = dynamic_cast(&scene_for_test()) != nullptr; ++render_count; } std::atomic render_count{}; bool scene_is_3d{}; }; TEST(scene3d_context_test, uses_scene_3d_base_contract) { Scene3D_Context<> scene; auto renderable = renderive_Owner::make(); attach_initial(scene, renderable); scene.render(); scene.wait_for_render(); EXPECT_EQ(renderable->render_count.load(), 1); EXPECT_TRUE(renderable->scene_is_3d); } struct Prepared_Mesh_Count { std::size_t vertex_count{}; }; struct Scene3D_Prepared_Data_Renderable : Renderable_Test_Harness { void prepare_for_test(const Prepare_Render_Context& context) override { context.publish( std::make_shared( Prepared_Mesh_Count{42})); } }; struct Scene3D_Render_Node_Test_Scene final : Scene3D_Context<>, Scene_Renderer { Node_Execution_Result render_scene( const Scene_Render_Context& context) override { prepared = context.prepared(owner); return Node_Execution_Result::completed(); } Renderable_Id owner{}; std::shared_ptr prepared; }; TEST(scene3d_context_test, transfers_frame_local_prepared_data_to_single_scene_render_node) { Scene3D_Render_Node_Test_Scene scene; auto renderable = renderive_Owner::make(); scene.owner = renderable->renderable_id(); attach_initial(scene, renderable); scene.render(); scene.wait_for_render(); ASSERT_TRUE(scene.prepared); EXPECT_EQ(scene.prepared->vertex_count, 42U); const auto plan = scene.render_plan_snapshot(); ASSERT_TRUE(plan); const auto render_node = std::find_if( plan->graph.nodes.begin(), plan->graph.nodes.end(), [](const Render_Node& node) { return node.kind == Render_Node_Kind::render; }); ASSERT_NE(render_node, plan->graph.nodes.end()); EXPECT_TRUE(std::any_of( plan->graph.edges.begin(), plan->graph.edges.end(), [&](const Render_Edge& edge) { return edge.to == render_node->node_id; })); } struct Scene3D_Historical_State { int value{}; }; struct Scene3D_Playback_Snapshot_Test_Scene final : Scene3D_Context, Scene3D_Historical_State>, Scene_Renderer { void enqueue(int value) { Scene_State_Strategy::set<&Scene3D_Historical_State::value>(value); auto frame = frame_control.acquire_painter(); ASSERT_TRUE(frame); publish_frame_state(*frame); } void render_next() { auto frame = frame_control.acquire_renderer(); ASSERT_TRUE(frame); render(*frame); wait_for_render(); } Node_Execution_Result render_scene( const Scene_Render_Context& context) override { rendered.push_back( context.frame.scene_state().value); return Node_Execution_Result::completed(); } std::vector rendered; }; TEST(scene3d_context_test, playback_uses_the_scene_snapshot_captured_at_frame_publication) { Scene3D_Playback_Snapshot_Test_Scene scene; scene.enqueue(11); scene.enqueue(23); scene.render_next(); scene.render_next(); EXPECT_EQ(scene.rendered, (std::vector{11, 23})); } struct Scene3D_Dependency_Cache_Test_Renderable : Renderable_Test_Harness { explicit Scene3D_Dependency_Cache_Test_Renderable() : Renderable_Test_Harness({.cache_enabled = true}) {} void prepare_for_test(const Prepare_Render_Context&) override { ++render_count; } int render_count{}; }; TEST(scene3d_context_test, propagates_cache_invalidation_through_unsorted_dependency_chain) { Scene3D_Context<> scene; auto leaf = renderive_Owner::make(); auto middle = renderive_Owner::make(); auto root = renderive_Owner::make(); with_attach_builder(scene, [&](auto& attach) { attach.attach(leaf); attach.attach(middle); attach.attach(root); attach.set_dependency_parent(leaf, middle); attach.set_dependency_parent(middle, root); }); scene.render(); scene.wait_for_render(); EXPECT_EQ(root->render_count, 1); EXPECT_EQ(middle->render_count, 1); EXPECT_EQ(leaf->render_count, 1); root->invalidate_prepare_for_test(); scene.render(); scene.wait_for_render(); EXPECT_EQ(root->render_count, 2); EXPECT_EQ(middle->render_count, 2); EXPECT_EQ(leaf->render_count, 2); } TEST(scene3d_context_test, runtime_edit_explicitly_reparents_children_before_detach) { Scene3D_Context<> scene; auto grandparent = renderive_Owner::make(); auto parent = renderive_Owner::make(); auto child = renderive_Owner::make(); with_attach_builder(scene, [&](auto& attach) { attach.attach(grandparent); attach.attach(parent); attach.attach(child); attach.set_dependency_parent(parent, grandparent); attach.set_dependency_parent(child, parent); }); scene.render(); scene.wait_for_render(); EXPECT_EQ(child->render_count, 1); wait_renderable_edit(scene, [grandparent, parent, child](auto& editor) { editor.set_dependency_parent(child, grandparent); editor.clear_dependency_parent(parent); editor.detach(parent); }); const auto topology = scene.topology_snapshot(); bool dependency_checked{}; for (const auto& relation : topology.dependency) { if (relation.child.get() == child.get()) { EXPECT_EQ(relation.parent.get(), grandparent.get()); dependency_checked = true; } } EXPECT_TRUE(topology.display.empty()); EXPECT_TRUE(dependency_checked); scene.render(); scene.wait_for_render(); EXPECT_EQ(parent->render_count, 1); EXPECT_EQ(child->render_count, 2); }