修复若干问题

This commit is contained in:
2026-08-15 03:21:01 +08:00
parent 04a9156cd6
commit 6655e8571b
54 changed files with 1525 additions and 397 deletions
+8
View File
@@ -1,5 +1,6 @@
set(Renderive_Web_dependencies global::drogon global::spdlog global::magic_enum)
if (RENDERIVE_BUILD_TESTS)
set(Renderive_Web_test_targets)
list(APPEND Renderive_Web_dependencies global::GTest)
endif ()
rcl_add_dependency_action_targets(Renderive_Web_env ${Renderive_Web_dependencies})
@@ -207,5 +208,12 @@ if (RENDERIVE_BUILD_TESTS)
target_link_libraries("${Renderive_Web_test_target}" PRIVATE Renderive_Web Adminive::Nlohmann GTest::gtest_main)
renderive_stage_render_3D_runtime("${Renderive_Web_test_target}")
add_test(NAME "${Renderive_Web_test_target}" COMMAND "${Renderive_Web_test_target}")
set_tests_properties("${Renderive_Web_test_target}" PROPERTIES LABELS "Renderive_Web")
list(APPEND Renderive_Web_test_targets "${Renderive_Web_test_target}")
endforeach ()
add_custom_target(Renderive_Web_check
COMMAND "${CMAKE_CTEST_COMMAND}" --test-dir "${CMAKE_BINARY_DIR}"
-C "$<CONFIG>" -L "^Renderive_Web$" --output-on-failure
DEPENDS ${Renderive_Web_test_targets}
USES_TERMINAL)
endif ()
+14 -14
View File
@@ -1,7 +1,7 @@
#pragma once
#include "common/Gallery_Types.h"
#include "renderive/scene/Scene2D_Context.hpp"
#include "render_2D/scene/Scene_Context.hpp"
#include <structive/property/property.hpp>
#include <cstdint>
@@ -83,19 +83,19 @@ struct Type_Descriptor<Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>> {
using renderive::web::gallery_action;
return object<T>(type_metadata(
gallery_action("mode_prepare", "准备帧",
"Manual_Refresh_Strategy::acquire_painter / Basic_Scene2D::prepare_frame",
"Manual_Refresh_Strategy::acquire_painter / Plot_Scene::prepare_frame",
"Manual_Refresh_Strategy"),
gallery_action("mode_refresh", "提交手动刷新",
"Manual_Refresh_Strategy::refresh / Basic_Scene2D::refresh_manual_frame",
"Manual_Refresh_Strategy::refresh / Plot_Scene::refresh_manual_frame",
"Manual_Refresh_Strategy"),
gallery_action("mode_render", "渲染已刷新帧",
"Manual_Refresh_Strategy::acquire_renderer / Basic_Scene2D::render_prepared_frame",
"Manual_Refresh_Strategy::acquire_renderer / Plot_Scene::render_prepared_frame",
"Manual_Refresh_Strategy", {}, {}, {}, 0.0, true),
gallery_action("mode_discard", "丢弃待刷新帧",
"Basic_Scene2D::discard_pending_frame",
"Plot_Scene::discard_pending_frame",
"Manual_Refresh_Strategy"),
gallery_action("mode_cycle", "执行完整手动帧周期",
"Basic_Scene2D::render_frame", "Manual_Refresh_Strategy",
"Plot_Scene::render_frame", "Manual_Refresh_Strategy",
{}, {}, {}, 0.0, true)));
}
};
@@ -107,15 +107,15 @@ struct Type_Descriptor<Low_Latency_Strategy<Scene_Frame, Mutex, Observer>> {
using renderive::web::gallery_action;
return object<T>(type_metadata(
gallery_action("mode_prepare", "发布低延迟帧",
"Low_Latency_Strategy::acquire_painter / Basic_Scene2D::prepare_frame",
"Low_Latency_Strategy::acquire_painter / Plot_Scene::prepare_frame",
"Low_Latency_Strategy"),
gallery_action("mode_render", "消费最新帧",
"Low_Latency_Strategy::acquire_renderer / Basic_Scene2D::render_prepared_frame",
"Low_Latency_Strategy::acquire_renderer / Plot_Scene::render_prepared_frame",
"Low_Latency_Strategy", {}, {}, {}, 0.0, true),
gallery_action("mode_discard", "丢弃陈旧帧",
"Basic_Scene2D::discard_pending_frame", "Low_Latency_Strategy"),
"Plot_Scene::discard_pending_frame", "Low_Latency_Strategy"),
gallery_action("mode_cycle", "执行低延迟帧周期",
"Basic_Scene2D::render_frame", "Low_Latency_Strategy",
"Plot_Scene::render_frame", "Low_Latency_Strategy",
{}, {}, {}, 0.0, true)));
}
};
@@ -127,16 +127,16 @@ struct Type_Descriptor<Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>> {
using renderive::web::gallery_action;
return object<T>(type_metadata(
gallery_action("mode_enqueue", "压入一帧",
"Flow_Refresh_Strategy::acquire_painter / Basic_Scene2D::prepare_frame",
"Flow_Refresh_Strategy::acquire_painter / Plot_Scene::prepare_frame",
"Flow_Refresh_Strategy"),
gallery_action("mode_enqueue_burst", "批量压入回放队列",
"Flow_Refresh_Strategy::acquire_painter / Basic_Scene2D::prepare_frame x N",
"Flow_Refresh_Strategy::acquire_painter / Plot_Scene::prepare_frame x N",
"Flow_Refresh_Strategy", {}, "number", "帧数", 8.0),
gallery_action("mode_dequeue", "消费队首帧",
"Flow_Refresh_Strategy::acquire_renderer / Basic_Scene2D::render_prepared_frame",
"Flow_Refresh_Strategy::acquire_renderer / Plot_Scene::render_prepared_frame",
"Flow_Refresh_Strategy", {}, {}, {}, 0.0, true),
gallery_action("mode_cycle", "执行回放帧周期",
"Basic_Scene2D::render_frame", "Flow_Refresh_Strategy",
"Plot_Scene::render_frame", "Flow_Refresh_Strategy",
{}, {}, {}, 0.0, true)));
}
};
+11 -11
View File
@@ -2,7 +2,7 @@
#include "Gallery_Enum.h"
#include "common/Gallery_Performance_Types.h"
#include "render_2D/scene/Scene.h"
#include "render_2D/scene/Plot_Scene.h"
#include "render_2D/renderable/Renderable.h"
#include "adminive/adminive.hpp"
@@ -66,15 +66,15 @@ struct Gallery_Render_Performance {
namespace adminive {
template <class Json>
struct Value_Adapter<renderive::Frame_Control_Mode, Json> {
struct Value_Adapter<renderive::Plot_Frame_Mode, Json> {
using value_type = std::string;
static std::string read(renderive::Frame_Control_Mode value) {
static std::string read(renderive::Plot_Frame_Mode value) {
return renderive::web::gallery_enum_id(value);
}
static void write(renderive::Frame_Control_Mode& target, std::string value) {
static void write(renderive::Plot_Frame_Mode& target, std::string value) {
const auto parsed =
renderive::web::gallery_enum_cast<renderive::Frame_Control_Mode>(value);
renderive::web::gallery_enum_cast<renderive::Plot_Frame_Mode>(value);
if (!parsed)
throw std::invalid_argument("unknown frame control mode: " + value);
target = *parsed;
@@ -82,14 +82,14 @@ struct Value_Adapter<renderive::Frame_Control_Mode, Json> {
};
template <>
struct Type_Descriptor<renderive::Frame_Observer_Snapshot> {
struct Type_Descriptor<renderive::Plot_Frame_Status> {
static auto get() {
using T = renderive::Frame_Observer_Snapshot;
using T = renderive::Plot_Frame_Status;
return object<T>("kernel_observer",
ADMINIVE_FIELD_LABEL(T, mode, "模式")
.enum_label<renderive::Frame_Control_Mode::Manual>("手动刷新")
.enum_label<renderive::Frame_Control_Mode::Low_Latency>("低延迟")
.enum_label<renderive::Frame_Control_Mode::Playback>("回放队列"),
.enum_label<renderive::Plot_Frame_Mode::Manual>("手动刷新")
.enum_label<renderive::Plot_Frame_Mode::Low_Latency>("低延迟")
.enum_label<renderive::Plot_Frame_Mode::Playback>("回放队列"),
ADMINIVE_FIELD_LABEL(T, last_event, "最近事件"),
ADMINIVE_FIELD_LABEL(T, limit_state, "当前瓶颈"),
ADMINIVE_FIELD_LABEL(T, frequency_hz, "配置频率"),
@@ -229,7 +229,7 @@ struct Type_Descriptor<renderive::Renderable_Observation> {
}
};
static_assert(Described_Type<renderive::Frame_Observer_Snapshot>);
static_assert(Described_Type<renderive::Plot_Frame_Status>);
static_assert(Described_Type<renderive::web::Gallery_Consumer_Feedback_Snapshot>);
static_assert(Described_Type<renderive::web::Gallery_Render_Performance>);
static_assert(Described_Type<renderive::web::Gallery_Client_Performance>);
+29 -29
View File
@@ -85,13 +85,13 @@ void append_session_actions(Gallery_Frame_Mode frame_mode,
std::vector<Action_Model>& result) {
switch (frame_mode) {
case Gallery_Frame_Mode::Manual:
append_gallery_actions<Gallery_Session_Control<Manual_Scene2D>>(result);
append_gallery_actions<Gallery_Session_Control<Plot_Scene>>(result);
return;
case Gallery_Frame_Mode::Low_Latency:
append_gallery_actions<Gallery_Session_Control<Scene2D>>(result);
append_gallery_actions<Gallery_Session_Control<Plot_Scene>>(result);
return;
case Gallery_Frame_Mode::Playback:
append_gallery_actions<Gallery_Session_Control<Playback_Scene2D>>(result);
append_gallery_actions<Gallery_Session_Control<Plot_Scene>>(result);
return;
}
throw std::invalid_argument("unknown gallery frame mode");
@@ -101,11 +101,11 @@ std::size_t session_control_count(std::string_view case_id, Gallery_Frame_Mode f
return 0;
switch (frame_mode) {
case Gallery_Frame_Mode::Manual:
return gallery_control_count<Gallery_Session_Control<Manual_Scene2D>>();
return gallery_control_count<Gallery_Session_Control<Plot_Scene>>();
case Gallery_Frame_Mode::Low_Latency:
return gallery_control_count<Gallery_Session_Control<Scene2D>>();
return gallery_control_count<Gallery_Session_Control<Plot_Scene>>();
case Gallery_Frame_Mode::Playback:
return gallery_control_count<Gallery_Session_Control<Playback_Scene2D>>();
return gallery_control_count<Gallery_Session_Control<Plot_Scene>>();
}
throw std::invalid_argument("unknown gallery frame mode");
}
@@ -298,16 +298,16 @@ bool observer_detail_field(std::string_view name) {
template <class Predicate>
Json observer_fields(Predicate&& predicate) {
const Json descriptor =
adminive::to_descriptor_json<Json, renderive::Frame_Observer_Snapshot>();
adminive::to_descriptor_json<Json, renderive::Plot_Frame_Status>();
Json result = Json::array();
for (const Json& field : descriptor.at("fields")) {
const std::string name = field.at("name");
if (predicate(name)) {
Json field = described_dashboard_field<renderive::Frame_Observer_Snapshot>(
Json field = described_dashboard_field<renderive::Plot_Frame_Status>(
"kernel_observer", name);
if (name == "frequency_hz") {
field["enabled_source"] =
described_source<renderive::Frame_Observer_Snapshot>(
described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "frequency_limit_enabled");
field["disabled_label"] = "已关闭";
} else if (name == "end_to_end_ns") {
@@ -345,17 +345,17 @@ Json dashboard_contract() {
Json point_pair{{"label", "输入→绘制"}, {"format", "pair"},
{"sources", Json::array({"data_shape.input_points", "data_shape.rendered_elements"})},
{"separator", ""}};
Json bottleneck = described_dashboard_field<renderive::Frame_Observer_Snapshot>(
Json bottleneck = described_dashboard_field<renderive::Plot_Frame_Status>(
"kernel_observer", "limit_state");
bottleneck["format"] = "duration_enum";
bottleneck["duration_sources"] = {
{"frequency_limited", described_source<renderive::Frame_Observer_Snapshot>(
{"frequency_limited", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "target_interval_ns")},
{"paint_limited", described_source<renderive::Frame_Observer_Snapshot>(
{"paint_limited", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "paint_duration_ns")},
{"render_limited", described_source<renderive::Frame_Observer_Snapshot>(
{"render_limited", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "render_duration_ns")},
{"consumer_limited", described_source<renderive::Frame_Observer_Snapshot>(
{"consumer_limited", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "consumer_interval_ns")}
};
Json dashboard{
@@ -392,12 +392,12 @@ Json dashboard_contract() {
dashboard_field("呈现 P95 ms", "client_performance.display_interval_p95_ms", "fixed", 2),
dashboard_field("呈现 P99 ms", "client_performance.display_interval_p99_ms", "fixed", 2),
dashboard_field("响应负载 MB/s", "performance.pixel_payload_megabytes_per_second", "fixed", 1),
described_dashboard_field<renderive::Frame_Observer_Snapshot>(
described_dashboard_field<renderive::Plot_Frame_Status>(
"kernel_observer", "pending_frame_count"),
described_dashboard_field<renderive::Frame_Observer_Snapshot>(
described_dashboard_field<renderive::Plot_Frame_Status>(
"kernel_observer", "dropped_frame_count"),
std::move(point_pair), std::move(bottleneck),
described_dashboard_field<renderive::Frame_Observer_Snapshot>(
described_dashboard_field<renderive::Plot_Frame_Status>(
"kernel_observer", "last_event"),
dashboard_field("像素超时", "client_performance.frame_request_timeout_count"),
dashboard_field("最近像素龄", "client_performance.last_pixel_receive_age_ms", "milliseconds", 0)
@@ -409,7 +409,7 @@ Json dashboard_contract() {
{"title", "内核帧观察器"},
{"source", "kernel_observer"},
{"descriptor", adminive::to_descriptor_json<
Json, renderive::Frame_Observer_Snapshot>()}
Json, renderive::Plot_Frame_Status>()}
}},
{"renderables_source", "renderable_observers"},
{"renderable_fields", Json::array({
@@ -439,26 +439,26 @@ Json dashboard_contract() {
}},
{"limits", {
{"aria_label", "低延迟限速来源"}, {"title", "限速来源"},
{"current_source", described_source<renderive::Frame_Observer_Snapshot>(
{"current_source", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "limit_state")},
{"active_label", "当前瓶颈"}, {"inactive_label", "未受限"},
{"disabled_label", "已关闭"},
{"fields", Json::array({
{{"label", "内核用户频率"}, {"active_value", "frequency_limited"},
{"duration_source", described_source<renderive::Frame_Observer_Snapshot>(
{"duration_source", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "target_interval_ns")},
{"enabled_source", described_source<renderive::Frame_Observer_Snapshot>(
{"enabled_source", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "frequency_limit_enabled")}},
{{"label", "内核绘制事件"}, {"active_value", "paint_limited"},
{"duration_source", described_source<renderive::Frame_Observer_Snapshot>(
{"duration_source", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "paint_duration_ns")}},
{{"label", "内核后台渲染"}, {"active_value", "render_limited"},
{"duration_source", described_source<renderive::Frame_Observer_Snapshot>(
{"duration_source", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "render_duration_ns")}},
{{"label", "消费者反馈"}, {"active_value", "consumer_limited"},
{"duration_source", described_source<renderive::Frame_Observer_Snapshot>(
{"duration_source", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "consumer_interval_ns")},
{"enabled_source", described_source<renderive::Frame_Observer_Snapshot>(
{"enabled_source", described_source<renderive::Plot_Frame_Status>(
"kernel_observer", "consumer_feedback_enabled")}}
})}
}},
@@ -466,11 +466,11 @@ Json dashboard_contract() {
{"aria_label", "内核低延迟全量统计"},
{"header", {
{"prefix", "内核"}, {"suffix", "观察器"}, {"event_label", "事件"},
{"mode", described_dashboard_field<renderive::Frame_Observer_Snapshot>(
{"mode", described_dashboard_field<renderive::Plot_Frame_Status>(
"kernel_observer", "mode")},
{"limit", described_dashboard_field<renderive::Frame_Observer_Snapshot>(
{"limit", described_dashboard_field<renderive::Plot_Frame_Status>(
"kernel_observer", "limit_state")},
{"event", described_dashboard_field<renderive::Frame_Observer_Snapshot>(
{"event", described_dashboard_field<renderive::Plot_Frame_Status>(
"kernel_observer", "last_event")}
}},
{"sections", Json::array({
@@ -507,7 +507,7 @@ Json dashboard_contract() {
}}
};
dashboard["observer"]["descriptor"] =
adminive::to_descriptor_json<Json, renderive::Frame_Observer_Snapshot>();
adminive::to_descriptor_json<Json, renderive::Plot_Frame_Status>();
dashboard["observer"]["consumer_feedback_descriptor"] =
adminive::to_descriptor_json<Json, Gallery_Consumer_Feedback_Snapshot>();
return dashboard;
+1 -1
View File
@@ -1,7 +1,7 @@
#pragma once
#include "render_2D/plottable/Performance_Overlay.h"
#include "render_2D/renderable/Renderable.h"
#include "render_2D/scene/Scene.h"
#include "render_2D/scene/Plot_Scene.h"
#include <memory>
#include <string>
namespace adminive {
@@ -43,7 +43,7 @@ struct Type_Descriptor<renderive::web::Gallery_Session_Control<Scene_Type>> {
static auto get() {
using T = renderive::web::Gallery_Session_Control<Scene_Type>;
using namespace renderive::web::gallery_adminive;
const auto dirty = [](T& value) { value.scene_.notify_model_dirty(); };
const auto dirty = [](T& value) { value.scene_.request_redraw(); };
return adminive::object<T>("scene", "场景",
callback_property<T, renderive::Color>(
"background_color", "背景颜色", Field_Control::color,
+3 -3
View File
@@ -49,7 +49,7 @@ double gaussian(double x, double center, double width) {
}
} // namespace
struct Web_Plot_Session::Impl {
Scene2D plot;
Plot_Scene plot;
renderive_Owner<Frequency_Axis> spectrum_frequency_axis;
renderive_Owner<Axis> spectrum_power_axis;
renderive_Owner<Frequency_Axis> waterfall_frequency_axis;
@@ -306,7 +306,7 @@ struct Web_Plot_Session::Impl {
}
else if constexpr (std::is_same_v<T, Set_Demo_Mode>) {
mode = value.mode;
plot.notify_model_dirty();
plot.request_redraw();
}
else if constexpr (std::is_same_v<T, Set_Center_Frequency>) {
set_center_frequency(value.megahertz);
@@ -316,7 +316,7 @@ struct Web_Plot_Session::Impl {
}
else if constexpr (std::is_same_v<T, Set_Gain>) {
gain_db = value.decibels;
plot.notify_model_dirty();
plot.request_redraw();
}
else if constexpr (std::is_same_v<T, Set_Max_Hold>) {
spectrum->set<&Spectrum::Properties::max_hold_visible>(value.enabled);
+31 -26
View File
@@ -143,12 +143,24 @@ std::optional<double> action_number(const Gallery_Action_Request& request) {
return std::nullopt;
return std::get<double>(*request.argument);
}
Plot_Frame_Mode plot_frame_mode(Gallery_Frame_Mode mode) {
switch (mode) {
case Gallery_Frame_Mode::Manual:
return Plot_Frame_Mode::Manual;
case Gallery_Frame_Mode::Low_Latency:
return Plot_Frame_Mode::Low_Latency;
case Gallery_Frame_Mode::Playback:
return Plot_Frame_Mode::Playback;
}
throw std::invalid_argument("unknown gallery frame mode");
}
} // namespace
template <class Scene_Type>
class Gallery_Scene final : public Gallery_Scene_Interface {
public:
Gallery_Scene(std::uint64_t session_id, std::string case_id, Gallery_Frame_Mode frame_mode, bool automatic_low_latency)
: session_id_(session_id), case_id_(std::move(case_id)), frame_mode_(frame_mode), automatic_low_latency_(automatic_low_latency), performance_started_(std::chrono::steady_clock::now()) {
: session_id_(session_id), case_id_(std::move(case_id)), frame_mode_(frame_mode), automatic_low_latency_(automatic_low_latency),
plot_(Plot_Scene_Options{plot_frame_mode(frame_mode)}), performance_started_(std::chrono::steady_clock::now()) {
plot_.init();
plot_.set_viewport_size({560, 320});
root_ = plot_.root_renderable();
@@ -172,7 +184,7 @@ public:
}
attach_performance_overlay(plot_);
apply_layout();
session_control_ = std::make_unique<Gallery_Session_Control<Scene_Type>>(
session_control_ = std::make_unique<Gallery_Session_Control<Plot_Scene>>(
plot_, *primary_, feedback_policy_);
register_controls();
set_performance_plot_name(plot_, case_id_ + "/" + gallery_enum_id(frame_mode_));
@@ -193,9 +205,9 @@ public:
[[nodiscard]] nlohmann::json controls() const {
return {
{"resources", controls_.resources()},
{"observers", controls_.observers(plot_)},
{"render_plan", controls_.render_plan(plot_)},
{"performance_capture", gallery_performance_capture_json(plot_)}
{"observers", controls_.observers(plot_.render_scene())},
{"render_plan", controls_.render_plan(plot_.render_scene())},
{"performance_capture", gallery_performance_capture_json(plot_.render_scene())}
};
}
[[nodiscard]] Gallery_Frame_Mode frame_mode() const noexcept {
@@ -262,7 +274,7 @@ public:
}
apply_layout();
update_model();
plot_.notify_model_dirty();
plot_.request_redraw();
return result;
}
void resize(int width, int height) override {
@@ -351,10 +363,10 @@ public:
last_action_result_.clear();
recognized = true;
if (request.id == "capture_next_frame") {
const auto state = plot_.capture_state();
const auto state = plot_.render_scene().capture_state();
if (state.enabled())
return "A performance capture session is already active";
const auto session_id = plot_.capture_next_frame();
const auto session_id = plot_.render_scene().capture_next_frame();
last_action_result_ = "capture session=" + std::to_string(session_id);
return "Capture next render frame requested";
}
@@ -364,10 +376,10 @@ public:
return invalid_argument(recognized);
const std::size_t count = static_cast<std::size_t>(
std::clamp(std::llround(*argument), 1LL, 1000LL));
const auto state = plot_.capture_state();
const auto state = plot_.render_scene().capture_state();
if (state.enabled())
return "A performance capture session is already active";
const auto session_id = plot_.capture_frames(count);
const auto session_id = plot_.render_scene().capture_frames(count);
last_action_result_ = "capture session=" + std::to_string(session_id) +
", frames=" + std::to_string(count);
return "Consecutive render frame capture requested";
@@ -557,7 +569,7 @@ public:
last_action_result_ = notice;
}
[[nodiscard]] std::string telemetry_json() const {
const auto observer = plot_.frame_observer_snapshot();
const auto observer = plot_.frame_status();
const auto telemetry_now = std::chrono::steady_clock::now();
const double elapsed_seconds = std::max(
std::chrono::duration<double>(telemetry_now - performance_started_).count(),
@@ -641,8 +653,8 @@ public:
{"kernel_observer", std::move(observer_data)},
{"consumer_feedback", std::move(consumer_feedback_data)},
{"client_performance", std::move(client_performance_data)},
{"renderable_observers", controls_.observers(plot_)},
{"performance_capture", gallery_performance_capture_json(plot_)},
{"renderable_observers", controls_.observers(plot_.render_scene())},
{"performance_capture", gallery_performance_capture_json(plot_.render_scene())},
{"last_action_result", last_action_result_}
};
if (primary_) {
@@ -925,7 +937,7 @@ private:
if (now - last_performance_log_ < std::chrono::seconds(1))
return;
last_performance_log_ = now;
const auto observer = plot_.frame_observer_snapshot();
const auto observer = plot_.frame_status();
const auto unix_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
@@ -1344,9 +1356,9 @@ private:
std::string case_id_;
Gallery_Frame_Mode frame_mode_ = Gallery_Frame_Mode::Low_Latency;
bool automatic_low_latency_{};
Scene_Type plot_;
Plot_Scene plot_;
Gallery_Feedback_Policy feedback_policy_;
std::unique_ptr<Gallery_Session_Control<Scene_Type>> session_control_;
std::unique_ptr<Gallery_Session_Control<Plot_Scene>> session_control_;
Gallery_Controls controls_;
std::chrono::steady_clock::time_point performance_started_;
std::chrono::steady_clock::time_point last_performance_log_;
@@ -1389,14 +1401,7 @@ private:
std::string last_action_result_;
};
std::unique_ptr<Gallery_Scene_Interface> make_gallery_scene_2d(std::uint64_t session_id, std::string case_id, Gallery_Frame_Mode mode, bool automatic_low_latency) {
switch (mode) {
case Gallery_Frame_Mode::Manual:
return std::make_unique<Gallery_Scene<Manual_Scene2D>>(session_id, std::move(case_id), mode, automatic_low_latency);
case Gallery_Frame_Mode::Low_Latency:
return std::make_unique<Gallery_Scene<Scene2D>>(session_id, std::move(case_id), mode, automatic_low_latency);
case Gallery_Frame_Mode::Playback:
return std::make_unique<Gallery_Scene<Playback_Scene2D>>(session_id, std::move(case_id), mode, automatic_low_latency);
}
throw std::invalid_argument("unknown gallery frame mode");
return std::make_unique<Gallery_Scene>(session_id, std::move(case_id),
mode, automatic_low_latency);
}
}
+40 -2
View File
@@ -13,6 +13,7 @@
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <deque>
#include <memory>
#include <optional>
#include <stdexcept>
@@ -219,6 +220,9 @@ public:
successful_render_count_ = 0;
pixel_frame_count_ = 0;
last_render_ms_ = 0.0;
last_pixel_encode_ms_ = 0.0;
render_history_.clear();
pixel_history_.clear();
last_event_ = "monitoring_reset";
}
@@ -325,6 +329,7 @@ public:
last_pixel_encode_ms_ = std::chrono::duration<double, std::milli>(
encode_finished - encode_started)
.count();
record_sample(pixel_history_, encode_finished);
}
[[nodiscard]] std::string action(const Gallery_Action_Request& request,
@@ -433,19 +438,21 @@ public:
[[nodiscard]] std::string telemetry_json() const override {
const auto status = scene_.frame_status();
const auto now = std::chrono::steady_clock::now();
return nlohmann::json{
{"kernel_observer",
{{"mode", mode_id(frame_mode_)},
{"last_event", last_event_},
{"pending_frame_count", status.pending_frame_count},
{"dropped_frame_count", status.dropped_frame_count}}},
{"render_performance",
{"performance",
{{"render_attempt_count", render_attempt_count_},
{"successful_render_count", successful_render_count_},
{"failed_render_count",
render_attempt_count_ - successful_render_count_},
{"measured_fps", recent_rate(render_history_, now)},
{"last_render_ms", last_render_ms_},
{"pixel_response_fps", 0.0},
{"pixel_response_fps", recent_rate(pixel_history_, now)},
{"last_pixel_encode_ms", last_pixel_encode_ms_},
{"last_pixel_bytes", last_pixel_bytes_},
{"automatic_low_latency_scheduler", can_render_automatically()}}},
@@ -462,6 +469,31 @@ public:
}
private:
using Performance_Clock = std::chrono::steady_clock;
static void record_sample(std::deque<Performance_Clock::time_point>& samples,
Performance_Clock::time_point now) {
samples.push_back(now);
const auto cutoff = now - std::chrono::seconds(2);
while (!samples.empty() && samples.front() < cutoff)
samples.pop_front();
}
static double recent_rate(
const std::deque<Performance_Clock::time_point>& samples,
Performance_Clock::time_point now) {
const auto cutoff = now - std::chrono::seconds(1);
const auto first = std::find_if(samples.begin(), samples.end(),
[cutoff](auto sample) {
return sample >= cutoff;
});
const auto count = std::distance(first, samples.end());
if (count < 2)
return 0.0;
const double seconds = std::chrono::duration<double>(samples.back() - *first).count();
return seconds > 0.0 ? static_cast<double>(count - 1) / seconds : 0.0;
}
static std::vector<Point> initial_points() {
return {{{-0.62F, -0.10F, 0.0F}, {255, 32, 24, 255}, 74.0F},
{{0.0F, -0.10F, 0.0F}, {24, 255, 36, 255}, 74.0F},
@@ -513,6 +545,8 @@ private:
std::chrono::steady_clock::now() - started)
.count();
successful_render_count_ += rendered ? 1U : 0U;
if (rendered)
record_sample(render_history_, std::chrono::steady_clock::now());
return rendered;
}
@@ -524,6 +558,8 @@ private:
std::chrono::steady_clock::now() - started)
.count();
successful_render_count_ += rendered ? 1U : 0U;
if (rendered)
record_sample(render_history_, std::chrono::steady_clock::now());
return rendered;
}
@@ -546,6 +582,8 @@ private:
double last_render_ms_{};
double last_pixel_encode_ms_{};
std::size_t last_pixel_bytes_{};
std::deque<Performance_Clock::time_point> render_history_;
std::deque<Performance_Clock::time_point> pixel_history_;
std::string last_event_{"initialized"};
std::string last_action_result_;
};
@@ -236,6 +236,15 @@ TEST(RenderiveWebDatovizGallery, PublishesRealRgbaAndRendersAfterResize) {
ASSERT_EQ(invoke_action(session, "mode_render").at("type"), "case_state");
const std::string resized = request_pixels(session);
expect_actual_datoviz_pixels(resized, 900, 500);
const auto observed = response_json(session.handle(gallery_request(
Gallery_Request_Kind::Observe,
R"({"category":"event","type":"gallery_observe","client_metrics":{}})")));
const auto& performance = observed.at("telemetry").at("performance");
EXPECT_GT(performance.at("measured_fps").get<double>(), 0.0);
EXPECT_GT(performance.at("pixel_response_fps").get<double>(), 0.0);
EXPECT_GT(performance.at("last_render_ms").get<double>(), 0.0);
EXPECT_GT(performance.at("last_pixel_encode_ms").get<double>(), 0.0);
}
TEST(RenderiveWebDatovizGallery, AutomaticLowLatencyFramesCarryAnimatedState) {