| /* |
| * Copyright 2026 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/core/SkBitmap.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkColor.h" |
| #include "include/core/SkImageInfo.h" |
| #include "include/core/SkPoint.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkSamplingOptions.h" |
| #include "include/core/SkSurface.h" |
| #include "include/effects/SkGradient.h" |
| #include "include/gpu/ganesh/SkImageGanesh.h" |
| #include "include/gpu/ganesh/SkSurfaceGanesh.h" |
| #include "src/gpu/ganesh/GrResourceProvider.h" |
| #include "tests/Test.h" |
| |
| namespace { |
| |
| sk_sp<SkImage> make_gpu_src_img(GrDirectContext* dContext, float srcSize) { |
| SkImageInfo ii = SkImageInfo::Make(srcSize, srcSize, |
| kRGBA_8888_SkColorType, kPremul_SkAlphaType); |
| |
| SkBitmap srcBM; |
| srcBM.allocPixels(ii); |
| |
| SkPaint p; |
| const SkPoint pts[] = { { 0, 0 }, { srcSize, srcSize } }; |
| const SkColor4f colors[] = { SkColors::kWhite, SkColors::kBlue }; |
| p.setShader(SkShaders::LinearGradient(pts, {{colors, {}, SkTileMode::kClamp}, {}})); |
| |
| SkCanvas c(srcBM); |
| c.drawRect(SkRect::MakeWH(srcSize, srcSize), p); |
| |
| sk_sp<SkImage> rasterImage = SkImages::RasterFromBitmap(srcBM); |
| return SkImages::TextureFromImage(dContext, rasterImage); |
| } |
| |
| // This test will trigger an assert if the max quads/TextureOp limit overflows |
| void run_test(GrDirectContext* dContext, uint32_t numXSteps, uint32_t numYSteps, bool withAA) { |
| constexpr uint32_t kRectSize = 8; |
| constexpr float kHalfRectSize = kRectSize / 2.0f; |
| |
| sk_sp<SkImage> srcImg = make_gpu_src_img(dContext, kRectSize); |
| |
| SkImageInfo ii = SkImageInfo::Make(numXSteps*kRectSize, numYSteps*kRectSize, |
| kRGBA_8888_SkColorType, kPremul_SkAlphaType); |
| auto surface(SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, ii)); |
| |
| SkCanvas* canvas = surface->getCanvas(); |
| canvas->clear(SK_ColorBLACK); |
| |
| SkCanvas::ImageSetEntry* entries = new SkCanvas::ImageSetEntry[numXSteps*numYSteps]; |
| SkMatrix* preViewMatrices = new SkMatrix[numXSteps*numYSteps]; |
| |
| SkMatrix perspMatrix; |
| perspMatrix.setPerspX(0.1f); |
| perspMatrix.setPerspY(0.1f); |
| |
| for (uint32_t y = 0; y < numYSteps; ++y) { |
| for (uint32_t x = 0; x < numXSteps; x++) { |
| uint32_t entry = y*numXSteps+x; |
| |
| // These matrices are arranged so every quad will be split due to |
| // perspective |
| preViewMatrices[entry].setTranslate(x*kRectSize+kHalfRectSize, |
| y*kRectSize+kHalfRectSize); |
| |
| preViewMatrices[entry].preConcat(perspMatrix); |
| |
| SkRect destRect = SkRect::MakeXYWH(-kHalfRectSize, -kHalfRectSize, |
| kRectSize, kRectSize); |
| |
| entries[entry].fImage = srcImg; |
| entries[entry].fSrcRect = SkRect::MakeLTRB(0, 0, kRectSize, kRectSize); |
| entries[entry].fDstRect = destRect; |
| entries[entry].fMatrixIndex = entry; |
| entries[entry].fAlpha = 1.f; |
| entries[entry].fAAFlags = withAA ? SkCanvas::kAll_QuadAAFlags |
| : SkCanvas::kNone_QuadAAFlags; |
| entries[entry].fHasClip = false; |
| } |
| } |
| |
| canvas->experimental_DrawEdgeAAImageSet( |
| entries, |
| numXSteps*numYSteps, |
| /* dstClips= */ nullptr, |
| preViewMatrices, |
| SkSamplingOptions()); |
| |
| dContext->flushAndSubmit(surface.get(), GrSyncCpu::kYes); |
| |
| delete [] preViewMatrices; |
| delete [] entries; |
| } |
| |
| } // anonymous namespace |
| |
| // This checks that the perspective quad splitting doesn't overflow for AA draws |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(crbug_500172224_aa_double, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| constexpr uint32_t kNumXSteps = 32; |
| constexpr uint32_t kNumYSteps = 32; |
| SkASSERT(kNumXSteps*kNumYSteps == 2*GrResourceProvider::MaxNumAAQuads()); |
| run_test(ctxInfo.directContext(), kNumXSteps, kNumYSteps, /* withAA= */ true); |
| } |
| |
| // This checks that the perspective quad splitting doesn't overflow for non-AA draws |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(crbug_500172224_non_aa_double, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| constexpr uint32_t kNumXSteps = 128; |
| constexpr uint32_t kNumYSteps = 64; |
| SkASSERT(kNumXSteps*kNumYSteps == 2*GrResourceProvider::MaxNumNonAAQuads()); |
| run_test(ctxInfo.directContext(), kNumXSteps, kNumYSteps, /* withAA= */ false); |
| } |
| |
| // This tests the AA perspective fast-path blocking in TextureOp::AddTextureSetOps. |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(crbug_500172224_aa, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| constexpr uint32_t kNumXSteps = 32; |
| constexpr uint32_t kNumYSteps = 16; |
| SkASSERT(kNumXSteps*kNumYSteps == GrResourceProvider::MaxNumAAQuads()); |
| run_test(ctxInfo.directContext(), kNumXSteps, kNumYSteps, /* withAA= */ true); |
| } |
| |