A better HW Bitmap uploader

Move all HW bitmap upload operations off of RenderThread.
Ensure EGL context outlives all upload requests

Bug: 79250950
Test: builds, boots, systrace is good, CTS bitmap tests pass

Change-Id: I5ace6c516d33b1afdf1a407cd8b183f6b60c22c1
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 4c2a0218..e2e3420 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -206,6 +206,7 @@
         "FrameInfoVisualizer.cpp",
         "GlLayer.cpp",
         "GpuMemoryTracker.cpp",
+        "HardwareBitmapUploader.cpp",
         "Interpolator.cpp",
         "JankTracker.cpp",
         "Layer.cpp",
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index c0944e03..8cf115e 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -44,9 +44,7 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-Caches::Caches(RenderState& renderState)
-        : mRenderState(&renderState)
-        , mInitialized(false) {
+Caches::Caches(RenderState& renderState) : mRenderState(&renderState), mInitialized(false) {
     INIT_LOGD("Creating OpenGL renderer caches");
     init();
     initConstraints();
@@ -153,8 +151,7 @@
 // Memory management
 ///////////////////////////////////////////////////////////////////////////////
 
-void Caches::clearGarbage() {
-}
+void Caches::clearGarbage() {}
 
 void Caches::flush(FlushMode mode) {
     clearGarbage();
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 40cc73a..10fcee8 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -31,7 +31,7 @@
 namespace android {
 namespace uirenderer {
 
-static constexpr android::DisplayInfo sDummyDisplay {
+static constexpr android::DisplayInfo sDummyDisplay{
         1080,   // w
         1920,   // h
         320.0,  // xdpi
diff --git a/libs/hwui/EglReadback.cpp b/libs/hwui/EglReadback.cpp
index 2d5367b..65becf8 100644
--- a/libs/hwui/EglReadback.cpp
+++ b/libs/hwui/EglReadback.cpp
@@ -25,8 +25,7 @@
 namespace android {
 namespace uirenderer {
 
-CopyResult EglReadback::copySurfaceInto(Surface& surface, const Rect& srcRect,
-                                           SkBitmap* bitmap) {
+CopyResult EglReadback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
     ATRACE_CALL();
     // Setup the source
     sp<GraphicBuffer> sourceBuffer;
@@ -55,9 +54,8 @@
     return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap);
 }
 
-CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
-                                                 Matrix4& texTransform, const Rect& srcRect,
-                                                 SkBitmap* bitmap) {
+CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform,
+                                              const Rect& srcRect, SkBitmap* bitmap) {
     mRenderThread.requireGlContext();
     // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
     // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
new file mode 100644
index 0000000..6408ce6
--- /dev/null
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HardwareBitmapUploader.h"
+
+#include "hwui/Bitmap.h"
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <SkCanvas.h>
+#include <utils/GLUtils.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+#include <thread>
+
+namespace android::uirenderer {
+
+static std::mutex sLock{};
+static ThreadBase* sUploadThread = nullptr;
+static renderthread::EglManager sEglManager;
+static int sPendingUploads = 0;
+static nsecs_t sLastUpload = 0;
+
+static bool shouldTimeOutLocked() {
+    nsecs_t durationSince = systemTime() - sLastUpload;
+    return durationSince > 2000_ms;
+}
+
+static void checkIdleTimeout() {
+    std::lock_guard{sLock};
+    if (sPendingUploads == 0 && shouldTimeOutLocked()) {
+        sEglManager.destroy();
+    } else {
+        sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout);
+    }
+}
+
+static void beginUpload() {
+    std::lock_guard{sLock};
+    sPendingUploads++;
+
+    if (!sUploadThread) {
+        sUploadThread = new ThreadBase{};
+    }
+
+    if (!sUploadThread->isRunning()) {
+        sUploadThread->start("GrallocUploadThread");
+    }
+
+    if (!sEglManager.hasEglContext()) {
+        sUploadThread->queue().runSync([]() {
+            sEglManager.initialize();
+            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+        });
+        sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout);
+    }
+}
+
+static void endUpload() {
+    std::lock_guard{sLock};
+    sPendingUploads--;
+    sLastUpload = systemTime();
+}
+
+static EGLDisplay getUploadEglDisplay() {
+    std::lock_guard{sLock};
+    LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?");
+    return sEglManager.eglDisplay();
+}
+
+static bool hasFP16Support() {
+    static std::once_flag sOnce;
+    static bool hasFP16Support = false;
+
+    // Gralloc shouldn't let us create a USAGE_HW_TEXTURE if GLES is unable to consume it, so
+    // we don't need to double-check the GLES version/extension.
+    std::call_once(sOnce, []() {
+        sp<GraphicBuffer> buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_FP16,
+                                                     GraphicBuffer::USAGE_HW_TEXTURE |
+                                                             GraphicBuffer::USAGE_SW_WRITE_NEVER |
+                                                             GraphicBuffer::USAGE_SW_READ_NEVER,
+                                                     "tempFp16Buffer");
+        status_t error = buffer->initCheck();
+        hasFP16Support = !error;
+    });
+
+    return hasFP16Support;
+}
+
+#define FENCE_TIMEOUT 2000000000
+
+class AutoEglImage {
+public:
+    AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
+        EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+        image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
+                                  imageAttrs);
+    }
+
+    ~AutoEglImage() {
+        if (image != EGL_NO_IMAGE_KHR) {
+            eglDestroyImageKHR(mDisplay, image);
+        }
+    }
+
+    EGLImageKHR image = EGL_NO_IMAGE_KHR;
+
+private:
+    EGLDisplay mDisplay = EGL_NO_DISPLAY;
+};
+
+class AutoSkiaGlTexture {
+public:
+    AutoSkiaGlTexture() {
+        glGenTextures(1, &mTexture);
+        glBindTexture(GL_TEXTURE_2D, mTexture);
+    }
+
+    ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
+
+private:
+    GLuint mTexture = 0;
+};
+
+struct FormatInfo {
+    PixelFormat pixelFormat;
+    GLint format, type;
+    bool isSupported = false;
+    bool valid = true;
+};
+
+static FormatInfo determineFormat(const SkBitmap& skBitmap) {
+    FormatInfo formatInfo;
+    // TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
+    switch (skBitmap.info().colorType()) {
+        case kRGBA_8888_SkColorType:
+            formatInfo.isSupported = true;
+        // ARGB_4444 is upconverted to RGBA_8888
+        case kARGB_4444_SkColorType:
+            formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+            formatInfo.format = GL_RGBA;
+            formatInfo.type = GL_UNSIGNED_BYTE;
+            break;
+        case kRGBA_F16_SkColorType:
+            formatInfo.isSupported = hasFP16Support();
+            if (formatInfo.isSupported) {
+                formatInfo.type = GL_HALF_FLOAT;
+                formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
+            } else {
+                formatInfo.type = GL_UNSIGNED_BYTE;
+                formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+            }
+            formatInfo.format = GL_RGBA;
+            break;
+        case kRGB_565_SkColorType:
+            formatInfo.isSupported = true;
+            formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
+            formatInfo.format = GL_RGB;
+            formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
+            break;
+        case kGray_8_SkColorType:
+            formatInfo.isSupported = true;
+            formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+            formatInfo.format = GL_LUMINANCE;
+            formatInfo.type = GL_UNSIGNED_BYTE;
+            break;
+        default:
+            ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
+            formatInfo.valid = false;
+    }
+    return formatInfo;
+}
+
+static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
+    if (format.isSupported) {
+        return source;
+    } else {
+        SkBitmap bitmap;
+        const SkImageInfo& info = source.info();
+        bitmap.allocPixels(
+                SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), nullptr));
+        bitmap.eraseColor(0);
+        if (info.colorType() == kRGBA_F16_SkColorType) {
+            // Drawing RGBA_F16 onto ARGB_8888 is not supported
+            source.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
+                              bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
+        } else {
+            SkCanvas canvas(bitmap);
+            canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
+        }
+        return bitmap;
+    }
+}
+
+class ScopedUploadRequest {
+public:
+    ScopedUploadRequest() { beginUpload(); }
+    ~ScopedUploadRequest() { endUpload(); }
+};
+
+sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) {
+    ATRACE_CALL();
+
+    FormatInfo format = determineFormat(sourceBitmap);
+    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()) +
+                    "]");
+
+    status_t error = buffer->initCheck();
+    if (error < 0) {
+        ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
+        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());
+
+        eglDestroySyncKHR(display, fence);
+    }
+
+    return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
+}
+
+};  // namespace android::uirenderer
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
new file mode 100644
index 0000000..c0113d8
--- /dev/null
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hwui/Bitmap.h>
+
+namespace android::uirenderer {
+
+class HardwareBitmapUploader {
+public:
+    static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
+};
+
+};  // namespace android::uirenderer
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 89bcddc..31878ac 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -19,9 +19,9 @@
 #include <GpuMemoryTracker.h>
 #include <utils/RefBase.h>
 
