策略抽离
This commit is contained in:
+104
-60
@@ -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>());
|
||||
|
||||
Reference in New Issue
Block a user