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

Commit 09cc270

Browse files
authored
Merge branch 'main' into wb/johnoneil/stb-atlas-padding-fix
2 parents 0936172 + 09dadce commit 09cc270

9 files changed

Lines changed: 251 additions & 28 deletions

File tree

runtime/dart_isolate.cc

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -504,9 +504,12 @@ bool DartIsolate::Initialize(Dart_Isolate dart_isolate) {
504504
Dart_SetCurrentUserTag(Dart_NewUserTag("AppStartUp"));
505505
}
506506

507-
SetMessageHandlingTaskRunner(is_platform_isolate_
508-
? GetTaskRunners().GetPlatformTaskRunner()
509-
: GetTaskRunners().GetUITaskRunner());
507+
if (is_platform_isolate_) {
508+
SetMessageHandlingTaskRunner(GetTaskRunners().GetPlatformTaskRunner(),
509+
true);
510+
} else {
511+
SetMessageHandlingTaskRunner(GetTaskRunners().GetUITaskRunner(), false);
512+
}
510513

511514
if (tonic::CheckAndHandleError(
512515
Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))) {
@@ -562,30 +565,41 @@ void DartIsolate::LoadLoadingUnitError(intptr_t loading_unit_id,
562565
}
563566

564567
void DartIsolate::SetMessageHandlingTaskRunner(
565-
const fml::RefPtr<fml::TaskRunner>& runner) {
568+
const fml::RefPtr<fml::TaskRunner>& runner,
569+
bool post_directly_to_runner) {
566570
if (!runner) {
567571
return;
568572
}
569573

570574
message_handling_task_runner_ = runner;
571575

572-
message_handler().Initialize([runner](std::function<void()> task) {
576+
tonic::DartMessageHandler::TaskDispatcher dispatcher;
577+
573578
#ifdef OS_FUCHSIA
574-
runner->PostTask([task = std::move(task)]() {
575-
TRACE_EVENT0("flutter", "DartIsolate::HandleMessage");
576-
task();
577-
});
578-
#else
579-
auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
580-
task_queues->RegisterTask(
581-
runner->GetTaskQueueId(),
582-
[task = std::move(task)]() {
583-
TRACE_EVENT0("flutter", "DartIsolate::HandleMessage");
584-
task();
585-
},
586-
fml::TimePoint::Now(), fml::TaskSourceGrade::kDartEventLoop);
579+
post_directly_to_runner = true;
587580
#endif
588-
});
581+
582+
if (post_directly_to_runner) {
583+
dispatcher = [runner](std::function<void()> task) {
584+
runner->PostTask([task = std::move(task)]() {
585+
TRACE_EVENT0("flutter", "DartIsolate::HandleMessage");
586+
task();
587+
});
588+
};
589+
} else {
590+
dispatcher = [runner](std::function<void()> task) {
591+
auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
592+
task_queues->RegisterTask(
593+
runner->GetTaskQueueId(),
594+
[task = std::move(task)]() {
595+
TRACE_EVENT0("flutter", "DartIsolate::HandleMessage");
596+
task();
597+
},
598+
fml::TimePoint::Now(), fml::TaskSourceGrade::kDartEventLoop);
599+
};
600+
}
601+
602+
message_handler().Initialize(dispatcher);
589603
}
590604

591605
// Updating thread names here does not change the underlying OS thread names.

runtime/dart_isolate.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,8 @@ class DartIsolate : public UIDartState {
470470
///
471471
[[nodiscard]] bool Initialize(Dart_Isolate dart_isolate);
472472

473-
void SetMessageHandlingTaskRunner(const fml::RefPtr<fml::TaskRunner>& runner);
473+
void SetMessageHandlingTaskRunner(const fml::RefPtr<fml::TaskRunner>& runner,
474+
bool post_directly_to_runner);
474475

475476
bool LoadKernel(const std::shared_ptr<const fml::Mapping>& mapping,
476477
bool last_piece);

shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import android.view.SurfaceView;
1313
import androidx.annotation.NonNull;
1414
import androidx.annotation.Nullable;
15+
import androidx.annotation.VisibleForTesting;
1516
import io.flutter.Log;
1617
import io.flutter.embedding.engine.renderer.FlutterRenderer;
1718
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
@@ -168,6 +169,10 @@ public FlutterRenderer getAttachedRenderer() {
168169
return flutterRenderer;
169170
}
170171

172+
@VisibleForTesting
173+
/* package */ boolean isSurfaceAvailableForRendering() {
174+
return isSurfaceAvailableForRendering;
175+
}
171176
/**
172177
* Invoked by the owner of this {@code FlutterSurfaceView} when it wants to begin rendering a
173178
* Flutter UI to this {@code FlutterSurfaceView}.
@@ -215,8 +220,6 @@ public void detachFromRenderer() {
215220
disconnectSurfaceFromRenderer();
216221
}
217222

218-
pause();
219-
220223
// Make the SurfaceView invisible to avoid showing a black rectangle.
221224
setAlpha(0.0f);
222225
flutterRenderer.removeIsDisplayingFlutterUiListener(flutterUiDisplayListener);
@@ -248,7 +251,7 @@ public void resume() {
248251

249252
// If we're already attached to an Android window then we're now attached to both a renderer
250253
// and the Android window. We can begin rendering now.
251-
if (isSurfaceAvailableForRendering) {
254+
if (isSurfaceAvailableForRendering()) {
252255
Log.v(
253256
TAG,
254257
"Surface is available for rendering. Connecting FlutterRenderer to Android surface.");

shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ public FlutterRenderer getAttachedRenderer() {
124124
return flutterRenderer;
125125
}
126126

127+
@VisibleForTesting
128+
/* package */ boolean isSurfaceAvailableForRendering() {
129+
return isSurfaceAvailableForRendering;
130+
}
131+
127132
/**
128133
* Invoked by the owner of this {@code FlutterTextureView} when it wants to begin rendering a
129134
* Flutter UI to this {@code FlutterTextureView}.
@@ -170,8 +175,6 @@ public void detachFromRenderer() {
170175
disconnectSurfaceFromRenderer();
171176
}
172177

173-
pause();
174-
175178
flutterRenderer = null;
176179
} else {
177180
Log.w(TAG, "detachFromRenderer() invoked when no FlutterRenderer was attached.");
@@ -198,7 +201,7 @@ public void resume() {
198201

199202
// If we're already attached to an Android window then we're now attached to both a renderer
200203
// and the Android window. We can begin rendering now.
201-
if (isSurfaceAvailableForRendering) {
204+
if (isSurfaceAvailableForRendering()) {
202205
Log.v(
203206
TAG,
204207
"Surface is available for rendering. Connecting FlutterRenderer to Android surface.");
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package io.flutter.embedding.android;
2+
3+
import static io.flutter.Build.API_LEVELS;
4+
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.Mockito.mock;
6+
import static org.mockito.Mockito.never;
7+
import static org.mockito.Mockito.spy;
8+
import static org.mockito.Mockito.times;
9+
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
11+
12+
import android.annotation.TargetApi;
13+
import android.view.Surface;
14+
import android.view.SurfaceHolder;
15+
import androidx.test.core.app.ApplicationProvider;
16+
import androidx.test.ext.junit.runners.AndroidJUnit4;
17+
import io.flutter.embedding.engine.FlutterJNI;
18+
import io.flutter.embedding.engine.renderer.FlutterRenderer;
19+
import org.junit.Test;
20+
import org.junit.runner.RunWith;
21+
import org.robolectric.annotation.Config;
22+
23+
@Config(manifest = Config.NONE)
24+
@RunWith(AndroidJUnit4.class)
25+
@TargetApi(API_LEVELS.API_30)
26+
public class FlutterSurfaceViewTest {
27+
@Test
28+
public void itShouldCreateANewSurfaceWhenReattachedAfterDetachingFromRenderer() {
29+
// Consider this scenario: In an add-to-app context, where multiple Flutter activities share the
30+
// same engine, a situation occurs. When navigating from FlutterActivity1 to FlutterActivity2,
31+
// the Flutter view associated with FlutterActivity1 is detached from the engine. Then, the
32+
// Flutter view of FlutterActivity2 is attached. Upon navigating back to FlutterActivity1, its
33+
// Flutter view is re-attached to the shared engine.
34+
//
35+
// The expected behavior is: When a Flutter view detaches from the shared engine, the associated
36+
// surface should be released. When the Flutter view re-attaches, a new surface should be
37+
// created.
38+
39+
// Setup the test.
40+
final FlutterSurfaceView surfaceView =
41+
spy(new FlutterSurfaceView(ApplicationProvider.getApplicationContext()));
42+
43+
FlutterJNI fakeFlutterJNI = mock(FlutterJNI.class);
44+
FlutterRenderer flutterRenderer = new FlutterRenderer(fakeFlutterJNI);
45+
46+
SurfaceHolder fakeSurfaceHolder = mock(SurfaceHolder.class);
47+
Surface fakeSurface = mock(Surface.class);
48+
when(surfaceView.getHolder()).thenReturn(fakeSurfaceHolder);
49+
when(fakeSurfaceHolder.getSurface()).thenReturn(fakeSurface);
50+
when(surfaceView.isSurfaceAvailableForRendering()).thenReturn(true);
51+
when(surfaceView.getWindowToken()).thenReturn(mock(android.os.IBinder.class));
52+
53+
// Execute the behavior under test.
54+
surfaceView.attachToRenderer(flutterRenderer);
55+
56+
// Verify the behavior under test.
57+
verify(fakeFlutterJNI, times(1)).onSurfaceCreated(any(Surface.class));
58+
59+
// Execute the behavior under test.
60+
surfaceView.detachFromRenderer();
61+
62+
// Verify the behavior under test.
63+
verify(fakeFlutterJNI, times(1)).onSurfaceDestroyed();
64+
65+
// Execute the behavior under test.
66+
surfaceView.attachToRenderer(flutterRenderer);
67+
68+
// Verify the behavior under test.
69+
verify(fakeFlutterJNI, never()).onSurfaceWindowChanged(any(Surface.class));
70+
verify(fakeFlutterJNI, times(2)).onSurfaceCreated(any(Surface.class));
71+
}
72+
}

shell/platform/android/test/io/flutter/embedding/android/FlutterTextureViewTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package io.flutter.embedding.android;
22

33
import static io.flutter.Build.API_LEVELS;
4+
import static org.mockito.ArgumentMatchers.any;
45
import static org.mockito.Mockito.mock;
6+
import static org.mockito.Mockito.never;
7+
import static org.mockito.Mockito.spy;
8+
import static org.mockito.Mockito.times;
59
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
611

712
import android.annotation.TargetApi;
813
import android.graphics.SurfaceTexture;
914
import android.view.Surface;
1015
import android.view.TextureView;
1116
import androidx.test.core.app.ApplicationProvider;
1217
import androidx.test.ext.junit.runners.AndroidJUnit4;
18+
import io.flutter.embedding.engine.FlutterJNI;
19+
import io.flutter.embedding.engine.renderer.FlutterRenderer;
1320
import org.junit.Test;
1421
import org.junit.runner.RunWith;
1522
import org.robolectric.annotation.Config;
@@ -31,4 +38,47 @@ public void surfaceTextureListenerReleasesRenderer() {
3138

3239
verify(mockRenderSurface).release();
3340
}
41+
42+
@Test
43+
public void itShouldCreateANewSurfaceWhenReattachedAfterDetachingFromRenderer() {
44+
// Consider this scenario: In an add-to-app context, where multiple Flutter activities share the
45+
// same engine, a situation occurs. When navigating from FlutterActivity1 to FlutterActivity2,
46+
// the Flutter view associated with FlutterActivity1 is detached from the engine. Then, the
47+
// Flutter view of FlutterActivity2 is attached. Upon navigating back to FlutterActivity1, its
48+
// Flutter view is re-attached to the shared engine.
49+
//
50+
// The expected behavior is: When a Flutter view detaches from the shared engine, the associated
51+
// surface should be released. When the Flutter view re-attaches, a new surface should be
52+
// created.
53+
54+
// Setup the test.
55+
final FlutterTextureView textureView =
56+
spy(new FlutterTextureView(ApplicationProvider.getApplicationContext()));
57+
58+
FlutterJNI fakeFlutterJNI = mock(FlutterJNI.class);
59+
FlutterRenderer flutterRenderer = new FlutterRenderer(fakeFlutterJNI);
60+
61+
when(textureView.isSurfaceAvailableForRendering()).thenReturn(true);
62+
when(textureView.getSurfaceTexture()).thenReturn(mock(SurfaceTexture.class));
63+
when(textureView.getWindowToken()).thenReturn(mock(android.os.IBinder.class));
64+
65+
// Execute the behavior under test.
66+
textureView.attachToRenderer(flutterRenderer);
67+
68+
// Verify the behavior under test.
69+
verify(fakeFlutterJNI, times(1)).onSurfaceCreated(any(Surface.class));
70+
71+
// Execute the behavior under test.
72+
textureView.detachFromRenderer();
73+
74+
// Verify the behavior under test.
75+
verify(fakeFlutterJNI, times(1)).onSurfaceDestroyed();
76+
77+
// Execute the behavior under test.
78+
textureView.attachToRenderer(flutterRenderer);
79+
80+
// Verify the behavior under test.
81+
verify(fakeFlutterJNI, never()).onSurfaceWindowChanged(any(Surface.class));
82+
verify(fakeFlutterJNI, times(2)).onSurfaceCreated(any(Surface.class));
83+
}
3484
}

shell/platform/darwin/graphics/FlutterDarwinContextMetalSkia.mm

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#include "flutter/fml/logging.h"
99
#include "flutter/shell/common/context_options.h"
1010
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
11+
#include "third_party/skia/include/gpu/GrDirectContext.h"
12+
#include "third_party/skia/include/gpu/ganesh/mtl/GrMtlBackendContext.h"
13+
#include "third_party/skia/include/gpu/ganesh/mtl/GrMtlDirectContext.h"
1114

1215
FLUTTER_ASSERT_ARC
1316

@@ -79,10 +82,12 @@ - (instancetype)initWithMTLDevice:(id<MTLDevice>)device
7982
commandQueue:(id<MTLCommandQueue>)commandQueue {
8083
const auto contextOptions =
8184
flutter::MakeDefaultContextOptions(flutter::ContextType::kRender, GrBackendApi::kMetal);
85+
GrMtlBackendContext backendContext = {};
8286
// Skia expect arguments to `MakeMetal` transfer ownership of the reference in for release later
8387
// when the GrDirectContext is collected.
84-
return GrDirectContext::MakeMetal((__bridge_retained void*)device,
85-
(__bridge_retained void*)commandQueue, contextOptions);
88+
backendContext.fDevice.reset((__bridge_retained void*)device);
89+
backendContext.fQueue.reset((__bridge_retained void*)commandQueue);
90+
return GrDirectContexts::MakeMetal(backendContext, contextOptions);
8691
}
8792

8893
- (void)dealloc {

shell/platform/embedder/fixtures/main.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ void invokePlatformTaskRunner() {
6060
PlatformDispatcher.instance.sendPlatformMessage('OhHi', null, null);
6161
}
6262

63+
@pragma('vm:entry-point')
64+
void invokePlatformThreadIsolate() {
65+
signalNativeTest();
66+
runOnPlatformThread(ffiSignalNativeTest);
67+
}
68+
6369
Float64List kTestTransform = () {
6470
final Float64List values = Float64List(16);
6571
values[0] = 1.0; // scaleX
@@ -87,6 +93,9 @@ external void notifyAccessibilityFeatures(bool reduceMotion);
8793
@pragma('vm:external-name', 'NotifySemanticsAction')
8894
external void notifySemanticsAction(int nodeId, int action, List<int> data);
8995

96+
@ffi.Native<ffi.Void Function()>(symbol: 'FFISignalNativeTest')
97+
external void ffiSignalNativeTest();
98+
9099
/// Returns a future that completes when
91100
/// `PlatformDispatcher.instance.onSemanticsEnabledChanged` fires.
92101
Future<void> get semanticsChanged {

0 commit comments

Comments
 (0)