fuchsia: Handle clips on platform views (flutter/engine#27149)

This commit is contained in:
David Worsham 2021-07-06 13:00:05 -07:00 committed by GitHub
parent 4153bcd1c3
commit 32cded5dcd
2 changed files with 188 additions and 91 deletions

View File

@ -23,39 +23,84 @@ constexpr float kScenicZElevationBetweenLayers = 0.0001f;
constexpr float kScenicZElevationForPlatformView = 100.f;
constexpr float kScenicElevationForInputInterceptor = 500.f;
SkScalar OpacityFromMutatorStack(const flutter::MutatorsStack& mutatorsStack) {
SkScalar mutatorsOpacity = 1.f;
for (auto i = mutatorsStack.Bottom(); i != mutatorsStack.Top(); ++i) {
ViewMutators ParseMutatorStack(const flutter::MutatorsStack& mutators_stack) {
ViewMutators mutators;
SkMatrix total_transform = SkMatrix::I();
SkMatrix transform_accumulator = SkMatrix::I();
for (auto i = mutators_stack.Begin(); i != mutators_stack.End(); ++i) {
const auto& mutator = *i;
switch (mutator->GetType()) {
case flutter::MutatorType::opacity: {
mutatorsOpacity *= std::clamp(mutator->GetAlphaFloat(), 0.f, 1.f);
mutators.opacity *= std::clamp(mutator->GetAlphaFloat(), 0.f, 1.f);
} break;
case flutter::MutatorType::transform: {
total_transform.preConcat(mutator->GetMatrix());
transform_accumulator.preConcat(mutator->GetMatrix());
} break;
case flutter::MutatorType::clip_rect: {
mutators.clips.emplace_back(TransformedClip{
.transform = transform_accumulator,
.rect = mutator->GetRect(),
});
transform_accumulator = SkMatrix::I();
} break;
case flutter::MutatorType::clip_rrect: {
mutators.clips.emplace_back(TransformedClip{
.transform = transform_accumulator,
.rect = mutator->GetRRect().getBounds(),
});
transform_accumulator = SkMatrix::I();
} break;
case flutter::MutatorType::clip_path: {
mutators.clips.emplace_back(TransformedClip{
.transform = transform_accumulator,
.rect = mutator->GetPath().getBounds(),
});
transform_accumulator = SkMatrix::I();
} break;
default: {
break;
}
}
}
mutators.total_transform = total_transform;
mutators.transform = transform_accumulator;
mutators.opacity = std::clamp(mutators.opacity, 0.f, 1.f);
return mutatorsOpacity;
return mutators;
}
SkMatrix TransformFromMutatorStack(
const flutter::MutatorsStack& mutatorsStack) {
SkMatrix mutatorsTransform;
for (auto i = mutatorsStack.Bottom(); i != mutatorsStack.Top(); ++i) {
const auto& mutator = *i;
switch (mutator->GetType()) {
case flutter::MutatorType::transform: {
mutatorsTransform.preConcat(mutator->GetMatrix());
} break;
default: {
break;
}
}
}
std::vector<fuchsia::ui::gfx::Plane3> ClipPlanesFromRect(SkRect rect) {
// We will generate 4 oriented planes, one for each edge of the bounding rect.
std::vector<fuchsia::ui::gfx::Plane3> clip_planes;
clip_planes.resize(4);
return mutatorsTransform;
// Top plane.
clip_planes[0].dist = rect.top();
clip_planes[0].dir.x = 0.f;
clip_planes[0].dir.y = 1.f;
clip_planes[0].dir.z = 0.f;
// Bottom plane.
clip_planes[1].dist = -rect.bottom();
clip_planes[1].dir.x = 0.f;
clip_planes[1].dir.y = -1.f;
clip_planes[1].dir.z = 0.f;
// Left plane.
clip_planes[2].dist = rect.left();
clip_planes[2].dir.x = 1.f;
clip_planes[2].dir.y = 0.f;
clip_planes[2].dir.z = 0.f;
// Right plane.
clip_planes[3].dist = -rect.right();
clip_planes[3].dir.x = -1.f;
clip_planes[3].dir.y = 0.f;
clip_planes[3].dir.z = 0.f;
return clip_planes;
}
} // namespace
@ -255,90 +300,121 @@ void FuchsiaExternalViewEmbedder::SubmitFrame(
FML_CHECK(layer->second.embedded_view_params.has_value());
auto& view_params = layer->second.embedded_view_params.value();
// Validate the MutatorsStack encodes the same transform as the
// transform matrix.
FML_DCHECK(TransformFromMutatorStack(view_params.mutatorsStack()) ==
view_params.transformMatrix());
// Get the ScenicView structure corresponding to the platform view.
auto found = scenic_views_.find(layer_id.value());
FML_CHECK(found != scenic_views_.end());
auto& view_holder = found->second;
// Compute offset and size for the platform view.
const SkMatrix& view_transform = view_params.transformMatrix();
const SkPoint view_offset = SkPoint::Make(
view_transform.getTranslateX(), view_transform.getTranslateY());
// Compute mutators, size, and elevation for the platform view.
const ViewMutators view_mutators =
ParseMutatorStack(view_params.mutatorsStack());
const SkSize view_size = view_params.sizePoints();
const SkSize view_scale = SkSize::Make(view_transform.getScaleX(),
view_transform.getScaleY());
FML_DCHECK(!view_size.isEmpty() && !view_scale.isEmpty());
// Compute opacity for the platform view.
const float view_opacity =
OpacityFromMutatorStack(view_params.mutatorsStack());
// Set opacity.
if (view_opacity != view_holder.opacity) {
view_holder.opacity_node.SetOpacity(view_opacity);
view_holder.opacity = view_opacity;
}
// Set transform and elevation.
const float view_elevation =
kScenicZElevationBetweenLayers * scenic_layer_index +
embedded_views_height;
if (view_offset != view_holder.offset ||
view_scale != view_holder.scale ||
FML_CHECK(view_mutators.total_transform ==
view_params.transformMatrix());
// Set clips for the platform view.
if (view_mutators.clips != view_holder.mutators.clips) {
// Expand the clip_nodes array to fit any new nodes.
while (view_holder.clip_nodes.size() < view_mutators.clips.size()) {
view_holder.clip_nodes.emplace_back(
scenic::EntityNode(session_.get()));
}
FML_CHECK(view_holder.clip_nodes.size() >=
view_mutators.clips.size());
// Adjust and re-parent all clip rects.
for (auto& clip_node : view_holder.clip_nodes) {
clip_node.DetachChildren();
}
for (size_t c = 0; c < view_mutators.clips.size(); c++) {
const SkMatrix& clip_transform = view_mutators.clips[c].transform;
const SkRect& clip_rect = view_mutators.clips[c].rect;
view_holder.clip_nodes[c].SetTranslation(
clip_transform.getTranslateX(), clip_transform.getTranslateY(),
0.f);
view_holder.clip_nodes[c].SetScale(clip_transform.getScaleX(),
clip_transform.getScaleY(), 1.f);
view_holder.clip_nodes[c].SetClipPlanes(
ClipPlanesFromRect(clip_rect));
if (c != (view_mutators.clips.size() - 1)) {
view_holder.clip_nodes[c].AddChild(view_holder.clip_nodes[c + 1]);
} else {
view_holder.clip_nodes[c].AddChild(view_holder.opacity_node);
}
}
view_holder.mutators.clips = view_mutators.clips;
}
// Set transform and elevation for the platform view.
if (view_mutators.transform != view_holder.mutators.transform ||
view_elevation != view_holder.elevation) {
view_holder.entity_node.SetTranslation(view_offset.fX, view_offset.fY,
-view_elevation);
view_holder.entity_node.SetScale(view_scale.fWidth,
view_scale.fHeight, 1.f);
view_holder.offset = view_offset;
view_holder.scale = view_scale;
view_holder.transform_node.SetTranslation(
view_mutators.transform.getTranslateX(),
view_mutators.transform.getTranslateY(), -view_elevation);
view_holder.transform_node.SetScale(
view_mutators.transform.getScaleX(),
view_mutators.transform.getScaleY(), 1.f);
view_holder.mutators.transform = view_mutators.transform;
view_holder.elevation = view_elevation;
}
// Set HitTestBehavior.
// Set HitTestBehavior for the platform view.
if (view_holder.pending_hit_testable != view_holder.hit_testable) {
view_holder.entity_node.SetHitTestBehavior(
view_holder.transform_node.SetHitTestBehavior(
view_holder.pending_hit_testable
? fuchsia::ui::gfx::HitTestBehavior::kDefault
: fuchsia::ui::gfx::HitTestBehavior::kSuppress);
view_holder.hit_testable = view_holder.pending_hit_testable;
}
// Set opacity for the platform view.
if (view_mutators.opacity != view_holder.mutators.opacity) {
view_holder.opacity_node.SetOpacity(view_mutators.opacity);
view_holder.mutators.opacity = view_mutators.opacity;
}
// Set size, occlusion hint, and focusable.
//
// Scenic rejects `SetViewProperties` calls with a zero size.
if (!view_size.isEmpty() &&
(view_size != view_holder.size ||
view_holder.pending_occlusion_hint != view_holder.occlusion_hint ||
view_holder.pending_focusable != view_holder.focusable)) {
view_holder.size = view_size;
view_holder.occlusion_hint = view_holder.pending_occlusion_hint;
view_holder.focusable = view_holder.pending_focusable;
if (view_size != view_holder.size ||
view_holder.pending_occlusion_hint != view_holder.occlusion_hint ||
view_holder.pending_focusable != view_holder.focusable) {
view_holder.view_holder.SetViewProperties({
.bounding_box =
{
.min = {.x = 0.f, .y = 0.f, .z = -1000.f},
.max = {.x = view_holder.size.fWidth,
.y = view_holder.size.fHeight,
.max = {.x = view_size.fWidth,
.y = view_size.fHeight,
.z = 0.f},
},
.inset_from_min = {.x = view_holder.occlusion_hint.fLeft,
.y = view_holder.occlusion_hint.fTop,
.inset_from_min = {.x = view_holder.pending_occlusion_hint.fLeft,
.y = view_holder.pending_occlusion_hint.fTop,
.z = 0.f},
.inset_from_max = {.x = view_holder.occlusion_hint.fRight,
.y = view_holder.occlusion_hint.fBottom,
.inset_from_max = {.x = view_holder.pending_occlusion_hint.fRight,
.y =
view_holder.pending_occlusion_hint.fBottom,
.z = 0.f},
.focus_change = view_holder.focusable,
.focus_change = view_holder.pending_focusable,
});
view_holder.size = view_size;
view_holder.occlusion_hint = view_holder.pending_occlusion_hint;
view_holder.focusable = view_holder.pending_focusable;
}
// Attach the ScenicView to the main scene graph.
layer_tree_node_.AddChild(view_holder.opacity_node);
if (view_holder.mutators.clips.empty()) {
layer_tree_node_.AddChild(view_holder.opacity_node);
} else {
layer_tree_node_.AddChild(view_holder.clip_nodes[0]);
}
// Account for the ScenicView's height when positioning the next layer.
embedded_views_height += kScenicZElevationForPlatformView;
@ -514,7 +590,7 @@ void FuchsiaExternalViewEmbedder::CreateView(int64_t view_id,
ScenicView new_view = {
.opacity_node = scenic::OpacityNodeHACK(session_.get()),
.entity_node = scenic::EntityNode(session_.get()),
.transform_node = scenic::EntityNode(session_.get()),
.view_holder = scenic::ViewHolder(
session_.get(),
scenic::ToViewHolderToken(zx::eventpair((zx_handle_t)view_id)),
@ -523,12 +599,12 @@ void FuchsiaExternalViewEmbedder::CreateView(int64_t view_id,
on_view_created();
on_view_bound(new_view.view_holder.id());
new_view.opacity_node.SetLabel("flutter::PlatformView::OpacityMutator");
new_view.entity_node.SetLabel("flutter::PlatformView::TransformMutator");
new_view.opacity_node.AddChild(new_view.entity_node);
new_view.entity_node.Attach(new_view.view_holder);
new_view.entity_node.SetTranslation(0.f, 0.f,
-kScenicZElevationBetweenLayers);
new_view.opacity_node.SetLabel("Flutter::PlatformView::OpacityMutator");
new_view.opacity_node.AddChild(new_view.transform_node);
new_view.transform_node.SetLabel("Flutter::PlatformView::TransformMutator");
new_view.transform_node.SetTranslation(0.f, 0.f,
-kScenicZElevationBetweenLayers);
new_view.transform_node.Attach(new_view.view_holder);
scenic_views_.emplace(std::make_pair(view_id, std::move(new_view)));
}

View File

@ -35,6 +35,30 @@ namespace flutter_runner {
using ViewCallback = std::function<void()>;
using ViewIdCallback = std::function<void(scenic::ResourceId)>;
// This struct represents a transformed clip rect.
struct TransformedClip {
SkMatrix transform = SkMatrix::I();
SkRect rect = SkRect::MakeEmpty();
bool operator==(const TransformedClip& other) const {
return transform == other.transform && rect == other.rect;
}
};
// This struct represents all the mutators that can be applied to a
// PlatformView, unpacked from the `MutatorStack`.
struct ViewMutators {
std::vector<TransformedClip> clips;
SkMatrix total_transform = SkMatrix::I();
SkMatrix transform = SkMatrix::I();
SkScalar opacity = 1.f;
bool operator==(const ViewMutators& other) const {
return clips == other.clips && total_transform == other.total_transform &&
transform == other.transform && opacity == other.opacity;
}
};
// This class orchestrates interaction with the Scenic compositor on Fuchsia. It
// ensures that flutter content and platform view content are both rendered
// correctly in a unified scene.
@ -104,8 +128,7 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
bool focusable);
private:
// Reset state for a new frame.
void Reset();
void Reset(); // Reset state for a new frame.
struct EmbedderLayer {
EmbedderLayer(const SkISize& frame_size,
@ -122,23 +145,24 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
std::unique_ptr<flutter::CanvasSpy> canvas_spy;
SkISize surface_size;
};
using EmbedderLayerId = std::optional<uint32_t>;
constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{};
struct ScenicView {
std::vector<scenic::EntityNode> clip_nodes;
scenic::OpacityNodeHACK opacity_node;
scenic::EntityNode entity_node;
scenic::EntityNode transform_node;
scenic::ViewHolder view_holder;
SkPoint offset = SkPoint::Make(0.f, 0.f);
SkSize scale = SkSize::MakeEmpty();
ViewMutators mutators;
float elevation = 0.f;
SkSize size = SkSize::MakeEmpty();
SkRect occlusion_hint = SkRect::MakeEmpty();
float elevation = 0.f;
float opacity = 1.f;
bool hit_testable = true;
bool focusable = true;
SkRect pending_occlusion_hint = SkRect::MakeEmpty();
bool hit_testable = true;
bool pending_hit_testable = true;
bool focusable = true;
bool pending_focusable = true;
};
@ -147,9 +171,6 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
scenic::Material material;
};
using EmbedderLayerId = std::optional<uint32_t>;
constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{};
DefaultSessionConnection& session_;
VulkanSurfaceProducer& surface_producer_;