性能优化
This commit is contained in:
+30
-23
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user