+#include <SkBlendMode.h>
 #include <SkColorFilter.h>
 #include <SkColorSpace.h>
-#include <SkBlendMode.h>
 #include <SkPaint.h>
 
 #include "Matrix.h"
@@ -95,8 +95,7 @@
     void postDecStrong();
 
 protected:
-    Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, int alpha,
-          SkBlendMode mode);
+    Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, int alpha, SkBlendMode mode);
 
     RenderState& mRenderState;
 
diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h
index db9509f..082e95f 100644
--- a/libs/hwui/NinePatchUtils.h
+++ b/libs/hwui/NinePatchUtils.h
@@ -53,9 +53,8 @@
     return xRects * yRects;
 }
 
-static inline void SetLatticeFlags(SkCanvas::Lattice* lattice,
-        SkCanvas::Lattice::RectType* flags, int numFlags, const Res_png_9patch& chunk,
-        SkColor* colors) {
+static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::RectType* flags,
+                                   int numFlags, const Res_png_9patch& chunk, SkColor* colors) {
     lattice->fRectTypes = flags;
     lattice->fColors = colors;
     sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::RectType));
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 4ddacc8..f1c3803 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -42,9 +42,8 @@
         mBounds.set(left, top, right, bottom);
         mRadius = radius;
 
-
         // Reuse memory if previous outline was the same shape (rect or round rect).
