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