2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "CanvasContext.h"
19 #include "AnimationContext.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"
32 #include <cutils/properties.h>
33 #include <private/hwui/DrawGlInfo.h>
35 #define TRIM_MEMORY_COMPLETE 80
36 #define TRIM_MEMORY_UI_HIDDEN 20
39 namespace uirenderer {
40 namespace renderthread {
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);
55 CanvasContext::~CanvasContext() {
57 mRenderThread.renderState().unregisterCanvasContext(this);
60 void CanvasContext::destroy() {
63 freePrefetechedLayers();
64 destroyHardwareResources();
65 mAnimationContext->destroy();
72 void CanvasContext::setSurface(ANativeWindow* window) {
75 mNativeWindow = window;
77 if (mEglSurface != EGL_NO_SURFACE) {
78 mEglManager.destroySurface(mEglSurface);
79 mEglSurface = EGL_NO_SURFACE;
83 mEglSurface = mEglManager.createSurface(window);
86 if (mEglSurface != EGL_NO_SURFACE) {
87 const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
88 mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
89 mHaveNewSurface = true;
92 mRenderThread.removeFrameCallback(this);
96 void CanvasContext::swapBuffers(const SkRect& dirty, EGLint width, EGLint height) {
97 if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface, dirty, width, height))) {
100 mHaveNewSurface = false;
103 void CanvasContext::requireSurface() {
104 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
105 "requireSurface() called but no surface set!");
109 void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
110 mSwapBehavior = swapBehavior;
113 bool CanvasContext::initialize(ANativeWindow* window) {
115 if (mCanvas) return false;
116 mCanvas = new OpenGLRenderer(mRenderThread.renderState());
117 mCanvas->initProperties();
121 void CanvasContext::updateSurface(ANativeWindow* window) {
125 bool CanvasContext::pauseSurface(ANativeWindow* window) {
126 return mRenderThread.removeFrameCallback(this);
129 // TODO: don't pass viewport size, it's automatic via EGL
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);
136 void CanvasContext::setLightCenter(const Vector3& lightCenter) {
137 if (!mCanvas) return;
138 mCanvas->setLightCenter(lightCenter);
141 void CanvasContext::setOpaque(bool opaque) {
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
149 mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error);
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());
163 static bool wasSkipped(FrameInfo* info) {
164 return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
167 void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued) {
168 mRenderThread.removeFrameCallback(this);
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();
175 mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
176 mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
177 mCurrentFrameInfo->markSyncStart();
179 info.damageAccumulator = &mDamageAccumulator;
180 info.renderer = mCanvas;
181 info.canvasContext = this;
183 mAnimationContext->startFrame(info.mode);
184 mRootRenderNode->prepareTree(info);
185 mAnimationContext->runRemainingAnimations(info);
187 freePrefetechedLayers();
189 if (CC_UNLIKELY(!mNativeWindow.get())) {
190 mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
191 info.out.canDrawThisFrame = false;
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;
203 if (!info.out.canDrawThisFrame) {
204 mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
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);
216 void CanvasContext::stopDrawing() {
217 mRenderThread.removeFrameCallback(this);
220 void CanvasContext::notifyFramePending() {
222 mRenderThread.pushBackFrameCallback(this);
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!");
230 mDamageAccumulator.finish(&dirty);
232 // TODO: Re-enable after figuring out cause of b/22592975
233 // if (dirty.isEmpty() && Properties::skipEmptyFrames) {
234 // mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
238 mCurrentFrameInfo->markIssueDrawCommandsStart();
240 EGLint width, height;
241 mEglManager.beginFrame(mEglSurface, &width, &height);
242 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
243 mCanvas->setViewport(width, height);
245 } else if (!mBufferPreserved || mHaveNewSurface) {
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);
253 profiler().unionDirty(&dirty);
256 if (!dirty.isEmpty()) {
257 mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
258 dirty.fRight, dirty.fBottom, mOpaque);
260 mCanvas->prepare(mOpaque);
264 mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
266 profiler().draw(mCanvas);
268 bool drew = mCanvas->finish();
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();
275 swapBuffers(dirty, width, height);
278 // TODO: Use a fence for real completion?
279 mCurrentFrameInfo->markFrameCompleted();
280 mJankTracker.addFrame(*mCurrentFrameInfo);
281 mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
284 // Called by choreographer to do an RT-driven animation
285 void CanvasContext::doFrame() {
286 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
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());
298 TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
299 prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC));
300 if (info.out.canDrawThisFrame) {
305 void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
307 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
308 if (thread.eglManager().hasEglContext()) {
309 mode = DrawGlInfo::kModeProcess;
312 thread.renderState().invokeFunctor(functor, mode, nullptr);
315 void CanvasContext::markLayerInUse(RenderNode* node) {
316 if (mPrefetechedLayers.erase(node)) {
317 node->decStrong(nullptr);
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);
327 void CanvasContext::freePrefetechedLayers() {
328 if (mPrefetechedLayers.size()) {
329 std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
330 mPrefetechedLayers.clear();
334 void CanvasContext::buildLayer(RenderNode* node) {
336 if (!mEglManager.hasEglContext() || !mCanvas) {
339 // buildLayer() will leave the tree in an unknown state, so we must stop drawing
342 TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
343 info.damageAccumulator = &mDamageAccumulator;
344 info.renderer = mCanvas;
345 info.runAnimations = false;
346 node->prepareTree(info);
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);
353 mCanvas->markLayersAsBuildLayers();
354 mCanvas->flushLayerUpdates();
356 node->incStrong(nullptr);
357 mPrefetechedLayers.insert(node);
360 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
362 return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
365 void CanvasContext::destroyHardwareResources() {
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
373 caches.textureCache.resetMarkInUse(this);
374 caches.flush(Caches::kFlushMode_Layers);
378 void CanvasContext::trimMemory(RenderThread& thread, int level) {
379 // No context means nothing to free
380 if (!thread.eglManager().hasEglContext()) return;
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);
391 void CanvasContext::runWithGlContext(RenderTask* task) {
392 LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(),
393 "GL context not initialized!");
397 Layer* CanvasContext::createTextureLayer() {
399 return LayerRenderer::createTextureLayer(mRenderThread.renderState());
402 void CanvasContext::setTextureAtlas(RenderThread& thread,
403 const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
404 thread.eglManager().setTextureAtlas(buffer, map, mapSize);
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());
414 for (size_t i = 0; i < mFrames.size(); i++) {
415 FrameInfo& frame = mFrames[i];
416 if (frame[FrameInfoIndex::SyncStart] == 0) {
420 for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
421 fprintf(file, "%" PRId64 ",", frame[i]);
424 fprintf(file, "\n---PROFILEDATA---\n\n");
428 void CanvasContext::resetFrameStats() {
430 mRenderThread.jankTracker().reset();
433 } /* namespace renderthread */
434 } /* namespace uirenderer */
435 } /* namespace android */