1 /*
2  * Copyright (C) 2014 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 "CanvasContext.h"
18 
19 #include "AnimationContext.h"
20 #include "Caches.h"
21 #include "DeferredLayerUpdater.h"
22 #include "EglManager.h"
23 #include "LayerRenderer.h"
24 #include "OpenGLRenderer.h"
25 #include "Properties.h"
26 #include "RenderThread.h"
27 #include "renderstate/RenderState.h"
28 #include "renderstate/Stencil.h"
29 
30 #include <algorithm>
31 #include <strings.h>
32 #include <cutils/properties.h>
33 #include <private/hwui/DrawGlInfo.h>
34 
35 #define TRIM_MEMORY_COMPLETE 80
36 #define TRIM_MEMORY_UI_HIDDEN 20
37 
38 namespace android {
39 namespace uirenderer {
40 namespace renderthread {
41 
CanvasContext(RenderThread & thread,bool translucent,RenderNode * rootRenderNode,IContextFactory * contextFactory)42 CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
43         RenderNode* rootRenderNode, IContextFactory* contextFactory)
44         : mRenderThread(thread)
45         , mEglManager(thread.eglManager())
46         , mOpaque(!translucent)
47         , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
48         , mRootRenderNode(rootRenderNode)
49         , mJankTracker(thread.timeLord().frameIntervalNanos())
50         , mProfiler(mFrames) {
51     mRenderThread.renderState().registerCanvasContext(this);
52     mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
53 }
54 
~CanvasContext()55 CanvasContext::~CanvasContext() {
56     destroy();
57     mRenderThread.renderState().unregisterCanvasContext(this);
58 }
59 
destroy()60 void CanvasContext::destroy() {
61     stopDrawing();
62     setSurface(nullptr);
63     freePrefetechedLayers();
64     destroyHardwareResources();
65     mAnimationContext->destroy();
66     if (mCanvas) {
67         delete mCanvas;
68         mCanvas = nullptr;
69     }
70 }
71 
setSurface(ANativeWindow * window)72 void CanvasContext::setSurface(ANativeWindow* window) {
73     ATRACE_CALL();
74 
75     mNativeWindow = window;
76 
77     if (mEglSurface != EGL_NO_SURFACE) {
78         mEglManager.destroySurface(mEglSurface);
79         mEglSurface = EGL_NO_SURFACE;
80     }
81 
82     if (window) {
83         mEglSurface = mEglManager.createSurface(window);
84     }
85 
86     if (mEglSurface != EGL_NO_SURFACE) {
87         const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
88         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
89         mHaveNewSurface = true;
90         makeCurrent();
91     } else {
92         mRenderThread.removeFrameCallback(this);
93     }
94 }
95 
swapBuffers(const SkRect & dirty,EGLint width,EGLint height)96 void CanvasContext::swapBuffers(const SkRect& dirty, EGLint width, EGLint height) {
97     if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface, dirty, width, height))) {
98         setSurface(nullptr);
99     }
100     mHaveNewSurface = false;
101 }
102 
requireSurface()103 void CanvasContext::requireSurface() {
104     LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
105             "requireSurface() called but no surface set!");
106     makeCurrent();
107 }
108 
setSwapBehavior(SwapBehavior swapBehavior)109 void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
110     mSwapBehavior = swapBehavior;
111 }
112 
initialize(ANativeWindow * window)113 bool CanvasContext::initialize(ANativeWindow* window) {
114     setSurface(window);
115     if (mCanvas) return false;
116     mCanvas = new OpenGLRenderer(mRenderThread.renderState());
117     mCanvas->initProperties();
118     return true;
119 }
120 
updateSurface(ANativeWindow * window)121 void CanvasContext::updateSurface(ANativeWindow* window) {
122     setSurface(window);
123 }
124 
pauseSurface(ANativeWindow * window)125 bool CanvasContext::pauseSurface(ANativeWindow* window) {
126     return mRenderThread.removeFrameCallback(this);
127 }
128 
129 // TODO: don't pass viewport size, it's automatic via EGL
setup(int width,int height,float lightRadius,uint8_t ambientShadowAlpha,uint8_t spotShadowAlpha)130 void CanvasContext::setup(int width, int height, float lightRadius,
131         uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
132     if (!mCanvas) return;
133     mCanvas->initLight(lightRadius, ambientShadowAlpha, spotShadowAlpha);
134 }
135 
setLightCenter(const Vector3 & lightCenter)136 void CanvasContext::setLightCenter(const Vector3& lightCenter) {
137     if (!mCanvas) return;
138     mCanvas->setLightCenter(lightCenter);
139 }
140 
setOpaque(bool opaque)141 void CanvasContext::setOpaque(bool opaque) {
142     mOpaque = opaque;
143 }
144 
makeCurrent()145 void CanvasContext::makeCurrent() {
146     // TODO: Figure out why this workaround is needed, see b/13913604
147     // In the meantime this matches the behavior of GLRenderer, so it is not a regression
148     EGLint error = 0;
149     mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error);
150     if (error) {
151         setSurface(nullptr);
152     }
153 }
154 
processLayerUpdate(DeferredLayerUpdater * layerUpdater)155 void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
156     bool success = layerUpdater->apply();
157     LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
158     if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
159         mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
160     }
161 }
162 
wasSkipped(FrameInfo * info)163 static bool wasSkipped(FrameInfo* info) {
164     return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
165 }
166 
prepareTree(TreeInfo & info,int64_t * uiFrameInfo,int64_t syncQueued)167 void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued) {
168     mRenderThread.removeFrameCallback(this);
169 
170     // If the previous frame was dropped we don't need to hold onto it, so
171     // just keep using the previous frame's structure instead
172     if (!wasSkipped(mCurrentFrameInfo)) {
173         mCurrentFrameInfo = &mFrames.next();
174     }
175     mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
176     mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
177     mCurrentFrameInfo->markSyncStart();
178 
179     info.damageAccumulator = &mDamageAccumulator;
180     info.renderer = mCanvas;
181     info.canvasContext = this;
182 
183     mAnimationContext->startFrame(info.mode);
184     mRootRenderNode->prepareTree(info);
185     mAnimationContext->runRemainingAnimations(info);
186 
187     freePrefetechedLayers();
188 
189     if (CC_UNLIKELY(!mNativeWindow.get())) {
190         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
191         info.out.canDrawThisFrame = false;
192         return;
193     }
194 
195     int runningBehind = 0;
196     // TODO: This query is moderately expensive, investigate adding some sort
197     // of fast-path based off when we last called eglSwapBuffers() as well as
198     // last vsync time. Or something.
199     mNativeWindow->query(mNativeWindow.get(),
200             NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
201     info.out.canDrawThisFrame = !runningBehind;
202 
203     if (!info.out.canDrawThisFrame) {
204         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
205     }
206 
207     if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
208         if (!info.out.requiresUiRedraw) {
209             // If animationsNeedsRedraw is set don't bother posting for an RT anim
210             // as we will just end up fighting the UI thread.
211             mRenderThread.postFrameCallback(this);
212         }
213     }
214 }
215 
stopDrawing()216 void CanvasContext::stopDrawing() {
217     mRenderThread.removeFrameCallback(this);
218 }
219 
notifyFramePending()220 void CanvasContext::notifyFramePending() {
221     ATRACE_CALL();
222     mRenderThread.pushBackFrameCallback(this);
223 }
224 
draw()225 void CanvasContext::draw() {
226     LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
227             "drawRenderNode called on a context with no canvas or surface!");
228 
229     SkRect dirty;
230     mDamageAccumulator.finish(&dirty);
231 
232     // TODO: Re-enable after figuring out cause of b/22592975
233 //    if (dirty.isEmpty() && Properties::skipEmptyFrames) {
234 //        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
235 //        return;
236 //    }
237 
238     mCurrentFrameInfo->markIssueDrawCommandsStart();
239 
240     EGLint width, height;
241     mEglManager.beginFrame(mEglSurface, &width, &height);
242     if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
243         mCanvas->setViewport(width, height);
244         dirty.setEmpty();
245     } else if (!mBufferPreserved || mHaveNewSurface) {
246         dirty.setEmpty();
247     } else {
248         if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
249             ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
250                     SK_RECT_ARGS(dirty), width, height);
251             dirty.setEmpty();
252         }
253         profiler().unionDirty(&dirty);
254     }
255 
256     if (!dirty.isEmpty()) {
257         mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
258                 dirty.fRight, dirty.fBottom, mOpaque);
259     } else {
260         mCanvas->prepare(mOpaque);
261     }
262 
263     Rect outBounds;
264     mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
265 
266     profiler().draw(mCanvas);
267 
268     bool drew = mCanvas->finish();
269 
270     // Even if we decided to cancel the frame, from the perspective of jank
271     // metrics the frame was swapped at this point
272     mCurrentFrameInfo->markSwapBuffers();
273 
274     if (drew) {
275         swapBuffers(dirty, width, height);
276     }
277 
278     // TODO: Use a fence for real completion?
279     mCurrentFrameInfo->markFrameCompleted();
280     mJankTracker.addFrame(*mCurrentFrameInfo);
281     mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
282 }
283 
284 // Called by choreographer to do an RT-driven animation
doFrame()285 void CanvasContext::doFrame() {
286     if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
287         return;
288     }
289 
290     ATRACE_CALL();
291 
292     int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
293     UiFrameInfoBuilder(frameInfo)
294         .addFlag(FrameInfoFlags::RTAnimation)
295         .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
296                 mRenderThread.timeLord().latestVsync());
297 
298     TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
299     prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC));
300     if (info.out.canDrawThisFrame) {
301         draw();
302     }
303 }
304 
invokeFunctor(RenderThread & thread,Functor * functor)305 void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
306     ATRACE_CALL();
307     DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
308     if (thread.eglManager().hasEglContext()) {
309         mode = DrawGlInfo::kModeProcess;
310     }
311 
312     thread.renderState().invokeFunctor(functor, mode, nullptr);
313 }
314 
markLayerInUse(RenderNode * node)315 void CanvasContext::markLayerInUse(RenderNode* node) {
316     if (mPrefetechedLayers.erase(node)) {
317         node->decStrong(nullptr);
318     }
319 }
320 
destroyPrefetechedNode(RenderNode * node)321 static void destroyPrefetechedNode(RenderNode* node) {
322     ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
323     node->destroyHardwareResources();
324     node->decStrong(nullptr);
325 }
326 
freePrefetechedLayers()327 void CanvasContext::freePrefetechedLayers() {
328     if (mPrefetechedLayers.size()) {
329         std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
330         mPrefetechedLayers.clear();
331     }
332 }
333 
buildLayer(RenderNode * node)334 void CanvasContext::buildLayer(RenderNode* node) {
335     ATRACE_CALL();
336     if (!mEglManager.hasEglContext() || !mCanvas) {
337         return;
338     }
339     // buildLayer() will leave the tree in an unknown state, so we must stop drawing
340     stopDrawing();
341 
342     TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
343     info.damageAccumulator = &mDamageAccumulator;
344     info.renderer = mCanvas;
345     info.runAnimations = false;
346     node->prepareTree(info);
347     SkRect ignore;
348     mDamageAccumulator.finish(&ignore);
349     // Tickle the GENERIC property on node to mark it as dirty for damaging
350     // purposes when the frame is actually drawn
351     node->setPropertyFieldsDirty(RenderNode::GENERIC);
352 
353     mCanvas->markLayersAsBuildLayers();
354     mCanvas->flushLayerUpdates();
355 
356     node->incStrong(nullptr);
357     mPrefetechedLayers.insert(node);
358 }
359 
copyLayerInto(DeferredLayerUpdater * layer,SkBitmap * bitmap)360 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
361     layer->apply();
362     return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
363 }
364 
destroyHardwareResources()365 void CanvasContext::destroyHardwareResources() {
366     stopDrawing();
367     if (mEglManager.hasEglContext()) {
368         freePrefetechedLayers();
369         mRootRenderNode->destroyHardwareResources();
370         Caches& caches = Caches::getInstance();
371         // Make sure to release all the textures we were owning as there won't
372         // be another draw
373         caches.textureCache.resetMarkInUse(this);
374         caches.flush(Caches::kFlushMode_Layers);
375     }
376 }
377 
trimMemory(RenderThread & thread,int level)378 void CanvasContext::trimMemory(RenderThread& thread, int level) {
379     // No context means nothing to free
380     if (!thread.eglManager().hasEglContext()) return;
381 
382     ATRACE_CALL();
383     if (level >= TRIM_MEMORY_COMPLETE) {
384         Caches::getInstance().flush(Caches::kFlushMode_Full);
385         thread.eglManager().destroy();
386     } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
387         Caches::getInstance().flush(Caches::kFlushMode_Moderate);
388     }
389 }
390 
runWithGlContext(RenderTask * task)391 void CanvasContext::runWithGlContext(RenderTask* task) {
392     LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(),
393             "GL context not initialized!");
394     task->run();
395 }
396 
createTextureLayer()397 Layer* CanvasContext::createTextureLayer() {
398     requireSurface();
399     return LayerRenderer::createTextureLayer(mRenderThread.renderState());
400 }
401 
setTextureAtlas(RenderThread & thread,const sp<GraphicBuffer> & buffer,int64_t * map,size_t mapSize)402 void CanvasContext::setTextureAtlas(RenderThread& thread,
403         const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
404     thread.eglManager().setTextureAtlas(buffer, map, mapSize);
405 }
406 
dumpFrames(int fd)407 void CanvasContext::dumpFrames(int fd) {
408     FILE* file = fdopen(fd, "a");
409     fprintf(file, "\n\n---PROFILEDATA---\n");
410     for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
411         fprintf(file, "%s", FrameInfoNames[i].c_str());
412         fprintf(file, ",");
413     }
414     for (size_t i = 0; i < mFrames.size(); i++) {
415         FrameInfo& frame = mFrames[i];
416         if (frame[FrameInfoIndex::SyncStart] == 0) {
417             continue;
418         }
419         fprintf(file, "\n");
420         for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
421             fprintf(file, "%" PRId64 ",", frame[i]);
422         }
423     }
424     fprintf(file, "\n---PROFILEDATA---\n\n");
425     fflush(file);
426 }
427 
resetFrameStats()428 void CanvasContext::resetFrameStats() {
429     mFrames.clear();
430     mRenderThread.jankTracker().reset();
431 }
432 
433 } /* namespace renderthread */
434 } /* namespace uirenderer */
435 } /* namespace android */
436