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 "SkTypes.h"
11 
12 #include "GrContextFactory.h"
13 #include "GrContextPriv.h"
14 #include "GrGpu.h"
15 #include "GrResourceProvider.h"
16 #include "GrSurfaceProxy.h"
17 #include "GrTexture.h"
18 #include "SkGr.h"
19 #include "SkSurface.h"
20 #include "Test.h"
21 
22 using sk_gpu_test::GrContextFactory;
23 
fill_transfer_data(int left,int top,int width,int height,int bufferWidth,GrColor * data)24 void fill_transfer_data(int left, int top, int width, int height, int bufferWidth,
25                         GrColor* data) {
26 
27     // build red-green gradient
28     for (int j = top; j < top + height; ++j) {
29         for (int i = left; i < left + width; ++i) {
30             unsigned int red = (unsigned int)(256.f*((i - left) / (float)width));
31             unsigned int green = (unsigned int)(256.f*((j - top) / (float)height));
32             data[i + j*bufferWidth] = GrColorPackRGBA(red - (red>>8),
33                                                       green - (green>>8), 0xff, 0xff);
34         }
35     }
36 }
37 
does_full_buffer_contain_correct_values(GrColor * srcBuffer,GrColor * dstBuffer,int width,int height,int bufferWidth,int bufferHeight)38 bool does_full_buffer_contain_correct_values(GrColor* srcBuffer,
39                                              GrColor* dstBuffer,
40                                              int width,
41                                              int height,
42                                              int bufferWidth,
43                                              int bufferHeight) {
44     GrColor* srcPtr = srcBuffer;
45     GrColor* dstPtr = dstBuffer;
46 
47     for (int j = 0; j < height; ++j) {
48         for (int i = 0; i < width; ++i) {
49             if (srcPtr[i] != dstPtr[i]) {
50                 return false;
51             }
52         }
53         srcPtr += bufferWidth;
54         dstPtr += bufferWidth;
55     }
56     return true;
57 }
58 
basic_transfer_test(skiatest::Reporter * reporter,GrContext * context,GrColorType colorType,bool renderTarget)59 void basic_transfer_test(skiatest::Reporter* reporter, GrContext* context, GrColorType colorType,
60                          bool renderTarget) {
61     if (GrCaps::kNone_MapFlags == context->priv().caps()->mapBufferFlags()) {
62         return;
63     }
64 
65     auto resourceProvider = context->priv().resourceProvider();
66     GrGpu* gpu = context->priv().getGpu();
67 
68     // set up the data
69     const int kTextureWidth = 16;
70     const int kTextureHeight = 16;
71 #ifdef SK_BUILD_FOR_IOS
72     // UNPACK_ROW_LENGTH is broken on iOS so rowBytes needs to match data width
73     const int kBufferWidth = GrBackendApi::kOpenGL == context->backend() ? 16 : 20;
74 #else
75     const int kBufferWidth = 20;
76 #endif
77     const int kBufferHeight = 16;
78     size_t rowBytes = kBufferWidth * sizeof(GrColor);
79     SkAutoTMalloc<GrColor> srcBuffer(kBufferWidth*kBufferHeight);
80     SkAutoTMalloc<GrColor> dstBuffer(kBufferWidth*kBufferHeight);
81 
82     fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, kBufferWidth, srcBuffer.get());
83 
84     // create and fill transfer buffer
85     size_t size = rowBytes*kBufferHeight;
86     sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(size, GrGpuBufferType::kXferCpuToGpu,
87                                                              kDynamic_GrAccessPattern));
88     if (!buffer) {
89         return;
90     }
91 
92     void* data = buffer->map();
93     memcpy(data, srcBuffer.get(), size);
94     buffer->unmap();
95 
96     for (auto srgbEncoding : {GrSRGBEncoded::kNo, GrSRGBEncoded::kYes}) {
97         // create texture
98         GrSurfaceDesc desc;
99         desc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
100         desc.fWidth = kTextureWidth;
101         desc.fHeight = kTextureHeight;
102         desc.fConfig = GrColorTypeToPixelConfig(colorType, srgbEncoding);
103         desc.fSampleCnt = 1;
104 
105         if (kUnknown_GrPixelConfig == desc.fConfig) {
106             SkASSERT(GrSRGBEncoded::kYes == srgbEncoding);
107             continue;
108         }
109 
110         if (!context->priv().caps()->isConfigTexturable(desc.fConfig) ||
111             (renderTarget && !context->priv().caps()->isConfigRenderable(desc.fConfig))) {
112             continue;
113         }
114 
115         sk_sp<GrTexture> tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
116         if (!tex) {
117             continue;
118         }
119 
120         //////////////////////////
121         // transfer full data
122 
123         bool result;
124         result = gpu->transferPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType,
125                                      buffer.get(), 0, rowBytes);
126         REPORTER_ASSERT(reporter, result);
127 
128         memset(dstBuffer.get(), 0xCDCD, size);
129         result = gpu->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType,
130                                  dstBuffer.get(), rowBytes);
131         if (result) {
132             REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_values(srcBuffer,
133                                                                               dstBuffer,
134                                                                               kTextureWidth,
135                                                                               kTextureHeight,
136                                                                               kBufferWidth,
137                                                                               kBufferHeight));
138         }
139 
140         //////////////////////////
141         // transfer partial data
142 #ifdef SK_BUILD_FOR_IOS
143         // UNPACK_ROW_LENGTH is broken on iOS so we can't do partial transfers
144         if (GrBackendApi::kOpenGL == context->backend()) {
145             continue;
146         }
147 #endif
148         const int kLeft = 2;
149         const int kTop = 10;
150         const int kWidth = 10;
151         const int kHeight = 2;
152 
153         // change color of subrectangle
154         fill_transfer_data(kLeft, kTop, kWidth, kHeight, kBufferWidth, srcBuffer.get());
155         data = buffer->map();
156         memcpy(data, srcBuffer.get(), size);
157         buffer->unmap();
158 
159         size_t offset = sizeof(GrColor) * (kTop * kBufferWidth + kLeft);
160         result = gpu->transferPixels(tex.get(), kLeft, kTop, kWidth, kHeight, colorType,
161                                      buffer.get(), offset, rowBytes);
162         REPORTER_ASSERT(reporter, result);
163 
164         memset(dstBuffer.get(), 0xCDCD, size);
165         result = gpu->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType,
166                                  dstBuffer.get(), rowBytes);
167         if (result) {
168             REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_values(srcBuffer,
169                                                                               dstBuffer,
170                                                                               kTextureWidth,
171                                                                               kTextureHeight,
172                                                                               kBufferWidth,
173                                                                               kBufferHeight));
174         }
175     }
176 }
177 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsTest,reporter,ctxInfo)178 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsTest, reporter, ctxInfo) {
179     // RGBA
180     basic_transfer_test(reporter, ctxInfo.grContext(), GrColorType::kRGBA_8888, false);
181     basic_transfer_test(reporter, ctxInfo.grContext(), GrColorType::kRGBA_8888, true);
182 
183     // BGRA
184     basic_transfer_test(reporter, ctxInfo.grContext(), GrColorType::kBGRA_8888, false);
185     basic_transfer_test(reporter, ctxInfo.grContext(), GrColorType::kBGRA_8888, true);
186 }
187