性能优化

This commit is contained in:
2026-08-23 02:08:33 +08:00
parent 15107a01ad
commit b3af1c1b04
6 changed files with 571 additions and 325 deletions
+30 -23
View File
@@ -41,7 +41,7 @@ enum class Frame_Pacing_Mode : std::uint32_t {
maximum_rate
};
struct Frame_Pacing_Properties {
Frame_Pacing_Mode mode{Frame_Pacing_Mode::fixed_rate}; /* 控制后继 render 请求节奏的策略。 */
Frame_Pacing_Mode mode{Frame_Pacing_Mode::maximum_rate}; /* 控制后继 render 请求节奏的策略。 */
double fixed_rate_fps{30.0}; /* 固定频率策略的目标帧率,单位为 FPS。 */
double minimum_latency_headroom{1.25}; /* 最低延迟策略相对 P95 生成耗时的安全系数。 */
};
@@ -278,10 +278,9 @@ struct Plot::Private {
std::unordered_map<const void*, Frame_Handler> handlers; /* 以连接身份索引的完成帧订阅。 */
std::uint64_t next_frame_sequence{1}; /* 下一外部帧使用的单调序号;只在 strand 访问。 */
Frame_Policy frame_policy{}; /* 仅保存可编辑刷新策略,不保存衍生统计。 */
std::optional<Managed_Frame> active_frame{}; /* 当前由 Scene/异步后端借用指针的外部帧。 */
std::unordered_map<Render_Frame*, Managed_Frame> active_frames{}; /* Scene 借用中的外部帧,以回调原地址为唯一索引。 */
std::deque<const void*> pending_order{}; /* 按首次等待顺序保存连接身份,避免连接间饥饿。 */
std::unordered_map<const void*, Managed_Frame> pending_frames{}; /* 每个连接只保留最新一个尚未提交 Scene 的外部帧。 */
bool frame_in_flight{}; /* Scene 是否已有一次尚未完成回调的帧。 */
template <typename Scene_Object>
Private(asio::any_io_executor executor,
std::unique_ptr<Scene_Object> value_scene,
@@ -301,7 +300,8 @@ struct Plot::Private {
void request_frame(Frame_Submission submission);
void render_frame(Managed_Frame frame);
void publish_completed_frame(Render_Frame* frame);
void frame_completed();
void frame_completed(Render_Frame* frame);
[[nodiscard]] std::size_t frame_capacity() const noexcept;
};
nlohmann::json Plot::Private::schema() const {
auto result = view->schema();
@@ -329,7 +329,7 @@ Plot::Private::Managed_Frame Plot::Private::make_frame(Frame_Submission submissi
}
void Plot::Private::request_frame(Frame_Submission submission) {
auto frame = make_frame(std::move(submission));
if (frame_in_flight) {
if (active_frames.size() >= frame_capacity()) {
const auto found = pending_frames.find(frame.owner);
if (found != pending_frames.end()) found->second = std::move(frame);
else {
@@ -342,47 +342,54 @@ void Plot::Private::request_frame(Frame_Submission submission) {
render_frame(std::move(frame));
}
void Plot::Private::render_frame(Managed_Frame frame) {
frame_in_flight = true;
active_frame = std::move(frame);
auto& request = active_frame->request;
Render_Frame* identity = std::visit(
[](const auto& value) -> Render_Frame* { return value.get(); }, frame.frame);
auto [active, inserted] = active_frames.emplace(identity, std::move(frame));
if (!inserted) throw std::logic_error("Plot received a duplicate frame address");
auto& managed = active->second;
auto& request = managed.request;
request.width = std::clamp(request.width, 160U, 1920U);
request.height = std::clamp(request.height, 120U, 1080U);
view->update(request);
if (auto* scene_2d = std::get_if<std::unique_ptr<Scene_2D>>(&scene)) {
(*scene_2d)->set<&Render_Scene_2D::Prop::viewport>(Size{static_cast<int>(request.width), static_cast<int>(request.height)});
const auto result = (*scene_2d)->render(std::get<std::unique_ptr<Frame_2D>>(active_frame->frame).get());
const auto result = (*scene_2d)->render(std::get<std::unique_ptr<Frame_2D>>(managed.frame).get());
if (result != Render_Scene_2D::Render_Result::completed)
asio::post(strand, [this] {
frame_completed();
asio::post(strand, [this, identity] {
frame_completed(identity);
});
return;
}
auto& scene_3d = std::get<std::unique_ptr<Scene_3D>>(scene);
scene_3d->set<&Render_Scene_3D::Prop::viewport>(Extent{request.width, request.height});
const auto result = scene_3d->render(std::get<std::unique_ptr<Frame_3D>>(active_frame->frame).get());
const auto result = scene_3d->render(std::get<std::unique_ptr<Frame_3D>>(managed.frame).get());
if (result != Render_Scene_3D::Render_Result::submitted)
asio::post(strand, [this] {
frame_completed();
asio::post(strand, [this, identity] {
frame_completed(identity);
});
}
void Plot::Private::publish_completed_frame(Render_Frame* frame) {
if (!active_frame) throw std::logic_error("frame callback has no externally owned active frame");
const auto active = active_frames.find(frame);
if (active == active_frames.end())
throw std::logic_error("frame callback has no externally owned active frame");
auto& managed = active->second;
const auto pacing = frame_policy.snapshot();
if (auto* frame_2d = std::get_if<std::unique_ptr<Frame_2D>>(&active_frame->frame); frame_2d && frame_2d->get() == frame) publish(active_frame->owner, encode_frame(frame_2d->get(), active_frame->request.delivery, pacing));
else if (auto* frame_3d = std::get_if<std::unique_ptr<Frame_3D>>(&active_frame->frame); frame_3d && frame_3d->get() == frame) publish(active_frame->owner, encode_frame(frame_3d->get(), active_frame->request.delivery, pacing));
if (auto* frame_2d = std::get_if<std::unique_ptr<Frame_2D>>(&managed.frame); frame_2d && frame_2d->get() == frame) publish(managed.owner, encode_frame(frame_2d->get(), managed.request.delivery, pacing));
else if (auto* frame_3d = std::get_if<std::unique_ptr<Frame_3D>>(&managed.frame); frame_3d && frame_3d->get() == frame) publish(managed.owner, encode_frame(frame_3d->get(), managed.request.delivery, pacing));
else throw std::logic_error("frame callback does not match the externally owned active frame");
frame_completed();
frame_completed(frame);
}
void Plot::Private::frame_completed() {
active_frame.reset();
frame_in_flight = false;
while (!pending_order.empty()) {
std::size_t Plot::Private::frame_capacity() const noexcept {
return std::holds_alternative<std::unique_ptr<Scene_3D>>(scene) ? 3U : 1U;
}
void Plot::Private::frame_completed(Render_Frame* frame) {
active_frames.erase(frame);
while (active_frames.size() < frame_capacity() && !pending_order.empty()) {
const auto owner = pending_order.front();
pending_order.pop_front();
auto next = pending_frames.extract(owner);
if (next.empty()) continue;
render_frame(std::move(next.mapped()));
return;
}
}
Plot::Plot(asio::any_io_executor executor,