-        if ( mPath.countVerbs() > 10) {
+        if (mPath.countVerbs() > 10) {
             mPath.reset();
         } else {
             mPath.rewind();
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index 47fcca9..ad599e9 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -210,9 +210,8 @@
     if (numberOfPointsExpected > 0) {
         result->failureMessage += "a multiple of ";
     }
-    result->failureMessage += std::to_string(numberOfPointsExpected)
-            + " floats. However, " + std::to_string(points)
-            + " float(s) are found. ";
+    result->failureMessage += std::to_string(numberOfPointsExpected) + " floats. However, " +
+                              std::to_string(points) + " float(s) are found. ";
 }
 
 void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
@@ -242,8 +241,8 @@
         validateVerbAndPoints(pathStr[start], points.size(), result);
         if (result->failureOccurred) {
             // If either verb or points is not valid, return immediately.
-            result->failureMessage += "Failure occurred at position " +
-                                     std::to_string(start) + " of path: " + pathStr;
+            result->failureMessage += "Failure occurred at position " + std::to_string(start) +
+                                      " of path: " + pathStr;
             return;
         }
         data->verbs.push_back(pathStr[start]);
@@ -257,8 +256,8 @@
         validateVerbAndPoints(pathStr[start], 0, result);
         if (result->failureOccurred) {
             // If either verb or points is not valid, return immediately.
-            result->failureMessage += "Failure occurred at position " +
-                                     std::to_string(start) + " of path: " + pathStr;
+            result->failureMessage += "Failure occurred at position " + std::to_string(start) +
+                                      " of path: " + pathStr;
             return;
         }
         data->verbs.push_back(pathStr[start]);
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index 1696661..70ca4e3 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -104,8 +104,8 @@
     dprintf(fd, "\nStats since: %" PRIu64 "ns", mStatStartTime);
     dprintf(fd, "\nTotal frames rendered: %u", mTotalFrameCount);
     dprintf(fd, "\nJanky frames: %u (%.2f%%)", mJankFrameCount,
-            mTotalFrameCount == 0 ? 0.0f :
-                (float)mJankFrameCount / (float)mTotalFrameCount * 100.0f);
+            mTotalFrameCount == 0 ? 0.0f
+                                  : (float)mJankFrameCount / (float)mTotalFrameCount * 100.0f);
     dprintf(fd, "\n50th percentile: %ums", findPercentile(50));
     dprintf(fd, "\n90th percentile: %ums", findPercentile(90));
     dprintf(fd, "\n95th percentile: %ums", findPercentile(95));
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 6470d4c..c024373 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -328,9 +328,7 @@
 
     bool isPivotExplicitlySet() const { return mPrimitiveFields.mPivotExplicitlySet; }
 
