| /* | 
 |  * Copyright 2018 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 "include/core/SkTypes.h" | 
 |  | 
 | #if SK_SUPPORT_GPU && defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26 | 
 |  | 
 | #include "include/core/SkCanvas.h" | 
 | #include "include/core/SkImage.h" | 
 | #include "include/core/SkSurface.h" | 
 | #include "include/gpu/GrDirectContext.h" | 
 | #include "src/gpu/GrAHardwareBufferImageGenerator.h" | 
 | #include "src/gpu/GrContextPriv.h" | 
 | #include "src/gpu/GrGpu.h" | 
 | #include "tests/Test.h" | 
 | #include "tools/gpu/GrContextFactory.h" | 
 |  | 
 | #include <android/hardware_buffer.h> | 
 | #include <cinttypes> | 
 |  | 
 | static const int DEV_W = 16, DEV_H = 16; | 
 |  | 
 | static SkPMColor get_src_color(int x, int y) { | 
 |     SkASSERT(x >= 0 && x < DEV_W); | 
 |     SkASSERT(y >= 0 && y < DEV_H); | 
 |  | 
 |     U8CPU r = x; | 
 |     U8CPU g = y; | 
 |     U8CPU b = 0xc; | 
 |  | 
 |     U8CPU a = 0xff; | 
 |     switch ((x+y) % 5) { | 
 |         case 0: | 
 |             a = 0xff; | 
 |             break; | 
 |         case 1: | 
 |             a = 0x80; | 
 |             break; | 
 |         case 2: | 
 |             a = 0xCC; | 
 |             break; | 
 |         case 4: | 
 |             a = 0x01; | 
 |             break; | 
 |         case 3: | 
 |             a = 0x00; | 
 |             break; | 
 |     } | 
 |     a = 0xff; | 
 |     return SkPremultiplyARGBInline(a, r, g, b); | 
 | } | 
 |  | 
 | static SkBitmap make_src_bitmap() { | 
 |     static SkBitmap bmp; | 
 |     if (bmp.isNull()) { | 
 |         bmp.allocN32Pixels(DEV_W, DEV_H); | 
 |         intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels()); | 
 |         for (int y = 0; y < DEV_H; ++y) { | 
 |             for (int x = 0; x < DEV_W; ++x) { | 
 |                 SkPMColor* pixel = reinterpret_cast<SkPMColor*>( | 
 |                         pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel()); | 
 |                 *pixel = get_src_color(x, y); | 
 |             } | 
 |         } | 
 |     } | 
 |     return bmp; | 
 | } | 
 |  | 
 | static bool check_read(skiatest::Reporter* reporter, const SkBitmap& expectedBitmap, | 
 |                        const SkBitmap& actualBitmap) { | 
 |     bool result = true; | 
 |     for (int y = 0; y < DEV_H && result; ++y) { | 
 |         for (int x = 0; x < DEV_W && result; ++x) { | 
 |             const uint32_t srcPixel = *expectedBitmap.getAddr32(x, y); | 
 |             const uint32_t dstPixel = *actualBitmap.getAddr32(x, y); | 
 |             if (srcPixel != dstPixel) { | 
 |                 ERRORF(reporter, "Expected readback pixel (%d, %d) value 0x%08x, got 0x%08x.", | 
 |                        x, y,  srcPixel, dstPixel); | 
 |                 result = false; | 
 |             }/* else { | 
 |                 SkDebugf("Got good pixel (%d, %d) value 0x%08x, got 0x%08x.\n", | 
 |                        x, y,  srcPixel, dstPixel); | 
 |             }*/ | 
 |         } | 
 |     } | 
 |     return result; | 
 | } | 
 |  | 
 | static void cleanup_resources(AHardwareBuffer* buffer) { | 
 |     if (buffer) { | 
 |         AHardwareBuffer_release(buffer); | 
 |     } | 
 | } | 
 |  | 
 | static void basic_draw_test_helper(skiatest::Reporter* reporter, | 
 |                                    const sk_gpu_test::ContextInfo& info, | 
 |                                    GrSurfaceOrigin surfaceOrigin) { | 
 |  | 
 |     auto context = info.directContext(); | 
 |     if (!context->priv().caps()->supportsAHardwareBufferImages()) { | 
 |         return; | 
 |     } | 
 |  | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |     // Setup SkBitmaps | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |  | 
 |     const SkBitmap srcBitmap = make_src_bitmap(); | 
 |  | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |     // Setup AHardwareBuffer | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |  | 
 |     AHardwareBuffer* buffer = nullptr; | 
 |  | 
 |     AHardwareBuffer_Desc hwbDesc; | 
 |     hwbDesc.width = DEV_W; | 
 |     hwbDesc.height = DEV_H; | 
 |     hwbDesc.layers = 1; | 
 |     hwbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | | 
 |                     AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | | 
 |                     AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; | 
 |     hwbDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; | 
 |     // The following three are not used in the allocate | 
 |     hwbDesc.stride = 0; | 
 |     hwbDesc.rfu0= 0; | 
 |     hwbDesc.rfu1= 0; | 
 |  | 
 |     if (int error = AHardwareBuffer_allocate(&hwbDesc, &buffer)) { | 
 |         ERRORF(reporter, "Failed to allocated hardware buffer, error: %d", error); | 
 |         cleanup_resources(buffer); | 
 |         return; | 
 |     } | 
 |  | 
 |     // Get actual desc for allocated buffer so we know the stride for uploading cpu data. | 
 |     AHardwareBuffer_describe(buffer, &hwbDesc); | 
 |  | 
 |     uint32_t* bufferAddr; | 
 |     if (AHardwareBuffer_lock(buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr, | 
 |                              reinterpret_cast<void**>(&bufferAddr))) { | 
 |         ERRORF(reporter, "Failed to lock hardware buffer"); | 
 |         cleanup_resources(buffer); | 
 |         return; | 
 |     } | 
 |  | 
 |     int bbp = srcBitmap.bytesPerPixel(); | 
 |     uint32_t* src = (uint32_t*)srcBitmap.getPixels(); | 
 |     int nextLineStep = DEV_W; | 
 |     if (surfaceOrigin == kBottomLeft_GrSurfaceOrigin) { | 
 |         nextLineStep = -nextLineStep; | 
 |         src += (DEV_H-1)*DEV_W; | 
 |     } | 
 |     uint32_t* dst = bufferAddr; | 
 |     for (int y = 0; y < DEV_H; ++y) { | 
 |         memcpy(dst, src, DEV_W * bbp); | 
 |         src += nextLineStep; | 
 |         dst += hwbDesc.stride; | 
 |     } | 
 |     AHardwareBuffer_unlock(buffer, nullptr); | 
 |  | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |     // Wrap AHardwareBuffer in SkImage | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |  | 
 |     sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(buffer, kPremul_SkAlphaType, | 
 |                                                             nullptr, surfaceOrigin); | 
 |     REPORTER_ASSERT(reporter, image); | 
 |  | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |     // Make a surface to draw into | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |  | 
 |     SkImageInfo imageInfo = SkImageInfo::Make(DEV_W, DEV_H, kRGBA_8888_SkColorType, | 
 |                                               kPremul_SkAlphaType); | 
 |     sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, | 
 |                                                            imageInfo); | 
 |     REPORTER_ASSERT(reporter, surface); | 
 |  | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |     // Draw the AHardwareBuffer SkImage into surface | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |  | 
 |     surface->getCanvas()->drawImage(image, 0, 0); | 
 |  | 
 |     SkBitmap readbackBitmap; | 
 |     readbackBitmap.allocN32Pixels(DEV_W, DEV_H); | 
 |  | 
 |     REPORTER_ASSERT(reporter, surface->readPixels(readbackBitmap, 0, 0)); | 
 |     REPORTER_ASSERT(reporter, check_read(reporter, srcBitmap, readbackBitmap)); | 
 |  | 
 |     image.reset(); | 
 |  | 
 |     cleanup_resources(buffer); | 
 |  | 
 | } | 
 |  | 
 | // Basic test to make sure we can import an AHardwareBuffer into an SkImage and draw it into a | 
 | // surface. | 
 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrAHardwareBuffer_BasicDrawTest, | 
 |                                    reporter, context_info) { | 
 |     basic_draw_test_helper(reporter, context_info, kTopLeft_GrSurfaceOrigin); | 
 |     basic_draw_test_helper(reporter, context_info, kBottomLeft_GrSurfaceOrigin); | 
 | } | 
 |  | 
 | static void surface_draw_test_helper(skiatest::Reporter* reporter, | 
 |                                      const sk_gpu_test::ContextInfo& info, | 
 |                                      GrSurfaceOrigin surfaceOrigin) { | 
 |  | 
 |     auto context = info.directContext(); | 
 |     if (!context->priv().caps()->supportsAHardwareBufferImages()) { | 
 |         return; | 
 |     } | 
 |  | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |     // Setup SkBitmaps | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |  | 
 |     const SkBitmap srcBitmap = make_src_bitmap(); | 
 |  | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |     // Setup AHardwareBuffer | 
 |     /////////////////////////////////////////////////////////////////////////// | 
 |  | 
 |     AHardwareBuffer* buffer = nullptr; | 
 |  | 
 |     AHardwareBuffer_Desc hwbDesc; | 
 |     hwbDesc.width = DEV_W; | 
 |     hwbDesc.height = DEV_H; | 
 |     hwbDesc.layers = 1; | 
 |     hwbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | | 
 |                     AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER | | 
 |                     AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | | 
 |                     AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT; | 
 |  | 
 |     hwbDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; | 
 |     // The following three are not used in the allocate | 
 |     hwbDesc.stride = 0; | 
 |     hwbDesc.rfu0= 0; | 
 |     hwbDesc.rfu1= 0; | 
 |  | 
 |     if (int error = AHardwareBuffer_allocate(&hwbDesc, &buffer)) { | 
 |         ERRORF(reporter, "Failed to allocated hardware buffer, error: %d", error); | 
 |         cleanup_resources(buffer); | 
 |         return; | 
 |     } | 
 |  | 
 |     sk_sp<SkSurface> surface = SkSurface::MakeFromAHardwareBuffer(context, buffer, surfaceOrigin, | 
 |                                                                   nullptr, nullptr); | 
 |     if (!surface) { | 
 |         ERRORF(reporter, "Failed to make SkSurface."); | 
 |         cleanup_resources(buffer); | 
 |         return; | 
 |     } | 
 |  | 
 |     surface->getCanvas()->drawBitmap(srcBitmap, 0, 0); | 
 |  | 
 |     SkBitmap readbackBitmap; | 
 |     readbackBitmap.allocN32Pixels(DEV_W, DEV_H); | 
 |  | 
 |     REPORTER_ASSERT(reporter, surface->readPixels(readbackBitmap, 0, 0)); | 
 |     REPORTER_ASSERT(reporter, check_read(reporter, srcBitmap, readbackBitmap)); | 
 |  | 
 |     cleanup_resources(buffer); | 
 | } | 
 |  | 
 | // Test to make sure we can import an AHardwareBuffer into an SkSurface and draw into it. | 
 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrAHardwareBuffer_ImportAsSurface, | 
 |                                    reporter, context_info) { | 
 |     surface_draw_test_helper(reporter, context_info, kTopLeft_GrSurfaceOrigin); | 
 |     surface_draw_test_helper(reporter, context_info, kBottomLeft_GrSurfaceOrigin); | 
 | } | 
 |  | 
 | #endif |