1/*
2 * Copyright (C) 2016 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 "SkiaDisplayList.h"
18
19#include "DumpOpsCanvas.h"
20#include "SkiaPipeline.h"
21#include "VectorDrawable.h"
22#include "renderthread/CanvasContext.h"
23
24#include <SkImagePriv.h>
25
26namespace android {
27namespace uirenderer {
28namespace skiapipeline {
29
30void SkiaDisplayList::syncContents() {
31    for (auto& functor : mChildFunctors) {
32        functor.syncFunctor();
33    }
34    for (auto& animatedImage : mAnimatedImages) {
35        animatedImage->syncProperties();
36    }
37    for (auto& vectorDrawable : mVectorDrawables) {
38        vectorDrawable->syncProperties();
39    }
40}
41
42bool SkiaDisplayList::reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
43    reset();
44    node->attachAvailableList(this);
45    return true;
46}
47
48void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) {
49    for (auto& child : mChildNodes) {
50        updateFn(child.getRenderNode());
51    }
52}
53
54bool SkiaDisplayList::prepareListAndChildren(
55        TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
56        std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
57    // If the prepare tree is triggered by the UI thread and no previous call to
58    // pinImages has failed then we must pin all mutable images in the GPU cache
59    // until the next UI thread draw.
60    if (info.prepareTextures && !info.canvasContext.pinImages(mMutableImages)) {
61        // In the event that pinning failed we prevent future pinImage calls for the
62        // remainder of this tree traversal and also unpin any currently pinned images
63        // to free up GPU resources.
64        info.prepareTextures = false;
65        info.canvasContext.unpinImages();
66    }
67
68    bool hasBackwardProjectedNodesHere = false;
69    bool hasBackwardProjectedNodesSubtree = false;
70
71    for (auto& child : mChildNodes) {
72        hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards();
73        RenderNode* childNode = child.getRenderNode();
74        Matrix4 mat4(child.getRecordedMatrix());
75        info.damageAccumulator->pushTransform(&mat4);
76        // TODO: a layer is needed if the canvas is rotated or has a non-rect clip
77        info.hasBackwardProjectedNodes = false;
78        childFn(childNode, observer, info, functorsNeedLayer);
79        hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes;
80        info.damageAccumulator->popTransform();
81    }
82
83    // The purpose of next block of code is to reset projected display list if there are no
84    // backward projected nodes. This speeds up drawing, by avoiding an extra walk of the tree
85    if (mProjectionReceiver) {
86        mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this
87                                                                                      : nullptr);
88        info.hasBackwardProjectedNodes = hasBackwardProjectedNodesHere;
89    } else {
90        info.hasBackwardProjectedNodes =
91                hasBackwardProjectedNodesSubtree || hasBackwardProjectedNodesHere;
92    }
93
94    bool isDirty = false;
95    for (auto& animatedImage : mAnimatedImages) {
96        nsecs_t timeTilNextFrame = TreeInfo::Out::kNoAnimatedImageDelay;
97        // If any animated image in the display list needs updated, then damage the node.
98        if (animatedImage->isDirty(&timeTilNextFrame)) {
99            isDirty = true;
100        }
101
102        if (animatedImage->isRunning() &&
103            timeTilNextFrame != TreeInfo::Out::kNoAnimatedImageDelay) {
104            auto& delay = info.out.animatedImageDelay;
105            if (delay == TreeInfo::Out::kNoAnimatedImageDelay || timeTilNextFrame < delay) {
106                delay = timeTilNextFrame;
107            }
108        }
109    }
110
111    for (auto& vectorDrawable : mVectorDrawables) {
112        // If any vector drawable in the display list needs update, damage the node.
113        if (vectorDrawable->isDirty()) {
114            isDirty = true;
115            static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
116                    ->getVectorDrawables()
117                    ->push_back(vectorDrawable);
118        }
119        vectorDrawable->setPropertyChangeWillBeConsumed(true);
120    }
121    return isDirty;
122}
123
124void SkiaDisplayList::reset() {
125    mProjectionReceiver = nullptr;
126
127    mDisplayList.reset();
128
129    mMutableImages.clear();
130    mVectorDrawables.clear();
131    mAnimatedImages.clear();
132    mChildFunctors.clear();
133    mChildNodes.clear();
134
135    projectionReceiveIndex = -1;
136    allocator.~LinearAllocator();
137    new (&allocator) LinearAllocator();
138}
139
140void SkiaDisplayList::output(std::ostream& output, uint32_t level) {
141    DumpOpsCanvas canvas(output, level, *this);
142    mDisplayList.draw(&canvas);
143}
144
145};  // namespace skiapipeline
146};  // namespace uirenderer
147};  // namespace android
148