-    bool resetPivot() {
-        return RP_SET_AND_DIRTY(mPrimitiveFields.mPivotExplicitlySet, false);
-    }
+    bool resetPivot() { return RP_SET_AND_DIRTY(mPrimitiveFields.mPivotExplicitlySet, false); }
 
     bool setCameraDistance(float distance) {
         if (distance != getCameraDistance()) {
@@ -510,17 +508,13 @@
                getOutline().getAlpha() != 0.0f;
     }
 
-    SkColor getSpotShadowColor() const {
-        return mPrimitiveFields.mSpotShadowColor;
-    }
+    SkColor getSpotShadowColor() const { return mPrimitiveFields.mSpotShadowColor; }
 
     bool setSpotShadowColor(SkColor shadowColor) {
         return RP_SET(mPrimitiveFields.mSpotShadowColor, shadowColor);
     }
 
-    SkColor getAmbientShadowColor() const {
-        return mPrimitiveFields.mAmbientShadowColor;
-    }
+    SkColor getAmbientShadowColor() const { return mPrimitiveFields.mAmbientShadowColor; }
 
     bool setAmbientShadowColor(SkColor shadowColor) {
         return RP_SET(mPrimitiveFields.mAmbientShadowColor, shadowColor);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 6bd12f4..7b41f89 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -736,8 +736,8 @@
     SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
     // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
     // older.
-    if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0
-            && paintCopy.getStyle() == SkPaint::kStroke_Style) {
+    if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0 &&
+        paintCopy.getStyle() == SkPaint::kStroke_Style) {
         paintCopy.setStyle(SkPaint::kFill_Style);
     }
 
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 18358e2..402fbad 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -558,8 +558,8 @@
     SkRect src;
     sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
     if (vdSurface) {
-        canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src,
-                bounds, getPaint(), SkCanvas::kFast_SrcRectConstraint);
+        canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, getPaint(),
+                              SkCanvas::kFast_SrcRectConstraint);
     } else {
         // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
         // We render the VD into a temporary standalone buffer and mark the frame as dirty. Next
@@ -570,8 +570,8 @@
 
         int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
         int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
-        canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight),
-                bounds, getPaint(), SkCanvas::kFast_SrcRectConstraint);
+        canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
+                               getPaint(), SkCanvas::kFast_SrcRectConstraint);
         mCache.clear();
         markDirty();
     }
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index a92b62d..bf98fd6 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -102,9 +102,7 @@
     Snapshot decodeNextFrame();
     Snapshot reset();
 
-    size_t byteSize() const {
-        return sizeof(this) + mBytesUsed;
-    }
+    size_t byteSize() const { return sizeof(this) + mBytesUsed; }
 
 protected:
     virtual void onDraw(SkCanvas* canvas) override;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index e3b5b8c..962fea9 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -16,9 +16,8 @@
 #include "Bitmap.h"
 
 #include "Caches.h"
-#include "renderthread/EglManager.h"
+#include "HardwareBitmapUploader.h"
 #include "renderthread/RenderProxy.h"
-#include "renderthread/RenderThread.h"
 #include "utils/Color.h"
 
 #include <sys/mman.h>
@@ -85,7 +84,7 @@
 }
 
 sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
-    return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap);
+    return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
 }
 
 sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
@@ -206,7 +205,7 @@
     buffer->incStrong(buffer);
     setImmutable();  // HW bitmaps are always immutable
     mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast<AHardwareBuffer*>(buffer),
-            mInfo.alphaType(), mInfo.refColorSpace());
+                                              mInfo.alphaType(), mInfo.refColorSpace());
 }
 
 Bitmap::~Bitmap() {
@@ -286,9 +285,8 @@
 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
     outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
     if (isHardware()) {
-            outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
-                                                     info().colorType(), info().alphaType(),
-                                                     nullptr));
+        outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
+                                                 info().colorType(), info().alphaType(), nullptr));
         uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
         if (mInfo.colorSpace()) {
             sk_sp<SkPixelRef> pixelRef = sk_ref_sp(outBitmap->pixelRef());
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 4f06656..dbd4456 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -65,9 +65,7 @@
     Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
     Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
 
-    int rowBytesAsPixels() const {
-        return rowBytes() >> mInfo.shiftPerPixel();
-    }
+    int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
 
     void reconfigure(const SkImageInfo& info, size_t rowBytes);
     void reconfigure(const SkImageInfo& info);
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 4b8b832..fb6bd6f 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -158,9 +158,8 @@
     // minikin may modify the original paint
     Paint paint(origPaint);
 
-    minikin::Layout layout =
-            MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, textSize, start, count,
-                                   contextStart, contextCount, mt);
+    minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, textSize,
+                                                    start, count, contextStart, contextCount, mt);
 
     x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
 
