blob: 0db85bb1869871b3437f4d722504fd9dcd56c1e1 [file] [log] [blame]
Jim Van Verth2e5eaf02017-06-21 15:55:46 -04001/*
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
Jim Van Verth2e5eaf02017-06-21 15:55:46 -040012#include "GrContextFactory.h"
13#include "GrContextPriv.h"
14#include "GrGpu.h"
15#include "GrResourceProvider.h"
16#include "GrSurfaceProxy.h"
17#include "GrTexture.h"
Jim Van Verth2e5eaf02017-06-21 15:55:46 -040018#include "SkGr.h"
19#include "SkSurface.h"
20#include "Test.h"
21
22using sk_gpu_test::GrContextFactory;
23
24void 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
Brian Salomone05ba5a2019-04-08 11:59:07 -040038bool do_buffers_contain_same_values(const GrColor* bufferA,
39 const GrColor* bufferB,
40 int width,
41 int height,
42 size_t rowBytesA,
Brian Salomon42cbedd2019-04-09 15:00:05 -040043 size_t rowBytesB,
44 bool swiz) {
Jim Van Verth2e5eaf02017-06-21 15:55:46 -040045 for (int j = 0; j < height; ++j) {
46 for (int i = 0; i < width; ++i) {
Brian Salomon42cbedd2019-04-09 15:00:05 -040047 auto colorA = bufferA[i];
48 if (swiz) {
49 colorA = GrColorPackRGBA(GrColorUnpackB(colorA), GrColorUnpackG(colorA),
50 GrColorUnpackR(colorA), GrColorUnpackA(colorA));
51 }
52 if (colorA != bufferB[i]) {
Jim Van Verth2e5eaf02017-06-21 15:55:46 -040053 return false;
54 }
55 }
Brian Salomone05ba5a2019-04-08 11:59:07 -040056 bufferA = reinterpret_cast<const GrColor*>(reinterpret_cast<const char*>(bufferA) +
57 rowBytesA);
58 bufferB = reinterpret_cast<const GrColor*>(reinterpret_cast<const char*>(bufferB) +
59 rowBytesB);
Jim Van Verth2e5eaf02017-06-21 15:55:46 -040060 }
61 return true;
62}
63
Brian Salomone05ba5a2019-04-08 11:59:07 -040064void basic_transfer_to_test(skiatest::Reporter* reporter, GrContext* context, GrColorType colorType,
65 bool renderTarget) {
Robert Phillips9da87e02019-02-04 13:26:26 -050066 if (GrCaps::kNone_MapFlags == context->priv().caps()->mapBufferFlags()) {
Brian Salomon7f56d3d2017-10-09 13:02:49 -040067 return;
68 }
69
Robert Phillips9da87e02019-02-04 13:26:26 -050070 auto resourceProvider = context->priv().resourceProvider();
71 GrGpu* gpu = context->priv().getGpu();
Robert Phillips6be756b2018-01-16 15:07:54 -050072
Jim Van Verth2e5eaf02017-06-21 15:55:46 -040073 // set up the data
74 const int kTextureWidth = 16;
75 const int kTextureHeight = 16;
Jim Van Verth3a749252018-12-07 10:57:26 -050076#ifdef SK_BUILD_FOR_IOS
77 // UNPACK_ROW_LENGTH is broken on iOS so rowBytes needs to match data width
Robert Phillips4217ea72019-01-30 13:08:28 -050078 const int kBufferWidth = GrBackendApi::kOpenGL == context->backend() ? 16 : 20;
Jim Van Verth3a749252018-12-07 10:57:26 -050079#else
Jim Van Verth2e5eaf02017-06-21 15:55:46 -040080 const int kBufferWidth = 20;
Jim Van Verth3a749252018-12-07 10:57:26 -050081#endif
Jim Van Verth2e5eaf02017-06-21 15:55:46 -040082 const int kBufferHeight = 16;
83 size_t rowBytes = kBufferWidth * sizeof(GrColor);
84 SkAutoTMalloc<GrColor> srcBuffer(kBufferWidth*kBufferHeight);
85 SkAutoTMalloc<GrColor> dstBuffer(kBufferWidth*kBufferHeight);
86
87 fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, kBufferWidth, srcBuffer.get());
88
89 // create and fill transfer buffer
90 size_t size = rowBytes*kBufferHeight;
Brian Salomondbf70722019-02-07 11:31:24 -050091 sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(size, GrGpuBufferType::kXferCpuToGpu,
92 kDynamic_GrAccessPattern));
Jim Van Verth2e5eaf02017-06-21 15:55:46 -040093 if (!buffer) {
94 return;
95 }
96
97 void* data = buffer->map();
98 memcpy(data, srcBuffer.get(), size);
99 buffer->unmap();
100
Brian Salomonc320b152018-02-20 14:05:36 -0500101 for (auto srgbEncoding : {GrSRGBEncoded::kNo, GrSRGBEncoded::kYes}) {
102 // create texture
103 GrSurfaceDesc desc;
104 desc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
Brian Salomonc320b152018-02-20 14:05:36 -0500105 desc.fWidth = kTextureWidth;
106 desc.fHeight = kTextureHeight;
107 desc.fConfig = GrColorTypeToPixelConfig(colorType, srgbEncoding);
108 desc.fSampleCnt = 1;
Jim Van Verth2e5eaf02017-06-21 15:55:46 -0400109
Brian Salomonc320b152018-02-20 14:05:36 -0500110 if (kUnknown_GrPixelConfig == desc.fConfig) {
111 SkASSERT(GrSRGBEncoded::kYes == srgbEncoding);
112 continue;
113 }
Jim Van Verth2e5eaf02017-06-21 15:55:46 -0400114
Robert Phillips9da87e02019-02-04 13:26:26 -0500115 if (!context->priv().caps()->isConfigTexturable(desc.fConfig) ||
116 (renderTarget && !context->priv().caps()->isConfigRenderable(desc.fConfig))) {
Brian Salomonc320b152018-02-20 14:05:36 -0500117 continue;
118 }
Jim Van Verth2e5eaf02017-06-21 15:55:46 -0400119
Brian Salomonc320b152018-02-20 14:05:36 -0500120 sk_sp<GrTexture> tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
Robert Phillips6fc30922018-07-27 12:21:37 -0400121 if (!tex) {
122 continue;
123 }
Robert Phillips16d8ec62017-07-27 16:16:25 -0400124
Brian Salomonc320b152018-02-20 14:05:36 -0500125 //////////////////////////
126 // transfer full data
Jim Van Verth2e5eaf02017-06-21 15:55:46 -0400127
Brian Salomonc320b152018-02-20 14:05:36 -0500128 bool result;
Brian Salomone05ba5a2019-04-08 11:59:07 -0400129 result = gpu->transferPixelsTo(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType,
130 buffer.get(), 0, rowBytes);
Brian Salomonc320b152018-02-20 14:05:36 -0500131 REPORTER_ASSERT(reporter, result);
Jim Van Verth2e5eaf02017-06-21 15:55:46 -0400132
Brian Salomonc320b152018-02-20 14:05:36 -0500133 memset(dstBuffer.get(), 0xCDCD, size);
Brian Salomona6948702018-06-01 15:33:20 -0400134 result = gpu->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType,
Brian Salomonc320b152018-02-20 14:05:36 -0500135 dstBuffer.get(), rowBytes);
136 if (result) {
Brian Salomone05ba5a2019-04-08 11:59:07 -0400137 REPORTER_ASSERT(reporter, do_buffers_contain_same_values(srcBuffer,
138 dstBuffer,
139 kTextureWidth,
140 kTextureHeight,
141 rowBytes,
Brian Salomon42cbedd2019-04-09 15:00:05 -0400142 rowBytes,
143 false));
Brian Salomonc320b152018-02-20 14:05:36 -0500144 }
Jim Van Verth2e5eaf02017-06-21 15:55:46 -0400145
Brian Salomonc320b152018-02-20 14:05:36 -0500146 //////////////////////////
147 // transfer partial data
Jim Van Verth3a749252018-12-07 10:57:26 -0500148#ifdef SK_BUILD_FOR_IOS
149 // UNPACK_ROW_LENGTH is broken on iOS so we can't do partial transfers
Robert Phillips4217ea72019-01-30 13:08:28 -0500150 if (GrBackendApi::kOpenGL == context->backend()) {
Jim Van Verth3a749252018-12-07 10:57:26 -0500151 continue;
152 }
153#endif
Brian Salomonc320b152018-02-20 14:05:36 -0500154 const int kLeft = 2;
155 const int kTop = 10;
156 const int kWidth = 10;
157 const int kHeight = 2;
158
159 // change color of subrectangle
160 fill_transfer_data(kLeft, kTop, kWidth, kHeight, kBufferWidth, srcBuffer.get());
161 data = buffer->map();
162 memcpy(data, srcBuffer.get(), size);
163 buffer->unmap();
164
165 size_t offset = sizeof(GrColor) * (kTop * kBufferWidth + kLeft);
Brian Salomone05ba5a2019-04-08 11:59:07 -0400166 result = gpu->transferPixelsTo(tex.get(), kLeft, kTop, kWidth, kHeight, colorType,
167 buffer.get(), offset, rowBytes);
Brian Salomonc320b152018-02-20 14:05:36 -0500168 REPORTER_ASSERT(reporter, result);
169
170 memset(dstBuffer.get(), 0xCDCD, size);
Brian Salomona6948702018-06-01 15:33:20 -0400171 result = gpu->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType,
Brian Salomonc320b152018-02-20 14:05:36 -0500172 dstBuffer.get(), rowBytes);
173 if (result) {
Brian Salomone05ba5a2019-04-08 11:59:07 -0400174 REPORTER_ASSERT(reporter, do_buffers_contain_same_values(srcBuffer,
175 dstBuffer,
176 kTextureWidth,
177 kTextureHeight,
178 rowBytes,
Brian Salomon42cbedd2019-04-09 15:00:05 -0400179 rowBytes,
180 false));
Brian Salomonc320b152018-02-20 14:05:36 -0500181 }
Jim Van Verth52fb02e2017-06-27 10:36:56 -0400182 }
Jim Van Verth2e5eaf02017-06-21 15:55:46 -0400183}
184
Brian Salomon42cbedd2019-04-09 15:00:05 -0400185void basic_transfer_from_test(skiatest::Reporter* reporter, const sk_gpu_test::ContextInfo& ctxInfo,
Brian Salomone05ba5a2019-04-08 11:59:07 -0400186 GrColorType colorType, bool renderTarget) {
Brian Salomon42cbedd2019-04-09 15:00:05 -0400187 auto context = ctxInfo.grContext();
Brian Salomone05ba5a2019-04-08 11:59:07 -0400188 if (GrCaps::kNone_MapFlags == context->priv().caps()->mapBufferFlags()) {
189 return;
190 }
Jim Van Verth686046b2019-03-18 15:39:22 -0400191
Brian Salomon42cbedd2019-04-09 15:00:05 -0400192 // On OpenGL ES it may not be possible to read back in to BGRA becagse GL_RGBA/GL_UNSIGNED_BYTE
193 // may be the only allowed format/type params to glReadPixels. So read back into GL_RGBA.
194 // TODO(bsalomon): Make this work in GrGLGpu.
195 auto readColorType = colorType;
196 if (GrColorType::kBGRA_8888 == colorType &&
197 ctxInfo.type() == sk_gpu_test::GrContextFactory::kGLES_ContextType) {
198 readColorType = GrColorType::kRGBA_8888;
199 }
200
Brian Salomone05ba5a2019-04-08 11:59:07 -0400201 auto resourceProvider = context->priv().resourceProvider();
202 GrGpu* gpu = context->priv().getGpu();
Jim Van Verth2e5eaf02017-06-21 15:55:46 -0400203
Brian Salomone05ba5a2019-04-08 11:59:07 -0400204 const int kTextureWidth = 16;
205 const int kTextureHeight = 16;
206
207 // We'll do a full texture read into the buffer followed by a partial read. These values
208 // describe the partial read subrect.
209 const int kPartialLeft = 2;
210 const int kPartialTop = 10;
211 const int kPartialWidth = 10;
212 const int kPartialHeight = 2;
213
214 size_t fullBufferRowBytes;
215 size_t partialBufferRowBytes;
216 size_t fullBufferOffsetAlignment;
217 size_t partialBufferOffsetAlignment;
218
219 SkAssertResult(context->priv().caps()->transferFromBufferRequirements(
220 colorType, kTextureWidth, &fullBufferRowBytes, &fullBufferOffsetAlignment));
221 SkAssertResult(context->priv().caps()->transferFromBufferRequirements(
222 colorType, kPartialWidth, &partialBufferRowBytes, &partialBufferOffsetAlignment));
223
224 size_t bufferSize = fullBufferRowBytes * kTextureHeight;
225 // Arbitrary starting offset for the partial read.
226 size_t partialReadOffset = GrSizeAlignUp(11, partialBufferOffsetAlignment);
227 bufferSize =
228 SkTMax(bufferSize, partialReadOffset + partialBufferRowBytes * partialBufferRowBytes);
229
230 sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(
231 bufferSize, GrGpuBufferType::kXferGpuToCpu, kDynamic_GrAccessPattern));
232 REPORTER_ASSERT(reporter, buffer);
233 if (!buffer) {
234 return;
235 }
236
237 int expectedTransferCnt = 0;
238 gpu->stats()->reset();
239 for (auto srgbEncoding : {GrSRGBEncoded::kNo, GrSRGBEncoded::kYes}) {
240 // create texture
241 GrSurfaceDesc desc;
242 desc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
243 desc.fWidth = kTextureWidth;
244 desc.fHeight = kTextureHeight;
245 desc.fConfig = GrColorTypeToPixelConfig(colorType, srgbEncoding);
246 desc.fSampleCnt = 1;
247
248 if (kUnknown_GrPixelConfig == desc.fConfig) {
249 SkASSERT(GrSRGBEncoded::kYes == srgbEncoding);
250 continue;
251 }
252
253 if (!context->priv().caps()->isConfigTexturable(desc.fConfig) ||
254 (renderTarget && !context->priv().caps()->isConfigRenderable(desc.fConfig))) {
255 continue;
256 }
257
258 SkAutoTMalloc<GrColor> textureData(kTextureWidth * kTextureHeight);
259 size_t textureDataRowBytes = kTextureWidth * sizeof(GrColor);
260 fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, kTextureWidth, textureData.get());
261 GrMipLevel data;
262 data.fPixels = textureData.get();
263 data.fRowBytes = kTextureWidth * sizeof(GrColor);
264 sk_sp<GrTexture> tex = resourceProvider->createTexture(desc, SkBudgeted::kNo, &data, 1);
265 if (!tex) {
266 continue;
267 }
268
269 //////////////////////////
270 // transfer full data
Brian Salomon42cbedd2019-04-09 15:00:05 -0400271 auto bufferRowBytes = gpu->transferPixelsFrom(
272 tex.get(), 0, 0, kTextureWidth, kTextureHeight, readColorType, buffer.get(), 0);
Brian Salomone05ba5a2019-04-08 11:59:07 -0400273 REPORTER_ASSERT(reporter, bufferRowBytes = fullBufferRowBytes);
274 if (!bufferRowBytes) {
275 continue;
276 }
277 ++expectedTransferCnt;
278
279 // TODO(bsalomon): caps to know if the map() is synchronous and skip the flush if so.
280 gpu->finishFlush(nullptr, SkSurface::BackendSurfaceAccess::kNoAccess,
281 SkSurface::kSyncCpu_FlushFlag, 0, nullptr);
282
283 const auto* map = reinterpret_cast<const GrColor*>(buffer->map());
284 REPORTER_ASSERT(reporter, map);
285 if (!map) {
286 continue;
287 }
288 REPORTER_ASSERT(reporter, do_buffers_contain_same_values(textureData.get(),
289 map,
290 kTextureWidth,
291 kTextureHeight,
292 textureDataRowBytes,
Brian Salomon42cbedd2019-04-09 15:00:05 -0400293 bufferRowBytes,
294 readColorType != colorType));
Brian Salomone05ba5a2019-04-08 11:59:07 -0400295 buffer->unmap();
296
Brian Salomon42cbedd2019-04-09 15:00:05 -0400297 ///////////////////////
Brian Salomone05ba5a2019-04-08 11:59:07 -0400298 // Now test a partial read at an offset into the buffer.
Brian Salomon42cbedd2019-04-09 15:00:05 -0400299 bufferRowBytes = gpu->transferPixelsFrom(tex.get(), kPartialLeft, kPartialTop,
300 kPartialWidth, kPartialHeight, readColorType,
301 buffer.get(), partialReadOffset);
Brian Salomone05ba5a2019-04-08 11:59:07 -0400302 REPORTER_ASSERT(reporter, bufferRowBytes = partialBufferRowBytes);
303 if (!bufferRowBytes) {
304 continue;
305 }
306 ++expectedTransferCnt;
307
308 // TODO(bsalomon): caps to know if the map() is synchronous and skip the flush if so.
309 gpu->finishFlush(nullptr, SkSurface::BackendSurfaceAccess::kNoAccess,
310 SkSurface::kSyncCpu_FlushFlag, 0, nullptr);
311
312 map = reinterpret_cast<const GrColor*>(buffer->map());
313 REPORTER_ASSERT(reporter, map);
314 if (!map) {
315 continue;
316 }
317 const GrColor* textureDataStart = reinterpret_cast<const GrColor*>(
318 reinterpret_cast<const char*>(textureData.get()) +
319 textureDataRowBytes * kPartialTop + sizeof(GrColor) * kPartialLeft);
320 const GrColor* bufferStart = reinterpret_cast<const GrColor*>(
321 reinterpret_cast<const char*>(map) + partialReadOffset);
322 REPORTER_ASSERT(reporter, do_buffers_contain_same_values(textureDataStart,
323 bufferStart,
324 kPartialWidth,
325 kPartialHeight,
326 textureDataRowBytes,
Brian Salomon42cbedd2019-04-09 15:00:05 -0400327 bufferRowBytes,
328 readColorType != colorType));
Brian Salomone05ba5a2019-04-08 11:59:07 -0400329 buffer->unmap();
330 }
331#if GR_GPU_STATS
332 REPORTER_ASSERT(reporter, gpu->stats()->transfersFromSurface() == expectedTransferCnt);
333#else
334 (void)expectedTransferCnt;
335#endif
Jim Van Verth2e5eaf02017-06-21 15:55:46 -0400336}
Jim Van Verth686046b2019-03-18 15:39:22 -0400337
Brian Salomone05ba5a2019-04-08 11:59:07 -0400338DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsToTest, reporter, ctxInfo) {
339 if (!ctxInfo.grContext()->priv().caps()->transferBufferSupport()) {
340 return;
341 }
342 // RGBA
343 basic_transfer_to_test(reporter, ctxInfo.grContext(), GrColorType::kRGBA_8888, false);
344 basic_transfer_to_test(reporter, ctxInfo.grContext(), GrColorType::kRGBA_8888, true);
345
346 // BGRA
347 basic_transfer_to_test(reporter, ctxInfo.grContext(), GrColorType::kBGRA_8888, false);
348 basic_transfer_to_test(reporter, ctxInfo.grContext(), GrColorType::kBGRA_8888, true);
349}
350
351// TODO(bsalomon): Vulkan
352DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(TransferPixelsFromTest, reporter, ctxInfo) {
353 if (!ctxInfo.grContext()->priv().caps()->transferBufferSupport()) {
354 return;
355 }
356 // RGBA
Brian Salomon42cbedd2019-04-09 15:00:05 -0400357 basic_transfer_from_test(reporter, ctxInfo, GrColorType::kRGBA_8888, false);
358 basic_transfer_from_test(reporter, ctxInfo, GrColorType::kRGBA_8888, true);
Brian Salomone05ba5a2019-04-08 11:59:07 -0400359
360 // BGRA
Brian Salomon42cbedd2019-04-09 15:00:05 -0400361 basic_transfer_from_test(reporter, ctxInfo, GrColorType::kBGRA_8888, false);
362 basic_transfer_from_test(reporter, ctxInfo, GrColorType::kBGRA_8888, true);
Brian Salomone05ba5a2019-04-08 11:59:07 -0400363}