vcl/inc/skia/osx/gdiimpl.hxx |    6 ++--
 vcl/osx/salframe.cxx         |   59 +------------------------------------------
 vcl/skia/osx/gdiimpl.cxx     |   54 +++++++++++++++++++++++++++++++++++++--
 3 files changed, 57 insertions(+), 62 deletions(-)

New commits:
commit 9d973cdf107d1be4db806e6ec8a6ced49275c4e6
Author:     Patrick Luby <guibmac...@gmail.com>
AuthorDate: Sat May 10 09:09:48 2025 -0400
Commit:     Patrick Luby <guibomac...@gmail.com>
CommitDate: Sun May 11 23:43:01 2025 +0200

    Related: tdf#163945 include Skia timer in Skia/Metal flushing rate limit
    
    Previously, Skia/Metal flushes were only limited when called from
    AquaSalFrame::doFlush(). While this fixed tdf#166258, this caused
    tdf#163945 to reoccur on some machines. So expand the Skia/Metal
    flushing rate limit to include flushes called from the Skia timer.
    
    Change-Id: I299f17eb406ddf1c635d4f75d51241807f15f58c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185138
    Tested-by: Jenkins
    Reviewed-by: Patrick Luby <guibomac...@gmail.com>

diff --git a/vcl/inc/skia/osx/gdiimpl.hxx b/vcl/inc/skia/osx/gdiimpl.hxx
index b189898082ab..f1e2214d3924 100644
--- a/vcl/inc/skia/osx/gdiimpl.hxx
+++ b/vcl/inc/skia/osx/gdiimpl.hxx
@@ -41,8 +41,6 @@ public:
     virtual void Flush(const tools::Rectangle&) override;
     virtual void WindowBackingPropertiesChanged() override;
 
-    void ScheduleFlush();
-
     CGImageRef createCGImageFromRasterSurface(const NSRect& rDirtyRect, 
CGPoint& rImageOrigin,
                                               bool& rImageFlipped);
 
@@ -51,6 +49,10 @@ private:
     virtual void createWindowSurfaceInternal(bool forceRaster = false) 
override;
     virtual void flushSurfaceToWindowContext() override;
     static inline sk_sp<SkFontMgr> fontManager;
+    static inline AquaSkiaSalGraphicsImpl* lastFlushedGraphicsImpl = nullptr;
+    static inline CFAbsoluteTime lastFlushedGraphicsImplTime = 0;
+
+    bool mbInFlushSurfaceToWindowContext;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/osx/salframe.cxx b/vcl/osx/salframe.cxx
index 37904e440bd3..e469882db3e2 100644
--- a/vcl/osx/salframe.cxx
+++ b/vcl/osx/salframe.cxx
@@ -57,10 +57,6 @@
 #include <quartz/CGHelpers.hxx>
 #include <postmac.h>
 
-#if HAVE_FEATURE_SKIA
-#include <vcl/skia/SkiaHelper.hxx>
-#endif
-
 const int nMinBlinkCursorDelay = 500;
 
 AquaSalFrame* AquaSalFrame::s_pCaptureFrame = nullptr;
