OSDN Git Service

Add support for uploading to AHBs using Vulkan.
authorGreg Daniel <egdaniel@google.com>
Wed, 20 Feb 2019 13:31:03 +0000 (08:31 -0500)
committerGreg Daniel <egdaniel@google.com>
Tue, 19 Mar 2019 14:09:53 +0000 (10:09 -0400)
Test: manual build and testing on blueline.

Change-Id: I240a5f1e3ba34677b03131eba36ffd8783d99041

libs/hwui/HardwareBitmapUploader.cpp
libs/hwui/HardwareBitmapUploader.h
libs/hwui/renderthread/RenderThread.cpp

index aeeb32c..8f7e814 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "hwui/Bitmap.h"
 #include "renderthread/EglManager.h"
+#include "renderthread/VulkanManager.h"
 #include "thread/ThreadBase.h"
 #include "utils/TimeUtils.h"
 
@@ -25,7 +26,9 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #include <GLES3/gl3.h>
+#include <GrContext.h>
 #include <SkCanvas.h>
+#include <SkImage.h>
 #include <utils/GLUtils.h>
 #include <utils/Trace.h>
 #include <utils/TraceUtils.h>
 
 namespace android::uirenderer {
 
-static std::mutex sLock{};
-static sp<ThreadBase> sUploadThread = nullptr;
-static renderthread::EglManager sEglManager;
-static int sPendingUploads = 0;
-static nsecs_t sLastUpload = 0;
+class AHBUploader;
+// This helper uploader classes allows us to upload using either EGL or Vulkan using the same
+// interface.
+static sp<AHBUploader> sUploader = nullptr;
 
-static bool shouldTimeOutLocked() {
-    nsecs_t durationSince = systemTime() - sLastUpload;
-    return durationSince > 2000_ms;
-}
+struct FormatInfo {
+    PixelFormat pixelFormat;
+    GLint format, type;
+    VkFormat vkFormat;
+    bool isSupported = false;
+    bool valid = true;
+};
 
-static void checkIdleTimeout() {
-    std::lock_guard _lock{sLock};
-    if (sPendingUploads == 0 && shouldTimeOutLocked()) {
-        sEglManager.destroy();
-    } else {
-        sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout);
+class AHBUploader : public RefBase {
+public:
+    virtual ~AHBUploader() {}
+
+    // Called to start creation of the Vulkan and EGL contexts on another thread before we actually
+    // need to do an upload.
+    void initialize() {
+        onInitialize();
     }
-}
 
-static void beginUpload() {
-    std::lock_guard _lock{sLock};
-    sPendingUploads++;
+    void destroy() {
+        std::lock_guard _lock{mLock};
+        LOG_ALWAYS_FATAL_IF(mPendingUploads, "terminate called while uploads in progress");
+        if (mUploadThread) {
+            mUploadThread->requestExit();
+            mUploadThread->join();
+            mUploadThread = nullptr;
+        }
+        onDestroy();
+    }
+
+    bool uploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
+                              sp<GraphicBuffer> graphicBuffer) {
+        ATRACE_CALL();
+        beginUpload();
+        bool result = onUploadHardwareBitmap(bitmap, format, graphicBuffer);
+        endUpload();
+        return result;
+    }
+
+    void postIdleTimeoutCheck() {
+        mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); });
+    }
 
-    if (!sUploadThread) {
-        sUploadThread = new ThreadBase{};
+protected:
+    std::mutex mLock;
+    sp<ThreadBase> mUploadThread = nullptr;
+
+private:
+    virtual void onInitialize() = 0;
+    virtual void onIdle() = 0;
+    virtual void onDestroy() = 0;
+
+    virtual bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
+                                        sp<GraphicBuffer> graphicBuffer) = 0;
+    virtual void onBeginUpload() = 0;
+
+    bool shouldTimeOutLocked() {
+        nsecs_t durationSince = systemTime() - mLastUpload;
+        return durationSince > 2000_ms;
     }
 
