修复若干bug

This commit is contained in:
2026-08-02 12:48:37 +08:00
parent 63b97c1bb2
commit 6d000f43b4
21 changed files with 495 additions and 233 deletions
+162 -4
View File
@@ -1,9 +1,11 @@
#include "Core/architecture/Frame_Scheduler.h"
#include "Core/architecture/Render_Lease.h"
#include "Core/architecture/Triple_Buffer.h"
#include "Core/plot/Latency_Eager_Refresh_Strategy.h"
#include "Core/plot/Plot_Frame_Pipeline.h"
#include "Core/plot/Plot_Core.h"
#include "Core/execution/Render_Executor.h"
#include "Core/primitive/Curve_Utils_p.h"
#include "Core/plottable/Performance_Overlay_p.h"
#include "Core/plottable/export.h"
#include "Core/render/Image.h"
@@ -13,6 +15,7 @@
#include <gtest/gtest.h>
#include <rigtorp/MPMCQueue.h>
#include <thread>
#include <utility>
namespace renderive {
namespace {
bool unique_roles(const Triple_Buffer_View& view) {
@@ -87,6 +90,22 @@ 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_Data : Typed_Render_Data<Lease_Test_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;
}
} // namespace
TEST(Renderive_Canvas, TextUsesPenAndFillUsesBrush) {
auto diagnostics = Canvas::default_font_diagnostics();
@@ -145,6 +164,48 @@ TEST(Renderive_Triple_Buffer, RoleSwapsAreTryOnlyAndDeterministic) {
ASSERT_TRUE(unique_roles(control.read_view()));
}
}
TEST(Renderive_State, CloseWakesBlockedAcquire) {
Triple_Role_Buffer_Control control;
control.reset();
auto held = control.acquire_role(State_Edit);
ASSERT_EQ(held.result, Triple_Buffer_Result::Success);
std::atomic_bool entered{false};
Triple_Buffer_Mark_Record blocked_result;
std::thread blocked([&]() {
entered.store(true, std::memory_order_release);
blocked_result = control.acquire_role(State_Edit);
});
ASSERT_TRUE(wait_until([&]() {
return entered.load(std::memory_order_acquire);
}));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
control.close();
blocked.join();
EXPECT_EQ(blocked_result.result, Triple_Buffer_Result::Cancelled);
control.unmark_use(held.lease);
}
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);
std::atomic_bool setter_returned{false};
std::thread setter([&]() {
data.set_state_value(&Lease_Test_State::value, 42);
setter_returned.store(true, std::memory_order_release);
});
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);
setter.join();
EXPECT_TRUE(setter_returned.load(std::memory_order_acquire));
EXPECT_EQ(data.edit_state_value(&Lease_Test_State::value), 42);
}
TEST(Renderive_Frame_Pipeline, PublishAndPaintUseMiddleVersionOnly) {
Plot_Frame_Pipeline pipeline;
auto lease = pipeline.try_acquire_render_frame();
@@ -154,8 +215,7 @@ TEST(Renderive_Frame_Pipeline, PublishAndPaintUseMiddleVersionOnly) {
lease.frame->metadata->render_begin_ns = 10;
lease.frame->metadata->render_end_ns = 20;
lease.frame->version.store(1, std::memory_order_release);
lease.reset();
auto published = pipeline.try_publish_rendered_frame(25);
auto published = pipeline.try_publish_rendered_frame(std::move(lease), 25);
EXPECT_TRUE(pipeline.middle_frame_ready());
EXPECT_TRUE(published.request_present);
EXPECT_EQ(published.dropped_frame_record, nullptr);
@@ -173,8 +233,7 @@ TEST(Renderive_Frame_Pipeline, PaintAcquireFailureKeepsPendingRequest) {
lease.frame->metadata->render_begin_ns = 10;
lease.frame->metadata->render_end_ns = 20;
lease.frame->version.store(1, std::memory_order_release);
lease.reset();
auto published = pipeline.try_publish_rendered_frame(25);
auto published = pipeline.try_publish_rendered_frame(std::move(lease), 25);
ASSERT_TRUE(published.request_present);
EXPECT_TRUE(pipeline.paint_request_pending.load(std::memory_order_acquire));
auto busy = pipeline.frame_control.try_acquire_role(Frame_Painting);
@@ -188,6 +247,26 @@ TEST(Renderive_Frame_Pipeline, PaintAcquireFailureKeepsPendingRequest) {
EXPECT_TRUE(consumed.present_request_was_pending);
EXPECT_TRUE(static_cast<bool>(consumed.lease));
}
TEST(Renderive_Frame_Pipeline, RequestPresentDoesNotWriteBusyMiddle) {
Plot_Frame_Pipeline pipeline;
auto lease = pipeline.try_acquire_render_frame();
ASSERT_TRUE(static_cast<bool>(lease));
ASSERT_TRUE(lease.frame->metadata);
lease.frame->metadata->frame_id = 1;
lease.frame->metadata->render_begin_ns = 10;
lease.frame->metadata->render_end_ns = 20;
lease.frame->version.store(1, std::memory_order_release);
auto published = pipeline.try_publish_rendered_frame(std::move(lease), 25);
ASSERT_TRUE(published.request_present);
pipeline.paint_request_pending.store(false, std::memory_order_release);
auto busy_middle = pipeline.frame_control.try_acquire_role(Frame_Middle);
ASSERT_EQ(busy_middle.result, Triple_Buffer_Result::Success);
auto blocked = pipeline.request_present(40);
EXPECT_FALSE(blocked.request_present);
EXPECT_FALSE(pipeline.paint_request_pending.load(std::memory_order_acquire));
pipeline.frame_control.unmark_use(busy_middle.lease);
}
TEST(Renderive_MPMCQueue, UsesTryOnlyBoundedCapacity) {
rigtorp::MPMCQueue<int> queue(2);
EXPECT_TRUE(queue.try_push(1));
@@ -200,6 +279,36 @@ TEST(Renderive_MPMCQueue, UsesTryOnlyBoundedCapacity) {
EXPECT_EQ(value, 2);
EXPECT_FALSE(queue.try_pop(value));
}
TEST(Renderive_Curve_Sampling, AllSamplesKeepsSourcePointCount) {
Axis_Mapping_2D mapping{
Axis_Frame_Snapshot{Orientation::Horizontal, Range{0.0, 4.0}, Range{0.0, 100.0}},
Axis_Frame_Snapshot{Orientation::Vertical, Range{0.0, 10.0}, Range{100.0, 0.0}}
};
std::array<double, 5> samples{0.0, 1.0, 2.0, 3.0, 4.0};
auto points = Curve_Utils::build_resampled_points(mapping, Range{0.0, 4.0}, 5, 5, false, Line_Interpolation_Mode::Linear_Value, [&samples](int i) {
return samples[static_cast<std::size_t>(i)];
});
ASSERT_EQ(points.size(), samples.size());
EXPECT_DOUBLE_EQ(points.front().x, 0.0);
EXPECT_DOUBLE_EQ(points.back().x, 100.0);
}
TEST(Renderive_Curve_Sampling, ReverseAxisEnvelopeKeepsSourceOrderInsideBucket) {
Axis_Mapping_2D mapping{
Axis_Frame_Snapshot{Orientation::Horizontal, Range{0.0, 4.0}, Range{1.0, 0.0}},
Axis_Frame_Snapshot{Orientation::Vertical, Range{-20.0, 20.0}, Range{20.0, 0.0}}
};
std::array<double, 5> samples{0.0, 10.0, -10.0, 20.0, -20.0};
auto points = Curve_Utils::build_pixel_envelope_points(mapping, Range{0.0, 4.0}, 5, 5, false, [&samples](int i) {
return samples[static_cast<std::size_t>(i)];
});
ASSERT_FALSE(points.empty());
double previous = -1.0;
for (const PointF& point : points) {
double coord = mapping.domain.pixel_to_coord(point.x);
EXPECT_GE(coord, previous);
previous = coord;
}
}
TEST(Renderive_Frame_Feedback, SelectsRenderPaintAndUserSources) {
Frame_Feedback_Controller controller;
controller.set_max_render_fps(200.0);
@@ -416,6 +525,55 @@ TEST(Renderive_Render_Executor, QueuedFramesRunRoundRobinByPlot) {
EXPECT_EQ(order[2], 3);
executor.shutdown();
}
TEST(Renderive_Render_Executor, ShutdownCompletesAcceptedQueuedFrameAsCancelled) {
Render_Executor executor(Render_Runtime_Config{1});
std::atomic_bool first_entered{false};
std::atomic_bool release_first{false};
std::atomic_bool queued_work_ran{false};
std::atomic_bool queued_completed{false};
ASSERT_TRUE(executor.try_submit(Render_Executor_Task{
1,
1,
Render_Task_Kind::Frame,
Task([&]() {
first_entered.store(true, std::memory_order_release);
while (!release_first.load(std::memory_order_acquire))
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}),
{},
Task([]() {}),
std::make_shared<Frame_Worker_Stats>(),
1,
true
}));
ASSERT_TRUE(wait_until([&]() {
return first_entered.load(std::memory_order_acquire);
}));
ASSERT_TRUE(executor.try_submit(Render_Executor_Task{
2,
1,
Render_Task_Kind::Frame,
Task([&]() {
queued_work_ran.store(true, std::memory_order_release);
}),
{},
Task([&]() {
queued_completed.store(true, std::memory_order_release);
}),
std::make_shared<Frame_Worker_Stats>(),
1,
true
}));
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));
release_first.store(true, std::memory_order_release);
shutdown_thread.join();
}
TEST(Renderive_Performance_Shower, ToggleDoesNotReenterRenderableTreeUnsafely) {
Plot_Core plot(std::make_unique<Latency_Eager_Refresh_Strategy>());
plot.init();