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