@@ -207,11 +206,11 @@
                             const SkPath& path, float hOffset, float vOffset, const Paint& paint,
                             const Typeface* typeface) {
     Paint paintCopy(paint);
-    minikin::Layout layout = MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface,
-            text, count,  // text buffer
-            0, count,  // draw range
-            0, count,  // context range
-            nullptr);
+    minikin::Layout layout =
+            MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, count,  // text buffer
+                                   0, count,                                      // draw range
+                                   0, count,                                      // context range
+                                   nullptr);
     hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
 
     // Set align to left for drawing, as we don't want individual
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 3ccb8f3..a52263c 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -135,9 +135,8 @@
     SkPaint::Hinting hinting = paint->getHinting();
     // select only flags that might affect text layout
     flags &= (SkPaint::kAntiAlias_Flag | SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
-              SkPaint::kSubpixelText_Flag |
-              SkPaint::kEmbeddedBitmapText_Flag | SkPaint::kAutoHinting_Flag |
-              SkPaint::kVerticalText_Flag);
+              SkPaint::kSubpixelText_Flag | SkPaint::kEmbeddedBitmapText_Flag |
+              SkPaint::kAutoHinting_Flag | SkPaint::kVerticalText_Flag);
     flags |= (hinting << 16);
     return flags;
 }
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index 60e0e7e..d27d544 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -25,11 +25,11 @@
 #define _ANDROID_GRAPHICS_MINIKIN_UTILS_H_
 
 #include <cutils/compiler.h>
+#include <log/log.h>
 #include <minikin/Layout.h>
 #include "MinikinSkia.h"
 #include "Paint.h"
 #include "Typeface.h"
-#include <log/log.h>
 
 namespace minikin {
 class MeasuredText;
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index dca9ef5..d985633 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -132,8 +132,10 @@
         bool italicFromFont;
 
         const minikin::FontStyle defaultStyle;
-        const minikin::MinikinFont* mf = families.empty() ? nullptr
-                : families[0]->getClosestMatch(defaultStyle).font->typeface().get();
+        const minikin::MinikinFont* mf =
+                families.empty()
+                        ? nullptr
+                        : families[0]->getClosestMatch(defaultStyle).font->typeface().get();
         if (mf != nullptr) {
             SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface();
             const SkFontStyle& style = skTypeface->fontStyle();
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 766fab6..78f5a71 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -251,210 +251,6 @@
     }
 }
 
