drawinglayer/source/processor2d/vclhelperbufferdevice.cxx |   34 ++++++++++--
 include/vcl/outdev.hxx                                    |    3 +
 vcl/headless/svpvd.cxx                                    |   37 ++++++++------
 vcl/source/outdev/outdev.cxx                              |    9 +++
 4 files changed, 62 insertions(+), 21 deletions(-)

New commits:
commit cf9be3417bc2be5f772c03180b7cbd248b82cad5
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Tue Jan 11 19:08:50 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Mon Jan 24 14:40:02 2022 +0100

    avoid Xlib cairo surfaces for small virtual devices (bsc#1183308)
    
    The (private :( ) document contains a large number of small shapes
    for which VclProcessor2D::RenderTransparencePrimitive2D() gets
    called, which results in GetBitmapEx() on the VirtualDevice.
    And Cairo normally needs to do a roundtrip to the XServer to fetch
    the content, which makes the rendering pretty slow. Forcing
    image-based surface for small sizes of VirtualDevice actually has
    better performance for the document, and the lack of possible
    HW acceleration generally shouldn't matter for a surface this small.
    
    Additionally drawinglayer's VirtualDevice reusing tries to reuse
    even a large one when a small one is needed, so a hack is needed
    to avoid that in this case, I couldn't find a better way.
    
    Change-Id: I0f58659ab88165a6bd915f100fc3b1d4802a0fa9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128309
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx 
b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
index 9129271bcb6b..7f20d094b446 100644
--- a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
+++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
@@ -63,6 +63,8 @@ private:
     // virtualdevice because that isn't safe to do at least for Gtk2
     std::map<VclPtr<VirtualDevice>, VclPtr<OutputDevice>> maDeviceTemplates;
 
+    static bool isSizeSuitable(const VclPtr<VirtualDevice>& device, const 
Size& size);
+
 public:
     VDevBuffer();
     virtual ~VDevBuffer() override;
@@ -98,6 +100,28 @@ VDevBuffer::~VDevBuffer()
     }
 }
 
+bool VDevBuffer::isSizeSuitable(const VclPtr<VirtualDevice>& device, const 
Size& rSizePixel)
+{
+    if (device->GetOutputWidthPixel() >= rSizePixel.getWidth()
+        && device->GetOutputHeightPixel() >= rSizePixel.getHeight())
+    {
+#if defined(UNX)
+        // HACK: See the small size handling in 
SvpSalVirtualDevice::CreateSurface().
+        // Make sure to not reuse a larger device when a small one should be 
preferred.
+        if (device->GetRenderBackendName() == "svp")
+        {
+            if (rSizePixel.getWidth() <= 32 && rSizePixel.getHeight() <= 32
+                && (device->GetOutputWidthPixel() > 32 || 
device->GetOutputHeightPixel() > 32))
+            {
+                return false;
+            }
+        }
+#endif
+        return true;
+    }
+    return false;
+}
+
 VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& 
rSizePixel,
                                         bool bTransparent)
 {
@@ -124,9 +148,7 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& 
rOutDev, const Size& rSize
                     if (bOkay)
                     {
                         // found is valid
-                        const bool bCandidateOkay(
-                            a->buf->GetOutputWidthPixel() >= 
rSizePixel.getWidth()
-                            && a->buf->GetOutputHeightPixel() >= 
rSizePixel.getHeight());
+                        const bool bCandidateOkay = isSizeSuitable(a->buf, 
rSizePixel);
 
                         if (bCandidateOkay)
                         {
@@ -151,16 +173,14 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& 
rOutDev, const Size& rSize
                     {
                         // found is invalid, use candidate
                         aFound = a;
-                        bOkay = aFound->buf->GetOutputWidthPixel() >= 
rSizePixel.getWidth()
-                                && aFound->buf->GetOutputHeightPixel() >= 
rSizePixel.getHeight();
+                        bOkay = isSizeSuitable(aFound->buf, rSizePixel);
                     }
                 }
                 else
                 {
                     // none yet, use candidate
                     aFound = a;
-                    bOkay = aFound->buf->GetOutputWidthPixel() >= 
rSizePixel.getWidth()
-                            && aFound->buf->GetOutputHeightPixel() >= 
rSizePixel.getHeight();
+                    bOkay = isSizeSuitable(aFound->buf, rSizePixel);
                 }
             }
         }
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index 7e1a0fad7dcd..a777f9d73569 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -295,6 +295,9 @@ public:
     const AllSettings&          GetSettings() const { return *mxSettings; }
 
     SystemGraphicsData          GetSystemGfxData() const;
