Add unit test to test basic importing and drawing of AHardwareBuffers.

Bug: skia:
Change-Id: If601693109148a7d5c6e6855443a3401c36e5f01
Reviewed-on: https://skia-review.googlesource.com/153545
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/gn/tests.gni b/gn/tests.gni
index e829c70..88a7b6d 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -91,6 +91,7 @@
   "$_tests/GpuLayerCacheTest.cpp",
   "$_tests/GpuRectanizerTest.cpp",
   "$_tests/GradientTest.cpp",
+  "$_tests/GrAHardwareBufferTest.cpp",
   "$_tests/GrAllocatorTest.cpp",
   "$_tests/GrCCPRTest.cpp",
   "$_tests/GrContextAbandonTest.cpp",
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index c700335..4fe6416 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -58,6 +58,7 @@
     fPreferClientSideDynamicBuffers = false;
     fPreferFullscreenClears = false;
     fMustClearUploadedBufferData = false;
+    fSupportsAHardwareBufferImages = false;
     fSampleShadingSupport = false;
     fFenceSyncSupport = false;
     fCrossContextTextureSupport = false;
@@ -169,6 +170,7 @@
     writer->appendBool("Prefer client-side dynamic buffers", fPreferClientSideDynamicBuffers);
     writer->appendBool("Prefer fullscreen clears", fPreferFullscreenClears);
     writer->appendBool("Must clear buffer memory", fMustClearUploadedBufferData);
+    writer->appendBool("Supports importing AHardwareBuffers", fSupportsAHardwareBufferImages);
     writer->appendBool("Sample shading support", fSampleShadingSupport);
     writer->appendBool("Fence sync support", fFenceSyncSupport);
     writer->appendBool("Cross context texture support", fCrossContextTextureSupport);
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 9e18c38..d70254b 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -228,6 +228,12 @@
         is not initialized (even if not read by draw calls). */
     bool mustClearUploadedBufferData() const { return fMustClearUploadedBufferData; }
 
+    /** Returns true if the given backend supports importing AHardwareBuffers via the
+     * GrAHardwarebufferImageGenerator. This will only ever be supported on Android devices with API
+     * level >= 26.
+     * */
+    bool supportsAHardwareBufferImages() const { return fSupportsAHardwareBufferImages; }
+
     bool wireframeMode() const { return fWireframeMode; }
 
     bool sampleShadingSupport() const { return fSampleShadingSupport; }
@@ -315,6 +321,7 @@
     bool fPreferClientSideDynamicBuffers             : 1;
     bool fPreferFullscreenClears                     : 1;
     bool fMustClearUploadedBufferData                : 1;
+    bool fSupportsAHardwareBufferImages              : 1;
 
     // Driver workaround
     bool fBlacklistCoverageCounting                  : 1;
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 37f255d..e6f5994 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -169,6 +169,11 @@
 void GrResourceCache::abandonAll() {
     AutoValidate av(this);
 
+    for (int i = 0; i < fResourcesWaitingForFreeMsg.count(); ++i) {
+        fResourcesWaitingForFreeMsg[i]->abandon();
+    }
+    fResourcesWaitingForFreeMsg.reset();
+
     while (fNonpurgeableResources.count()) {
         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
         SkASSERT(!back->wasDestroyed());
@@ -189,6 +194,7 @@
     SkASSERT(!fBudgetedCount);
     SkASSERT(!fBudgetedBytes);
     SkASSERT(!fPurgeableBytes);
+    SkASSERT(!fResourcesWaitingForFreeMsg.count());
 }
 
 void GrResourceCache::releaseAll() {
@@ -201,6 +207,7 @@
     for (int i = 0; i < fResourcesWaitingForFreeMsg.count(); ++i) {
         fResourcesWaitingForFreeMsg[i]->unref();
     }
+    fResourcesWaitingForFreeMsg.reset();
 
     SkASSERT(fProxyProvider); // better have called setProxyProvider
     // We must remove the uniqueKeys from the proxies here. While they possess a uniqueKey
@@ -227,6 +234,7 @@
     SkASSERT(!fBudgetedCount);
     SkASSERT(!fBudgetedBytes);
     SkASSERT(!fPurgeableBytes);
+    SkASSERT(!fResourcesWaitingForFreeMsg.count());
 }
 
 class GrResourceCache::AvailableForScratchUse {
@@ -586,9 +594,14 @@
     for (int i = 0; i < msgs.count(); ++i) {
         SkASSERT(msgs[i].fOwningUniqueID == fContextUniqueID);
         int index = fResourcesWaitingForFreeMsg.find(msgs[i].fResource);
-        SkASSERT(index != -1);
-        fResourcesWaitingForFreeMsg.removeShuffle(index);
-        msgs[i].fResource->unref();
+        // If we called release or abandon on the GrContext we will have already released our ref on
+        // the GrGpuResource. If then the message arrives before the actual GrContext gets destroyed
+        // we will try to process the message when we destroy the GrContext. This protects us from
+        // trying to unref the resource twice.
+        if (index != -1) {
+            fResourcesWaitingForFreeMsg.removeShuffle(index);
+            msgs[i].fResource->unref();
+        }
     }
 }
 
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 55e85d9..f32afb0 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -291,6 +291,10 @@
         fClearTextureSupport = true;
     }
 
+#if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
+    fSupportsAHardwareBufferImages = true;
+#endif
+
     /**************************************************************************
     * GrShaderCaps fields
     **************************************************************************/
diff --git a/tests/GrAHardwareBufferTest.cpp b/tests/GrAHardwareBufferTest.cpp
new file mode 100644
index 0000000..2c698cb
--- /dev/null
+++ b/tests/GrAHardwareBufferTest.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 "SkTypes.h"
+
+#if SK_SUPPORT_GPU && defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
+
+#include "GrAHardwareBufferImageGenerator.h"
+#include "GrContext.h"
+#include "GrContextFactory.h"
+#include "GrContextPriv.h"
+#include "GrGpu.h"
+#include "SkImage.h"
+#include "SkSurface.h"
+#include "Test.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;
+            }
+        }
+    }
+    return result;
+}
+
+static void cleanup_resources(AHardwareBuffer* buffer) {
+    if (buffer) {
+        AHardwareBuffer_release(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) {
+    GrContext* context = context_info.grContext();
+    if (!context->contextPriv().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();
+    uint32_t* dst = bufferAddr;
+    for (int y = 0; y < DEV_H; ++y) {
+        memcpy(dst, src, DEV_W * bbp);
+        src += DEV_W;
+        dst += hwbDesc.stride;
+    }
+    AHardwareBuffer_unlock(buffer, nullptr);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Wrap AHardwareBuffer in SkImage
+    ///////////////////////////////////////////////////////////////////////////
+
+    sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(buffer);
+    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);
+}
+
+#endif