| /* | |
| * Copyright 2017 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| // This is a GPU-backend specific test. It relies on static intializers to work | |
| #include "SkTypes.h" | |
| #include "GrContextFactory.h" | |
| #include "GrContextPriv.h" | |
| #include "GrGpu.h" | |
| #include "GrResourceProvider.h" | |
| #include "GrSurfaceProxy.h" | |
| #include "GrTexture.h" | |
| #include "SkGr.h" | |
| #include "SkSurface.h" | |
| #include "Test.h" | |
| using sk_gpu_test::GrContextFactory; | |
| void fill_transfer_data(int left, int top, int width, int height, int bufferWidth, | |
| GrColor* data) { | |
| // build red-green gradient | |
| for (int j = top; j < top + height; ++j) { | |
| for (int i = left; i < left + width; ++i) { | |
| unsigned int red = (unsigned int)(256.f*((i - left) / (float)width)); | |
| unsigned int green = (unsigned int)(256.f*((j - top) / (float)height)); | |
| data[i + j*bufferWidth] = GrColorPackRGBA(red - (red>>8), | |
| green - (green>>8), 0xff, 0xff); | |
| } | |
| } | |
| } | |
| bool does_full_buffer_contain_correct_values(GrColor* srcBuffer, | |
| GrColor* dstBuffer, | |
| int width, | |
| int height, | |
| int bufferWidth, | |
| int bufferHeight) { | |
| GrColor* srcPtr = srcBuffer; | |
| GrColor* dstPtr = dstBuffer; | |
| for (int j = 0; j < height; ++j) { | |
| for (int i = 0; i < width; ++i) { | |
| if (srcPtr[i] != dstPtr[i]) { | |
| return false; | |
| } | |
| } | |
| srcPtr += bufferWidth; | |
| dstPtr += bufferWidth; | |
| } | |
| return true; | |
| } | |
| void basic_transfer_test(skiatest::Reporter* reporter, GrContext* context, GrColorType colorType, | |
| bool renderTarget) { | |
| if (GrCaps::kNone_MapFlags == context->priv().caps()->mapBufferFlags()) { | |
| return; | |
| } | |
| auto resourceProvider = context->priv().resourceProvider(); | |
| GrGpu* gpu = context->priv().getGpu(); | |
| // set up the data | |
| const int kTextureWidth = 16; | |
| const int kTextureHeight = 16; | |
| #ifdef SK_BUILD_FOR_IOS | |
| // UNPACK_ROW_LENGTH is broken on iOS so rowBytes needs to match data width | |
| const int kBufferWidth = GrBackendApi::kOpenGL == context->backend() ? 16 : 20; | |
| #else | |
| const int kBufferWidth = 20; | |
| #endif | |
| const int kBufferHeight = 16; | |
| size_t rowBytes = kBufferWidth * sizeof(GrColor); | |
| SkAutoTMalloc<GrColor> srcBuffer(kBufferWidth*kBufferHeight); | |
| SkAutoTMalloc<GrColor> dstBuffer(kBufferWidth*kBufferHeight); | |
| fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, kBufferWidth, srcBuffer.get()); | |
| // create and fill transfer buffer | |
| size_t size = rowBytes*kBufferHeight; | |
| sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(size, GrGpuBufferType::kXferCpuToGpu, | |
| kDynamic_GrAccessPattern)); | |
| if (!buffer) { | |
| return; | |
| } | |
| void* data = buffer->map(); | |
| memcpy(data, srcBuffer.get(), size); | |
| buffer->unmap(); | |
| for (auto srgbEncoding : {GrSRGBEncoded::kNo, GrSRGBEncoded::kYes}) { | |
| // create texture | |
| GrSurfaceDesc desc; | |
| desc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags; | |
| desc.fWidth = kTextureWidth; | |
| desc.fHeight = kTextureHeight; | |
| desc.fConfig = GrColorTypeToPixelConfig(colorType, srgbEncoding); | |
| desc.fSampleCnt = 1; | |
| if (kUnknown_GrPixelConfig == desc.fConfig) { | |
| SkASSERT(GrSRGBEncoded::kYes == srgbEncoding); | |
| continue; | |
| } | |
| if (!context->priv().caps()->isConfigTexturable(desc.fConfig) || | |
| (renderTarget && !context->priv().caps()->isConfigRenderable(desc.fConfig))) { | |
| continue; | |
| } | |
| sk_sp<GrTexture> tex = resourceProvider->createTexture(desc, SkBudgeted::kNo); | |
| if (!tex) { | |
| continue; | |
| } | |
| ////////////////////////// | |
| // transfer full data | |
| bool result; | |
| result = gpu->transferPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType, | |
| buffer.get(), 0, rowBytes); | |
| REPORTER_ASSERT(reporter, result); | |
| memset(dstBuffer.get(), 0xCDCD, size); | |
| result = gpu->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType, | |
| dstBuffer.get(), rowBytes); | |
| if (result) { | |
| REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_values(srcBuffer, | |
| dstBuffer, | |
| kTextureWidth, | |
| kTextureHeight, | |
| kBufferWidth, | |
| kBufferHeight)); | |
| } | |
| ////////////////////////// | |
| // transfer partial data | |
| #ifdef SK_BUILD_FOR_IOS | |
| // UNPACK_ROW_LENGTH is broken on iOS so we can't do partial transfers | |
| if (GrBackendApi::kOpenGL == context->backend()) { | |
| continue; | |
| } | |
| #endif | |
| const int kLeft = 2; | |
| const int kTop = 10; | |
| const int kWidth = 10; | |
| const int kHeight = 2; | |
| // change color of subrectangle | |
| fill_transfer_data(kLeft, kTop, kWidth, kHeight, kBufferWidth, srcBuffer.get()); | |
| data = buffer->map(); | |
| memcpy(data, srcBuffer.get(), size); | |
| buffer->unmap(); | |
| size_t offset = sizeof(GrColor) * (kTop * kBufferWidth + kLeft); | |
| result = gpu->transferPixels(tex.get(), kLeft, kTop, kWidth, kHeight, colorType, | |
| buffer.get(), offset, rowBytes); | |
| REPORTER_ASSERT(reporter, result); | |
| memset(dstBuffer.get(), 0xCDCD, size); | |
| result = gpu->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, colorType, | |
| dstBuffer.get(), rowBytes); | |
| if (result) { | |
| REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_values(srcBuffer, | |
| dstBuffer, | |
| kTextureWidth, | |
| kTextureHeight, | |
| kBufferWidth, | |
| kBufferHeight)); | |
| } | |
| } | |
| } | |
| DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsTest, reporter, ctxInfo) { | |
| // RGBA | |
| basic_transfer_test(reporter, ctxInfo.grContext(), GrColorType::kRGBA_8888, false); | |
| basic_transfer_test(reporter, ctxInfo.grContext(), GrColorType::kRGBA_8888, true); | |
| // BGRA | |
| basic_transfer_test(reporter, ctxInfo.grContext(), GrColorType::kBGRA_8888, false); | |
| basic_transfer_test(reporter, ctxInfo.grContext(), GrColorType::kBGRA_8888, true); | |
| } |