Skip to content

Commit

Permalink
Multiview ExternalViewEmbedder (flutter#46169)
Browse files Browse the repository at this point in the history
This PR adds multiview support for `ExternalViewEmbedder`.

## Nomenclature

The term **view** can be ambiguous in `ExternalViewEmbedder`, and therefore the following terms are used:
* A **native view** refers to the final drawing surface that to composite layers to. It is the "view" used in other places of the engine, such as `Shell::AddView`.
* A **platform view** refers a platform view, a layer that holds content to be embedded.

## Change

The lifecycle of `ExternalViewEmbedder` is changed:

<table>
    <thead>
        <tr>
            <th>Before PR</th>
            <th>After PR</th>
            <th>How it's called</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td rowspan=2>BeginFrame</td>
            <td>BeginFrame</td>
            <td>Once per frame</td>
        </tr>
        <tr>
            <td>PrepareFlutterView</td>
            <td>Once per flutter view</td>
        </tr>
        <tr>
            <td>SubmitFrame</td>
            <td>SubmitFlutterView (renamed)</td>
            <td>Once per flutter view</td>
        </tr>
        <tr>
            <td>EndFrame</td>
            <td>EndFrame (unchanged)</td>
            <td>Once per frame</td>
        </tr>
    </tbody>
</table>

* `BeginFrame` should perform per-frame actions, such as merge-unmerging threads.
* `PrepareView` should perform per-native-view preparations, such as recording the view ID and view size.

This change is necessary because some actions in `PrepareView` needs to be refreshed at the beginning of drawing every native view.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
  • Loading branch information
dkwingsmt authored Dec 4, 2023
1 parent e22d34f commit e532e4b
Show file tree
Hide file tree
Showing 21 changed files with 380 additions and 172 deletions.
14 changes: 7 additions & 7 deletions flow/embedded_views.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,19 @@ bool DisplayListEmbedderViewSlice::recording_ended() {
return builder_ == nullptr;
}

void ExternalViewEmbedder::SubmitFrame(
void ExternalViewEmbedder::SubmitFlutterView(
GrDirectContext* context,
const std::shared_ptr<impeller::AiksContext>& aiks_context,
std::unique_ptr<SurfaceFrame> frame) {
frame->Submit();
}

bool ExternalViewEmbedder::SupportsDynamicThreadMerging() {
return false;
}

void ExternalViewEmbedder::Teardown() {}

void MutatorsStack::PushClipRect(const SkRect& rect) {
std::shared_ptr<Mutator> element = std::make_shared<Mutator>(rect);
vector_.push_back(element);
Expand Down Expand Up @@ -112,10 +118,4 @@ const std::vector<std::shared_ptr<Mutator>>::const_iterator MutatorsStack::End()
return vector_.end();
}

bool ExternalViewEmbedder::SupportsDynamicThreadMerging() {
return false;
}

void ExternalViewEmbedder::Teardown() {}

} // namespace flutter
40 changes: 33 additions & 7 deletions flow/embedded_views.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,29 @@ class DisplayListEmbedderViewSlice : public EmbedderViewSlice {
//
// Used on iOS, Android (hybrid composite mode), and on embedded platforms
// that provide a system compositor as part of the project arguments.
//
// There are two kinds of "view IDs" in the context of ExternalViewEmbedder, and
// specific names are used to avoid ambiguation:
//
// * ExternalViewEmbedder composites a stack of layers. Each layer's content
// might be from Flutter widgets, or a platform view, which displays platform
// native components. Each platform view is labeled by a view ID, which
// corresponds to the ID from `PlatformViewsRegistry.getNextPlatformViewId`
// from the framework. In the context of `ExternalViewEmbedder`, this ID is
// called platform_view_id.
// * The layers are compositied into a single rectangular surface, displayed by
// taking up an entire native window or part of a window. Each such surface
// is labeled by a view ID, which corresponds to `FlutterView.viewID` from
// dart:ui. In the context of `ExternalViewEmbedder`, this ID is called
// flutter_view_id.
//
// The lifecycle of drawing a frame using ExternalViewEmbedder is:
//
// 1. At the start of a frame, call |BeginFrame|, then |SetUsedThisFrame| to
// true.
// 2. For each view to be drawn, call |PrepareFlutterView|, then
// |SubmitFlutterView|.
// 3. At the end of a frame, if |GetUsedThisFrame| is true, call |EndFrame|.
class ExternalViewEmbedder {
// TODO(cyanglaz): Make embedder own the `EmbeddedViewParams`.

Expand All @@ -383,7 +406,7 @@ class ExternalViewEmbedder {
// from the on-screen render target.
virtual DlCanvas* GetRootCanvas() = 0;

// Call this in-lieu of |SubmitFrame| to clear pre-roll state and
// Call this in-lieu of |SubmitFlutterView| to clear pre-roll state and
// sets the stage for the next pre-roll.
virtual void CancelFrame() = 0;

Expand All @@ -392,13 +415,11 @@ class ExternalViewEmbedder {
// The `raster_thread_merger` will be null if |SupportsDynamicThreadMerging|
// returns false.
virtual void BeginFrame(
SkISize frame_size,
GrDirectContext* context,
double device_pixel_ratio,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) = 0;

virtual void PrerollCompositeEmbeddedView(
int64_t view_id,
int64_t platform_view_id,
std::unique_ptr<EmbeddedViewParams> params) = 0;

// This needs to get called after |Preroll| finishes on the layer tree.
Expand All @@ -411,14 +432,19 @@ class ExternalViewEmbedder {
}

// Must be called on the UI thread.
virtual DlCanvas* CompositeEmbeddedView(int64_t view_id) = 0;
virtual DlCanvas* CompositeEmbeddedView(int64_t platform_view_id) = 0;

// Prepare for a view to be drawn.
virtual void PrepareFlutterView(int64_t flutter_view_id,
SkISize frame_size,
double device_pixel_ratio) = 0;

// Implementers must submit the frame by calling frame.Submit().
//
// This method can mutate the root Skia canvas before submitting the frame.
//
// It can also allocate frames for overlay surfaces to compose hybrid views.
virtual void SubmitFrame(
virtual void SubmitFlutterView(
GrDirectContext* context,
const std::shared_ptr<impeller::AiksContext>& aiks_context,
std::unique_ptr<SurfaceFrame> frame);
Expand Down Expand Up @@ -462,7 +488,7 @@ class ExternalViewEmbedder {

// Pushes the platform view id of a visited platform view to a list of
// visited platform views.
virtual void PushVisitedPlatformView(int64_t view_id) {}
virtual void PushVisitedPlatformView(int64_t platform_view_id) {}

// Pushes a DlImageFilter object to each platform view within a list of
// visited platform views.
Expand Down
7 changes: 5 additions & 2 deletions flow/testing/mock_embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ void MockViewEmbedder::CancelFrame() {}

// |ExternalViewEmbedder|
void MockViewEmbedder::BeginFrame(
SkISize frame_size,
GrDirectContext* context,
double device_pixel_ratio,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {}

// |ExternalViewEmbedder|
void MockViewEmbedder::PrepareFlutterView(int64_t flutter_view_id,
SkISize frame_size,
double device_pixel_ratio) {}

// |ExternalViewEmbedder|
void MockViewEmbedder::PrerollCompositeEmbeddedView(
int64_t view_id,
Expand Down
9 changes: 6 additions & 3 deletions flow/testing/mock_embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ class MockViewEmbedder : public ExternalViewEmbedder {
void CancelFrame() override;

// |ExternalViewEmbedder|
void BeginFrame(SkISize frame_size,
GrDirectContext* context,
double device_pixel_ratio,
void BeginFrame(GrDirectContext* context,
const fml::RefPtr<fml::RasterThreadMerger>&
raster_thread_merger) override;

// |ExternalViewEmbedder|
void PrepareFlutterView(int64_t flutter_view_id,
SkISize frame_size,
double device_pixel_ratio) override;

// |ExternalViewEmbedder|
void PrerollCompositeEmbeddedView(
int64_t view_id,
Expand Down
4 changes: 4 additions & 0 deletions lib/web_ui/test/engine/hash_codes_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ void main() {
internalBootstrapBrowserTest(() => testMain);
}

// Ignoring the deprecated member use because we're specifically testing
// deprecated API.
// ignore: deprecated_member_use

void testMain() {
test('hashValues and hashList can hash lots of huge values effectively', () {
final int hashValueFromArgs = hashValues(
Expand Down
20 changes: 6 additions & 14 deletions shell/common/rasterizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -562,13 +562,6 @@ Rasterizer::DoDrawResult Rasterizer::DrawToSurfaces(
std::unique_ptr<FrameItem> Rasterizer::DrawToSurfacesUnsafe(
FrameTimingsRecorder& frame_timings_recorder,
std::vector<std::unique_ptr<LayerTreeTask>> tasks) {
// TODO(dkwingsmt): The rasterizer only supports rendering a single view
// and that view must be the implicit view. Properly support multi-view
// in the future.
// See https://github.com/flutter/flutter/issues/135530, item 2 & 4.
FML_CHECK(tasks.size() == 1u) << "Unexpected size of " << tasks.size();
FML_DCHECK(tasks.front()->view_id == kFlutterImplicitViewId);

compositor_context_->ui_time().SetLapTime(
frame_timings_recorder.GetBuildDuration());

Expand All @@ -593,11 +586,8 @@ std::unique_ptr<FrameItem> Rasterizer::DrawToSurfacesUnsafe(
if (external_view_embedder_) {
FML_DCHECK(!external_view_embedder_->GetUsedThisFrame());
external_view_embedder_->SetUsedThisFrame(true);
external_view_embedder_->BeginFrame(
// TODO(dkwingsmt): Add all views here.
// See https://github.com/flutter/flutter/issues/135530, item 4.
tasks.front()->layer_tree->frame_size(), surface_->GetContext(),
tasks.front()->device_pixel_ratio, raster_thread_merger_);
external_view_embedder_->BeginFrame(surface_->GetContext(),
raster_thread_merger_);
}

std::optional<fml::TimePoint> presentation_time = std::nullopt;
Expand Down Expand Up @@ -664,6 +654,8 @@ DrawSurfaceStatus Rasterizer::DrawToSurfaceUnsafe(

DlCanvas* embedder_root_canvas = nullptr;
if (external_view_embedder_) {
external_view_embedder_->PrepareFlutterView(
view_id, layer_tree.frame_size(), device_pixel_ratio);
// TODO(dkwingsmt): Add view ID here.
embedder_root_canvas = external_view_embedder_->GetRootCanvas();
}
Expand Down Expand Up @@ -704,7 +696,7 @@ DrawSurfaceStatus Rasterizer::DrawToSurfaceUnsafe(
// for accurate performance metrics.
if (frame->framebuffer_info().supports_partial_repaint &&
!layer_tree.is_leaf_layer_tracing_enabled()) {
// Disable partial repaint if external_view_embedder_ SubmitFrame is
// Disable partial repaint if external_view_embedder_ SubmitFlutterView is
// involved - ExternalViewEmbedder unconditionally clears the entire
// surface and also partial repaint with platform view present is
// something that still need to be figured out.
Expand Down Expand Up @@ -750,7 +742,7 @@ DrawSurfaceStatus Rasterizer::DrawToSurfaceUnsafe(
if (external_view_embedder_ &&
(!raster_thread_merger_ || raster_thread_merger_->IsMerged())) {
FML_DCHECK(!frame->IsSubmitted());
external_view_embedder_->SubmitFrame(
external_view_embedder_->SubmitFlutterView(
surface_->GetContext(), surface_->GetAiksContext(), std::move(frame));
} else {
frame->Submit();
Expand Down
Loading

0 comments on commit e532e4b

Please sign in to comment.