diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2021-11-11 20:51:55 +0100 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2021-11-12 18:49:39 +0100 |
commit | 110fa313628c55fef1d35830358aea7e27c1e3ee (patch) | |
tree | 6a7f817e299093d1c3db916897dcd3eaf968f191 | |
parent | 754697f0dcd63e1f0ce2edd70ab8b42b1b4d4484 (diff) |
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.lunak@collabora.com>
-rw-r--r-- | vcl/inc/skia/utils.hxx | 3 | ||||
-rw-r--r-- | vcl/skia/SkiaHelper.cxx | 35 | ||||
-rw-r--r-- | vcl/skia/gdiimpl.cxx | 35 |
3 files changed, 40 insertions, 33 deletions
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(), ©, - SkCanvas::kFast_SrcRectConstraint); - aPath.offset(-area.x(), -area.y()); - surface->getCanvas()->drawPath(aPath, aPaint); - getDrawCanvas()->drawImageRect(makeCheckedImageSnapshot(surface), size, area, - SkSamplingOptions(), ©, - SkCanvas::kFast_SrcRectConstraint); - } + getDrawCanvas()->drawPath(aPath, aPaint); postDraw(); } |