策略抽离

This commit is contained in:
2026-08-02 16:02:15 +08:00
parent dda25f9038
commit ffaca5b05f
55 changed files with 1431 additions and 886 deletions
+104 -60
View File
@@ -1,5 +1,6 @@
#include "Core/architecture/Frame_Scheduler.h"
#include "Core/flow/low_latency/Render_Lease.h"
#include "Core/architecture/Render_Lease.h"
#include "Core/flow/low_latency/Low_Latency_Roles.h"
#include "Core/flow/low_latency/Triple_Buffer.h"
#include "Core/flow/explicit/Explicit_Frame_Flow.h"
#include "Core/flow/low_latency/Low_Latency_Feedback_Controller.h"
@@ -121,22 +122,38 @@ bool valid_utf8(const std::string& text) {
return true;
}
struct Lease_Test_Owner : Renderable {};
struct Lease_Test_State : Render_State {
int value{};
};
struct Lease_Test_Input : Input_Data {};
struct Lease_Test_Owner : Renderable {
Render_Object_Owner create_render_state() override {
return make_render_object<Lease_Test_State>();
}
Render_Object_Owner create_input_data() override {
return make_render_object<Lease_Test_Input>();
}
};
struct Lease_Test_Data : Typed_Render_Data<Lease_Test_Owner, Lease_Test_State, Lease_Test_Input> {};
struct No_Input_Test_Owner : Renderable {};
struct No_Input_Test_Data : Typed_Render_Data<No_Input_Test_Owner, Lease_Test_State, Input_Data> {};
struct Runtime_Count_Owner : Renderable {
int state_create_count{};
int input_create_count{};
Render_Object_Owner create_render_state() override {
++state_create_count;
return make_render_object<Lease_Test_State>();
}
Render_Object_Owner create_input_data() override {
++input_create_count;
return make_render_object<Lease_Test_Input>();
}
};
struct Runtime_Count_Data : Typed_Render_Data<Runtime_Count_Owner, Lease_Test_State, Lease_Test_Input> {};
void init_lease_test_state(Lease_Test_Data& data) {
data.clear_state_buffers();
for (std::uint8_t role = 0; role < 3; ++role) {
data.state_buffer_owners[role] = make_render_object<Lease_Test_State>();
data.state_buffers[role] = data.state_buffer_owners[role].get<Render_State>();
}
data.state_buffers[State_Edit]->version = 1;
Lease_Test_Owner owner;
data.initialize_runtime(owner, Renderable_Runtime_Strategy::Low_Latency);
}
struct Flow_Test_Host : Frame_Flow_Host {
@@ -265,37 +282,35 @@ TEST(Renderive_State, CloseDoesNotReleaseActiveLease) {
EXPECT_FALSE(control.role_busy(State_Edit));
}
TEST(Renderive_State, ClearBuffersWaitsForActiveLease) {
TEST(Renderive_State, ReinitializeWaitsForActiveLease) {
Lease_Test_Data data;
init_lease_test_state(data);
auto held = data.state_control.acquire_role(State_Edit);
ASSERT_EQ(held.result, Triple_Buffer_Result::Success);
auto held = data.acquire_edit_state_read();
ASSERT_TRUE(static_cast<bool>(held));
std::atomic_bool clear_returned{false};
std::latch clear_started(1);
std::thread clearer([&]() {
clear_started.count_down();
data.clear_state_buffers();
clear_returned.store(true, std::memory_order_release);
std::atomic_bool reinitialize_returned{false};
std::latch reinitialize_started(1);
std::thread reinitializer([&]() {
Lease_Test_Owner owner;
reinitialize_started.count_down();
data.initialize_runtime(owner, Renderable_Runtime_Strategy::Explicit);
reinitialize_returned.store(true, std::memory_order_release);
});
clear_started.wait();
ASSERT_TRUE(wait_until([&]() {
return data.state_control.role_busy(State_Edit);
}));
EXPECT_FALSE(clear_returned.load(std::memory_order_acquire));
data.state_control.unmark_use(held.lease);
clearer.join();
EXPECT_TRUE(clear_returned.load(std::memory_order_acquire));
for (auto* state : data.state_buffers)
EXPECT_EQ(state, nullptr);
reinitialize_started.wait();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
EXPECT_FALSE(reinitialize_returned.load(std::memory_order_acquire));
held = {};
reinitializer.join();
EXPECT_TRUE(reinitialize_returned.load(std::memory_order_acquire));
EXPECT_EQ(data.runtime_strategy(), Renderable_Runtime_Strategy::Explicit);
}
TEST(Renderive_State, BusyEditSetterWaitsAndCommits) {
Lease_Test_Data data;
init_lease_test_state(data);
auto held = data.state_control.acquire_role(State_Edit);
ASSERT_EQ(held.result, Triple_Buffer_Result::Success);
auto held = data.acquire_edit_state_read();
ASSERT_TRUE(static_cast<bool>(held));
std::atomic_bool setter_returned{false};
std::thread setter([&]() {
@@ -304,7 +319,7 @@ TEST(Renderive_State, BusyEditSetterWaitsAndCommits) {
});
std::this_thread::sleep_for(std::chrono::milliseconds(20));
EXPECT_FALSE(setter_returned.load(std::memory_order_acquire));
data.state_control.unmark_use(held.lease);
held = {};
setter.join();
EXPECT_TRUE(setter_returned.load(std::memory_order_acquire));
@@ -313,8 +328,11 @@ TEST(Renderive_State, BusyEditSetterWaitsAndCommits) {
TEST(Renderive_State, GetterWaitsForWriter) {
Lease_Test_Data data;
init_lease_test_state(data);
auto held = data.state_control.acquire_role(State_Edit);
ASSERT_EQ(held.result, Triple_Buffer_Result::Success);
auto held = data.acquire_edit_state();
ASSERT_TRUE(static_cast<bool>(held));
auto* state = static_cast<Lease_Test_State*>(held.state);
ASSERT_NE(state, nullptr);
state->value = 77;
std::atomic_bool getter_started{false};
std::atomic_bool getter_returned{false};
@@ -329,8 +347,7 @@ TEST(Renderive_State, GetterWaitsForWriter) {
}));
std::this_thread::sleep_for(std::chrono::milliseconds(20));
EXPECT_FALSE(getter_returned.load(std::memory_order_acquire));
data.state_data_by_index(held.lease.index)->value = 77;
data.state_control.unmark_use(held.lease);
held = {};
getter.join();
EXPECT_TRUE(getter_returned.load(std::memory_order_acquire));
@@ -347,6 +364,31 @@ TEST(Renderive_Input_Runtime, ExactInputDataBindingSkipsAllocation) {
EXPECT_FALSE(static_cast<bool>(lease));
}
TEST(Renderive_Input_Runtime, ExplicitRuntimeAllocatesSingleStateAndInput) {
Runtime_Count_Owner owner;
Runtime_Count_Data explicit_data;
explicit_data.initialize_runtime(owner, Renderable_Runtime_Strategy::Explicit);
EXPECT_EQ(explicit_data.runtime_strategy(), Renderable_Runtime_Strategy::Explicit);
EXPECT_EQ(owner.state_create_count, 1);
EXPECT_EQ(owner.input_create_count, 1);
EXPECT_NE(explicit_data.edit_state(), nullptr);
{
Render_Edit_Lease<Runtime_Count_Data, Lease_Test_State> edit(&explicit_data);
ASSERT_TRUE(static_cast<bool>(edit));
edit->value = 42;
}
EXPECT_EQ(explicit_data.edit_state_value(&Lease_Test_State::value), 42);
Runtime_Count_Data low_latency_data;
low_latency_data.initialize_runtime(owner, Renderable_Runtime_Strategy::Low_Latency);
EXPECT_EQ(low_latency_data.runtime_strategy(), Renderable_Runtime_Strategy::Low_Latency);
EXPECT_EQ(owner.state_create_count, 4);
EXPECT_EQ(owner.input_create_count, 4);
EXPECT_NE(low_latency_data.edit_state(), nullptr);
EXPECT_NE(low_latency_data.ready_state(), nullptr);
EXPECT_NE(low_latency_data.render_state(), nullptr);
}
TEST(Renderive_Frame_Pipeline, PublishAndPaintUseMiddleVersionOnly) {
Low_Latency_Frame_Pipeline pipeline;
auto lease = pipeline.try_acquire_render_frame();
@@ -608,7 +650,7 @@ TEST(Renderive_Frame_Flow, ExplicitTicketsDrainIntoAcceptedFrame) {
complete_update_states(states, Update_Stage::Presented);
EXPECT_TRUE(ticket.is_complete());
}
TEST(Renderive_Render_Executor, FrameAdmissionIsBounded) {
TEST(Renderive_Render_Executor, ExecutorDoesNotOwnPlotAdmission) {
Render_Executor executor(Render_Runtime_Config{1});
std::atomic_bool entered{false};
std::atomic_bool release{false};
@@ -663,32 +705,36 @@ TEST(Renderive_Render_Executor, FrameAdmissionIsBounded) {
true
}));
auto duplicate_stat = std::make_shared<Frame_Worker_Stats>();
EXPECT_FALSE(executor.try_submit(Render_Executor_Task{
EXPECT_TRUE(executor.try_submit(Render_Executor_Task{
2,
2,
Render_Task_Kind::Frame,
Task([]() {}),
{},
Task([]() {}),
Task([&]() {
completed.fetch_add(1, std::memory_order_acq_rel);
}),
duplicate_stat,
1,
true
}));
auto overflow_stat = std::make_shared<Frame_Worker_Stats>();
EXPECT_FALSE(executor.try_submit(Render_Executor_Task{
EXPECT_TRUE(executor.try_submit(Render_Executor_Task{
4,
1,
Render_Task_Kind::Frame,
Task([]() {}),
{},
Task([]() {}),
Task([&]() {
completed.fetch_add(1, std::memory_order_acq_rel);
}),
overflow_stat,
1,
true
}));
release.store(true, std::memory_order_release);
ASSERT_TRUE(wait_until([&]() {
return completed.load(std::memory_order_acquire) == 3;
return completed.load(std::memory_order_acquire) == 5;
}));
executor.shutdown();
}
@@ -702,23 +748,23 @@ TEST(Renderive_Render_Executor, TaskflowTopologyRunsInFrameOrder) {
1,
Render_Task_Kind::Frame,
Task{},
[&sequence](tf::Taskflow& taskflow, tf::Task entry, tf::Task exit) {
auto prepare = taskflow.emplace([&sequence]() {
[&sequence](Render_Task_Graph& graph, Render_Task_Graph_Node entry, Render_Task_Graph_Node exit) {
auto prepare = graph.emplace(Task([&sequence]() {
int expected = 0;
sequence.compare_exchange_strong(expected, 1, std::memory_order_acq_rel);
});
auto draw = taskflow.emplace([&sequence]() {
}));
auto draw = graph.emplace(Task([&sequence]() {
int expected = 1;
sequence.compare_exchange_strong(expected, 2, std::memory_order_acq_rel);
});
auto finalize = taskflow.emplace([&sequence]() {
}));
auto finalize = graph.emplace(Task([&sequence]() {
int expected = 2;
sequence.compare_exchange_strong(expected, 3, std::memory_order_acq_rel);
});
entry.precede(prepare);
prepare.precede(draw);
draw.precede(finalize);
finalize.precede(exit);
}));
graph.precede(entry, prepare);
graph.precede(prepare, draw);
graph.precede(draw, finalize);
graph.precede(finalize, exit);
},
Task([&completed]() {
completed.store(true, std::memory_order_release);
@@ -734,7 +780,7 @@ TEST(Renderive_Render_Executor, TaskflowTopologyRunsInFrameOrder) {
EXPECT_EQ(stat->task_count, 3u);
executor.shutdown();
}
TEST(Renderive_Render_Executor, QueuedFramesRunRoundRobinByPlot) {
TEST(Renderive_Render_Executor, SubmittedFramesAllCompleteOnSingleWorker) {
Render_Executor executor(Render_Runtime_Config{1});
std::atomic_bool first_entered{false};
std::atomic_bool release_first{false};
@@ -789,12 +835,10 @@ TEST(Renderive_Render_Executor, QueuedFramesRunRoundRobinByPlot) {
ASSERT_TRUE(wait_until([&]() {
return order_index.load(std::memory_order_acquire) == 3;
}));
EXPECT_EQ(order[0], 1);
EXPECT_EQ(order[1], 2);
EXPECT_EQ(order[2], 3);
EXPECT_EQ(order_index.load(std::memory_order_acquire), 3);
executor.shutdown();
}
TEST(Renderive_Render_Executor, ShutdownCompletesAcceptedQueuedFrameAsCancelled) {
TEST(Renderive_Render_Executor, ShutdownWaitsForAcceptedQueuedFrame) {
Render_Executor executor(Render_Runtime_Config{1});
std::atomic_bool first_entered{false};
std::atomic_bool release_first{false};
@@ -836,12 +880,12 @@ TEST(Renderive_Render_Executor, ShutdownCompletesAcceptedQueuedFrameAsCancelled)
std::thread shutdown_thread([&]() {
executor.shutdown();
});
ASSERT_TRUE(wait_until([&]() {
return queued_completed.load(std::memory_order_acquire);
}));
EXPECT_FALSE(queued_work_ran.load(std::memory_order_acquire));
std::this_thread::sleep_for(std::chrono::milliseconds(20));
EXPECT_FALSE(queued_completed.load(std::memory_order_acquire));
release_first.store(true, std::memory_order_release);
shutdown_thread.join();
EXPECT_TRUE(queued_work_ran.load(std::memory_order_acquire));
EXPECT_TRUE(queued_completed.load(std::memory_order_acquire));
}
TEST(Renderive_Performance_Shower, ToggleDoesNotReenterRenderableTreeUnsafely) {
Plot_Core plot(std::make_unique<Low_Latency_Frame_Flow>());