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