@@ -1191,63 +1187,12 @@ bool AquaSalFrame::doFlush()
 
     if( mbForceFlushScrolling || mbForceFlushProgressBar || 
ImplGetSVData()->maAppData.mnDispatchLevel <= 0 )
     {
-        // Related: tdf#163945 don't directly flush graphics with Skia/Metal
-        // When dragging a selection box on an empty background in
-        // Impress and only with Skia/Metal, the selection box
-        // would not keep up with the pointer. The selection box
-        // would repaint sporadically or not at all if the pointer
-        // was dragged rapidly and the status bar was visible.
-        // Apparently, flushing a graphics doesn't actually do much
-        // of anything with Skia/Raster and Skia disabled so the
-        // selection box repaints without any noticeable delay.
-        // However, with Skia/Metal every flush of a graphics
-        // creates and queues a new CAMetalLayer drawable. During
-        // rapid dragging, this can lead to creating and queueing
-        // up to 200 drawables per second leaving no spare time for
-        // the Impress selection box painting timer to fire.
-        // So with Skia/Metal, throttle the rate of flushing.
-        bool bNeedsFlush = true;
-        bool bFlushed = false;
-#if HAVE_FEATURE_SKIA
-        // tdf#164428 Skia/Metal needs flush after drawing progress bar
-        if (!mbForceFlushProgressBar && SkiaHelper::isVCLSkiaEnabled() && 
SkiaHelper::renderMethodToUse() != SkiaHelper::RenderRaster)
-        {
-            // Assume a certain frame rate is the fastest flushing rate
-            // that can be handled with Skia/Metal. Note that the Skia
-            // timer is running separately so the overall flushing rate
-            // may still be faster than this limit. Previously, the
-            // limit was set to 200 flushes per second but that caused
-            // tdf#163945 to reappear so reduce the limit to 40 flushes
-            // per second.
-            static const CFAbsoluteTime fMinFlushInterval = 0.025;
-            static CFAbsoluteTime fLastFlushTime = 0;
-
-            CFAbsoluteTime fInterval = CFAbsoluteTimeGetCurrent() - 
fLastFlushTime;
-            if (fInterval >= 0.0f && fInterval < fMinFlushInterval)
-            {
-                // Do not schedule the Skia timer to run as it appears that
-                // it might be part of the cause for the reappearance of
-                // tdf#163945 on some machines.
-            }
-            else
-            {
-                mpGraphics->Flush();
-                fLastFlushTime = CFAbsoluteTimeGetCurrent();
-                bFlushed = true;
-            }
-            bNeedsFlush = false;
-        }
-#endif
-        if (bNeedsFlush)
-        {
-            mpGraphics->Flush();
-            bFlushed = true;
-        }
+        mpGraphics->Flush();
 
         // Related: tdf#155266 skip redisplay of the view when forcing flush
         // It appears that calling -[NSView display] overwhelms some Intel Macs
         // so only flush the graphics and skip immediate redisplay of the view.
-        bRet = !bFlushed || ImplGetSVData()->maAppData.mnDispatchLevel <= 0;
+        bRet = ImplGetSVData()->maAppData.mnDispatchLevel <= 0;
 
         mbForceFlushScrolling = false;
         mbForceFlushProgressBar = false;
diff --git a/vcl/skia/osx/gdiimpl.cxx b/vcl/skia/osx/gdiimpl.cxx
index 8e7e7b7beecf..7c0789bfe89f 100644
--- a/vcl/skia/osx/gdiimpl.cxx
+++ b/vcl/skia/osx/gdiimpl.cxx
@@ -59,11 +59,15 @@ 
AquaSkiaSalGraphicsImpl::AquaSkiaSalGraphicsImpl(AquaSalGraphics& rParent,
                                                  AquaSharedAttributes& rShared)
     : SkiaSalGraphicsImpl(rParent, rShared.mpFrame)
     , AquaGraphicsBackendBase(rShared, this)
+    , mbInFlushSurfaceToWindowContext(false)
 {
 }
 
 AquaSkiaSalGraphicsImpl::~AquaSkiaSalGraphicsImpl()
 {
+    if (lastFlushedGraphicsImpl == this)
+        lastFlushedGraphicsImpl = nullptr;
+
     DeInit(); // mac code doesn't call DeInit()
 }
 
@@ -125,12 +129,56 @@ void 
AquaSkiaSalGraphicsImpl::flushSurfaceToWindowContext()
     }
     else
     {
-        SkiaSalGraphicsImpl::flushSurfaceToWindowContext();
+        // Related: tdf#163945 don't directly flush graphics with Skia/Metal
+        // When dragging a selection box on an empty background in
+        // Impress and only with Skia/Metal, the selection box would
+        // not keep up with the pointer. The selection box would
+        // repaint sporadically or not at all if the pointer was
+        // dragged rapidly and the status bar was visible.
+        // Apparently, flushing a graphics doesn't actually do much of
+        // anything with Skia/Raster and Skia disabled so the selection
+        // box repaints without any noticeable delay.
+        // However, with Skia/Metal every flush of a graphics creates
+        // and queues a new CAMetalLayer drawable. During rapid
+        // dragging, this can lead to creating and queueing up to 200
+        // drawables per second leaving no spare time for the Impress
+        // selection box painting timer to fire. So with Skia/Metal,
+        // throttle the rate of flushing.
+        // tdf#166258 Assume a certain maximum flushing rate with Skia/Metal
+        // Previously, the fix for tdf#163945 was done in the
+        // AquaSalFrame::Flush() methods, but that caused tdf#166258
+        // so include flushes done by the Skia timer in the throttling
+        // of the maximum flushing rate. Currently, the maximum flushing
+        // rate is 100 flushes per second.
+        static const CFAbsoluteTime fMinFlushInterval = 0.01;
+
+        // Related: tdf#166258 prevent recursion as calling scheduleFlush()
+        // may immediately recurse into performFlush() leading to infinite
+        // recursion. If scheduleFlush() is recursing, assume that a flush
+        // is necessary.
+        // Also assume that a flush is necessary if the current graphics
+        // is different than the last flushed graphics. Otherwise, the
+        // "tip of the day" dialog or other windows will fail to draw
+        // immediately after the window opens.
+        CFAbsoluteTime fInterval = CFAbsoluteTimeGetCurrent() - 
lastFlushedGraphicsImplTime;
+        if (!mbInFlushSurfaceToWindowContext && lastFlushedGraphicsImpl == 
this && fInterval >= 0.0f
+            && fInterval < fMinFlushInterval)
+        {
+            // Just to be safe, enable the Skia timer so that the surface
+            // will eventually be flushed.
+            mbInFlushSurfaceToWindowContext = true;
+            scheduleFlush();
+            mbInFlushSurfaceToWindowContext = false;
+        }
+        else
+        {
+            lastFlushedGraphicsImpl = this;
+            SkiaSalGraphicsImpl::flushSurfaceToWindowContext();
+            lastFlushedGraphicsImplTime = CFAbsoluteTimeGetCurrent();
+        }
     }
 }
 
-void AquaSkiaSalGraphicsImpl::ScheduleFlush() { scheduleFlush(); }
-
 // For Raster we use our own screen blitting (see above).
 CGImageRef AquaSkiaSalGraphicsImpl::createCGImageFromRasterSurface(const 
NSRect& rDirtyRect,
                                                                    CGPoint& 
rImageOrigin,

Reply via email to