xref: /aosp_15_r20/external/skia/tests/ClearTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPixmap.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkRefCnt.h"
19 #include "include/core/SkSurface.h"
20 #include "include/core/SkSurfaceProps.h"
21 #include "include/core/SkTypes.h"
22 #include "include/gpu/GpuTypes.h"
23 #include "include/gpu/ganesh/GrContextOptions.h"
24 #include "include/gpu/ganesh/GrDirectContext.h"
25 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
26 #include "include/private/SkColorData.h"
27 #include "include/private/gpu/ganesh/GrTypesPriv.h"
28 #include "src/core/SkAutoPixmapStorage.h"
29 #include "src/gpu/SkBackingFit.h"
30 #include "src/gpu/ganesh/GrCaps.h"
31 #include "src/gpu/ganesh/GrColor.h"
32 #include "src/gpu/ganesh/GrDirectContextPriv.h"
33 #include "src/gpu/ganesh/SurfaceDrawContext.h"
34 #include "src/gpu/ganesh/ops/ClearOp.h"
35 #include "src/gpu/ganesh/ops/GrOp.h"
36 #include "src/gpu/ganesh/ops/OpsTask.h"
37 #include "tests/CtsEnforcement.h"
38 #include "tests/Test.h"
39 
40 #include <array>
41 #include <cstdint>
42 #include <memory>
43 
44 class GrRecordingContext;
45 
46 using SurfaceDrawContext = skgpu::ganesh::SurfaceDrawContext;
47 using ClearOp = skgpu::ganesh::ClearOp;
48 
pixel_matches(const SkPixmap & pm,int x,int y,uint32_t expectedValue,uint32_t * actualValue,int * failX,int * failY)49 static bool pixel_matches(const SkPixmap& pm, int x, int y, uint32_t expectedValue,
50                           uint32_t* actualValue, int* failX, int* failY) {
51     uint32_t pixel = pm.addr32()[y * pm.width() + x];
52     if (pixel != expectedValue) {
53         *actualValue = pixel;
54         *failX = x;
55         *failY = y;
56         return false;
57     }
58 
59     return true;
60 }
61 
check_rect(GrDirectContext * dContext,SurfaceDrawContext * sdc,const SkIRect & rect,uint32_t expectedValue,uint32_t * actualValue,int * failX,int * failY)62 static bool check_rect(GrDirectContext* dContext,
63                        SurfaceDrawContext* sdc,
64                        const SkIRect& rect,
65                        uint32_t expectedValue,
66                        uint32_t* actualValue,
67                        int* failX,
68                        int* failY) {
69     int w = sdc->width();
70     int h = sdc->height();
71 
72     SkImageInfo dstInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
73 
74     SkAutoPixmapStorage readback;
75     readback.alloc(dstInfo);
76 
77     readback.erase(~expectedValue);
78     if (!sdc->readPixels(dContext, readback, {0, 0})) {
79         return false;
80     }
81 
82     SkASSERT(rect.fTop < rect.fBottom && rect.fLeft < rect.fRight);
83     for (int y = rect.fTop; y < rect.fBottom; ++y) {
84         for (int x = rect.fLeft; x < rect.fRight; ++x) {
85             if (!pixel_matches(readback, x, y, expectedValue, actualValue, failX, failY)) {
86                 return false;
87             }
88         }
89     }
90     return true;
91 }
92 
93 // Check a 1-pixel wide ring 'inset' from the outer edge
check_ring(GrDirectContext * dContext,SurfaceDrawContext * sdc,int inset,uint32_t expectedValue,uint32_t * actualValue,int * failX,int * failY)94 static bool check_ring(GrDirectContext* dContext,
95                        SurfaceDrawContext* sdc,
96                        int inset,
97                        uint32_t expectedValue,
98                        uint32_t* actualValue,
99                        int* failX,
100                        int* failY) {
101     int w = sdc->width();
102     int h = sdc->height();
103 
104     SkImageInfo dstInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
105 
106     SkAutoPixmapStorage readback;
107     readback.alloc(dstInfo);
108 
109     readback.erase(~expectedValue);
110     if (!sdc->readPixels(dContext, readback, {0, 0})) {
111         return false;
112     }
113 
114     for (int y = inset; y < h-inset; ++y) {
115         if (!pixel_matches(readback, inset, y, expectedValue, actualValue, failX, failY) ||
116             !pixel_matches(readback, w-1-inset, y, expectedValue, actualValue, failX, failY)) {
117             return false;
118         }
119     }
120     for (int x = inset+1; x < w-inset-1; ++x) {
121         if (!pixel_matches(readback, x, inset, expectedValue, actualValue, failX, failY) ||
122             !pixel_matches(readback, x, h-1-inset, expectedValue, actualValue, failX, failY)) {
123             return false;
124         }
125     }
126 
127     return true;
128 }
129 
newSDC(GrRecordingContext * rContext,int w,int h)130 std::unique_ptr<SurfaceDrawContext> newSDC(GrRecordingContext* rContext, int w, int h) {
131     return SurfaceDrawContext::Make(rContext, GrColorType::kRGBA_8888, nullptr,
132                                     SkBackingFit::kExact, {w, h}, SkSurfaceProps(),
133                                     /*label=*/{});
134 }
135 
clear_op_test(skiatest::Reporter * reporter,GrDirectContext * dContext)136 static void clear_op_test(skiatest::Reporter* reporter, GrDirectContext* dContext) {
137     static const int kW = 10;
138     static const int kH = 10;
139 
140     SkIRect fullRect = SkIRect::MakeWH(kW, kH);
141     std::unique_ptr<SurfaceDrawContext> sdc;
142 
143     // A rectangle that is inset by one on all sides and the 1-pixel wide rectangles that surround
144     // it.
145     SkIRect mid1Rect = SkIRect::MakeXYWH(1, 1, kW-2, kH-2);
146 
147     // A rectangle that is inset by two on all sides and the 1-pixel wide rectangles that surround
148     // it.
149     SkIRect mid2Rect = SkIRect::MakeXYWH(2, 2, kW-4, kH-4);
150 
151     uint32_t actualValue;
152     int failX, failY;
153 
154     static const GrColor kColor1 = 0xABCDEF01;
155     static const GrColor kColor2 = ~kColor1;
156     static const SkPMColor4f kColor1f = SkPMColor4f::FromBytes_RGBA(kColor1);
157     static const SkPMColor4f kColor2f = SkPMColor4f::FromBytes_RGBA(kColor2);
158 
159     sdc = newSDC(dContext, kW, kH);
160     SkASSERT(sdc);
161 
162     // Check a full clear
163     sdc->clear(fullRect, kColor1f);
164     if (!check_rect(dContext, sdc.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
165         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
166                failX, failY);
167     }
168 
169     sdc = newSDC(dContext, kW, kH);
170     SkASSERT(sdc);
171 
172     // Check two full clears, same color
173     sdc->clear(fullRect, kColor1f);
174     sdc->clear(fullRect, kColor1f);
175     if (!check_rect(dContext, sdc.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
176         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
177                failX, failY);
178     }
179 
180     sdc = newSDC(dContext, kW, kH);
181     SkASSERT(sdc);
182 
183     // Check two full clears, different colors
184     sdc->clear(fullRect, kColor1f);
185     sdc->clear(fullRect, kColor2f);
186     if (!check_rect(dContext, sdc.get(), fullRect, kColor2, &actualValue, &failX, &failY)) {
187         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue,
188                failX, failY);
189     }
190 
191     sdc = newSDC(dContext, kW, kH);
192     SkASSERT(sdc);
193 
194     // Test a full clear followed by a same color inset clear
195     sdc->clear(fullRect, kColor1f);
196     sdc->clear(mid1Rect, kColor1f);
197     if (!check_rect(dContext, sdc.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
198         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
199                failX, failY);
200     }
201 
202     sdc = newSDC(dContext, kW, kH);
203     SkASSERT(sdc);
204 
205     // Test a inset clear followed by same color full clear
206     sdc->clear(mid1Rect, kColor1f);
207     sdc->clear(fullRect, kColor1f);
208     if (!check_rect(dContext, sdc.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
209         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
210                failX, failY);
211     }
212 
213     sdc = newSDC(dContext, kW, kH);
214     SkASSERT(sdc);
215 
216     // Test a full clear followed by a different color inset clear
217     sdc->clear(fullRect, kColor1f);
218     sdc->clear(mid1Rect, kColor2f);
219     if (!check_rect(dContext, sdc.get(), mid1Rect, kColor2, &actualValue, &failX, &failY)) {
220         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue,
221                failX, failY);
222     }
223 
224     if (!check_ring(dContext, sdc.get(), /*inset=*/ 0, kColor1, &actualValue, &failX, &failY)) {
225         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
226                failX, failY);
227     }
228 
229     sdc = newSDC(dContext, kW, kH);
230     SkASSERT(sdc);
231 
232     // Test a inset clear followed by a different full clear
233     sdc->clear(mid1Rect, kColor2f);
234     sdc->clear(fullRect, kColor1f);
235     if (!check_rect(dContext, sdc.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
236         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
237                failX, failY);
238     }
239 
240     sdc = newSDC(dContext, kW, kH);
241     SkASSERT(sdc);
242 
243     // Check three nested clears from largest to smallest where outermost and innermost are same
244     // color.
245     sdc->clear(fullRect, kColor1f);
246     sdc->clear(mid1Rect, kColor2f);
247     sdc->clear(mid2Rect, kColor1f);
248     if (!check_rect(dContext, sdc.get(), mid2Rect, kColor1, &actualValue, &failX, &failY)) {
249         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
250                failX, failY);
251     }
252 
253     if (!check_ring(dContext, sdc.get(), /*inset=*/ 1, kColor2, &actualValue, &failX, &failY)) {
254         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue,
255                failX, failY);
256     }
257 
258     if (!check_ring(dContext, sdc.get(), /*inset=*/ 0, kColor1, &actualValue, &failX, &failY)) {
259         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
260                failX, failY);
261     }
262 
263     sdc = newSDC(dContext, kW, kH);
264     SkASSERT(sdc);
265 
266     // Swap the order of the second two clears in the above test.
267     sdc->clear(fullRect, kColor1f);
268     sdc->clear(mid2Rect, kColor1f);
269     sdc->clear(mid1Rect, kColor2f);
270     if (!check_rect(dContext, sdc.get(), mid1Rect, kColor2, &actualValue, &failX, &failY)) {
271         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue,
272                failX, failY);
273     }
274     if (!check_ring(dContext, sdc.get(), /*inset=*/ 0, kColor1, &actualValue, &failX, &failY)) {
275         ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
276                failX, failY);
277     }
278 
279     // Clear calls need to remain ClearOps for the following combining-tests to work as expected
280     if (!dContext->priv().caps()->performColorClearsAsDraws() &&
281         !dContext->priv().caps()->performStencilClearsAsDraws() &&
282         !dContext->priv().caps()->performPartialClearsAsDraws()) {
283         static constexpr SkIRect kScissorRect = SkIRect::MakeXYWH(1, 1, kW-1, kH-1);
284 
285         // Try combining a pure-color clear w/ a combined stencil & color clear
286         // (re skbug.com/10963)
287         {
288             sdc = newSDC(dContext, kW, kH);
289             SkASSERT(sdc);
290 
291             sdc->clearStencilClip(kScissorRect, true);
292             // This color clear can combine w/ the preceding stencil clear
293             sdc->clear(kScissorRect, SK_PMColor4fWHITE);
294 
295             // This should combine w/ the prior combined clear and overwrite the color
296             sdc->clear(kScissorRect, SK_PMColor4fBLACK);
297 
298             auto opsTask = sdc->getOpsTask();
299             REPORTER_ASSERT(reporter, opsTask->numOpChains() == 1);
300 
301             const ClearOp& clearOp = opsTask->getChain(0)->cast<ClearOp>();
302 
303             constexpr std::array<float, 4> kExpected { 0, 0, 0, 1 };
304             REPORTER_ASSERT(reporter, clearOp.color() == kExpected);
305             REPORTER_ASSERT(reporter, clearOp.stencilInsideMask());
306 
307             dContext->flushAndSubmit();
308         }
309 
310         // Try combining a pure-stencil clear w/ a combined stencil & color clear
311         // (re skbug.com/10963)
312         {
313             sdc = newSDC(dContext, kW, kH);
314             SkASSERT(sdc);
315 
316             sdc->clearStencilClip(kScissorRect, true);
317             // This color clear can combine w/ the preceding stencil clear
318             sdc->clear(kScissorRect, SK_PMColor4fWHITE);
319 
320             // This should combine w/ the prior combined clear and overwrite the 'insideStencilMask'
321             // field
322             sdc->clearStencilClip(kScissorRect, false);
323 
324             auto opsTask = sdc->getOpsTask();
325             REPORTER_ASSERT(reporter, opsTask->numOpChains() == 1);
326 
327             const ClearOp& clearOp = opsTask->getChain(0)->cast<ClearOp>();
328 
329             constexpr std::array<float, 4> kExpected { 1, 1, 1, 1 };
330             REPORTER_ASSERT(reporter, clearOp.color() == kExpected);
331             REPORTER_ASSERT(reporter, !clearOp.stencilInsideMask());
332 
333             dContext->flushAndSubmit();
334         }
335     }
336 }
337 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ClearOp,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)338 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ClearOp, reporter, ctxInfo, CtsEnforcement::kApiLevel_T) {
339     // Regular clear
340     clear_op_test(reporter, ctxInfo.directContext());
341 
342     // Force drawing for clears
343     GrContextOptions options(ctxInfo.options());
344     options.fUseDrawInsteadOfClear = GrContextOptions::Enable::kYes;
345     sk_gpu_test::GrContextFactory workaroundFactory(options);
346     clear_op_test(reporter, workaroundFactory.get(ctxInfo.type()));
347 }
348 
fullscreen_clear_with_layer_test(skiatest::Reporter * reporter,GrRecordingContext * rContext)349 void fullscreen_clear_with_layer_test(skiatest::Reporter* reporter, GrRecordingContext* rContext) {
350     const SkImageInfo ii = SkImageInfo::Make(400, 77, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
351 
352     sk_sp<SkSurface> surf = SkSurfaces::RenderTarget(rContext, skgpu::Budgeted::kYes, ii);
353     SkCanvas* canvas = surf->getCanvas();
354 
355     SkPaint paints[2];
356     paints[0].setColor(SK_ColorGREEN);
357     paints[1].setColor(SK_ColorGRAY);
358 
359     static const int kLeftX = 158;
360     static const int kMidX = 258;
361     static const int kRightX = 383;
362     static const int kTopY = 26;
363     static const int kBotY = 51;
364 
365     const SkRect rects[2] = {
366         { kLeftX, kTopY, kMidX, kBotY },
367         { kMidX, kTopY, kRightX, kBotY },
368     };
369 
370     for (int i = 0; i < 2; ++i) {
371         // the bounds parameter is required to cause a full screen clear
372         canvas->saveLayer(&rects[i], nullptr);
373             canvas->drawRect(rects[i], paints[i]);
374         canvas->restore();
375     }
376 
377     SkBitmap bm;
378     bm.allocPixels(ii, 0);
379 
380     SkAssertResult(surf->readPixels(bm, 0, 0));
381 
382     bool isCorrect = true;
383     for (int y = kTopY; isCorrect && y < kBotY; ++y) {
384         const uint32_t* sl = bm.getAddr32(0, y);
385 
386         for (int x = kLeftX; x < kMidX; ++x) {
387             if (SK_ColorGREEN != sl[x]) {
388                 isCorrect = false;
389                 break;
390             }
391         }
392 
393         for (int x = kMidX; x < kRightX; ++x) {
394             if (SK_ColorGRAY != sl[x]) {
395                 isCorrect = false;
396                 break;
397             }
398         }
399     }
400 
401     REPORTER_ASSERT(reporter, isCorrect);
402 }
403 // From crbug.com/768134
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(FullScreenClearWithLayers,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)404 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(FullScreenClearWithLayers,
405                                        reporter,
406                                        ctxInfo,
407                                        CtsEnforcement::kApiLevel_T) {
408     // Regular clear
409     fullscreen_clear_with_layer_test(reporter, ctxInfo.directContext());
410 
411     // Use draws for clears
412     GrContextOptions options(ctxInfo.options());
413     options.fUseDrawInsteadOfClear = GrContextOptions::Enable::kYes;
414     sk_gpu_test::GrContextFactory workaroundFactory(options);
415     fullscreen_clear_with_layer_test(reporter, workaroundFactory.get(ctxInfo.type()));
416 }
417