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