-    if (!sUploadThread->isRunning()) {
-        sUploadThread->start("GrallocUploadThread");
+    void idleTimeoutCheck() {
+        std::lock_guard _lock{mLock};
+        if (mPendingUploads == 0 && shouldTimeOutLocked()) {
+            onIdle();
+        } else {
+            this->postIdleTimeoutCheck();
+        }
     }
 
-    if (!sEglManager.hasEglContext()) {
-        sUploadThread->queue().runSync([]() {
-            sEglManager.initialize();
-            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    void beginUpload() {
+        std::lock_guard _lock{mLock};
+        mPendingUploads++;
+
+        if (!mUploadThread) {
+            mUploadThread = new ThreadBase{};
+        }
+        if (!mUploadThread->isRunning()) {
+            mUploadThread->start("GrallocUploadThread");
+        }
+
+        onBeginUpload();
+    }
+
+    void endUpload() {
+        std::lock_guard _lock{mLock};
+        mPendingUploads--;
+        mLastUpload = systemTime();
+    }
+
+    int mPendingUploads = 0;
+    nsecs_t mLastUpload = 0;
+};
+
+#define FENCE_TIMEOUT 2000000000
+
+class EGLUploader : public AHBUploader {
+private:
+    void onInitialize() override {}
+    void onDestroy() override {
+        mEglManager.destroy();
+    }
+    void onIdle() override {
+        mEglManager.destroy();
+    }
+
+    void onBeginUpload() override {
+        if (!mEglManager.hasEglContext()) {
+            mUploadThread->queue().runSync([this]() {
+                this->mEglManager.initialize();
+                glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+            });
+
+            this->postIdleTimeoutCheck();
+        }
+    }
+
+
+    EGLDisplay getUploadEglDisplay() {
+        std::lock_guard _lock{mLock};
+        LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(), "Forgot to begin an upload?");
+        return mEglManager.eglDisplay();
+    }
+
+    bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
+                                sp<GraphicBuffer> graphicBuffer) override {
+        ATRACE_CALL();
+
+        EGLDisplay display = getUploadEglDisplay();
+
+        LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+                            uirenderer::renderthread::EglManager::eglErrorString());
+        // We use an EGLImage to access the content of the GraphicBuffer
+        // The EGL image is later bound to a 2D texture
+        EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
+        AutoEglImage autoImage(display, clientBuffer);
+        if (autoImage.image == EGL_NO_IMAGE_KHR) {
+            ALOGW("Could not create EGL image, err =%s",
+                  uirenderer::renderthread::EglManager::eglErrorString());
+            return false;
+        }
+
+        {
+            ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
+            EGLSyncKHR fence = mUploadThread->queue().runSync([&]() -> EGLSyncKHR {
+                AutoSkiaGlTexture glTexture;
+                glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+                GL_CHECKPOINT(MODERATE);
+
+                // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
+                // provide.
+                // But asynchronous in sense that driver may upload texture onto hardware buffer
+                // when we first use it in drawing
+                glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
+                                format.format, format.type, bitmap.getPixels());
+                GL_CHECKPOINT(MODERATE);
+
+                EGLSyncKHR uploadFence =
+                        eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+                LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR,
+                                    "Could not create sync fence %#x", eglGetError());
+                glFlush();
+                return uploadFence;
+            });
+
+            EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
+            LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+                                "Failed to wait for the fence %#x", eglGetError());
+
+            eglDestroySyncKHR(display, fence);
+        }
+        return true;
+    }
+
+    renderthread::EglManager mEglManager;
+};
+
+class VkUploader : public AHBUploader {
+private:
+    void onInitialize() override {
+        std::lock_guard _lock{mLock};
+        if (!mUploadThread) {
+            mUploadThread = new ThreadBase{};
+        }
+        if (!mUploadThread->isRunning()) {
+            mUploadThread->start("GrallocUploadThread");
+        }
+
+        mUploadThread->queue().post([this]() {
+            std::lock_guard _lock{mVkLock};
+            if (!mVulkanManager.hasVkContext()) {
+                mVulkanManager.initialize();
+            }
         });
-        sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout);
     }
