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 #include <SkPathOps.h>
26 
27 namespace android {
28 namespace uirenderer {
29 namespace skiapipeline {
30 
syncContents(const WebViewSyncData & data)31 void SkiaDisplayList::syncContents(const WebViewSyncData& data) {
32     for (auto& functor : mChildFunctors) {
33         functor->syncFunctor(data);
34     }
35     for (auto& animatedImage : mAnimatedImages) {
36         animatedImage->syncProperties();
37     }
38     for (auto& vectorDrawable : mVectorDrawables) {
39         vectorDrawable.first->syncProperties();
40     }
41 }
42 
reuseDisplayList(RenderNode * node,renderthread::CanvasContext * context)43 bool SkiaDisplayList::reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
44     reset();
45     node->attachAvailableList(this);
46     return true;
47 }
48 
updateChildren(std::function<void (RenderNode *)> updateFn)49 void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) {
50     for (auto& child : mChildNodes) {
51         updateFn(child.getRenderNode());
52     }
53 }
54 
intersects(const SkISize screenSize,const Matrix4 & mat,const SkRect & bounds)55 static bool intersects(const SkISize screenSize, const Matrix4& mat, const SkRect& bounds) {
56     Vector3 points[] = { Vector3 {bounds.fLeft, bounds.fTop, 0},
57                          Vector3 {bounds.fRight, bounds.fTop, 0},
58                          Vector3 {bounds.fRight, bounds.fBottom, 0},
59                          Vector3 {bounds.fLeft, bounds.fBottom, 0}};
60     float minX, minY, maxX, maxY;
61     bool first = true;
62     for (auto& point : points) {
63         mat.mapPoint3d(point);
64         if (first) {
65             minX = maxX = point.x;
66             minY = maxY = point.y;
67             first = false;
68         } else {
69             minX = std::min(minX, point.x);
70             minY = std::min(minY, point.y);
71             maxX = std::max(maxX, point.x);
72             maxY = std::max(maxY, point.y);
73         }
74     }
75     return SkRect::Make(screenSize).intersects(SkRect::MakeLTRB(minX, minY, maxX, maxY));
76 }
77 
prepareListAndChildren(TreeObserver & observer,TreeInfo & info,bool functorsNeedLayer,std::function<void (RenderNode *,TreeObserver &,TreeInfo &,bool)> childFn)78 bool SkiaDisplayList::prepareListAndChildren(
79         TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
80         std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
81     // If the prepare tree is triggered by the UI thread and no previous call to
82     // pinImages has failed then we must pin all mutable images in the GPU cache
83     // until the next UI thread draw.
84     if (info.prepareTextures && !info.canvasContext.pinImages(mMutableImages)) {
85         // In the event that pinning failed we prevent future pinImage calls for the
86         // remainder of this tree traversal and also unpin any currently pinned images
87         // to free up GPU resources.
88         info.prepareTextures = false;
89         info.canvasContext.unpinImages();
90     }
91 
92     bool hasBackwardProjectedNodesHere = false;
93     bool hasBackwardProjectedNodesSubtree = false;
94 
95     for (auto& child : mChildNodes) {
96         hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards();
97         RenderNode* childNode = child.getRenderNode();
98         Matrix4 mat4(child.getRecordedMatrix());
99         info.damageAccumulator->pushTransform(&mat4);
100         info.hasBackwardProjectedNodes = false;
101         childFn(childNode, observer, info, functorsNeedLayer);
102         hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes;
103         info.damageAccumulator->popTransform();
104     }
105 
106     // The purpose of next block of code is to reset projected display list if there are no
107     // backward projected nodes. This speeds up drawing, by avoiding an extra walk of the tree
108     if (mProjectionReceiver) {
109         mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this
110                                                                                       : nullptr);
111         info.hasBackwardProjectedNodes = hasBackwardProjectedNodesHere;
112     } else {
113         info.hasBackwardProjectedNodes =
114                 hasBackwardProjectedNodesSubtree || hasBackwardProjectedNodesHere;
115     }
116 
117     bool isDirty = false;
118     for (auto& animatedImage : mAnimatedImages) {
119         nsecs_t timeTilNextFrame = TreeInfo::Out::kNoAnimatedImageDelay;
120         // If any animated image in the display list needs updated, then damage the node.
121         if (animatedImage->isDirty(&timeTilNextFrame)) {
122             isDirty = true;
123         }
124 
125         if (animatedImage->isRunning() &&
126             timeTilNextFrame != TreeInfo::Out::kNoAnimatedImageDelay) {
127             auto& delay = info.out.animatedImageDelay;
128             if (delay == TreeInfo::Out::kNoAnimatedImageDelay || timeTilNextFrame < delay) {
129                 delay = timeTilNextFrame;
130             }
131         }
132     }
133 
134     for (auto& vectorDrawablePair : mVectorDrawables) {
135         // If any vector drawable in the display list needs update, damage the node.
136         auto& vectorDrawable = vectorDrawablePair.first;
137         if (vectorDrawable->isDirty()) {
138             Matrix4 totalMatrix;
139             info.damageAccumulator->computeCurrentTransform(&totalMatrix);
140             Matrix4 canvasMatrix(vectorDrawablePair.second);
141             totalMatrix.multiply(canvasMatrix);
142             const SkRect& bounds = vectorDrawable->properties().getBounds();
143             if (intersects(info.screenSize, totalMatrix, bounds)) {
144                 isDirty = true;
145                 static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
146                         ->getVectorDrawables()
147                         ->push_back(vectorDrawable);
148                 vectorDrawable->setPropertyChangeWillBeConsumed(true);
149             }
150         }
151     }
152     return isDirty;
153 }
154 
reset()155 void SkiaDisplayList::reset() {
156     mProjectionReceiver = nullptr;
157 
158     mDisplayList.reset();
159 
160     mMutableImages.clear();
161     mVectorDrawables.clear();
162     mAnimatedImages.clear();
163     mChildFunctors.clear();
164     mChildNodes.clear();
165 
166     allocator.~LinearAllocator();
167     new (&allocator) LinearAllocator();
168 }
169 
output(std::ostream & output,uint32_t level)170 void SkiaDisplayList::output(std::ostream& output, uint32_t level) {
171     DumpOpsCanvas canvas(output, level, *this);
172     mDisplayList.draw(&canvas);
173 }
174 
175 }  // namespace skiapipeline
176 }  // namespace uirenderer
177 }  // namespace android
178