Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit aab33c1

Browse files
author
Emmanuel Garcia
authored
Ensure JNI is not called from raster thread (#21665)
1 parent 1573991 commit aab33c1

File tree

5 files changed

+154
-3
lines changed

5 files changed

+154
-3
lines changed

shell/platform/android/external_view_embedder/external_view_embedder.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,15 +265,17 @@ void AndroidExternalViewEmbedder::BeginFrame(
265265

266266
// The surface size changed. Therefore, destroy existing surfaces as
267267
// the existing surfaces in the pool can't be recycled.
268-
if (frame_size_ != frame_size) {
268+
if (frame_size_ != frame_size && raster_thread_merger->IsOnPlatformThread()) {
269269
surface_pool_->DestroyLayers(jni_facade_);
270270
}
271-
frame_size_ = frame_size;
272-
device_pixel_ratio_ = device_pixel_ratio;
271+
surface_pool_->SetFrameSize(frame_size);
273272
// JNI method must be called on the platform thread.
274273
if (raster_thread_merger->IsOnPlatformThread()) {
275274
jni_facade_->FlutterViewBeginFrame();
276275
}
276+
277+
frame_size_ = frame_size;
278+
device_pixel_ratio_ = device_pixel_ratio;
277279
}
278280

279281
// |ExternalViewEmbedder|

shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,91 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) {
557557
raster_thread_merger);
558558
}
559559