-}
+    void onDestroy() override {
+        mGrContext.reset();
+        mVulkanManager.destroy();
+    }
+    void onIdle() override {
+        mGrContext.reset();
+    }
 
-static void endUpload() {
-    std::lock_guard _lock{sLock};
-    sPendingUploads--;
-    sLastUpload = systemTime();
-}
+    void onBeginUpload() override {
+        {
+            std::lock_guard _lock{mVkLock};
+            if (!mVulkanManager.hasVkContext()) {
+                LOG_ALWAYS_FATAL_IF(mGrContext,
+                    "GrContext exists with no VulkanManager for vulkan uploads");
+                mUploadThread->queue().runSync([this]() {
+                    mVulkanManager.initialize();
+                });
+            }
+        }
+        if (!mGrContext) {
+            GrContextOptions options;
+            mGrContext = mVulkanManager.createContext(options);
+            LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads");
+            this->postIdleTimeoutCheck();
+        }
+    }
 
-static EGLDisplay getUploadEglDisplay() {
-    std::lock_guard _lock{sLock};
-    LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?");
-    return sEglManager.eglDisplay();
-}
+    bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
+                                sp<GraphicBuffer> graphicBuffer) override {
+        ATRACE_CALL();
+
+        std::lock_guard _lock{mLock};
+
+        sk_sp<SkImage> image = SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(),
+            bitmap.pixmap(), reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()));
+        return (image.get() != nullptr);
+    }
+
+    sk_sp<GrContext> mGrContext;
+    renderthread::VulkanManager mVulkanManager;
+    std::mutex mVkLock;
+};
 
 bool HardwareBitmapUploader::hasFP16Support() {
     static std::once_flag sOnce;
@@ -105,16 +297,7 @@ bool HardwareBitmapUploader::hasFP16Support() {
     return hasFP16Support;
 }
 
-#define FENCE_TIMEOUT 2000000000
-
-struct FormatInfo {
-    PixelFormat pixelFormat;
-    GLint format, type;
-    bool isSupported = false;
-    bool valid = true;
-};
-
-static FormatInfo determineFormat(const SkBitmap& skBitmap) {
+static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
     FormatInfo formatInfo;
     switch (skBitmap.info().colorType()) {
         case kRGBA_8888_SkColorType:
@@ -124,15 +307,18 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap) {
             formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
             formatInfo.format = GL_RGBA;
             formatInfo.type = GL_UNSIGNED_BYTE;
+            formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
             break;
         case kRGBA_F16_SkColorType:
             formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support();
             if (formatInfo.isSupported) {
                 formatInfo.type = GL_HALF_FLOAT;
                 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
+                formatInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
             } else {
                 formatInfo.type = GL_UNSIGNED_BYTE;
                 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+                formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
             }
             formatInfo.format = GL_RGBA;
             break;
@@ -141,12 +327,14 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap) {
             formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
             formatInfo.format = GL_RGB;
             formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
+            formatInfo.vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
             break;
         case kGray_8_SkColorType:
-            formatInfo.isSupported = true;
+            formatInfo.isSupported = usingGL;
             formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
             formatInfo.format = GL_LUMINANCE;
             formatInfo.type = GL_UNSIGNED_BYTE;
+            formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
             break;
         default:
             ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
@@ -172,29 +360,37 @@ static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& sourc
     }
 }
 
-class ScopedUploadRequest {
-public:
-    ScopedUploadRequest() { beginUpload(); }
-    ~ScopedUploadRequest() { endUpload(); }
-};
+
+static void createUploader(bool usingGL) {
+    static std::mutex lock;
+    std::lock_guard _lock{lock};
+    if (!sUploader.get()) {
+        if (usingGL) {
+            sUploader = new EGLUploader();
+        } else {
+            sUploader = new VkUploader();
+        }
+    }
+}
 
 sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) {
     ATRACE_CALL();
 
-    FormatInfo format = determineFormat(sourceBitmap);
+    bool usingGL = uirenderer::Properties::getRenderPipelineType() ==
+            uirenderer::RenderPipelineType::SkiaGL;
+
+    FormatInfo format = determineFormat(sourceBitmap, usingGL);
     if (!format.valid) {
         return nullptr;
     }
 
-    ScopedUploadRequest _uploadRequest{};
-
     SkBitmap bitmap = makeHwCompatible(format, sourceBitmap);
     sp<GraphicBuffer> buffer = new GraphicBuffer(
             static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()),
             format.pixelFormat,
             GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
                     GraphicBuffer::USAGE_SW_READ_NEVER,
-            std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) +
+            std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) +
                     "]");
 
     status_t error = buffer->initCheck();
