From 173652875d6cab5c64dec7c6202d1260f491ac50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Marinier?= Date: Thu, 21 Jul 2016 10:48:16 +0200 Subject: [PATCH] Reduced CPU usage of Audacity when idle. Before this change, an idle Audacity on a recent Linux laptop uses 6% of CPU. This is because DrawOverlays() is called every 50ms through a timer in TrackPanel, which instanciates a wxClientDC. This is quite expensive and dominates the profiles: creating wxClientDCs is 84% of the CPU audacity spends while idle, according to callgrind. With this change, we avoid creating wxClientDCs when it's not necessary. After this change, and idle Audacity consumes 1.6% of CPU, and most of the time is spend in gtk/wxwidgets processing events. Here are the perf stats of an idle Audacity before the change: 524.425485 task-clock (msec) # 0.060 CPUs utilized 825 context-switches # 0.002 M/sec 36 cpu-migrations # 0.069 K/sec 0 page-faults # 0.000 K/sec 1,198,433,346 cycles # 2.285 GHz 1,243,329,771 instructions # 1.04 insn per cycle 308,073,049 branches # 587.449 M/sec 5,801,494 branch-misses # 1.88% of all branches 8.808129958 seconds time elapsed and after: 149.110455 task-clock (msec) # 0.016 CPUs utilized 908 context-switches # 0.006 M/sec 32 cpu-migrations # 0.215 K/sec 0 page-faults # 0.000 K/sec 313,372,582 cycles # 2.102 GHz 167,401,770 instructions # 0.53 insn per cycle 41,857,947 branches # 280.718 M/sec 1,189,566 branch-misses # 2.84% of all branches 9.076940003 seconds time elapsed --- src/widgets/OverlayPanel.cpp | 23 +++++++++++++++++------ src/widgets/OverlayPanel.h | 13 +++++++++---- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/widgets/OverlayPanel.cpp b/src/widgets/OverlayPanel.cpp index 1e6e8f9c8..bbd5e2d30 100644 --- a/src/widgets/OverlayPanel.cpp +++ b/src/widgets/OverlayPanel.cpp @@ -37,11 +37,11 @@ void OverlayPanel::ClearOverlays() mOverlays.clear(); } -void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC) +void OverlayPanel::DrawOverlays(bool repaint_all, wxDC *pDC) { size_t n_pairs = mOverlays.size(); - std::vector< std::pair > pairs; + std::vector< std::pair > pairs; pairs.reserve(n_pairs); // Find out the rectangles and outdatedness for each overlay @@ -53,12 +53,15 @@ void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC) // If not, then whatever is outdated, and whatever will be damaged by // undrawing. // By redrawing only what needs it, we avoid flashing things like - // the cursor that are drawn with invert. - if (!repaint) { + // the cursor that are drawn with invert, and also avoid + // unnecessary work. + bool some_overlays_need_repainting = repaint_all; + if (!repaint_all) { bool done; do { done = true; for (size_t ii = 0; ii < n_pairs; ++ii) { + some_overlays_need_repainting |= pairs[ii].second; for (size_t jj = ii + 1; jj < n_pairs; ++jj) { if (pairs[ii].second != pairs[jj].second && pairs[ii].first.Intersects(pairs[jj].first)) { @@ -70,6 +73,14 @@ void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC) } while (!done); } + if (!some_overlays_need_repainting) { + // This function (OverlayPanel::DrawOverlays()) is called at + // fairly high frequency through a timer in TrackPanel. In case + // there is nothing to do, we exit early because creating the + // wxClientDC below is expensive, at least on Linux. + return; + } + Maybe myDC; auto &dc = pDC ? *pDC : (myDC.create(this), *myDC); @@ -77,7 +88,7 @@ void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC) bool done = true; auto it2 = pairs.begin(); for (auto pOverlay : mOverlays) { - if (repaint || it2->second) { + if (repaint_all || it2->second) { done = false; pOverlay->Erase(dc, GetBackingDC()); } @@ -88,7 +99,7 @@ void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC) if (!done) { it2 = pairs.begin(); for (auto pOverlay : mOverlays) { - if (repaint || it2->second) { + if (repaint_all || it2->second) { // Guarantee a clean state of the dc each pass: ADCChanger changer{ &dc }; diff --git a/src/widgets/OverlayPanel.h b/src/widgets/OverlayPanel.h index b5b3fa8d9..208786544 100644 --- a/src/widgets/OverlayPanel.h +++ b/src/widgets/OverlayPanel.h @@ -22,7 +22,7 @@ public: // default as for wxPanel: long style = wxTAB_TRAVERSAL | wxNO_BORDER); - // Register and unregister overlay objects. + // Registers and unregisters overlay objects. // The sequence in which they were registered is the sequence in // which they are painted. // OverlayPanel is not responsible for their memory management. @@ -31,9 +31,14 @@ public: bool RemoveOverlay(Overlay *pOverlay); void ClearOverlays(); - // Erase and redraw things like the cursor, cheaply and directly to the - // client area, without full refresh. - void DrawOverlays(bool repaint, wxDC *pDC = nullptr); + // Erases and redraws to the client area the overlays that have + // been previously added with AddOverlay(). If "repaint_all" is + // true, all overlays will be erased and re-drawn. Otherwise, only + // the ones that are out-of-date, as well as the intersecting ones, + // will be erased and re-drawn. + // pDC can be null, in which case, DrawOverlays() will create a + // wxClientDC internally when necessary. + void DrawOverlays(bool repaint_all, wxDC *pDC = nullptr); private: std::vector mOverlays;