From 9e598a0318f927ffa14637781450885e7a35dd39 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Thu, 13 Aug 2026 09:16:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/renderive/scene/base/Scene_Base.cpp | 176 +++++++----------- .../src/renderive/scene/base/Scene_Base.hpp | 57 +++--- .../Dynamic_Renderable_Lifecycle_Test.cpp | 22 ++- .../scene/Render_Plan_Execution_Test.cpp | 15 +- .../renderive/scene/Scene2D_Context_Test.cpp | 12 +- .../scene/Scene2D_Render_Order_Test.cpp | 12 +- .../renderive/scene/Scene3D_Context_Test.cpp | 12 +- .../renderive/scene/base/Scene_Base_Test.cpp | 46 ++--- .../threading/Threading_Contract_Test.cpp | 5 +- render_2D/scene/Scene.h | 8 +- web_server/app/Web_Server.cpp | 38 ++-- web_server/server/main.cpp | 8 +- 12 files changed, 182 insertions(+), 229 deletions(-) diff --git a/Kernel/src/renderive/scene/base/Scene_Base.cpp b/Kernel/src/renderive/scene/base/Scene_Base.cpp index 700c7aa..fb0812b 100644 --- a/Kernel/src/renderive/scene/base/Scene_Base.cpp +++ b/Kernel/src/renderive/scene/base/Scene_Base.cpp @@ -336,44 +336,40 @@ bool Scene_Base::Mutation::ready() const { std::lock_guard lock(state_->mutex); return state_->completed; } +void Scene_Base::Mutation::then(Mutation::Callback callback) const { + if (!callback) + return; + if (!state_) { + try { + callback({}); + } catch (...) { + } + return; + } + std::exception_ptr exception; + { + std::lock_guard lock(state_->mutex); + if (!state_->completed) { + state_->callbacks.push_back(std::move(callback)); + return; + } + exception = state_->exception; + } + try { + callback(exception); + } catch (...) { + } +} Scene_Base::Attach_Builder::Attach_Builder(Scene_Base& scene) : scene_(&scene), task_lock_(scene.task_mutex_) { if (scene.runtime_started_) throw std::logic_error("scene attach builder is only available before runtime starts"); } -void Scene_Base::Attach_Builder::attach_renderable(Renderable renderable, - Attach_Relationships relationships) { +void Scene_Base::Attach_Builder::attach(Renderable renderable, + Attach_Relationships relationships) { std::lock_guard lock(scene_->model_mutex_); scene_->apply_attach_renderable_locked(std::move(renderable), std::move(relationships)); } -void Scene_Base::Attach_Builder::set_display_parent(const Renderable& child, - const Renderable& parent) { - std::lock_guard lock(scene_->model_mutex_); - scene_->apply_set_display_parent_locked(child, parent); -} -void Scene_Base::Attach_Builder::add_display_parent(const Renderable& child, - const Renderable& parent) { - std::lock_guard lock(scene_->model_mutex_); - scene_->apply_add_display_parent_locked(child, parent); -} -void Scene_Base::Attach_Builder::clear_display_parent(const Renderable& child) { - std::lock_guard lock(scene_->model_mutex_); - scene_->apply_clear_display_parent_locked(child); -} -void Scene_Base::Attach_Builder::set_dependency_parent(const Renderable& child, - const Renderable& parent) { - std::lock_guard lock(scene_->model_mutex_); - scene_->apply_set_dependency_parent_locked(child, parent); -} -void Scene_Base::Attach_Builder::add_dependency_parent(const Renderable& child, - const Renderable& parent) { - std::lock_guard lock(scene_->model_mutex_); - scene_->apply_add_dependency_parent_locked(child, parent); -} -void Scene_Base::Attach_Builder::clear_dependency_parent(const Renderable& child) { - std::lock_guard lock(scene_->model_mutex_); - scene_->apply_clear_dependency_parent_locked(child); -} Scene_Base::Attach_Builder Scene_Base::attach_builder() { return Attach_Builder(*this); } @@ -468,15 +464,6 @@ void Scene_Base::wait_for_render() { if (exception) std::rethrow_exception(exception); } -void Scene_Base::wait_for_mutations() { - if (is_render_worker_thread()) - throw std::logic_error("scene mutation wait is not allowed from render execution"); - std::unique_lock lock(task_mutex_); - render_completed_.wait(lock, [this] { - return mutation_queue_.empty() && !mutating_; - }); -} - std::vector Scene_Base::relationship_ids_locked( const std::vector& renderables) const { std::vector ids; @@ -490,28 +477,28 @@ std::vector Scene_Base::relationship_ids_locked( } return ids; } -Scene_Base::Mutation Scene_Base::enqueue_mutation(std::function operation, - Mutation_Callback callback) { +Scene_Base::Mutation Scene_Base::enqueue_mutation(std::function operation) { std::lock_guard lock(task_mutex_); - return enqueue_mutation_locked(std::move(operation), std::move(callback)); + return enqueue_mutation_locked(std::move(operation)); } -Scene_Base::Mutation Scene_Base::enqueue_mutation_locked(std::function operation, - Mutation_Callback callback) { +Scene_Base::Mutation Scene_Base::enqueue_mutation_locked(std::function operation) { if (stop_) throw std::logic_error("scene is shutting down"); runtime_started_ = true; auto completion = std::make_shared(*this); - mutation_queue_.push_back({std::move(operation), std::move(callback), completion}); + mutation_queue_.push_back({std::move(operation), completion}); task_ready_.notify_one(); return Mutation(std::move(completion)); } void Scene_Base::complete_mutation(Mutation_Command& command, - std::exception_ptr exception) noexcept { + std::exception_ptr exception, + std::vector& callbacks) noexcept { { std::lock_guard lock(command.completion->mutex); command.completion->exception = exception; command.completion->completed = true; command.completion->scene = nullptr; + callbacks = std::move(command.completion->callbacks); } command.completion->condition.notify_all(); } @@ -639,80 +626,60 @@ void Scene_Base::apply_clear_dependency_parent_locked(const Renderable& child) { notify_model_dirty(); } Scene_Base::Mutation Scene_Base::attach_renderable(Renderable renderable, - Attach_Relationships relationships, - Mutation_Callback callback) { + Attach_Relationships relationships) { if (!renderable) throw std::invalid_argument("renderable is null"); validate_renderable_scene(*renderable); return enqueue_mutation( [this, renderable = std::move(renderable), relationships = std::move(relationships)]() mutable { apply_attach_renderable_locked(std::move(renderable), std::move(relationships)); - }, - std::move(callback)); + }); } -Scene_Base::Mutation Scene_Base::detach_renderable(const Renderable& renderable, - Mutation_Callback callback) { +Scene_Base::Mutation Scene_Base::detach_renderable(const Renderable& renderable) { if (!renderable) throw std::invalid_argument("renderable is null"); validate_renderable_scene(*renderable); - return enqueue_mutation([this, renderable] { apply_detach_renderable_locked(renderable); }, - std::move(callback)); + return enqueue_mutation([this, renderable] { apply_detach_renderable_locked(renderable); }); } -Scene_Base::Mutation Scene_Base::set_display_parent(const Renderable& child, - const Renderable& parent, - Mutation_Callback callback) { +Scene_Base::Mutation Scene_Base::set_parent(Relationship relationship, + const Renderable& child, + const Renderable& parent) { if (!child || !parent) - throw std::invalid_argument("display relationship endpoint is null"); + throw std::invalid_argument("topology relationship endpoint is null"); validate_renderable_scene(*child); validate_renderable_scene(*parent); - return enqueue_mutation([this, child, parent] { apply_set_display_parent_locked(child, parent); }, - std::move(callback)); + return enqueue_mutation([this, relationship, child, parent] { + if (relationship == Relationship::display) + apply_set_display_parent_locked(child, parent); + else + apply_set_dependency_parent_locked(child, parent); + }); } -Scene_Base::Mutation Scene_Base::add_display_parent(const Renderable& child, - const Renderable& parent, - Mutation_Callback callback) { +Scene_Base::Mutation Scene_Base::add_parent(Relationship relationship, + const Renderable& child, + const Renderable& parent) { if (!child || !parent) - throw std::invalid_argument("display relationship endpoint is null"); + throw std::invalid_argument("topology relationship endpoint is null"); validate_renderable_scene(*child); validate_renderable_scene(*parent); - return enqueue_mutation([this, child, parent] { apply_add_display_parent_locked(child, parent); }, - std::move(callback)); + return enqueue_mutation([this, relationship, child, parent] { + if (relationship == Relationship::display) + apply_add_display_parent_locked(child, parent); + else + apply_add_dependency_parent_locked(child, parent); + }); } -Scene_Base::Mutation Scene_Base::clear_display_parent(const Renderable& child, - Mutation_Callback callback) { +Scene_Base::Mutation Scene_Base::clear_parents(Relationship relationship, + const Renderable& child) { if (!child) - throw std::invalid_argument("display child is null"); + throw std::invalid_argument("topology child is null"); validate_renderable_scene(*child); - return enqueue_mutation([this, child] { apply_clear_display_parent_locked(child); }, - std::move(callback)); -} -Scene_Base::Mutation Scene_Base::set_dependency_parent(const Renderable& child, - const Renderable& parent, - Mutation_Callback callback) { - if (!child || !parent) - throw std::invalid_argument("dependency relationship endpoint is null"); - validate_renderable_scene(*child); - validate_renderable_scene(*parent); - return enqueue_mutation([this, child, parent] { apply_set_dependency_parent_locked(child, parent); }, - std::move(callback)); -} -Scene_Base::Mutation Scene_Base::add_dependency_parent(const Renderable& child, - const Renderable& parent, - Mutation_Callback callback) { - if (!child || !parent) - throw std::invalid_argument("dependency relationship endpoint is null"); - validate_renderable_scene(*child); - validate_renderable_scene(*parent); - return enqueue_mutation([this, child, parent] { apply_add_dependency_parent_locked(child, parent); }, - std::move(callback)); -} -Scene_Base::Mutation Scene_Base::clear_dependency_parent(const Renderable& child, - Mutation_Callback callback) { - if (!child) - throw std::invalid_argument("dependency child is null"); - validate_renderable_scene(*child); - return enqueue_mutation([this, child] { apply_clear_dependency_parent_locked(child); }, - std::move(callback)); + return enqueue_mutation([this, relationship, child] { + if (relationship == Relationship::display) + apply_clear_display_parent_locked(child); + else + apply_clear_dependency_parent_locked(child); + }); } void Scene_Base::request_render_graph_rebuild(Renderable_Base& renderable) { validate_renderable_scene(renderable); @@ -726,7 +693,7 @@ void Scene_Base::request_render_graph_rebuild(Renderable_Base& renderable) { static_cast(enqueue_mutation_locked([this, owner = std::move(owner)] { owner->reset_render_graph(); notify_model_dirty(); - }, {})); + })); } void Scene_Base::publish_frame_state() { auto task_lock = lock_render_idle(); @@ -1055,6 +1022,7 @@ void Scene_Base::render_loop() { } if (!mutations.empty()) { std::vector exceptions(mutations.size()); + std::vector> callbacks(mutations.size()); { std::lock_guard lock(model_mutex_); for (std::size_t index = 0; index < mutations.size(); ++index) { @@ -1066,16 +1034,16 @@ void Scene_Base::render_loop() { } } for (std::size_t index = 0; index < mutations.size(); ++index) - complete_mutation(mutations[index], exceptions[index]); + complete_mutation(mutations[index], exceptions[index], callbacks[index]); { std::lock_guard lock(task_mutex_); mutating_ = false; } render_completed_.notify_all(); - for (std::size_t index = 0; index < mutations.size(); ++index) { - if (mutations[index].callback) { + for (std::size_t index = 0; index < callbacks.size(); ++index) { + for (auto& callback : callbacks[index]) { try { - mutations[index].callback(exceptions[index]); + callback(exceptions[index]); } catch (...) { } } diff --git a/Kernel/src/renderive/scene/base/Scene_Base.hpp b/Kernel/src/renderive/scene/base/Scene_Base.hpp index 875da47..e8d8f8e 100644 --- a/Kernel/src/renderive/scene/base/Scene_Base.hpp +++ b/Kernel/src/renderive/scene/base/Scene_Base.hpp @@ -44,7 +44,11 @@ private: public: using Renderable = std::shared_ptr; using Const_Renderable = std::shared_ptr; - using Mutation_Callback = std::function; + + enum class Relationship { + display, + dependency + }; struct Attach_Relationships { std::vector display_parents; @@ -54,15 +58,18 @@ public: class Mutation { public: + using Callback = std::function; Mutation() = default; void wait() const; [[nodiscard]] bool ready() const; + void then(Callback callback) const; private: struct State { explicit State(Scene_Base& scene) : scene(&scene) {} Scene_Base* scene{}; mutable std::mutex mutex; std::condition_variable condition; + std::vector callbacks; std::exception_ptr exception; bool completed{}; }; @@ -75,16 +82,9 @@ public: public: Attach_Builder(const Attach_Builder&) = delete; Attach_Builder& operator=(const Attach_Builder&) = delete; - Attach_Builder(Attach_Builder&&) noexcept = default; - Attach_Builder& operator=(Attach_Builder&&) noexcept = default; - void attach_renderable(Renderable renderable, - Attach_Relationships relationships = {}); - void set_display_parent(const Renderable& child, const Renderable& parent); - void add_display_parent(const Renderable& child, const Renderable& parent); - void clear_display_parent(const Renderable& child); - void set_dependency_parent(const Renderable& child, const Renderable& parent); - void add_dependency_parent(const Renderable& child, const Renderable& parent); - void clear_dependency_parent(const Renderable& child); + Attach_Builder(Attach_Builder&&) = delete; + Attach_Builder& operator=(Attach_Builder&&) = delete; + void attach(Renderable renderable, Attach_Relationships relationships = {}); private: explicit Attach_Builder(Scene_Base& scene); Scene_Base* scene_{}; @@ -129,28 +129,18 @@ public: void render(); void render(Abstract_Frame& frame); void wait_for_render(); - void wait_for_mutations(); void publish_frame_state(); void notify_model_dirty() noexcept; Attach_Builder attach_builder(); Mutation attach_renderable(Renderable renderable, - Attach_Relationships relationships = {}, - Mutation_Callback callback = {}); - Mutation detach_renderable(const Renderable& renderable, - Mutation_Callback callback = {}); - Mutation set_display_parent(const Renderable& child, const Renderable& parent, - Mutation_Callback callback = {}); - Mutation add_display_parent(const Renderable& child, const Renderable& parent, - Mutation_Callback callback = {}); - Mutation clear_display_parent(const Renderable& child, - Mutation_Callback callback = {}); - Mutation set_dependency_parent(const Renderable& child, const Renderable& parent, - Mutation_Callback callback = {}); - Mutation add_dependency_parent(const Renderable& child, const Renderable& parent, - Mutation_Callback callback = {}); - Mutation clear_dependency_parent(const Renderable& child, - Mutation_Callback callback = {}); + Attach_Relationships relationships = {}); + Mutation detach_renderable(const Renderable& renderable); + Mutation set_parent(Relationship relationship, const Renderable& child, + const Renderable& parent); + Mutation add_parent(Relationship relationship, const Renderable& child, + const Renderable& parent); + Mutation clear_parents(Relationship relationship, const Renderable& child); void set_renderable_configuration(const Renderable& renderable, Renderable_Configuration configuration); @@ -265,7 +255,6 @@ private: struct Mutation_Command { std::function operation; - Mutation_Callback callback; std::shared_ptr completion; }; @@ -279,12 +268,10 @@ private: }; void submit_render(Abstract_Frame* frame); - [[nodiscard]] Mutation enqueue_mutation(std::function operation, - Mutation_Callback callback); - [[nodiscard]] Mutation enqueue_mutation_locked(std::function operation, - Mutation_Callback callback); - void complete_mutation(Mutation_Command& command, - std::exception_ptr exception) noexcept; + [[nodiscard]] Mutation enqueue_mutation(std::function operation); + [[nodiscard]] Mutation enqueue_mutation_locked(std::function operation); + void complete_mutation(Mutation_Command& command, std::exception_ptr exception, + std::vector& callbacks) noexcept; void apply_attach_renderable_locked(Renderable renderable, Attach_Relationships relationships); void apply_detach_renderable_locked(const Renderable& renderable); diff --git a/Kernel/tests/renderive/scene/Dynamic_Renderable_Lifecycle_Test.cpp b/Kernel/tests/renderive/scene/Dynamic_Renderable_Lifecycle_Test.cpp index 97a4fae..2fedf2b 100644 --- a/Kernel/tests/renderive/scene/Dynamic_Renderable_Lifecycle_Test.cpp +++ b/Kernel/tests/renderive/scene/Dynamic_Renderable_Lifecycle_Test.cpp @@ -104,7 +104,8 @@ TEST(dynamic_renderable_lifecycle_test, scene.render(); gate->wait_until_arrived(); std::atomic callback_called{}; - auto mutation = scene.attach_renderable(attached, {}, [&](std::exception_ptr exception) { + auto mutation = scene.attach_renderable(attached); + mutation.then([&](std::exception_ptr exception) { EXPECT_FALSE(exception); callback_called.store(true, std::memory_order_release); }); @@ -154,13 +155,13 @@ TEST(dynamic_renderable_lifecycle_test, auto child = std::make_shared(scene); scene.attach_renderable(first_parent); scene.attach_renderable(second_parent); - scene.attach_renderable(child, { + auto attach = scene.attach_renderable(child, { .dependency_parents = {first_parent} }); - scene.wait_for_mutations(); + attach.wait(); scene.render(); first_gate->wait_until_arrived(); - auto reparent = scene.set_dependency_parent(child, second_parent); + auto reparent = scene.set_parent(Scene_Base::Relationship::dependency, child, second_parent); EXPECT_FALSE(reparent.ready()); EXPECT_EQ(child->prepare_count.load(std::memory_order_acquire), 0); first_gate->open(); @@ -252,7 +253,7 @@ TEST(dynamic_renderable_lifecycle_test, scene.render(); scene.wait_for_render(); - scene.wait_for_mutations(); + EXPECT_TRUE(wait_until([&] { return scene.renderable_count() == 0; })); EXPECT_EQ(renderable->prepare_count.load(std::memory_order_acquire), 1); EXPECT_EQ(scene.renderable_count(), 0u); scene.render(); @@ -428,11 +429,11 @@ TEST(dynamic_renderable_lifecycle_test, .display_parents = {parent}, .dependency_parents = {parent} }); - scene.set_display_parent(child, alternate_parent); - scene.set_dependency_parent(child, alternate_parent); + scene.set_parent(Scene_Base::Relationship::display, child, alternate_parent); + scene.set_parent(Scene_Base::Relationship::dependency, child, alternate_parent); child->invalidate_graph(); - scene.set_display_parent(child, parent); - scene.set_dependency_parent(child, parent); + scene.set_parent(Scene_Base::Relationship::display, child, parent); + scene.set_parent(Scene_Base::Relationship::dependency, child, parent); scene.detach_renderable(child); } catch (...) { failures.fetch_add(1, std::memory_order_relaxed); @@ -444,7 +445,8 @@ TEST(dynamic_renderable_lifecycle_test, controller.join(); controls_done.store(true, std::memory_order_release); renderer.join(); - scene.wait_for_mutations(); + scene.render(); + scene.wait_for_render(); EXPECT_EQ(failures.load(std::memory_order_acquire), 0); EXPECT_EQ(violations.load(std::memory_order_acquire), 0); EXPECT_EQ(scene.renderable_count(), 2u); diff --git a/Kernel/tests/renderive/scene/Render_Plan_Execution_Test.cpp b/Kernel/tests/renderive/scene/Render_Plan_Execution_Test.cpp index 57d8ef0..9a9ad64 100644 --- a/Kernel/tests/renderive/scene/Render_Plan_Execution_Test.cpp +++ b/Kernel/tests/renderive/scene/Render_Plan_Execution_Test.cpp @@ -95,8 +95,8 @@ TEST(render_plan_execution_test, dependency_edges_connect_prepare_only_and_layer auto child = std::make_shared(scene); scene.attach_renderable(parent); scene.attach_renderable(child); - scene.set_dependency_parent(child, parent); - scene.set_display_parent(child, parent); + scene.set_parent(Scene_Base::Relationship::dependency, child, parent); + scene.set_parent(Scene_Base::Relationship::display, child, parent); scene.render(); scene.wait_for_render(); @@ -166,7 +166,7 @@ TEST(render_plan_execution_test, dependent_prepare_does_not_wait_for_dependency_ child->prepare_action = [&] { child_prepared.store(true, std::memory_order_release); }; scene.attach_renderable(parent); scene.attach_renderable(child); - scene.set_dependency_parent(child, parent); + scene.set_parent(Scene_Base::Relationship::dependency, child, parent); scene.render(); { std::unique_lock lock(mutex); @@ -248,21 +248,24 @@ TEST(render_plan_execution_test, logical_node_ids_are_stable_and_retired_ids_are const Render_Node_Id retired_chunk = first->graph.nodes[2].node_id; renderable->set_chunk_count(2); - scene.wait_for_mutations(); + scene.render(); + scene.wait_for_render(); const auto unchanged = renderable->graph_snapshot(); EXPECT_EQ(unchanged->graph.nodes[0].node_id, root); EXPECT_EQ(unchanged->graph.nodes[1].node_id, first_chunk); EXPECT_EQ(unchanged->graph.nodes[2].node_id, retired_chunk); renderable->set_chunk_count(1); - scene.wait_for_mutations(); + scene.render(); + scene.wait_for_render(); const auto reduced = renderable->graph_snapshot(); ASSERT_EQ(reduced->graph.nodes.size(), 2u); EXPECT_EQ(reduced->graph.nodes[0].node_id, root); EXPECT_EQ(reduced->graph.nodes[1].node_id, first_chunk); renderable->set_chunk_count(2); - scene.wait_for_mutations(); + scene.render(); + scene.wait_for_render(); const auto expanded = renderable->graph_snapshot(); ASSERT_EQ(expanded->graph.nodes.size(), 3u); EXPECT_EQ(expanded->graph.nodes[0].node_id, root); diff --git a/Kernel/tests/renderive/scene/Scene2D_Context_Test.cpp b/Kernel/tests/renderive/scene/Scene2D_Context_Test.cpp index b7f41c8..2028f66 100644 --- a/Kernel/tests/renderive/scene/Scene2D_Context_Test.cpp +++ b/Kernel/tests/renderive/scene/Scene2D_Context_Test.cpp @@ -110,13 +110,13 @@ TEST(scene2d_context_test, dependency_reparent_invalidates_cached_child) { scene.attach_renderable(first_parent); scene.attach_renderable(second_parent); scene.attach_renderable(child); - scene.set_dependency_parent(child, first_parent); + scene.set_parent(Scene_Base::Relationship::dependency, child, first_parent); scene.render(); scene.wait_for_render(); EXPECT_EQ(first_parent->render_count, 1); EXPECT_EQ(second_parent->render_count, 1); EXPECT_EQ(child->render_count, 1); - scene.set_dependency_parent(child, second_parent); + scene.set_parent(Scene_Base::Relationship::dependency, child, second_parent); scene.render(); scene.wait_for_render(); EXPECT_EQ(first_parent->render_count, 1); @@ -131,8 +131,8 @@ TEST(scene2d_context_test, multiple_dependency_parents_invalidate_cached_child) scene.attach_renderable(first_parent); scene.attach_renderable(second_parent); scene.attach_renderable(child); - scene.set_dependency_parent(child, first_parent); - scene.add_dependency_parent(child, second_parent); + scene.set_parent(Scene_Base::Relationship::dependency, child, first_parent); + scene.add_parent(Scene_Base::Relationship::dependency, child, second_parent); scene.render(); scene.wait_for_render(); EXPECT_EQ(child->render_count, 1); @@ -171,8 +171,8 @@ TEST(scene2d_context_test, detach_dependency_parent_invalidates_promoted_cached_ scene.attach_renderable(grandparent); scene.attach_renderable(parent); scene.attach_renderable(child); - scene.set_dependency_parent(parent, grandparent); - scene.set_dependency_parent(child, parent); + scene.set_parent(Scene_Base::Relationship::dependency, parent, grandparent); + scene.set_parent(Scene_Base::Relationship::dependency, child, parent); scene.render(); scene.wait_for_render(); EXPECT_EQ(grandparent->render_count, 1); diff --git a/Kernel/tests/renderive/scene/Scene2D_Render_Order_Test.cpp b/Kernel/tests/renderive/scene/Scene2D_Render_Order_Test.cpp index 5f08165..068c922 100644 --- a/Kernel/tests/renderive/scene/Scene2D_Render_Order_Test.cpp +++ b/Kernel/tests/renderive/scene/Scene2D_Render_Order_Test.cpp @@ -39,8 +39,8 @@ TEST(scene2d_render_order_test, separates_dependency_order_from_display_order) { auto axis = std::make_shared(scene, state, 1); scene.attach_renderable(spectrum); scene.attach_renderable(axis); - scene.set_dependency_parent(spectrum, axis); - scene.set_display_parent(axis, spectrum); + scene.set_parent(Scene_Base::Relationship::dependency, spectrum, axis); + scene.set_parent(Scene_Base::Relationship::display, axis, spectrum); scene.render(); scene.wait_for_render(); EXPECT_EQ(state.snapshot(), (std::vector{1, 2})); @@ -61,10 +61,10 @@ TEST(scene2d_render_order_test, shared_overlay_waits_for_every_display_parent) { scene.attach_renderable(first_plot); scene.attach_renderable(axis); scene.attach_renderable(second_plot); - scene.add_dependency_parent(first_plot, axis); - scene.add_dependency_parent(second_plot, axis); - scene.add_display_parent(axis, first_plot); - scene.add_display_parent(axis, second_plot); + scene.add_parent(Scene_Base::Relationship::dependency, first_plot, axis); + scene.add_parent(Scene_Base::Relationship::dependency, second_plot, axis); + scene.add_parent(Scene_Base::Relationship::display, axis, first_plot); + scene.add_parent(Scene_Base::Relationship::display, axis, second_plot); scene.render(); scene.wait_for_render(); const auto render_order = state.snapshot(); diff --git a/Kernel/tests/renderive/scene/Scene3D_Context_Test.cpp b/Kernel/tests/renderive/scene/Scene3D_Context_Test.cpp index f0eb3fc..56560bf 100644 --- a/Kernel/tests/renderive/scene/Scene3D_Context_Test.cpp +++ b/Kernel/tests/renderive/scene/Scene3D_Context_Test.cpp @@ -36,8 +36,8 @@ TEST(scene3d_context_test, propagates_cache_invalidation_through_unsorted_depend scene.attach_renderable(leaf); scene.attach_renderable(middle); scene.attach_renderable(root); - scene.set_dependency_parent(leaf, middle); - scene.set_dependency_parent(middle, root); + scene.set_parent(Scene_Base::Relationship::dependency, leaf, middle); + scene.set_parent(Scene_Base::Relationship::dependency, middle, root); scene.render(); scene.wait_for_render(); EXPECT_EQ(root->render_count, 1); @@ -58,10 +58,10 @@ TEST(scene3d_context_test, detach_parent_promotes_children_and_invalidates_depen scene.attach_renderable(grandparent); scene.attach_renderable(parent); scene.attach_renderable(child); - scene.set_display_parent(parent, grandparent); - scene.set_display_parent(child, parent); - scene.set_dependency_parent(parent, grandparent); - scene.set_dependency_parent(child, parent); + scene.set_parent(Scene_Base::Relationship::display, parent, grandparent); + scene.set_parent(Scene_Base::Relationship::display, child, parent); + scene.set_parent(Scene_Base::Relationship::dependency, parent, grandparent); + scene.set_parent(Scene_Base::Relationship::dependency, child, parent); scene.render(); scene.wait_for_render(); EXPECT_EQ(child->render_count, 1); diff --git a/Kernel/tests/renderive/scene/base/Scene_Base_Test.cpp b/Kernel/tests/renderive/scene/base/Scene_Base_Test.cpp index f83d542..52b77b4 100644 --- a/Kernel/tests/renderive/scene/base/Scene_Base_Test.cpp +++ b/Kernel/tests/renderive/scene/base/Scene_Base_Test.cpp @@ -124,11 +124,11 @@ TEST(scene_base_test, concurrent_render_and_topology_updates_remain_serialized) std::thread mutator([&] { for (int index = 0; index < 100; ++index) { if (index % 2 == 0) { - scene.set_dependency_parent(child, parent); - scene.set_display_parent(child, parent); + scene.set_parent(Scene_Base::Relationship::dependency, child, parent); + scene.set_parent(Scene_Base::Relationship::display, child, parent); } else { - scene.clear_dependency_parent(child); - scene.clear_display_parent(child); + scene.clear_parents(Scene_Base::Relationship::dependency, child); + scene.clear_parents(Scene_Base::Relationship::display, child); } scene.set_renderable_configuration(child, {.cache_enabled = index % 2 == 0}); } @@ -158,11 +158,11 @@ TEST(scene_base_test, topology_rejects_unattached_renderables) { auto parent = std::make_shared(scene); auto child = std::make_shared(scene); scene.attach_renderable(child); - EXPECT_THROW(scene.set_dependency_parent(child, parent).wait(), std::invalid_argument); - EXPECT_THROW(scene.set_display_parent(child, parent).wait(), std::invalid_argument); + EXPECT_THROW(scene.set_parent(Scene_Base::Relationship::dependency, child, parent).wait(), std::invalid_argument); + EXPECT_THROW(scene.set_parent(Scene_Base::Relationship::display, child, parent).wait(), std::invalid_argument); scene.attach_renderable(parent); auto detached_child = std::make_shared(scene); - EXPECT_THROW(scene.set_dependency_parent(detached_child, parent).wait(), std::invalid_argument); + EXPECT_THROW(scene.set_parent(Scene_Base::Relationship::dependency, detached_child, parent).wait(), std::invalid_argument); } TEST(scene_base_test, setting_same_topology_parent_is_a_noop) { Scene2D_Context<> scene; @@ -170,9 +170,9 @@ TEST(scene_base_test, setting_same_topology_parent_is_a_noop) { auto child = std::make_shared(scene); scene.attach_renderable(parent); scene.attach_renderable(child); - scene.set_dependency_parent(child, parent).wait(); + scene.set_parent(Scene_Base::Relationship::dependency, child, parent).wait(); const auto revision = child->prepare_revision(); - EXPECT_NO_THROW(scene.set_dependency_parent(child, parent).wait()); + EXPECT_NO_THROW(scene.set_parent(Scene_Base::Relationship::dependency, child, parent).wait()); EXPECT_EQ(child->prepare_revision(), revision); const auto topology = scene.topology_snapshot(); EXPECT_EQ(topology.dependency.size(), 2); @@ -193,9 +193,9 @@ TEST(scene_base_test, display_graph_rejects_cycles) { scene.attach_renderable(first); scene.attach_renderable(second); scene.attach_renderable(third); - scene.set_display_parent(second, first); - scene.add_display_parent(third, second); - EXPECT_THROW(scene.add_display_parent(first, third).wait(), std::invalid_argument); + scene.set_parent(Scene_Base::Relationship::display, second, first); + scene.add_parent(Scene_Base::Relationship::display, third, second); + EXPECT_THROW(scene.add_parent(Scene_Base::Relationship::display, first, third).wait(), std::invalid_argument); } TEST(scene_base_test, mutation_callback_receives_apply_failure) { Scene2D_Context<> scene; @@ -203,10 +203,11 @@ TEST(scene_base_test, mutation_callback_receives_apply_failure) { auto second = std::make_shared(scene); scene.attach_renderable(first).wait(); scene.attach_renderable(second).wait(); - scene.set_display_parent(second, first).wait(); + scene.set_parent(Scene_Base::Relationship::display, second, first).wait(); std::atomic callback_called{}; std::atomic callback_failed{}; - auto mutation = scene.set_display_parent(first, second, [&](std::exception_ptr exception) { + auto mutation = scene.set_parent(Scene_Base::Relationship::display, first, second); + mutation.then([&](std::exception_ptr exception) { callback_failed.store(static_cast(exception), std::memory_order_release); callback_called.store(true, std::memory_order_release); }); @@ -221,12 +222,12 @@ TEST(scene_base_test, atomic_attach_can_place_existing_display_children) { auto renderable = std::make_shared(scene); scene.attach_renderable(parent); scene.attach_renderable(axis); - scene.attach_renderable(renderable, { + auto mutation = scene.attach_renderable(renderable, { .display_parents = {parent}, .display_children = {axis}, .dependency_parents = {parent, axis} }); - scene.wait_for_mutations(); + mutation.wait(); const auto topology = scene.topology_snapshot(); const auto contains = [](const auto& relationships, const auto& child, const auto& parent_value) { @@ -275,16 +276,15 @@ TEST(scene_base_test, topology_snapshot_is_safe_during_topology_updates) { auto parent = std::make_shared(scene); auto child = std::make_shared(scene); scene.attach_renderable(parent); - scene.attach_renderable(child); - scene.wait_for_mutations(); + scene.attach_renderable(child).wait(); std::thread writer([&] { for (int index = 0; index < 500; ++index) { if (index % 2 == 0) { - scene.set_dependency_parent(child, parent); - scene.set_display_parent(child, parent); + scene.set_parent(Scene_Base::Relationship::dependency, child, parent); + scene.set_parent(Scene_Base::Relationship::display, child, parent); } else { - scene.clear_dependency_parent(child); - scene.clear_display_parent(child); + scene.clear_parents(Scene_Base::Relationship::dependency, child); + scene.clear_parents(Scene_Base::Relationship::display, child); } } }); @@ -363,7 +363,7 @@ TEST(scene_base_test, attach_builder_is_synchronous_before_runtime) { auto renderable = std::make_shared(scene); { auto builder = scene.attach_builder(); - builder.attach_renderable(renderable); + builder.attach(renderable); EXPECT_EQ(scene.renderable_count(), 1u); } scene.render(); diff --git a/Kernel/tests/renderive/threading/Threading_Contract_Test.cpp b/Kernel/tests/renderive/threading/Threading_Contract_Test.cpp index 7566516..b8c1127 100644 --- a/Kernel/tests/renderive/threading/Threading_Contract_Test.cpp +++ b/Kernel/tests/renderive/threading/Threading_Contract_Test.cpp @@ -556,8 +556,9 @@ TEST(threading_contract_test, scene_render_and_render_graph_rebuild_are_serializ start.store(true, std::memory_order_release); renderer.join(); rebuilder.join(); - scene.wait_for_mutations(); - EXPECT_EQ(executed.load(std::memory_order_acquire), render_count); + scene.render(); + scene.wait_for_render(); + EXPECT_EQ(executed.load(std::memory_order_acquire), render_count + 1); } TEST(threading_contract_test, real_time_data_updates_can_race_with_scene_destruction) { using Data = Latest_Real_Time_Data>; diff --git a/render_2D/scene/Scene.h b/render_2D/scene/Scene.h index 087975a..8f51b7b 100644 --- a/render_2D/scene/Scene.h +++ b/render_2D/scene/Scene.h @@ -210,7 +210,7 @@ public: auto root = detail::make_renderable_group(*this, true); root->set_object_name("root"); auto builder = this->attach_builder(); - builder.attach_renderable(root); + builder.attach(root); } [[nodiscard]] std::shared_ptr root_renderable() const { const auto topology = this->topology_snapshot(); @@ -236,12 +236,10 @@ public: attach_renderable_node(renderable, parent); return renderable; } - ::Scene_Base::Mutation remove_renderable( - const std::shared_ptr& renderable, - ::Scene_Base::Mutation_Callback callback = {}) { + ::Scene_Base::Mutation remove_renderable(const std::shared_ptr& renderable) { if (!renderable || renderable == root_renderable()) return {}; - return this->detach_renderable(renderable, std::move(callback)); + return this->detach_renderable(renderable); } void set_background_color(Color color) { if (background_color() == color) diff --git a/web_server/app/Web_Server.cpp b/web_server/app/Web_Server.cpp index d9129e3..f675a86 100644 --- a/web_server/app/Web_Server.cpp +++ b/web_server/app/Web_Server.cpp @@ -1,22 +1,17 @@ #include "Web_Server.h" - #include "Gallery_WebSocket_Controller.h" #include "Renderive_WebSocket_Controller.h" #include "Web_Performance_Log.h" - #include - #include #include #include #include #include #include - namespace renderive::web { - int run_web_server(std::uint16_t port, const std::filesystem::path& asset_root) { - const std::filesystem::path gallery_root = asset_root / "webapp_gallery"; + const std::filesystem::path gallery_root = asset_root / "webapp_gallery" / "dist"; const std::filesystem::path gallery_index = gallery_root / "index.html"; if (!std::filesystem::is_regular_file(gallery_index)) { std::cerr << "Renderive Gallery assets not found: " << gallery_index << '\n'; @@ -27,12 +22,12 @@ int run_web_server(std::uint16_t port, const std::filesystem::path& asset_root) const auto gallery_controller = std::make_shared(); const auto hardware_threads = std::max(2U, std::thread::hardware_concurrency()); std::cout << "Renderive WebSocket backend: ws://127.0.0.1:" << port - << "/renderive\n" - << "Renderive control gallery: ws://127.0.0.1:" << port - << "/renderive/gallery\n" - << "Renderive hosted gallery: http://127.0.0.1:" << port << "/\n" - << "Renderive performance log: " << web_performance_log_path().string() << "\n" - << "HTTP only serves static HTML/CSS/JS; events and pixels stay on WebSocket.\n"; + << "/renderive\n" + << "Renderive control gallery: ws://127.0.0.1:" << port + << "/renderive/gallery\n" + << "Renderive hosted gallery: http://127.0.0.1:" << port << "/\n" + << "Renderive performance log: " << web_performance_log_path().string() << "\n" + << "HTTP only serves static HTML/CSS/JS; events and pixels stay on WebSocket.\n"; auto& app = drogon::app(); const auto redirect_to_gallery = []( const drogon::HttpRequestPtr&, @@ -42,16 +37,15 @@ int run_web_server(std::uint16_t port, const std::filesystem::path& asset_root) app.registerHandler("/gallery", redirect_to_gallery, {drogon::Get}); app.registerHandler("/gallery/", redirect_to_gallery, {drogon::Get}); app - .registerController(controller) - .registerController(gallery_controller) - .setDocumentRoot(gallery_root.string()) - .setHomePage("index.html") - .setStaticFileHeaders({{"Cache-Control", "no-store"}}) - .addListener("127.0.0.1", port) - .setThreadNum(std::min(8U, hardware_threads)) - .setIdleConnectionTimeout(90) - .run(); + .registerController(controller) + .registerController(gallery_controller) + .setDocumentRoot(gallery_root.string()) + .setHomePage("index.html") + .setStaticFileHeaders({{"Cache-Control", "no-store"}}) + .addListener("127.0.0.1", port) + .setThreadNum(std::min(8U, hardware_threads)) + .setIdleConnectionTimeout(90) + .run(); return 0; } - } // namespace renderive::web diff --git a/web_server/server/main.cpp b/web_server/server/main.cpp index 2d595a1..5226834 100644 --- a/web_server/server/main.cpp +++ b/web_server/server/main.cpp @@ -7,12 +7,12 @@ namespace { std::uint16_t parse_port(int argc, char** argv) { constexpr std::uint16_t default_port = 8848; - if(argc != 3 || std::string_view(argv[1]) != "--port") + if (argc != 3 || std::string_view(argv[1]) != "--port") return default_port; unsigned value{}; const std::string_view text(argv[2]); const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), value); - if(error != std::errc{} || end != text.data() + text.size() || value == 0 || value > 65535) { + if (error != std::errc{} || end != text.data() + text.size() || value == 0 || value > 65535) { std::cerr << "Invalid port: " << text << '\n'; return 0; } @@ -21,9 +21,9 @@ std::uint16_t parse_port(int argc, char** argv) { } int main(int argc, char** argv) { const std::uint16_t port = parse_port(argc, argv); - if(port == 0) + if (port == 0) return 2; std::cout << "http://127.0.0.1:8848" << std::endl; const std::filesystem::path executable = std::filesystem::absolute(argv[0]); - return renderive::web::run_web_server(port, executable.parent_path()); + return renderive::web::run_web_server(port, executable.parent_path().parent_path().parent_path()); }