Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ vars = {
# Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS.
# You can use //tools/dart/create_updated_flutter_deps.py to produce
# updated revision list of existing dependencies.
'dart_revision': 'efd753621946a89008b76b76d85d54d1aa57fce8',
'dart_revision': '1d923a386d7a9f384435a2b4c1287b2937504566',

# WARNING: DO NOT EDIT MANUALLY
# The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,15 +265,17 @@ void AndroidExternalViewEmbedder::BeginFrame(

// The surface size changed. Therefore, destroy existing surfaces as
// the existing surfaces in the pool can't be recycled.
if (frame_size_ != frame_size) {
if (frame_size_ != frame_size && raster_thread_merger->IsOnPlatformThread()) {
surface_pool_->DestroyLayers(jni_facade_);
}
frame_size_ = frame_size;
device_pixel_ratio_ = device_pixel_ratio;
surface_pool_->SetFrameSize(frame_size);
// JNI method must be called on the platform thread.
if (raster_thread_merger->IsOnPlatformThread()) {
jni_facade_->FlutterViewBeginFrame();
}

frame_size_ = frame_size;
device_pixel_ratio_ = device_pixel_ratio;
}

// |ExternalViewEmbedder|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,91 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) {
raster_thread_merger);
}

TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) {
auto jni_mock = std::make_shared<JNIMock>();
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);

auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
auto gr_context = GrDirectContext::MakeMock(nullptr);
auto frame_size = SkISize::Make(1000, 1000);
auto surface_factory =
[gr_context, window, frame_size](
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
auto surface_frame_1 = std::make_unique<SurfaceFrame>(
SkSurface::MakeNull(1000, 1000), false,
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
return true;
});

auto surface_mock = std::make_unique<SurfaceMock>();
EXPECT_CALL(*surface_mock, AcquireFrame(frame_size))
.WillOnce(Return(ByMove(std::move(surface_frame_1))));

auto android_surface_mock = std::make_unique<AndroidSurfaceMock>();
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));

EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()))
.WillOnce(Return(ByMove(std::move(surface_mock))));

EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));

return android_surface_mock;
};

auto embedder = std::make_unique<AndroidExternalViewEmbedder>(
android_context, jni_mock, surface_factory);

// ------------------ First frame ------------------ //
{
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger);

// Add an Android view.
MutatorsStack stack1;
// TODO(egarciad): Investigate why Flow applies the device pixel ratio to
// the offsetPixels, but not the sizePoints.
auto view_params_1 = std::make_unique<EmbeddedViewParams>(
SkMatrix(), SkSize::Make(200, 200), stack1);

embedder->PrerollCompositeEmbeddedView(0, std::move(view_params_1));

// This simulates Flutter UI that intersects with the Android view.
embedder->CompositeEmbeddedView(0)->drawRect(
SkRect::MakeXYWH(50, 50, 200, 200), SkPaint());

// Create a new overlay surface.
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
0, window))));
// The JNI call to display the Android view.
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200,
300, 300, stack1));
EXPECT_CALL(*jni_mock,
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));

auto surface_frame =
std::make_unique<SurfaceFrame>(SkSurface::MakeNull(1000, 1000), false,
[](const SurfaceFrame& surface_frame,
SkCanvas* canvas) { return true; });
embedder->SubmitFrame(gr_context.get(), std::move(surface_frame));

EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger);
}

// Changing the frame size from the raster thread does not make JNI calls.

EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(0);
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()).Times(0);

embedder->BeginFrame(SkISize::Make(30, 40), nullptr, 1.0,
GetThreadMergerFromRasterThread());
}

