From b2eb81448b50e975426e8dd387395baceefc140b Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Mon, 3 Aug 2026 09:30:03 +0800 Subject: [PATCH] =?UTF-8?q?performance=20=E5=A4=8D=E5=88=B6=E6=8C=89?= =?UTF-8?q?=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/architecture/Plot_Render_Context.h | 2 + Core/plottable/Performance_Overlay.cpp | 47 +++++++++++++- Core/plottable/Performance_Overlay.h | 2 + Core/plottable/Performance_Overlay_p.h | 84 ++++++++++++++++++++++--- Core/plottable/Performance_Snapshot.h | 2 + Core/plottable/export.h | 1 + Qt/plot/Plot.cpp | 6 ++ 7 files changed, 136 insertions(+), 8 deletions(-) diff --git a/Core/architecture/Plot_Render_Context.h b/Core/architecture/Plot_Render_Context.h index 09f5f73..2c64174 100644 --- a/Core/architecture/Plot_Render_Context.h +++ b/Core/architecture/Plot_Render_Context.h @@ -24,6 +24,8 @@ struct Plot_Render_Context { std::shared_ptr frame_lifecycle_observer; std::mutex performance_update_callback_mutex; std::function performance_update_callback; + std::mutex performance_clipboard_callback_mutex; + std::function performance_clipboard_callback; std::atomic performance_plot_id{0}; mutable std::mutex performance_plot_name_mutex; std::string performance_plot_name; diff --git a/Core/plottable/Performance_Overlay.cpp b/Core/plottable/Performance_Overlay.cpp index 93a3f92..9a6e890 100644 --- a/Core/plottable/Performance_Overlay.cpp +++ b/Core/plottable/Performance_Overlay.cpp @@ -152,6 +152,25 @@ static std::function performance_update_callback(const std::shared_ptr

lock(ctx->performance_update_callback_mutex); return ctx->performance_update_callback; } +static std::function performance_clipboard_callback(const std::shared_ptr& ctx) { + if (!ctx) + return {}; + std::lock_guard lock(ctx->performance_clipboard_callback_mutex); + return ctx->performance_clipboard_callback; +} +static std::string performance_snapshot_text(const Performance_Display_Snapshot& snapshot) { + std::size_t size = 0; + for (const auto& line : snapshot.lines) + size += line.size() + 1; + std::string text; + text.reserve(size); + for (std::size_t i = 0; i < snapshot.lines.size(); ++i) { + if (i) + text.push_back('\n'); + text += snapshot.lines[i]; + } + return text; +} static void request_performance_update(const std::shared_ptr& worker) { if (!worker) return; @@ -198,6 +217,7 @@ static std::shared_ptr attach_performance_overlay_impl(Plot if (options) created_shower->set_options(*options); created_shower->set_update_callback(performance_update_callback(ctx)); + created_shower->set_clipboard_callback(performance_clipboard_callback(ctx)); std::atomic_store_explicit(&ctx->frame_lifecycle_observer, std::static_pointer_cast(created_shower), std::memory_order_release); return created_shower; } @@ -267,6 +287,18 @@ void set_performance_overlay_update_callback(Plot_Core& plot, std::functionset_update_callback(performance_update_callback(ctx)); } +void set_performance_overlay_clipboard_callback(Plot_Core& plot, std::function callback) { + auto ctx = Plot_Core_Context_Access::render_context(plot); + if (!ctx) + return; + { + std::lock_guard lock(ctx->performance_clipboard_callback_mutex); + ctx->performance_clipboard_callback = std::move(callback); + } + auto shower = performance_overlay_owner(ctx); + if (shower) + shower->set_clipboard_callback(performance_clipboard_callback(ctx)); +} void notify_performance_overlay_viewport_changed(Plot_Core& plot) { auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); if (!shower || !shower->enabled()) @@ -400,7 +432,17 @@ bool Performance_Overlay::handle_wheel(PointF position, double angle_delta_y, do } bool Performance_Overlay::handle_pointer_press(PointF position) { auto snapshot = display_snapshot(); - if (!snapshot || snapshot->max_scroll_offset <= 0.0) + if (!snapshot) + return false; + if (!snapshot->copy_button_rect.empty() && snapshot->copy_button_rect.contains(position)) { + d()->copy_to_clipboard(performance_snapshot_text(*snapshot)); + return true; + } + if (!snapshot->close_button_rect.empty() && snapshot->close_button_rect.contains(position)) { + set_enabled(false); + return true; + } + if (snapshot->max_scroll_offset <= 0.0) return false; if (snapshot->scroll_thumb_rect.contains(position)) { d()->metrics_worker->drag_anchor_y.store(position.y - snapshot->scroll_thumb_rect.y, std::memory_order_release); @@ -437,6 +479,9 @@ void Performance_Overlay::set_update_callback(std::function callback) { std::lock_guard lock(d()->metrics_worker->update_callback_mutex); d()->metrics_worker->update_callback = std::move(callback); } +void Performance_Overlay::set_clipboard_callback(std::function callback) { + d()->set_clipboard_callback(std::move(callback)); +} void write_frame_performance_log(std::shared_ptr frame) { if (!frame) return; diff --git a/Core/plottable/Performance_Overlay.h b/Core/plottable/Performance_Overlay.h index 6c7371a..5de4eb0 100644 --- a/Core/plottable/Performance_Overlay.h +++ b/Core/plottable/Performance_Overlay.h @@ -2,6 +2,7 @@ #include #include #include +#include #include "../architecture/Frame_Scheduler.h" #include "../base/Color.h" #include "../base/Geometry.h" @@ -63,6 +64,7 @@ public: bool handle_pointer_move(PointF position); bool handle_pointer_release(PointF position); void set_update_callback(std::function callback); + void set_clipboard_callback(std::function callback); private: Performance_Overlay_Private* d(); diff --git a/Core/plottable/Performance_Overlay_p.h b/Core/plottable/Performance_Overlay_p.h index e14cad7..7b13466 100644 --- a/Core/plottable/Performance_Overlay_p.h +++ b/Core/plottable/Performance_Overlay_p.h @@ -453,18 +453,45 @@ struct Performance_Layout_Builder { RectF panel_rect; RectF scroll_bar_rect; RectF scroll_thumb_rect; + RectF copy_button_rect; + RectF close_button_rect; double content_width{}; + double copy_button_width{}; + double close_button_width{}; + double toolbar_height{}; + double button_gap{4.0}; static std::string value(double number, int precision = 2) { return format_number(number, {.precision = precision}); } double panel_height(double line_height, Performance_Overlay_Render_State* state, int plot_height) const { return std::min(content_height(line_height, state), static_cast(std::max(0, plot_height))); } + double text_top(Performance_Overlay_Render_State* state) const { + return static_cast(state->top_margin) + toolbar_height + 4.0; + } double content_height(double line_height, Performance_Overlay_Render_State* state) const { - return line_height * static_cast(render_lines.size()) + state->top_margin + state->bottom_margin; + return line_height * static_cast(render_lines.size()) + text_top(state) + state->bottom_margin; } double content_viewport_height(Performance_Overlay_Render_State* state, double height) const { - return std::max(0.0, height - state->top_margin - state->bottom_margin); + return std::max(0.0, height - text_top(state) - state->bottom_margin); + } + void update_toolbar_button_rects(Performance_Overlay_Render_State* state) { + copy_button_rect = {}; + close_button_rect = {}; + if (panel_rect.empty() || toolbar_height <= 0.0) + return; + double right = panel_rect.width - static_cast(state->right_margin + state->scroll_bar_width + state->scroll_bar_margin * 2); + double left = static_cast(state->left_margin); + double available_width = std::max(0.0, right - left); + double close_width = std::min(close_button_width, available_width); + if (close_width <= 0.0) + return; + close_button_rect = RectF{right - close_width, static_cast(state->top_margin), close_width, toolbar_height}; + available_width = std::max(0.0, close_button_rect.x - button_gap - left); + double copy_width = std::min(copy_button_width, available_width); + if (copy_width <= 0.0) + return; + copy_button_rect = RectF{close_button_rect.x - button_gap - copy_width, static_cast(state->top_margin), copy_width, toolbar_height}; } void update_scroll_bar_rects(Performance_Overlay_Render_State* state, double max_scroll, double scroll, double visible_height, double total_height) { if (max_scroll <= 0.0 || panel_rect.width <= 0.0 || panel_rect.height <= 0.0 || total_height <= 0.0) { @@ -1038,6 +1065,7 @@ struct Performance_Layout_Builder { double max_width = 0.0; for (const auto& line : render_lines) max_width = std::max(max_width, line.width); + max_width = std::max(max_width, copy_button_width + button_gap + close_button_width); double measured_width = max_width + static_cast(state->left_margin + state->right_margin + state->scroll_bar_width + state->scroll_bar_margin * 2); held_width = std::max(held_width, measured_width); @@ -1201,6 +1229,8 @@ struct Performance_Overlay_Private { std::shared_ptr metrics_worker; mutable std::mutex identity_mutex; Performance_Log_Plot_Identity log_identity; + mutable std::mutex clipboard_callback_mutex; + std::function clipboard_callback; Performance_Overlay_Private() : metrics_worker(std::make_shared()) {} @@ -1233,6 +1263,21 @@ struct Performance_Overlay_Private { return log_identity; } + void set_clipboard_callback(std::function callback) { + std::lock_guard lock(clipboard_callback_mutex); + clipboard_callback = std::move(callback); + } + + void copy_to_clipboard(std::string text) const { + std::function callback; + { + std::lock_guard lock(clipboard_callback_mutex); + callback = clipboard_callback; + } + if (callback) + callback(std::move(text)); + } + static const char* feedback_outcome_event(Frame_Outcome outcome) { switch (outcome) { case Frame_Outcome::Presented: @@ -1334,6 +1379,11 @@ struct Performance_Overlay_Private { metrics = measure_canvas.measure_text("0123456789 ABC \xE4\xB8\xAD\xE6\x96\x87"); if (metrics.line_height() <= 0.0) metrics = measure_canvas.measure_text("M"); + Text_Metrics copy_metrics = measure_canvas.measure_text("Copy"); + Text_Metrics close_metrics = measure_canvas.measure_text("Close"); + layout.copy_button_width = std::max(48.0, copy_metrics.width + 16.0); + layout.close_button_width = std::max(48.0, close_metrics.width + 16.0); + layout.toolbar_height = std::max(20.0, metrics.line_height() + 4.0); layout.seed_static_field_widths(measure_canvas); layout.build_render_lines(measure_canvas); } @@ -1346,11 +1396,13 @@ struct Performance_Overlay_Private { if (panel_width <= 0.0 || height <= 0.0) return snapshot; double content_height = layout.content_height(line_height, &state); + double text_content_height = line_height * static_cast(layout.render_lines.size()); double content_viewport_height = layout.content_viewport_height(&state, height); - double max_scroll = std::max(0.0, content_height - content_viewport_height); + double max_scroll = std::max(0.0, text_content_height - content_viewport_height); double scroll = performance_clamp_scroll_offset(worker_state.scroll_offset.load(std::memory_order_acquire), max_scroll); worker_state.scroll_offset.store(scroll, std::memory_order_release); layout.panel_rect = RectF{0.0, 0.0, panel_width, height}; + layout.update_toolbar_button_rects(&state); Image image(static_cast(std::ceil(panel_width)), static_cast(std::ceil(height))); image.fill(Color::transparent()); Canvas canvas(image); @@ -1361,13 +1413,13 @@ struct Performance_Overlay_Private { canvas.set_pen(Pen{state.foreground}); RectF text_rect{ static_cast(state.left_margin), - static_cast(state.top_margin), + layout.text_top(&state), std::max(0.0, panel_width - state.left_margin - state.right_margin - state.scroll_bar_width - state.scroll_bar_margin * 2), - std::max(0.0, height - state.top_margin - state.bottom_margin) + std::max(0.0, height - layout.text_top(&state) - state.bottom_margin) }; canvas.set_clip_rect(text_rect); int first_line = std::max(0, static_cast(std::floor(scroll / line_height))); - double y = static_cast(state.top_margin) - (scroll - static_cast(first_line) * line_height); + double y = layout.text_top(&state) - (scroll - static_cast(first_line) * line_height); layout.text_runs.clear(); for (int i = first_line; i < static_cast(layout.render_lines.size()) && y < text_rect.bottom(); ++i, y += line_height) { if (y + line_height < text_rect.y) @@ -1375,7 +1427,23 @@ struct Performance_Overlay_Private { layout.draw_render_line(canvas, layout.render_lines[static_cast(i)], text_rect.x, y, line_height, state); } canvas.reset_clip(); - layout.update_scroll_bar_rects(&state, max_scroll, scroll, content_viewport_height, content_height); + if (!layout.copy_button_rect.empty()) { + canvas.set_brush(Brush{state.scroll_track, Brush_Style::Solid}); + canvas.fill_rect(layout.copy_button_rect); + canvas.set_pen(Pen{state.scroll_thumb}); + canvas.draw_rect(layout.copy_button_rect); + canvas.set_pen(Pen{state.foreground}); + canvas.draw_text(layout.copy_button_rect, Text_Align::Center, "Copy"); + } + if (!layout.close_button_rect.empty()) { + canvas.set_brush(Brush{state.scroll_track, Brush_Style::Solid}); + canvas.fill_rect(layout.close_button_rect); + canvas.set_pen(Pen{state.scroll_thumb}); + canvas.draw_rect(layout.close_button_rect); + canvas.set_pen(Pen{state.warning_color}); + canvas.draw_text(layout.close_button_rect, Text_Align::Center, "Close"); + } + layout.update_scroll_bar_rects(&state, max_scroll, scroll, content_viewport_height, text_content_height); if (!layout.scroll_bar_rect.empty()) { canvas.set_brush(Brush{state.scroll_track, Brush_Style::Solid}); canvas.fill_rect(layout.scroll_bar_rect); @@ -1386,6 +1454,8 @@ struct Performance_Overlay_Private { snapshot->destination_rect = layout.panel_rect; snapshot->scroll_track_rect = layout.scroll_bar_rect; snapshot->scroll_thumb_rect = layout.scroll_thumb_rect; + snapshot->copy_button_rect = layout.copy_button_rect; + snapshot->close_button_rect = layout.close_button_rect; snapshot->content_height = content_height; snapshot->scroll_offset = scroll; snapshot->max_scroll_offset = max_scroll; diff --git a/Core/plottable/Performance_Snapshot.h b/Core/plottable/Performance_Snapshot.h index 51a1cfb..f1ad6eb 100644 --- a/Core/plottable/Performance_Snapshot.h +++ b/Core/plottable/Performance_Snapshot.h @@ -29,6 +29,8 @@ struct Performance_Display_Snapshot { RectF viewport_rect; RectF scroll_track_rect; RectF scroll_thumb_rect; + RectF copy_button_rect; + RectF close_button_rect; Size viewport_size; double content_height{}; double scroll_offset{}; diff --git a/Core/plottable/export.h b/Core/plottable/export.h index 15fea94..46adfb7 100644 --- a/Core/plottable/export.h +++ b/Core/plottable/export.h @@ -23,6 +23,7 @@ LIB_DECL bool performance_overlay_enabled(const Plot_Core& plot); LIB_DECL void set_performance_overlay_enabled(Plot_Core& plot, bool enabled); LIB_DECL std::shared_ptr performance_overlay_snapshot(const Plot_Core& plot); LIB_DECL void set_performance_overlay_update_callback(Plot_Core& plot, std::function callback); +LIB_DECL void set_performance_overlay_clipboard_callback(Plot_Core& plot, std::function callback); LIB_DECL void notify_performance_overlay_viewport_changed(Plot_Core& plot); LIB_DECL bool performance_overlay_handle_wheel(Plot_Core& plot, PointF position, double angle_delta_y, double pixel_delta_y); LIB_DECL bool performance_overlay_handle_pointer_press(Plot_Core& plot, PointF position); diff --git a/Qt/plot/Plot.cpp b/Qt/plot/Plot.cpp index 4411e30..110938c 100644 --- a/Qt/plot/Plot.cpp +++ b/Qt/plot/Plot.cpp @@ -7,6 +7,8 @@ #include "Core/plottable/export.h" #include "Core/plot/Plot_Core_Access.h" #include "Core/architecture/Plot_Render_Context.h" +#include +#include #include #include #include @@ -60,6 +62,9 @@ void Abs_Plot_Private::attach_widget(Abs_Plot* plot) { guarded_plot->update(); }); }); + set_performance_overlay_clipboard_callback(*core, [](std::string text) { + QGuiApplication::clipboard()->setText(QString::fromUtf8(text.data(), static_cast(text.size()))); + }); } Abs_Plot::Abs_Plot(Abs_Plot_Private* private_data) @@ -73,6 +78,7 @@ Abs_Plot::Abs_Plot(Abs_Plot_Private* private_data) Abs_Plot::~Abs_Plot() { if (d) { set_performance_overlay_update_callback(*d->core, {}); + set_performance_overlay_clipboard_callback(*d->core, {}); d->core->deactivate_view(); delete d; d = nullptr;