架构微调

This commit is contained in:
2026-08-11 11:01:42 +08:00
parent 37bb77d280
commit 0dcf1fbb35
6 changed files with 249 additions and 58 deletions
+32 -54
View File
@@ -279,8 +279,14 @@ public:
}
void set_state(Gallery_State state) {
const bool render_schedule_changed = frame_mode_ == Gallery_Frame_Mode::Low_Latency &&
(bool_value(state_, "frequency_limit_enabled") != bool_value(state, "frequency_limit_enabled") ||
number_value(state_, "max_render_fps") != number_value(state, "max_render_fps") ||
bool_value(state_, "consumer_feedback_enabled") != bool_value(state, "consumer_feedback_enabled"));
state_ = std::move(state);
apply_state();
if (render_schedule_changed)
render_history_.clear();
}
void resize(Size size) {
@@ -1572,75 +1578,47 @@ struct Gallery_Plot_Session::Impl {
void run_automatic_renderer(std::stop_token stop) {
using Clock = std::chrono::steady_clock;
std::unique_lock lock(mutex);
auto next_deadline = Clock::now();
std::uint64_t active_revision = std::numeric_limits<std::uint64_t>::max();
std::optional<Clock::time_point> last_render_started;
std::uint64_t active_generation = std::numeric_limits<std::uint64_t>::max();
while (!stop.stop_requested()) {
scheduler_condition.wait(lock, [this, &stop] {
return stop.stop_requested() ||
(scene && scene->can_render_automatically());
return stop.stop_requested() || (scene && scene->can_render_automatically());
});
if (stop.stop_requested())
break;
if (active_generation != scene_generation) {
active_generation = scene_generation;
last_render_started.reset();
}
if (foreground_waiters.load(std::memory_order_acquire) != 0) {
scheduler_condition.wait(lock, [this, &stop] {
return stop.stop_requested() ||
foreground_waiters.load(std::memory_order_acquire) == 0;
return stop.stop_requested() || foreground_waiters.load(std::memory_order_acquire) == 0;
});
continue;
}
const auto revision = scheduler_revision;
const auto cycle_started = Clock::now();
if (active_revision != revision) {
active_revision = revision;
next_deadline = cycle_started;
}
(void)scene->render_latest_frame();
const auto interval = std::chrono::nanoseconds(
std::max<std::uint64_t>(1, scene->kernel_refresh_interval_ns()));
if (next_deadline + interval < cycle_started)
next_deadline = cycle_started;
next_deadline += interval;
const auto deadline = next_deadline;
// A render that already consumed the whole interval used to let the
// worker immediately reacquire the session mutex. Heavy scenes
// (notably Constellation) could then starve pixel pulls, observer
// requests and shutdown indefinitely. Explicitly hand the mutex to
// registered foreground waiters; otherwise yield without using a
// timed wait (which Windows may round to ~15 ms).
if (Clock::now() >= deadline) {
if (foreground_waiters.load(std::memory_order_acquire) != 0) {
scheduler_condition.wait(lock, [this, &stop] {
return stop.stop_requested() ||
foreground_waiters.load(std::memory_order_acquire) == 0;
const auto interval = std::chrono::nanoseconds(std::max<std::uint64_t>(1, scene->kernel_refresh_interval_ns()));
const auto now = Clock::now();
const auto deadline = last_render_started ? *last_render_started + interval : now;
if (now < deadline) {
const auto spin_window = std::min(interval / 4, std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::microseconds(250)));
const auto coarse_deadline = deadline > now + spin_window ? deadline - spin_window : now;
if (now < coarse_deadline) {
scheduler_condition.wait_until(lock, coarse_deadline, [this, &stop, revision, active_generation] {
return stop.stop_requested() || scheduler_revision != revision || scene_generation != active_generation ||
foreground_waiters.load(std::memory_order_acquire) != 0 || !scene || !scene->can_render_automatically();
});
} else {
lock.unlock();
std::this_thread::yield();
lock.lock();
continue;
}
lock.unlock();
while (!stop.stop_requested() && Clock::now() < deadline)
std::this_thread::yield();
lock.lock();
continue;
}
const auto spin_window = std::min(
interval / 4,
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::microseconds(250)));
const auto coarse_deadline = deadline > cycle_started + spin_window ?
deadline - spin_window : cycle_started;
if (Clock::now() < coarse_deadline) {
const bool interrupted = scheduler_condition.wait_until(
lock, coarse_deadline, [this, &stop, revision] {
return stop.stop_requested() || scheduler_revision != revision ||
foreground_waiters.load(std::memory_order_acquire) != 0;
});
if (interrupted)
continue;
}
lock.unlock();
while (!stop.stop_requested() && Clock::now() < deadline)
std::this_thread::yield();
lock.lock();
const auto started = Clock::now();
(void)scene->render_latest_frame();
last_render_started = started;
}
}
+79
View File
@@ -975,6 +975,85 @@ TEST(RenderiveWebGallery, LowLatencyStrategyKeepsForegroundResponsiveUnderAggres
observer.at("limit_state") == "consumer_limited");
}
TEST(RenderiveWebGallery, FrequencyLimitCannotBeBypassedByForegroundRescheduleEvents) {
Gallery_Plot_Session session(true);
ASSERT_EQ(response_json(session.handle(gallery_request(
Gallery_Request_Kind::Open, open_message("spectrum", "low_latency")))).at("type"),
"case_state");
const auto patched = patch_controls(session, {
{"frequency_limit_enabled", true},
{"max_render_fps", 30.0},
{"consumer_feedback_enabled", false}
});
ASSERT_EQ(patched.at("type"), "case_state");
const auto& patched_observer = patched.at("telemetry").at("kernel_observer");
EXPECT_TRUE(patched_observer.at("frequency_limit_enabled").get<bool>());
EXPECT_FALSE(patched_observer.at("consumer_feedback_enabled").get<bool>());
EXPECT_EQ(patched_observer.at("target_interval_ns"), 33'333'333);
ASSERT_TRUE(wait_for_condition([&session] {
return successful_render_count(session) >= 3;
}));
const auto pointer = Web_Event_Adapter::decode(
R"({"category":"event","type":"pointer_move","x":320,"y":180,"button":"none","buttons":0,"modifiers":0})");
ASSERT_TRUE(pointer.has_value());
const auto before = successful_render_count(session);
const auto started = std::chrono::steady_clock::now();
const auto deadline = started + std::chrono::milliseconds(450);
while (std::chrono::steady_clock::now() < deadline) {
EXPECT_FALSE(session.handle(*pointer).has_value());
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
const auto elapsed = std::chrono::steady_clock::now() - started;
const auto after = successful_render_count(session);
const auto rendered = after - before;
EXPECT_GT(rendered, 0U);
EXPECT_LE(rendered, 16U);
const auto telemetry = observe_telemetry(session);
const auto& observer = telemetry.at("kernel_observer");
EXPECT_EQ(observer.at("target_interval_ns"), 33'333'333);
EXPECT_GE(observer.at("next_refresh_interval_ns").get<std::uint64_t>(), 33'333'333U);
EXPECT_GE(elapsed, std::chrono::milliseconds(450));
}
TEST(RenderiveWebGallery, ConsumerFeedbackCannotBeBypassedByForegroundRescheduleEvents) {
Gallery_Plot_Session session(true);
ASSERT_EQ(response_json(session.handle(gallery_request(
Gallery_Request_Kind::Open, open_message("spectrum", "low_latency")))).at("type"),
"case_state");
ASSERT_EQ(patch_controls(session, {
{"frequency_limit_enabled", false},
{"consumer_feedback_enabled", true}
}).at("type"), "case_state");
auto observed = response_json(session.handle(gallery_request(
Gallery_Request_Kind::Observe,
R"({"category":"event","type":"gallery_observe","client_metrics":{"transport_fps":25,"presentation_fps":25,"websocket_buffered_bytes":0,"changed_pixel_frames":1,"duplicate_pixel_frames":0,"frame_request_timeout_count":0,"frame_round_trip_ms":5,"display_interval_ms":40,"overwritten_pixel_frames":0,"last_pixel_receive_age_ms":1,"last_pixel_change_age_ms":1}})")));
auto observer = observed.at("telemetry").at("kernel_observer");
EXPECT_FALSE(observer.at("frequency_limit_enabled").get<bool>());
EXPECT_TRUE(observer.at("consumer_feedback_enabled").get<bool>());
EXPECT_EQ(observer.at("consumer_interval_ns"), 40'000'000);
ASSERT_TRUE(wait_for_condition([&session] {
return successful_render_count(session) >= 3;
}));
const auto pointer = Web_Event_Adapter::decode(
R"({"category":"event","type":"pointer_move","x":320,"y":180,"button":"none","buttons":0,"modifiers":0})");
ASSERT_TRUE(pointer.has_value());
const auto before = successful_render_count(session);
const auto started = std::chrono::steady_clock::now();
const auto deadline = started + std::chrono::milliseconds(450);
while (std::chrono::steady_clock::now() < deadline) {
EXPECT_FALSE(session.handle(*pointer).has_value());
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
const auto after = successful_render_count(session);
const auto rendered = after - before;
EXPECT_GT(rendered, 0U);
EXPECT_LE(rendered, 14U);
observed = observe_telemetry(session);
observer = observed.at("kernel_observer");
EXPECT_EQ(observer.at("consumer_interval_ns"), 40'000'000);
EXPECT_GE(observer.at("next_refresh_interval_ns").get<std::uint64_t>(), 40'000'000U);
}
TEST(RenderiveWebGallery, WebRuntimeMetricsPublishKernelSmoothedConsumerFeedbackWithoutTransportFpsCap) {
Gallery_Plot_Session session(true);
ASSERT_EQ(response_json(session.handle(gallery_request(