| /* |
| * Copyright 2021 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "tests/Test.h" |
| |
| #include "include/core/SkVertices.h" |
| #include "src/core/SkBlendModePriv.h" |
| #include "src/core/SkMatrixProvider.h" |
| #include "src/core/SkSurfacePriv.h" |
| #include "src/gpu/GrStyle.h" |
| #include "src/gpu/GrSurfaceDrawContext.h" |
| |
| static SkSurfaceProps kDMSAAProps(kDMSAA_SkSurfacePropsPrivateFlag, kUnknown_SkPixelGeometry); |
| constexpr static SkPMColor4f kTransYellow = {.5f,.5f,.0f,.5f}; |
| constexpr static SkPMColor4f kTransCyan = {.0f,.5f,.5f,.5f}; |
| constexpr static int w=10, h=10; |
| |
| static void draw_paint_with_aa(GrSurfaceDrawContext* sdc, const SkPMColor4f& color, |
| SkBlendMode blendMode) { |
| GrPaint paint; |
| paint.setColor4f(color); |
| paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode)); |
| sdc->drawRect(nullptr, std::move(paint), GrAA::kYes, SkMatrix::I(), SkRect::MakeIWH(w, h), |
| nullptr); |
| } |
| |
| static void draw_paint_with_dmsaa(GrSurfaceDrawContext* sdc, const SkPMColor4f& color, |
| SkBlendMode blendMode) { |
| // drawVertices should always trigger dmsaa, but draw something non-rectangular just to be 100% |
| // certain. |
| static const SkPoint kVertices[3] = {{-.5f,-.5f}, {w * 2.1f, 0}, {0, h * 2.1f}}; |
| SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 3, 0, 0); |
| memcpy(builder.positions(), kVertices, sizeof(kVertices)); |
| auto vertices = builder.detach(); |
| |
| GrPaint paint; |
| paint.setColor4f(color); |
| paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode)); |
| sdc->drawVertices(nullptr, std::move(paint), SkSimpleMatrixProvider(SkMatrix::I()), vertices); |
| } |
| |
| static bool fuzzy_equals(const float a[4], const SkPMColor4f& b) { |
| constexpr static float kTolerance = 2.5f / 256; |
| for (int i = 0; i < 4; ++i) { |
| if (!SkScalarNearlyEqual(a[i], b.vec()[i], kTolerance)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static void check_sdc_color(skiatest::Reporter* reporter, GrSurfaceDrawContext* sdc, |
| GrDirectContext* ctx, const SkPMColor4f& color) { |
| auto info = SkImageInfo::Make(w, h, kRGBA_F32_SkColorType, kPremul_SkAlphaType); |
| GrPixmap pixmap = GrPixmap::Allocate(info); |
| sdc->readPixels(ctx, pixmap, {0, 0}); |
| auto pix = static_cast<const float*>(pixmap.addr()); |
| for (int y = 0; y < h; ++y) { |
| for (int x = 0; x < w; ++x) { |
| if (!fuzzy_equals(pix, color)) { |
| ERRORF(reporter, "SDC color mismatch.\n" |
| "Got [%0.3f, %0.3f, %0.3f, %0.3f]\n" |
| "Expected [%0.3f, %0.3f, %0.3f, %0.3f]", |
| pix[0], pix[1], pix[2], pix[3], color.fR, color.fG, color.fB, color.fA); |
| return; |
| } |
| pix += 4; |
| } |
| } |
| } |
| |
| DEF_GPUTEST_FOR_CONTEXTS(DMSAA_preserve_contents, |
| &sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo, |
| nullptr) { |
| auto ctx = ctxInfo.directContext(); |
| auto sdc = GrSurfaceDrawContext::Make(ctx, GrColorType::kRGBA_8888, nullptr, |
| SkBackingFit::kApprox, {w, h}, kDMSAAProps); |
| |
| // Initialize the texture and dmsaa attachment with transparent. |
| draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc); |
| check_sdc_color(reporter, sdc.get(), ctx, SK_PMColor4fTRANSPARENT); |
| |
| // Clear the main texture to yellow. |
| sdc->clear(kTransYellow); |
| |
| // Close the opsTask by doing a readback. |
| check_sdc_color(reporter, sdc.get(), ctx, kTransYellow); |
| |
| // Now the DMSAA attachment is clear and the texture is yellow. Blend cyan into the DMSAA |
| // attachment. This will fail if the yellow from the main texture doesn't get copied into the |
| // DMSAA attachment before the renderPass. |
| draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kSrcOver); |
| SkPMColor4f dstColor = SkBlendMode_Apply(SkBlendMode::kSrcOver, kTransCyan, kTransYellow); |
| |
| check_sdc_color(reporter, sdc.get(), ctx, dstColor); |
| } |
| |
| static void require_dst_reads(GrContextOptions* options) { |
| options->fSuppressAdvancedBlendEquations = true; |
| options->fSuppressFramebufferFetch = true; |
| } |
| |
| DEF_GPUTEST_FOR_CONTEXTS(DMSAA_dst_read, &sk_gpu_test::GrContextFactory::IsRenderingContext, |
| reporter, ctxInfo, require_dst_reads) { |
| auto ctx = ctxInfo.directContext(); |
| auto sdc = GrSurfaceDrawContext::Make(ctx, GrColorType::kRGBA_8888, nullptr, |
| SkBackingFit::kApprox, {w, h}, kDMSAAProps); |
| |
| // Initialize the texture and dmsaa attachment with transparent. |
| draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc); |
| check_sdc_color(reporter, sdc.get(), ctx, SK_PMColor4fTRANSPARENT); |
| |
| sdc->clear(SK_PMColor4fWHITE); |
| SkPMColor4f dstColor = SK_PMColor4fWHITE; |
| |
| draw_paint_with_dmsaa(sdc.get(), kTransYellow, SkBlendMode::kDarken); |
| dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransYellow, dstColor); |
| |
| draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kDarken); |
| dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransCyan, dstColor); |
| |
| check_sdc_color(reporter, sdc.get(), ctx, dstColor); |
| } |
| |
| DEF_GPUTEST_FOR_CONTEXTS(DMSAA_aa_dst_read_after_dmsaa, |
| &sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo, |
| require_dst_reads) { |
| auto ctx = ctxInfo.directContext(); |
| auto sdc = GrSurfaceDrawContext::Make(ctx, GrColorType::kRGBA_8888, nullptr, |
| SkBackingFit::kApprox, {w, h}, kDMSAAProps); |
| |
| // Initialize the texture and dmsaa attachment with transparent. |
| draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc); |
| check_sdc_color(reporter, sdc.get(), ctx, SK_PMColor4fTRANSPARENT); |
| |
| sdc->clear(SK_PMColor4fWHITE); |
| SkPMColor4f dstColor = SK_PMColor4fWHITE; |
| |
| draw_paint_with_dmsaa(sdc.get(), kTransYellow, SkBlendMode::kDarken); |
| dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransYellow, dstColor); |
| |
| // Draw with aa after dmsaa. This should break up the render pass and issue a texture barrier. |
| draw_paint_with_aa(sdc.get(), kTransCyan, SkBlendMode::kDarken); |
| dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransCyan, dstColor); |
| |
| check_sdc_color(reporter, sdc.get(), ctx, dstColor); |
| } |
| |
| DEF_GPUTEST_FOR_CONTEXTS(DMSAA_dst_read_with_existing_barrier, |
| &sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo, |
| require_dst_reads) { |
| auto ctx = ctxInfo.directContext(); |
| auto sdc = GrSurfaceDrawContext::Make(ctx, GrColorType::kRGBA_8888, nullptr, |
| SkBackingFit::kApprox, {w, h}, kDMSAAProps); |
| |
| // Initialize the texture and dmsaa attachment with transparent. |
| draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc); |
| check_sdc_color(reporter, sdc.get(), ctx, SK_PMColor4fTRANSPARENT); |
| |
| sdc->clear(SK_PMColor4fWHITE); |
| SkPMColor4f dstColor = SK_PMColor4fWHITE; |
| |
| // Blend to the texture (not the dmsaa attachment) with a dst read. This creates a texture |
| // barrier. |
| draw_paint_with_aa(sdc.get(), kTransYellow, SkBlendMode::kDarken); |
| dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransYellow, dstColor); |
| |
| // Blend to the msaa attachment _without_ a dst read. This ensures we respect the prior texture |
| // barrier by splitting the opsTask. |
| draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kSrcOver); |
| dstColor = SkBlendMode_Apply(SkBlendMode::kSrcOver, kTransCyan, dstColor); |
| |
| check_sdc_color(reporter, sdc.get(), ctx, dstColor); |
| } |