CanvasContext.cpp revision e486d932ca5a10446a3c98d6d065213913277268
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#include "protos/hwui.pb.h"
30#include "utils/TimeUtils.h"
31
32#if HWUI_NEW_OPS
33#include "BakedOpRenderer.h"
34#include "OpReorderer.h"
35#endif
36
37#include <cutils/properties.h>
38#include <google/protobuf/io/zero_copy_stream_impl.h>
39#include <private/hwui/DrawGlInfo.h>
40#include <strings.h>
41
42#include <algorithm>
43#include <fcntl.h>
44#include <sys/stat.h>
45
46#define TRIM_MEMORY_COMPLETE 80
47#define TRIM_MEMORY_UI_HIDDEN 20
48
49#define ENABLE_RENDERNODE_SERIALIZATION false
50
51#define LOG_FRAMETIME_MMA 0
52
53#if LOG_FRAMETIME_MMA
54static float sBenchMma = 0;
55static int sFrameCount = 0;
56static const float NANOS_PER_MILLIS_F = 1000000.0f;
57#endif
58
59namespace android {
60namespace uirenderer {
61namespace renderthread {
62
63CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
64        RenderNode* rootRenderNode, IContextFactory* contextFactory)
65        : mRenderThread(thread)
66        , mEglManager(thread.eglManager())
67        , mOpaque(!translucent)
68        , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
69        , mJankTracker(thread.timeLord().frameIntervalNanos())
70        , mProfiler(mFrames)
71        , mContentDrawBounds(0, 0, 0, 0) {
72    mRenderNodes.emplace_back(rootRenderNode);
73    mRenderThread.renderState().registerCanvasContext(this);
74    mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
75}
76
77CanvasContext::~CanvasContext() {
78    destroy();
79    mRenderThread.renderState().unregisterCanvasContext(this);
80}
81
82void CanvasContext::destroy() {
83    stopDrawing();
84    setSurface(nullptr);
85    freePrefetechedLayers();
86    destroyHardwareResources();
87    mAnimationContext->destroy();
88    if (mCanvas) {
89        delete mCanvas;
90        mCanvas = nullptr;
91    }
92}
93
94void CanvasContext::setSurface(ANativeWindow* window) {
95    ATRACE_CALL();
96
97    mNativeWindow = window;
98
99    if (mEglSurface != EGL_NO_SURFACE) {
100        mEglManager.destroySurface(mEglSurface);
101        mEglSurface = EGL_NO_SURFACE;
102    }
103
104    if (window) {
105        mEglSurface = mEglManager.createSurface(window);
106    }
107
108    if (mEglSurface != EGL_NO_SURFACE) {
109        const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
110        mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
111        mHaveNewSurface = true;
112        mSwapHistory.clear();
113        makeCurrent();
114    } else {
115        mRenderThread.removeFrameCallback(this);
116    }
117}
118
119void CanvasContext::requireSurface() {
120    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
121            "requireSurface() called but no surface set!");
122    makeCurrent();
123}
124
125void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
126    mSwapBehavior = swapBehavior;
127}
128
129bool CanvasContext::initialize(ANativeWindow* window) {
130    setSurface(window);
131#if !HWUI_NEW_OPS
132    if (mCanvas) return false;
133    mCanvas = new OpenGLRenderer(mRenderThread.renderState());
134    mCanvas->initProperties();
135#endif
136    return true;
137}
138
139void CanvasContext::updateSurface(ANativeWindow* window) {
140    setSurface(window);
141}
142
143bool CanvasContext::pauseSurface(ANativeWindow* window) {
144    return mRenderThread.removeFrameCallback(this);
145}
146
147// TODO: don't pass viewport size, it's automatic via EGL
148void CanvasContext::setup(int width, int height, float lightRadius,
149        uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
150    if (!mCanvas) return;
151    mCanvas->initLight(lightRadius, ambientShadowAlpha, spotShadowAlpha);
152}
153
154void CanvasContext::setLightCenter(const Vector3& lightCenter) {
155    if (!mCanvas) return;
156    mCanvas->setLightCenter(lightCenter);
157}
158
159void CanvasContext::setOpaque(bool opaque) {
160    mOpaque = opaque;
161}
162
163void CanvasContext::makeCurrent() {
164    // TODO: Figure out why this workaround is needed, see b/13913604
165    // In the meantime this matches the behavior of GLRenderer, so it is not a regression
166    EGLint error = 0;
167    mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error);
168    if (error) {
169        setSurface(nullptr);
170    }
171}
172
173void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
174#if !HWUI_NEW_OPS
175    bool success = layerUpdater->apply();
176    LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
177    if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
178        mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
179    }
180#endif
181}
182
183static bool wasSkipped(FrameInfo* info) {
184    return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
185}
186
187void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
188        int64_t syncQueued, RenderNode* target) {
189    mRenderThread.removeFrameCallback(this);
190
191    // If the previous frame was dropped we don't need to hold onto it, so
192    // just keep using the previous frame's structure instead
193    if (!wasSkipped(mCurrentFrameInfo)) {
194        mCurrentFrameInfo = &mFrames.next();
195    }
196    mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
197    mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
198    mCurrentFrameInfo->markSyncStart();
199
200    info.damageAccumulator = &mDamageAccumulator;
201    info.renderer = mCanvas;
202    info.canvasContext = this;
203
204    mAnimationContext->startFrame(info.mode);
205    for (const sp<RenderNode>& node : mRenderNodes) {
206        // Only the primary target node will be drawn full - all other nodes would get drawn in
207        // real time mode. In case of a window, the primary node is the window content and the other
208        // node(s) are non client / filler nodes.
209        info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
210        node->prepareTree(info);
211    }
212    mAnimationContext->runRemainingAnimations(info);
213
214    freePrefetechedLayers();
215
216    if (CC_UNLIKELY(!mNativeWindow.get())) {
217        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
218        info.out.canDrawThisFrame = false;
219        return;
220    }
221
222    if (CC_LIKELY(mSwapHistory.size())) {
223        nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
224        const SwapHistory& lastSwap = mSwapHistory.back();
225        int vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
226        // The slight fudge-factor is to deal with cases where
227        // the vsync was estimated due to being slow handling the signal.
228        // See the logic in TimeLord#computeFrameTimeNanos or in
229        // Choreographer.java for details on when this happens
230        if (vsyncDelta < 2_ms) {
231            // Already drew for this vsync pulse, UI draw request missed
232            // the deadline for RT animations
233            info.out.canDrawThisFrame = false;
234        } else if (lastSwap.swapTime < latestVsync) {
235            info.out.canDrawThisFrame = true;
236        } else {
237            // We're maybe behind? Find out for sure
238            int runningBehind = 0;
239            mNativeWindow->query(mNativeWindow.get(),
240                    NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
241            info.out.canDrawThisFrame = !runningBehind;
242        }
243    } else {
244        info.out.canDrawThisFrame = true;
245    }
246
247    if (!info.out.canDrawThisFrame) {
248        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
249    }
250
251    if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
252        if (!info.out.requiresUiRedraw) {
253            // If animationsNeedsRedraw is set don't bother posting for an RT anim
254            // as we will just end up fighting the UI thread.
255            mRenderThread.postFrameCallback(this);
256        }
257    }
258}
259
260void CanvasContext::stopDrawing() {
261    mRenderThread.removeFrameCallback(this);
262}
263
264void CanvasContext::notifyFramePending() {
265    ATRACE_CALL();
266    mRenderThread.pushBackFrameCallback(this);
267}
268
269void CanvasContext::draw() {
270#if !HWUI_NEW_OPS
271    LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
272            "drawRenderNode called on a context with no canvas or surface!");
273#endif
274
275    SkRect dirty;
276    mDamageAccumulator.finish(&dirty);
277
278    // TODO: Re-enable after figuring out cause of b/22592975
279//    if (dirty.isEmpty() && Properties::skipEmptyFrames) {
280//        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
281//        return;
282//    }
283
284    mCurrentFrameInfo->markIssueDrawCommandsStart();
285
286    Frame frame = mEglManager.beginFrame(mEglSurface);
287
288    if (frame.width() != lastFrameWidth || frame.height() != lastFrameHeight) {
289        // can't rely on prior content of window if viewport size changes
290        dirty.setEmpty();
291        lastFrameWidth = frame.width();
292        lastFrameHeight = frame.height();
293    } else if (mHaveNewSurface || frame.bufferAge() == 0) {
294        // New surface needs a full draw
295        dirty.setEmpty();
296    } else {
297        if (!dirty.isEmpty() && !dirty.intersect(0, 0, frame.width(), frame.height())) {
298            ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
299                    SK_RECT_ARGS(dirty), frame.width(), frame.height());
300            dirty.setEmpty();
301        }
302        profiler().unionDirty(&dirty);
303    }
304
305    if (dirty.isEmpty()) {
306        dirty.set(0, 0, frame.width(), frame.height());
307    }
308
309    // At this point dirty is the area of the screen to update. However,
310    // the area of the frame we need to repaint is potentially different, so
311    // stash the screen area for later
312    SkRect screenDirty(dirty);
313
314    // If the buffer age is 0 we do a full-screen repaint (handled above)
315    // If the buffer age is 1 the buffer contents are the same as they were
316    // last frame so there's nothing to union() against
317    // Therefore we only care about the > 1 case.
318    if (frame.bufferAge() > 1) {
319        if (frame.bufferAge() > (int) mSwapHistory.size()) {
320            // We don't have enough history to handle this old of a buffer
321            // Just do a full-draw
322            dirty.set(0, 0, frame.width(), frame.height());
323        } else {
324            // At this point we haven't yet added the latest frame
325            // to the damage history (happens below)
326            // So we need to damage
327            for (int i = mSwapHistory.size() - 1;
328                    i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) {
329                dirty.join(mSwapHistory[i].damage);
330            }
331        }
332    }
333
334    mEglManager.damageFrame(frame, dirty);
335
336#if HWUI_NEW_OPS
337    OpReorderer reorderer(dirty, frame.width(), frame.height(), mRenderNodes);
338    BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
339    // TODO: profiler().draw(mCanvas);
340    reorderer.replayBakedOps<BakedOpRenderer>(info);
341
342    bool drew = info.didDraw;
343
344#else
345    mCanvas->prepareDirty(frame.width(), frame.height(),
346            dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque);
347
348    Rect outBounds;
349    // It there are multiple render nodes, they are as follows:
350    // #0 - backdrop
351    // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
352    // #2 - frame
353    // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
354    // resizing however it might become partially visible. The following render loop will crop the
355    // backdrop against the content and draw the remaining part of it. It will then crop the content
356    // against the backdrop (since that indicates a shrinking of the window) and then the frame
357    // around everything.
358    // The bounds of the backdrop against which the content should be clipped.
359    Rect backdropBounds = mContentDrawBounds;
360    // Usually the contents bounds should be mContentDrawBounds - however - we will
361    // move it towards the fixed edge to give it a more stable appearance (for the moment).
362    Rect contentBounds;
363    // If there is no content bounds we ignore the layering as stated above and start with 2.
364    int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() <= 2) ? 2 : 0;
365    // Draw all render nodes. Note that
366    for (const sp<RenderNode>& node : mRenderNodes) {
367        if (layer == 0) { // Backdrop.
368            // Draw the backdrop clipped to the inverse content bounds, but assume that the content
369            // was moved to the upper left corner.
370            const RenderProperties& properties = node->properties();
371            Rect targetBounds(properties.getLeft(), properties.getTop(),
372                              properties.getRight(), properties.getBottom());
373            // Move the content bounds towards the fixed corner of the backdrop.
374            const int x = targetBounds.left;
375            const int y = targetBounds.top;
376            contentBounds.set(x, y, x + mContentDrawBounds.getWidth(),
377                                    y + mContentDrawBounds.getHeight());
378            // Remember the intersection of the target bounds and the intersection bounds against
379            // which we have to crop the content.
380            backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
381            backdropBounds.doIntersect(targetBounds);
382            // Check if we have to draw something on the left side ...
383            if (targetBounds.left < contentBounds.left) {
384                mCanvas->save(SkCanvas::kClip_SaveFlag);
385                if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
386                                      contentBounds.left, targetBounds.bottom,
387                                      SkRegion::kIntersect_Op)) {
388                    mCanvas->drawRenderNode(node.get(), outBounds);
389                }
390                // Reduce the target area by the area we have just painted.
391                targetBounds.left = std::min(contentBounds.left, targetBounds.right);
392                mCanvas->restore();
393            }
394            // ... or on the right side ...
395            if (targetBounds.right > contentBounds.right &&
396                !targetBounds.isEmpty()) {
397                mCanvas->save(SkCanvas::kClip_SaveFlag);
398                if (mCanvas->clipRect(contentBounds.right, targetBounds.top,
399                                      targetBounds.right, targetBounds.bottom,
400                                      SkRegion::kIntersect_Op)) {
401                    mCanvas->drawRenderNode(node.get(), outBounds);
402                }
403                // Reduce the target area by the area we have just painted.
404                targetBounds.right = std::max(targetBounds.left, contentBounds.right);
405                mCanvas->restore();
406            }
407            // ... or at the top ...
408            if (targetBounds.top < contentBounds.top &&
409                !targetBounds.isEmpty()) {
410                mCanvas->save(SkCanvas::kClip_SaveFlag);
411                if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
412                                      contentBounds.top,
413                                      SkRegion::kIntersect_Op)) {
414                    mCanvas->drawRenderNode(node.get(), outBounds);
415                }
416                // Reduce the target area by the area we have just painted.
417                targetBounds.top = std::min(contentBounds.top, targetBounds.bottom);
418                mCanvas->restore();
419            }
420            // ... or at the bottom.
421            if (targetBounds.bottom > contentBounds.bottom &&
422                !targetBounds.isEmpty()) {
423                mCanvas->save(SkCanvas::kClip_SaveFlag);
424                if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right,
425                                      targetBounds.bottom, SkRegion::kIntersect_Op)) {
426                    mCanvas->drawRenderNode(node.get(), outBounds);
427                }
428                mCanvas->restore();
429            }
430        } else if (layer == 1) { // Content
431            // It gets cropped against the bounds of the backdrop to stay inside.
432            mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
433
434            // We shift and clip the content to match its final location in the window.
435            const float left = mContentDrawBounds.left;
436            const float top = mContentDrawBounds.top;
437            const float dx = backdropBounds.left - left;
438            const float dy = backdropBounds.top - top;
439            const float width = backdropBounds.getWidth();
440            const float height = backdropBounds.getHeight();
441            mCanvas->translate(dx, dy);
442            if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) {
443                mCanvas->drawRenderNode(node.get(), outBounds);
444            }
445            mCanvas->restore();
446        } else { // draw the rest on top at will!
447            mCanvas->drawRenderNode(node.get(), outBounds);
448        }
449        layer++;
450    }
451
452    profiler().draw(mCanvas);
453
454    bool drew = mCanvas->finish();
455#endif
456    // Even if we decided to cancel the frame, from the perspective of jank
457    // metrics the frame was swapped at this point
458    mCurrentFrameInfo->markSwapBuffers();
459
460    if (drew) {
461        if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) {
462            setSurface(nullptr);
463        }
464        SwapHistory& swap = mSwapHistory.next();
465        swap.damage = screenDirty;
466        swap.swapTime = systemTime(CLOCK_MONOTONIC);
467        swap.vsyncTime = mRenderThread.timeLord().latestVsync();
468        mHaveNewSurface = false;
469    }
470
471    // TODO: Use a fence for real completion?
472    mCurrentFrameInfo->markFrameCompleted();
473
474#if LOG_FRAMETIME_MMA
475    float thisFrame = mCurrentFrameInfo->duration(
476            FrameInfoIndex::IssueDrawCommandsStart,
477            FrameInfoIndex::FrameCompleted) / NANOS_PER_MILLIS_F;
478    if (sFrameCount) {
479        sBenchMma = ((9 * sBenchMma) + thisFrame) / 10;
480    } else {
481        sBenchMma = thisFrame;
482    }
483    if (++sFrameCount == 10) {
484        sFrameCount = 1;
485        ALOGD("Average frame time: %.4f", sBenchMma);
486    }
487#endif
488
489    mJankTracker.addFrame(*mCurrentFrameInfo);
490    mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
491}
492
493// Called by choreographer to do an RT-driven animation
494void CanvasContext::doFrame() {
495    if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
496        return;
497    }
498    prepareAndDraw(nullptr);
499}
500
501void CanvasContext::prepareAndDraw(RenderNode* node) {
502    ATRACE_CALL();
503
504    int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
505    UiFrameInfoBuilder(frameInfo)
506        .addFlag(FrameInfoFlags::RTAnimation)
507        .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
508                mRenderThread.timeLord().latestVsync());
509
510    TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
511    prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node);
512    if (info.out.canDrawThisFrame) {
513        draw();
514    }
515}
516
517void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
518    ATRACE_CALL();
519    DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
520    if (thread.eglManager().hasEglContext()) {
521        mode = DrawGlInfo::kModeProcess;
522    }
523
524    thread.renderState().invokeFunctor(functor, mode, nullptr);
525}
526
527void CanvasContext::markLayerInUse(RenderNode* node) {
528    if (mPrefetechedLayers.erase(node)) {
529        node->decStrong(nullptr);
530    }
531}
532
533static void destroyPrefetechedNode(RenderNode* node) {
534    ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
535    node->destroyHardwareResources();
536    node->decStrong(nullptr);
537}
538
539void CanvasContext::freePrefetechedLayers() {
540    if (mPrefetechedLayers.size()) {
541        std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
542        mPrefetechedLayers.clear();
543    }
544}
545
546void CanvasContext::buildLayer(RenderNode* node) {
547    ATRACE_CALL();
548    if (!mEglManager.hasEglContext() || !mCanvas) {
549        return;
550    }
551    // buildLayer() will leave the tree in an unknown state, so we must stop drawing
552    stopDrawing();
553
554    TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
555    info.damageAccumulator = &mDamageAccumulator;
556    info.renderer = mCanvas;
557    info.runAnimations = false;
558    node->prepareTree(info);
559    SkRect ignore;
560    mDamageAccumulator.finish(&ignore);
561    // Tickle the GENERIC property on node to mark it as dirty for damaging
562    // purposes when the frame is actually drawn
563    node->setPropertyFieldsDirty(RenderNode::GENERIC);
564
565    mCanvas->markLayersAsBuildLayers();
566    mCanvas->flushLayerUpdates();
567
568    node->incStrong(nullptr);
569    mPrefetechedLayers.insert(node);
570}
571
572bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
573    layer->apply();
574    return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
575}
576
577void CanvasContext::destroyHardwareResources() {
578    stopDrawing();
579    if (mEglManager.hasEglContext()) {
580        freePrefetechedLayers();
581        for (const sp<RenderNode>& node : mRenderNodes) {
582            node->destroyHardwareResources();
583        }
584        Caches& caches = Caches::getInstance();
585        // Make sure to release all the textures we were owning as there won't
586        // be another draw
587        caches.textureCache.resetMarkInUse(this);
588        caches.flush(Caches::FlushMode::Layers);
589    }
590}
591
592void CanvasContext::trimMemory(RenderThread& thread, int level) {
593    // No context means nothing to free
594    if (!thread.eglManager().hasEglContext()) return;
595
596    ATRACE_CALL();
597    if (level >= TRIM_MEMORY_COMPLETE) {
598        Caches::getInstance().flush(Caches::FlushMode::Full);
599        thread.eglManager().destroy();
600    } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
601        Caches::getInstance().flush(Caches::FlushMode::Moderate);
602    }
603}
604
605void CanvasContext::runWithGlContext(RenderTask* task) {
606    LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(),
607            "GL context not initialized!");
608    task->run();
609}
610
611Layer* CanvasContext::createTextureLayer() {
612    requireSurface();
613    return LayerRenderer::createTextureLayer(mRenderThread.renderState());
614}
615
616void CanvasContext::setTextureAtlas(RenderThread& thread,
617        const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
618    thread.eglManager().setTextureAtlas(buffer, map, mapSize);
619}
620
621void CanvasContext::dumpFrames(int fd) {
622    FILE* file = fdopen(fd, "a");
623    fprintf(file, "\n\n---PROFILEDATA---\n");
624    for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
625        fprintf(file, "%s", FrameInfoNames[i].c_str());
626        fprintf(file, ",");
627    }
628    for (size_t i = 0; i < mFrames.size(); i++) {
629        FrameInfo& frame = mFrames[i];
630        if (frame[FrameInfoIndex::SyncStart] == 0) {
631            continue;
632        }
633        fprintf(file, "\n");
634        for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
635            fprintf(file, "%" PRId64 ",", frame[i]);
636        }
637    }
638    fprintf(file, "\n---PROFILEDATA---\n\n");
639    fflush(file);
640}
641
642void CanvasContext::resetFrameStats() {
643    mFrames.clear();
644    mRenderThread.jankTracker().reset();
645}
646
647void CanvasContext::serializeDisplayListTree() {
648#if ENABLE_RENDERNODE_SERIALIZATION
649    using namespace google::protobuf::io;
650    char package[128];
651    // Check whether tracing is enabled for this process.
652    FILE * file = fopen("/proc/self/cmdline", "r");
653    if (file) {
654        if (!fgets(package, 128, file)) {
655            ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
656            fclose(file);
657            return;
658        }
659        fclose(file);
660    } else {
661        ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
662                errno);
663        return;
664    }
665    char path[1024];
666    snprintf(path, 1024, "/data/data/%s/cache/rendertree_dump", package);
667    int fd = open(path, O_CREAT | O_WRONLY, S_IRWXU | S_IRGRP | S_IROTH);
668    if (fd == -1) {
669        ALOGD("Failed to open '%s'", path);
670        return;
671    }
672    proto::RenderNode tree;
673    // TODO: Streaming writes?
674    mRootRenderNode->copyTo(&tree);
675    std::string data = tree.SerializeAsString();
676    write(fd, data.c_str(), data.length());
677    close(fd);
678#endif
679}
680
681} /* namespace renderthread */
682} /* namespace uirenderer */
683} /* namespace android */
684