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