vcl/inc/skia/utils.hxx  |    3 +++
 vcl/skia/SkiaHelper.cxx |   35 +++++++++++++++++++++++++++++++++++
 vcl/skia/gdiimpl.cxx    |   35 ++---------------------------------
 3 files changed, 40 insertions(+), 33 deletions(-)

New commits:
commit 110fa313628c55fef1d35830358aea7e27c1e3ee
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Thu Nov 11 20:51:55 2021 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Fri Nov 12 18:49:39 2021 +0100

    get rid of Skia's 'rasterhack' for Invert()
    
    It seems that manually writing a shader that does the same
    as SkBlendMode::kDifference works fine even though the blend mode
    crashes e.g. on Windows/AMD. So get rid of the memory<->GPU
    conversions and use the shader as a workaround.
    
    Change-Id: I971deeeb98f40e5ffa90f6a8dd7b0b21ec491c1a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/125101
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/vcl/inc/skia/utils.hxx b/vcl/inc/skia/utils.hxx
index 0bcc5989493e..ba479c58f234 100644
--- a/vcl/inc/skia/utils.hxx
+++ b/vcl/inc/skia/utils.hxx
@@ -71,6 +71,9 @@ VCL_DLLPUBLIC sk_sp<SkImage> 
makeCheckedImageSnapshot(sk_sp<SkSurface> surface,
 
 inline Size imageSize(const sk_sp<SkImage>& image) { return 
Size(image->width(), image->height()); }
 
+// Do 'paint->setBlendMode(SkBlendMode::kDifference)' (workaround for buggy 
drivers).
+void setBlendModeDifference(SkPaint* paint);
+
 // Must be called in any VCL backend before any Skia functionality is used.
 // If not set, Skia will be disabled.
 VCL_DLLPUBLIC void
diff --git a/vcl/skia/SkiaHelper.cxx b/vcl/skia/SkiaHelper.cxx
index 458f415befa4..ed04e5f20ec0 100644
--- a/vcl/skia/SkiaHelper.cxx
+++ b/vcl/skia/SkiaHelper.cxx
@@ -42,6 +42,7 @@ bool isVCLSkiaEnabled() { return false; }
 #include <SkSurface.h>
 #include <SkGraphics.h>
 #include <GrDirectContext.h>
+#include <SkRuntimeEffect.h>
 #include <skia_compiler.hxx>
 #include <skia_opts.hxx>
 #include <tools/sk_app/VulkanWindowContext.h>
@@ -645,11 +646,45 @@ tools::Long maxImageCacheSize()
     return officecfg::Office::Common::Cache::Skia::ImageCacheSize::get();
 }
 
