Implement WebView support for Vulkan using temporary buffer

Draw WebView in an offscreen GL buffer, then import and draw the
buffer with Vulkan.

Bug: 115610873
Test: Passed WebView CTS tests that are part of UiRendering.
Change-Id: Ida137fe9b8652d2a936ec2798b909be7e77b3462
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index b011363..11dad2e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -175,6 +175,7 @@
         "pipeline/skia/SkiaRecordingCanvas.cpp",
         "pipeline/skia/SkiaVulkanPipeline.cpp",
         "pipeline/skia/VectorDrawableAtlas.cpp",
+        "pipeline/skia/VkFunctorDrawable.cpp",
         "renderstate/RenderState.cpp",
         "renderthread/CacheManager.cpp",
         "renderthread/CanvasContext.cpp",
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index ab80d3d..7dbef65 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -107,39 +107,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;
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index dcfe6b3..e4ba13d 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -138,7 +138,7 @@
             renderNodeDrawable->getRenderNode()->output(mOutput, mLevel + 1);
             return;
         }
-        auto glFunctorDrawable = getGLFunctorDrawable(drawable);
+        auto glFunctorDrawable = getFunctorDrawable(drawable);
         if (nullptr != glFunctorDrawable) {
             mOutput << std::string(mLevel * 2, ' ') << "drawGLFunctorDrawable" << std::endl;
             return;
@@ -157,10 +157,10 @@
         return nullptr;
     }
 