+    OUString                    GetRenderBackendName() const;
+
+    // Used by the canvas module. Despite the name it does not always return 
true if Cairo is supported.
     bool                        SupportsCairo() const;
     /// Create Surface from given cairo surface
     cairo::SurfaceSharedPtr     CreateSurface(const 
cairo::CairoSurfaceSharedPtr& rSurface) const;
diff --git a/vcl/headless/svpvd.cxx b/vcl/headless/svpvd.cxx
index f8e9cb5e6874..4d82a46e50c4 100644
--- a/vcl/headless/svpvd.cxx
+++ b/vcl/headless/svpvd.cxx
@@ -76,29 +76,38 @@ void SvpSalVirtualDevice::CreateSurface(tools::Long nNewDX, 
tools::Long nNewDY,
         cairo_surface_destroy(m_pSurface);
     }
 
-    if (pBuffer)
+    double fXScale, fYScale;
+    if (comphelper::LibreOfficeKit::isActive())
+    {
+        // Force scaling of the painting
+        fXScale = fYScale = comphelper::LibreOfficeKit::getDPIScale();
+    }
+    else
     {
-        double fXScale, fYScale;
-        if (comphelper::LibreOfficeKit::isActive())
-        {
-            // Force scaling of the painting
-            fXScale = fYScale = comphelper::LibreOfficeKit::getDPIScale();
-        }
-        else
-        {
-            dl_cairo_surface_get_device_scale(m_pRefSurface, &fXScale, 
&fYScale);
-            nNewDX *= fXScale;
-            nNewDY *= fYScale;
-        }
+        dl_cairo_surface_get_device_scale(m_pRefSurface, &fXScale, &fYScale);
+        nNewDX *= fXScale;
+        nNewDY *= fYScale;
+    }
 
+    if (pBuffer)
+    {
         m_pSurface = cairo_image_surface_create_for_data(pBuffer, 
CAIRO_FORMAT_ARGB32,
                             nNewDX, nNewDY, 
cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, nNewDX));
-
+        dl_cairo_surface_set_device_scale(m_pSurface, fXScale, fYScale);
+    }
+    else if(nNewDX <= 32 && nNewDY <= 32)
+    {
+        // Force image-based surface if small. Small VirtualDevice instances 
are often used for small
+        // temporary bitmaps that will eventually have GetBitmap() called on 
them, which would cause
+        // X Server roundtrip with Xlib-based surface, which may be way more 
costly than doing the drawing
+        // in software (which should be fairly cheap for small surfaces 
anyway).
+        m_pSurface = cairo_surface_create_similar_image(m_pRefSurface, 
CAIRO_FORMAT_ARGB32, nNewDX, nNewDY);
         dl_cairo_surface_set_device_scale(m_pSurface, fXScale, fYScale);
     }
     else
     {
         m_pSurface = cairo_surface_create_similar(m_pRefSurface, 
CAIRO_CONTENT_COLOR_ALPHA, nNewDX, nNewDY);
+        // Device scale is inherited in this case.
     }
 }
 
diff --git a/vcl/source/outdev/outdev.cxx b/vcl/source/outdev/outdev.cxx
index 708a283f44a8..0d9248d2fd77 100644
--- a/vcl/source/outdev/outdev.cxx
+++ b/vcl/source/outdev/outdev.cxx
@@ -231,6 +231,15 @@ SystemGraphicsData OutputDevice::GetSystemGfxData() const
     return mpGraphics->GetGraphicsData();
 }
 
+OUString OutputDevice::GetRenderBackendName() const
+{
+    if (!mpGraphics && !AcquireGraphics())
+        return {};
+    assert(mpGraphics);
+
+    return mpGraphics->getRenderBackendName();
+}
+
 #if ENABLE_CAIRO_CANVAS
 
 bool OutputDevice::SupportsCairo() const

Reply via email to