+static sk_sp<SkBlender> differenceBlender;
+
+void setBlendModeDifference(SkPaint* paint)
+{
+    // This should normally do 
'paint->setBlendMode(SkBlendMode::kDifference);'.
+    // But some drivers have a problem with this, namely currently AMD on 
Windows
+    // (e.g. 'Vulkan API version: 1.2.170, driver version: 2.0.179, vendor: 
0x1002 (AMD),
+    // device: 0x15dd, type: integrated, name: AMD Radeon(TM) Vega 8 Graphics')
+    // simply crashes when kDifference is used.
+    // Intel also had repaint problems with kDifference (tdf#130430), but it 
seems
+    // those do not(?) exist anymore.
+    // Interestingly, explicitly writing a shader that does exactly the same 
works fine,
+    // so do that.
+    if (!differenceBlender)
+    {
+        const char* diff = R"(
+            vec4 main( vec4 src, vec4 dst )
+            {
+                return vec4(abs( src.r - dst.r ), abs( src.g - dst.g ), abs( 
src.b - dst.b ), dst.a );
+            }
+        )";
+        auto effect = SkRuntimeEffect::MakeForBlender(SkString(diff));
+        if (!effect.effect)
+        {
+            SAL_WARN("vcl.skia",
+                     "SKRuntimeEffect::MakeForBlender failed: " << 
effect.errorText.c_str());
+            abort();
+        }
+        differenceBlender = effect.effect->makeBlender(nullptr);
+    }
+    paint->setBlender(differenceBlender);
+}
+
 void cleanup()
 {
     sharedWindowContext.reset();
     imageCache.clear();
     imageCacheSize = 0;
+    differenceBlender.reset();
 }
 
 static SkSurfaceProps commonSurfaceProps;
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index 9e2da70323de..fd86928c24c9 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -1412,16 +1412,6 @@ void SkiaSalGraphicsImpl::invert(basegfx::B2DPolygon 
const& rPoly, SalInvert eFl
     preDraw();
     SAL_INFO("vcl.skia.trace", "invert(" << this << "): " << rPoly << ":" << 
int(eFlags));
     assert(!mXorMode);
-    // Intel Vulkan drivers (up to current 0.401.3889) have a problem
-    // with SkBlendMode::kDifference(?) and surfaces wider than 1024 pixels, 
resulting
-    // in drawing errors. Work that around by fetching the relevant part of 
the surface
-    // and drawing using CPU.
-    bool rasterHack = (isGPU() && getVendor() == DriverBlocklist::VendorIntel 
&& !mXorMode);
-    // BackendTest::testDrawInvertTrackFrameWithRectangle() also has a problem
-    // with SkBlendMode::kDifference on AMD, leading to crashes or even
-    // driver instability. Also work around by drawing using CPU.
-    if (isGPU() && getVendor() == DriverBlocklist::VendorAMD && !mXorMode)
-        rasterHack = true;
     SkPath aPath;
     aPath.incReserve(rPoly.count());
     addPolygonToPath(rPoly, aPath);
@@ -1429,6 +1419,7 @@ void SkiaSalGraphicsImpl::invert(basegfx::B2DPolygon 
const& rPoly, SalInvert eFl
     addUpdateRegion(aPath.getBounds());
     SkAutoCanvasRestore autoRestore(getDrawCanvas(), true);
     SkPaint aPaint;
+    setBlendModeDifference(&aPaint);
     // TrackFrame just inverts a dashed path around the polygon
     if (eFlags == SalInvert::TrackFrame)
     {
@@ -1441,13 +1432,11 @@ void SkiaSalGraphicsImpl::invert(basegfx::B2DPolygon 
const& rPoly, SalInvert eFl
         aPaint.setStyle(SkPaint::kStroke_Style);
         aPaint.setPathEffect(SkDashPathEffect::Make(intervals, 
SK_ARRAY_COUNT(intervals), 0));
         aPaint.setColor(SkColorSetARGB(255, 255, 255, 255));
-        aPaint.setBlendMode(SkBlendMode::kDifference);
     }
     else
     {
         aPaint.setColor(SkColorSetARGB(255, 255, 255, 255));
         aPaint.setStyle(SkPaint::kFill_Style);
-        aPaint.setBlendMode(SkBlendMode::kDifference);
 
         // N50 inverts in checker pattern
         if (eFlags == SalInvert::N50)
@@ -1472,27 +1461,7 @@ void SkiaSalGraphicsImpl::invert(basegfx::B2DPolygon 
const& rPoly, SalInvert eFl
                 aBitmap.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, 
SkSamplingOptions()));
         }
     }
-    if (!rasterHack)
-        getDrawCanvas()->drawPath(aPath, aPaint);
-    else
-    {
-        SkRect area;
-        aPath.getBounds().roundOut(&area);
-        SkRect size = SkRect::MakeWH(area.width(), area.height());
-        sk_sp<SkSurface> surface
-            = SkSurface::MakeRasterN32Premul(area.width(), area.height(), 
surfaceProps());
-        SkPaint copy;
-        copy.setBlendMode(SkBlendMode::kSrc);
-        flushDrawing();
-        
surface->getCanvas()->drawImageRect(makeCheckedImageSnapshot(mSurface), area, 
size,
-                                            SkSamplingOptions(), &copy,
-                                            SkCanvas::kFast_SrcRectConstraint);
-        aPath.offset(-area.x(), -area.y());
-        surface->getCanvas()->drawPath(aPath, aPaint);
-        getDrawCanvas()->drawImageRect(makeCheckedImageSnapshot(surface), 
size, area,
-                                       SkSamplingOptions(), &copy,
-                                       SkCanvas::kFast_SrcRectConstraint);
-    }
+    getDrawCanvas()->drawPath(aPath, aPaint);
     postDraw();
 }
 

Reply via email to