• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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