• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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