Flutter Engine
scene_update_context.cc
Go to the documentation of this file.
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 <lib/ui/scenic/cpp/commands.h>
8 #include <lib/ui/scenic/cpp/view_token_pair.h>
9 
10 #include "flutter/flow/layers/layer.h"
11 #include "flutter/flow/matrix_decomposition.h"
12 #include "flutter/flow/view_holder.h"
13 #include "flutter/fml/trace_event.h"
14 #include "include/core/SkColor.h"
15 
16 namespace flutter {
17 namespace {
18 
19 void SetEntityNodeClipPlanes(scenic::EntityNode& entity_node,
20  const SkRect& bounds) {
21  const float top = bounds.top();
22  const float bottom = bounds.bottom();
23  const float left = bounds.left();
24  const float right = bounds.right();
25 
26  // We will generate 4 oriented planes, one for each edge of the bounding rect.
27  std::vector<fuchsia::ui::gfx::Plane3> clip_planes;
28  clip_planes.resize(4);
29 
30  // Top plane.
31  clip_planes[0].dist = top;
32  clip_planes[0].dir.x = 0.f;
33  clip_planes[0].dir.y = 1.f;
34  clip_planes[0].dir.z = 0.f;
35 
36  // Bottom plane.
37  clip_planes[1].dist = -bottom;
38  clip_planes[1].dir.x = 0.f;
39  clip_planes[1].dir.y = -1.f;
40  clip_planes[1].dir.z = 0.f;
41 
42  // Left plane.
43  clip_planes[2].dist = left;
44  clip_planes[2].dir.x = 1.f;
45  clip_planes[2].dir.y = 0.f;
46  clip_planes[2].dir.z = 0.f;
47 
48  // Right plane.
49  clip_planes[3].dist = -right;
50  clip_planes[3].dir.x = -1.f;
51  clip_planes[3].dir.y = 0.f;
52  clip_planes[3].dir.z = 0.f;
53 
54  entity_node.SetClipPlanes(std::move(clip_planes));
55 }
56 
57 void SetMaterialColor(scenic::Material& material,
58  SkColor color,
59  SkAlpha opacity) {
60  const SkAlpha color_alpha = static_cast<SkAlpha>(
61  ((float)SkColorGetA(color) * (float)opacity) / 255.0f);
62  material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color),
63  color_alpha);
64 }
65 
66 } // namespace
67 
69  fuchsia::ui::views::ViewToken view_token,
70  scenic::ViewRefPair view_ref_pair,
71  SessionWrapper& session)
72  : session_(session),
73  root_view_(session_.get(),
74  std::move(view_token),
75  std::move(view_ref_pair.control_ref),
76  std::move(view_ref_pair.view_ref),
77  debug_label),
78  root_node_(session_.get()) {
79  root_view_.AddChild(root_node_);
80  root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
81 
82  session_.Present();
83 }
84 
85 std::vector<SceneUpdateContext::PaintTask> SceneUpdateContext::GetPaintTasks() {
86  std::vector<PaintTask> frame_paint_tasks = std::move(paint_tasks_);
87 
88  paint_tasks_.clear();
89 
90  return frame_paint_tasks;
91 }
92 
94  session_.get()->Enqueue(
95  scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable));
96 }
97 
99  paint_tasks_.clear();
100  top_entity_ = nullptr;
101  top_scale_x_ = 1.f;
102  top_scale_y_ = 1.f;
103  top_elevation_ = 0.f;
104  next_elevation_ = 0.f;
105  alpha_ = 1.f;
106 
107  // We are going to be sending down a fresh node hierarchy every frame. So just
108  // enqueue a detach op on the imported root node.
109  session_.get()->Enqueue(scenic::NewDetachChildrenCmd(root_node_.id()));
110 }
111 
112 void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node,
113  const SkRRect& rrect,
114  SkColor color,
115  SkAlpha opacity,
116  const SkRect& paint_bounds,
117  std::vector<Layer*> paint_layers) {
118  // We don't need a shape if the frame is zero size.
119  if (rrect.isEmpty())
120  return;
121 
122  // Frames always clip their children.
123  SkRect shape_bounds = rrect.getBounds();
124  SetEntityNodeClipPlanes(entity_node, shape_bounds);
125 
126  // TODO(SCN-137): Need to be able to express the radii as vectors.
127  scenic::ShapeNode shape_node(session_.get());
128  scenic::Rectangle shape(session_.get(), rrect.width(), rrect.height());
129  shape_node.SetShape(shape);
130  shape_node.SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(),
131  shape_bounds.height() * 0.5f + shape_bounds.top(),
132  0.f);
133 
134  // Check whether the painted layers will be visible.
135  if (paint_bounds.isEmpty() || !paint_bounds.intersects(shape_bounds))
136  paint_layers.clear();
137 
138  scenic::Material material(session_.get());
139  shape_node.SetMaterial(material);
140  entity_node.AddChild(shape_node);
141 
142  // Check whether a solid color will suffice.
143  if (paint_layers.empty()) {
144  SetMaterialColor(material, color, opacity);
145  } else {
146  // The final shape's color is material_color * texture_color. The passed in
147  // material color was already used as a background when generating the
148  // texture, so set the model color to |SK_ColorWHITE| in order to allow
149  // using the texture's color unmodified.
150  SetMaterialColor(material, SK_ColorWHITE, opacity);
151 
152  // Enqueue a paint task for these layers, to apply a texture to the whole
153  // shape.
154  //
155  // The task uses the |shape_bounds| as its rendering bounds instead of the
156  // |paint_bounds|. If the paint_bounds is large than the shape_bounds it
157  // will be clipped.
158  paint_tasks_.emplace_back(PaintTask{.paint_bounds = shape_bounds,
159  .scale_x = top_scale_x_,
160  .scale_y = top_scale_y_,
161  .background_color = color,
162  .material = std::move(material),
163  .layers = std::move(paint_layers)});
164  }
165 }
166 
167 void SceneUpdateContext::UpdateView(int64_t view_id,
168  const SkPoint& offset,
169  const SkSize& size,
170  std::optional<bool> override_hit_testable) {
171  auto* view_holder = ViewHolder::FromId(view_id);
172  FML_DCHECK(view_holder);
173 
174  if (size.width() > 0.f && size.height() > 0.f) {
175  view_holder->SetProperties(size.width(), size.height(), 0, 0, 0, 0,
176  view_holder->focusable());
177  }
178 
179  bool hit_testable = override_hit_testable.has_value()
180  ? *override_hit_testable
181  : view_holder->hit_testable();
182  view_holder->UpdateScene(session_.get(), top_entity_->embedder_node(), offset,
183  size, SkScalarRoundToInt(alphaf() * 255),
184  hit_testable);
185 
186  // Assume embedded views are 10 "layers" wide.
187  next_elevation_ += 10 * kScenicZElevationBetweenLayers;
188 }
189 
190 void SceneUpdateContext::CreateView(int64_t view_id,
191  bool hit_testable,
192  bool focusable) {
193  zx_handle_t handle = (zx_handle_t)view_id;
194  flutter::ViewHolder::Create(handle, nullptr,
195  scenic::ToViewHolderToken(zx::eventpair(handle)),
196  nullptr);
197  auto* view_holder = ViewHolder::FromId(view_id);
198  FML_DCHECK(view_holder);
199 
200  view_holder->set_hit_testable(hit_testable);
201  view_holder->set_focusable(focusable);
202 }
203 
204 void SceneUpdateContext::UpdateView(int64_t view_id,
205  bool hit_testable,
206  bool focusable) {
207  auto* view_holder = ViewHolder::FromId(view_id);
208  FML_DCHECK(view_holder);
209 
210  view_holder->set_hit_testable(hit_testable);
211  view_holder->set_focusable(focusable);
212 }
213 
214 void SceneUpdateContext::DestroyView(int64_t view_id) {
215  ViewHolder::Destroy(view_id);
216 }
217 
219  : context_(context),
220  previous_entity_(context.top_entity_),
221  entity_node_(context.session_.get()) {
222  context.top_entity_ = this;
223 }
224 
226  if (previous_entity_) {
227  previous_entity_->embedder_node().AddChild(entity_node_);
228  } else {
229  context_.root_node_.AddChild(entity_node_);
230  }
231 
232  FML_DCHECK(context_.top_entity_ == this);
233  context_.top_entity_ = previous_entity_;
234 }
235 
237  const SkMatrix& transform)
238  : Entity(context),
239  previous_scale_x_(context.top_scale_x_),
240  previous_scale_y_(context.top_scale_y_) {
241  entity_node().SetLabel("flutter::Transform");
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  // Don't allow clients to control the z dimension; we control that
248  // instead to make sure layers appear in proper order.
249  entity_node().SetTranslation(decomposition.translation().x, //
250  decomposition.translation().y, //
251  0.f //
252  );
253 
254  entity_node().SetScale(decomposition.scale().x, //
255  decomposition.scale().y, //
256  1.f //
257  );
258  context.top_scale_x_ *= decomposition.scale().x;
259  context.top_scale_y_ *= decomposition.scale().y;
260 
261  entity_node().SetRotation(decomposition.rotation().x, //
262  decomposition.rotation().y, //
263  decomposition.rotation().z, //
264  decomposition.rotation().w //
265  );
266  }
267  }
268 }
269 
271  float scale_x,
272  float scale_y,
273  float scale_z)
274  : Entity(context),
275  previous_scale_x_(context.top_scale_x_),
276  previous_scale_y_(context.top_scale_y_) {
277  entity_node().SetLabel("flutter::Transform");
278  if (scale_x != 1.f || scale_y != 1.f || scale_z != 1.f) {
279  entity_node().SetScale(scale_x, scale_y, scale_z);
280  context.top_scale_x_ *= scale_x;
281  context.top_scale_y_ *= scale_y;
282  }
283 }
284 
286  context().top_scale_x_ = previous_scale_x_;
287  context().top_scale_y_ = previous_scale_y_;
288 }
289 
291  const SkRRect& rrect,
292  SkColor color,
293  SkAlpha opacity,
294  std::string label)
295  : Entity(context),
296  previous_elevation_(context.top_elevation_),
297  rrect_(rrect),
298  color_(color),
299  opacity_(opacity),
300  opacity_node_(context.session_.get()),
301  paint_bounds_(SkRect::MakeEmpty()) {
302  // Increment elevation trackers before calculating any local elevation.
303  // |UpdateView| can modify context.next_elevation_, which is why it is
304  // neccesary to track this addtional state.
305  context.top_elevation_ += kScenicZElevationBetweenLayers;
306  context.next_elevation_ += kScenicZElevationBetweenLayers;
307 
308  float local_elevation = context.next_elevation_ - previous_elevation_;
309  entity_node().SetTranslation(0.f, 0.f, -local_elevation);
310  entity_node().SetLabel(label);
311  entity_node().AddChild(opacity_node_);
312 
313  // Scenic currently lacks an API to enable rendering of alpha channel; alpha
314  // channels are only rendered if there is a OpacityNode higher in the tree
315  // with opacity != 1. For now, clamp to a infinitesimally smaller value than
316  // 1, which does not cause visual problems in practice.
317  opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f));
318 }
319 
321  context().top_elevation_ = previous_elevation_;
322 
323  // Add a part which represents the frame's geometry for clipping purposes
324  context().CreateFrame(entity_node(), rrect_, color_, opacity_, paint_bounds_,
325  std::move(paint_layers_));
326 }
327 
329  FML_DCHECK(layer->needs_painting());
330  paint_layers_.push_back(layer);
331  paint_bounds_.join(layer->paint_bounds());
332 }
333 
335  const SkRect& shape_bounds)
336  : Entity(context) {
337  entity_node().SetLabel("flutter::Clip");
338  SetEntityNodeClipPlanes(entity_node(), shape_bounds);
339 }
340 
341 } // namespace flutter
virtual void Present()=0
#define FML_DCHECK(condition)
Definition: logging.h:86
Frame(SceneUpdateContext &context, const SkRRect &rrect, SkColor color, SkAlpha opacity, std::string label)
constexpr float kOneMinusEpsilon
Definition: ref_ptr.h:252
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
SceneUpdateContext(std::string debug_label, fuchsia::ui::views::ViewToken view_token, scenic::ViewRefPair view_ref_pair, SessionWrapper &session)
Clip(SceneUpdateContext &context, const SkRect &shape_bounds)
const SkV3 & translation() const
virtual scenic::Session * get()=0
Entity(SceneUpdateContext &context)
bool needs_painting() const
Definition: layer.h:174
constexpr float kScenicZElevationBetweenLayers
const SkRect & paint_bounds() const
Definition: layer.h:166
void UpdateView(int64_t view_id, bool hit_testable, bool focusable)
static void Destroy(zx_koid_t id)
Definition: view_holder.cc:73
static void Create(zx_koid_t id, fml::RefPtr< fml::TaskRunner > ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, const BindCallback &on_bind_callback)
Definition: view_holder.cc:53
static ViewHolder * FromId(zx_koid_t id)
Definition: view_holder.cc:80
std::vector< PaintTask > GetPaintTasks()
void DestroyView(int64_t view_id)
Transform(SceneUpdateContext &context, const SkMatrix &transform)
void CreateView(int64_t view_id, bool hit_testable, bool focusable)
virtual scenic::ContainerNode & embedder_node()