修改成异步的接口

This commit is contained in:
2026-08-13 08:45:18 +08:00
parent 6d2cf825ae
commit b964dcdcd9
23 changed files with 553 additions and 227 deletions
@@ -144,7 +144,7 @@ TEST(scene_base_test, detach_releases_renderable_from_the_live_model) {
scene.attach_renderable(renderable);
scene.render();
scene.wait_for_render();
scene.detach_renderable(renderable);
scene.detach_renderable(renderable).wait();
renderable.reset();
EXPECT_TRUE(weak.expired());
EXPECT_EQ(scene.renderable_count(), 0);
@@ -158,11 +158,11 @@ TEST(scene_base_test, topology_rejects_unattached_renderables) {
auto parent = std::make_shared<Scene_Base_Test_Renderable>(scene);
auto child = std::make_shared<Scene_Base_Test_Renderable>(scene);
scene.attach_renderable(child);
EXPECT_THROW(scene.set_dependency_parent(child, parent), std::invalid_argument);
EXPECT_THROW(scene.set_display_parent(child, parent), std::invalid_argument);
EXPECT_THROW(scene.set_dependency_parent(child, parent).wait(), std::invalid_argument);
EXPECT_THROW(scene.set_display_parent(child, parent).wait(), std::invalid_argument);
scene.attach_renderable(parent);
auto detached_child = std::make_shared<Scene_Base_Test_Renderable>(scene);
EXPECT_THROW(scene.set_dependency_parent(detached_child, parent), std::invalid_argument);
EXPECT_THROW(scene.set_dependency_parent(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_Base_Test_Renderable>(scene);
scene.attach_renderable(parent);
scene.attach_renderable(child);
scene.set_dependency_parent(child, parent);
scene.set_dependency_parent(child, parent).wait();
const auto revision = child->prepare_revision();
EXPECT_NO_THROW(scene.set_dependency_parent(child, parent));
EXPECT_NO_THROW(scene.set_dependency_parent(child, parent).wait());
EXPECT_EQ(child->prepare_revision(), revision);
const auto topology = scene.topology_snapshot();
EXPECT_EQ(topology.dependency.size(), 2);
@@ -195,7 +195,24 @@ TEST(scene_base_test, display_graph_rejects_cycles) {
scene.attach_renderable(third);
scene.set_display_parent(second, first);
scene.add_display_parent(third, second);
EXPECT_THROW(scene.add_display_parent(first, third), std::invalid_argument);
EXPECT_THROW(scene.add_display_parent(first, third).wait(), std::invalid_argument);
}
TEST(scene_base_test, mutation_callback_receives_apply_failure) {
Scene2D_Context<> scene;
auto first = std::make_shared<Scene_Base_Test_Renderable>(scene);
auto second = std::make_shared<Scene_Base_Test_Renderable>(scene);
scene.attach_renderable(first).wait();
scene.attach_renderable(second).wait();
scene.set_display_parent(second, first).wait();
std::atomic<bool> callback_called{};
std::atomic<bool> callback_failed{};
auto mutation = scene.set_display_parent(first, second, [&](std::exception_ptr exception) {
callback_failed.store(static_cast<bool>(exception), std::memory_order_release);
callback_called.store(true, std::memory_order_release);
});
EXPECT_THROW(mutation.wait(), std::invalid_argument);
EXPECT_TRUE(wait_until([&] { return callback_called.load(std::memory_order_acquire); }));
EXPECT_TRUE(callback_failed.load(std::memory_order_acquire));
}
TEST(scene_base_test, atomic_attach_can_place_existing_display_children) {
Scene2D_Context<> scene;
@@ -209,7 +226,7 @@ TEST(scene_base_test, atomic_attach_can_place_existing_display_children) {
.display_children = {axis},
.dependency_parents = {parent, axis}
});
scene.wait_for_mutations();
const auto topology = scene.topology_snapshot();
const auto contains = [](const auto& relationships, const auto& child,
const auto& parent_value) {
@@ -233,21 +250,21 @@ TEST(scene_base_test, failed_atomic_attach_rolls_back_every_topology) {
.display_parents = {parent},
.display_children = {parent},
.dependency_parents = {parent}
}), std::invalid_argument);
}).wait(), std::invalid_argument);
EXPECT_EQ(scene.renderable_count(), 1u);
EXPECT_NO_THROW(scene.attach_renderable(renderable, {
.display_parents = {parent},
.dependency_parents = {parent}
}));
}).wait());
EXPECT_EQ(scene.renderable_count(), 2u);
}
TEST(scene_base_test, topology_snapshot_retains_renderable_lifetime) {
Scene2D_Context<> scene;
auto renderable = std::make_shared<Scene_Base_Test_Renderable>(scene);
std::weak_ptr<Scene_Base_Test_Renderable> weak = renderable;
scene.attach_renderable(renderable);
scene.attach_renderable(renderable).wait();
const auto snapshot = scene.topology_snapshot();
scene.detach_renderable(renderable);
scene.detach_renderable(renderable).wait();
renderable.reset();
EXPECT_FALSE(weak.expired());
EXPECT_EQ(snapshot.renderables.size(), 1);
@@ -259,6 +276,7 @@ TEST(scene_base_test, topology_snapshot_is_safe_during_topology_updates) {
auto child = std::make_shared<Scene_Base_Test_Renderable>(scene);
scene.attach_renderable(parent);
scene.attach_renderable(child);
scene.wait_for_mutations();
std::thread writer([&] {
for (int index = 0; index < 500; ++index) {
if (index % 2 == 0) {
@@ -339,6 +357,25 @@ struct Scene_Base_Render_Execution_Renderable : Renderable_Base {
std::atomic<bool> nested_render_rejected{};
std::atomic<bool> mutation_completed{};
};
TEST(scene_base_test, attach_builder_is_synchronous_before_runtime) {
Scene2D_Context<> scene;
auto renderable = std::make_shared<Scene_Base_Test_Renderable>(scene);
{
auto builder = scene.attach_builder();
builder.attach_renderable(renderable);
EXPECT_EQ(scene.renderable_count(), 1u);
}
scene.render();
scene.wait_for_render();
EXPECT_EQ(renderable->render_count, 1);
}
TEST(scene_base_test, attach_builder_is_rejected_after_runtime_starts) {
Scene2D_Context<> scene;
auto renderable = std::make_shared<Scene_Base_Test_Renderable>(scene);
scene.attach_renderable(renderable).wait();
EXPECT_THROW(scene.attach_builder(), std::logic_error);
}
TEST(scene_base_test, taskflow_execution_can_mutate_live_model_but_cannot_submit_nested_render) {
Scene2D_Context<> scene;
auto renderable = std::make_shared<Scene_Base_Render_Execution_Renderable>(scene);