Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
Chris Blume | 5f1ac2b | 2018-11-05 16:10:39 -0800 | [diff] [blame] | 17 | #include "VkInteropFunctorDrawable.h" |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 18 | #include <private/hwui/DrawGlInfo.h> |
| 19 | |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 20 | #include <utils/Color.h> |
| 21 | #include <utils/Trace.h> |
| 22 | #include <utils/TraceUtils.h> |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 23 | #include <thread> |
| 24 | #include "renderthread/EglManager.h" |
| 25 | #include "thread/ThreadBase.h" |
| 26 | #include "utils/TimeUtils.h" |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 27 | |
| 28 | #include <EGL/eglext.h> |
| 29 | #include <GLES2/gl2.h> |
| 30 | #include <GLES2/gl2ext.h> |
| 31 | #include <GLES3/gl3.h> |
| 32 | |
| 33 | #include <utils/GLUtils.h> |
| 34 | |
| 35 | namespace android { |
| 36 | namespace uirenderer { |
| 37 | namespace skiapipeline { |
| 38 | |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 39 | static renderthread::EglManager sEglManager; |
| 40 | |
| 41 | // ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it. |
| 42 | class ScopedDrawRequest { |
| 43 | public: |
| 44 | ScopedDrawRequest() { beginDraw(); } |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 45 | |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 46 | private: |
| 47 | void beginDraw() { |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 48 | if (!sEglManager.hasEglContext()) { |
Bo Liu | 30eef07 | 2019-01-09 11:43:16 -0800 | [diff] [blame] | 49 | sEglManager.initialize(); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 50 | } |
| 51 | } |
| 52 | }; |
| 53 | |
Chris Blume | 5f1ac2b | 2018-11-05 16:10:39 -0800 | [diff] [blame] | 54 | void VkInteropFunctorDrawable::vkInvokeFunctor(Functor* functor) { |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 55 | ScopedDrawRequest _drawRequest{}; |
Bo Liu | 30eef07 | 2019-01-09 11:43:16 -0800 | [diff] [blame] | 56 | EGLDisplay display = sEglManager.eglDisplay(); |
| 57 | DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; |
| 58 | if (display != EGL_NO_DISPLAY) { |
| 59 | mode = DrawGlInfo::kModeProcess; |
| 60 | } |
| 61 | (*functor)(mode, nullptr); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 62 | } |
| 63 | |
| 64 | #define FENCE_TIMEOUT 2000000000 |
| 65 | |
Chris Blume | 5f1ac2b | 2018-11-05 16:10:39 -0800 | [diff] [blame] | 66 | void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 67 | ATRACE_CALL(); |
| 68 | |
| 69 | if (canvas->getGrContext() == nullptr) { |
Chris Blume | d699343 | 2018-11-06 11:47:03 -0800 | [diff] [blame] | 70 | SkDEBUGF(("Attempting to draw VkInteropFunctor into an unsupported surface")); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 71 | return; |
| 72 | } |
| 73 | |
| 74 | ScopedDrawRequest _drawRequest{}; |
| 75 | |
| 76 | SkImageInfo surfaceInfo = canvas->imageInfo(); |
| 77 | |
| 78 | if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) { |
| 79 | // Buffer will be used as an OpenGL ES render target. |
| 80 | mFrameBuffer = new GraphicBuffer( |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 81 | // TODO: try to reduce the size of the buffer: possibly by using clip bounds. |
| 82 | static_cast<uint32_t>(surfaceInfo.width()), |
| 83 | static_cast<uint32_t>(surfaceInfo.height()), |
| 84 | ColorTypeToPixelFormat(surfaceInfo.colorType()), |
| 85 | GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | |
| 86 | GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER, |
| 87 | std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) + |
| 88 | "]"); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 89 | status_t error = mFrameBuffer->initCheck(); |
| 90 | if (error < 0) { |
Chris Blume | d699343 | 2018-11-06 11:47:03 -0800 | [diff] [blame] | 91 | ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()"); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 92 | return; |
| 93 | } |
| 94 | |
| 95 | mFBInfo = surfaceInfo; |
| 96 | } |
| 97 | |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 98 | // TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan |
| 99 | // TODO: draw command has completed. |
| 100 | // TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See |
| 101 | // TODO: GrVkGpu::destroyResources() for example. |
Bo Liu | 30eef07 | 2019-01-09 11:43:16 -0800 | [diff] [blame] | 102 | { |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 103 | ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height()); |
| 104 | EGLDisplay display = sEglManager.eglDisplay(); |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 105 | LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", |
| 106 | uirenderer::renderthread::EglManager::eglErrorString()); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 107 | // We use an EGLImage to access the content of the GraphicBuffer |
| 108 | // The EGL image is later bound to a 2D texture |
| 109 | EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer(); |
| 110 | AutoEglImage autoImage(display, clientBuffer); |
| 111 | if (autoImage.image == EGL_NO_IMAGE_KHR) { |
| 112 | ALOGW("Could not create EGL image, err =%s", |
| 113 | uirenderer::renderthread::EglManager::eglErrorString()); |
Bo Liu | 30eef07 | 2019-01-09 11:43:16 -0800 | [diff] [blame] | 114 | return; |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | AutoSkiaGlTexture glTexture; |
| 118 | glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image); |
| 119 | GL_CHECKPOINT(MODERATE); |
| 120 | |
| 121 | glBindTexture(GL_TEXTURE_2D, 0); |
| 122 | |
| 123 | DrawGlInfo info; |
| 124 | SkMatrix44 mat4(canvas->getTotalMatrix()); |
| 125 | SkIRect clipBounds = canvas->getDeviceClipBounds(); |
| 126 | |
| 127 | info.clipLeft = clipBounds.fLeft; |
| 128 | info.clipTop = clipBounds.fTop; |
| 129 | info.clipRight = clipBounds.fRight; |
| 130 | info.clipBottom = clipBounds.fBottom; |
| 131 | info.isLayer = true; |
| 132 | info.width = mFBInfo.width(); |
| 133 | info.height = mFBInfo.height(); |
| 134 | mat4.asColMajorf(&info.transform[0]); |
Bo Liu | b6da7f6 | 2019-01-23 20:59:00 -0800 | [diff] [blame] | 135 | info.color_space_ptr = canvas->imageInfo().colorSpace(); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 136 | |
| 137 | glViewport(0, 0, info.width, info.height); |
| 138 | |
| 139 | AutoGLFramebuffer glFb; |
| 140 | // Bind texture to the frame buffer. |
| 141 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 142 | glTexture.mTexture, 0); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 143 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { |
| 144 | ALOGE("Failed framebuffer check for created target buffer: %s", |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 145 | GLUtils::getGLFramebufferError()); |
Bo Liu | 30eef07 | 2019-01-09 11:43:16 -0800 | [diff] [blame] | 146 | return; |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 147 | } |
| 148 | |
| 149 | glDisable(GL_STENCIL_TEST); |
| 150 | glDisable(GL_SCISSOR_TEST); |
| 151 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| 152 | glClear(GL_COLOR_BUFFER_BIT); |
| 153 | |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 154 | if (mAnyFunctor.index() == 0) { |
| 155 | std::get<0>(mAnyFunctor).handle->drawGl(info); |
| 156 | } else { |
| 157 | (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info); |
| 158 | } |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 159 | |
| 160 | EGLSyncKHR glDrawFinishedFence = |
| 161 | eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL); |
| 162 | LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR, |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 163 | "Could not create sync fence %#x", eglGetError()); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 164 | glFlush(); |
| 165 | // TODO: export EGLSyncKHR in file descr |
| 166 | // TODO: import file desc in Vulkan Semaphore |
| 167 | // TODO: instead block the GPU: probably by using external Vulkan semaphore. |
| 168 | // Block the CPU until the glFlush finish. |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 169 | EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0, FENCE_TIMEOUT); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 170 | LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, |
| 171 | "Failed to wait for the fence %#x", eglGetError()); |
| 172 | eglDestroySyncKHR(display, glDrawFinishedFence); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | SkPaint paint; |
| 176 | paint.setBlendMode(SkBlendMode::kSrcOver); |
| 177 | canvas->save(); |
| 178 | // The size of the image matches the size of the canvas. We've used the matrix already, while |
| 179 | // drawing into the offscreen surface, so we need to reset it here. |
| 180 | canvas->resetMatrix(); |
| 181 | |
| 182 | auto functorImage = SkImage::MakeFromAHardwareBuffer( |
Bo Liu | b6da7f6 | 2019-01-23 20:59:00 -0800 | [diff] [blame] | 183 | reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType, |
| 184 | canvas->imageInfo().refColorSpace(), kBottomLeft_GrSurfaceOrigin); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 185 | canvas->drawImage(functorImage, 0, 0, &paint); |
| 186 | canvas->restore(); |
| 187 | } |
| 188 | |
Chris Blume | 5f1ac2b | 2018-11-05 16:10:39 -0800 | [diff] [blame] | 189 | VkInteropFunctorDrawable::~VkInteropFunctorDrawable() { |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 190 | if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) { |
| 191 | if (lp->listener) { |
| 192 | ScopedDrawRequest _drawRequest{}; |
Bo Liu | 30eef07 | 2019-01-09 11:43:16 -0800 | [diff] [blame] | 193 | lp->listener->onGlFunctorReleased(lp->functor); |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 194 | } |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 195 | } |
| 196 | } |
| 197 | |
John Reck | 283bb46 | 2018-12-13 16:40:14 -0800 | [diff] [blame] | 198 | void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const { |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 199 | ScopedDrawRequest _drawRequest{}; |
Bo Liu | 30eef07 | 2019-01-09 11:43:16 -0800 | [diff] [blame] | 200 | FunctorDrawable::syncFunctor(data); |
Stan Iliev | 11606ff | 2018-09-17 14:01:16 -0400 | [diff] [blame] | 201 | } |
| 202 | |
Chris Blume | 7b8a808 | 2018-11-30 15:51:58 -0800 | [diff] [blame] | 203 | } // namespace skiapipeline |
| 204 | } // namespace uirenderer |
| 205 | } // namespace android |