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