5package io.flutter.embedding.engine.renderer;
7import static io.flutter.Build.API_LEVELS;
9import android.annotation.TargetApi;
10import android.content.ComponentCallbacks2;
12import android.graphics.ImageFormat;
14import android.graphics.SurfaceTexture;
15import android.hardware.HardwareBuffer;
16import android.hardware.SyncFence;
18import android.media.ImageReader;
23import androidx.annotation.Keep;
24import androidx.annotation.NonNull;
25import androidx.annotation.Nullable;
26import androidx.annotation.VisibleForTesting;
28import io.flutter.embedding.engine.FlutterJNI;
29import io.flutter.view.TextureRegistry;
30import java.io.IOException;
31import java.lang.ref.WeakReference;
32import java.nio.ByteBuffer;
33import java.util.ArrayDeque;
34import java.util.ArrayList;
35import java.util.HashMap;
36import java.util.HashSet;
37import java.util.Iterator;
40import java.util.concurrent.atomic.AtomicLong;
69 private static final String TAG =
"FlutterRenderer";
72 @NonNull
private final AtomicLong nextTextureId =
new AtomicLong(0
L);
73 @Nullable
private Surface surface;
74 private boolean isDisplayingFlutterUi =
false;
75 private final Handler handler =
new Handler();
85 public void onFlutterUiDisplayed() {
86 isDisplayingFlutterUi =
true;
90 public void onFlutterUiNoLongerDisplayed() {
91 isDisplayingFlutterUi =
false;
96 this.flutterJNI = flutterJNI;
97 this.flutterJNI.addIsDisplayingFlutterUiListener(flutterUiDisplayListener);
105 return isDisplayingFlutterUi;
113 flutterJNI.addIsDisplayingFlutterUiListener(listener);
115 if (isDisplayingFlutterUi) {
116 listener.onFlutterUiDisplayed();
125 flutterJNI.removeIsDisplayingFlutterUiListener(listener);
128 private void clearDeadListeners() {
129 final Iterator<WeakReference<OnTrimMemoryListener>> iterator = onTrimMemoryListeners.iterator();
130 while (iterator.hasNext()) {
131 WeakReference<OnTrimMemoryListener> listenerRef = iterator.next();
132 final OnTrimMemoryListener listener = listenerRef.get();
133 if (listener ==
null) {
143 clearDeadListeners();
144 onTrimMemoryListeners.add(
new WeakReference<>(listener));
153 for (WeakReference<OnTrimMemoryListener> listenerRef : onTrimMemoryListeners) {
154 if (listenerRef.get() == listener) {
155 onTrimMemoryListeners.remove(listenerRef);
196 final long id = nextTextureId.getAndIncrement();
198 registerImageTexture(
id, producer);
200 Log.
v(
TAG,
"New ImageReaderSurfaceProducer ID: " +
id);
211 Log.
v(
TAG,
"New SurfaceTextureSurfaceProducer ID: " +
texture.id());
224 Log.
v(
TAG,
"Creating a SurfaceTexture.");
225 final SurfaceTexture surfaceTexture =
new SurfaceTexture(0);
248 long textureId, @NonNull SurfaceTexture surfaceTexture) {
249 surfaceTexture.detachFromGLContext();
250 final SurfaceTextureRegistryEntry entry =
251 new SurfaceTextureRegistryEntry(textureId, surfaceTexture);
252 Log.
v(TAG,
"New SurfaceTexture ID: " + entry.id());
253 registerTexture(entry.id(), entry.textureWrapper());
263 Log.
v(
TAG,
"New ImageTextureEntry ID: " + entry.id());
264 registerImageTexture(entry.id(), entry);
270 final Iterator<WeakReference<OnTrimMemoryListener>> iterator = onTrimMemoryListeners.iterator();
271 while (iterator.hasNext()) {
272 WeakReference<OnTrimMemoryListener> listenerRef = iterator.next();
274 if (listener !=
null) {
285 private final long id;
287 private boolean released;
293 Runnable onFrameConsumed =
295 if (frameConsumedListener !=
null) {
296 frameConsumedListener.onFrameConsumed();
305 SurfaceTexture.OnFrameAvailableListener onFrameListener =
307 if (released || !flutterJNI.isAttached()) {
314 textureWrapper.markDirty();
315 scheduleEngineFrame();
321 this.
surfaceTexture().setOnFrameAvailableListener(onFrameListener,
new Handler());
326 if (trimMemoryListener !=
null) {
327 trimMemoryListener.onTrimMemory(
level);
331 private void removeListener() {
337 return textureWrapper;
343 return textureWrapper.surfaceTexture();
356 Log.
v(
TAG,
"Releasing a SurfaceTexture (" +
id +
").");
357 textureWrapper.release();
358 unregisterTexture(
id);
378 frameConsumedListener = listener;
383 trimMemoryListener = listener;
388 private final long id;
393 this.flutterJNI = flutterJNI;
401 Log.
v(
TAG,
"Releasing a Texture (" +
id +
").");
416 private static final String
TAG =
"ImageReaderSurfaceProducer";
417 private static final int MAX_IMAGES = 5;
420 private static final boolean VERBOSE_LOGS =
false;
424 private static final boolean CLEANUP_ON_MEMORY_PRESSURE =
true;
426 private final long id;
428 private boolean released;
430 private boolean ignoringFence =
false;
432 private boolean trimOnMemoryPressure = CLEANUP_ON_MEMORY_PRESSURE;
435 private int requestedWidth = 1;
436 private int requestedHeight = 1;
441 private boolean createNewReader =
true;
444 private long lastDequeueTime = 0;
445 private long lastQueueTime = 0;
446 private long lastScheduleTime = 0;
447 private int numTrims = 0;
449 private Object lock =
new Object();
451 private final ArrayDeque<PerImageReader> imageReaderQueue =
new ArrayDeque<PerImageReader>();
452 private final HashMap<ImageReader, PerImageReader> perImageReaders =
453 new HashMap<ImageReader, PerImageReader>();
454 private PerImage lastDequeuedImage =
null;
455 private PerImageReader lastReaderDequeuedFrom =
null;
458 private class PerImage {
460 public final long queuedTime;
462 public PerImage(
Image image,
long queuedTime) {
464 this.queuedTime = queuedTime;
469 private class PerImageReader {
470 public final ImageReader reader;
471 private final ArrayDeque<PerImage> imageQueue =
new ArrayDeque<PerImage>();
472 private boolean closed =
false;
474 private final ImageReader.OnImageAvailableListener onImageAvailableListener =
478 image = reader.acquireLatestImage();
479 }
catch (IllegalStateException
e) {
480 Log.
e(
TAG,
"onImageAvailable acquireLatestImage failed: " +
e);
485 if (released || closed) {
489 onImage(reader,
image);
492 public PerImageReader(ImageReader reader) {
493 this.reader = reader;
494 reader.setOnImageAvailableListener(
495 onImageAvailableListener,
new Handler(Looper.getMainLooper()));
502 PerImage perImage =
new PerImage(
image, System.nanoTime());
503 imageQueue.add(perImage);
505 while (imageQueue.size() > 2) {
506 PerImage r = imageQueue.removeFirst();
508 Log.
i(
TAG,
"" + reader.hashCode() +
" force closed image=" + r.image.hashCode());
515 PerImage dequeueImage() {
516 if (imageQueue.size() == 0) {
519 PerImage r = imageQueue.removeFirst();
525 return imageQueue.size() == 0 && lastReaderDequeuedFrom !=
this;
531 Log.
i(
TAG,
"Closing reader=" + reader.hashCode());
539 double ms = (double) deltaNanos / (
double) 1000000.0;
544 PerImageReader r = perImageReaders.get(reader);
546 r =
new PerImageReader(reader);
547 perImageReaders.put(reader, r);
548 imageReaderQueue.add(r);
550 Log.
i(
TAG,
"imageReaderQueue#=" + imageReaderQueue.size());
557 boolean change =
false;
559 while (imageReaderQueue.size() > 1) {
560 PerImageReader r = imageReaderQueue.peekFirst();
565 imageReaderQueue.removeFirst();
566 perImageReaders.remove(r.reader);
570 if (change && VERBOSE_LOGS) {
571 Log.
i(
TAG,
"Pruned image reader queue length=" + imageReaderQueue.size());
576 PerImage queuedImage =
null;
577 synchronized (lock) {
578 PerImageReader perReader = getOrCreatePerImageReader(reader);
579 queuedImage = perReader.queueImage(
image);
581 if (queuedImage ==
null) {
586 if (lastQueueTime != 0) {
587 long now = System.nanoTime();
588 long queueDelta = now - lastQueueTime;
594 + queuedImage.image.hashCode()
596 + deltaMillis(queueDelta));
599 lastQueueTime = System.nanoTime();
602 scheduleEngineFrame();
607 synchronized (lock) {
608 for (PerImageReader reader : imageReaderQueue) {
609 r = reader.dequeueImage();
615 if (lastDequeueTime != 0) {
616 long now = System.nanoTime();
617 long dequeueDelta = now - lastDequeueTime;
618 long queuedFor = now - r.queuedTime;
619 long scheduleDelay = now - lastScheduleTime;
623 + reader.reader.hashCode()
627 + deltaMillis(queuedFor)
629 + deltaMillis(dequeueDelta)
631 + deltaMillis(scheduleDelay));
632 lastDequeueTime = now;
634 lastDequeueTime = System.nanoTime();
637 if (lastDequeuedImage !=
null) {
642 + lastReaderDequeuedFrom.reader.hashCode()
644 + lastDequeuedImage.image.hashCode());
649 lastDequeuedImage.image.close();
650 lastDequeuedImage =
null;
654 lastDequeuedImage = r;
655 lastReaderDequeuedFrom = reader;
658 pruneImageReaderQueue();
665 if (!trimOnMemoryPressure) {
668 if (
level < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
671 synchronized (lock) {
675 createNewReader =
true;
678 private void releaseInternal() {
683 private void cleanup() {
684 synchronized (lock) {
685 for (PerImageReader pir : perImageReaders.values()) {
686 if (lastReaderDequeuedFrom == pir) {
687 lastReaderDequeuedFrom =
null;
691 perImageReaders.clear();
692 if (lastDequeuedImage !=
null) {
693 lastDequeuedImage.image.close();
694 lastDequeuedImage =
null;
696 if (lastReaderDequeuedFrom !=
null) {
697 lastReaderDequeuedFrom.close();
698 lastReaderDequeuedFrom =
null;
700 imageReaderQueue.clear();
704 @TargetApi(API_LEVELS.API_33)
707 SyncFence fence =
image.getFence();
708 fence.awaitForever();
709 }
catch (IOException
e) {
721 if (
Build.VERSION.SDK_INT >= API_LEVELS.API_33) {
727 ignoringFence =
true;
728 Log.w(TAG,
"ImageTextureEntry can't wait on the fence on Android < 33");
746 unregisterTexture(
id);
755 if (requestedWidth ==
width && requestedHeight ==
height) {
759 this.createNewReader =
true;
760 this.requestedHeight =
height;
761 this.requestedWidth =
width;
766 return this.requestedWidth;
771 return this.requestedHeight;
776 PerImageReader pir = getActiveReader();
778 Log.
i(
TAG,
"" + pir.reader.hashCode() +
" returning surface to render a new frame.");
780 return pir.reader.getSurface();
786 long now = System.nanoTime();
787 if (lastScheduleTime != 0) {
788 long delta = now - lastScheduleTime;
789 Log.
v(
TAG,
"scheduleFrame delta=" + deltaMillis(
delta));
791 lastScheduleTime = now;
793 scheduleEngineFrame();
799 PerImage r = dequeueImage();
803 maybeWaitOnFence(r.image);
807 private PerImageReader getActiveReader() {
808 synchronized (lock) {
809 if (createNewReader) {
810 createNewReader =
false;
812 ImageReader reader = createImageReader();
816 "" + reader.hashCode() +
" created w=" + requestedWidth +
" h=" + requestedHeight);
818 return getOrCreatePerImageReader(reader);
820 return imageReaderQueue.peekLast();
838 private ImageReader createImageReader33() {
839 final ImageReader.Builder
builder =
new ImageReader.Builder(requestedWidth, requestedHeight);
841 builder.setMaxImages(MAX_IMAGES);
848 builder.setImageFormat(ImageFormat.PRIVATE);
850 builder.setUsage(HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
851 final ImageReader reader =
builder.build();
856 private ImageReader createImageReader29() {
857 final ImageReader reader =
858 ImageReader.newInstance(
863 HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
867 private ImageReader createImageReader() {
869 return createImageReader33();
870 }
else if (
Build.VERSION.SDK_INT >= API_LEVELS.API_29) {
871 return createImageReader29();
873 throw new UnsupportedOperationException(
874 "ImageReaderPlatformViewRenderTarget requires API version 29+");
880 ignoringFence =
true;
885 synchronized (lock) {
886 return imageReaderQueue.size();
892 synchronized (lock) {
900 synchronized (lock) {
901 for (PerImageReader reader : imageReaderQueue) {
902 r += reader.imageQueue.size();
912 private static final String
TAG =
"ImageTextureRegistryEntry";
913 private final long id;
914 private boolean released;
915 private boolean ignoringFence =
false;
937 unregisterTexture(
id);
946 synchronized (
this) {
947 toClose = this.image;
951 if (toClose !=
null) {
952 Log.
e(
TAG,
"Dropping PlatformView Frame");
956 scheduleEngineFrame();
961 private
void waitOnFence(
Image image) {
963 SyncFence fence = image.getFence();
964 fence.awaitForever();
965 }
catch (IOException
e) {
970 @TargetApi(API_LEVELS.API_29)
971 private
void maybeWaitOnFence(
Image image) {
978 if (
Build.VERSION.SDK_INT >= API_LEVELS.API_33) {
984 ignoringFence =
true;
985 Log.w(TAG,
"ImageTextureEntry can't wait on the fence on Android < 33");
989 @TargetApi(API_LEVELS.API_29)
992 synchronized (
this) {
1006 if (image !=
null) {
1049 flutterJNI.onSurfaceWindowChanged(
surface);
1052 flutterJNI.onSurfaceCreated(
surface);
1065 flutterJNI.onSurfaceWindowChanged(
surface);
1090 flutterJNI.onSurfaceDestroyed();
1096 if (isDisplayingFlutterUi) {
1097 flutterUiDisplayListener.onFlutterUiNoLongerDisplayed();
1100 isDisplayingFlutterUi =
false;
1116 if (!viewportMetrics.validate()) {
1121 "Setting viewport metrics\n"
1123 + viewportMetrics.width
1125 + viewportMetrics.height
1128 + viewportMetrics.viewPaddingLeft
1130 + viewportMetrics.viewPaddingTop
1132 + viewportMetrics.viewPaddingRight
1134 + viewportMetrics.viewPaddingBottom
1137 + viewportMetrics.viewInsetLeft
1139 + viewportMetrics.viewInsetTop
1141 + viewportMetrics.viewInsetRight
1143 + viewportMetrics.viewInsetBottom
1145 +
"System Gesture Insets - L: "
1146 + viewportMetrics.systemGestureInsetLeft
1148 + viewportMetrics.systemGestureInsetTop
1150 + viewportMetrics.systemGestureInsetRight
1152 + viewportMetrics.systemGestureInsetRight
1154 +
"Display Features: "
1155 + viewportMetrics.displayFeatures.size());
1157 int[] displayFeaturesBounds =
new int[viewportMetrics.displayFeatures.size() * 4];
1158 int[] displayFeaturesType =
new int[viewportMetrics.displayFeatures.size()];
1159 int[] displayFeaturesState =
new int[viewportMetrics.displayFeatures.size()];
1160 for (
int i = 0;
i < viewportMetrics.displayFeatures.size();
i++) {
1161 DisplayFeature displayFeature = viewportMetrics.displayFeatures.get(
i);
1162 displayFeaturesBounds[4 *
i] = displayFeature.
bounds.left;
1163 displayFeaturesBounds[4 *
i + 1] = displayFeature.
bounds.top;
1164 displayFeaturesBounds[4 *
i + 2] = displayFeature.
bounds.right;
1165 displayFeaturesBounds[4 *
i + 3] = displayFeature.
bounds.bottom;
1170 flutterJNI.setViewportMetrics(
1171 viewportMetrics.devicePixelRatio,
1172 viewportMetrics.width,
1173 viewportMetrics.height,
1174 viewportMetrics.viewPaddingTop,
1175 viewportMetrics.viewPaddingRight,
1176 viewportMetrics.viewPaddingBottom,
1177 viewportMetrics.viewPaddingLeft,
1178 viewportMetrics.viewInsetTop,
1179 viewportMetrics.viewInsetRight,
1180 viewportMetrics.viewInsetBottom,
1181 viewportMetrics.viewInsetLeft,
1182 viewportMetrics.systemGestureInsetTop,
1183 viewportMetrics.systemGestureInsetRight,
1184 viewportMetrics.systemGestureInsetBottom,
1185 viewportMetrics.systemGestureInsetLeft,
1186 viewportMetrics.physicalTouchSlop,
1187 displayFeaturesBounds,
1188 displayFeaturesType,
1189 displayFeaturesState);
1195 return flutterJNI.getBitmap();
1200 flutterJNI.dispatchPointerDataPacket(
buffer, position);
1205 flutterJNI.registerTexture(textureId, textureWrapper);
1208 private void registerImageTexture(
1210 flutterJNI.registerImageTexture(textureId, imageTexture);
1213 private void scheduleEngineFrame() {
1214 flutterJNI.scheduleFrame();
1218 private void markTextureFrameAvailable(
long textureId) {
1219 flutterJNI.markTextureFrameAvailable(textureId);
1223 private void unregisterTexture(
long textureId) {
1224 flutterJNI.unregisterTexture(textureId);
1229 return flutterJNI.getIsSoftwareRenderingEnabled();
1234 flutterJNI.setAccessibilityFeatures(
flags);
1239 flutterJNI.setSemanticsEnabled(enabled);
1244 int nodeId,
int action, @Nullable ByteBuffer
args,
int argsPosition) {
1245 flutterJNI.dispatchSemanticsAction(nodeId,
action,
args, argsPosition);
1350 this.encodedValue = encodedValue;
1379 this.encodedValue = encodedValue;
static void v(@NonNull String tag, @NonNull String message)
static void e(@NonNull String tag, @NonNull String message)
static void i(@NonNull String tag, @NonNull String message)
void unregisterTexture(long textureId)
DisplayFeature(Rect bounds, DisplayFeatureType type, DisplayFeatureState state)
final DisplayFeatureType type
DisplayFeature(Rect bounds, DisplayFeatureType type)
final DisplayFeatureState state
void onTrimMemory(int level)
PerImageReader getOrCreatePerImageReader(ImageReader reader)
double deltaMillis(long deltaNanos)
void disableFenceForTest()
void pruneImageReaderQueue()
ImageReaderSurfaceProducer(long id)
void onImage(ImageReader reader, Image image)
void setSize(int width, int height)
ImageTextureRegistryEntry(long id)
void pushImage(Image image)
Image acquireLatestImage()
SurfaceTextureWrapper textureWrapper()
void setOnFrameConsumedListener(@Nullable OnFrameConsumedListener listener)
SurfaceTextureRegistryEntry(long id, @NonNull SurfaceTexture surfaceTexture)
void setOnTrimMemoryListener(@Nullable OnTrimMemoryListener listener)
SurfaceTexture surfaceTexture()
void onTrimMemory(int level)
TextureFinalizerRunnable(long id, @NonNull FlutterJNI flutterJNI)
int systemGestureInsetBottom
List< DisplayFeature > displayFeatures
int systemGestureInsetRight
int systemGestureInsetLeft
static final int unsetValue
int systemGestureInsetTop
SurfaceProducer createSurfaceProducer()
SurfaceTextureEntry registerSurfaceTexture(@NonNull SurfaceTexture surfaceTexture)
void removeIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener)
void swapSurface(@NonNull Surface surface)
void stopRenderingToSurface()
void surfaceChanged(int width, int height)
void startRenderingToSurface(@NonNull Surface surface, boolean onlySwap)
SurfaceTextureEntry createSurfaceTexture()
void dispatchSemanticsAction(int nodeId, int action, @Nullable ByteBuffer args, int argsPosition)
void removeOnTrimMemoryListener(@NonNull OnTrimMemoryListener listener)
void onTrimMemory(int level)
void addOnTrimMemoryListener(@NonNull OnTrimMemoryListener listener)
static boolean debugForceSurfaceProducerGlTextures
FlutterRenderer(@NonNull FlutterJNI flutterJNI)
ImageTextureEntry createImageTexture()
void addIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener)
static boolean debugDisableSurfaceClear
void setAccessibilityFeatures(int flags)
boolean isDisplayingFlutterUi()
void setViewportMetrics(@NonNull ViewportMetrics viewportMetrics)
void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position)
void setSemanticsEnabled(boolean enabled)
boolean isSoftwareRenderingEnabled()
DisplayFeatureState(int encodedValue)
DisplayFeatureType(int encodedValue)
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
void onTrimMemory(int level)
sk_sp< const SkImage > image
void Log(const char *format,...) SK_PRINTF_LIKE(1
def Build(configs, env, options)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer