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