1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/flow/scene_update_context.h"
6
7 #include "flutter/flow/layers/layer.h"
8 #include "flutter/flow/matrix_decomposition.h"
9 #include "flutter/fml/trace_event.h"
10
11 namespace flutter {
12
13 // Helper function to generate clip planes for a scenic::EntityNode.
SetEntityNodeClipPlanes(scenic::EntityNode & entity_node,const SkRect & bounds)14 static void SetEntityNodeClipPlanes(scenic::EntityNode& entity_node,
15 const SkRect& bounds) {
16 const float top = bounds.top();
17 const float bottom = bounds.bottom();
18 const float left = bounds.left();
19 const float right = bounds.right();
20
21 // We will generate 4 oriented planes, one for each edge of the bounding rect.
22 std::vector<fuchsia::ui::gfx::Plane3> clip_planes;
23 clip_planes.resize(4);
24
25 // Top plane.
26 clip_planes[0].dist = top;
27 clip_planes[0].dir.x = 0.f;
28 clip_planes[0].dir.y = 1.f;
29 clip_planes[0].dir.z = 0.f;
30
31 // Bottom plane.
32 clip_planes[1].dist = -bottom;
33 clip_planes[1].dir.x = 0.f;
34 clip_planes[1].dir.y = -1.f;
35 clip_planes[1].dir.z = 0.f;
36
37 // Left plane.
38 clip_planes[2].dist = left;
39 clip_planes[2].dir.x = 1.f;
40 clip_planes[2].dir.y = 0.f;
41 clip_planes[2].dir.z = 0.f;
42
43 // Right plane.
44 clip_planes[3].dist = -right;
45 clip_planes[3].dir.x = -1.f;
46 clip_planes[3].dir.y = 0.f;
47 clip_planes[3].dir.z = 0.f;
48
49 entity_node.SetClipPlanes(std::move(clip_planes));
50 }
51
SceneUpdateContext(scenic::Session * session,SurfaceProducer * surface_producer)52 SceneUpdateContext::SceneUpdateContext(scenic::Session* session,
53 SurfaceProducer* surface_producer)
54 : session_(session), surface_producer_(surface_producer) {
55 FML_DCHECK(surface_producer_ != nullptr);
56 }
57
CreateFrame(scenic::EntityNode entity_node,scenic::ShapeNode shape_node,const SkRRect & rrect,SkColor color,const SkRect & paint_bounds,std::vector<Layer * > paint_layers,Layer * layer)58 void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node,
59 scenic::ShapeNode shape_node,
60 const SkRRect& rrect,
61 SkColor color,
62 const SkRect& paint_bounds,
63 std::vector<Layer*> paint_layers,
64 Layer* layer) {
65 // Frames always clip their children.
66 SetEntityNodeClipPlanes(entity_node, rrect.getBounds());
67 // TODO(SCN-1274): SetClip() will be deleted.
68 entity_node.SetClip(0u, true /* clip to self */);
69
70 // We don't need a shape if the frame is zero size.
71 if (rrect.isEmpty())
72 return;
73
74 // Add a part which represents the frame's geometry for clipping purposes
75 // and possibly for its texture.
76 // TODO(SCN-137): Need to be able to express the radii as vectors.
77 SkRect shape_bounds = rrect.getBounds();
78 scenic::RoundedRectangle shape(
79 session_, // session
80 rrect.width(), // width
81 rrect.height(), // height
82 rrect.radii(SkRRect::kUpperLeft_Corner).x(), // top_left_radius
83 rrect.radii(SkRRect::kUpperRight_Corner).x(), // top_right_radius
84 rrect.radii(SkRRect::kLowerRight_Corner).x(), // bottom_right_radius
85 rrect.radii(SkRRect::kLowerLeft_Corner).x() // bottom_left_radius
86 );
87 shape_node.SetShape(shape);
88 shape_node.SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(),
89 shape_bounds.height() * 0.5f + shape_bounds.top(),
90 0.f);
91
92 // Check whether the painted layers will be visible.
93 if (paint_bounds.isEmpty() || !paint_bounds.intersects(shape_bounds))
94 paint_layers.clear();
95
96 // Check whether a solid color will suffice.
97 if (paint_layers.empty()) {
98 SetShapeColor(shape_node, color);
99 return;
100 }
101
102 // Apply current metrics and transformation scale factors.
103 const float scale_x = ScaleX();
104 const float scale_y = ScaleY();
105
106 // Apply a texture to the whole shape.
107 SetShapeTextureAndColor(shape_node, color, scale_x, scale_y, shape_bounds,
108 std::move(paint_layers), layer,
109 std::move(entity_node));
110 }
111
SetShapeTextureAndColor(scenic::ShapeNode & shape_node,SkColor color,SkScalar scale_x,SkScalar scale_y,const SkRect & paint_bounds,std::vector<Layer * > paint_layers,Layer * layer,scenic::EntityNode entity_node)112 void SceneUpdateContext::SetShapeTextureAndColor(
113 scenic::ShapeNode& shape_node,
114 SkColor color,
115 SkScalar scale_x,
116 SkScalar scale_y,
117 const SkRect& paint_bounds,
118 std::vector<Layer*> paint_layers,
119 Layer* layer,
120 scenic::EntityNode entity_node) {
121 scenic::Image* image = GenerateImageIfNeeded(
122 color, scale_x, scale_y, paint_bounds, std::move(paint_layers), layer,
123 std::move(entity_node));
124 if (image != nullptr) {
125 scenic::Material material(session_);
126 material.SetTexture(*image);
127 shape_node.SetMaterial(material);
128 return;
129 }
130
131 SetShapeColor(shape_node, color);
132 }
133
SetShapeColor(scenic::ShapeNode & shape_node,SkColor color)134 void SceneUpdateContext::SetShapeColor(scenic::ShapeNode& shape_node,
135 SkColor color) {
136 if (SkColorGetA(color) == 0)
137 return;
138
139 scenic::Material material(session_);
140 material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color),
141 SkColorGetA(color));
142 shape_node.SetMaterial(material);
143 }
144
GenerateImageIfNeeded(SkColor color,SkScalar scale_x,SkScalar scale_y,const SkRect & paint_bounds,std::vector<Layer * > paint_layers,Layer * layer,scenic::EntityNode entity_node)145 scenic::Image* SceneUpdateContext::GenerateImageIfNeeded(
146 SkColor color,
147 SkScalar scale_x,
148 SkScalar scale_y,
149 const SkRect& paint_bounds,
150 std::vector<Layer*> paint_layers,
151 Layer* layer,
152 scenic::EntityNode entity_node) {
153 // Bail if there's nothing to paint.
154 if (paint_layers.empty())
155 return nullptr;
156
157 // Bail if the physical bounds are empty after rounding.
158 SkISize physical_size = SkISize::Make(paint_bounds.width() * scale_x,
159 paint_bounds.height() * scale_y);
160 if (physical_size.isEmpty())
161 return nullptr;
162
163 // Acquire a surface from the surface producer and register the paint tasks.
164 std::unique_ptr<SurfaceProducerSurface> surface =
165 surface_producer_->ProduceSurface(
166 physical_size,
167 LayerRasterCacheKey(
168 // Root frame has a nullptr layer
169 layer ? layer->unique_id() : 0, Matrix()),
170 std::make_unique<scenic::EntityNode>(std::move(entity_node)));
171
172 if (!surface) {
173 FML_LOG(ERROR) << "Could not acquire a surface from the surface producer "
174 "of size: "
175 << physical_size.width() << "x" << physical_size.height();
176 return nullptr;
177 }
178
179 auto image = surface->GetImage();
180
181 // Enqueue the paint task.
182 paint_tasks_.push_back({.surface = std::move(surface),
183 .left = paint_bounds.left(),
184 .top = paint_bounds.top(),
185 .scale_x = scale_x,
186 .scale_y = scale_y,
187 .background_color = color,
188 .layers = std::move(paint_layers)});
189 return image;
190 }
191
192 std::vector<
193 std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>>
ExecutePaintTasks(CompositorContext::ScopedFrame & frame)194 SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) {
195 TRACE_EVENT0("flutter", "SceneUpdateContext::ExecutePaintTasks");
196 std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces_to_submit;
197 for (auto& task : paint_tasks_) {
198 FML_DCHECK(task.surface);
199 SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas();
200 Layer::PaintContext context = {canvas,
201 canvas,
202 frame.gr_context(),
203 nullptr,
204 frame.context().raster_time(),
205 frame.context().ui_time(),
206 frame.context().texture_registry(),
207 &frame.context().raster_cache(),
208 false};
209 canvas->restoreToCount(1);
210 canvas->save();
211 canvas->clear(task.background_color);
212 canvas->scale(task.scale_x, task.scale_y);
213 canvas->translate(-task.left, -task.top);
214 for (Layer* layer : task.layers) {
215 layer->Paint(context);
216 }
217 surfaces_to_submit.emplace_back(std::move(task.surface));
218 }
219 paint_tasks_.clear();
220 return surfaces_to_submit;
221 }
222
Entity(SceneUpdateContext & context)223 SceneUpdateContext::Entity::Entity(SceneUpdateContext& context)
224 : context_(context),
225 previous_entity_(context.top_entity_),
226 entity_node_(context.session()) {
227 if (previous_entity_)
228 previous_entity_->embedder_node().AddChild(entity_node_);
229 context.top_entity_ = this;
230 }
231
~Entity()232 SceneUpdateContext::Entity::~Entity() {
233 FML_DCHECK(context_.top_entity_ == this);
234 context_.top_entity_ = previous_entity_;
235 }
236
Transform(SceneUpdateContext & context,const SkMatrix & transform)237 SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
238 const SkMatrix& transform)
239 : Entity(context),
240 previous_scale_x_(context.top_scale_x_),
241 previous_scale_y_(context.top_scale_y_) {
242 if (!transform.isIdentity()) {
243 // TODO(SCN-192): The perspective and shear components in the matrix
244 // are not handled correctly.
245 MatrixDecomposition decomposition(transform);
246 if (decomposition.IsValid()) {
247 entity_node().SetTranslation(decomposition.translation().x(), //
248 decomposition.translation().y(), //
249 -decomposition.translation().z() //
250 );
251
252 entity_node().SetScale(decomposition.scale().x(), //
253 decomposition.scale().y(), //
254 decomposition.scale().z() //
255 );
256 context.top_scale_x_ *= decomposition.scale().x();
257 context.top_scale_y_ *= decomposition.scale().y();
258
259 entity_node().SetRotation(decomposition.rotation().fData[0], //
260 decomposition.rotation().fData[1], //
261 decomposition.rotation().fData[2], //
262 decomposition.rotation().fData[3] //
263 );
264 }
265 }
266 }
267
Transform(SceneUpdateContext & context,float scale_x,float scale_y,float scale_z)268 SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
269 float scale_x,
270 float scale_y,
271 float scale_z)
272 : Entity(context),
273 previous_scale_x_(context.top_scale_x_),
274 previous_scale_y_(context.top_scale_y_) {
275 if (scale_x != 1.f || scale_y != 1.f || scale_z != 1.f) {
276 entity_node().SetScale(scale_x, scale_y, scale_z);
277 context.top_scale_x_ *= scale_x;
278 context.top_scale_y_ *= scale_y;
279 }
280 }
281
~Transform()282 SceneUpdateContext::Transform::~Transform() {
283 context().top_scale_x_ = previous_scale_x_;
284 context().top_scale_y_ = previous_scale_y_;
285 }
286
Shape(SceneUpdateContext & context)287 SceneUpdateContext::Shape::Shape(SceneUpdateContext& context)
288 : Entity(context), shape_node_(context.session()) {
289 entity_node().AddChild(shape_node_);
290 }
291
Frame(SceneUpdateContext & context,const SkRRect & rrect,SkColor color,float local_elevation,float world_elevation,float depth,Layer * layer)292 SceneUpdateContext::Frame::Frame(SceneUpdateContext& context,
293 const SkRRect& rrect,
294 SkColor color,
295 float local_elevation,
296 float world_elevation,
297 float depth,
298 Layer* layer)
299 : Shape(context),
300 rrect_(rrect),
301 color_(color),
302 paint_bounds_(SkRect::MakeEmpty()),
303 layer_(layer) {
304 if (depth > -1 && world_elevation > depth) {
305 // TODO(mklim): Deal with bounds overflow more elegantly. We'd like to be
306 // able to have developers specify the behavior here to alternatives besides
307 // clamping, like normalization on some arbitrary curve.
308
309 // Clamp the local z coordinate at our max bound. Take into account the
310 // parent z position here to fix clamping in cases where the child is
311 // overflowing because of its parents.
312 const float parent_elevation = world_elevation - local_elevation;
313 local_elevation = depth - parent_elevation;
314 }
315 if (local_elevation != 0.0) {
316 entity_node().SetTranslation(0.f, 0.f, -local_elevation);
317 }
318 }
319
~Frame()320 SceneUpdateContext::Frame::~Frame() {
321 context().CreateFrame(std::move(entity_node()), std::move(shape_node()),
322 rrect_, color_, paint_bounds_, std::move(paint_layers_),
323 layer_);
324 }
325
AddPaintLayer(Layer * layer)326 void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) {
327 FML_DCHECK(layer->needs_painting());
328 paint_layers_.push_back(layer);
329 paint_bounds_.join(layer->paint_bounds());
330 }
331
Clip(SceneUpdateContext & context,const SkRect & shape_bounds)332 SceneUpdateContext::Clip::Clip(SceneUpdateContext& context,
333 const SkRect& shape_bounds)
334 : Entity(context) {
335 SetEntityNodeClipPlanes(entity_node(), shape_bounds);
336 }
337
338 } // namespace flutter
339