TEST(AndroidExternalViewEmbedder, SupportsDynamicThreadMerging) {
auto jni_mock = std::make_shared<JNIMock>();

Expand Down
10 changes: 10 additions & 0 deletions shell/platform/android/external_view_embedder/surface_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ std::shared_ptr<OverlayLayer> SurfacePool::GetLayer(
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
const AndroidSurface::Factory& surface_factory) {
// Destroy current layers in the pool if the frame size has changed.
if (requested_frame_size_ != current_frame_size_) {
DestroyLayers(jni_facade);
}

intptr_t gr_context_key = reinterpret_cast<intptr_t>(gr_context);
// Allocate a new surface if there isn't one available.
if (available_layer_index_ >= layers_.size()) {
Expand Down Expand Up @@ -63,6 +68,7 @@ std::shared_ptr<OverlayLayer> SurfacePool::GetLayer(
layer->surface = std::move(surface);
}
available_layer_index_++;
current_frame_size_ = requested_frame_size_;
return layer;
}

Expand All @@ -87,4 +93,8 @@ std::vector<std::shared_ptr<OverlayLayer>> SurfacePool::GetUnusedLayers() {
return results;
}

void SurfacePool::SetFrameSize(SkISize frame_size) {
requested_frame_size_ = frame_size;
}

} // namespace flutter
13 changes: 13 additions & 0 deletions shell/platform/android/external_view_embedder/surface_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ class SurfacePool {
// Destroys all the layers in the pool.
void DestroyLayers(std::shared_ptr<PlatformViewAndroidJNI> jni_facade);

// Sets the frame size used by the layers in the pool.
// If the current layers in the pool have a different frame size,
// then they are deallocated as soon as |GetLayer| is called.
void SetFrameSize(SkISize frame_size);

private:
// The index of the entry in the layers_ vector that determines the beginning
// of the unused layers. For example, consider the following vector:
Expand All @@ -81,7 +86,15 @@ class SurfacePool {
// This indicates that entries starting from 1 can be reused meanwhile the
// entry at position 0 cannot be reused.
size_t available_layer_index_ = 0;

// The layers in the pool.
std::vector<std::shared_ptr<OverlayLayer>> layers_;

// The frame size of the layers in the pool.
SkISize current_frame_size_;

// The frame size to be used by future layers.
SkISize requested_frame_size_;
};

} // namespace flutter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,5 +201,46 @@ TEST(SurfacePool, DestroyLayers) {
ASSERT_TRUE(pool->GetUnusedLayers().empty());
}

TEST(SurfacePool, DestroyLayers__frameSizeChanged) {
auto pool = std::make_unique<SurfacePool>();
auto jni_mock = std::make_shared<JNIMock>();

auto gr_context = GrDirectContext::MakeMock(nullptr);
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);

auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);

auto surface_factory =
[gr_context, window](std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
auto android_surface_mock = std::make_unique<AndroidSurfaceMock>();
EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()));
EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));
return android_surface_mock;
};
pool->SetFrameSize(SkISize::Make(10, 10));
EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(0);
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
.Times(1)
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
0, window))));

pool->GetLayer(gr_context.get(), android_context, jni_mock, surface_factory);

pool->SetFrameSize(SkISize::Make(20, 20));
EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(1);
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
.Times(1)
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
1, window))));
pool->GetLayer(gr_context.get(), android_context, jni_mock, surface_factory);

ASSERT_TRUE(pool->GetUnusedLayers().empty());
}

} // namespace testing
} // namespace flutter
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand Down Expand Up @@ -83,7 +84,7 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
// The views returned by `PlatformView#getView()`.
//
// This only applies to hybrid composition.
private final SparseArray<View> platformViews;
private final SparseArray<PlatformView> platformViews;

// The platform view parents that are appended to `FlutterView`.
// If an entry in `platformViews` doesn't have an entry in this array, the platform view isn't
Expand Down Expand Up @@ -143,32 +144,24 @@ public void createAndroidViewForPlatformView(
}

