summaryrefslogtreecommitdiff
path: root/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
diff options
context:
space:
mode:
authorBill Peckham <bpeckham@google.com>2018-10-09 17:33:34 -0700
committerBill Peckham <bpeckham@google.com>2018-10-15 17:46:00 -0700
commitddcaa93e851eb5e57692799446f2ef3fe31436ae (patch)
tree41f5481541b8c4e26dd8fef5cbba7a24aa1003c7 /libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
parent760f366150e46580bfa808a897bc99c3e8907ded (diff)
parentef229d9195a2bdff34f94420687c0c05f4447a88 (diff)
Merge QP1A.181008.001
Change-Id: Iff68e8d0501ac5c2998c96f9df4042a94a1ce9e1
Diffstat (limited to 'libs/hwui/pipeline/skia/VkFunctorDrawable.cpp')
-rw-r--r--libs/hwui/pipeline/skia/VkFunctorDrawable.cpp223
1 files changed, 223 insertions, 0 deletions
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
new file mode 100644
index 000000000000..6486ddb05aac
--- /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