560+
TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) {
561+
auto jni_mock = std::make_shared<JNIMock>();
562+
auto android_context =
563+
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
564+
565+
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
566+
auto gr_context = GrDirectContext::MakeMock(nullptr);
567+
auto frame_size = SkISize::Make(1000, 1000);
568+
auto surface_factory =
569+
[gr_context, window, frame_size](
570+
std::shared_ptr<AndroidContext> android_context,
571+
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
572+
auto surface_frame_1 = std::make_unique<SurfaceFrame>(
573+
SkSurface::MakeNull(1000, 1000), false,
574+
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
575+
return true;
576+
});
577+
578+
auto surface_mock = std::make_unique<SurfaceMock>();
579+
EXPECT_CALL(*surface_mock, AcquireFrame(frame_size))
580+
.WillOnce(Return(ByMove(std::move(surface_frame_1))));
581+
582+
auto android_surface_mock = std::make_unique<AndroidSurfaceMock>();
583+
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));
584+
585+
EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()))
586+
.WillOnce(Return(ByMove(std::move(surface_mock))));
587+
588+
EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
589+
590+
return android_surface_mock;
591+
};
592+
593+
auto embedder = std::make_unique<AndroidExternalViewEmbedder>(
594+
android_context, jni_mock, surface_factory);
595+
596+
// ------------------ First frame ------------------ //
597+
{
598+
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
599+
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
600+
embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger);
601+
602+
// Add an Android view.
603+
MutatorsStack stack1;
604+
// TODO(egarciad): Investigate why Flow applies the device pixel ratio to
605+
// the offsetPixels, but not the sizePoints.
606+
auto view_params_1 = std::make_unique<EmbeddedViewParams>(
607+
SkMatrix(), SkSize::Make(200, 200), stack1);
608+
609+
embedder->PrerollCompositeEmbeddedView(0, std::move(view_params_1));
610+
611+
// This simulates Flutter UI that intersects with the Android view.
612+
embedder->CompositeEmbeddedView(0)->drawRect(
613+
SkRect::MakeXYWH(50, 50, 200, 200), SkPaint());
614+
615+
// Create a new overlay surface.
616+
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
617+
.WillOnce(Return(
618+
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
619+
0, window))));
620+
// The JNI call to display the Android view.
621+
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200,
622+
300, 300, stack1));
623+
EXPECT_CALL(*jni_mock,
624+
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
625+
626+
auto surface_frame =
627+
std::make_unique<SurfaceFrame>(SkSurface::MakeNull(1000, 1000), false,
628+
[](const SurfaceFrame& surface_frame,
629+
SkCanvas* canvas) { return true; });
630+
embedder->SubmitFrame(gr_context.get(), std::move(surface_frame));
631+
632+
EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
633+
embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger);
634+
}
635+
636+
// Changing the frame size from the raster thread does not make JNI calls.
637+
638+
EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(0);
639+
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()).Times(0);
640+
641+
embedder->BeginFrame(SkISize::Make(30, 40), nullptr, 1.0,
642+
GetThreadMergerFromRasterThread());
643+
}
644+
560645
TEST(AndroidExternalViewEmbedder, SupportsDynamicThreadMerging) {
561646
auto jni_mock = std::make_shared<JNIMock>();
562647

shell/platform/android/external_view_embedder/surface_pool.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ std::shared_ptr<OverlayLayer> SurfacePool::GetLayer(
2424
std::shared_ptr<AndroidContext> android_context,
2525
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
2626
const AndroidSurface::Factory& surface_factory) {
27+
// Destroy current layers in the pool if the frame size has changed.
28+
if (requested_frame_size_ != current_frame_size_) {
29+
DestroyLayers(jni_facade);
30+
}
31+
2732
intptr_t gr_context_key = reinterpret_cast<intptr_t>(gr_context);
2833
// Allocate a new surface if there isn't one available.
2934
if (available_layer_index_ >= layers_.size()) {
@@ -63,6 +68,7 @@ std::shared_ptr<OverlayLayer> SurfacePool::GetLayer(
6368
layer->surface = std::move(surface);
6469
}
6570
available_layer_index_++;
71+
current_frame_size_ = requested_frame_size_;
6672
return layer;
6773
}
6874

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

96+
void SurfacePool::SetFrameSize(SkISize frame_size) {
97+
requested_frame_size_ = frame_size;
98+
}
99+
90100
} // namespace flutter

shell/platform/android/external_view_embedder/surface_pool.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ class SurfacePool {
6767
// Destroys all the layers in the pool.
6868
void DestroyLayers(std::shared_ptr<PlatformViewAndroidJNI> jni_facade);
6969

70+
// Sets the frame size used by the layers in the pool.
71+
// If the current layers in the pool have a different frame size,
72+
// then they are deallocated as soon as |GetLayer| is called.
73+
void SetFrameSize(SkISize frame_size);
74+
7075
private:
7176
// The index of the entry in the layers_ vector that determines the beginning
7277
// of the unused layers. For example, consider the following vector:
@@ -81,7 +86,15 @@ class SurfacePool {
8186
// This indicates that entries starting from 1 can be reused meanwhile the
8287
// entry at position 0 cannot be reused.
8388
size_t available_layer_index_ = 0;
89+
90+
// The layers in the pool.
8491
std::vector<std::shared_ptr<OverlayLayer>> layers_;
92+
93+
// The frame size of the layers in the pool.
94+
SkISize current_frame_size_;
95+
96+
// The frame size to be used by future layers.
97+
SkISize requested_frame_size_;
8598
};
8699

87100
} // namespace flutter

shell/platform/android/external_view_embedder/surface_pool_unittests.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,5 +202,46 @@ TEST(SurfacePool, DestroyLayers) {
202202
ASSERT_TRUE(pool->GetUnusedLayers().empty());
203203
}
204204

205+
TEST(SurfacePool, DestroyLayers__frameSizeChanged) {
206+
auto pool = std::make_unique<SurfacePool>();
207+
auto jni_mock = std::make_shared<JNIMock>();
208+
209+
auto gr_context = GrDirectContext::MakeMock(nullptr);
210+
auto android_context =
211+
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
212+
213+
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
214+
215+
auto surface_factory =
216+
[gr_context, window](std::shared_ptr<AndroidContext> android_context,
217+
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
218+
auto android_surface_mock = std::make_unique<AndroidSurfaceMock>();
219+
EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()));
220+
EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
221+
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));
222+
return android_surface_mock;
223+
};
224+
pool->SetFrameSize(SkISize::Make(10, 10));
225+
EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(0);
226+
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
227+
.Times(1)
228+
.WillOnce(Return(
229+
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
230+
0, window))));
231+
232+
pool->GetLayer(gr_context.get(), android_context, jni_mock, surface_factory);
233+
234+
pool->SetFrameSize(SkISize::Make(20, 20));
235+
EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(1);
236+
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
237+
.Times(1)
238+
.WillOnce(Return(
239+
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
240+
1, window))));
241+
pool->GetLayer(gr_context.get(), android_context, jni_mock, surface_factory);
242+
243+
ASSERT_TRUE(pool->GetUnusedLayers().empty());
244+
}
245+
205246
} // namespace testing
206247
} // namespace flutter

0 commit comments

Comments
 (0)