@@ -203,64 +399,24 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou
         return nullptr;
     }
 
-    EGLDisplay display = getUploadEglDisplay();
-
-    LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
-                        uirenderer::renderthread::EglManager::eglErrorString());
-    // We use an EGLImage to access the content of the GraphicBuffer
-    // The EGL image is later bound to a 2D texture
-    EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer->getNativeBuffer();
-    AutoEglImage autoImage(display, clientBuffer);
-    if (autoImage.image == EGL_NO_IMAGE_KHR) {
-        ALOGW("Could not create EGL image, err =%s",
-              uirenderer::renderthread::EglManager::eglErrorString());
-        return nullptr;
-    }
-
-    {
-        ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
-        EGLSyncKHR fence = sUploadThread->queue().runSync([&]() -> EGLSyncKHR {
-            AutoSkiaGlTexture glTexture;
-            glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
-            GL_CHECKPOINT(MODERATE);
-
-            // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
-            // provide.
-            // But asynchronous in sense that driver may upload texture onto hardware buffer when we
-            // first
-            // use it in drawing
-            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format.format,
-                            format.type, bitmap.getPixels());
-            GL_CHECKPOINT(MODERATE);
-
-            EGLSyncKHR uploadFence =
-                    eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
-            LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, "Could not create sync fence %#x",
-                                eglGetError());
-            glFlush();
-            return uploadFence;
-        });
-
-        EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
-        LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
-                            "Failed to wait for the fence %#x", eglGetError());
+    createUploader(usingGL);
 
-        eglDestroySyncKHR(display, fence);
+    if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) {
+        return nullptr;
     }
-
-    return Bitmap::createFrom(buffer.get(), bitmap.colorType(), bitmap.refColorSpace(),
+    return Bitmap::createFrom(buffer, bitmap.colorType(), bitmap.refColorSpace(),
                               bitmap.alphaType(), Bitmap::computePalette(bitmap));
 }
 
+void HardwareBitmapUploader::initialize() {
+    bool usingGL = uirenderer::Properties::getRenderPipelineType() ==
+            uirenderer::RenderPipelineType::SkiaGL;
+    createUploader(usingGL);
+    sUploader->initialize();
+}
+
 void HardwareBitmapUploader::terminate() {
-    std::lock_guard _lock{sLock};
-    LOG_ALWAYS_FATAL_IF(sPendingUploads, "terminate called while uploads in progress");
-    if (sUploadThread) {
-        sUploadThread->requestExit();
-        sUploadThread->join();
-        sUploadThread = nullptr;
-    }
-    sEglManager.destroy();
+    sUploader->destroy();
 }
 
 }  // namespace android::uirenderer
index 6f41e6d..c300593 100644 (file)
@@ -22,9 +22,11 @@ namespace android::uirenderer {
 
 class ANDROID_API HardwareBitmapUploader {
 public:
-    static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
+    static void initialize();
     static void terminate();
 
+    static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
+
     static bool hasFP16Support();
 };
 
index def805a..6cce319 100644 (file)
@@ -29,6 +29,7 @@
 #include "utils/FatVector.h"
 #include "utils/TimeUtils.h"
 #include "utils/TraceUtils.h"
+#include "../HardwareBitmapUploader.h"
 
 #ifdef HWUI_GLES_WRAP_ENABLED
 #include "debug/GlesDriver.h"
@@ -415,6 +416,7 @@ void RenderThread::preload() {
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
         requireVkContext();
     }
+    HardwareBitmapUploader::initialize();
 }
 
 } /* namespace renderthread */