| /* | |
| * 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); | |
| } | |