Flutter Engine
fuchsia_layer_unittests.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 <fuchsia/ui/scenic/cpp/fidl.h>
6 #include <fuchsia/ui/scenic/cpp/fidl_test_base.h>
7 #include <lib/async-loop/cpp/loop.h>
8 #include <lib/async/default.h>
9 #include <lib/fidl/cpp/optional.h>
10 #include <lib/sys/cpp/component_context.h>
11 #include <lib/ui/scenic/cpp/commands.h>
12 #include <lib/ui/scenic/cpp/id.h>
13 #include <lib/ui/scenic/cpp/view_token_pair.h>
14 
15 #include <deque>
16 
17 #include "flutter/flow/layers/child_scene_layer.h"
18 #include "flutter/flow/layers/container_layer.h"
19 #include "flutter/flow/layers/opacity_layer.h"
20 #include "flutter/flow/layers/physical_shape_layer.h"
21 #include "flutter/flow/layers/transform_layer.h"
22 #include "flutter/flow/view_holder.h"
23 #include "flutter/fml/platform/fuchsia/message_loop_fuchsia.h"
24 #include "flutter/fml/task_runner.h"
25 #include "gtest/gtest.h"
26 
27 namespace flutter {
28 namespace testing {
29 
30 using FuchsiaLayerTest = ::testing::Test;
31 
32 class MockSession : public fuchsia::ui::scenic::testing::Session_TestBase {
33  public:
34  MockSession() : binding_(this) {}
35 
36  void NotImplemented_(const std::string& name) final {}
37 
38  void Bind(fidl::InterfaceRequest<::fuchsia::ui::scenic::Session> request,
39  ::fuchsia::ui::scenic::SessionListenerPtr listener) {
40  binding_.Bind(std::move(request));
41  listener_ = std::move(listener);
42  }
43 
44  static std::string Vec3ValueToString(fuchsia::ui::gfx::Vector3Value value) {
45  return "{" + std::to_string(value.value.x) + ", " +
46  std::to_string(value.value.y) + ", " +
47  std::to_string(value.value.z) + "}";
48  }
49 
50  static std::string QuaternionValueToString(
51  fuchsia::ui::gfx::QuaternionValue value) {
52  return "{" + std::to_string(value.value.x) + ", " +
53  std::to_string(value.value.y) + ", " +
54  std::to_string(value.value.z) + ", " +
55  std::to_string(value.value.w) + "}";
56  }
57 
58  static std::string GfxCreateResourceCmdToString(
59  const fuchsia::ui::gfx::CreateResourceCmd& cmd) {
60  std::string id = " id: " + std::to_string(cmd.id);
61  switch (cmd.resource.Which()) {
62  case fuchsia::ui::gfx::ResourceArgs::Tag::kRectangle:
63  return "Rectangle" + id;
64  case fuchsia::ui::gfx::ResourceArgs::Tag::kRoundedRectangle:
65  return "RoundedRectangle" + id;
66  case fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder:
67  return "ViewHolder" + id;
68  case fuchsia::ui::gfx::ResourceArgs::Tag::kOpacityNode:
69  return "OpacityNode" + id;
70  case fuchsia::ui::gfx::ResourceArgs::Tag::kEntityNode:
71  return "EntityNode" + id;
72  case fuchsia::ui::gfx::ResourceArgs::Tag::kShapeNode:
73  return "ShapeNode" + id;
74  case fuchsia::ui::gfx::ResourceArgs::Tag::kMaterial:
75  return "Material" + id;
76  case fuchsia::ui::gfx::ResourceArgs::Tag::kImage:
77  return "Image" + id + ", memory_id: " +
78  std::to_string(cmd.resource.image().memory_id) +
79  ", memory_offset: " +
80  std::to_string(cmd.resource.image().memory_offset);
81  default:
82  return "Unhandled CreateResource command" +
83  std::to_string(cmd.resource.Which());
84  }
85  }
86 
87  static std::string GfxCmdToString(const fuchsia::ui::gfx::Command& cmd) {
88  switch (cmd.Which()) {
89  case fuchsia::ui::gfx::Command::Tag::kCreateResource:
90  return "CreateResource: " +
91  GfxCreateResourceCmdToString(cmd.create_resource());
92  case fuchsia::ui::gfx::Command::Tag::kReleaseResource:
93  return "ReleaseResource id: " +
94  std::to_string(cmd.release_resource().id);
95  case fuchsia::ui::gfx::Command::Tag::kAddChild:
96  return "AddChild id: " + std::to_string(cmd.add_child().node_id) +
97  " child_id: " + std::to_string(cmd.add_child().child_id);
98  case fuchsia::ui::gfx::Command::Tag::kSetTranslation:
99  return "SetTranslation id: " +
100  std::to_string(cmd.set_translation().id) +
101  " value: " + Vec3ValueToString(cmd.set_translation().value);
102  case fuchsia::ui::gfx::Command::Tag::kSetScale:
103  return "SetScale id: " + std::to_string(cmd.set_scale().id) +
104  " value: " + Vec3ValueToString(cmd.set_scale().value);
105  case fuchsia::ui::gfx::Command::Tag::kSetRotation:
106  return "SetRotation id: " + std::to_string(cmd.set_rotation().id) +
107  " value: " + QuaternionValueToString(cmd.set_rotation().value);
108  case fuchsia::ui::gfx::Command::Tag::kSetOpacity:
109  return "SetOpacity id: " + std::to_string(cmd.set_opacity().node_id) +
110  ", opacity: " + std::to_string(cmd.set_opacity().opacity);
111  case fuchsia::ui::gfx::Command::Tag::kSetColor:
112  return "SetColor id: " + std::to_string(cmd.set_color().material_id) +
113  ", rgba: (" + std::to_string(cmd.set_color().color.value.red) +
114  ", " + std::to_string(cmd.set_color().color.value.green) + ", " +
115  std::to_string(cmd.set_color().color.value.blue) + ", " +
116  std::to_string(cmd.set_color().color.value.alpha) + ")";
117  case fuchsia::ui::gfx::Command::Tag::kSetLabel:
118  return "SetLabel id: " + std::to_string(cmd.set_label().id) + " " +
119  cmd.set_label().label;
120  case fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior:
121  return "SetHitTestBehavior node_id: " +
122  std::to_string(cmd.set_hit_test_behavior().node_id);
123  case fuchsia::ui::gfx::Command::Tag::kSetClipPlanes:
124  return "SetClipPlanes node_id: " +
125  std::to_string(cmd.set_clip_planes().node_id);
126  case fuchsia::ui::gfx::Command::Tag::kSetShape:
127  return "SetShape node_id: " + std::to_string(cmd.set_shape().node_id) +
128  ", shape_id: " + std::to_string(cmd.set_shape().shape_id);
129  case fuchsia::ui::gfx::Command::Tag::kSetMaterial:
130  return "SetMaterial node_id: " +
131  std::to_string(cmd.set_material().node_id) + ", material_id: " +
132  std::to_string(cmd.set_material().material_id);
133  case fuchsia::ui::gfx::Command::Tag::kSetTexture:
134  return "SetTexture material_id: " +
135  std::to_string(cmd.set_texture().material_id) +
136  ", texture_id: " + std::to_string(cmd.set_texture().texture_id);
137 
138  default:
139  return "Unhandled gfx command" + std::to_string(cmd.Which());
140  }
141  }
142 
143  static std::string ScenicCmdToString(
144  const fuchsia::ui::scenic::Command& cmd) {
145  if (cmd.Which() != fuchsia::ui::scenic::Command::Tag::kGfx) {
146  return "Unhandled non-gfx command: " + std::to_string(cmd.Which());
147  }
148  return GfxCmdToString(cmd.gfx());
149  }
150 
151  // |fuchsia::ui::scenic::Session|
152  void Enqueue(std::vector<fuchsia::ui::scenic::Command> cmds) override {
153  for (const auto& cmd : cmds) {
154  num_enqueued_commands_++;
155  EXPECT_FALSE(expected_.empty())
156  << "Received more commands than expected; command: <"
157  << ScenicCmdToString(cmd)
158  << ">, num_enqueued_commands: " << num_enqueued_commands_;
159  if (!expected_.empty()) {
160  EXPECT_TRUE(AreCommandsEqual(expected_.front(), cmd))
161  << "actual command: <" << ScenicCmdToString(cmd)
162  << ">, expected command: <" << ScenicCmdToString(expected_.front())
163  << ">, num_enqueued_commands: " << num_enqueued_commands_;
164  expected_.pop_front();
165  }
166  }
167  }
168 
169  void SetExpectedCommands(std::vector<fuchsia::ui::gfx::Command> gfx_cmds) {
170  std::deque<fuchsia::ui::scenic::Command> scenic_commands;
171  for (auto it = gfx_cmds.begin(); it != gfx_cmds.end(); it++) {
172  scenic_commands.push_back(scenic::NewCommand(std::move((*it))));
173  }
174  expected_ = std::move(scenic_commands);
175  num_enqueued_commands_ = 0;
176  }
177 
178  size_t num_enqueued_commands() { return num_enqueued_commands_; }
179 
180  private:
181  static bool IsGfxCommand(const fuchsia::ui::scenic::Command& cmd,
182  fuchsia::ui::gfx::Command::Tag tag) {
183  return cmd.Which() == fuchsia::ui::scenic::Command::Tag::kGfx &&
184  cmd.gfx().Which() == tag;
185  }
186 
187  static bool IsCreateResourceCommand(const fuchsia::ui::scenic::Command& cmd,
188  fuchsia::ui::gfx::ResourceArgs::Tag tag) {
189  return IsGfxCommand(cmd, fuchsia::ui::gfx::Command::Tag::kCreateResource) &&
190  cmd.gfx().create_resource().resource.Which() == tag;
191  }
192 
193  static bool AreCommandsEqual(const fuchsia::ui::scenic::Command& command1,
194  const fuchsia::ui::scenic::Command& command2) {
195  // For CreateViewHolderCommand, just compare the id and ignore the
196  // view_holder_token.
197  if (IsCreateResourceCommand(
198  command1, fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder)) {
199  return IsCreateResourceCommand(
200  command2, fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder) &&
201  command1.gfx().create_resource().id ==
202  command2.gfx().create_resource().id;
203  }
204  // For CreateImageCommand, just compare the id and memory_id.
205  if (IsCreateResourceCommand(command1,
206  fuchsia::ui::gfx::ResourceArgs::Tag::kImage)) {
207  return IsCreateResourceCommand(
208  command2, fuchsia::ui::gfx::ResourceArgs::Tag::kImage) &&
209  command1.gfx().create_resource().id ==
210  command2.gfx().create_resource().id &&
211  command1.gfx().create_resource().resource.image().memory_id ==
212  command2.gfx().create_resource().resource.image().memory_id;
213  }
214  // For SetHitTestBehaviorCommand, just compare the node_id.
215  if (IsGfxCommand(command1,
216  fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior)) {
217  return IsGfxCommand(
218  command2,
219  fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior) &&
220  command1.gfx().set_hit_test_behavior().node_id ==
221  command2.gfx().set_hit_test_behavior().node_id;
222  }
223  // For SetHitTestBehaviorCommand, just compare the node_id.
224  if (IsGfxCommand(command1,
225  fuchsia::ui::gfx::Command::Tag::kSetClipPlanes)) {
226  return IsGfxCommand(command2,
227  fuchsia::ui::gfx::Command::Tag::kSetClipPlanes) &&
228  command1.gfx().set_clip_planes().node_id ==
229  command2.gfx().set_clip_planes().node_id;
230  }
231  return fidl::Equals(command1, command2);
232  }
233 
234  std::deque<fuchsia::ui::scenic::Command> expected_;
235  size_t num_enqueued_commands_ = 0;
236  fidl::Binding<fuchsia::ui::scenic::Session> binding_;
237  fuchsia::ui::scenic::SessionListenerPtr listener_;
238 };
239 
241  public:
242  MockSessionWrapper(fuchsia::ui::scenic::SessionPtr session_ptr)
243  : session_(std::move(session_ptr)) {}
244  ~MockSessionWrapper() override = default;
245 
246  scenic::Session* get() override { return &session_; }
247  void Present() override { session_.Flush(); }
248 
249  private:
250  scenic::Session session_;
251 };
252 
253 struct TestContext {
254  // Message loop.
257 
258  // Session.
259  fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener> listener_request;
261  std::unique_ptr<MockSessionWrapper> mock_session_wrapper;
262 
263  // SceneUpdateContext.
264  std::shared_ptr<SceneUpdateContext> scene_update_context;
265 
266  // PrerollContext.
270  std::unique_ptr<PrerollContext> preroll_context;
271 };
272 
273 std::unique_ptr<TestContext> InitTest() {
274  std::unique_ptr<TestContext> context = std::make_unique<TestContext>();
275 
276  // Init message loop.
277  context->loop = fml::MakeRefCounted<fml::MessageLoopFuchsia>();
278  context->task_runner = fml::MakeRefCounted<fml::TaskRunner>(context->loop);
279 
280  // Init Session.
281  fuchsia::ui::scenic::SessionPtr session_ptr;
282  fuchsia::ui::scenic::SessionListenerPtr listener;
283  context->listener_request = listener.NewRequest();
284  context->mock_session.Bind(session_ptr.NewRequest(), std::move(listener));
285  context->mock_session_wrapper =
286  std::make_unique<MockSessionWrapper>(std::move(session_ptr));
287 
288  // Init SceneUpdateContext.
289  context->scene_update_context = std::make_shared<SceneUpdateContext>(
290  "fuchsia_layer_unittest", fuchsia::ui::views::ViewToken(),
291  scenic::ViewRefPair::New(), *(context->mock_session_wrapper));
292 
293  // Init PrerollContext.
294  context->preroll_context = std::unique_ptr<PrerollContext>(new PrerollContext{
295  nullptr, // raster_cache (don't consult the cache)
296  nullptr, // gr_context (used for the raster cache)
297  nullptr, // external view embedder
298  context->unused_stack, // mutator stack
299  nullptr, // SkColorSpace* dst_color_space
300  kGiantRect, // SkRect cull_rect
301  false, // layer reads from surface
302  context->unused_stopwatch, // frame time (dont care)
303  context->unused_stopwatch, // engine time (dont care)
304  context->unused_texture_registry, // texture registry (not
305  // supported)
306  false, // checkerboard_offscreen_layers
307  1.f // ratio between logical and physical
308  });
309 
310  return context;
311 }
312 
313 zx_koid_t GetChildLayerId() {
314  static zx_koid_t sChildLayerId = 17324;
315  return sChildLayerId++;
316 }
317 
319  public:
320  AutoDestroyChildLayerId(zx_koid_t id) : id_(id) {}
322 
323  private:
324  zx_koid_t id_;
325 };
326 
327 // Create a hierarchy with PhysicalShapeLayers and ChildSceneLayers, and
328 // inspect the commands sent to Scenic.
329 //
330 //
331 // What we expect:
332 //
333 // The Scenic elevations of the PhysicalShapeLayers are monotically
334 // increasing, even though the elevations we gave them when creating them are
335 // decreasing. The two should not have any correlation; we're merely mirror
336 // the paint order using Scenic elevation.
337 //
338 // PhysicalShapeLayers created before/below a ChildView do not get their own
339 // node; PhysicalShapeLayers created afterward do.
340 //
341 // Nested PhysicalShapeLayers are collapsed.
342 //
343 // This test has been temporarily disabled: fxb/52028
344 TEST_F(FuchsiaLayerTest, DISABLED_PhysicalShapeLayersAndChildSceneLayers) {
345  auto test_context = InitTest();
346 
347  // Root.
348  auto root = std::make_shared<ContainerLayer>();
349  SkPath path;
350  path.addRect(SkRect::MakeWH(10.f, 10.f));
351 
352  // Child #1: PhysicalShapeLayer.
353  auto physical_shape1 = std::make_shared<PhysicalShapeLayer>(
354  /*color=*/SK_ColorCYAN,
355  /*shadow_color=*/SK_ColorBLACK,
356  /*elevation*/ 23.f, path, Clip::antiAlias);
357  root->Add(physical_shape1);
358 
359  // Child #2: ChildSceneLayer.
360  const zx_koid_t kChildLayerId1 = GetChildLayerId();
361  auto [unused_view_token1, unused_view_holder_token1] =
362  scenic::ViewTokenPair::New();
363  ViewHolder::Create(kChildLayerId1, test_context->task_runner,
364  std::move(unused_view_holder_token1),
365  /*bind_callback=*/[](scenic::ResourceId id) {});
366  // Will destroy only when we go out of scope (i.e. end of the test).
367  AutoDestroyChildLayerId auto_destroy1(kChildLayerId1);
368  auto child_view1 = std::make_shared<ChildSceneLayer>(
369  kChildLayerId1, SkPoint::Make(1, 1), SkSize::Make(10, 10),
370  /*hit_testable=*/false);
371  root->Add(child_view1);
372 
373  // Child #3: PhysicalShapeLayer
374  auto physical_shape2 = std::make_shared<PhysicalShapeLayer>(
375  /*color=*/SK_ColorCYAN,
376  /*shadow_color=*/SK_ColorBLACK,
377  /*elevation*/ 21.f, path, Clip::antiAlias);
378  root->Add(physical_shape2);
379 
380  // Grandchild (child of #3): PhysicalShapeLayer
381  auto physical_shape3 = std::make_shared<PhysicalShapeLayer>(
382  /*color=*/SK_ColorCYAN,
383  /*shadow_color=*/SK_ColorBLACK,
384  /*elevation*/ 19.f, path, Clip::antiAlias);
385  physical_shape2->Add(physical_shape3);
386 
387  // Child #4: ChildSceneLayer
388  const zx_koid_t kChildLayerId2 = GetChildLayerId();
389  auto [unused_view_token2, unused_view_holder_token2] =
390  scenic::ViewTokenPair::New();
391  ViewHolder::Create(kChildLayerId2, test_context->task_runner,
392  std::move(unused_view_holder_token2),
393  /*bind_callback=*/[](scenic::ResourceId id) {});
394  // Will destroy only when we go out of scope (i.e. end of the test).
395  AutoDestroyChildLayerId auto_destroy2(kChildLayerId2);
396  auto child_view2 = std::make_shared<ChildSceneLayer>(
397  kChildLayerId2, SkPoint::Make(1, 1), SkSize::Make(10, 10),
398  /*hit_testable=*/false);
399  root->Add(child_view2);
400 
401  // Child #5: PhysicalShapeLayer
402  auto physical_shape4 = std::make_shared<PhysicalShapeLayer>(
403  /*color=*/SK_ColorCYAN,
404  /*shadow_color=*/SK_ColorBLACK,
405  /*elevation*/ 17.f, path, Clip::antiAlias);
406  root->Add(physical_shape4);
407 
408  // Preroll.
409  root->Preroll(test_context->preroll_context.get(), SkMatrix());
410 
411  // Create another frame to be the "real" root. Required because
412  // UpdateScene() traversal expects there to already be a top node.
413  SceneUpdateContext::Frame frame(test_context->scene_update_context,
414  SkRRect::MakeRect(SkRect::MakeWH(100, 100)),
415  SK_ColorTRANSPARENT, SK_AlphaOPAQUE,
416  "fuchsia test root");
417 
418  // Submit the list of command we will expect Scenic to see.
419  //
420  // Some things we expect:
421  //
422  // The Scenic elevations of the PhysicalShapeLayers are monotically
423  // increasing, even though the elevations we gave them when creating them are
424  // decreasing. The two should not have any correlation; we're merely mirror
425  // the paint order using Scenic elevation.
426  //
427  // PhysicalShapeLayers created before/below a ChildView do not get their own
428  // node; PhysicalShapeLayers created afterward do.
429  //
430  // Nested PhysicalShapeLayers are collapsed.
431 
432  std::vector<fuchsia::ui::gfx::Command> expected;
433 
434  //
435  // Test root.
436  //
437  expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/1));
438  expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/2));
439  expected.push_back(scenic::NewSetLabelCmd(/*id=*/1, "fuchsia test root"));
440  expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0}));
441  expected.push_back(scenic::NewAddChildCmd(/*id=*/1, /*child_id=*/2));
442  expected.push_back(scenic::NewSetOpacityCmd(/*id=*/2, kOneMinusEpsilon));
443 
444  //
445  // Child #1: PhysicalShapeLayer
446  //
447  // Expect no new commands! Should be composited into base layer.
448 
449  //
450  // Child #2: ChildSceneLayer.
451  //
452  expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/3));
453  expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/4));
454  auto [view_token1, view_holder_token1] = scenic::ViewTokenPair::New();
455  expected.push_back(scenic::NewCreateViewHolderCmd(
456  /*id=*/5, std::move(view_holder_token1), ""));
457  expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/3));
458  expected.push_back(scenic::NewSetLabelCmd(/*id=*/4, "flutter::ViewHolder"));
459  expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/5));
460  expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/4));
461  expected.push_back(scenic::NewSetOpacityCmd(/*id=*/4, 1.f));
462  expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {1, 1, -0.1}));
463  expected.push_back(scenic::NewSetHitTestBehaviorCmd(
464  /*id=*/3, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress));
465 
466  //
467  // Child #3: PhysicalShapeLayer
468  //
469  expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/6));
470  expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/6));
471  expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/7));
472  expected.push_back(
473  scenic::NewSetLabelCmd(/*id=*/6, "flutter::PhysicalShapeLayer"));
474  expected.push_back(scenic::NewSetTranslationCmd(
475  /*id=*/6, {0, 0, -kScenicZElevationBetweenLayers}));
476  expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/7));
477  expected.push_back(scenic::NewSetOpacityCmd(/*id=*/7, kOneMinusEpsilon));
478  expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/6, /*ignored*/ {}));
479  expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/8));
480  expected.push_back(scenic::NewCreateRectangleCmd(
481  /*id=*/9, /*width=*/10, /*height=*/10));
482  expected.push_back(scenic::NewSetShapeCmd(/*id=*/8, /*shape_id=*/9));
483  expected.push_back(scenic::NewSetTranslationCmd(/*id=*/8, {5, 5, 0}));
484  expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/10));
485  expected.push_back(scenic::NewSetMaterialCmd(/*id=*/8, /*material_id=*/10));
486  expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/8));
487 
488  expected.push_back(scenic::NewCreateImageCmd(/*id=*/11, 0, 0, {}));
489  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/6));
490  expected.push_back(scenic::NewSetColorCmd(/*id=*/10, /*r*/ 255, /*g*/ 255,
491  /*b*/ 255, /*a*/ 255));
492  expected.push_back(
493  scenic::NewSetTextureCmd(/*material_id=*/10, /*texture_id=*/11));
494  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/10));
495  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/9));
496  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/8));
497  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7));
498 
499  //
500  // Grandchild (child of #3): PhysicalShapeLayer
501  //
502  // Expect no new commands! Should be composited into parent.
503 
504  //
505  // Child #4: ChildSceneLayer
506  //
507  expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/12));
508  expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/13));
509  auto [view_token2, view_holder_token2] = scenic::ViewTokenPair::New();
510  expected.push_back(scenic::NewCreateViewHolderCmd(
511  /*id=*/14, std::move(view_holder_token2), ""));
512  expected.push_back(scenic::NewAddChildCmd(/*id=*/13, /*child_id=*/12));
513  expected.push_back(scenic::NewSetLabelCmd(/*id=*/13, "flutter::ViewHolder"));
514  expected.push_back(scenic::NewAddChildCmd(/*id=*/12, /*child_id=*/14));
515  expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/13));
516  expected.push_back(scenic::NewSetOpacityCmd(/*id=*/13, 1.f));
517  expected.push_back(scenic::NewSetTranslationCmd(/*id=*/12, {1, 1, -0.1}));
518  expected.push_back(scenic::NewSetHitTestBehaviorCmd(
519  /*id=*/12, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress));
520 
521  //
522  // Child #5: PhysicalShapeLayer
523  //
524  expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/15));
525  expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/15));
526  expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/16));
527  expected.push_back(
528  scenic::NewSetLabelCmd(/*id=*/15, "flutter::PhysicalShapeLayer"));
529  expected.push_back(scenic::NewSetTranslationCmd(
530  /*id=*/15, {0, 0, -2 * kScenicZElevationBetweenLayers}));
531  expected.push_back(scenic::NewAddChildCmd(/*id=*/15, /*child_id=*/16));
532  expected.push_back(scenic::NewSetOpacityCmd(/*id=*/16, kOneMinusEpsilon));
533  expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/15, /*ignored*/ {}));
534  expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/17));
535  expected.push_back(scenic::NewCreateRectangleCmd(
536  /*id=*/18, /*width=*/10, /*height=*/10));
537  expected.push_back(scenic::NewSetShapeCmd(/*id=*/17, /*shape_id=*/18));
538  expected.push_back(scenic::NewSetTranslationCmd(/*id=*/17, {5, 5, 0}));
539  expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/19));
540  expected.push_back(scenic::NewSetMaterialCmd(/*id=*/17, /*material_id=*/19));
541  expected.push_back(scenic::NewAddChildCmd(/*id=*/15, /*child_id=*/17));
542 
543  expected.push_back(scenic::NewCreateImageCmd(/*id=*/20, 0, 0, {}));
544  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/15));
545  expected.push_back(scenic::NewSetColorCmd(/*id=*/19, /*r*/ 255, /*g*/ 255,
546  /*b*/ 255, /*a*/ 255));
547  expected.push_back(
548  scenic::NewSetTextureCmd(/*material_id=*/19, /*texture_id=*/20));
549  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/19));
550  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/18));
551  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/17));
552  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/16));
553 
554  test_context->mock_session.SetExpectedCommands(std::move(expected));
555 
556  // Finally, UpdateScene(). The MockSession will check the emitted commands
557  // against the list above.
558  root->UpdateScene(test_context->scene_update_context);
559 
560  test_context->mock_session_wrapper->Present();
561 
562  // Run loop until idle, so that the Session receives and processes
563  // its method calls.
564  async_loop_run_until_idle(
565  async_loop_from_dispatcher(async_get_default_dispatcher()));
566 
567  // Ensure we saw enough commands.
568  EXPECT_EQ(72u, test_context->mock_session.num_enqueued_commands());
569 }
570 
571 // Create a hierarchy with OpacityLayers, TransformLayer, PhysicalShapeLayers
572 // and ChildSceneLayers, and inspect the commands sent to Scenic.
573 //
574 // We are interested in verifying that the opacity values of children are
575 // correct, and the transform values as well.
576 //
577 // This test has been temporarily disabled: fxb/52028
578 TEST_F(FuchsiaLayerTest, DISABLED_OpacityAndTransformLayer) {
579  auto test_context = InitTest();
580 
581  // Root.
582  auto root = std::make_shared<ContainerLayer>();
583  SkPath path;
584  path.addRect(SkRect::MakeWH(10.f, 10.f));
585 
586  // OpacityLayer #1
587  auto opacity_layer1 =
588  std::make_shared<OpacityLayer>(127, SkPoint::Make(0, 0));
589  root->Add(opacity_layer1);
590 
591  // OpacityLayer #2
592  auto opacity_layer2 =
593  std::make_shared<OpacityLayer>(127, SkPoint::Make(0, 0));
594  opacity_layer1->Add(opacity_layer2);
595 
596  // TransformLayer
597  SkMatrix translate_and_scale;
598  translate_and_scale.setScaleTranslate(1.1f, 1.1f, 2.f, 2.f);
599  auto transform_layer = std::make_shared<TransformLayer>(translate_and_scale);
600  opacity_layer2->Add(transform_layer);
601 
602  // TransformLayer Child #1: ChildSceneLayer.
603  const zx_koid_t kChildLayerId1 = GetChildLayerId();
604  auto [unused_view_token1, unused_view_holder_token1] =
605  scenic::ViewTokenPair::New();
606 
607  ViewHolder::Create(kChildLayerId1, test_context->task_runner,
608  std::move(unused_view_holder_token1),
609  /*bind_callback=*/[](scenic::ResourceId id) {});
610  // Will destroy only when we go out of scope (i.e. end of the test).
611  AutoDestroyChildLayerId auto_destroy1(kChildLayerId1);
612  auto child_view1 = std::make_shared<ChildSceneLayer>(
613  kChildLayerId1, SkPoint::Make(1, 1), SkSize::Make(10, 10),
614  /*hit_testable=*/false);
615  transform_layer->Add(child_view1);
616 
617  // TransformLayer Child #2: PhysicalShapeLayer.
618  auto physical_shape1 = std::make_shared<PhysicalShapeLayer>(
619  /*color=*/SK_ColorCYAN,
620  /*shadow_color=*/SK_ColorBLACK,
621  /*elevation*/ 23.f, path, Clip::antiAlias);
622  transform_layer->Add(physical_shape1);
623 
624  // Preroll.
625  root->Preroll(test_context->preroll_context.get(), SkMatrix());
626 
627  // Create another frame to be the "real" root. Required because
628  // UpdateScene() traversal expects there to already be a top node.
629  SceneUpdateContext::Frame frame(test_context->scene_update_context,
630  SkRRect::MakeRect(SkRect::MakeWH(100, 100)),
631  SK_ColorTRANSPARENT, SK_AlphaOPAQUE,
632  "fuchsia test root");
633 
634  // Submit the list of command we will expect Scenic to see.
635  //
636  // We are interested in verifying that the opacity values of children are
637  // correct.
638 
639  std::vector<fuchsia::ui::gfx::Command> expected;
640 
641  //
642  // Test root.
643  //
644  expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/1));
645  expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/2));
646  expected.push_back(scenic::NewSetLabelCmd(/*id=*/1, "fuchsia test root"));
647  expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0, 0}));
648  expected.push_back(scenic::NewAddChildCmd(/*id=*/1, /*child_id=*/2));
649  expected.push_back(scenic::NewSetOpacityCmd(/*id=*/2, kOneMinusEpsilon));
650 
651  //
652  // OpacityLayer #1
653  //
654  // Expect no new commands for this.
655 
656  //
657  // OpacityLayer #2
658  //
659  // Expect no new commands for this.
660 
661  //
662  // TransformLayer
663  //
664  //
665  expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/3));
666  expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/3));
667  expected.push_back(scenic::NewSetLabelCmd(/*id=*/3, "flutter::Transform"));
668  expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {2.f, 2.f, 0.f}));
669  expected.push_back(scenic::NewSetScaleCmd(/*id=*/3, {1.1f, 1.1f, 1.f}));
670  expected.push_back(scenic::NewSetRotationCmd(/*id=*/3, {0.f, 0.f, 0.f, 1.f}));
671 
672  //
673  // TransformLayer Child #1: ChildSceneLayer.
674  //
675  expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/4));
676  expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/5));
677  auto [view_token1, view_holder_token1] = scenic::ViewTokenPair::New();
678  expected.push_back(scenic::NewCreateViewHolderCmd(
679  /*id=*/6, std::move(view_holder_token1), ""));
680  expected.push_back(scenic::NewAddChildCmd(/*id=*/5, /*child_id=*/4));
681  expected.push_back(scenic::NewSetLabelCmd(/*id=*/5, "flutter::ViewHolder"));
682  expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/6));
683  expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/5));
684 
685  // Check opacity value. Extra rounding required because we pass alpha as
686  // a uint/SkAlpha to SceneUpdateContext::Frame.
687  float opacity1 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f);
688  opacity1 = SkScalarRoundToInt(opacity1 * 255) / 255.f;
689  expected.push_back(scenic::NewSetOpacityCmd(/*id=*/5, opacity1));
690  expected.push_back(scenic::NewSetTranslationCmd(/*id=*/4, {1, 1, -0.1}));
691  expected.push_back(scenic::NewSetHitTestBehaviorCmd(
692  /*id=*/4, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress));
693 
694  //
695  // TransformLayer Child #2: PhysicalShapeLayer
696  //
697  expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/7));
698  expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/7));
699  expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/8));
700  expected.push_back(
701  scenic::NewSetLabelCmd(/*id=*/7, "flutter::PhysicalShapeLayer"));
702  expected.push_back(scenic::NewSetTranslationCmd(
703  /*id=*/7, {0, 0, -kScenicZElevationBetweenLayers}));
704  expected.push_back(scenic::NewAddChildCmd(/*id=*/7, /*child_id=*/8));
705 
706  // Check opacity value. Extra rounding required because we pass alpha as
707  // a uint/SkAlpha to SceneUpdateContext::Frame.
708  float opacity2 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f);
709  opacity2 = SkScalarRoundToInt(opacity2 * 255) / 255.f;
710  expected.push_back(scenic::NewSetOpacityCmd(/*id=*/8, opacity2));
711  expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/7, /*ignored*/ {}));
712  expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/9));
713  expected.push_back(scenic::NewCreateRectangleCmd(
714  /*id=*/10, /*width=*/10, /*height=*/10));
715  expected.push_back(scenic::NewSetShapeCmd(/*id=*/9, /*shape_id=*/10));
716  expected.push_back(scenic::NewSetTranslationCmd(/*id=*/9, {5, 5, 0}));
717  expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/11));
718  expected.push_back(scenic::NewSetMaterialCmd(/*id=*/9,
719  /*material_id=*/11));
720  expected.push_back(scenic::NewAddChildCmd(/*id=*/7,
721  /*child_id=*/9));
722 
723  expected.push_back(scenic::NewCreateImageCmd(/*id=*/12, 0, 0, {}));
724  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7));
725  expected.push_back(scenic::NewSetColorCmd(/*id=*/11, /*r*/ 255,
726  /*g*/ 255,
727  /*b*/ 255, /*a*/ 63));
728  expected.push_back(
729  scenic::NewSetTextureCmd(/*material_id=*/11, /*texture_id=*/12));
730  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/11));
731  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/10));
732  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/9));
733  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/8));
734  expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/3));
735 
736  test_context->mock_session.SetExpectedCommands(std::move(expected));
737 
738  // Finally, UpdateScene(). The MockSession will check the emitted
739  // commands against the list above.
740  root->UpdateScene(test_context->scene_update_context);
741 
742  test_context->mock_session_wrapper->Present();
743 
744  // Run loop until idle, so that the Session receives and processes
745  // its method calls.
746  async_loop_run_until_idle(
747  async_loop_from_dispatcher(async_get_default_dispatcher()));
748 
749  // Ensure we saw enough commands.
750  EXPECT_EQ(46u, test_context->mock_session.num_enqueued_commands());
751 }
752 
753 } // namespace testing
754 } // namespace flutter
MockSessionWrapper(fuchsia::ui::scenic::SessionPtr session_ptr)
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
void NotImplemented_(const std::string &name) final
std::unique_ptr< TestContext > InitTest()
fidl::InterfaceRequest< fuchsia::ui::scenic::SessionListener > listener_request
constexpr float kOneMinusEpsilon
void SetExpectedCommands(std::vector< fuchsia::ui::gfx::Command > gfx_cmds)
static std::string QuaternionValueToString(fuchsia::ui::gfx::QuaternionValue value)
void Bind(fidl::InterfaceRequest<::fuchsia::ui::scenic::Session > request, ::fuchsia::ui::scenic::SessionListenerPtr listener)
Definition: ref_ptr.h:252
void Enqueue(std::vector< fuchsia::ui::scenic::Command > cmds) override
static std::string GfxCreateResourceCmdToString(const fuchsia::ui::gfx::CreateResourceCmd &cmd)
std::shared_ptr< SceneUpdateContext > scene_update_context
std::unique_ptr< MockSessionWrapper > mock_session_wrapper
static std::string ScenicCmdToString(const fuchsia::ui::scenic::Command &cmd)
fml::RefPtr< fml::TaskRunner > task_runner
static constexpr SkRect kGiantRect
Definition: layer.h:38
static std::string Vec3ValueToString(fuchsia::ui::gfx::Vector3Value value)
static std::string GfxCmdToString(const fuchsia::ui::gfx::Command &cmd)
uint8_t value
::testing::Test FuchsiaLayerTest
constexpr float kScenicZElevationBetweenLayers
std::unique_ptr< PrerollContext > preroll_context
fml::RefPtr< fml::MessageLoopFuchsia > loop
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies)
const char * name
Definition: fuchsia.cc:50
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
int32_t id