• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 #include "OpenGLPipeline.h"
18 
19 #include "DeferredLayerUpdater.h"
20 #include "EglManager.h"
21 #include "Frame.h"
22 #include "GlLayer.h"
23 #include "OpenGLReadback.h"
24 #include "ProfileRenderer.h"
25 #include "renderstate/RenderState.h"
26 #include "TreeInfo.h"
27 
28 #include <cutils/properties.h>
29 #include <strings.h>
30 
31 namespace android {
32 namespace uirenderer {
33 namespace renderthread {
34 
OpenGLPipeline(RenderThread & thread)35 OpenGLPipeline::OpenGLPipeline(RenderThread& thread)
36         : mEglManager(thread.eglManager()), mRenderThread(thread) {}
37 
makeCurrent()38 MakeCurrentResult OpenGLPipeline::makeCurrent() {
39     // TODO: Figure out why this workaround is needed, see b/13913604
40     // In the meantime this matches the behavior of GLRenderer, so it is not a regression
41     EGLint error = 0;
42     bool haveNewSurface = mEglManager.makeCurrent(mEglSurface, &error);
43 
44     Caches::getInstance().textureCache.resetMarkInUse(this);
45     if (!haveNewSurface) {
46         return MakeCurrentResult::AlreadyCurrent;
47     }
48     return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
49 }
50 
getFrame()51 Frame OpenGLPipeline::getFrame() {
52     LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
53                         "drawRenderNode called on a context with no surface!");
54     return mEglManager.beginFrame(mEglSurface);
55 }
56 
draw(const Frame & frame,const SkRect & screenDirty,const SkRect & dirty,const FrameBuilder::LightGeometry & lightGeometry,LayerUpdateQueue * layerUpdateQueue,const Rect & contentDrawBounds,bool opaque,bool wideColorGamut,const BakedOpRenderer::LightInfo & lightInfo,const std::vector<sp<RenderNode>> & renderNodes,FrameInfoVisualizer * profiler)57 bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
58                           const FrameBuilder::LightGeometry& lightGeometry,
59                           LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
60                           bool opaque, bool wideColorGamut,
61                           const BakedOpRenderer::LightInfo& lightInfo,
62                           const std::vector<sp<RenderNode>>& renderNodes,
63                           FrameInfoVisualizer* profiler) {
64     mEglManager.damageFrame(frame, dirty);
65 
66     bool drew = false;
67 
68     auto& caches = Caches::getInstance();
69     FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), lightGeometry, caches);
70 
71     frameBuilder.deferLayers(*layerUpdateQueue);
72     layerUpdateQueue->clear();
73 
74     frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds);
75 
76     BakedOpRenderer renderer(caches, mRenderThread.renderState(), opaque, wideColorGamut,
77                              lightInfo);
78     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
79     ProfileRenderer profileRenderer(renderer);
80     profiler->draw(profileRenderer);
81     drew = renderer.didDraw();
82 
83     // post frame cleanup
84     caches.clearGarbage();
85     caches.pathCache.trim();
86     caches.tessellationCache.trim();
87 
88 #if DEBUG_MEMORY_USAGE
89     caches.dumpMemoryUsage();
90 #else
91     if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
92         caches.dumpMemoryUsage();
93     }
94 #endif
95 
96     return drew;
97 }
98 
swapBuffers(const Frame & frame,bool drew,const SkRect & screenDirty,FrameInfo * currentFrameInfo,bool * requireSwap)99 bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
100                                  FrameInfo* currentFrameInfo, bool* requireSwap) {
101     GL_CHECKPOINT(LOW);
102 
103     // Even if we decided to cancel the frame, from the perspective of jank
104     // metrics the frame was swapped at this point
105     currentFrameInfo->markSwapBuffers();
106 
107     *requireSwap = drew || mEglManager.damageRequiresSwap();
108 
109     if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
110         return false;
111     }
112 
113     return *requireSwap;
114 }
115 
copyLayerInto(DeferredLayerUpdater * layer,SkBitmap * bitmap)116 bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
117     ATRACE_CALL();
118     // acquire most recent buffer for drawing
119     layer->updateTexImage();
120     layer->apply();
121     return OpenGLReadbackImpl::copyLayerInto(mRenderThread,
122                                              static_cast<GlLayer&>(*layer->backingLayer()), bitmap);
123 }
124 
createLayer(RenderState & renderState,uint32_t layerWidth,uint32_t layerHeight,sk_sp<SkColorFilter> colorFilter,int alpha,SkBlendMode mode,bool blend)125 static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
126                           sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
127                           bool blend) {
128     GlLayer* layer =
129             new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
130     Caches::getInstance().textureState().activateTexture(0);
131     layer->generateTexture();
132     return layer;
133 }
134 
createTextureLayer()135 DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() {
136     mEglManager.initialize();
137     return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL);
138 }
139 
onStop()140 void OpenGLPipeline::onStop() {
141     if (mEglManager.isCurrent(mEglSurface)) {
142         mEglManager.makeCurrent(EGL_NO_SURFACE);
143     }
144 }
145 
setSurface(Surface * surface,SwapBehavior swapBehavior,ColorMode colorMode)146 bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, ColorMode colorMode) {
147     if (mEglSurface != EGL_NO_SURFACE) {
148         mEglManager.destroySurface(mEglSurface);
149         mEglSurface = EGL_NO_SURFACE;
150     }
151 
152     if (surface) {
153         const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
154         mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
155     }
156 
157     if (mEglSurface != EGL_NO_SURFACE) {
158         const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
159         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
160         return true;
161     }
162 
163     return false;
164 }
165 
isSurfaceReady()166 bool OpenGLPipeline::isSurfaceReady() {
167     return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
168 }
169 
isContextReady()170 bool OpenGLPipeline::isContextReady() {
171     return CC_LIKELY(mEglManager.hasEglContext());
172 }
173 
onDestroyHardwareResources()174 void OpenGLPipeline::onDestroyHardwareResources() {
175     Caches& caches = Caches::getInstance();
176     // Make sure to release all the textures we were owning as there won't
177     // be another draw
178     caches.textureCache.resetMarkInUse(this);
179     mRenderThread.renderState().flush(Caches::FlushMode::Layers);
180 }
181 
renderLayers(const FrameBuilder::LightGeometry & lightGeometry,LayerUpdateQueue * layerUpdateQueue,bool opaque,bool wideColorGamut,const BakedOpRenderer::LightInfo & lightInfo)182 void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
183                                   LayerUpdateQueue* layerUpdateQueue, bool opaque,
184                                   bool wideColorGamut,
185                                   const BakedOpRenderer::LightInfo& lightInfo) {
186     static const std::vector<sp<RenderNode>> emptyNodeList;
187     auto& caches = Caches::getInstance();
188     FrameBuilder frameBuilder(*layerUpdateQueue, lightGeometry, caches);
189     layerUpdateQueue->clear();
190     // TODO: Handle wide color gamut contexts
191     BakedOpRenderer renderer(caches, mRenderThread.renderState(), opaque, wideColorGamut,
192                              lightInfo);
193     LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
194     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
195 }
196 
getTaskManager()197 TaskManager* OpenGLPipeline::getTaskManager() {
198     return &Caches::getInstance().tasks;
199 }
200 
layerMatchesWH(OffscreenBuffer * layer,int width,int height)201 static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
202     return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height;
203 }
204 
createOrUpdateLayer(RenderNode * node,const DamageAccumulator & damageAccumulator,bool wideColorGamut,ErrorHandler * errorHandler)205 bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
206                                          const DamageAccumulator& damageAccumulator,
207                                          bool wideColorGamut,
208                                          ErrorHandler* errorHandler) {
209     RenderState& renderState = mRenderThread.renderState();
210     OffscreenBufferPool& layerPool = renderState.layerPool();
211     bool transformUpdateNeeded = false;
212     if (node->getLayer() == nullptr) {
213         node->setLayer(
214                 layerPool.get(renderState, node->getWidth(), node->getHeight(), wideColorGamut));
215         transformUpdateNeeded = true;
216     } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) {
217         // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
218         // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
219         if (node->properties().fitsOnLayer()) {
220             node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight()));
221         } else {
222             destroyLayer(node);
223         }
224         transformUpdateNeeded = true;
225     }
226 
227     if (transformUpdateNeeded && node->getLayer()) {
228         // update the transform in window of the layer to reset its origin wrt light source position
229         Matrix4 windowTransform;
230         damageAccumulator.computeCurrentTransform(&windowTransform);
231         node->getLayer()->setWindowTransform(windowTransform);
232     }
233 
234     if (!node->hasLayer()) {
235         Caches::getInstance().dumpMemoryUsage();
236         if (errorHandler) {
237             std::ostringstream err;
238             err << "Unable to create layer for " << node->getName();
239             const int maxTextureSize = Caches::getInstance().maxTextureSize;
240             if (node->getWidth() > maxTextureSize || node->getHeight() > maxTextureSize) {
241                 err << ", size " << node->getWidth() << "x" << node->getHeight()
242                     << " exceeds max size " << maxTextureSize;
243             } else {
244                 err << ", see logcat for more info";
245             }
246             errorHandler->onError(err.str());
247         }
248     }
249 
250     return transformUpdateNeeded;
251 }
252 
pinImages(LsaVector<sk_sp<Bitmap>> & images)253 bool OpenGLPipeline::pinImages(LsaVector<sk_sp<Bitmap>>& images) {
254     TextureCache& cache = Caches::getInstance().textureCache;
255     bool prefetchSucceeded = true;
256     for (auto& bitmapResource : images) {
257         prefetchSucceeded &= cache.prefetchAndMarkInUse(this, bitmapResource.get());
258     }
259     return prefetchSucceeded;
260 }
261 
unpinImages()262 void OpenGLPipeline::unpinImages() {
263     Caches::getInstance().textureCache.resetMarkInUse(this);
264 }
265 
destroyLayer(RenderNode * node)266 void OpenGLPipeline::destroyLayer(RenderNode* node) {
267     if (OffscreenBuffer* layer = node->getLayer()) {
268         layer->renderState.layerPool().putOrDelete(layer);
269         node->setLayer(nullptr);
270     }
271 }
272 
prepareToDraw(const RenderThread & thread,Bitmap * bitmap)273 void OpenGLPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
274     if (Caches::hasInstance() && thread.eglManager().hasEglContext()) {
275         ATRACE_NAME("Bitmap#prepareToDraw task");
276         Caches::getInstance().textureCache.prefetch(bitmap);
277     }
278 }
279 
invokeFunctor(const RenderThread & thread,Functor * functor)280 void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
281     DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
282     if (thread.eglManager().hasEglContext()) {
283         mode = DrawGlInfo::kModeProcess;
284     }
285     thread.renderState().invokeFunctor(functor, mode, nullptr);
286 }
287 
288 #define FENCE_TIMEOUT 2000000000
289 
290 class AutoEglFence {
291 public:
AutoEglFence(EGLDisplay display)292     AutoEglFence(EGLDisplay display) : mDisplay(display) {
293         fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
294     }
295 
~AutoEglFence()296     ~AutoEglFence() {
297         if (fence != EGL_NO_SYNC_KHR) {
298             eglDestroySyncKHR(mDisplay, fence);
299         }
300     }
301 
302     EGLSyncKHR fence = EGL_NO_SYNC_KHR;
303 
304 private:
305     EGLDisplay mDisplay = EGL_NO_DISPLAY;
306 };
307 
308 class AutoEglImage {
309 public:
AutoEglImage(EGLDisplay display,EGLClientBuffer clientBuffer)310     AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
311         EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
312         image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
313                                   imageAttrs);
314     }
315 
~AutoEglImage()316     ~AutoEglImage() {
317         if (image != EGL_NO_IMAGE_KHR) {
318             eglDestroyImageKHR(mDisplay, image);
319         }
320     }
321 
322     EGLImageKHR image = EGL_NO_IMAGE_KHR;
323 
324 private:
325     EGLDisplay mDisplay = EGL_NO_DISPLAY;
326 };
327 
328 class AutoGlTexture {
329 public:
AutoGlTexture(uirenderer::Caches & caches)330     AutoGlTexture(uirenderer::Caches& caches) : mCaches(caches) {
331         glGenTextures(1, &mTexture);
332         caches.textureState().bindTexture(mTexture);
333     }
334 
~AutoGlTexture()335     ~AutoGlTexture() { mCaches.textureState().deleteTexture(mTexture); }
336 
337 private:
338     uirenderer::Caches& mCaches;
339     GLuint mTexture = 0;
340 };
341 
uploadBitmapToGraphicBuffer(uirenderer::Caches & caches,SkBitmap & bitmap,GraphicBuffer & buffer,GLint format,GLint type)342 static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap,
343                                         GraphicBuffer& buffer, GLint format, GLint type) {
344     EGLDisplay display = eglGetCurrentDisplay();
345     LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
346                         uirenderer::renderthread::EglManager::eglErrorString());
347     // We use an EGLImage to access the content of the GraphicBuffer
348     // The EGL image is later bound to a 2D texture
349     EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer.getNativeBuffer();
350     AutoEglImage autoImage(display, clientBuffer);
351     if (autoImage.image == EGL_NO_IMAGE_KHR) {
352         ALOGW("Could not create EGL image, err =%s",
353               uirenderer::renderthread::EglManager::eglErrorString());
354         return false;
355     }
356     AutoGlTexture glTexture(caches);
357     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
358 
359     GL_CHECKPOINT(MODERATE);
360 
361     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format, type,
362                     bitmap.getPixels());
363 
364     GL_CHECKPOINT(MODERATE);
365 
366     // The fence is used to wait for the texture upload to finish
367     // properly. We cannot rely on glFlush() and glFinish() as
368     // some drivers completely ignore these API calls
369     AutoEglFence autoFence(display);
370     if (autoFence.fence == EGL_NO_SYNC_KHR) {
371         LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError());
372         return false;
373     }
374     // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a
375     // pipeline flush (similar to what a glFlush() would do.)
376     EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence,
377                                              EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT);
378     if (waitStatus != EGL_CONDITION_SATISFIED_KHR) {
379         LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError());
380         return false;
381     }
382     return true;
383 }
384 
385 // TODO: handle SRGB sanely
internalFormatToPixelFormat(GLint internalFormat)386 static PixelFormat internalFormatToPixelFormat(GLint internalFormat) {
387     switch (internalFormat) {
388         case GL_LUMINANCE:
389             return PIXEL_FORMAT_RGBA_8888;
390         case GL_SRGB8_ALPHA8:
391             return PIXEL_FORMAT_RGBA_8888;
392         case GL_RGBA:
393             return PIXEL_FORMAT_RGBA_8888;
394         case GL_RGB:
395             return PIXEL_FORMAT_RGB_565;
396         case GL_RGBA16F:
397             return PIXEL_FORMAT_RGBA_FP16;
398         default:
399             LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat);
400             return PIXEL_FORMAT_UNKNOWN;
401     }
402 }
403 
allocateHardwareBitmap(RenderThread & renderThread,SkBitmap & skBitmap)404 sk_sp<Bitmap> OpenGLPipeline::allocateHardwareBitmap(RenderThread& renderThread,
405                                                      SkBitmap& skBitmap) {
406     renderThread.eglManager().initialize();
407     uirenderer::Caches& caches = uirenderer::Caches::getInstance();
408 
409     const SkImageInfo& info = skBitmap.info();
410     if (info.colorType() == kUnknown_SkColorType || info.colorType() == kAlpha_8_SkColorType) {
411         ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
412         return nullptr;
413     }
414 
415     bool needSRGB = uirenderer::transferFunctionCloseToSRGB(skBitmap.info().colorSpace());
416     bool hasLinearBlending = caches.extensions().hasLinearBlending();
417     GLint format, type, internalFormat;
418     uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
419                                                     needSRGB && hasLinearBlending, &internalFormat,
420                                                     &format, &type);
421 
422     PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
423     sp<GraphicBuffer> buffer = new GraphicBuffer(
424             info.width(), info.height(), pixelFormat,
425             GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
426                     GraphicBuffer::USAGE_SW_READ_NEVER,
427             std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + "]");
428 
429     status_t error = buffer->initCheck();
430     if (error < 0) {
431         ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
432         return nullptr;
433     }
434 
435     SkBitmap bitmap;
436     if (CC_UNLIKELY(
437                 uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(), hasLinearBlending))) {
438         sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
439         bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
440     } else {
441         bitmap = skBitmap;
442     }
443 
444     if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) {
445         return nullptr;
446     }
447     return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
448 }
449 
450 } /* namespace renderthread */
451 } /* namespace uirenderer */
452 } /* namespace android */
453