OSDN Git Service

6dfb6e811e60bb4510b5e5980a255e2b65d29f12
[android-x86/frameworks-base.git] / libs / hwui / renderthread / CanvasContext.cpp
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
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
55 CanvasContext::~CanvasContext() {
56     destroy();
57     mRenderThread.renderState().unregisterCanvasContext(this);
58 }
59
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
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
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
103 void CanvasContext::requireSurface() {
104     LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
105             "requireSurface() called but no surface set!");
106     makeCurrent();
107 }
108
109 void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
110     mSwapBehavior = swapBehavior;
111 }
112
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
121 void CanvasContext::updateSurface(ANativeWindow* window) {
122     setSurface(window);
123 }
124
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
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
136 void CanvasContext::setLightCenter(const Vector3& lightCenter) {
137     if (!mCanvas) return;
138     mCanvas->setLightCenter(lightCenter);
139 }
140
141 void CanvasContext::setOpaque(bool opaque) {
142     mOpaque = opaque;
143 }
144
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
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
163 static bool wasSkipped(FrameInfo* info) {
164     return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
165 }
166
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
216 void CanvasContext::stopDrawing() {
217     mRenderThread.removeFrameCallback(this);
218 }
219
220 void CanvasContext::notifyFramePending() {
221     ATRACE_CALL();
222     mRenderThread.pushBackFrameCallback(this);
223 }
224
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
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
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
315 void CanvasContext::markLayerInUse(RenderNode* node) {
316     if (mPrefetechedLayers.erase(node)) {
317         node->decStrong(nullptr);
318     }
319 }
320
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
327 void CanvasContext::freePrefetechedLayers() {
328     if (mPrefetechedLayers.size()) {
329         std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
330         mPrefetechedLayers.clear();
331     }
332 }
333
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
360 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
361     layer->apply();
362     return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
363 }
364
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
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
391 void CanvasContext::runWithGlContext(RenderTask* task) {
392     LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(),
393             "GL context not initialized!");
394     task->run();
395 }
396
397 Layer* CanvasContext::createTextureLayer() {
398     requireSurface();
399     return LayerRenderer::createTextureLayer(mRenderThread.renderState());
400 }
401
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
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
428 void CanvasContext::resetFrameStats() {
429     mFrames.clear();
430     mRenderThread.jankTracker().reset();
431 }
432
433 } /* namespace renderthread */
434 } /* namespace uirenderer */
435 } /* namespace android */