-    GLFunctorDrawable* getGLFunctorDrawable(SkDrawable* drawable) {
+    FunctorDrawable* getFunctorDrawable(SkDrawable* drawable) {
         for (auto& child : mDisplayList.mChildFunctors) {
-            if (drawable == &child) {
-                return &child;
+            if (drawable == child) {
+                return child;
             }
         }
         return nullptr;
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
new file mode 100644
index 0000000..162d137
--- /dev/null
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -0,0 +1,53 @@
+/*
+ * 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 "GlFunctorLifecycleListener.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+#include <utils/Functor.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class FunctorDrawable : public SkDrawable {
+public:
+    FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+            : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+    virtual ~FunctorDrawable() {}
+
+    virtual void syncFunctor() const = 0;
+
+protected:
+    virtual SkRect onGetBounds() override { return mBounds; }
+
+    Functor* mFunctor;
+    sp<GlFunctorLifecycleListener> mListener;
+    const SkRect mBounds;
+};
+
+};  // namespace skiapipeline
+};  // namespace uirenderer
+};  // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index b0fec7a..90d5e71 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -18,7 +18,6 @@
 #include <GrContext.h>
 #include <private/hwui/DrawGlInfo.h>
 #include "GlFunctorLifecycleListener.h"
-#include "Properties.h"
 #include "RenderNode.h"
 #include "SkAndroidFrameworkUtils.h"
 #include "SkClipStack.h"
@@ -80,11 +79,6 @@
         return;
     }
 
-    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        canvas->clear(SK_ColorRED);
-        return;
-    }
-
     GLuint fboID = 0;
     SkISize fboSize;
     if (!GetFboDetails(canvas, &fboID, &fboSize)) {
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index d9e65c9..dd6ef25 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -16,12 +16,8 @@
 
 #pragma once
 
-#include "GlFunctorLifecycleListener.h"
+#include "FunctorDrawable.h"
 
-#include <SkCanvas.h>
-#include <SkDrawable.h>
-
-#include <utils/Functor.h>
 #include <utils/RefBase.h>
 
 namespace android {
@@ -33,22 +29,16 @@
  * This drawable wraps a OpenGL functor enabling it to be recorded into a list
  * of Skia drawing commands.
  */
-class GLFunctorDrawable : public SkDrawable {
+class GLFunctorDrawable : public FunctorDrawable {
 public:
     GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
-            : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+            : FunctorDrawable(functor, listener, canvas) {}
     virtual ~GLFunctorDrawable();
 
-    void syncFunctor() const;
+    void syncFunctor() const override;
 
 protected:
-    virtual SkRect onGetBounds() override { return mBounds; }
     virtual void onDraw(SkCanvas* canvas) override;
-
-private:
-    Functor* mFunctor;
-    sp<GlFunctorLifecycleListener> mListener;
-    const SkRect mBounds;
 };
 
 };  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 82179a3..78b64b2 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -29,7 +29,7 @@
 
 void SkiaDisplayList::syncContents() {
     for (auto& functor : mChildFunctors) {
-        functor.syncFunctor();
+        functor->syncFunctor();
     }
     for (auto& animatedImage : mAnimatedImages) {
         animatedImage->syncProperties();
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 4f30f98..4c78539 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -17,7 +17,7 @@
 #pragma once
 
 #include "hwui/AnimatedImageDrawable.h"
-#include "GLFunctorDrawable.h"
+#include "FunctorDrawable.h"
 #include "RecordingCanvas.h"
 #include "RenderNodeDrawable.h"
 #include "TreeInfo.h"
@@ -77,7 +77,7 @@
      * that creates them. Allocator dtor invokes all SkDrawable dtors.
      */
     template <class T, typename... Params>
-    SkDrawable* allocateDrawable(Params&&... params) {
+    T* allocateDrawable(Params&&... params) {
         return allocator.create<T>(std::forward<Params>(params)...);
     }
 
@@ -155,7 +155,7 @@
      * cannot relocate.
      */
     std::deque<RenderNodeDrawable> mChildNodes;
-    std::deque<GLFunctorDrawable> mChildFunctors;
+    std::deque<FunctorDrawable*> mChildFunctors;
     std::vector<SkImage*> mMutableImages;
     std::vector<VectorDrawableRoot*> mVectorDrawables;
     std::vector<AnimatedImageDrawable*> mAnimatedImages;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 83d7e6a..3c281e78 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -23,6 +23,8 @@
 #include "NinePatchUtils.h"
 #include "RenderNode.h"
 #include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
+#include "pipeline/skia/VkFunctorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -118,9 +120,16 @@
 
 void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
                                              uirenderer::GlFunctorLifecycleListener* listener) {
-    // Drawable dtor will be invoked when mChildFunctors deque is cleared.
-    mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
-    drawDrawable(&mDisplayList->mChildFunctors.back());
+    FunctorDrawable* functorDrawable;
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, listener,
+                asSkCanvas());
+    } else {
+        functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
+                asSkCanvas());
+    }
+    mDisplayList->mChildFunctors.push_back(functorDrawable);
+    drawDrawable(functorDrawable);
 }
 
 class VectorDrawable : public SkDrawable {
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 5cbe33d..e34f160 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -22,6 +22,7 @@
 #include "SkiaProfileRenderer.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/Frame.h"
+#include "VkFunctorDrawable.h"
 
 #include <SkSurface.h>
 #include <SkTypes.h>
@@ -146,9 +147,7 @@
 }
 
 void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
-    // TODO: we currently don't support OpenGL WebView's
-    DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
-    (*functor)(mode, nullptr);
+    VkFunctorDrawable::vkInvokeFunctor(functor);
 }
 
 sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
new file mode 100644
index 0000000..6486ddb
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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 "VkFunctorDrawable.h"
+#include <private/hwui/DrawGlInfo.h>
+
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+#include <thread>
+#include <utils/Color.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include <utils/GLUtils.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+static std::mutex sLock{};
+static ThreadBase* sGLDrawThread = nullptr;
+static renderthread::EglManager sEglManager;
+
+// ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it.
+class ScopedDrawRequest {
+public:
+    ScopedDrawRequest() { beginDraw(); }
+private:
+    void beginDraw() {
+        std::lock_guard{sLock};
+
+        if (!sGLDrawThread) {
+            sGLDrawThread = new ThreadBase{};
+        }
+
+        if (!sGLDrawThread->isRunning()) {
+            sGLDrawThread->start("GLFunctorThread");
+        }
+
+        if (!sEglManager.hasEglContext()) {
+            sGLDrawThread->queue().runSync([]() {
+                sEglManager.initialize();
+            });
+        }
+    }
+};
+
+void VkFunctorDrawable::vkInvokeFunctor(Functor* functor) {
+    ScopedDrawRequest _drawRequest{};
+    sGLDrawThread->queue().runSync([&]() {
+        EGLDisplay display = sEglManager.eglDisplay();
+        DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+        if (display != EGL_NO_DISPLAY) {
+            mode = DrawGlInfo::kModeProcess;
+        }
+        (*functor)(mode, nullptr);
+    });
+}
+
+#define FENCE_TIMEOUT 2000000000
+
+void VkFunctorDrawable::onDraw(SkCanvas* canvas) {
+    ATRACE_CALL();
+
+    if (canvas->getGrContext() == nullptr) {
+        SkDEBUGF(("Attempting to draw VkFunctor into an unsupported surface"));
+        return;
+    }
+
+    ScopedDrawRequest _drawRequest{};
+
+    SkImageInfo surfaceInfo = canvas->imageInfo();
+
+    if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
+        // Buffer will be used as an OpenGL ES render target.
+        mFrameBuffer = new GraphicBuffer(
+                 //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+                 static_cast<uint32_t>(surfaceInfo.width()),
+                 static_cast<uint32_t>(surfaceInfo.height()),
+                 ColorTypeToPixelFormat(surfaceInfo.colorType()),
+                 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+                         GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+                 std::string("VkFunctorDrawable::onDraw pid [") + std::to_string(getpid()) + "]");
+        status_t error = mFrameBuffer->initCheck();
+        if (error < 0) {
+            ALOGW("VkFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
+            return;
+        }
+
+        mFBInfo = surfaceInfo;
+    }
+
+    //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+    //TODO: draw command has completed.
+    //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
+    //TODO: GrVkGpu::destroyResources() for example.
+    bool success = sGLDrawThread->queue().runSync([&]() -> bool {
+        ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
+        EGLDisplay display = sEglManager.eglDisplay();
+        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)mFrameBuffer->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;
+        }
+
+        AutoSkiaGlTexture glTexture;
+        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+        GL_CHECKPOINT(MODERATE);
+
+        glBindTexture(GL_TEXTURE_2D, 0);
+
+        DrawGlInfo info;
+        SkMatrix44 mat4(canvas->getTotalMatrix());
+        SkIRect clipBounds = canvas->getDeviceClipBounds();
+
+        info.clipLeft = clipBounds.fLeft;
+        info.clipTop = clipBounds.fTop;
+        info.clipRight = clipBounds.fRight;
+        info.clipBottom = clipBounds.fBottom;
+        info.isLayer = true;
+        info.width = mFBInfo.width();
+        info.height = mFBInfo.height();
+        mat4.asColMajorf(&info.transform[0]);
+
+        glViewport(0, 0, info.width, info.height);
+
+        AutoGLFramebuffer glFb;
+        // Bind texture to the frame buffer.
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                             glTexture.mTexture, 0);
+        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+            ALOGE("Failed framebuffer check for created target buffer: %s",
+                    GLUtils::getGLFramebufferError());
+            return false;
+        }
+
+        glDisable(GL_STENCIL_TEST);
+        glDisable(GL_SCISSOR_TEST);
+        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+        EGLSyncKHR glDrawFinishedFence =
+                eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+        LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
+                "Could not create sync fence %#x", eglGetError());
+        glFlush();
+        // TODO: export EGLSyncKHR in file descr
+        // TODO: import file desc in Vulkan Semaphore
+        // TODO: instead block the GPU: probably by using external Vulkan semaphore.
+        // Block the CPU until the glFlush finish.
+        EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
+                FENCE_TIMEOUT);
+        LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+                            "Failed to wait for the fence %#x", eglGetError());
+        eglDestroySyncKHR(display, glDrawFinishedFence);
+        return true;
+    });
+
+    if (!success) {
+        return;
+    }
+
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrcOver);
+    canvas->save();
+    // The size of the image matches the size of the canvas. We've used the matrix already, while
+    // drawing into the offscreen surface, so we need to reset it here.
+    canvas->resetMatrix();
+
+    auto functorImage = SkImage::MakeFromAHardwareBuffer(
+        reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
+        nullptr, kBottomLeft_GrSurfaceOrigin);
+    canvas->drawImage(functorImage, 0, 0, &paint);
+    canvas->restore();
+}
+
+VkFunctorDrawable::~VkFunctorDrawable() {
+    if (mListener.get() != nullptr) {
+        ScopedDrawRequest _drawRequest{};
+        sGLDrawThread->queue().runSync([&]() {
+             mListener->onGlFunctorReleased(mFunctor);
+        });
+    }
+}
+
+void VkFunctorDrawable::syncFunctor() const {
+    ScopedDrawRequest _drawRequest{};
+    sGLDrawThread->queue().runSync([&]() {
+         (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+    });
+}
+
+};  // namespace skiapipeline
+};  // namespace uirenderer
+};  // namespace android
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
new file mode 100644
index 0000000..e37f6fb
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -0,0 +1,55 @@
+/*
+ * 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 "FunctorDrawable.h"
+
+#include <utils/RefBase.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a Vulkan functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class VkFunctorDrawable : public FunctorDrawable {
+public:
+    VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+            : FunctorDrawable(functor, listener, canvas) {}
+    virtual ~VkFunctorDrawable();
+
+    void syncFunctor() const override;
+
+    static void vkInvokeFunctor(Functor* functor);
+
+protected:
+    virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+
+    // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
+    sp<GraphicBuffer> mFrameBuffer;
+    SkImageInfo mFBInfo;
+};
+
+};  // namespace skiapipeline
+};  // namespace uirenderer
+};  // namespace android
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 6c398ee..415f9e8 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -20,6 +20,7 @@
 #include "AnimationContext.h"
 #include "DamageAccumulator.h"
 #include "IContextFactory.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "renderthread/CanvasContext.h"
 #include "tests/common/TestUtils.h"
@@ -46,7 +47,8 @@
     SkCanvas dummyCanvas;
     RenderNodeDrawable drawable(nullptr, &dummyCanvas);
     skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
-    skiaDL->mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
+    GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
+    skiaDL->mChildFunctors.push_back(&functorDrawable);
     skiaDL->mMutableImages.push_back(nullptr);
     skiaDL->mVectorDrawables.push_back(nullptr);
     skiaDL->mProjectionReceiver = &drawable;
@@ -95,7 +97,8 @@
 
     SkCanvas dummyCanvas;
     TestUtils::MockFunctor functor;
-    skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
+    GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
+    skiaDL.mChildFunctors.push_back(&functorDrawable);
 
     SkRect bounds = SkRect::MakeWH(200, 200);
     VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index d35fe4f..9f71e91 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -72,6 +72,26 @@
     }
 }
 
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
+    switch (colorType) {
+        case kRGBA_8888_SkColorType:
+            return PIXEL_FORMAT_RGBA_8888;
+        case kRGBA_F16_SkColorType:
+            return PIXEL_FORMAT_RGBA_FP16;
+        case kRGB_565_SkColorType:
+            return PIXEL_FORMAT_RGB_565;
+        case kRGB_888x_SkColorType:
+            return PIXEL_FORMAT_RGBX_8888;
+        case kRGBA_1010102_SkColorType:
+            return PIXEL_FORMAT_RGBA_1010102;
+        case kARGB_4444_SkColorType:
+            return PIXEL_FORMAT_RGBA_4444;
+        default:
+            ALOGW("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
+            return PIXEL_FORMAT_RGBA_8888;
+    }
+}
+
 SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace) {
     switch (dataSpace & HAL_DATASPACE_STANDARD_MASK) {
         case HAL_DATASPACE_STANDARD_BT709:
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index ff0e755..e935a0d 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -18,6 +18,7 @@
 
 #include <math.h>
 #include <system/graphics.h>
+#include <ui/PixelFormat.h>
 
 #include <SkColor.h>
 #include <SkColorSpace.h>
@@ -116,6 +117,8 @@
 
 SkColorType PixelFormatToColorType(android_pixel_format pixelFormat);
 
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
+
 SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace);
 
 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index bf27300..fcd036c 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -59,5 +59,22 @@
 #endif
 }
 
+const char* GLUtils::getGLFramebufferError() {
+    switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
+        case GL_FRAMEBUFFER_COMPLETE:
+            return "GL_FRAMEBUFFER_COMPLETE";
+        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+            return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+            return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+        case GL_FRAMEBUFFER_UNSUPPORTED:
+            return "GL_FRAMEBUFFER_UNSUPPORTED";
+        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+            return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+        default:
+            return "Unknown error";
+    }
+}
+
 };  // namespace uirenderer
 };  // namespace android
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index debfb5d..ca8810b 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -20,6 +20,12 @@
 
 #include <log/log.h>
 
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -43,8 +49,53 @@
      */
     static bool dumpGLErrors();
 
+    static const char* getGLFramebufferError();
 };  // class GLUtils
 
+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); }
+
+    GLuint mTexture = 0;
+};
+
+class AutoGLFramebuffer {
+public:
+    AutoGLFramebuffer() {
+        glGenFramebuffers(1, &mFb);
+        glBindFramebuffer(GL_FRAMEBUFFER, mFb);
+    }
+
+    ~AutoGLFramebuffer() { glDeleteFramebuffers(1, &mFb); }
+
+    GLuint mFb;
+};
+
 } /* namespace uirenderer */
 } /* namespace android */