Flutter Engine
The Flutter Engine
ClearTest.cpp
Go to the documentation of this file.
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
17#include "include/core/SkRect.h"
38#include "tests/Test.h"
39
40#include <array>
41#include <cstdint>
42#include <memory>
43
45
48
49static 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
62static bool check_rect(GrDirectContext* dContext,
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
73
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
94static bool check_ring(GrDirectContext* dContext,
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
105
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
130std::unique_ptr<SurfaceDrawContext> newSDC(GrRecordingContext* rContext, int w, int h) {
131 return SurfaceDrawContext::Make(rContext, GrColorType::kRGBA_8888, nullptr,
133 /*label=*/{});
134}
135
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);
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);
332
333 dContext->flushAndSubmit();
334 }
335 }
336}
337
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
351
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
405 reporter,
406 ctxInfo,
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}
static void readback(const SkBitmap &src, int *result, int resultCount)
Definition: BlurTest.cpp:264
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ClearOp, reporter, ctxInfo, CtsEnforcement::kApiLevel_T)
Definition: ClearTest.cpp:338
static void clear_op_test(skiatest::Reporter *reporter, GrDirectContext *dContext)
Definition: ClearTest.cpp:136
skgpu::ganesh::SurfaceDrawContext SurfaceDrawContext
Definition: ClearTest.cpp:46
static bool check_ring(GrDirectContext *dContext, SurfaceDrawContext *sdc, int inset, uint32_t expectedValue, uint32_t *actualValue, int *failX, int *failY)
Definition: ClearTest.cpp:94
void fullscreen_clear_with_layer_test(skiatest::Reporter *reporter, GrRecordingContext *rContext)
Definition: ClearTest.cpp:349
std::unique_ptr< SurfaceDrawContext > newSDC(GrRecordingContext *rContext, int w, int h)
Definition: ClearTest.cpp:130
static bool check_rect(GrDirectContext *dContext, SurfaceDrawContext *sdc, const SkIRect &rect, uint32_t expectedValue, uint32_t *actualValue, int *failX, int *failY)
Definition: ClearTest.cpp:62
skgpu::ganesh::ClearOp ClearOp
Definition: ClearTest.cpp:47
static bool pixel_matches(const SkPixmap &pm, int x, int y, uint32_t expectedValue, uint32_t *actualValue, int *failX, int *failY)
Definition: ClearTest.cpp:49
const char * options
reporter
Definition: FontMgrTest.cpp:39
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
uint32_t GrColor
Definition: GrColor.h:25
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
constexpr SkPMColor4f SK_PMColor4fWHITE
Definition: SkColorData.h:380
constexpr SkPMColor4f SK_PMColor4fBLACK
Definition: SkColorData.h:379
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
constexpr SkColor SK_ColorGRAY
Definition: SkColor.h:113
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define ERRORF(r,...)
Definition: Test.h:293
constexpr int kH
constexpr int kW
const GrCaps * caps() const
bool performPartialClearsAsDraws() const
Definition: GrCaps.h:426
bool performColorClearsAsDraws() const
Definition: GrCaps.h:431
bool performStencilClearsAsDraws() const
Definition: GrCaps.h:438
void flushAndSubmit(GrSyncCpu sync=GrSyncCpu::kNo)
GrDirectContextPriv priv()
const T & cast() const
Definition: GrOp.h:148
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition: SkBitmap.cpp:258
uint32_t * getAddr32(int x, int y) const
Definition: SkBitmap.h:1260
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void restore()
Definition: SkCanvas.cpp:461
void setColor(SkColor color)
Definition: SkPaint.cpp:119
const uint32_t * addr32() const
Definition: SkPixmap.h:352
int width() const
Definition: SkPixmap.h:160
SkCanvas * getCanvas()
Definition: SkSurface.cpp:82
bool readPixels(const SkPixmap &dst, int srcX, int srcY)
Definition: SkSurface.cpp:125
GrDirectContext * get(ContextType type, ContextOverrides overrides=ContextOverrides::kNone)
bool stencilInsideMask() const
Definition: ClearOp.h:36
const std::array< float, 4 > & color() const
Definition: ClearOp.h:35
bool readPixels(GrDirectContext *dContext, GrPixmap dst, SkIPoint srcPt)
static std::unique_ptr< SurfaceDrawContext > Make(GrRecordingContext *, GrColorType, sk_sp< GrSurfaceProxy >, sk_sp< SkColorSpace >, GrSurfaceOrigin, const SkSurfaceProps &)
static SkColor kColor1
static SkColor kColor2
double y
double x
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
static SkRect inset(const SkRect &r)
SkScalar w
SkScalar h
Definition: SkRect.h:32
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition: SkRect.h:104
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static SkRGBA4f FromBytes_RGBA(uint32_t color)