blob: aa9e5212544c4de28d3f45432e920e547f72d62e [file] [log] [blame]
Brian Salomonf84dfd62020-12-29 15:09:33 -05001/*
2 * Copyright 2020 Google LLC.
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/SkCanvas.h"
9#include "include/core/SkImage.h"
10#include "include/core/SkSurface.h"
11#include "include/effects/SkGradientShader.h"
12#include "include/gpu/GrDirectContext.h"
13#include "src/core/SkAutoPixmapStorage.h"
14#include "src/core/SkConvertPixels.h"
15#include "src/gpu/GrDirectContextPriv.h"
16#include "src/gpu/GrImageInfo.h"
17#include "src/gpu/GrSurfaceContext.h"
18#include "tests/Test.h"
19#include "tests/TestUtils.h"
20#include "tools/ToolUtils.h"
21#include "tools/gpu/BackendTextureImageFactory.h"
22#include "tools/gpu/GrContextFactory.h"
23#include "tools/gpu/ProxyUtils.h"
24
25#include <initializer_list>
26
27static constexpr int min_rgb_channel_bits(SkColorType ct) {
28 switch (ct) {
29 case kUnknown_SkColorType: return 0;
30 case kAlpha_8_SkColorType: return 0;
31 case kA16_unorm_SkColorType: return 0;
32 case kA16_float_SkColorType: return 0;
33 case kRGB_565_SkColorType: return 5;
34 case kARGB_4444_SkColorType: return 4;
35 case kR8G8_unorm_SkColorType: return 8;
36 case kR16G16_unorm_SkColorType: return 16;
37 case kR16G16_float_SkColorType: return 16;
38 case kRGBA_8888_SkColorType: return 8;
39 case kRGB_888x_SkColorType: return 8;
40 case kBGRA_8888_SkColorType: return 8;
41 case kRGBA_1010102_SkColorType: return 10;
42 case kRGB_101010x_SkColorType: return 10;
43 case kBGRA_1010102_SkColorType: return 10;
44 case kBGR_101010x_SkColorType: return 10;
45 case kGray_8_SkColorType: return 8; // counting gray as "rgb"
46 case kRGBA_F16Norm_SkColorType: return 10; // just counting the mantissa
47 case kRGBA_F16_SkColorType: return 10; // just counting the mantissa
48 case kRGBA_F32_SkColorType: return 23; // just counting the mantissa
49 case kR16G16B16A16_unorm_SkColorType: return 16;
50 }
51 SkUNREACHABLE;
52}
53
54static constexpr int alpha_channel_bits(SkColorType ct) {
55 switch (ct) {
56 case kUnknown_SkColorType: return 0;
57 case kAlpha_8_SkColorType: return 8;
58 case kA16_unorm_SkColorType: return 16;
59 case kA16_float_SkColorType: return 16;
60 case kRGB_565_SkColorType: return 0;
61 case kARGB_4444_SkColorType: return 4;
62 case kR8G8_unorm_SkColorType: return 0;
63 case kR16G16_unorm_SkColorType: return 0;
64 case kR16G16_float_SkColorType: return 0;
65 case kRGBA_8888_SkColorType: return 8;
66 case kRGB_888x_SkColorType: return 0;
67 case kBGRA_8888_SkColorType: return 8;
68 case kRGBA_1010102_SkColorType: return 2;
69 case kRGB_101010x_SkColorType: return 0;
70 case kBGRA_1010102_SkColorType: return 2;
71 case kBGR_101010x_SkColorType: return 0;
72 case kGray_8_SkColorType: return 0;
73 case kRGBA_F16Norm_SkColorType: return 10; // just counting the mantissa
74 case kRGBA_F16_SkColorType: return 10; // just counting the mantissa
75 case kRGBA_F32_SkColorType: return 23; // just counting the mantissa
76 case kR16G16B16A16_unorm_SkColorType: return 16;
77 }
78 SkUNREACHABLE;
79}
80
Brian Salomon237911a2021-01-05 12:33:26 -050081std::vector<SkIRect> make_long_rect_array(int w, int h) {
82 return {
83 // entire thing
84 SkIRect::MakeWH(w, h),
85 // larger on all sides
86 SkIRect::MakeLTRB(-10, -10, w + 10, h + 10),
87 // fully contained
88 SkIRect::MakeLTRB(w/4, h/4, 3*w/4, 3*h/4),
89 // outside top left
90 SkIRect::MakeLTRB(-10, -10, -1, -1),
91 // touching top left corner
92 SkIRect::MakeLTRB(-10, -10, 0, 0),
93 // overlapping top left corner
94 SkIRect::MakeLTRB(-10, -10, w/4, h/4),
95 // overlapping top left and top right corners
96 SkIRect::MakeLTRB(-10, -10, w + 10, h/4),
97 // touching entire top edge
98 SkIRect::MakeLTRB(-10, -10, w + 10, 0),
99 // overlapping top right corner
100 SkIRect::MakeLTRB(3*w/4, -10, w + 10, h/4),
101 // contained in x, overlapping top edge
102 SkIRect::MakeLTRB(w/4, -10, 3*w/4, h/4),
103 // outside top right corner
104 SkIRect::MakeLTRB(w + 1, -10, w + 10, -1),
105 // touching top right corner
106 SkIRect::MakeLTRB(w, -10, w + 10, 0),
107 // overlapping top left and bottom left corners
108 SkIRect::MakeLTRB(-10, -10, w/4, h + 10),
109 // touching entire left edge
110 SkIRect::MakeLTRB(-10, -10, 0, h + 10),
111 // overlapping bottom left corner
112 SkIRect::MakeLTRB(-10, 3*h/4, w/4, h + 10),
113 // contained in y, overlapping left edge
114 SkIRect::MakeLTRB(-10, h/4, w/4, 3*h/4),
115 // outside bottom left corner
116 SkIRect::MakeLTRB(-10, h + 1, -1, h + 10),
117 // touching bottom left corner
118 SkIRect::MakeLTRB(-10, h, 0, h + 10),
119 // overlapping bottom left and bottom right corners
120 SkIRect::MakeLTRB(-10, 3*h/4, w + 10, h + 10),
121 // touching entire left edge
122 SkIRect::MakeLTRB(0, h, w, h + 10),
123 // overlapping bottom right corner
124 SkIRect::MakeLTRB(3*w/4, 3*h/4, w + 10, h + 10),
125 // overlapping top right and bottom right corners
126 SkIRect::MakeLTRB(3*w/4, -10, w + 10, h + 10),
127 };
128}
129
130std::vector<SkIRect> make_short_rect_array(int w, int h) {
131 return {
132 // entire thing
133 SkIRect::MakeWH(w, h),
134 // fully contained
135 SkIRect::MakeLTRB(w/4, h/4, 3*w/4, 3*h/4),
136 // overlapping top right corner
137 SkIRect::MakeLTRB(3*w/4, -10, w + 10, h/4),
138 };
139}
140
Brian Salomonf84dfd62020-12-29 15:09:33 -0500141namespace {
142
143struct GpuReadPixelTestRules {
144 // Test unpremul sources? We could omit this and detect that creating the source of the read
145 // failed but having it lets us skip generating reference color data.
146 bool fAllowUnpremulSrc = true;
147 // Expect read function to succeed for kUnpremul?
148 bool fAllowUnpremulRead = true;
149 // Are reads that are overlapping but not contained by the src bounds expected to succeed?
150 bool fUncontainedRectSucceeds = true;
151};
152
153// Makes a src populated with the pixmap. The src should get its image info (or equivalent) from
154// the pixmap.
155template <typename T> using GpuSrcFactory = T(SkPixmap&);
156
Brian Salomon237911a2021-01-05 12:33:26 -0500157enum class Result {
Brian Salomonf84dfd62020-12-29 15:09:33 -0500158 kFail,
159 kSuccess,
160 kExcusedFailure,
161};
162
163// Does a read from the T into the pixmap.
164template <typename T>
Brian Salomon237911a2021-01-05 12:33:26 -0500165using GpuReadSrcFn = Result(const T&, const SkIPoint& offset, const SkPixmap&);
166
167// Makes a dst for testing writes.
168template <typename T> using GpuDstFactory = T(const SkImageInfo& ii);
169
170// Does a write from the pixmap to the T.
171template <typename T>
172using GpuWriteDstFn = Result(const T&, const SkIPoint& offset, const SkPixmap&);
173
174// To test the results of the write we do a read. This reads the entire src T. It should do a non-
175// converting read (i.e. the image info of the returned pixmap matches that of the T).
176template <typename T>
177using GpuReadDstFn = SkAutoPixmapStorage(const T&);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500178
179} // anonymous namespace
180
Brian Salomon237911a2021-01-05 12:33:26 -0500181SkPixmap make_pixmap_have_valid_alpha_type(SkPixmap pm) {
182 if (pm.alphaType() == kUnknown_SkAlphaType) {
183 return {pm.info().makeAlphaType(kUnpremul_SkAlphaType), pm.addr(), pm.rowBytes()};
184 }
185 return pm;
186}
187
188SkAutoPixmapStorage make_ref_data(const SkImageInfo& info, bool forceOpaque) {
189 SkAutoPixmapStorage result;
190 result.alloc(info);
191 auto surface = SkSurface::MakeRasterDirect(make_pixmap_have_valid_alpha_type(result));
192 if (!surface) {
193 return result;
194 }
195
196 SkPoint pts1[] = {{0, 0}, {float(info.width()), float(info.height())}};
197 static constexpr SkColor kColors1[] = {SK_ColorGREEN, SK_ColorRED};
198 SkPaint paint;
199 paint.setShader(SkGradientShader::MakeLinear(pts1, kColors1, nullptr, 2, SkTileMode::kClamp));
200 surface->getCanvas()->drawPaint(paint);
201
202 SkPoint pts2[] = {{float(info.width()), 0}, {0, float(info.height())}};
203 static constexpr SkColor kColors2[] = {SK_ColorBLUE, SK_ColorBLACK};
204 paint.setShader(SkGradientShader::MakeLinear(pts2, kColors2, nullptr, 2, SkTileMode::kClamp));
205 paint.setBlendMode(SkBlendMode::kPlus);
206 surface->getCanvas()->drawPaint(paint);
207
208 // If not opaque add some fractional alpha.
209 if (info.alphaType() != kOpaque_SkAlphaType && !forceOpaque) {
210 static constexpr SkColor kColors3[] = {SK_ColorWHITE,
211 SK_ColorWHITE,
212 0x60FFFFFF,
213 SK_ColorWHITE,
214 SK_ColorWHITE};
215 static constexpr SkScalar kPos3[] = {0.f, 0.15f, 0.5f, 0.85f, 1.f};
216 paint.setShader(SkGradientShader::MakeRadial({info.width()/2.f, info.height()/2.f},
217 (info.width() + info.height())/10.f,
218 kColors3, kPos3, 5, SkTileMode::kMirror));
219 paint.setBlendMode(SkBlendMode::kDstIn);
220 surface->getCanvas()->drawPaint(paint);
221 }
222 return result;
223};
224
Brian Salomonf84dfd62020-12-29 15:09:33 -0500225template <typename T>
226static void gpu_read_pixels_test_driver(skiatest::Reporter* reporter,
227 const GpuReadPixelTestRules& rules,
228 const std::function<GpuSrcFactory<T>>& srcFactory,
229 const std::function<GpuReadSrcFn<T>>& read) {
230 // Separate this out just to give it some line width to breathe. Note 'srcPixels' should have
231 // the same image info as src. We will do a converting readPixels() on it to get the data
232 // to compare with the results of 'read'.
233 auto runTest = [&](const T& src,
234 const SkPixmap& srcPixels,
235 const SkImageInfo& readInfo,
Brian Salomon237911a2021-01-05 12:33:26 -0500236 SkIPoint offset) {
Brian Salomonf84dfd62020-12-29 15:09:33 -0500237 const bool csConversion =
238 !SkColorSpace::Equals(readInfo.colorSpace(), srcPixels.info().colorSpace());
239 const auto readCT = readInfo.colorType();
240 const auto readAT = readInfo.alphaType();
241 const auto srcCT = srcPixels.info().colorType();
242 const auto srcAT = srcPixels.info().alphaType();
243 const auto rect = SkIRect::MakeWH(readInfo.width(), readInfo.height()).makeOffset(offset);
244 const auto surfBounds = SkIRect::MakeWH(srcPixels.width(), srcPixels.height());
245 const size_t readBpp = SkColorTypeBytesPerPixel(readCT);
246
247 // Make the row bytes in the dst be loose for extra stress.
248 const size_t dstRB = readBpp * readInfo.width() + 10 * readBpp;
249 // This will make the last row tight.
250 const size_t dstSize = readInfo.computeByteSize(dstRB);
251 std::unique_ptr<char[]> dstData(new char[dstSize]);
252 SkPixmap dstPixels(readInfo, dstData.get(), dstRB);
253 // Initialize with an arbitrary value for each byte. Later we will check that only the
254 // correct part of the destination gets overwritten by 'read'.
255 static constexpr auto kInitialByte = static_cast<char>(0x1B);
256 std::fill_n(static_cast<char*>(dstPixels.writable_addr()),
257 dstPixels.computeByteSize(),
258 kInitialByte);
259
Brian Salomon237911a2021-01-05 12:33:26 -0500260 const Result result = read(src, offset, dstPixels);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500261
262 if (!SkIRect::Intersects(rect, surfBounds)) {
Brian Salomon237911a2021-01-05 12:33:26 -0500263 REPORTER_ASSERT(reporter, result != Result::kSuccess);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500264 } else if (readCT == kUnknown_SkColorType) {
Brian Salomon237911a2021-01-05 12:33:26 -0500265 REPORTER_ASSERT(reporter, result != Result::kSuccess);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500266 } else if ((readAT == kUnknown_SkAlphaType) != (srcAT == kUnknown_SkAlphaType)) {
Brian Salomon237911a2021-01-05 12:33:26 -0500267 REPORTER_ASSERT(reporter, result != Result::kSuccess);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500268 } else if (!rules.fUncontainedRectSucceeds && !surfBounds.contains(rect)) {
Brian Salomon237911a2021-01-05 12:33:26 -0500269 REPORTER_ASSERT(reporter, result != Result::kSuccess);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500270 } else if (!rules.fAllowUnpremulRead && readAT == kUnpremul_SkAlphaType) {
Brian Salomon237911a2021-01-05 12:33:26 -0500271 REPORTER_ASSERT(reporter, result != Result::kSuccess);
272 } else if (result == Result::kFail) {
Brian Salomonf84dfd62020-12-29 15:09:33 -0500273 // TODO: Support RGB/BGR 101010x, BGRA 1010102 on the GPU.
274 if (SkColorTypeToGrColorType(readCT) != GrColorType::kUnknown) {
275 ERRORF(reporter,
276 "Read failed. Src CT: %s, Src AT: %s Read CT: %s, Read AT: %s, "
277 "Rect [%d, %d, %d, %d], CS conversion: %d\n",
278 ToolUtils::colortype_name(srcCT), ToolUtils::alphatype_name(srcAT),
279 ToolUtils::colortype_name(readCT), ToolUtils::alphatype_name(readAT),
280 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, csConversion);
281 }
282 return result;
283 }
284
285 bool guardOk = true;
286 auto guardCheck = [](char x) { return x == kInitialByte; };
287
288 // Considering the rect we tried to read and the surface bounds figure out which pixels in
289 // both src and dst space should actually have been read and written.
290 SkIRect srcReadRect;
Brian Salomon237911a2021-01-05 12:33:26 -0500291 if (result == Result::kSuccess && srcReadRect.intersect(surfBounds, rect)) {
Brian Salomonf84dfd62020-12-29 15:09:33 -0500292 SkIRect dstWriteRect = srcReadRect.makeOffset(-rect.fLeft, -rect.fTop);
293
294 const bool lumConversion =
295 !(SkColorTypeChannelFlags(srcCT) & kGray_SkColorChannelFlag) &&
296 (SkColorTypeChannelFlags(readCT) & kGray_SkColorChannelFlag);
297 // A CS or luminance conversion allows a 3 value difference and otherwise a 2 value
298 // difference. Note that sometimes read back on GPU can be lossy even when there no
299 // conversion at all because GPU->CPU read may go to a lower bit depth format and then
300 // be promoted back to the original type. For example, GL ES cannot read to 1010102, so
301 // we go through 8888.
302 float numer = (lumConversion || csConversion) ? 3.f : 2.f;
303 // Allow some extra tolerance if unpremuling.
304 if (srcAT == kPremul_SkAlphaType && readAT == kUnpremul_SkAlphaType) {
305 numer += 1;
306 }
307 int rgbBits = std::min({min_rgb_channel_bits(readCT), min_rgb_channel_bits(srcCT), 8});
308 float tol = numer / (1 << rgbBits);
309 float alphaTol = 0;
310 if (readAT != kOpaque_SkAlphaType && srcAT != kOpaque_SkAlphaType) {
311 // Alpha can also get squashed down to 8 bits going through an intermediate
312 // color format.
313 const int alphaBits = std::min({alpha_channel_bits(readCT),
314 alpha_channel_bits(srcCT),
315 8});
316 alphaTol = 2.f / (1 << alphaBits);
317 }
318
319 const float tols[4] = {tol, tol, tol, alphaTol};
320 auto error = std::function<ComparePixmapsErrorReporter>([&](int x, int y,
321 const float diffs[4]) {
322 SkASSERT(x >= 0 && y >= 0);
323 ERRORF(reporter,
324 "Src CT: %s, Src AT: %s, Read CT: %s, Read AT: %s, Rect [%d, %d, %d, %d]"
325 ", CS conversion: %d\n"
326 "Error at %d, %d. Diff in floats: (%f, %f, %f %f)",
327 ToolUtils::colortype_name(srcCT), ToolUtils::alphatype_name(srcAT),
328 ToolUtils::colortype_name(readCT), ToolUtils::alphatype_name(readAT),
329 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, csConversion, x, y,
330 diffs[0], diffs[1], diffs[2], diffs[3]);
331 });
332 SkAutoPixmapStorage ref;
333 SkImageInfo refInfo = readInfo.makeDimensions(dstWriteRect.size());
334 ref.alloc(refInfo);
335 if (readAT == kUnknown_SkAlphaType) {
336 // Do a spoofed read where src and dst alpha type are both kUnpremul. This will
337 // allow SkPixmap readPixels to succeed and won't do any alpha type conversion.
338 SkPixmap unpremulRef(refInfo.makeAlphaType(kUnpremul_SkAlphaType),
339 ref.addr(),
340 ref.rowBytes());
341 SkPixmap unpremulSRc(srcPixels.info().makeAlphaType(kUnpremul_SkAlphaType),
342 srcPixels.addr(),
343 srcPixels.rowBytes());
344
345 unpremulSRc.readPixels(unpremulRef, srcReadRect.x(), srcReadRect.y());
346 } else {
347 srcPixels.readPixels(ref, srcReadRect.x(), srcReadRect.y());
348 }
349 // This is the part of dstPixels that should have been updated.
350 SkPixmap actual;
351 SkAssertResult(dstPixels.extractSubset(&actual, dstWriteRect));
352 ComparePixels(ref, actual, tols, error);
353
354 const auto* v = dstData.get();
355 const auto* end = dstData.get() + dstSize;
356 guardOk = std::all_of(v, v + dstWriteRect.top() * dstPixels.rowBytes(), guardCheck);
357 v += dstWriteRect.top() * dstPixels.rowBytes();
358 for (int y = dstWriteRect.top(); y < dstWriteRect.bottom(); ++y) {
359 guardOk |= std::all_of(v, v + dstWriteRect.left() * readBpp, guardCheck);
360 auto pad = v + dstWriteRect.right() * readBpp;
361 auto rowEnd = std::min(end, v + dstPixels.rowBytes());
362 // min protects against reading past the end of the tight last row.
363 guardOk |= std::all_of(pad, rowEnd, guardCheck);
364 v = rowEnd;
365 }
366 guardOk |= std::all_of(v, end, guardCheck);
367 } else {
368 guardOk = std::all_of(dstData.get(), dstData.get() + dstSize, guardCheck);
369 }
370 if (!guardOk) {
371 ERRORF(reporter,
372 "Result pixels modified result outside read rect [%d, %d, %d, %d]. "
373 "Src CT: %s, Read CT: %s, CS conversion: %d",
374 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
375 ToolUtils::colortype_name(srcCT), ToolUtils::colortype_name(readCT),
376 csConversion);
377 }
378 return result;
379 };
380
381 static constexpr int kW = 16;
382 static constexpr int kH = 16;
383
Brian Salomon237911a2021-01-05 12:33:26 -0500384 const std::vector<SkIRect> longRectArray = make_long_rect_array(kW, kH);
385 const std::vector<SkIRect> shortRectArray = make_short_rect_array(kW, kH);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500386
Brian Salomonf84dfd62020-12-29 15:09:33 -0500387 // We ensure we use the long array once per src and read color type and otherwise use the
388 // short array to improve test run time.
389 // Also, some color types have no alpha values and thus Opaque Premul and Unpremul are
390 // equivalent. Just ensure each redundant AT is tested once with each CT (src and read).
391 // Similarly, alpha-only color types behave the same for all alpha types so just test premul
392 // after one iter.
Brian Salomon237911a2021-01-05 12:33:26 -0500393 // We consider a src or read CT thoroughly tested once it has run through the long rect array
Brian Salomonf84dfd62020-12-29 15:09:33 -0500394 // and full complement of alpha types with one successful read in the loop.
395 std::array<bool, kLastEnum_SkColorType + 1> srcCTTestedThoroughly = {},
396 readCTTestedThoroughly = {};
397 for (int sat = 0; sat < kLastEnum_SkAlphaType; ++sat) {
398 const auto srcAT = static_cast<SkAlphaType>(sat);
399 if (srcAT == kUnpremul_SkAlphaType && !rules.fAllowUnpremulSrc) {
400 continue;
401 }
402 for (int sct = 0; sct <= kLastEnum_SkColorType; ++sct) {
403 const auto srcCT = static_cast<SkColorType>(sct);
Brian Salomon237911a2021-01-05 12:33:26 -0500404 // We always make our ref data as F32
405 auto refInfo = SkImageInfo::Make(kW, kH,
406 kRGBA_F32_SkColorType,
407 srcAT,
408 SkColorSpace::MakeSRGB());
409 // 1010102 formats have an issue where it's easy to make a resulting
410 // color where r, g, or b is greater than a. CPU/GPU differ in whether the stored color
411 // channels are clipped to the alpha value. CPU clips but GPU does not.
412 // Note that we only currently use srcCT for the 1010102 workaround. If we remove this
413 // we can also put the ref data setup above the srcCT loop.
414 bool forceOpaque = srcAT == kPremul_SkAlphaType &&
415 (srcCT == kRGBA_1010102_SkColorType || srcCT == kBGRA_1010102_SkColorType);
416
417 SkAutoPixmapStorage srcPixels = make_ref_data(refInfo, forceOpaque);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500418 auto src = srcFactory(srcPixels);
419 if (!src) {
420 continue;
421 }
422 if (SkColorTypeIsAlwaysOpaque(srcCT) && srcCTTestedThoroughly[srcCT] &&
423 (kPremul_SkAlphaType == srcAT || kUnpremul_SkAlphaType == srcAT)) {
424 continue;
425 }
426 if (SkColorTypeIsAlphaOnly(srcCT) && srcCTTestedThoroughly[srcCT] &&
427 (kUnpremul_SkAlphaType == srcAT ||
428 kOpaque_SkAlphaType == srcAT ||
429 kUnknown_SkAlphaType == srcAT)) {
430 continue;
431 }
432 for (int rct = 0; rct <= kLastEnum_SkColorType; ++rct) {
433 const auto readCT = static_cast<SkColorType>(rct);
434 for (const sk_sp<SkColorSpace>& readCS :
435 {SkColorSpace::MakeSRGB(), SkColorSpace::MakeSRGBLinear()}) {
436 for (int at = 0; at <= kLastEnum_SkAlphaType; ++at) {
437 const auto readAT = static_cast<SkAlphaType>(at);
438 if (srcAT != kOpaque_SkAlphaType && readAT == kOpaque_SkAlphaType) {
439 // This doesn't make sense.
440 continue;
441 }
442 if (SkColorTypeIsAlwaysOpaque(readCT) && readCTTestedThoroughly[readCT] &&
443 (kPremul_SkAlphaType == readAT || kUnpremul_SkAlphaType == readAT)) {
444 continue;
445 }
446 if (SkColorTypeIsAlphaOnly(readCT) && readCTTestedThoroughly[readCT] &&
447 (kUnpremul_SkAlphaType == readAT ||
448 kOpaque_SkAlphaType == readAT ||
449 kUnknown_SkAlphaType == readAT)) {
450 continue;
451 }
452 const auto& rects =
453 srcCTTestedThoroughly[sct] && readCTTestedThoroughly[rct]
454 ? shortRectArray
455 : longRectArray;
456 for (const auto& rect : rects) {
457 const auto readInfo = SkImageInfo::Make(rect.width(), rect.height(),
458 readCT, readAT, readCS);
Brian Salomon237911a2021-01-05 12:33:26 -0500459 const SkIPoint offset = rect.topLeft();
460 Result r = runTest(src, srcPixels, readInfo, offset);
461 if (r == Result::kSuccess) {
Brian Salomonf84dfd62020-12-29 15:09:33 -0500462 srcCTTestedThoroughly[sct] = true;
463 readCTTestedThoroughly[rct] = true;
464 }
465 }
466 }
467 }
468 }
469 }
470 }
471}
472
473DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceContextReadPixels, reporter, ctxInfo) {
474 using Surface = std::unique_ptr<GrSurfaceContext>;
475 GrDirectContext* direct = ctxInfo.directContext();
476 auto reader = std::function<GpuReadSrcFn<Surface>>(
Brian Salomon237911a2021-01-05 12:33:26 -0500477 [direct](const Surface& surface, const SkIPoint& offset, const SkPixmap& pixels) {
478 if (surface->readPixels(direct, pixels, offset)) {
479 return Result::kSuccess;
Brian Salomonf84dfd62020-12-29 15:09:33 -0500480 } else {
481 // Reading from a non-renderable format is not guaranteed to work on GL.
482 // We'd have to be able to force a copy or draw draw to a renderable format.
483 const auto& caps = *direct->priv().caps();
484 if (direct->backend() == GrBackendApi::kOpenGL &&
485 !caps.isFormatRenderable(surface->asSurfaceProxy()->backendFormat(), 1)) {
Brian Salomon237911a2021-01-05 12:33:26 -0500486 return Result::kExcusedFailure;
Brian Salomonf84dfd62020-12-29 15:09:33 -0500487 }
Brian Salomon237911a2021-01-05 12:33:26 -0500488 return Result::kFail;
Brian Salomonf84dfd62020-12-29 15:09:33 -0500489 }
490 });
491 GpuReadPixelTestRules rules;
492 rules.fAllowUnpremulSrc = true;
493 rules.fAllowUnpremulRead = true;
494 rules.fUncontainedRectSucceeds = true;
495
496 for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
497 for (GrSurfaceOrigin origin : {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
498 auto factory = std::function<GpuSrcFactory<Surface>>(
499 [direct, origin, renderable](const SkPixmap& src) {
500 if (src.colorType() == kRGB_888x_SkColorType) {
501 return Surface();
502 }
503 auto surfContext = GrSurfaceContext::Make(
504 direct, src.info(), SkBackingFit::kExact, origin, renderable);
505 if (surfContext) {
506 surfContext->writePixels(direct, src, {0, 0});
507 }
508 return surfContext;
509 });
510 gpu_read_pixels_test_driver(reporter, rules, factory, reader);
511 }
512 }
513}
514
515namespace {
516struct AsyncContext {
517 bool fCalled = false;
518 std::unique_ptr<const SkImage::AsyncReadResult> fResult;
519};
520} // anonymous namespace
521
522// Making this a lambda in the test functions caused:
523// "error: cannot compile this forwarded non-trivially copyable parameter yet"
524// on x86/Win/Clang bot, referring to 'result'.
525static void async_callback(void* c, std::unique_ptr<const SkImage::AsyncReadResult> result) {
526 auto context = static_cast<AsyncContext*>(c);
527 context->fResult = std::move(result);
528 context->fCalled = true;
529};
530
531DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceAsyncReadPixels, reporter, ctxInfo) {
532 using Surface = sk_sp<SkSurface>;
533 auto reader = std::function<GpuReadSrcFn<Surface>>(
Brian Salomon237911a2021-01-05 12:33:26 -0500534 [](const Surface& surface, const SkIPoint& offset, const SkPixmap& pixels) {
Brian Salomonf84dfd62020-12-29 15:09:33 -0500535 auto direct = surface->recordingContext()->asDirectContext();
536 SkASSERT(direct);
537
538 AsyncContext context;
539 auto rect = SkIRect::MakeSize(pixels.dimensions()).makeOffset(offset);
540
541 // Rescale quality and linearity don't matter since we're doing a non-scaling
542 // readback.
Mike Reed1efa14d2021-01-02 21:44:59 -0500543 surface->asyncRescaleAndReadPixels(pixels.info(), rect,
544 SkImage::RescaleGamma::kSrc,
545 SkImage::RescaleMode::kNearest,
546 async_callback, &context);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500547 direct->submit();
548 while (!context.fCalled) {
549 direct->checkAsyncWorkCompletion();
550 }
551 if (!context.fResult) {
Brian Salomon237911a2021-01-05 12:33:26 -0500552 return Result::kFail;
Brian Salomonf84dfd62020-12-29 15:09:33 -0500553 }
554 SkRectMemcpy(pixels.writable_addr(), pixels.rowBytes(), context.fResult->data(0),
555 context.fResult->rowBytes(0), pixels.info().minRowBytes(),
556 pixels.height());
Brian Salomon237911a2021-01-05 12:33:26 -0500557 return Result::kSuccess;
Brian Salomonf84dfd62020-12-29 15:09:33 -0500558 });
559 GpuReadPixelTestRules rules;
560 rules.fAllowUnpremulSrc = false;
561 rules.fAllowUnpremulRead = false;
562 rules.fUncontainedRectSucceeds = false;
563
564 for (GrSurfaceOrigin origin : {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
565 auto factory = std::function<GpuSrcFactory<Surface>>(
566 [context = ctxInfo.directContext(), origin](const SkPixmap& src) {
567 if (src.colorType() == kRGB_888x_SkColorType) {
568 return Surface();
569 }
570 auto surf = SkSurface::MakeRenderTarget(context,
571 SkBudgeted::kYes,
572 src.info(),
573 0,
574 origin,
575 nullptr);
576 if (surf) {
577 surf->writePixels(src, 0, 0);
578 }
579 return surf;
580 });
581 gpu_read_pixels_test_driver(reporter, rules, factory, reader);
582 }
583}
584
585DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageAsyncReadPixels, reporter, ctxInfo) {
586 using Image = sk_sp<SkImage>;
587 auto context = ctxInfo.directContext();
588 auto reader = std::function<GpuReadSrcFn<Image>>([context](const Image& image,
Brian Salomon237911a2021-01-05 12:33:26 -0500589 const SkIPoint& offset,
Brian Salomonf84dfd62020-12-29 15:09:33 -0500590 const SkPixmap& pixels) {
591 AsyncContext asyncContext;
592 auto rect = SkIRect::MakeSize(pixels.dimensions()).makeOffset(offset);
593 // The GPU implementation is based on rendering and will fail for non-renderable color
594 // types.
595 auto ct = SkColorTypeToGrColorType(image->colorType());
596 auto format = context->priv().caps()->getDefaultBackendFormat(ct, GrRenderable::kYes);
597 if (!context->priv().caps()->isFormatAsColorTypeRenderable(ct, format)) {
Brian Salomon237911a2021-01-05 12:33:26 -0500598 return Result::kExcusedFailure;
Brian Salomonf84dfd62020-12-29 15:09:33 -0500599 }
600
601 // Rescale quality and linearity don't matter since we're doing a non-scaling readback.
Mike Reed1efa14d2021-01-02 21:44:59 -0500602 image->asyncRescaleAndReadPixels(pixels.info(), rect,
603 SkImage::RescaleGamma::kSrc,
604 SkImage::RescaleMode::kNearest,
605 async_callback, &asyncContext);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500606 context->submit();
607 while (!asyncContext.fCalled) {
608 context->checkAsyncWorkCompletion();
609 }
610 if (!asyncContext.fResult) {
Brian Salomon237911a2021-01-05 12:33:26 -0500611 return Result::kFail;
Brian Salomonf84dfd62020-12-29 15:09:33 -0500612 }
613 SkRectMemcpy(pixels.writable_addr(), pixels.rowBytes(), asyncContext.fResult->data(0),
614 asyncContext.fResult->rowBytes(0), pixels.info().minRowBytes(),
615 pixels.height());
Brian Salomon237911a2021-01-05 12:33:26 -0500616 return Result::kSuccess;
Brian Salomonf84dfd62020-12-29 15:09:33 -0500617 });
618
619 GpuReadPixelTestRules rules;
620 rules.fAllowUnpremulSrc = true;
621 // GPU doesn't support reading to kUnpremul because the rescaling works by rendering and now
622 // we only support premul rendering.
623 rules.fAllowUnpremulRead = false;
624 rules.fUncontainedRectSucceeds = false;
625
626 for (auto origin : {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
627 for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
628 auto factory = std::function<GpuSrcFactory<Image>>([&](const SkPixmap& src) {
629 if (src.colorType() == kRGB_888x_SkColorType) {
630 return Image();
631 }
632 return sk_gpu_test::MakeBackendTextureImage(ctxInfo.directContext(), src,
633 renderable, origin);
634 });
635 gpu_read_pixels_test_driver(reporter, rules, factory, reader);
636 }
637 }
638}
639
640DEF_GPUTEST(AsyncReadPixelsContextShutdown, reporter, options) {
641 const auto ii = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
642 SkColorSpace::MakeSRGB());
643 enum class ShutdownSequence {
644 kFreeResult_DestroyContext,
645 kDestroyContext_FreeResult,
646 kFreeResult_ReleaseAndAbandon_DestroyContext,
647 kFreeResult_Abandon_DestroyContext,
648 kReleaseAndAbandon_FreeResult_DestroyContext,
649 kAbandon_FreeResult_DestroyContext,
650 kReleaseAndAbandon_DestroyContext_FreeResult,
651 kAbandon_DestroyContext_FreeResult,
652 };
653 for (int t = 0; t < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++t) {
654 auto type = static_cast<sk_gpu_test::GrContextFactory::ContextType>(t);
655 for (auto sequence : {ShutdownSequence::kFreeResult_DestroyContext,
656 ShutdownSequence::kDestroyContext_FreeResult,
657 ShutdownSequence::kFreeResult_ReleaseAndAbandon_DestroyContext,
658 ShutdownSequence::kFreeResult_Abandon_DestroyContext,
659 ShutdownSequence::kReleaseAndAbandon_FreeResult_DestroyContext,
660 ShutdownSequence::kAbandon_FreeResult_DestroyContext,
661 ShutdownSequence::kReleaseAndAbandon_DestroyContext_FreeResult,
662 ShutdownSequence::kAbandon_DestroyContext_FreeResult}) {
663 // Vulkan context abandoning without resource release has issues outside of the scope of
664 // this test.
665 if (type == sk_gpu_test::GrContextFactory::kVulkan_ContextType &&
666 (sequence == ShutdownSequence::kFreeResult_ReleaseAndAbandon_DestroyContext ||
667 sequence == ShutdownSequence::kFreeResult_Abandon_DestroyContext ||
668 sequence == ShutdownSequence::kReleaseAndAbandon_FreeResult_DestroyContext ||
669 sequence == ShutdownSequence::kReleaseAndAbandon_DestroyContext_FreeResult ||
670 sequence == ShutdownSequence::kAbandon_FreeResult_DestroyContext ||
671 sequence == ShutdownSequence::kAbandon_DestroyContext_FreeResult)) {
672 continue;
673 }
674 for (bool yuv : {false, true}) {
675 sk_gpu_test::GrContextFactory factory(options);
676 auto direct = factory.get(type);
677 if (!direct) {
678 continue;
679 }
680 // This test is only meaningful for contexts that support transfer buffers for
681 // reads.
682 if (!direct->priv().caps()->transferFromSurfaceToBufferSupport()) {
683 continue;
684 }
685 auto surf = SkSurface::MakeRenderTarget(direct, SkBudgeted::kYes, ii, 1, nullptr);
686 if (!surf) {
687 continue;
688 }
689 AsyncContext cbContext;
690 if (yuv) {
691 surf->asyncRescaleAndReadPixelsYUV420(
692 kIdentity_SkYUVColorSpace, SkColorSpace::MakeSRGB(), ii.bounds(),
Mike Reed1efa14d2021-01-02 21:44:59 -0500693 ii.dimensions(), SkImage::RescaleGamma::kSrc,
694 SkImage::RescaleMode::kNearest, &async_callback, &cbContext);
Brian Salomonf84dfd62020-12-29 15:09:33 -0500695 } else {
696 surf->asyncRescaleAndReadPixels(ii, ii.bounds(), SkImage::RescaleGamma::kSrc,
Mike Reed1efa14d2021-01-02 21:44:59 -0500697 SkImage::RescaleMode::kNearest, &async_callback,
Brian Salomonf84dfd62020-12-29 15:09:33 -0500698 &cbContext);
699 }
700 direct->submit();
701 while (!cbContext.fCalled) {
702 direct->checkAsyncWorkCompletion();
703 }
704 if (!cbContext.fResult) {
705 ERRORF(reporter, "Callback failed on %s. is YUV: %d",
706 sk_gpu_test::GrContextFactory::ContextTypeName(type), yuv);
707 continue;
708 }
709 // For vulkan we need to release all refs to the GrDirectContext before trying to
710 // destroy the test context. The surface here is holding a ref.
711 surf.reset();
712
713 // The real test is that we don't crash, get Vulkan validation errors, etc, during
714 // this shutdown sequence.
715 switch (sequence) {
716 case ShutdownSequence::kFreeResult_DestroyContext:
717 case ShutdownSequence::kFreeResult_ReleaseAndAbandon_DestroyContext:
718 case ShutdownSequence::kFreeResult_Abandon_DestroyContext:
719 break;
720 case ShutdownSequence::kDestroyContext_FreeResult:
721 factory.destroyContexts();
722 break;
723 case ShutdownSequence::kReleaseAndAbandon_FreeResult_DestroyContext:
724 factory.releaseResourcesAndAbandonContexts();
725 break;
726 case ShutdownSequence::kAbandon_FreeResult_DestroyContext:
727 factory.abandonContexts();
728 break;
729 case ShutdownSequence::kReleaseAndAbandon_DestroyContext_FreeResult:
730 factory.releaseResourcesAndAbandonContexts();
731 factory.destroyContexts();
732 break;
733 case ShutdownSequence::kAbandon_DestroyContext_FreeResult:
734 factory.abandonContexts();
735 factory.destroyContexts();
736 break;
737 }
738 cbContext.fResult.reset();
739 switch (sequence) {
740 case ShutdownSequence::kFreeResult_ReleaseAndAbandon_DestroyContext:
741 factory.releaseResourcesAndAbandonContexts();
742 break;
743 case ShutdownSequence::kFreeResult_Abandon_DestroyContext:
744 factory.abandonContexts();
745 break;
746 case ShutdownSequence::kFreeResult_DestroyContext:
747 case ShutdownSequence::kDestroyContext_FreeResult:
748 case ShutdownSequence::kReleaseAndAbandon_FreeResult_DestroyContext:
749 case ShutdownSequence::kAbandon_FreeResult_DestroyContext:
750 case ShutdownSequence::kReleaseAndAbandon_DestroyContext_FreeResult:
751 case ShutdownSequence::kAbandon_DestroyContext_FreeResult:
752 break;
753 }
754 }
755 }
756 }
757}
Brian Salomon237911a2021-01-05 12:33:26 -0500758
759template <typename T>
760static void gpu_write_pixels_test_driver(skiatest::Reporter* reporter,
761 const std::function<GpuDstFactory<T>>& dstFactory,
762 const std::function<GpuWriteDstFn<T>>& write,
763 const std::function<GpuReadDstFn<T>>& read) {
764 // Separate this out just to give it some line width to breathe.
765 auto runTest = [&](const T& dst,
766 const SkImageInfo& dstInfo,
767 const SkPixmap& srcPixels,
768 SkIPoint offset) {
769 const bool csConversion =
770 !SkColorSpace::Equals(dstInfo.colorSpace(), srcPixels.info().colorSpace());
771 const auto writeCT = srcPixels.colorType();
772 const auto writeAT = srcPixels.alphaType();
773 const auto dstCT = dstInfo.colorType();
774 const auto dstAT = dstInfo.alphaType();
775 const auto rect = SkIRect::MakePtSize(offset, srcPixels.dimensions());
776 const auto surfBounds = SkIRect::MakeSize(dstInfo.dimensions());
777
778 // Do an initial read before the write.
779 SkAutoPixmapStorage firstReadPM = read(dst);
780 if (!firstReadPM.addr()) {
781 // Particularly with GLES 2 we can have formats that are unreadable with our current
782 // implementation of read pixels. If the format can't be attached to a FBO we don't have
783 // a code path that draws it to another readable color type/format combo and reads from
784 // that.
785 return Result::kExcusedFailure;
786 }
787
788 const Result result = write(dst, offset, srcPixels);
789
790 if (!SkIRect::Intersects(rect, surfBounds)) {
791 REPORTER_ASSERT(reporter, result != Result::kSuccess);
792 } else if (writeCT == kUnknown_SkColorType) {
793 REPORTER_ASSERT(reporter, result != Result::kSuccess);
794 } else if ((writeAT == kUnknown_SkAlphaType) != (dstAT == kUnknown_SkAlphaType)) {
795 REPORTER_ASSERT(reporter, result != Result::kSuccess);
796 } else if (result == Result::kExcusedFailure) {
797 return result;
798 } else if (result == Result::kFail) {
799 // TODO: Support RGB/BGR 101010x, BGRA 1010102 on the GPU.
800 if (SkColorTypeToGrColorType(writeCT) != GrColorType::kUnknown) {
801 ERRORF(reporter,
802 "Write failed. Write CT: %s, Write AT: %s Dst CT: %s, Dst AT: %s, "
803 "Rect [%d, %d, %d, %d], CS conversion: %d\n",
804 ToolUtils::colortype_name(writeCT), ToolUtils::alphatype_name(writeAT),
805 ToolUtils::colortype_name(dstCT), ToolUtils::alphatype_name(dstAT),
806 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, csConversion);
807 }
808 return result;
809 }
810
811 SkIRect checkRect;
812 if (result == Result::kSuccess && checkRect.intersect(surfBounds, rect)) {
813 // Do an initial read before the write. We'll use this to verify that areas outside the
814 // write are unaffected.
815 SkAutoPixmapStorage secondReadPM = read(dst);
816 if (!secondReadPM.addr()) {
817 // The first read succeeded so this one should, too.
818 ERRORF(reporter, "could not read from dst (CT: %s, AT: %s)\n",
819 ToolUtils::colortype_name(dstCT), ToolUtils::alphatype_name(dstAT));
820 return Result::kFail;
821 }
822
823 // Sometimes wider types go through 8bit unorm intermediates because of API
824 // restrictions.
825 int rgbBits = std::min({min_rgb_channel_bits(writeCT), min_rgb_channel_bits(dstCT), 8});
826 float tol = 2.f/(1 << rgbBits);
827 float alphaTol = 0;
828 if (writeAT != kOpaque_SkAlphaType && dstAT != kOpaque_SkAlphaType) {
829 // Alpha can also get squashed down to 8 bits going through an intermediate
830 // color format.
831 const int alphaBits = std::min({alpha_channel_bits(writeCT),
832 alpha_channel_bits(dstCT),
833 8});
834 alphaTol = 2.f/(1 << alphaBits);
835 }
836
837 const float tols[4] = {tol, tol, tol, alphaTol};
838 auto error = std::function<ComparePixmapsErrorReporter>([&](int x, int y,
839 const float diffs[4]) {
840 SkASSERT(x >= 0 && y >= 0);
841 ERRORF(reporter,
842 "Write CT: %s, Write AT: %s, Dst CT: %s, Dst AT: %s, Rect [%d, %d, %d, %d]"
843 ", CS conversion: %d\n"
844 "Error at %d, %d. Diff in floats: (%f, %f, %f %f)",
845 ToolUtils::colortype_name(writeCT), ToolUtils::alphatype_name(writeAT),
846 ToolUtils::colortype_name(dstCT), ToolUtils::alphatype_name(dstAT),
847 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, csConversion, x, y,
848 diffs[0], diffs[1], diffs[2], diffs[3]);
849 });
850
851 SkAutoPixmapStorage ref;
852 ref.alloc(secondReadPM.info().makeDimensions(checkRect.size()));
853 // Here we use the CPU backend to do the equivalent conversion as the write we're
854 // testing, using kUnpremul instead of kUnknown since CPU requires a valid alpha type.
855 SkAssertResult(make_pixmap_have_valid_alpha_type(srcPixels).readPixels(
856 make_pixmap_have_valid_alpha_type(ref),
857 std::max(0, -offset.fX),
858 std::max(0, -offset.fY)));
859 // This is the part of secondReadPixels that should have been updated by the write.
860 SkPixmap actual;
861 SkAssertResult(secondReadPM.extractSubset(&actual, checkRect));
862 ComparePixels(ref, actual, tols, error);
863 // The area around written rect should be the same in the first and second read.
864 SkIRect borders[]{
865 { 0, 0, secondReadPM.width(), secondReadPM.height()},
866 {checkRect.fRight, 0, checkRect.fLeft, secondReadPM.height()},
867 { checkRect.fLeft, 0, checkRect.fRight, checkRect.fTop},
868 { checkRect.fLeft, checkRect.fBottom, checkRect.fRight, secondReadPM.height()}
869 };
870 for (const auto r : borders) {
871 if (!r.isEmpty()) {
872 // Make a copy because MSVC for some reason doesn't correctly capture 'r'.
873 SkIPoint tl = r.topLeft();
874 auto guardError = std::function<
875 ComparePixmapsErrorReporter>([&](int x, int y, const float diffs[4]) {
876 x += tl.x();
877 y += tl.y();
878 ERRORF(reporter,
879 "Write CT: %s, Write AT: %s, Dst CT: %s, Dst AT: %s,"
880 "Rect [%d, %d, %d, %d], CS conversion: %d\n"
881 "Error in guard region %d, %d. Diff in floats: (%f, %f, %f %f)",
882 ToolUtils::colortype_name(writeCT),
883 ToolUtils::alphatype_name(writeAT), ToolUtils::colortype_name(dstCT),
884 ToolUtils::alphatype_name(dstAT), rect.fLeft, rect.fTop, rect.fRight,
885 rect.fBottom, csConversion, x, y, diffs[0], diffs[1], diffs[2],
886 diffs[3]);
887 });
888 SkPixmap a, b;
889 SkAssertResult(firstReadPM.extractSubset(&a, r));
890 SkAssertResult(firstReadPM.extractSubset(&b, r));
891 float zeroTols[4] = {};
892 ComparePixels(a, b, zeroTols, guardError);
893 }
894 }
895 }
896 return result;
897 };
898
899 static constexpr int kW = 16;
900 static constexpr int kH = 16;
901
902 const std::vector<SkIRect> longRectArray = make_long_rect_array(kW, kH);
903 const std::vector<SkIRect> shortRectArray = make_short_rect_array(kW, kH);
904
905 // We ensure we use the long array once per src and read color type and otherwise use the
906 // short array to improve test run time.
907 // Also, some color types have no alpha values and thus Opaque Premul and Unpremul are
908 // equivalent. Just ensure each redundant AT is tested once with each CT (dst and write).
909 // Similarly, alpha-only color types behave the same for all alpha types so just test premul
910 // after one iter.
911 // We consider a dst or write CT thoroughly tested once it has run through the long rect array
912 // and full complement of alpha types with one successful read in the loop.
913 std::array<bool, kLastEnum_SkColorType + 1> dstCTTestedThoroughly = {},
914 writeCTTestedThoroughly = {};
915 for (int dat = 0; dat < kLastEnum_SkAlphaType; ++dat) {
916 const auto dstAT = static_cast<SkAlphaType>(dat);
917 for (int dct = 0; dct <= kLastEnum_SkColorType; ++dct) {
918 const auto dstCT = static_cast<SkColorType>(dct);
919 const auto dstInfo = SkImageInfo::Make(kW, kH, dstCT, dstAT, SkColorSpace::MakeSRGB());
920 auto dst = dstFactory(dstInfo);
921 if (!dst) {
922 continue;
923 }
924 if (SkColorTypeIsAlwaysOpaque(dstCT) && dstCTTestedThoroughly[dstCT] &&
925 (kPremul_SkAlphaType == dstAT || kUnpremul_SkAlphaType == dstAT)) {
926 continue;
927 }
928 if (SkColorTypeIsAlphaOnly(dstCT) && dstCTTestedThoroughly[dstCT] &&
929 (kUnpremul_SkAlphaType == dstAT ||
930 kOpaque_SkAlphaType == dstAT ||
931 kUnknown_SkAlphaType == dstAT)) {
932 continue;
933 }
934 for (int wct = 0; wct <= kLastEnum_SkColorType; ++wct) {
935 const auto writeCT = static_cast<SkColorType>(wct);
936 for (const sk_sp<SkColorSpace>& writeCS : {SkColorSpace::MakeSRGB(),
937 SkColorSpace::MakeSRGBLinear()}) {
938 for (int wat = 0; wat <= kLastEnum_SkAlphaType; ++wat) {
939 const auto writeAT = static_cast<SkAlphaType>(wat);
940 if (writeAT != kOpaque_SkAlphaType && dstAT == kOpaque_SkAlphaType) {
941 // This doesn't make sense.
942 continue;
943 }
944 if (SkColorTypeIsAlwaysOpaque(writeCT) &&
945 writeCTTestedThoroughly[writeCT] &&
946 (kPremul_SkAlphaType == writeAT || kUnpremul_SkAlphaType == writeAT)) {
947 continue;
948 }
949 if (SkColorTypeIsAlphaOnly(writeCT) && writeCTTestedThoroughly[writeCT] &&
950 (kUnpremul_SkAlphaType == writeAT ||
951 kOpaque_SkAlphaType == writeAT ||
952 kUnknown_SkAlphaType == writeAT)) {
953 continue;
954 }
955 const auto& rects =
956 dstCTTestedThoroughly[dct] && writeCTTestedThoroughly[wct]
957 ? shortRectArray
958 : longRectArray;
959 for (const auto& rect : rects) {
960 auto writeInfo = SkImageInfo::Make(rect.size(),
961 writeCT,
962 writeAT,
963 writeCS);
964 // CPU and GPU handle 1010102 differently. CPU clamps RGB to A, GPU
965 // doesn't.
966 bool forceOpaque = writeCT == kRGBA_1010102_SkColorType ||
967 writeCT == kBGRA_1010102_SkColorType;
968 SkAutoPixmapStorage writePixels = make_ref_data(writeInfo, forceOpaque);
969 const SkIPoint offset = rect.topLeft();
970 Result r = runTest(dst, dstInfo, writePixels, offset);
971 if (r == Result::kSuccess) {
972 dstCTTestedThoroughly[dct] = true;
973 writeCTTestedThoroughly[wct] = true;
974 }
975 }
976 }
977 }
978 }
979 }
980 }
981}
982
983DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceContextWritePixels, reporter, ctxInfo) {
984 using Surface = std::unique_ptr<GrSurfaceContext>;
985 GrDirectContext* direct = ctxInfo.directContext();
986 auto writer = std::function<GpuWriteDstFn<Surface>>(
987 [direct](const Surface& surface, const SkIPoint& offset, const SkPixmap& pixels) {
988 if (surface->writePixels(direct, pixels, offset)) {
989 return Result::kSuccess;
990 } else {
991 return Result::kFail;
992 }
993 });
994 auto reader = std::function<GpuReadDstFn<Surface>>([direct](const Surface& s) {
995 SkAutoPixmapStorage result;
996 auto grInfo = s->imageInfo();
997 SkColorType ct = GrColorTypeToSkColorType(grInfo.colorType());
998 SkASSERT(ct != kUnknown_SkColorType);
999 auto skInfo = SkImageInfo::Make(grInfo.dimensions(), ct, grInfo.alphaType(),
1000 grInfo.refColorSpace());
1001 result.alloc(skInfo);
1002 if (!s->readPixels(direct, result, {0, 0})) {
1003 SkAutoPixmapStorage badResult;
1004 return badResult;
1005 }
1006 return result;
1007 });
1008
1009 for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
1010 for (GrSurfaceOrigin origin : {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
1011 auto factory = std::function<GpuDstFactory<Surface>>(
1012 [direct, origin, renderable](const SkImageInfo& info) {
1013 if (info.colorType() == kRGB_888x_SkColorType) {
1014 return Surface();
1015 }
1016 return GrSurfaceContext::Make(direct,
1017 info,
1018 SkBackingFit::kExact,
1019 origin,
1020 renderable);
1021 });
1022
1023 gpu_write_pixels_test_driver(reporter, factory, writer, reader);
1024 }
1025 }
1026}