final PlatformView platformView = factory.create(context, request.viewId, createParams);
final View view = platformView.getView();
if (view == null) {
throw new IllegalStateException(
"PlatformView#getView() returned null, but an Android view reference was expected.");
}
if (view.getParent() != null) {
throw new IllegalStateException(
"The Android view returned from PlatformView#getView() was already added to a parent view.");
}
platformViews.put(request.viewId, view);
platformViews.put(request.viewId, platformView);
}

@Override
public void disposeAndroidViewForPlatformView(int viewId) {
// Hybrid view.
final View platformView = platformViews.get(viewId);
final PlatformView platformView = platformViews.get(viewId);
final FlutterMutatorView parentView = platformViewParent.get(viewId);
if (platformView != null) {
if (parentView != null) {
parentView.removeView(platformView);
parentView.removeView(platformView.getView());
}
platformViews.remove(viewId);
platformView.dispose();
}

if (parentView != null) {
((FlutterView) flutterView).removeView(parentView);
((ViewGroup) parentView.getParent()).removeView(parentView);
platformViewParent.remove(viewId);
}
}
Expand Down Expand Up @@ -311,8 +304,10 @@ public void onTouch(@NonNull PlatformViewsChannel.PlatformViewTouch touch) {
vdControllers.get(touch.viewId).dispatchTouchEvent(event);
} else if (platformViews.get(viewId) != null) {
final MotionEvent event = toMotionEvent(density, touch, /*usingVirtualDiplays=*/ false);
View view = platformViews.get(touch.viewId);
view.dispatchTouchEvent(event);
View view = platformViews.get(touch.viewId).getView();
if (view != null) {
view.dispatchTouchEvent(event);
}
} else {
throw new IllegalStateException("Sending touch to an unknown view with id: " + viewId);
}
Expand Down Expand Up @@ -578,7 +573,7 @@ public void onPreEngineRestart() {
public View getPlatformViewById(Integer id) {
// Hybrid composition.
if (platformViews.get(id) != null) {
return platformViews.get(id);
return platformViews.get(id).getView();
}
VirtualDisplayController controller = vdControllers.get(id);
if (controller == null) {
Expand Down Expand Up @@ -688,6 +683,10 @@ private void flushAllViews() {
controller.dispose();
}
vdControllers.clear();

while (platformViews.size() > 0) {
channelHandler.disposeAndroidViewForPlatformView(platformViews.keyAt(0));
}
}

private void initializeRootImageViewIfNeeded() {
Expand All @@ -699,19 +698,27 @@ private void initializeRootImageViewIfNeeded() {

@VisibleForTesting
void initializePlatformViewIfNeeded(int viewId) {
final View view = platformViews.get(viewId);
if (view == null) {
final PlatformView platformView = platformViews.get(viewId);
if (platformView == null) {
throw new IllegalStateException(
"Platform view hasn't been initialized from the platform view channel.");
}
if (platformViewParent.get(viewId) != null) {
return;
}
if (platformView.getView() == null) {
throw new IllegalStateException(
"PlatformView#getView() returned null, but an Android view reference was expected.");
}
if (platformView.getView().getParent() != null) {
throw new IllegalStateException(
"The Android view returned from PlatformView#getView() was already added to a parent view.");
}
final FlutterMutatorView parentView =
new FlutterMutatorView(
context, context.getResources().getDisplayMetrics().density, androidTouchProcessor);
platformViewParent.put(viewId, parentView);
parentView.addView(view);
parentView.addView(platformView.getView());
((FlutterView) flutterView).addView(parentView);
}

Expand All @@ -738,9 +745,11 @@ public void onDisplayPlatformView(

final FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(viewWidth, viewHeight);
final View platformView = platformViews.get(viewId);
platformView.setLayoutParams(layoutParams);
platformView.bringToFront();
final View view = platformViews.get(viewId).getView();
if (view != null) {
view.setLayoutParams(layoutParams);
view.bringToFront();
}
currentFrameUsedPlatformViewIds.add(viewId);
}

Expand Down
Loading