mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
* Reland fuchsia external view embedder will be shared with platform view This reverts commit 9b752790f4d25ab238576c719208d7a38c6293f3. * wait for the external view embedder to be initialized before creating shell
755 lines
32 KiB
C++
755 lines
32 KiB
C++
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <fuchsia/ui/scenic/cpp/fidl.h>
|
|
#include <fuchsia/ui/scenic/cpp/fidl_test_base.h>
|
|
#include <lib/async-loop/cpp/loop.h>
|
|
#include <lib/async/default.h>
|
|
#include <lib/fidl/cpp/optional.h>
|
|
#include <lib/sys/cpp/component_context.h>
|
|
#include <lib/ui/scenic/cpp/commands.h>
|
|
#include <lib/ui/scenic/cpp/id.h>
|
|
#include <lib/ui/scenic/cpp/view_token_pair.h>
|
|
|
|
#include <deque>
|
|
|
|
#include "flutter/flow/layers/child_scene_layer.h"
|
|
#include "flutter/flow/layers/container_layer.h"
|
|
#include "flutter/flow/layers/opacity_layer.h"
|
|
#include "flutter/flow/layers/physical_shape_layer.h"
|
|
#include "flutter/flow/layers/transform_layer.h"
|
|
#include "flutter/flow/view_holder.h"
|
|
#include "flutter/fml/platform/fuchsia/message_loop_fuchsia.h"
|
|
#include "flutter/fml/task_runner.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace flutter {
|
|
namespace testing {
|
|
|
|
using FuchsiaLayerTest = ::testing::Test;
|
|
|
|
class MockSession : public fuchsia::ui::scenic::testing::Session_TestBase {
|
|
public:
|
|
MockSession() : binding_(this) {}
|
|
|
|
void NotImplemented_(const std::string& name) final {}
|
|
|
|
void Bind(fidl::InterfaceRequest<::fuchsia::ui::scenic::Session> request,
|
|
::fuchsia::ui::scenic::SessionListenerPtr listener) {
|
|
binding_.Bind(std::move(request));
|
|
listener_ = std::move(listener);
|
|
}
|
|
|
|
static std::string Vec3ValueToString(fuchsia::ui::gfx::Vector3Value value) {
|
|
return "{" + std::to_string(value.value.x) + ", " +
|
|
std::to_string(value.value.y) + ", " +
|
|
std::to_string(value.value.z) + "}";
|
|
}
|
|
|
|
static std::string QuaternionValueToString(
|
|
fuchsia::ui::gfx::QuaternionValue value) {
|
|
return "{" + std::to_string(value.value.x) + ", " +
|
|
std::to_string(value.value.y) + ", " +
|
|
std::to_string(value.value.z) + ", " +
|
|
std::to_string(value.value.w) + "}";
|
|
}
|
|
|
|
static std::string GfxCreateResourceCmdToString(
|
|
const fuchsia::ui::gfx::CreateResourceCmd& cmd) {
|
|
std::string id = " id: " + std::to_string(cmd.id);
|
|
switch (cmd.resource.Which()) {
|
|
case fuchsia::ui::gfx::ResourceArgs::Tag::kRectangle:
|
|
return "Rectangle" + id;
|
|
case fuchsia::ui::gfx::ResourceArgs::Tag::kRoundedRectangle:
|
|
return "RoundedRectangle" + id;
|
|
case fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder:
|
|
return "ViewHolder" + id;
|
|
case fuchsia::ui::gfx::ResourceArgs::Tag::kOpacityNode:
|
|
return "OpacityNode" + id;
|
|
case fuchsia::ui::gfx::ResourceArgs::Tag::kEntityNode:
|
|
return "EntityNode" + id;
|
|
case fuchsia::ui::gfx::ResourceArgs::Tag::kShapeNode:
|
|
return "ShapeNode" + id;
|
|
case fuchsia::ui::gfx::ResourceArgs::Tag::kMaterial:
|
|
return "Material" + id;
|
|
case fuchsia::ui::gfx::ResourceArgs::Tag::kImage:
|
|
return "Image" + id + ", memory_id: " +
|
|
std::to_string(cmd.resource.image().memory_id) +
|
|
", memory_offset: " +
|
|
std::to_string(cmd.resource.image().memory_offset);
|
|
default:
|
|
return "Unhandled CreateResource command" +
|
|
std::to_string(cmd.resource.Which());
|
|
}
|
|
}
|
|
|
|
static std::string GfxCmdToString(const fuchsia::ui::gfx::Command& cmd) {
|
|
switch (cmd.Which()) {
|
|
case fuchsia::ui::gfx::Command::Tag::kCreateResource:
|
|
return "CreateResource: " +
|
|
GfxCreateResourceCmdToString(cmd.create_resource());
|
|
case fuchsia::ui::gfx::Command::Tag::kReleaseResource:
|
|
return "ReleaseResource id: " +
|
|
std::to_string(cmd.release_resource().id);
|
|
case fuchsia::ui::gfx::Command::Tag::kAddChild:
|
|
return "AddChild id: " + std::to_string(cmd.add_child().node_id) +
|
|
" child_id: " + std::to_string(cmd.add_child().child_id);
|
|
case fuchsia::ui::gfx::Command::Tag::kSetTranslation:
|
|
return "SetTranslation id: " +
|
|
std::to_string(cmd.set_translation().id) +
|
|
" value: " + Vec3ValueToString(cmd.set_translation().value);
|
|
case fuchsia::ui::gfx::Command::Tag::kSetScale:
|
|
return "SetScale id: " + std::to_string(cmd.set_scale().id) +
|
|
" value: " + Vec3ValueToString(cmd.set_scale().value);
|
|
case fuchsia::ui::gfx::Command::Tag::kSetRotation:
|
|
return "SetRotation id: " + std::to_string(cmd.set_rotation().id) +
|
|
" value: " + QuaternionValueToString(cmd.set_rotation().value);
|
|
case fuchsia::ui::gfx::Command::Tag::kSetOpacity:
|
|
return "SetOpacity id: " + std::to_string(cmd.set_opacity().node_id) +
|
|
", opacity: " + std::to_string(cmd.set_opacity().opacity);
|
|
case fuchsia::ui::gfx::Command::Tag::kSetColor:
|
|
return "SetColor id: " + std::to_string(cmd.set_color().material_id) +
|
|
", rgba: (" + std::to_string(cmd.set_color().color.value.red) +
|
|
", " + std::to_string(cmd.set_color().color.value.green) + ", " +
|
|
std::to_string(cmd.set_color().color.value.blue) + ", " +
|
|
std::to_string(cmd.set_color().color.value.alpha) + ")";
|
|
case fuchsia::ui::gfx::Command::Tag::kSetLabel:
|
|
return "SetLabel id: " + std::to_string(cmd.set_label().id) + " " +
|
|
cmd.set_label().label;
|
|
case fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior:
|
|
return "SetHitTestBehavior node_id: " +
|
|
std::to_string(cmd.set_hit_test_behavior().node_id);
|
|
case fuchsia::ui::gfx::Command::Tag::kSetClipPlanes:
|
|
return "SetClipPlanes node_id: " +
|
|
std::to_string(cmd.set_clip_planes().node_id);
|
|
case fuchsia::ui::gfx::Command::Tag::kSetShape:
|
|
return "SetShape node_id: " + std::to_string(cmd.set_shape().node_id) +
|
|
", shape_id: " + std::to_string(cmd.set_shape().shape_id);
|
|
case fuchsia::ui::gfx::Command::Tag::kSetMaterial:
|
|
return "SetMaterial node_id: " +
|
|
std::to_string(cmd.set_material().node_id) + ", material_id: " +
|
|
std::to_string(cmd.set_material().material_id);
|
|
case fuchsia::ui::gfx::Command::Tag::kSetTexture:
|
|
return "SetTexture material_id: " +
|
|
std::to_string(cmd.set_texture().material_id) +
|
|
", texture_id: " + std::to_string(cmd.set_texture().texture_id);
|
|
|
|
default:
|
|
return "Unhandled gfx command" + std::to_string(cmd.Which());
|
|
}
|
|
}
|
|
|
|
static std::string ScenicCmdToString(
|
|
const fuchsia::ui::scenic::Command& cmd) {
|
|
if (cmd.Which() != fuchsia::ui::scenic::Command::Tag::kGfx) {
|
|
return "Unhandled non-gfx command: " + std::to_string(cmd.Which());
|
|
}
|
|
return GfxCmdToString(cmd.gfx());
|
|
}
|
|
|
|
// |fuchsia::ui::scenic::Session|
|
|
void Enqueue(std::vector<fuchsia::ui::scenic::Command> cmds) override {
|
|
for (const auto& cmd : cmds) {
|
|
num_enqueued_commands_++;
|
|
EXPECT_FALSE(expected_.empty())
|
|
<< "Received more commands than expected; command: <"
|
|
<< ScenicCmdToString(cmd)
|
|
<< ">, num_enqueued_commands: " << num_enqueued_commands_;
|
|
if (!expected_.empty()) {
|
|
EXPECT_TRUE(AreCommandsEqual(expected_.front(), cmd))
|
|
<< "actual command: <" << ScenicCmdToString(cmd)
|
|
<< ">, expected command: <" << ScenicCmdToString(expected_.front())
|
|
<< ">, num_enqueued_commands: " << num_enqueued_commands_;
|
|
expected_.pop_front();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetExpectedCommands(std::vector<fuchsia::ui::gfx::Command> gfx_cmds) {
|
|
std::deque<fuchsia::ui::scenic::Command> scenic_commands;
|
|
for (auto it = gfx_cmds.begin(); it != gfx_cmds.end(); it++) {
|
|
scenic_commands.push_back(scenic::NewCommand(std::move((*it))));
|
|
}
|
|
expected_ = std::move(scenic_commands);
|
|
num_enqueued_commands_ = 0;
|
|
}
|
|
|
|
size_t num_enqueued_commands() { return num_enqueued_commands_; }
|
|
|
|
private:
|
|
static bool IsGfxCommand(const fuchsia::ui::scenic::Command& cmd,
|
|
fuchsia::ui::gfx::Command::Tag tag) {
|
|
return cmd.Which() == fuchsia::ui::scenic::Command::Tag::kGfx &&
|
|
cmd.gfx().Which() == tag;
|
|
}
|
|
|
|
static bool IsCreateResourceCommand(const fuchsia::ui::scenic::Command& cmd,
|
|
fuchsia::ui::gfx::ResourceArgs::Tag tag) {
|
|
return IsGfxCommand(cmd, fuchsia::ui::gfx::Command::Tag::kCreateResource) &&
|
|
cmd.gfx().create_resource().resource.Which() == tag;
|
|
}
|
|
|
|
static bool AreCommandsEqual(const fuchsia::ui::scenic::Command& command1,
|
|
const fuchsia::ui::scenic::Command& command2) {
|
|
// For CreateViewHolderCommand, just compare the id and ignore the
|
|
// view_holder_token.
|
|
if (IsCreateResourceCommand(
|
|
command1, fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder)) {
|
|
return IsCreateResourceCommand(
|
|
command2, fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder) &&
|
|
command1.gfx().create_resource().id ==
|
|
command2.gfx().create_resource().id;
|
|
}
|
|
// For CreateImageCommand, just compare the id and memory_id.
|
|
if (IsCreateResourceCommand(command1,
|
|
fuchsia::ui::gfx::ResourceArgs::Tag::kImage)) {
|
|
return IsCreateResourceCommand(
|
|
command2, fuchsia::ui::gfx::ResourceArgs::Tag::kImage) &&
|
|
command1.gfx().create_resource().id ==
|
|
command2.gfx().create_resource().id &&
|
|
command1.gfx().create_resource().resource.image().memory_id ==
|
|
command2.gfx().create_resource().resource.image().memory_id;
|
|
}
|
|
// For SetHitTestBehaviorCommand, just compare the node_id.
|
|
if (IsGfxCommand(command1,
|
|
fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior)) {
|
|
return IsGfxCommand(
|
|
command2,
|
|
fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior) &&
|
|
command1.gfx().set_hit_test_behavior().node_id ==
|
|
command2.gfx().set_hit_test_behavior().node_id;
|
|
}
|
|
// For SetHitTestBehaviorCommand, just compare the node_id.
|
|
if (IsGfxCommand(command1,
|
|
fuchsia::ui::gfx::Command::Tag::kSetClipPlanes)) {
|
|
return IsGfxCommand(command2,
|
|
fuchsia::ui::gfx::Command::Tag::kSetClipPlanes) &&
|
|
command1.gfx().set_clip_planes().node_id ==
|
|
command2.gfx().set_clip_planes().node_id;
|
|
}
|
|
return fidl::Equals(command1, command2);
|
|
}
|
|
|
|
std::deque<fuchsia::ui::scenic::Command> expected_;
|
|
size_t num_enqueued_commands_ = 0;
|
|
fidl::Binding<fuchsia::ui::scenic::Session> binding_;
|
|
fuchsia::ui::scenic::SessionListenerPtr listener_;
|
|
};
|
|
|
|
class MockSessionWrapper : public flutter::SessionWrapper {
|
|
public:
|
|
MockSessionWrapper(fuchsia::ui::scenic::SessionPtr session_ptr)
|
|
: session_(std::move(session_ptr)) {}
|
|
~MockSessionWrapper() override = default;
|
|
|
|
scenic::Session* get() override { return &session_; }
|
|
void Present() override { session_.Flush(); }
|
|
|
|
private:
|
|
scenic::Session session_;
|
|
};
|
|
|
|
struct TestContext {
|
|
// Message loop.
|
|
fml::RefPtr<fml::MessageLoopFuchsia> loop;
|
|
fml::RefPtr<fml::TaskRunner> task_runner;
|
|
|
|
// Session.
|
|
fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener> listener_request;
|
|
MockSession mock_session;
|
|
std::unique_ptr<MockSessionWrapper> mock_session_wrapper;
|
|
|
|
// SceneUpdateContext.
|
|
std::shared_ptr<SceneUpdateContext> scene_update_context;
|
|
|
|
// PrerollContext.
|
|
MutatorsStack unused_stack;
|
|
const Stopwatch unused_stopwatch;
|
|
TextureRegistry unused_texture_registry;
|
|
std::unique_ptr<PrerollContext> preroll_context;
|
|
};
|
|
|
|
std::unique_ptr<TestContext> InitTest() {
|
|
std::unique_ptr<TestContext> context = std::make_unique<TestContext>();
|
|
|
|
// Init message loop.
|
|
context->loop = fml::MakeRefCounted<fml::MessageLoopFuchsia>();
|
|
context->task_runner = fml::MakeRefCounted<fml::TaskRunner>(context->loop);
|
|
|
|
// Init Session.
|
|
fuchsia::ui::scenic::SessionPtr session_ptr;
|
|
fuchsia::ui::scenic::SessionListenerPtr listener;
|
|
context->listener_request = listener.NewRequest();
|
|
context->mock_session.Bind(session_ptr.NewRequest(), std::move(listener));
|
|
context->mock_session_wrapper =
|
|
std::make_unique<MockSessionWrapper>(std::move(session_ptr));
|
|
|
|
// Init SceneUpdateContext.
|
|
context->scene_update_context = std::make_shared<SceneUpdateContext>(
|
|
"fuchsia_layer_unittest", fuchsia::ui::views::ViewToken(),
|
|
scenic::ViewRefPair::New(), *(context->mock_session_wrapper));
|
|
|
|
// Init PrerollContext.
|
|
context->preroll_context = std::unique_ptr<PrerollContext>(new PrerollContext{
|
|
nullptr, // raster_cache (don't consult the cache)
|
|
nullptr, // gr_context (used for the raster cache)
|
|
nullptr, // external view embedder
|
|
context->unused_stack, // mutator stack
|
|
nullptr, // SkColorSpace* dst_color_space
|
|
kGiantRect, // SkRect cull_rect
|
|
false, // layer reads from surface
|
|
context->unused_stopwatch, // frame time (dont care)
|
|
context->unused_stopwatch, // engine time (dont care)
|
|
context->unused_texture_registry, // texture registry (not
|
|
// supported)
|
|
false, // checkerboard_offscreen_layers
|
|
1.f // ratio between logical and physical
|
|
});
|
|
|
|
return context;
|
|
}
|
|
|
|
zx_koid_t GetChildLayerId() {
|
|
static zx_koid_t sChildLayerId = 17324;
|
|
return sChildLayerId++;
|
|
}
|
|
|
|
class AutoDestroyChildLayerId {
|
|
public:
|
|
AutoDestroyChildLayerId(zx_koid_t id) : id_(id) {}
|
|
~AutoDestroyChildLayerId() { ViewHolder::Destroy(id_); }
|
|
|
|
private:
|
|
zx_koid_t id_;
|
|
};
|
|
|
|
// Create a hierarchy with PhysicalShapeLayers and ChildSceneLayers, and
|
|
// inspect the commands sent to Scenic.
|
|
//
|
|
//
|
|
// What we expect:
|
|
//
|
|
// The Scenic elevations of the PhysicalShapeLayers are monotically
|
|
// increasing, even though the elevations we gave them when creating them are
|
|
// decreasing. The two should not have any correlation; we're merely mirror
|
|
// the paint order using Scenic elevation.
|
|
//
|
|
// PhysicalShapeLayers created before/below a ChildView do not get their own
|
|
// node; PhysicalShapeLayers created afterward do.
|
|
//
|
|
// Nested PhysicalShapeLayers are collapsed.
|
|
//
|
|
// This test has been temporarily disabled: fxb/52028
|
|
TEST_F(FuchsiaLayerTest, DISABLED_PhysicalShapeLayersAndChildSceneLayers) {
|
|
auto test_context = InitTest();
|
|
|
|
// Root.
|
|
auto root = std::make_shared<ContainerLayer>();
|
|
SkPath path;
|
|
path.addRect(SkRect::MakeWH(10.f, 10.f));
|
|
|
|
// Child #1: PhysicalShapeLayer.
|
|
auto physical_shape1 = std::make_shared<PhysicalShapeLayer>(
|
|
/*color=*/SK_ColorCYAN,
|
|
/*shadow_color=*/SK_ColorBLACK,
|
|
/*elevation*/ 23.f, path, Clip::antiAlias);
|
|
root->Add(physical_shape1);
|
|
|
|
// Child #2: ChildSceneLayer.
|
|
const zx_koid_t kChildLayerId1 = GetChildLayerId();
|
|
auto [unused_view_token1, unused_view_holder_token1] =
|
|
scenic::ViewTokenPair::New();
|
|
ViewHolder::Create(kChildLayerId1, test_context->task_runner,
|
|
std::move(unused_view_holder_token1),
|
|
/*bind_callback=*/[](scenic::ResourceId id) {});
|
|
// Will destroy only when we go out of scope (i.e. end of the test).
|
|
AutoDestroyChildLayerId auto_destroy1(kChildLayerId1);
|
|
auto child_view1 = std::make_shared<ChildSceneLayer>(
|
|
kChildLayerId1, SkPoint::Make(1, 1), SkSize::Make(10, 10),
|
|
/*hit_testable=*/false);
|
|
root->Add(child_view1);
|
|
|
|
// Child #3: PhysicalShapeLayer
|
|
auto physical_shape2 = std::make_shared<PhysicalShapeLayer>(
|
|
/*color=*/SK_ColorCYAN,
|
|
/*shadow_color=*/SK_ColorBLACK,
|
|
/*elevation*/ 21.f, path, Clip::antiAlias);
|
|
root->Add(physical_shape2);
|
|
|
|
// Grandchild (child of #3): PhysicalShapeLayer
|
|
auto physical_shape3 = std::make_shared<PhysicalShapeLayer>(
|
|
/*color=*/SK_ColorCYAN,
|
|
/*shadow_color=*/SK_ColorBLACK,
|
|
/*elevation*/ 19.f, path, Clip::antiAlias);
|
|
physical_shape2->Add(physical_shape3);
|
|
|
|
// Child #4: ChildSceneLayer
|
|
const zx_koid_t kChildLayerId2 = GetChildLayerId();
|
|
auto [unused_view_token2, unused_view_holder_token2] =
|
|
scenic::ViewTokenPair::New();
|
|
ViewHolder::Create(kChildLayerId2, test_context->task_runner,
|
|
std::move(unused_view_holder_token2),
|
|
/*bind_callback=*/[](scenic::ResourceId id) {});
|
|
// Will destroy only when we go out of scope (i.e. end of the test).
|
|
AutoDestroyChildLayerId auto_destroy2(kChildLayerId2);
|
|
auto child_view2 = std::make_shared<ChildSceneLayer>(
|
|
kChildLayerId2, SkPoint::Make(1, 1), SkSize::Make(10, 10),
|
|
/*hit_testable=*/false);
|
|
root->Add(child_view2);
|
|
|
|
// Child #5: PhysicalShapeLayer
|
|
auto physical_shape4 = std::make_shared<PhysicalShapeLayer>(
|
|
/*color=*/SK_ColorCYAN,
|
|
/*shadow_color=*/SK_ColorBLACK,
|
|
/*elevation*/ 17.f, path, Clip::antiAlias);
|
|
root->Add(physical_shape4);
|
|
|
|
// Preroll.
|
|
root->Preroll(test_context->preroll_context.get(), SkMatrix());
|
|
|
|
// Create another frame to be the "real" root. Required because
|
|
// UpdateScene() traversal expects there to already be a top node.
|
|
SceneUpdateContext::Frame frame(test_context->scene_update_context,
|
|
SkRRect::MakeRect(SkRect::MakeWH(100, 100)),
|
|
SK_ColorTRANSPARENT, SK_AlphaOPAQUE,
|
|
"fuchsia test root");
|
|
|
|
// Submit the list of command we will expect Scenic to see.
|
|
//
|
|
// Some things we expect:
|
|
//
|
|
// The Scenic elevations of the PhysicalShapeLayers are monotically
|
|
// increasing, even though the elevations we gave them when creating them are
|
|
// decreasing. The two should not have any correlation; we're merely mirror
|
|
// the paint order using Scenic elevation.
|
|
//
|
|
// PhysicalShapeLayers created before/below a ChildView do not get their own
|
|
// node; PhysicalShapeLayers created afterward do.
|
|
//
|
|
// Nested PhysicalShapeLayers are collapsed.
|
|
|
|
std::vector<fuchsia::ui::gfx::Command> expected;
|
|
|
|
//
|
|
// Test root.
|
|
//
|
|
expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/1));
|
|
expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/2));
|
|
expected.push_back(scenic::NewSetLabelCmd(/*id=*/1, "fuchsia test root"));
|
|
expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0}));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/1, /*child_id=*/2));
|
|
expected.push_back(scenic::NewSetOpacityCmd(/*id=*/2, kOneMinusEpsilon));
|
|
|
|
//
|
|
// Child #1: PhysicalShapeLayer
|
|
//
|
|
// Expect no new commands! Should be composited into base layer.
|
|
|
|
//
|
|
// Child #2: ChildSceneLayer.
|
|
//
|
|
expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/3));
|
|
expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/4));
|
|
auto [view_token1, view_holder_token1] = scenic::ViewTokenPair::New();
|
|
expected.push_back(scenic::NewCreateViewHolderCmd(
|
|
/*id=*/5, std::move(view_holder_token1), ""));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/3));
|
|
expected.push_back(scenic::NewSetLabelCmd(/*id=*/4, "flutter::ViewHolder"));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/5));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/4));
|
|
expected.push_back(scenic::NewSetOpacityCmd(/*id=*/4, 1.f));
|
|
expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {1, 1, -0.1}));
|
|
expected.push_back(scenic::NewSetHitTestBehaviorCmd(
|
|
/*id=*/3, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress));
|
|
|
|
//
|
|
// Child #3: PhysicalShapeLayer
|
|
//
|
|
expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/6));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/6));
|
|
expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/7));
|
|
expected.push_back(
|
|
scenic::NewSetLabelCmd(/*id=*/6, "flutter::PhysicalShapeLayer"));
|
|
expected.push_back(scenic::NewSetTranslationCmd(
|
|
/*id=*/6, {0, 0, -kScenicZElevationBetweenLayers}));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/7));
|
|
expected.push_back(scenic::NewSetOpacityCmd(/*id=*/7, kOneMinusEpsilon));
|
|
expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/6, /*ignored*/ {}));
|
|
expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/8));
|
|
expected.push_back(scenic::NewCreateRectangleCmd(
|
|
/*id=*/9, /*width=*/10, /*height=*/10));
|
|
expected.push_back(scenic::NewSetShapeCmd(/*id=*/8, /*shape_id=*/9));
|
|
expected.push_back(scenic::NewSetTranslationCmd(/*id=*/8, {5, 5, 0}));
|
|
expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/10));
|
|
expected.push_back(scenic::NewSetMaterialCmd(/*id=*/8, /*material_id=*/10));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/8));
|
|
|
|
expected.push_back(scenic::NewCreateImageCmd(/*id=*/11, 0, 0, {}));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/6));
|
|
expected.push_back(scenic::NewSetColorCmd(/*id=*/10, /*r*/ 255, /*g*/ 255,
|
|
/*b*/ 255, /*a*/ 255));
|
|
expected.push_back(
|
|
scenic::NewSetTextureCmd(/*material_id=*/10, /*texture_id=*/11));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/10));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/9));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/8));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7));
|
|
|
|
//
|
|
// Grandchild (child of #3): PhysicalShapeLayer
|
|
//
|
|
// Expect no new commands! Should be composited into parent.
|
|
|
|
//
|
|
// Child #4: ChildSceneLayer
|
|
//
|
|
expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/12));
|
|
expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/13));
|
|
auto [view_token2, view_holder_token2] = scenic::ViewTokenPair::New();
|
|
expected.push_back(scenic::NewCreateViewHolderCmd(
|
|
/*id=*/14, std::move(view_holder_token2), ""));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/13, /*child_id=*/12));
|
|
expected.push_back(scenic::NewSetLabelCmd(/*id=*/13, "flutter::ViewHolder"));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/12, /*child_id=*/14));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/13));
|
|
expected.push_back(scenic::NewSetOpacityCmd(/*id=*/13, 1.f));
|
|
expected.push_back(scenic::NewSetTranslationCmd(/*id=*/12, {1, 1, -0.1}));
|
|
expected.push_back(scenic::NewSetHitTestBehaviorCmd(
|
|
/*id=*/12, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress));
|
|
|
|
//
|
|
// Child #5: PhysicalShapeLayer
|
|
//
|
|
expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/15));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/15));
|
|
expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/16));
|
|
expected.push_back(
|
|
scenic::NewSetLabelCmd(/*id=*/15, "flutter::PhysicalShapeLayer"));
|
|
expected.push_back(scenic::NewSetTranslationCmd(
|
|
/*id=*/15, {0, 0, -2 * kScenicZElevationBetweenLayers}));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/15, /*child_id=*/16));
|
|
expected.push_back(scenic::NewSetOpacityCmd(/*id=*/16, kOneMinusEpsilon));
|
|
expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/15, /*ignored*/ {}));
|
|
expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/17));
|
|
expected.push_back(scenic::NewCreateRectangleCmd(
|
|
/*id=*/18, /*width=*/10, /*height=*/10));
|
|
expected.push_back(scenic::NewSetShapeCmd(/*id=*/17, /*shape_id=*/18));
|
|
expected.push_back(scenic::NewSetTranslationCmd(/*id=*/17, {5, 5, 0}));
|
|
expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/19));
|
|
expected.push_back(scenic::NewSetMaterialCmd(/*id=*/17, /*material_id=*/19));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/15, /*child_id=*/17));
|
|
|
|
expected.push_back(scenic::NewCreateImageCmd(/*id=*/20, 0, 0, {}));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/15));
|
|
expected.push_back(scenic::NewSetColorCmd(/*id=*/19, /*r*/ 255, /*g*/ 255,
|
|
/*b*/ 255, /*a*/ 255));
|
|
expected.push_back(
|
|
scenic::NewSetTextureCmd(/*material_id=*/19, /*texture_id=*/20));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/19));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/18));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/17));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/16));
|
|
|
|
test_context->mock_session.SetExpectedCommands(std::move(expected));
|
|
|
|
// Finally, UpdateScene(). The MockSession will check the emitted commands
|
|
// against the list above.
|
|
root->UpdateScene(test_context->scene_update_context);
|
|
|
|
test_context->mock_session_wrapper->Present();
|
|
|
|
// Run loop until idle, so that the Session receives and processes
|
|
// its method calls.
|
|
async_loop_run_until_idle(
|
|
async_loop_from_dispatcher(async_get_default_dispatcher()));
|
|
|
|
// Ensure we saw enough commands.
|
|
EXPECT_EQ(72u, test_context->mock_session.num_enqueued_commands());
|
|
}
|
|
|
|
// Create a hierarchy with OpacityLayers, TransformLayer, PhysicalShapeLayers
|
|
// and ChildSceneLayers, and inspect the commands sent to Scenic.
|
|
//
|
|
// We are interested in verifying that the opacity values of children are
|
|
// correct, and the transform values as well.
|
|
//
|
|
// This test has been temporarily disabled: fxb/52028
|
|
TEST_F(FuchsiaLayerTest, DISABLED_OpacityAndTransformLayer) {
|
|
auto test_context = InitTest();
|
|
|
|
// Root.
|
|
auto root = std::make_shared<ContainerLayer>();
|
|
SkPath path;
|
|
path.addRect(SkRect::MakeWH(10.f, 10.f));
|
|
|
|
// OpacityLayer #1
|
|
auto opacity_layer1 =
|
|
std::make_shared<OpacityLayer>(127, SkPoint::Make(0, 0));
|
|
root->Add(opacity_layer1);
|
|
|
|
// OpacityLayer #2
|
|
auto opacity_layer2 =
|
|
std::make_shared<OpacityLayer>(127, SkPoint::Make(0, 0));
|
|
opacity_layer1->Add(opacity_layer2);
|
|
|
|
// TransformLayer
|
|
SkMatrix translate_and_scale;
|
|
translate_and_scale.setScaleTranslate(1.1f, 1.1f, 2.f, 2.f);
|
|
auto transform_layer = std::make_shared<TransformLayer>(translate_and_scale);
|
|
opacity_layer2->Add(transform_layer);
|
|
|
|
// TransformLayer Child #1: ChildSceneLayer.
|
|
const zx_koid_t kChildLayerId1 = GetChildLayerId();
|
|
auto [unused_view_token1, unused_view_holder_token1] =
|
|
scenic::ViewTokenPair::New();
|
|
|
|
ViewHolder::Create(kChildLayerId1, test_context->task_runner,
|
|
std::move(unused_view_holder_token1),
|
|
/*bind_callback=*/[](scenic::ResourceId id) {});
|
|
// Will destroy only when we go out of scope (i.e. end of the test).
|
|
AutoDestroyChildLayerId auto_destroy1(kChildLayerId1);
|
|
auto child_view1 = std::make_shared<ChildSceneLayer>(
|
|
kChildLayerId1, SkPoint::Make(1, 1), SkSize::Make(10, 10),
|
|
/*hit_testable=*/false);
|
|
transform_layer->Add(child_view1);
|
|
|
|
// TransformLayer Child #2: PhysicalShapeLayer.
|
|
auto physical_shape1 = std::make_shared<PhysicalShapeLayer>(
|
|
/*color=*/SK_ColorCYAN,
|
|
/*shadow_color=*/SK_ColorBLACK,
|
|
/*elevation*/ 23.f, path, Clip::antiAlias);
|
|
transform_layer->Add(physical_shape1);
|
|
|
|
// Preroll.
|
|
root->Preroll(test_context->preroll_context.get(), SkMatrix());
|
|
|
|
// Create another frame to be the "real" root. Required because
|
|
// UpdateScene() traversal expects there to already be a top node.
|
|
SceneUpdateContext::Frame frame(test_context->scene_update_context,
|
|
SkRRect::MakeRect(SkRect::MakeWH(100, 100)),
|
|
SK_ColorTRANSPARENT, SK_AlphaOPAQUE,
|
|
"fuchsia test root");
|
|
|
|
// Submit the list of command we will expect Scenic to see.
|
|
//
|
|
// We are interested in verifying that the opacity values of children are
|
|
// correct.
|
|
|
|
std::vector<fuchsia::ui::gfx::Command> expected;
|
|
|
|
//
|
|
// Test root.
|
|
//
|
|
expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/1));
|
|
expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/2));
|
|
expected.push_back(scenic::NewSetLabelCmd(/*id=*/1, "fuchsia test root"));
|
|
expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0, 0}));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/1, /*child_id=*/2));
|
|
expected.push_back(scenic::NewSetOpacityCmd(/*id=*/2, kOneMinusEpsilon));
|
|
|
|
//
|
|
// OpacityLayer #1
|
|
//
|
|
// Expect no new commands for this.
|
|
|
|
//
|
|
// OpacityLayer #2
|
|
//
|
|
// Expect no new commands for this.
|
|
|
|
//
|
|
// TransformLayer
|
|
//
|
|
//
|
|
expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/3));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/3));
|
|
expected.push_back(scenic::NewSetLabelCmd(/*id=*/3, "flutter::Transform"));
|
|
expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {2.f, 2.f, 0.f}));
|
|
expected.push_back(scenic::NewSetScaleCmd(/*id=*/3, {1.1f, 1.1f, 1.f}));
|
|
expected.push_back(scenic::NewSetRotationCmd(/*id=*/3, {0.f, 0.f, 0.f, 1.f}));
|
|
|
|
//
|
|
// TransformLayer Child #1: ChildSceneLayer.
|
|
//
|
|
expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/4));
|
|
expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/5));
|
|
auto [view_token1, view_holder_token1] = scenic::ViewTokenPair::New();
|
|
expected.push_back(scenic::NewCreateViewHolderCmd(
|
|
/*id=*/6, std::move(view_holder_token1), ""));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/5, /*child_id=*/4));
|
|
expected.push_back(scenic::NewSetLabelCmd(/*id=*/5, "flutter::ViewHolder"));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/6));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/5));
|
|
|
|
// Check opacity value. Extra rounding required because we pass alpha as
|
|
// a uint/SkAlpha to SceneUpdateContext::Frame.
|
|
float opacity1 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f);
|
|
opacity1 = SkScalarRoundToInt(opacity1 * 255) / 255.f;
|
|
expected.push_back(scenic::NewSetOpacityCmd(/*id=*/5, opacity1));
|
|
expected.push_back(scenic::NewSetTranslationCmd(/*id=*/4, {1, 1, -0.1}));
|
|
expected.push_back(scenic::NewSetHitTestBehaviorCmd(
|
|
/*id=*/4, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress));
|
|
|
|
//
|
|
// TransformLayer Child #2: PhysicalShapeLayer
|
|
//
|
|
expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/7));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/7));
|
|
expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/8));
|
|
expected.push_back(
|
|
scenic::NewSetLabelCmd(/*id=*/7, "flutter::PhysicalShapeLayer"));
|
|
expected.push_back(scenic::NewSetTranslationCmd(
|
|
/*id=*/7, {0, 0, -kScenicZElevationBetweenLayers}));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/7, /*child_id=*/8));
|
|
|
|
// Check opacity value. Extra rounding required because we pass alpha as
|
|
// a uint/SkAlpha to SceneUpdateContext::Frame.
|
|
float opacity2 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f);
|
|
opacity2 = SkScalarRoundToInt(opacity2 * 255) / 255.f;
|
|
expected.push_back(scenic::NewSetOpacityCmd(/*id=*/8, opacity2));
|
|
expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/7, /*ignored*/ {}));
|
|
expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/9));
|
|
expected.push_back(scenic::NewCreateRectangleCmd(
|
|
/*id=*/10, /*width=*/10, /*height=*/10));
|
|
expected.push_back(scenic::NewSetShapeCmd(/*id=*/9, /*shape_id=*/10));
|
|
expected.push_back(scenic::NewSetTranslationCmd(/*id=*/9, {5, 5, 0}));
|
|
expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/11));
|
|
expected.push_back(scenic::NewSetMaterialCmd(/*id=*/9,
|
|
/*material_id=*/11));
|
|
expected.push_back(scenic::NewAddChildCmd(/*id=*/7,
|
|
/*child_id=*/9));
|
|
|
|
expected.push_back(scenic::NewCreateImageCmd(/*id=*/12, 0, 0, {}));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7));
|
|
expected.push_back(scenic::NewSetColorCmd(/*id=*/11, /*r*/ 255,
|
|
/*g*/ 255,
|
|
/*b*/ 255, /*a*/ 63));
|
|
expected.push_back(
|
|
scenic::NewSetTextureCmd(/*material_id=*/11, /*texture_id=*/12));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/11));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/10));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/9));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/8));
|
|
expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/3));
|
|
|
|
test_context->mock_session.SetExpectedCommands(std::move(expected));
|
|
|
|
// Finally, UpdateScene(). The MockSession will check the emitted
|
|
// commands against the list above.
|
|
root->UpdateScene(test_context->scene_update_context);
|
|
|
|
test_context->mock_session_wrapper->Present();
|
|
|
|
// Run loop until idle, so that the Session receives and processes
|
|
// its method calls.
|
|
async_loop_run_until_idle(
|
|
async_loop_from_dispatcher(async_get_default_dispatcher()));
|
|
|
|
// Ensure we saw enough commands.
|
|
EXPECT_EQ(46u, test_context->mock_session.num_enqueued_commands());
|
|
}
|
|
|
|
} // namespace testing
|
|
} // namespace flutter
|