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 "ProfileRenderer.h"
24 #include "renderstate/RenderState.h"
25 #include "OpenGLReadback.h"
26
27 #include <cutils/properties.h>
28 #include <strings.h>
29
30 namespace android {
31 namespace uirenderer {
32 namespace renderthread {
33
OpenGLPipeline(RenderThread & thread)34 OpenGLPipeline::OpenGLPipeline(RenderThread& thread)
35 : mEglManager(thread.eglManager())
36 , mRenderThread(thread) {
37 }
38
makeCurrent()39 MakeCurrentResult OpenGLPipeline::makeCurrent() {
40 // TODO: Figure out why this workaround is needed, see b/13913604
41 // In the meantime this matches the behavior of GLRenderer, so it is not a regression
42 EGLint error = 0;
43 bool haveNewSurface = mEglManager.makeCurrent(mEglSurface, &error);
44
45 Caches::getInstance().textureCache.resetMarkInUse(this);
46 if (!haveNewSurface) {
47 return MakeCurrentResult::AlreadyCurrent;
48 }
49 return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
50 }
51
getFrame()52 Frame OpenGLPipeline::getFrame() {
53 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
54 "drawRenderNode called on a context with no surface!");
55 return mEglManager.beginFrame(mEglSurface);
56 }
57
draw(const Frame & frame,const SkRect & screenDirty,const SkRect & dirty,const FrameBuilder::LightGeometry & lightGeometry,LayerUpdateQueue * layerUpdateQueue,const Rect & contentDrawBounds,bool opaque,const BakedOpRenderer::LightInfo & lightInfo,const std::vector<sp<RenderNode>> & renderNodes,FrameInfoVisualizer * profiler)58 bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
59 const FrameBuilder::LightGeometry& lightGeometry,
60 LayerUpdateQueue* layerUpdateQueue,
61 const Rect& contentDrawBounds, bool opaque,
62 const BakedOpRenderer::LightInfo& lightInfo,
63 const std::vector< sp<RenderNode> >& renderNodes,
64 FrameInfoVisualizer* profiler) {
65
66 mEglManager.damageFrame(frame, dirty);
67
68 bool drew = false;
69
70
71 auto& caches = Caches::getInstance();
72 FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), lightGeometry, caches);
73
74 frameBuilder.deferLayers(*layerUpdateQueue);
75 layerUpdateQueue->clear();
76
77 frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds);
78
79 BakedOpRenderer renderer(caches, mRenderThread.renderState(),
80 opaque, lightInfo);
81 frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
82 ProfileRenderer profileRenderer(renderer);
83 profiler->draw(profileRenderer);
84 drew = renderer.didDraw();
85
86 // post frame cleanup
87 caches.clearGarbage();
88 caches.pathCache.trim();
89 caches.tessellationCache.trim();
90
91 #if DEBUG_MEMORY_USAGE
92 caches.dumpMemoryUsage();
93 #else
94 if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
95 caches.dumpMemoryUsage();
96 }
97 #endif
98
99 return drew;
100 }
101
swapBuffers(const Frame & frame,bool drew,const SkRect & screenDirty,FrameInfo * currentFrameInfo,bool * requireSwap)102 bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
103 FrameInfo* currentFrameInfo, bool* requireSwap) {
104
105 GL_CHECKPOINT(LOW);
106
107 // Even if we decided to cancel the frame, from the perspective of jank
108 // metrics the frame was swapped at this point
109 currentFrameInfo->markSwapBuffers();
110
111 *requireSwap = drew || mEglManager.damageRequiresSwap();
112
113 if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
114 return false;
115 }
116
117 return *requireSwap;
118 }
119
copyLayerInto(DeferredLayerUpdater * layer,SkBitmap * bitmap)120 bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
121 ATRACE_CALL();
122 // acquire most recent buffer for drawing
123 layer->updateTexImage();
124 layer->apply();
125 return OpenGLReadbackImpl::copyLayerInto(mRenderThread,
126 static_cast<GlLayer&>(*layer->backingLayer()), bitmap);
127 }
128
createLayer(RenderState & renderState,uint32_t layerWidth,uint32_t layerHeight,SkColorFilter * colorFilter,int alpha,SkBlendMode mode,bool blend)129 static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
130 SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
131 GlLayer* layer = new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha,
132 mode, blend);
133 Caches::getInstance().textureState().activateTexture(0);
134 layer->generateTexture();
135 return layer;
136 }
137
createTextureLayer()138 DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() {
139 mEglManager.initialize();
140 return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL);
141 }
142
onStop()143 void OpenGLPipeline::onStop() {
144 if (mEglManager.isCurrent(mEglSurface)) {
145 mEglManager.makeCurrent(EGL_NO_SURFACE);
146 }
147 }
148
setSurface(Surface * surface,SwapBehavior swapBehavior)149 bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
150
151 if (mEglSurface != EGL_NO_SURFACE) {
152 mEglManager.destroySurface(mEglSurface);
153 mEglSurface = EGL_NO_SURFACE;
154 }
155
156 if (surface) {
157 mEglSurface = mEglManager.createSurface(surface);
158 }
159
160 if (mEglSurface != EGL_NO_SURFACE) {
161 const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
162 mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
163 return true;
164 }
165
166 return false;
167 }
168
isSurfaceReady()169 bool OpenGLPipeline::isSurfaceReady() {
170 return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
171 }
172
isContextReady()173 bool OpenGLPipeline::isContextReady() {
174 return CC_LIKELY(mEglManager.hasEglContext());
175 }
176
onDestroyHardwareResources()177 void OpenGLPipeline::onDestroyHardwareResources() {
178 Caches& caches = Caches::getInstance();
179 // Make sure to release all the textures we were owning as there won't
180 // be another draw
181 caches.textureCache.resetMarkInUse(this);
182 mRenderThread.renderState().flush(Caches::FlushMode::Layers);
183 }
184
renderLayers(const FrameBuilder::LightGeometry & lightGeometry,LayerUpdateQueue * layerUpdateQueue,bool opaque,const BakedOpRenderer::LightInfo & lightInfo)185 void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
186 LayerUpdateQueue* layerUpdateQueue, bool opaque,
187 const BakedOpRenderer::LightInfo& lightInfo) {
188 static const std::vector< sp<RenderNode> > emptyNodeList;
189 auto& caches = Caches::getInstance();
190 FrameBuilder frameBuilder(*layerUpdateQueue, lightGeometry, caches);
191 layerUpdateQueue->clear();
192 BakedOpRenderer renderer(caches, mRenderThread.renderState(),
193 opaque, lightInfo);
194 LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
195 frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
196 }
197
getTaskManager()198 TaskManager* OpenGLPipeline::getTaskManager() {
199 return &Caches::getInstance().tasks;
200 }
201
layerMatchesWH(OffscreenBuffer * layer,int width,int height)202 static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
203 return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height;
204 }
205
createOrUpdateLayer(RenderNode * node,const DamageAccumulator & damageAccumulator)206 bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
207 const DamageAccumulator& damageAccumulator) {
208 RenderState& renderState = mRenderThread.renderState();
209 OffscreenBufferPool& layerPool = renderState.layerPool();
210 bool transformUpdateNeeded = false;
211 if (node->getLayer() == nullptr) {
212 node->setLayer(layerPool.get(renderState, node->getWidth(), node->getHeight()));
213 transformUpdateNeeded = true;
214 } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) {
215 // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
216 // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
217 if (node->properties().fitsOnLayer()) {
218 node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight()));
219 } else {
220 destroyLayer(node);
221 }
222 transformUpdateNeeded = true;
223 }
224
225 if (transformUpdateNeeded && node->getLayer()) {
226 // update the transform in window of the layer to reset its origin wrt light source position
227 Matrix4 windowTransform;
228 damageAccumulator.computeCurrentTransform(&windowTransform);
229 node->getLayer()->setWindowTransform(windowTransform);
230 }
231
232 return transformUpdateNeeded;
233 }
234
pinImages(LsaVector<sk_sp<Bitmap>> & images)235 bool OpenGLPipeline::pinImages(LsaVector<sk_sp<Bitmap>>& images) {
236 TextureCache& cache = Caches::getInstance().textureCache;
237 bool prefetchSucceeded = true;
238 for (auto& bitmapResource : images) {
239 prefetchSucceeded &= cache.prefetchAndMarkInUse(this, bitmapResource.get());
240 }
241 return prefetchSucceeded;
242 }
243
unpinImages()244 void OpenGLPipeline::unpinImages() {
245 Caches::getInstance().textureCache.resetMarkInUse(this);
246 }
247
destroyLayer(RenderNode * node)248 void OpenGLPipeline::destroyLayer(RenderNode* node) {
249 if (OffscreenBuffer* layer = node->getLayer()) {
250 layer->renderState.layerPool().putOrDelete(layer);
251 node->setLayer(nullptr);
252 }
253 }
254
prepareToDraw(const RenderThread & thread,Bitmap * bitmap)255 void OpenGLPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
256 if (Caches::hasInstance() && thread.eglManager().hasEglContext()) {
257 ATRACE_NAME("Bitmap#prepareToDraw task");
258 Caches::getInstance().textureCache.prefetch(bitmap);
259 }
260 }
261
invokeFunctor(const RenderThread & thread,Functor * functor)262 void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
263 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
264 if (thread.eglManager().hasEglContext()) {
265 mode = DrawGlInfo::kModeProcess;
266 }
267 thread.renderState().invokeFunctor(functor, mode, nullptr);
268 }
269
270 } /* namespace renderthread */
271 } /* namespace uirenderer */
272 } /* namespace android */
273