#include #include #include #include #include #include #include "renderive/scene/Scene.hpp" #include "../state/State_Test_Types.hpp" struct Scene_State_Observer_Test_State : Scene2D_State { int value{}; }; struct Scene_State_Observer_Test_Time_Source { std::uint64_t now_ns() const noexcept { return 100; } }; struct Scene_State_Observer_Test_Data { std::vector events; std::atomic complete_snapshot_count{}; }; struct Scene_State_Observer_Test_Recorder { static constexpr bool enabled = true; std::shared_ptr data{std::make_shared()}; void observe(const Scene_Base::Observation& observation) noexcept { data->events.push_back(observation.event); if (observation.topology && observation.frame_snapshot && observation.render_plan) data->complete_snapshot_count.fetch_add(1, std::memory_order_relaxed); } }; using Scene_State_Observer_Test_Scene_Observer = Observer_State; using Scene_State_Observer_Test_State_Observer = Observer_State<>; TEST(scene_state_observer_test, scene_uses_double_state_and_observes_lifecycle) { State_Render_State_Reader render; Scene_State_Observer_Test_Recorder recorder; using Scene = Scene2D_Context, Recording_Color_Cache, Scene_State_Observer_Test_State, Scene_State_Observer_Test_State_Observer, Scene_State_Observer_Test_Scene_Observer>; Scene scene(With_Observer(Scene_State_Observer_Test_State_Observer{}), With_Observer(Scene_State_Observer_Test_Scene_Observer(recorder, Scene_State_Observer_Test_Time_Source{}))); scene.Scene_State_Strategy::set<&Scene_State_Observer_Test_State::value>(9); scene.Scene_State_Strategy::publish(); scene.render(); scene.wait_for_render(); EXPECT_EQ(render.read(static_cast(scene)).value, 9); EXPECT_EQ(recorder.data->events, (std::vector{Scene_Base::Observation_Event::render_submitted, Scene_Base::Observation_Event::render_started, Scene_Base::Observation_Event::render_completed})); EXPECT_EQ(recorder.data->complete_snapshot_count.load(), 3); } struct Scene_Reentrant_Observer_Data { std::function callback; }; struct Scene_Reentrant_Observer { static constexpr bool enabled = true; std::shared_ptr data{std::make_shared()}; void observe(const Scene_Base::Observation& observation) noexcept { if (data->callback) { data->callback(observation.event); } } }; using Scene_Reentrant_Observer_State = Observer_State; TEST(scene_state_observer_test, render_worker_observer_can_wait_for_current_render_without_deadlock) { Scene_Reentrant_Observer recorder; auto data = recorder.data; using Scene = Scene2D_Context, Recording_Color_Cache, Scene_State_Observer_Test_State, Scene_State_Observer_Test_State_Observer, Scene_Reentrant_Observer_State>; Scene scene(With_Observer(Scene_State_Observer_Test_State_Observer{}), With_Observer(Scene_Reentrant_Observer_State(recorder, Scene_State_Observer_Test_Time_Source{}))); std::atomic reentered{}; data->callback = [&](Scene_Base::Observation_Event event) { if (event == Scene_Base::Observation_Event::render_started) { scene.wait_for_render(); reentered.store(true, std::memory_order_release); } }; scene.render(); scene.wait_for_render(); EXPECT_TRUE(reentered.load(std::memory_order_acquire)); } TEST(scene_state_observer_test, render_submitted_observer_can_submit_next_render_without_deadlock) { Scene_Reentrant_Observer recorder; auto data = recorder.data; using Scene = Scene2D_Context, Recording_Color_Cache, Scene_State_Observer_Test_State, Scene_State_Observer_Test_State_Observer, Scene_Reentrant_Observer_State>; Scene scene(With_Observer(Scene_State_Observer_Test_State_Observer{}), With_Observer(Scene_Reentrant_Observer_State(recorder, Scene_State_Observer_Test_Time_Source{}))); std::atomic submitted{}; data->callback = [&](Scene_Base::Observation_Event event) { if (event != Scene_Base::Observation_Event::render_submitted) { return; } if (submitted.fetch_add(1, std::memory_order_acq_rel) == 0) { scene.render(); } }; scene.render(); scene.wait_for_render(); EXPECT_EQ(submitted.load(std::memory_order_acquire), 2); }