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