From 0b62a05d9ae528fad7b18e842588b331ec032bcd Mon Sep 17 00:00:00 2001 From: John Stiles Date: Wed, 14 Jul 2021 17:24:20 -0400 Subject: [PATCH] Add unit test confirming Runtime Blends match native blends. We perform the same draw operation twice--once with a `setBlendMode` based blend, and then once more with `setBlender` and passing SkSL which is equivalent to the native blend op. If this test fails, it would indicate that Runtime Blends aren't being applied appropriately (or perhaps that a GPU's built-in blending modes are cutting corners?). Change-Id: I4ca1f9ed600d9ec733687cc6de7d3e2eb6e765c9 Bug: skia:12080 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/428318 Auto-Submit: John Stiles Reviewed-by: Mike Reed Commit-Queue: John Stiles --- gn/tests.gni | 1 + tests/RuntimeBlendTest.cpp | 89 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 tests/RuntimeBlendTest.cpp diff --git a/gn/tests.gni b/gn/tests.gni index 642dafb804..58434def0e 100644 --- a/gn/tests.gni +++ b/gn/tests.gni @@ -219,6 +219,7 @@ tests_sources = [ "$_tests/ResourceAllocatorTest.cpp", "$_tests/ResourceCacheTest.cpp", "$_tests/RoundRectTest.cpp", + "$_tests/RuntimeBlendTest.cpp", "$_tests/SRGBReadWritePixelsTest.cpp", "$_tests/SRGBTest.cpp", "$_tests/SVGDeviceTest.cpp", diff --git a/tests/RuntimeBlendTest.cpp b/tests/RuntimeBlendTest.cpp new file mode 100644 index 0000000000..39669bfbfd --- /dev/null +++ b/tests/RuntimeBlendTest.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2021 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm/gm.h" +#include "include/core/SkBitmap.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkPaint.h" +#include "include/core/SkSize.h" +#include "include/core/SkSurface.h" +#include "include/effects/SkRuntimeEffect.h" +#include "src/gpu/GrCaps.h" +#include "src/gpu/GrDirectContextPriv.h" +#include "tests/Test.h" +#include "tools/Resources.h" +#include "tools/RuntimeBlendUtils.h" +#include "tools/ToolUtils.h" + +static bool nearly_equal(const SkColor& x, const SkColor& y) { + const int kTolerance = 1; + return abs((int)SkColorGetA(x) - (int)SkColorGetA(y)) <= kTolerance && + abs((int)SkColorGetR(x) - (int)SkColorGetR(y)) <= kTolerance && + abs((int)SkColorGetG(x) - (int)SkColorGetG(y)) <= kTolerance && + abs((int)SkColorGetB(x) - (int)SkColorGetB(y)) <= kTolerance; +} + +static void test_blend(skiatest::Reporter* r, SkSurface* surface) { + SkBitmap bitmap; + REPORTER_ASSERT(r, bitmap.tryAllocPixels(surface->imageInfo())); + + for (int m = 0; m <= (int)SkBlendMode::kLastMode; ++m) { + SkBlendMode mode = (SkBlendMode)m; + for (int alpha : {0x80, 0xFF}) { + std::vector colors; + for (bool useRuntimeBlend : {false, true}) { + // Draw a solid red pixel. + SkPaint paint; + paint.setColor(SK_ColorRED); + paint.setBlendMode(SkBlendMode::kSrc); + surface->getCanvas()->drawRect(SkRect::MakeWH(1, 1), paint); + + // Draw a blue pixel on top of it, using the passed-in blend mode. + paint.setColor(SkColorSetARGB(alpha, 0x00, 0x00, 0xFF)); + if (useRuntimeBlend) { + paint.setBlender(GetRuntimeBlendForBlendMode(mode)); + } else { + paint.setBlendMode(mode); + } + surface->getCanvas()->drawRect(SkRect::MakeWH(1, 1), paint); + + // Read back the red/blue blended pixel. + REPORTER_ASSERT(r, surface->readPixels(bitmap.info(), bitmap.getPixels(), + bitmap.rowBytes(), /*srcX=*/0, /*srcY=*/0)); + colors.push_back(bitmap.getColor(/*x=*/0, /*y=*/0)); + } + + REPORTER_ASSERT(r, nearly_equal(colors[0], colors[1]), + "Expected: %s %s blend matches. Actual: Built-in " + "A=%02X R=%02X G=%02X B=%02X, Runtime A=%02X R=%02X G=%02X B=%02X", + SkBlendMode_Name(mode), + (alpha == 0xFF) ? "solid" : "transparent", + SkColorGetA(colors[0]), + SkColorGetR(colors[0]), + SkColorGetG(colors[0]), + SkColorGetB(colors[0]), + SkColorGetA(colors[1]), + SkColorGetR(colors[1]), + SkColorGetG(colors[1]), + SkColorGetB(colors[1])); + } + } +} + +DEF_TEST(SkRuntimeBlender_CPU, r) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(/*width=*/1, /*height=*/1); + sk_sp surface(SkSurface::MakeRaster(info)); + + test_blend(r, surface.get()); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeBlender_GPU, r, ctxInfo) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(/*width=*/1, /*height=*/1); + sk_sp surface(SkSurface::MakeRenderTarget(ctxInfo.directContext(), + SkBudgeted::kNo, info)); + test_blend(r, surface.get()); +}