drawinglayer/source/processor2d/vclhelperbufferdevice.cxx | 34 +++++++++--- include/vcl/outdev.hxx | 3 + vcl/headless/svpvd.cxx | 39 +++++++++----- vcl/source/outdev/outdev.cxx | 9 +++ 4 files changed, 65 insertions(+), 20 deletions(-)
New commits: commit 7c29b13ac67e3796b5998789371fa68acb01a260 Author: Luboš Luňák <l.lu...@collabora.com> AuthorDate: Tue Jan 11 19:08:50 2022 +0100 Commit: Caolán McNamara <caol...@redhat.com> CommitDate: Wed Jan 26 12:59:39 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> (cherry picked from commit cf9be3417bc2be5f772c03180b7cbd248b82cad5) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128815 Reviewed-by: Caolán McNamara <caol...@redhat.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 eec957e14973..e8011412955c 100644 --- a/include/vcl/outdev.hxx +++ b/include/vcl/outdev.hxx @@ -294,6 +294,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..21d231b229e9 100644 --- a/vcl/headless/svpvd.cxx +++ b/vcl/headless/svpvd.cxx @@ -76,29 +76,42 @@ void SvpSalVirtualDevice::CreateSurface(tools::Long nNewDX, tools::Long nNewDY, cairo_surface_destroy(m_pSurface); } + 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); + } + if (pBuffer) { - 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; - } + nNewDX *= fXScale; + nNewDY *= fYScale; 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) + { + nNewDX *= fXScale; + nNewDY *= fYScale; + + // 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 1083ff9b0d04..9231f7779a6b 100644 --- a/vcl/source/outdev/outdev.cxx +++ b/vcl/source/outdev/outdev.cxx @@ -230,6 +230,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