-#define FENCE_TIMEOUT 2000000000
-
-class AutoEglImage {
-public:
-    AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
-        EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
-        image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
-                                  imageAttrs);
-    }
-
-    ~AutoEglImage() {
-        if (image != EGL_NO_IMAGE_KHR) {
-            eglDestroyImageKHR(mDisplay, image);
-        }
-    }
-
-    EGLImageKHR image = EGL_NO_IMAGE_KHR;
-
-private:
-    EGLDisplay mDisplay = EGL_NO_DISPLAY;
-};
-
-class AutoSkiaGlTexture {
-public:
-    AutoSkiaGlTexture() {
-        glGenTextures(1, &mTexture);
-        glBindTexture(GL_TEXTURE_2D, mTexture);
-    }
-
-    ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
-
-private:
-    GLuint mTexture = 0;
-};
-
-struct FormatInfo {
-    PixelFormat pixelFormat;
-    GLint format, type;
-    bool isSupported = false;
-    bool valid = true;
-};
-
-static bool gpuSupportsHalfFloatTextures(renderthread::RenderThread& renderThread) {
-    static bool isSupported = renderThread.queue().runSync([&renderThread]() -> bool {
-        renderThread.requireGlContext();
-        sk_sp<GrContext> grContext = sk_ref_sp(renderThread.getGrContext());
-        if (!grContext->colorTypeSupportedAsImage(kRGBA_F16_SkColorType)) {
-            return false;
-        }
-        sp<GraphicBuffer> buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_FP16,
-                                                     GraphicBuffer::USAGE_HW_TEXTURE |
-                                                             GraphicBuffer::USAGE_SW_WRITE_NEVER |
-                                                             GraphicBuffer::USAGE_SW_READ_NEVER,
-                                                     "tempFp16Buffer");
-        status_t error = buffer->initCheck();
-        return error != OK;
-    });
-    return isSupported;
-}
-
-static FormatInfo determineFormat(renderthread::RenderThread& renderThread,
-                                  const SkBitmap& skBitmap) {
-    FormatInfo formatInfo;
-    // TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
-    switch (skBitmap.info().colorType()) {
-        case kRGBA_8888_SkColorType:
-            formatInfo.isSupported = true;
-        // ARGB_4444 is upconverted to RGBA_8888
-        case kARGB_4444_SkColorType:
-            formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
-            formatInfo.format = GL_RGBA;
-            formatInfo.type = GL_UNSIGNED_BYTE;
-            break;
-        case kRGBA_F16_SkColorType:
-            formatInfo.isSupported = gpuSupportsHalfFloatTextures(renderThread);
-            if (formatInfo.isSupported) {
-                formatInfo.type = GL_HALF_FLOAT;
-                formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
-            } else {
-                formatInfo.type = GL_UNSIGNED_BYTE;
-                formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
-            }
-            formatInfo.format = GL_RGBA;
-            break;
-        case kRGB_565_SkColorType:
-            formatInfo.isSupported = true;
-            formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
-            formatInfo.format = GL_RGB;
-            formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
-            break;
-        case kGray_8_SkColorType:
-            formatInfo.isSupported = true;
-            formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
-            formatInfo.format = GL_LUMINANCE;
-            formatInfo.type = GL_UNSIGNED_BYTE;
-            break;
-        default:
-            ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
-            formatInfo.valid = false;
-    }
-    return formatInfo;
-}
-
-static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
-    if (format.isSupported) {
-        return source;
-    } else {
-        SkBitmap bitmap;
-        const SkImageInfo& info = source.info();
-        bitmap.allocPixels(
-                SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), nullptr));
-        bitmap.eraseColor(0);
-        if (info.colorType() == kRGBA_F16_SkColorType) {
-            // Drawing RGBA_F16 onto ARGB_8888 is not supported
-            source.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
-                              bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
-        } else {
-            SkCanvas canvas(bitmap);
-            canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
-        }
-        return bitmap;
-    }
-}
-
-sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& thread,
-                                                         const SkBitmap& sourceBitmap) {
-    ATRACE_CALL();
-
-    LOG_ALWAYS_FATAL_IF(thread.isCurrent(), "Must not be called on RenderThread");
-
-    FormatInfo format = determineFormat(thread, sourceBitmap);
-    if (!format.valid) {
-        return nullptr;
-    }
-
-    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()) +
-                    "]");
-
-    status_t error = buffer->initCheck();
-    if (error < 0) {
-        ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
-        return nullptr;
-    }
-
-    EGLDisplay display = thread.queue().runSync([&]() -> EGLDisplay {
-        thread.requireGlContext();
-        return eglGetCurrentDisplay();
-    });
-
-    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 = thread.queue().runSync([&]() -> EGLSyncKHR {
-            thread.requireGlContext();
-            sk_sp<GrContext> grContext = sk_ref_sp(thread.getGrContext());
-            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();
-            grContext->resetContext(kTextureBinding_GrGLBackendState);
-            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 sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
-}
-
 } /* namespace skiapipeline */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 04b68f5..2e2e152 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -50,10 +50,6 @@
 
     static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
 
-    // May be called by any thread except RenderThread.
-    static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
-                                                const SkBitmap& skBitmap);
-
 private:
     renderthread::EglManager& mEglManager;
     EGLSurface mEglSurface = EGL_NO_SURFACE;
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index ca6d1b8..8e8bb8b 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -64,6 +64,8 @@
 
     void fence();
 
+    EGLDisplay eglDisplay() const { return mEglDisplay; }
+
 private:
 
     void initExtensions();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 4d1e1e8..02d0b6d 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -319,17 +319,6 @@
     }
 }
 
-sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
-    auto& thread = RenderThread::getInstance();
-    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
-        return skiapipeline::SkiaOpenGLPipeline::allocateHardwareBitmap(thread, bitmap);
-    } else {
-        return thread.queue().runSync([&]() -> auto {
-            return thread.allocateHardwareBitmap(bitmap);
-        });
-    }
-}
-
 int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) {
     RenderThread& thread = RenderThread::getInstance();
     if (gettid() == thread.getTid()) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 433adeb..9e46161 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -123,8 +123,6 @@
                                            int bottom, SkBitmap* bitmap);
     ANDROID_API static void prepareToDraw(Bitmap& bitmap);
 
-    static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
-
     static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap);
 
     static void onBitmapDestroyed(uint32_t pixelRefId);
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h
index 8068121..f9de8a5 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/thread/ThreadBase.h
@@ -47,6 +47,8 @@
 
     void join() { Thread::join(); }
 
+    bool isRunning() const { return Thread::isRunning(); }
+
 protected:
     void waitForWork() {
         nsecs_t nextWakeup;