修复了一些bug

This commit is contained in:
2026-08-11 08:32:25 +08:00
parent feb7c72e94
commit b1fca95250
6 changed files with 394 additions and 150 deletions
+150 -63
View File
@@ -12,11 +12,13 @@
#include <chrono>
#include <cmath>
#include <cstring>
#include <memory>
#include <set>
#include <string>
#include <string_view>
#include <thread>
#include <variant>
#include <vector>
namespace renderive::web {
namespace {
@@ -74,6 +76,31 @@ nlohmann::json patch_controls(Gallery_Plot_Session& session, nlohmann::json patc
Gallery_Request_Kind::Patch, request.dump())));
}
template <class Predicate>
bool wait_for_condition(Predicate&& predicate,
std::chrono::milliseconds timeout = std::chrono::milliseconds(750)) {
const auto deadline = std::chrono::steady_clock::now() + timeout;
do {
if (predicate())
return true;
std::this_thread::sleep_for(std::chrono::milliseconds(5));
} while (std::chrono::steady_clock::now() < deadline);
return predicate();
}
void set_gallery_view_active(Gallery_Plot_Session& session, bool active) {
const auto event = Web_Event_Adapter::decode(active ?
R"({"category":"event","type":"show"})" :
R"({"category":"event","type":"hide"})");
ASSERT_TRUE(event.has_value());
EXPECT_FALSE(session.handle(*event).has_value());
}
std::uint64_t successful_render_count(Gallery_Plot_Session& session) {
return observe_telemetry(session).at("performance").at("successful_render_count")
.get<std::uint64_t>();
}
TEST(RenderiveWebBridge, DecodesOnlyEventMessages) {
const auto resize = Web_Event_Adapter::decode(
R"({"category":"event","type":"resize","width":800,"height":450})");
@@ -648,7 +675,7 @@ TEST(RenderiveWebGallery, EveryCanvasAcceptsTheCompleteWebEventSetAndStillRender
for (const auto case_id : cases) {
for (const auto mode : modes) {
SCOPED_TRACE(std::string(case_id) + "/" + std::string(mode));
Gallery_Plot_Session session;
Gallery_Plot_Session session(mode == "low_latency");
ASSERT_EQ(response_json(session.handle(gallery_request(
Gallery_Request_Kind::Open, open_message(case_id, mode)))).at("type"),
"case_state");
@@ -704,41 +731,41 @@ TEST(RenderiveWebGallery, EveryIndependentCore2CanvasBuildsAndRenders) {
};
constexpr std::array<std::string_view, 3> modes{"manual", "low_latency", "playback"};
for (const std::string_view mode : modes) {
for (const std::string_view case_id : cases) {
Gallery_Plot_Session session;
const auto opened = session.handle(gallery_request(
Gallery_Request_Kind::Open, open_message(case_id, mode)));
ASSERT_TRUE(opened.has_value()) << case_id;
ASSERT_EQ(opened->type, Web_Response_Type::Json) << case_id;
const auto state = parse_json(opened->payload);
EXPECT_EQ(state.at("type"), "case_state") << case_id;
EXPECT_EQ(state.at("case").at("id").get<std::string>(), std::string(case_id)) << case_id;
EXPECT_EQ(state.at("frame_mode").at("id"), std::string(mode)) << case_id;
EXPECT_EQ(state.at("controls").at("descriptor").at("protocol"),
"adminive.resource") << case_id;
EXPECT_EQ(state.at("controls").at("view").at("protocol"),
"adminive.view") << case_id;
EXPECT_FALSE(session.handle(Viewport_Resize{{480, 280}}).has_value()) << case_id;
const auto frame = session.handle(Frame_Request{});
ASSERT_TRUE(frame.has_value()) << case_id;
ASSERT_EQ(frame->type, Web_Response_Type::Pixels) << case_id;
EXPECT_EQ(frame->payload.substr(0, 4), "RVP1") << case_id;
EXPECT_EQ(read_u32_le(frame->payload, 4), 480U) << case_id;
EXPECT_EQ(read_u32_le(frame->payload, 8), 280U) << case_id;
const auto observed = session.handle(gallery_request(
Gallery_Request_Kind::Observe,
R"({"category":"event","type":"gallery_observe"})"));
ASSERT_TRUE(observed.has_value()) << case_id;
const auto telemetry = parse_json(observed->payload);
EXPECT_EQ(telemetry.at("type"), "observer_state") << case_id;
EXPECT_EQ(telemetry.at("frame_mode").at("id"), std::string(mode)) << case_id;
EXPECT_TRUE(telemetry.at("telemetry").contains("kernel_observer")) << case_id;
EXPECT_TRUE(telemetry.at("telemetry").contains("performance")) << case_id;
EXPECT_TRUE(telemetry.at("telemetry").contains("data_shape")) << case_id;
EXPECT_GT(telemetry.at("telemetry").at("data_shape").at("rendered_elements")
.get<std::size_t>(), 0U) << case_id;
}
for (const std::string_view case_id : cases) {
Gallery_Plot_Session session(mode == "low_latency");
const auto opened = session.handle(gallery_request(
Gallery_Request_Kind::Open, open_message(case_id, mode)));
ASSERT_TRUE(opened.has_value()) << case_id;
ASSERT_EQ(opened->type, Web_Response_Type::Json) << case_id;
const auto state = parse_json(opened->payload);
EXPECT_EQ(state.at("type"), "case_state") << case_id;
EXPECT_EQ(state.at("case").at("id").get<std::string>(), std::string(case_id))
<< case_id;
EXPECT_EQ(state.at("frame_mode").at("id"), std::string(mode)) << case_id;
EXPECT_EQ(state.at("controls").at("descriptor").at("protocol"),
"adminive.resource") << case_id;
EXPECT_EQ(state.at("controls").at("view").at("protocol"),
"adminive.view") << case_id;
EXPECT_FALSE(session.handle(Viewport_Resize{{480, 280}}).has_value()) << case_id;
const auto frame = session.handle(Frame_Request{});
ASSERT_TRUE(frame.has_value()) << case_id;
ASSERT_EQ(frame->type, Web_Response_Type::Pixels) << case_id;
EXPECT_EQ(frame->payload.substr(0, 4), "RVP1") << case_id;
EXPECT_EQ(read_u32_le(frame->payload, 4), 480U) << case_id;
EXPECT_EQ(read_u32_le(frame->payload, 8), 280U) << case_id;
const auto observed = session.handle(gallery_request(
Gallery_Request_Kind::Observe,
R"({"category":"event","type":"gallery_observe"})"));
ASSERT_TRUE(observed.has_value()) << case_id;
const auto telemetry = parse_json(observed->payload);
EXPECT_EQ(telemetry.at("type"), "observer_state") << case_id;
EXPECT_EQ(telemetry.at("frame_mode").at("id"), std::string(mode)) << case_id;
EXPECT_TRUE(telemetry.at("telemetry").contains("kernel_observer")) << case_id;
EXPECT_TRUE(telemetry.at("telemetry").contains("performance")) << case_id;
EXPECT_TRUE(telemetry.at("telemetry").contains("data_shape")) << case_id;
EXPECT_GT(telemetry.at("telemetry").at("data_shape").at("rendered_elements")
.get<std::size_t>(), 0U) << case_id;
}
}
}
@@ -903,7 +930,7 @@ TEST(RenderiveWebGallery, ManualStrategyRunsOnlyExplicitPrepareRefreshRenderCycl
sequence_after_actions);
}
TEST(RenderiveWebGallery, LowLatencyStrategyKeepsForegroundResponsiveAt1000Hz) {
TEST(RenderiveWebGallery, LowLatencyStrategyKeepsForegroundResponsiveUnderAggressiveFrequency) {
Gallery_Plot_Session session(true);
ASSERT_EQ(response_json(session.handle(gallery_request(
Gallery_Request_Kind::Open, open_message("spectrum", "low_latency")))).at("type"),
@@ -913,7 +940,9 @@ TEST(RenderiveWebGallery, LowLatencyStrategyKeepsForegroundResponsiveAt1000Hz) {
R"({"category":"event","type":"gallery_patch","patch":{"max_render_fps":1000,"pixel_stream_fps":60}})"))).at("type"),
"case_state");
std::this_thread::sleep_for(std::chrono::milliseconds(80));
ASSERT_TRUE(wait_for_condition([&session] {
return successful_render_count(session) >= 4;
}));
std::string first_pixels;
std::string last_pixels;
for (int iteration = 0; iteration < 12; ++iteration) {
@@ -935,13 +964,11 @@ TEST(RenderiveWebGallery, LowLatencyStrategyKeepsForegroundResponsiveAt1000Hz) {
const auto telemetry = observe_telemetry(session);
const auto& observer = telemetry.at("kernel_observer");
EXPECT_EQ(observer.at("target_interval_ns"), 1'000'000);
EXPECT_TRUE(observer.at("limit_state") == "paint_limited" ||
EXPECT_GT(observer.at("latest_sequence").get<std::uint64_t>(), 4U);
EXPECT_GT(telemetry.at("performance").at("successful_render_count").get<std::uint64_t>(), 4U);
EXPECT_TRUE(observer.at("limit_state") == "frequency_limited" ||
observer.at("limit_state") == "paint_limited" ||
observer.at("limit_state") == "render_limited");
EXPECT_GT(observer.at("latest_sequence").get<std::uint64_t>(), 12U);
EXPECT_GT(telemetry.at("performance").at("measured_fps").get<double>(), 100.0);
EXPECT_TRUE(telemetry.at("low_latency_limit").at("paint_limited").get<bool>() ||
telemetry.at("low_latency_limit").at("render_limited").get<bool>());
EXPECT_FALSE(telemetry.at("low_latency_limit").at("frequency_limited").get<bool>());
}
TEST(RenderiveWebGallery, PlaybackStrategyQueuesWithoutAutomaticConsumptionAndReportsEmptyQueue) {
@@ -1023,38 +1050,98 @@ TEST(RenderiveWebGallery, ProductionLowLatencySessionRendersWithoutPixelPulls) {
Gallery_Request_Kind::Open, open_message("spectrum", "low_latency"))));
ASSERT_TRUE(session.handle(gallery_request(
Gallery_Request_Kind::Patch,
R"({"category":"event","type":"gallery_patch","patch":{"max_render_fps":1000}})")));
std::this_thread::sleep_for(std::chrono::milliseconds(350));
R"({"category":"event","type":"gallery_patch","patch":{"max_render_fps":120}})")));
ASSERT_TRUE(wait_for_condition([&session] {
return successful_render_count(session) >= 4;
}));
const auto observed = session.handle(gallery_request(
Gallery_Request_Kind::Observe,
R"({"category":"event","type":"gallery_observe","client_metrics":{"transport_fps":120,"presentation_fps":60,"websocket_buffered_bytes":0,"changed_pixel_frames":17,"duplicate_pixel_frames":3,"last_pixel_change_age_ms":12.5}})"));
R"({"category":"event","type":"gallery_observe","client_metrics":{"transport_fps":120,"presentation_fps":60,"websocket_buffered_bytes":0,"changed_pixel_frames":17,"duplicate_pixel_frames":3,"frame_request_timeout_count":2,"last_pixel_receive_age_ms":8.5,"last_pixel_change_age_ms":12.5}})"));
ASSERT_TRUE(observed.has_value());
const auto telemetry = parse_json(observed->payload).at("telemetry");
SCOPED_TRACE(telemetry.dump(2));
const auto& performance = telemetry.at("performance");
RecordProperty("configured_fps", telemetry.at("kernel_observer")
.at("configured_frequency_hz").get<double>());
RecordProperty("measured_fps", performance.at("measured_fps").get<double>());
RecordProperty("successful_render_count",
performance.at("successful_render_count").get<std::uint64_t>());
RecordProperty("limit_state",
telemetry.at("kernel_observer").at("limit_state").get<std::string>());
EXPECT_TRUE(performance.at("automatic_low_latency_scheduler").get<bool>());
EXPECT_GT(performance.at("successful_render_count").get<std::uint64_t>(), 100U);
EXPECT_GT(performance.at("measured_fps").get<double>(), 300.0);
EXPECT_DOUBLE_EQ(telemetry.at("kernel_observer").at("configured_frequency_hz"), 1000.0);
EXPECT_EQ(telemetry.at("kernel_observer").at("target_interval_ns"), 1'000'000);
EXPECT_TRUE(telemetry.at("low_latency_limit").at("paint_limited").get<bool>() ||
telemetry.at("low_latency_limit").at("render_limited").get<bool>());
EXPECT_FALSE(telemetry.at("low_latency_limit").at("frequency_limited").get<bool>());
EXPECT_GE(performance.at("successful_render_count").get<std::uint64_t>(), 4U);
EXPECT_GT(performance.at("measured_fps").get<double>(), 0.0);
EXPECT_DOUBLE_EQ(telemetry.at("kernel_observer").at("configured_frequency_hz"), 120.0);
EXPECT_EQ(telemetry.at("kernel_observer").at("target_interval_ns"), 8'333'333);
EXPECT_TRUE(telemetry.at("kernel_observer").at("limit_state") == "frequency_limited" ||
telemetry.at("kernel_observer").at("limit_state") == "paint_limited" ||
telemetry.at("kernel_observer").at("limit_state") == "render_limited");
EXPECT_DOUBLE_EQ(telemetry.at("client_performance").at("transport_fps"), 120.0);
EXPECT_DOUBLE_EQ(telemetry.at("client_performance").at("presentation_fps"), 60.0);
EXPECT_EQ(telemetry.at("client_performance").at("changed_pixel_frames"), 17);
EXPECT_EQ(telemetry.at("client_performance").at("duplicate_pixel_frames"), 3);
EXPECT_EQ(telemetry.at("client_performance").at("frame_request_timeout_count"), 2);
EXPECT_DOUBLE_EQ(telemetry.at("client_performance").at("last_pixel_receive_age_ms"), 8.5);
EXPECT_DOUBLE_EQ(telemetry.at("client_performance").at("last_pixel_change_age_ms"), 12.5);
}
TEST(RenderiveWebGallery, ProductionLowLatencyStopsWhileHiddenAndResumes) {
Gallery_Plot_Session session(true);
ASSERT_TRUE(session.handle(gallery_request(
Gallery_Request_Kind::Open, open_message("spectrum", "low_latency"))));
ASSERT_TRUE(session.handle(gallery_request(
Gallery_Request_Kind::Patch,
R"({"category":"event","type":"gallery_patch","patch":{"max_render_fps":120}})")));
ASSERT_TRUE(wait_for_condition([&session] {
return successful_render_count(session) >= 3;
}));
set_gallery_view_active(session, false);
const auto stopped_count = successful_render_count(session);
EXPECT_FALSE(session.handle(Frame_Request{}).has_value());
std::this_thread::sleep_for(std::chrono::milliseconds(80));
EXPECT_EQ(successful_render_count(session), stopped_count);
set_gallery_view_active(session, true);
EXPECT_TRUE(wait_for_condition([&session, stopped_count] {
return successful_render_count(session) > stopped_count;
}));
EXPECT_TRUE(session.handle(Frame_Request{}).has_value());
}
TEST(RenderiveWebGallery, ProductionLowLatencyOnlyRendersActiveSessions) {
constexpr std::size_t session_count = 8;
std::vector<std::unique_ptr<Gallery_Plot_Session>> sessions;
sessions.reserve(session_count);
for (std::size_t index = 0; index < session_count; ++index) {
auto session = std::make_unique<Gallery_Plot_Session>(true);
ASSERT_TRUE(session->handle(gallery_request(
Gallery_Request_Kind::Open, open_message("spectrum", "low_latency"))));
ASSERT_TRUE(session->handle(gallery_request(
Gallery_Request_Kind::Patch,
R"({"category":"event","type":"gallery_patch","patch":{"max_render_fps":60}})")));
if (index >= 2)
set_gallery_view_active(*session, false);
sessions.push_back(std::move(session));
}
for (std::size_t index = 0; index < 2; ++index) {
ASSERT_TRUE(wait_for_condition([&sessions, index] {
return successful_render_count(*sessions[index]) >= 2;
}));
}
std::array<std::uint64_t, session_count> hidden_counts{};
for (std::size_t index = 2; index < session_count; ++index)
hidden_counts[index] = successful_render_count(*sessions[index]);
std::this_thread::sleep_for(std::chrono::milliseconds(80));
for (std::size_t index = 2; index < session_count; ++index)
EXPECT_EQ(successful_render_count(*sessions[index]), hidden_counts[index]) << index;
set_gallery_view_active(*sessions[0], false);
set_gallery_view_active(*sessions[1], false);
const auto first_stopped = successful_render_count(*sessions[0]);
const auto second_stopped = successful_render_count(*sessions[1]);
set_gallery_view_active(*sessions[2], true);
set_gallery_view_active(*sessions[3], true);
EXPECT_TRUE(wait_for_condition([&sessions, baseline = hidden_counts[2]] {
return successful_render_count(*sessions[2]) > baseline;
}));
EXPECT_TRUE(wait_for_condition([&sessions, baseline = hidden_counts[3]] {
return successful_render_count(*sessions[3]) > baseline;
}));
std::this_thread::sleep_for(std::chrono::milliseconds(80));
EXPECT_EQ(successful_render_count(*sessions[0]), first_stopped);
EXPECT_EQ(successful_render_count(*sessions[1]), second_stopped);
}
void expect_low_latency_canvas_to_change(std::string_view case_id) {
SCOPED_TRACE(std::string(case_id));
Gallery_Plot_Session session(true);