1 /*
2 * Copyright 2017 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 // This is a GPU-backend specific test. It relies on static intializers to work
9
10 #include "include/core/SkTypes.h"
11
12 #include "include/core/SkSurface.h"
13 #include "include/gpu/GrTexture.h"
14 #include "src/gpu/GrContextPriv.h"
15 #include "src/gpu/GrGpu.h"
16 #include "src/gpu/GrResourceProvider.h"
17 #include "src/gpu/GrSurfaceProxy.h"
18 #include "src/gpu/SkGr.h"
19 #include "tests/Test.h"
20 #include "tests/TestUtils.h"
21 #include "tools/gpu/GrContextFactory.h"
22
23 using sk_gpu_test::GrContextFactory;
24
fill_transfer_data(int left,int top,int width,int height,int bufferWidth,GrColorType dstType,char * dst)25 void fill_transfer_data(int left, int top, int width, int height, int bufferWidth,
26 GrColorType dstType, char* dst) {
27 size_t dstBpp = GrColorTypeBytesPerPixel(dstType);
28 auto dstLocation = [dst, dstBpp, bufferWidth](int x, int y) {
29 return dst + y * dstBpp * bufferWidth + x * dstBpp;
30 };
31 // build red-green gradient
32 for (int j = top; j < top + height; ++j) {
33 for (int i = left; i < left + width; ++i) {
34 auto r = (unsigned int)(256.f*((i - left) / (float)width));
35 auto g = (unsigned int)(256.f*((j - top) / (float)height));
36 r -= (r >> 8);
37 g -= (g >> 8);
38 // set b and a channels to be inverse of r and g just to have interesting values to
39 // test.
40 uint32_t srcPixel = GrColorPackRGBA(r, g, 0xff - r, 0xff - g);
41 GrPixelInfo srcInfo(GrColorType::kRGBA_8888, kUnpremul_SkAlphaType, nullptr, 1, 1);
42 GrPixelInfo dstInfo(dstType, kUnpremul_SkAlphaType, nullptr, 1, 1);
43 GrConvertPixels(dstInfo, dstLocation(i, j), dstBpp, srcInfo, &srcPixel, 4);
44 }
45 }
46 }
47
determine_tolerances(GrColorType a,GrColorType b,float tolerances[4])48 void determine_tolerances(GrColorType a, GrColorType b, float tolerances[4]) {
49 std::fill_n(tolerances, 4, 0);
50
51 auto descA = GrGetColorTypeDesc(a);
52 auto descB = GrGetColorTypeDesc(b);
53 // For each channel x set the tolerance to 1 / (2^min(bits_in_a, bits_in_b) - 1) unless
54 // one color type is missing the channel. In that case leave it at 0. If the other color
55 // has the channel then it better be exactly 1 for alpha or 0 for rgb.
56 for (int i = 0; i < 4; ++i) {
57 if (descA[i] != descB[i]) {
58 auto m = std::min(descA[i], descB[i]);
59 if (m) {
60 tolerances[i] = 1.f / (m - 1);
61 }
62 }
63 }
64 }
65
read_pixels_from_texture(GrTexture * texture,GrColorType colorType,char * dst,float tolerances[4])66 bool read_pixels_from_texture(GrTexture* texture, GrColorType colorType, char* dst,
67 float tolerances[4]) {
68 auto* context = texture->getContext();
69 auto* gpu = context->priv().getGpu();
70 auto* caps = context->priv().caps();
71
72 int w = texture->width();
73 int h = texture->height();
74 size_t rowBytes = GrColorTypeBytesPerPixel(colorType) * w;
75
76 GrColorType srcCT = GrPixelConfigToColorType(texture->config());
77
78 GrCaps::SupportedRead supportedRead =
79 caps->supportedReadPixelsColorType(srcCT, texture->backendFormat(), colorType);
80 std::fill_n(tolerances, 4, 0);
81 if (supportedRead.fColorType != colorType) {
82 size_t tmpRowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * w;
83 std::unique_ptr<char[]> tmpPixels(new char[tmpRowBytes * h]);
84 if (!gpu->readPixels(texture, 0, 0, w, h, colorType, supportedRead.fColorType,
85 tmpPixels.get(), tmpRowBytes)) {
86 return false;
87 }
88 GrPixelInfo tmpInfo(supportedRead.fColorType, kUnpremul_SkAlphaType, nullptr, w, h);
89 GrPixelInfo dstInfo(colorType, kUnpremul_SkAlphaType, nullptr, w, h);
90 determine_tolerances(tmpInfo.colorType(), dstInfo.colorType(), tolerances);
91 return GrConvertPixels(dstInfo, dst, rowBytes, tmpInfo, tmpPixels.get(), tmpRowBytes,
92 false);
93 }
94 return gpu->readPixels(texture, 0, 0, w, h, colorType, supportedRead.fColorType, dst, rowBytes);
95 }
96
basic_transfer_to_test(skiatest::Reporter * reporter,GrContext * context,GrColorType colorType,GrRenderable renderable)97 void basic_transfer_to_test(skiatest::Reporter* reporter, GrContext* context, GrColorType colorType,
98 GrRenderable renderable) {
99 if (GrCaps::kNone_MapFlags == context->priv().caps()->mapBufferFlags()) {
100 return;
101 }
102
103 auto* caps = context->priv().caps();
104
105 auto backendFormat = caps->getDefaultBackendFormat(colorType, renderable);
106 if (!backendFormat.isValid()) {
107 return;
108 }
109
110 auto resourceProvider = context->priv().resourceProvider();
111 GrGpu* gpu = context->priv().getGpu();
112
113 const int kTextureWidth = 16;
114 const int kTextureHeight = 16;
115 int srcBufferWidth = caps->writePixelsRowBytesSupport() ? 20 : 16;
116 const int kBufferHeight = 16;
117
118 GrSurfaceDesc desc;
119 desc.fWidth = kTextureWidth;
120 desc.fHeight = kTextureHeight;
121 desc.fConfig = GrColorTypeToPixelConfig(colorType);
122
123 sk_sp<GrTexture> tex = resourceProvider->createTexture(desc, backendFormat, renderable, 1,
124 SkBudgeted::kNo, GrProtected::kNo,
125 GrResourceProvider::Flags::kNoPendingIO);
126 if (!tex) {
127 ERRORF(reporter, "Could not create texture");
128 return;
129 }
130
131 // We validate the results using GrGpu::readPixels, so exit if this is not supported.
132 // TODO: Do this through GrSurfaceContext once it works for all color types or support
133 // kCopyToTexture2D here.
134 if (GrCaps::SurfaceReadPixelsSupport::kSupported !=
135 caps->surfaceSupportsReadPixels(tex.get())) {
136 return;
137 }
138 // GL requires a texture to be framebuffer bindable to call glReadPixels. However, we have not
139 // incorporated that test into surfaceSupportsReadPixels(). TODO: Remove this once we handle
140 // drawing to a bindable format.
141 if (!caps->isFormatAsColorTypeRenderable(colorType, tex->backendFormat())) {
142 return;
143 }
144
145 // The caps tell us what color type we are allowed to upload and read back from this texture,
146 // either of which may differ from 'colorType'.
147 GrCaps::SupportedWrite allowedSrc =
148 caps->supportedWritePixelsColorType(colorType, tex->backendFormat(), colorType);
149 size_t srcRowBytes = GrColorTypeBytesPerPixel(allowedSrc.fColorType) * srcBufferWidth;
150 std::unique_ptr<char[]> srcData(new char[kTextureHeight * srcRowBytes]);
151
152 fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, srcBufferWidth, allowedSrc.fColorType,
153 srcData.get());
154
155 // create and fill transfer buffer
156 size_t size = srcRowBytes * kBufferHeight;
157 sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(size, GrGpuBufferType::kXferCpuToGpu,
158 kDynamic_GrAccessPattern));
159 if (!buffer) {
160 return;
161 }
162 void* data = buffer->map();
163 if (!buffer) {
164 ERRORF(reporter, "Could not map buffer");
165 return;
166 }
167 memcpy(data, srcData.get(), size);
168 buffer->unmap();
169
170 //////////////////////////
171 // transfer full data
172
173 bool result;
174 result = gpu->transferPixelsTo(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType,
175 allowedSrc.fColorType, buffer.get(), 0, srcRowBytes);
176 REPORTER_ASSERT(reporter, result);
177
178 size_t dstRowBytes = GrColorTypeBytesPerPixel(colorType) * kTextureWidth;
179 std::unique_ptr<char[]> dstBuffer(new char[dstRowBytes * kTextureHeight]());
180
181 float compareTolerances[4] = {};
182 result = read_pixels_from_texture(tex.get(), colorType, dstBuffer.get(), compareTolerances);
183 if (!result) {
184 ERRORF(reporter, "Could not read pixels from texture, color type: %d",
185 static_cast<int>(colorType));
186 return;
187 }
188
189 auto error = std::function<ComparePixmapsErrorReporter>(
190 [reporter, colorType](int x, int y, const float diffs[4]) {
191 ERRORF(reporter,
192 "Error at (%d %d) in transfer, color type: %d, diffs: (%f, %f, %f, %f)", x,
193 y, colorType, diffs[0], diffs[1], diffs[2], diffs[3]);
194 });
195 GrPixelInfo srcInfo(allowedSrc.fColorType, kUnpremul_SkAlphaType, nullptr, tex->width(),
196 tex->height());
197 GrPixelInfo dstInfo(colorType, kUnpremul_SkAlphaType, nullptr, tex->width(), tex->height());
198 compare_pixels(srcInfo, srcData.get(), srcRowBytes, dstInfo, dstBuffer.get(), dstRowBytes,
199 compareTolerances, error);
200
201 //////////////////////////
202 // transfer partial data
203
204 // We're relying on this cap to write partial texture data
205 if (!caps->writePixelsRowBytesSupport()) {
206 return;
207 }
208 // We keep a 1 to 1 correspondence between pixels in the buffer and the entire texture. We
209 // update the contents of a sub-rect of the buffer and push that rect to the texture. We start
210 // with a left sub-rect inset of 2 but may adjust that so we can fulfill the transfer buffer
211 // offset alignment requirement.
212 int left = 2;
213 const int top = 10;
214 const int width = 10;
215 const int height = 2;
216 size_t offset = top * srcRowBytes + left * GrColorTypeBytesPerPixel(allowedSrc.fColorType);
217 while (offset % allowedSrc.fOffsetAlignmentForTransferBuffer) {
218 offset += GrColorTypeBytesPerPixel(allowedSrc.fColorType);
219 ++left;
220 // We're assuming that the required alignment is 1 or a small multiple of the bpp, which
221 // it is currently for all color types across all backends.
222 SkASSERT(left + width <= tex->width());
223 }
224
225 // change color of subrectangle
226 fill_transfer_data(left, top, width, height, srcBufferWidth, allowedSrc.fColorType,
227 srcData.get());
228 data = buffer->map();
229 memcpy(data, srcData.get(), size);
230 buffer->unmap();
231
232 result = gpu->transferPixelsTo(tex.get(), left, top, width, height, colorType,
233 allowedSrc.fColorType, buffer.get(), offset, srcRowBytes);
234 if (!result) {
235 gpu->transferPixelsTo(tex.get(), left, top, width, height, colorType, allowedSrc.fColorType,
236 buffer.get(), offset, srcRowBytes);
237 ERRORF(reporter, "Could not transfer pixels to texture, color type: %d",
238 static_cast<int>(colorType));
239 return;
240 }
241
242 result = read_pixels_from_texture(tex.get(), colorType, dstBuffer.get(), compareTolerances);
243 if (!result) {
244 ERRORF(reporter, "Could not read pixels from texture, color type: %d",
245 static_cast<int>(colorType));
246 return;
247 }
248 compare_pixels(srcInfo, srcData.get(), srcRowBytes, dstInfo, dstBuffer.get(), dstRowBytes,
249 compareTolerances, error);
250 }
251
basic_transfer_from_test(skiatest::Reporter * reporter,const sk_gpu_test::ContextInfo & ctxInfo,GrColorType colorType,GrRenderable renderable)252 void basic_transfer_from_test(skiatest::Reporter* reporter, const sk_gpu_test::ContextInfo& ctxInfo,
253 GrColorType colorType, GrRenderable renderable) {
254 auto context = ctxInfo.grContext();
255 auto caps = context->priv().caps();
256 if (GrCaps::kNone_MapFlags == caps->mapBufferFlags()) {
257 return;
258 }
259
260 auto resourceProvider = context->priv().resourceProvider();
261 GrGpu* gpu = context->priv().getGpu();
262
263 const int kTextureWidth = 16;
264 const int kTextureHeight = 16;
265
266 // We'll do a full texture read into the buffer followed by a partial read. These values
267 // describe the partial read subrect.
268 const int kPartialLeft = 2;
269 const int kPartialTop = 10;
270 const int kPartialWidth = 10;
271 const int kPartialHeight = 2;
272
273 // create texture
274 GrSurfaceDesc desc;
275 desc.fWidth = kTextureWidth;
276 desc.fHeight = kTextureHeight;
277 desc.fConfig = GrColorTypeToPixelConfig(colorType);
278
279 auto format = context->priv().caps()->getDefaultBackendFormat(colorType, renderable);
280 if (!format.isValid()) {
281 return;
282 }
283
284 size_t textureDataBpp = GrColorTypeBytesPerPixel(colorType);
285 size_t textureDataRowBytes = kTextureWidth * textureDataBpp;
286 std::unique_ptr<char[]> textureData(new char[kTextureHeight * textureDataRowBytes]);
287 fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, kTextureWidth, colorType,
288 textureData.get());
289 GrMipLevel data;
290 data.fPixels = textureData.get();
291 data.fRowBytes = textureDataRowBytes;
292 sk_sp<GrTexture> tex = resourceProvider->createTexture(
293 desc, format, renderable, 1, SkBudgeted::kNo, GrProtected::kNo, &data, 1);
294 if (!tex) {
295 return;
296 }
297
298 if (GrCaps::SurfaceReadPixelsSupport::kSupported !=
299 caps->surfaceSupportsReadPixels(tex.get())) {
300 return;
301 }
302 // GL requires a texture to be framebuffer bindable to call glReadPixels. However, we have not
303 // incorporated that test into surfaceSupportsReadPixels(). TODO: Remove this once we handle
304 // drawing to a bindable format.
305 if (!caps->isFormatAsColorTypeRenderable(colorType, tex->backendFormat())) {
306 return;
307 }
308
309 // Create the transfer buffer.
310 auto allowedRead =
311 caps->supportedReadPixelsColorType(colorType, tex->backendFormat(), colorType);
312 GrPixelInfo readInfo(allowedRead.fColorType, kUnpremul_SkAlphaType, nullptr, kTextureWidth,
313 kTextureHeight);
314
315 size_t bpp = GrColorTypeBytesPerPixel(allowedRead.fColorType);
316 size_t fullBufferRowBytes = kTextureWidth * bpp;
317 size_t partialBufferRowBytes = kPartialWidth * bpp;
318 size_t offsetAlignment = allowedRead.fOffsetAlignmentForTransferBuffer;
319 SkASSERT(offsetAlignment);
320
321 size_t bufferSize = fullBufferRowBytes * kTextureHeight;
322 // Arbitrary starting offset for the partial read.
323 size_t partialReadOffset = GrSizeAlignUp(11, offsetAlignment);
324 bufferSize = SkTMax(bufferSize, partialReadOffset + partialBufferRowBytes * kPartialHeight);
325
326 sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(
327 bufferSize, GrGpuBufferType::kXferGpuToCpu, kDynamic_GrAccessPattern));
328 REPORTER_ASSERT(reporter, buffer);
329 if (!buffer) {
330 return;
331 }
332
333 int expectedTransferCnt = 0;
334 gpu->stats()->reset();
335
336 //////////////////////////
337 // transfer full data
338 bool result = gpu->transferPixelsFrom(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType,
339 allowedRead.fColorType, buffer.get(), 0);
340 if (!result) {
341 ERRORF(reporter, "transferPixelsFrom failed.");
342 return;
343 }
344 ++expectedTransferCnt;
345
346 GrFlushInfo flushInfo;
347 flushInfo.fFlags = kSyncCpu_GrFlushFlag;
348 if (context->priv().caps()->mapBufferFlags() & GrCaps::kAsyncRead_MapFlag) {
349 gpu->finishFlush(nullptr, 0, SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo,
350 GrPrepareForExternalIORequests());
351 }
352
353 // Copy the transfer buffer contents to a temporary so we can manipulate it.
354 const auto* map = reinterpret_cast<const char*>(buffer->map());
355 REPORTER_ASSERT(reporter, map);
356 if (!map) {
357 ERRORF(reporter, "Failed to map transfer buffer.");
358 return;
359 }
360 std::unique_ptr<char[]> transferData(new char[kTextureHeight * fullBufferRowBytes]);
361 memcpy(transferData.get(), map, fullBufferRowBytes * kTextureHeight);
362 buffer->unmap();
363
364 GrPixelInfo transferInfo(allowedRead.fColorType, kUnpremul_SkAlphaType, nullptr, kTextureWidth,
365 kTextureHeight);
366
367 float tol[4];
368 determine_tolerances(allowedRead.fColorType, colorType, tol);
369 auto error = std::function<ComparePixmapsErrorReporter>(
370 [reporter, colorType](int x, int y, const float diffs[4]) {
371 ERRORF(reporter,
372 "Error at (%d %d) in transfer, color type: %d, diffs: (%f, %f, %f, %f)", x,
373 y, colorType, diffs[0], diffs[1], diffs[2], diffs[3]);
374 });
375 GrPixelInfo textureDataInfo(colorType, kUnpremul_SkAlphaType, nullptr, kTextureWidth,
376 kTextureHeight);
377 compare_pixels(textureDataInfo, textureData.get(), textureDataRowBytes, transferInfo,
378 transferData.get(), fullBufferRowBytes, tol, error);
379
380 ///////////////////////
381 // Now test a partial read at an offset into the buffer.
382 result = gpu->transferPixelsFrom(tex.get(), kPartialLeft, kPartialTop, kPartialWidth,
383 kPartialHeight, colorType, allowedRead.fColorType,
384 buffer.get(), partialReadOffset);
385 if (!result) {
386 ERRORF(reporter, "transferPixelsFrom failed.");
387 return;
388 }
389 ++expectedTransferCnt;
390
391 if (context->priv().caps()->mapBufferFlags() & GrCaps::kAsyncRead_MapFlag) {
392 gpu->finishFlush(nullptr, 0, SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo,
393 GrPrepareForExternalIORequests());
394 }
395
396 map = reinterpret_cast<const char*>(buffer->map());
397 REPORTER_ASSERT(reporter, map);
398 if (!map) {
399 ERRORF(reporter, "Failed to map transfer buffer.");
400 return;
401 }
402 const char* bufferStart = reinterpret_cast<const char*>(map) + partialReadOffset;
403 memcpy(transferData.get(), bufferStart, partialBufferRowBytes * kTextureHeight);
404 buffer->unmap();
405
406 transferInfo = transferInfo.makeWH(kPartialWidth, kPartialHeight);
407 const char* textureDataStart =
408 textureData.get() + textureDataRowBytes * kPartialTop + textureDataBpp * kPartialLeft;
409 textureDataInfo = textureDataInfo.makeWH(kPartialWidth, kPartialHeight);
410 compare_pixels(textureDataInfo, textureDataStart, textureDataRowBytes, transferInfo,
411 transferData.get(), partialBufferRowBytes, tol, error);
412 #if GR_GPU_STATS
413 REPORTER_ASSERT(reporter, gpu->stats()->transfersFromSurface() == expectedTransferCnt);
414 #else
415 (void)expectedTransferCnt;
416 #endif
417 }
418
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsToTest,reporter,ctxInfo)419 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsToTest, reporter, ctxInfo) {
420 if (!ctxInfo.grContext()->priv().caps()->transferBufferSupport()) {
421 return;
422 }
423 for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
424 for (auto colorType : {
425 GrColorType::kAlpha_8,
426 GrColorType::kBGR_565,
427 GrColorType::kABGR_4444,
428 GrColorType::kRGBA_8888,
429 GrColorType::kRGBA_8888_SRGB,
430 // GrColorType::kRGB_888x, Broken in GL until we have kRGB_888
431 GrColorType::kRG_88,
432 GrColorType::kBGRA_8888,
433 GrColorType::kRGBA_1010102,
434 // GrColorType::kGray_8, Reading back to kGray is busted.
435 GrColorType::kAlpha_F16,
436 GrColorType::kRGBA_F16,
437 GrColorType::kRGBA_F16_Clamped,
438 GrColorType::kRGBA_F32,
439 GrColorType::kR_16,
440 GrColorType::kRG_1616,
441 GrColorType::kRGBA_16161616,
442 GrColorType::kRG_F16,
443 }) {
444 basic_transfer_to_test(reporter, ctxInfo.grContext(), colorType, renderable);
445 }
446 }
447 }
448
449 // TODO(bsalomon): Metal
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsFromTest,reporter,ctxInfo)450 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsFromTest, reporter, ctxInfo) {
451 if (!ctxInfo.grContext()->priv().caps()->transferBufferSupport()) {
452 return;
453 }
454 for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
455 for (auto colorType : {
456 GrColorType::kAlpha_8,
457 GrColorType::kBGR_565,
458 GrColorType::kABGR_4444,
459 GrColorType::kRGBA_8888,
460 GrColorType::kRGBA_8888_SRGB,
461 // GrColorType::kRGB_888x, Broken in GL until we have kRGB_888
462 GrColorType::kRG_88,
463 GrColorType::kBGRA_8888,
464 GrColorType::kRGBA_1010102,
465 // GrColorType::kGray_8, Reading back to kGray is busted.
466 GrColorType::kAlpha_F16,
467 GrColorType::kRGBA_F16,
468 GrColorType::kRGBA_F16_Clamped,
469 GrColorType::kRGBA_F32,
470 GrColorType::kR_16,
471 GrColorType::kRG_1616,
472 GrColorType::kRGBA_16161616,
473 GrColorType::kRG_F16,
474 }) {
475 basic_transfer_from_test(reporter, ctxInfo, colorType, renderable);
476 }
477 }
478 }
479