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