10#include "flutter/common/constants.h"
11#include "flutter/fml/trace_event.h"
20void AttachClipTransformChild(
21 FlatlandConnection* flatland,
22 ExternalViewEmbedder::ClipTransform* parent_clip_transform,
23 const fuchsia::ui::composition::TransformId& child_transform_id) {
24 flatland->flatland()->AddChild(parent_clip_transform->transform_id,
26 parent_clip_transform->children.push_back(child_transform_id);
29void DetachClipTransformChildren(
30 FlatlandConnection* flatland,
31 ExternalViewEmbedder::ClipTransform* clip_transform) {
32 for (
auto& child : clip_transform->children) {
33 flatland->flatland()->RemoveChild(clip_transform->transform_id, child);
35 clip_transform->children.clear();
41 fuchsia::ui::views::ViewCreationToken view_creation_token,
42 fuchsia::ui::views::ViewIdentityOnCreation view_identity,
43 fuchsia::ui::composition::ViewBoundProtocols view_protocols,
44 fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher>
45 parent_viewport_watcher_request,
46 std::shared_ptr<FlatlandConnection> flatland,
47 std::shared_ptr<SurfaceProducer> surface_producer,
48 bool intercept_all_input)
49 : flatland_(flatland), surface_producer_(surface_producer) {
50 flatland_->flatland()->CreateView2(
51 std::move(view_creation_token), std::move(view_identity),
52 std::move(view_protocols), std::move(parent_viewport_watcher_request));
54 root_transform_id_ = flatland_->NextTransformId();
55 flatland_->flatland()->CreateTransform(root_transform_id_);
56 flatland_->flatland()->SetRootTransform(root_transform_id_);
58 if (intercept_all_input) {
59 input_interceptor_transform_ = flatland_->NextTransformId();
60 flatland_->flatland()->CreateTransform(*input_interceptor_transform_);
61 flatland_->flatland()->SetInfiniteHitRegion(
62 *input_interceptor_transform_,
63 fuchsia::ui::composition::HitTestInteraction::SEMANTICALLY_INVISIBLE);
65 flatland_->flatland()->AddChild(root_transform_id_,
66 *input_interceptor_transform_);
67 child_transforms_.emplace_back(*input_interceptor_transform_);
74 auto found = frame_layers_.find(kRootLayerId);
75 if (found == frame_layers_.end()) {
77 <<
"No root canvas could be found. This is extremely unlikely and "
78 "indicates that the external view embedder did not receive the "
79 "notification to begin the frame.";
83 return found->second.canvas_spy->GetSpyingCanvas();
88 std::unique_ptr<flutter::EmbeddedViewParams>
params) {
89 zx_handle_t handle =
static_cast<zx_handle_t
>(view_id);
90 FML_CHECK(frame_layers_.count(handle) == 0);
92 frame_layers_.emplace(std::make_pair(
93 EmbedderLayerId{handle},
95 frame_composition_order_.push_back(handle);
100 zx_handle_t handle =
static_cast<zx_handle_t
>(view_id);
101 auto found = frame_layers_.find(handle);
104 return found->second.canvas_spy->GetSpyingCanvas();
118 double device_pixel_ratio) {
121 frame_size_ = frame_size;
122 frame_dpr_ = device_pixel_ratio;
125 frame_layers_.emplace(std::make_pair(
128 frame_composition_order_.push_back(kRootLayerId);
132 bool should_resubmit_frame,
134 TRACE_EVENT0(
"flutter",
"ExternalViewEmbedder::EndFrame");
138 int64_t flutter_view_id,
140 const std::shared_ptr<impeller::AiksContext>& aiks_context,
141 std::unique_ptr<flutter::SurfaceFrame>
frame) {
145 TRACE_EVENT0(
"flutter",
"ExternalViewEmbedder::SubmitFlutterView");
146 std::vector<std::unique_ptr<SurfaceProducerSurface>> frame_surfaces;
147 std::unordered_map<EmbedderLayerId, size_t> frame_surface_indices;
153 for (
const auto& layer : frame_layers_) {
154 if (!layer.second.canvas_spy->DidDrawIntoCanvas()) {
159 surface_producer_->ProduceSurface(layer.second.surface_size);
161 const std::string layer_id_str =
164 FML_LOG(
ERROR) <<
"Failed to create surface for layer " << layer_id_str
165 <<
"; size (" << layer.second.surface_size.width()
166 <<
", " << layer.second.surface_size.height() <<
")";
173 if (
surface->GetImageId() == 0) {
174 auto image_id = flatland_->NextContentId().value;
176 fuchsia::ui::composition::ImageProperties image_properties;
177 image_properties.set_size({
static_cast<uint32_t
>(
size.width()),
178 static_cast<uint32_t
>(
size.height())});
179 flatland_->flatland()->CreateImage(
180 {image_id},
surface->GetBufferCollectionImportToken(), 0,
181 std::move(image_properties));
184 surface->SetReleaseImageCallback([flatland = flatland_, image_id]() {
185 flatland->flatland()->ReleaseImage({image_id});
190 flatland_->EnqueueAcquireFence(
surface->GetAcquireFence());
191 flatland_->EnqueueReleaseFence(
surface->GetReleaseFence());
193 frame_surface_indices.emplace(
194 std::make_pair(layer.first, frame_surfaces.size()));
195 frame_surfaces.emplace_back(std::move(
surface));
203 for (
const auto& surface_index : frame_surface_indices) {
204 const auto& layer = frame_layers_.find(surface_index.first);
206 layer->second.picture =
207 layer->second.recorder->finishRecordingAsPicture();
208 FML_CHECK(layer->second.picture !=
nullptr);
217 const float inv_dpr = 1.0f / frame_dpr_;
218 flatland_->flatland()->SetScale(root_transform_id_, {inv_dpr, inv_dpr});
220 size_t layer_index = 0;
221 for (
const auto& layer_id : frame_composition_order_) {
222 const auto& layer = frame_layers_.find(layer_id);
226 if (layer_id.has_value()) {
227 FML_CHECK(layer->second.embedded_view_params.has_value());
228 auto& view_params = layer->second.embedded_view_params.value();
231 auto found = views_.find(layer_id.value());
233 <<
"No View for layer_id = " << layer_id.value()
234 <<
". This typically indicates that the Dart code in "
235 "Fuchsia's fuchsia_scenic_flutter library failed to create "
236 "the platform view, leading to a crash later down the road in "
237 "the Flutter Engine code that tries to find that platform view. "
238 "Check the Flutter Framework for changes to PlatformView that "
239 "might have caused a regression.";
240 auto& viewport = found->second;
243 const ViewMutators view_mutators =
244 ParseMutatorStack(view_params.mutatorsStack());
245 const SkSize view_size = view_params.sizePoints();
250 for (
int index = 0; index < 9; index++) {
251 const SkScalar mutators_transform_value =
252 view_mutators.total_transform.get(index);
253 const SkScalar params_transform_value =
254 view_params.transformMatrix().get(index);
256 params_transform_value, 0.0005f)) {
258 <<
"Assertion failed: view_mutators.total_transform[" << index
259 <<
"] (" << mutators_transform_value
260 <<
") != view_params.transformMatrix()[" << index <<
"] ("
261 << params_transform_value
262 <<
"). This likely means there is a bug with the "
263 <<
"logic for parsing embedded views' transform matrices.";
267 if (viewport.pending_create_viewport_callback) {
269 viewport.pending_create_viewport_callback(
270 view_size, viewport.pending_occlusion_hint);
271 viewport.size = view_size;
272 viewport.occlusion_hint = viewport.pending_occlusion_hint;
275 <<
"Failed to create viewport because width or height is zero.";
280 if (view_mutators.transform != viewport.mutators.transform) {
281 flatland_->flatland()->SetTranslation(
282 viewport.transform_id,
283 {static_cast<int32_t>(view_mutators.transform.getTranslateX()),
284 static_cast<int32_t>(view_mutators.transform.getTranslateY())});
285 flatland_->flatland()->SetScale(
286 viewport.transform_id, {view_mutators.transform.getScaleX(),
287 view_mutators.transform.getScaleY()});
288 viewport.mutators.transform = view_mutators.transform;
292 if (view_mutators.clips != viewport.mutators.clips) {
294 while (viewport.clip_transforms.size() < view_mutators.clips.size()) {
296 clip_transform.
transform_id = flatland_->NextTransformId();
297 flatland_->flatland()->CreateTransform(clip_transform.
transform_id);
298 viewport.clip_transforms.emplace_back(std::move(clip_transform));
300 FML_CHECK(viewport.clip_transforms.size() >=
301 view_mutators.clips.size());
304 for (
auto& clip_transform : viewport.clip_transforms) {
305 DetachClipTransformChildren(flatland_.get(), &clip_transform);
308 for (
size_t c = 0; c < view_mutators.clips.size(); c++) {
309 const SkMatrix& clip_matrix = view_mutators.clips[c].transform;
310 const SkRect& clip_rect = view_mutators.clips[c].rect;
312 flatland_->flatland()->SetTranslation(
313 viewport.clip_transforms[c].transform_id,
314 {static_cast<int32_t>(clip_matrix.getTranslateX()),
315 static_cast<int32_t>(clip_matrix.getTranslateY())});
316 flatland_->flatland()->SetScale(
317 viewport.clip_transforms[c].transform_id,
318 {clip_matrix.getScaleX(), clip_matrix.getScaleY()});
320 static_cast<int32_t
>(clip_rect.
x()),
321 static_cast<int32_t
>(clip_rect.
y()),
322 static_cast<int32_t
>(clip_rect.
width()),
323 static_cast<int32_t
>(clip_rect.
height())};
324 flatland_->flatland()->SetClipBoundary(
325 viewport.clip_transforms[c].transform_id,
326 std::make_unique<fuchsia::math::Rect>(std::move(
rect)));
328 const auto child_transform_id =
329 c != (view_mutators.clips.size() - 1)
330 ? viewport.clip_transforms[c + 1].transform_id
331 : viewport.transform_id;
332 AttachClipTransformChild(flatland_.get(),
333 &(viewport.clip_transforms[c]),
336 viewport.mutators.clips = view_mutators.clips;
340 if (view_mutators.opacity != viewport.mutators.opacity) {
341 flatland_->flatland()->SetOpacity(viewport.transform_id,
342 view_mutators.opacity);
343 viewport.mutators.opacity = view_mutators.opacity;
347 if (view_size != viewport.size ||
348 viewport.pending_occlusion_hint != viewport.occlusion_hint) {
349 fuchsia::ui::composition::ViewportProperties properties;
350 properties.set_logical_size(
351 {
static_cast<uint32_t
>(view_size.
fWidth),
352 static_cast<uint32_t
>(view_size.
fHeight)});
353 properties.set_inset(
354 {
static_cast<int32_t
>(viewport.pending_occlusion_hint.fTop),
355 static_cast<int32_t
>(viewport.pending_occlusion_hint.fRight),
356 static_cast<int32_t
>(viewport.pending_occlusion_hint.fBottom),
357 static_cast<int32_t
>(viewport.pending_occlusion_hint.fLeft)});
358 flatland_->flatland()->SetViewportProperties(viewport.viewport_id,
359 std::move(properties));
360 viewport.size = view_size;
361 viewport.occlusion_hint = viewport.pending_occlusion_hint;
365 const auto main_child_transform =
366 viewport.mutators.clips.empty()
367 ? viewport.transform_id
368 : viewport.clip_transforms[0].transform_id;
369 flatland_->flatland()->AddChild(root_transform_id_,
370 main_child_transform);
371 child_transforms_.emplace_back(main_child_transform);
376 if (layer->second.canvas_spy->DidDrawIntoCanvas()) {
377 const auto& surface_index = frame_surface_indices.find(layer_id);
378 if (surface_index != frame_surface_indices.end()) {
379 FML_CHECK(surface_index->second < frame_surfaces.size());
380 surface_for_layer = frame_surfaces[surface_index->second].get();
383 const std::string layer_id_str =
386 FML_LOG(
ERROR) <<
"Missing surface for layer " << layer_id_str
387 <<
"; skipping scene graph add of layer.";
393 if (surface_for_layer !=
nullptr) {
395 FML_CHECK(layer_index <= layers_.size());
396 if (layer_index == layers_.size()) {
397 Layer new_layer{.transform_id = flatland_->NextTransformId()};
398 flatland_->flatland()->CreateTransform(new_layer.transform_id);
399 layers_.emplace_back(std::move(new_layer));
403 flatland_->flatland()->SetContent(layers_[layer_index].transform_id,
405 flatland_->flatland()->SetImageDestinationSize(
407 {
static_cast<uint32_t
>(surface_for_layer->
GetSize().
width()),
408 static_cast<uint32_t
>(surface_for_layer->
GetSize().
height())});
412 flatland_->flatland()->SetImageBlendingFunction(
415 : fuchsia::ui::composition::BlendMode::SRC_OVER);
421 std::list<SkRect> intersection_rects =
422 layer->second.rtree->searchNonOverlappingDrawnRects(
425 std::vector<fuchsia::ui::composition::HitRegion> hit_regions;
426 for (
const SkRect&
rect : intersection_rects) {
427 hit_regions.emplace_back();
428 auto& new_hit_region = hit_regions.back();
429 new_hit_region.region.x =
rect.x();
430 new_hit_region.region.y =
rect.y();
431 new_hit_region.region.width =
rect.width();
432 new_hit_region.region.height =
rect.height();
433 new_hit_region.hit_test =
434 fuchsia::ui::composition::HitTestInteraction::DEFAULT;
437 flatland_->flatland()->SetHitRegions(
438 layers_[layer_index].transform_id, std::move(hit_regions));
442 flatland_->flatland()->AddChild(root_transform_id_,
443 layers_[layer_index].transform_id);
444 child_transforms_.emplace_back(layers_[layer_index].transform_id);
454 if (input_interceptor_transform_.has_value()) {
455 flatland_->flatland()->AddChild(root_transform_id_,
456 *input_interceptor_transform_);
457 child_transforms_.emplace_back(*input_interceptor_transform_);
465 flatland_->Present();
472 for (
const auto& surface_index : frame_surface_indices) {
475 FML_CHECK(surface_index.second < frame_surfaces.size());
477 frame_surfaces[surface_index.second].get();
487 const auto& layer = frame_layers_.find(surface_index.first);
495 direct_context->flushAndSubmit();
504 surface_producer_->SubmitSurfaces(std::move(frame_surfaces));
514 FML_CHECK(views_.find(view_id) == views_.end());
516 const auto transform_id = flatland_->NextTransformId();
517 const auto viewport_id = flatland_->NextContentId();
518 View new_view = {.transform_id = transform_id, .viewport_id = viewport_id};
519 flatland_->flatland()->CreateTransform(new_view.transform_id);
520 fuchsia::ui::composition::ChildViewWatcherHandle child_view_watcher;
521 new_view.pending_create_viewport_callback =
522 [
this, transform_id, viewport_id, view_id,
523 child_view_watcher_request = child_view_watcher.NewRequest()](
525 fuchsia::ui::composition::ViewportProperties properties;
526 properties.set_logical_size({
static_cast<uint32_t
>(
size.fWidth),
527 static_cast<uint32_t
>(
size.fHeight)});
528 properties.set_inset({
static_cast<int32_t
>(
inset.
fTop),
532 flatland_->flatland()->CreateViewport(
533 viewport_id, {zx::channel((zx_handle_t)view_id)},
534 std::move(properties), std::move(child_view_watcher_request));
535 flatland_->flatland()->SetContent(transform_id, viewport_id);
539 on_view_bound(new_view.viewport_id, std::move(child_view_watcher));
540 views_.emplace(std::make_pair(view_id, std::move(new_view)));
545 auto view = views_.find(view_id);
548 auto viewport_id = view->second.viewport_id;
549 auto transform_id = view->second.transform_id;
550 auto& clip_transforms = view->second.clip_transforms;
551 if (!view->second.pending_create_viewport_callback) {
552 flatland_->flatland()->ReleaseViewport(viewport_id, [](
auto) {});
554 auto itr = std::find_if(
555 child_transforms_.begin(), child_transforms_.end(),
557 &clip_transforms](fuchsia::ui::composition::TransformId
id) {
558 return id.value == transform_id.value ||
559 (!clip_transforms.empty() &&
560 (id.value == clip_transforms[0].transform_id.value));
562 if (itr != child_transforms_.end()) {
563 flatland_->flatland()->RemoveChild(root_transform_id_, *itr);
564 child_transforms_.erase(itr);
566 for (
auto& clip_transform : clip_transforms) {
567 DetachClipTransformChildren(flatland_.get(), &clip_transform);
569 flatland_->flatland()->ReleaseTransform(transform_id);
570 for (
auto& clip_transform : clip_transforms) {
571 flatland_->flatland()->ReleaseTransform(clip_transform.transform_id);
575 on_view_unbound(viewport_id);
579 const SkRect& occlusion_hint,
582 auto found = views_.find(view_id);
587 auto& viewport = found->second;
588 viewport.pending_occlusion_hint = occlusion_hint;
591void ExternalViewEmbedder::Reset() {
592 frame_layers_.clear();
593 frame_composition_order_.clear();
598 for (
const auto&
transform : child_transforms_) {
599 flatland_->flatland()->RemoveChild(root_transform_id_,
transform);
601 child_transforms_.clear();
604 for (
const auto& layer : layers_) {
605 flatland_->flatland()->SetContent(layer.transform_id, {0});
609ExternalViewEmbedder::ViewMutators ExternalViewEmbedder::ParseMutatorStack(
611 ViewMutators mutators;
615 for (
auto i = mutators_stack.
Begin();
i != mutators_stack.
End(); ++
i) {
616 const auto& mutator = *
i;
617 switch (mutator->GetType()) {
618 case flutter::MutatorType::kOpacity: {
619 mutators.opacity *=
std::clamp(mutator->GetAlphaFloat(), 0.f, 1.f);
621 case flutter::MutatorType::kTransform: {
622 total_transform.
preConcat(mutator->GetMatrix());
623 transform_accumulator.
preConcat(mutator->GetMatrix());
626 mutators.clips.emplace_back(TransformedClip{
627 .transform = transform_accumulator,
628 .rect = mutator->GetRect(),
633 mutators.clips.emplace_back(TransformedClip{
634 .transform = transform_accumulator,
635 .rect = mutator->GetRRect().getBounds(),
639 case flutter::MutatorType::kClipPath: {
640 mutators.clips.emplace_back(TransformedClip{
641 .transform = transform_accumulator,
642 .rect = mutator->GetPath().getBounds(),
651 mutators.total_transform = total_transform;
652 mutators.transform = transform_accumulator;
653 mutators.opacity =
std::clamp(mutators.opacity, 0.f, 1.f);
static GrDirectContext * GrAsDirectContext(GrContext_Base *base)
static unsigned clamp(SkFixed fx, int max)
constexpr SkColor SK_ColorTRANSPARENT
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
virtual GrRecordingContext * recordingContext() const
void clear(SkColor color)
void setMatrix(const SkM44 &matrix)
void drawPicture(const SkPicture *picture)
static const SkMatrix & I()
SkMatrix & preConcat(const SkMatrix &other)
Developer-facing API for rendering anything within the engine.
ExternalViewEmbedder()=default
void EndFrame(bool should_resubmit_frame, const fml::RefPtr< fml::RasterThreadMerger > &raster_thread_merger) override
flutter::PostPrerollResult PostPrerollAction(const fml::RefPtr< fml::RasterThreadMerger > &raster_thread_merger) override
void PrepareFlutterView(SkISize frame_size, double device_pixel_ratio) override
void DestroyView(int64_t view_id, ViewIdCallback on_view_unbound)
void CreateView(int64_t view_id, ViewCallback on_view_created, ViewCreatedCallback on_view_bound)
flutter::DlCanvas * CompositeEmbeddedView(int64_t view_id) override
flutter::DlCanvas * GetRootCanvas() override
void SubmitFlutterView(int64_t flutter_view_id, GrDirectContext *context, const std::shared_ptr< impeller::AiksContext > &aiks_context, std::unique_ptr< flutter::SurfaceFrame > frame) override
void BeginFrame(GrDirectContext *context, const fml::RefPtr< fml::RasterThreadMerger > &raster_thread_merger) override
void SetViewProperties(int64_t view_id, const SkRect &occlusion_hint, bool hit_testable, bool focusable)
void PrerollCompositeEmbeddedView(int64_t view_id, std::unique_ptr< flutter::EmbeddedViewParams > params) override
virtual SkISize GetSize() const =0
virtual uint32_t GetImageId()=0
const EmbeddedViewParams * params
#define FML_DLOG(severity)
#define FML_LOG(severity)
#define FML_CHECK(condition)
#define FML_DCHECK(condition)
sk_sp< SkBlender > blender SkRect rect
std::function< void()> ViewCallback
std::function< void(fuchsia::ui::composition::ContentId)> ViewIdCallback
std::function< void(fuchsia::ui::composition::ContentId, fuchsia::ui::composition::ChildViewWatcherHandle child_view_watcher)> ViewCreatedCallback
constexpr int64_t kFlutterImplicitViewId
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
static SkString to_string(int n)
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
static SkRect inset(const SkRect &r)
static constexpr SkISize Make(int32_t w, int32_t h)
constexpr int32_t height() const
static SkRect Make(const SkISize &size)
SkScalar fBottom
larger y-axis bounds
SkScalar fLeft
smaller x-axis bounds
constexpr float x() const
constexpr float y() const
SkScalar fRight
larger x-axis bounds
constexpr float height() const
constexpr float width() const
SkScalar fTop
smaller y-axis bounds
#define TRACE_EVENT0(category_group, name)