5package io.flutter.embedding.engine.renderer;
7import static io.flutter.Build.API_LEVELS;
9import android.annotation.TargetApi;
11import android.graphics.ImageFormat;
13import android.graphics.SurfaceTexture;
14import android.hardware.HardwareBuffer;
15import android.hardware.SyncFence;
17import android.media.ImageReader;
22import androidx.annotation.Keep;
23import androidx.annotation.NonNull;
24import androidx.annotation.Nullable;
25import androidx.annotation.VisibleForTesting;
27import io.flutter.embedding.engine.FlutterJNI;
28import io.flutter.view.TextureRegistry;
29import java.io.IOException;
30import java.lang.ref.WeakReference;
31import java.nio.ByteBuffer;
32import java.util.ArrayDeque;
33import java.util.ArrayList;
34import java.util.HashMap;
35import java.util.HashSet;
36import java.util.Iterator;
39import java.util.concurrent.atomic.AtomicLong;
68 private static final String TAG =
"FlutterRenderer";
71 @NonNull
private final AtomicLong nextTextureId =
new AtomicLong(0L);
72 @Nullable
private Surface surface;
73 private boolean isDisplayingFlutterUi =
false;
74 private final Handler handler =
new Handler();
77 private final Set<WeakReference<TextureRegistry.OnTrimMemoryListener>> onTrimMemoryListeners =
84 public void onFlutterUiDisplayed() {
85 isDisplayingFlutterUi =
true;
89 public void onFlutterUiNoLongerDisplayed() {
90 isDisplayingFlutterUi =
false;
95 this.flutterJNI = flutterJNI;
96 this.flutterJNI.addIsDisplayingFlutterUiListener(flutterUiDisplayListener);
104 return isDisplayingFlutterUi;
112 flutterJNI.addIsDisplayingFlutterUiListener(listener);
114 if (isDisplayingFlutterUi) {
115 listener.onFlutterUiDisplayed();
124 flutterJNI.removeIsDisplayingFlutterUiListener(listener);
127 private void clearDeadListeners() {
128 final Iterator<WeakReference<OnTrimMemoryListener>> iterator = onTrimMemoryListeners.iterator();
129 while (iterator.hasNext()) {
130 WeakReference<OnTrimMemoryListener> listenerRef = iterator.next();
131 final OnTrimMemoryListener listener = listenerRef.get();
132 if (listener ==
null) {
142 clearDeadListeners();
143 onTrimMemoryListeners.add(
new WeakReference<>(listener));
152 for (WeakReference<OnTrimMemoryListener> listenerRef : onTrimMemoryListeners) {
153 if (listenerRef.get() == listener) {
154 onTrimMemoryListeners.remove(listenerRef);
193 final SurfaceProducer entry;
195 final long id = nextTextureId.getAndIncrement();
197 registerImageTexture(
id, producer);
199 Log.
v(
TAG,
"New ImageReaderSurfaceProducer ID: " +
id);
210 Log.
v(
TAG,
"New SurfaceTextureSurfaceProducer ID: " +
texture.id());
223 Log.
v(
TAG,
"Creating a SurfaceTexture.");
224 final SurfaceTexture surfaceTexture =
new SurfaceTexture(0);
247 long textureId, @NonNull SurfaceTexture surfaceTexture) {
248 surfaceTexture.detachFromGLContext();
249 final SurfaceTextureRegistryEntry entry =
250 new SurfaceTextureRegistryEntry(textureId, surfaceTexture);
251 Log.
v(TAG,
"New SurfaceTexture ID: " + entry.id());
252 registerTexture(entry.id(), entry.textureWrapper());
262 Log.
v(
TAG,
"New ImageTextureEntry ID: " + entry.id());
263 registerImageTexture(entry.id(), entry);
269 final Iterator<WeakReference<OnTrimMemoryListener>> iterator = onTrimMemoryListeners.iterator();
270 while (iterator.hasNext()) {
271 WeakReference<OnTrimMemoryListener> listenerRef = iterator.next();
272 final OnTrimMemoryListener listener = listenerRef.get();
273 if (listener !=
null) {
274 listener.onTrimMemory(level);
283 implements TextureRegistry.SurfaceTextureEntry, TextureRegistry.OnTrimMemoryListener {
284 private final long id;
286 private boolean released;
287 @Nullable
private OnTrimMemoryListener trimMemoryListener;
288 @Nullable
private OnFrameConsumedListener frameConsumedListener;
292 Runnable onFrameConsumed =
294 if (frameConsumedListener !=
null) {
295 frameConsumedListener.onFrameConsumed();
304 SurfaceTexture.OnFrameAvailableListener onFrameListener =
306 if (released || !flutterJNI.isAttached()) {
313 textureWrapper.markDirty();
314 scheduleEngineFrame();
320 this.
surfaceTexture().setOnFrameAvailableListener(onFrameListener,
new Handler());
325 if (trimMemoryListener !=
null) {
326 trimMemoryListener.onTrimMemory(level);
330 private void removeListener() {
336 return textureWrapper;
342 return textureWrapper.surfaceTexture();
355 Log.
v(
TAG,
"Releasing a SurfaceTexture (" +
id +
").");
356 textureWrapper.release();
357 unregisterTexture(
id);
377 frameConsumedListener = listener;
382 trimMemoryListener = listener;
387 private final long id;
392 this.flutterJNI = flutterJNI;
400 Log.
v(
TAG,
"Releasing a Texture (" +
id +
").");
410 @TargetApi(API_LEVELS.API_29)
412 implements TextureRegistry.SurfaceProducer,
413 TextureRegistry.ImageConsumer,
414 TextureRegistry.OnTrimMemoryListener {
415 private static final String
TAG =
"ImageReaderSurfaceProducer";
416 private static final int MAX_IMAGES = 5;
419 private static final boolean VERBOSE_LOGS =
false;
424 private static final boolean CLEANUP_ON_MEMORY_PRESSURE =
false;
426 private final long id;
428 private boolean released;
430 private boolean ignoringFence =
false;
433 private int requestedWidth = 1;
434 private int requestedHeight = 1;
439 private boolean createNewReader =
true;
442 private long lastDequeueTime = 0;
443 private long lastQueueTime = 0;
444 private long lastScheduleTime = 0;
446 private Object lock =
new Object();
448 private final ArrayDeque<PerImageReader> imageReaderQueue =
new ArrayDeque<PerImageReader>();
449 private final HashMap<ImageReader, PerImageReader> perImageReaders =
450 new HashMap<ImageReader, PerImageReader>();
451 private PerImage lastDequeuedImage =
null;
452 private PerImageReader lastReaderDequeuedFrom =
null;
455 private class PerImage {
456 public final Image
image;
457 public final long queuedTime;
459 public PerImage(Image
image,
long queuedTime) {
461 this.queuedTime = queuedTime;
466 private class PerImageReader {
467 public final ImageReader reader;
468 private final ArrayDeque<PerImage> imageQueue =
new ArrayDeque<PerImage>();
469 private boolean closed =
false;
471 private final ImageReader.OnImageAvailableListener onImageAvailableListener =
475 image = reader.acquireLatestImage();
476 }
catch (IllegalStateException e) {
477 Log.
e(
TAG,
"onImageAvailable acquireLatestImage failed: " + e);
482 if (released || closed) {
486 onImage(reader,
image);
489 public PerImageReader(ImageReader reader) {
490 this.reader = reader;
491 reader.setOnImageAvailableListener(
492 onImageAvailableListener,
new Handler(Looper.getMainLooper()));
495 PerImage queueImage(Image
image) {
499 PerImage perImage =
new PerImage(
image, System.nanoTime());
500 imageQueue.add(perImage);
502 while (imageQueue.size() > 2) {
503 PerImage r = imageQueue.removeFirst();
505 Log.
i(
TAG,
"" + reader.hashCode() +
" force closed image=" + r.image.hashCode());
512 PerImage dequeueImage() {
513 if (imageQueue.size() == 0) {
516 PerImage r = imageQueue.removeFirst();
522 return imageQueue.size() == 0 && lastReaderDequeuedFrom !=
this;
528 Log.
i(
TAG,
"Closing reader=" + reader.hashCode());
536 double ms = (double) deltaNanos / (
double) 1000000.0;
541 PerImageReader r = perImageReaders.get(reader);
543 r =
new PerImageReader(reader);
544 perImageReaders.put(reader, r);
545 imageReaderQueue.add(r);
547 Log.
i(
TAG,
"imageReaderQueue#=" + imageReaderQueue.size());
554 boolean change =
false;
556 while (imageReaderQueue.size() > 1) {
557 PerImageReader r = imageReaderQueue.peekFirst();
562 imageReaderQueue.removeFirst();
563 perImageReaders.remove(r.reader);
567 if (change && VERBOSE_LOGS) {
568 Log.
i(
TAG,
"Pruned image reader queue length=" + imageReaderQueue.size());
573 PerImage queuedImage =
null;
574 synchronized (lock) {
575 PerImageReader perReader = getOrCreatePerImageReader(reader);
576 queuedImage = perReader.queueImage(
image);
578 if (queuedImage ==
null) {
583 if (lastQueueTime != 0) {
584 long now = System.nanoTime();
585 long queueDelta = now - lastQueueTime;
591 + queuedImage.image.hashCode()
593 + deltaMillis(queueDelta));
596 lastQueueTime = System.nanoTime();
599 scheduleEngineFrame();
604 synchronized (lock) {
605 for (PerImageReader reader : imageReaderQueue) {
606 r = reader.dequeueImage();
612 if (lastDequeueTime != 0) {
613 long now = System.nanoTime();
614 long dequeueDelta = now - lastDequeueTime;
615 long queuedFor = now - r.queuedTime;
616 long scheduleDelay = now - lastScheduleTime;
620 + reader.reader.hashCode()
624 + deltaMillis(queuedFor)
626 + deltaMillis(dequeueDelta)
628 + deltaMillis(scheduleDelay));
629 lastDequeueTime = now;
631 lastDequeueTime = System.nanoTime();
634 if (lastDequeuedImage !=
null) {
639 + lastReaderDequeuedFrom.reader.hashCode()
641 + lastDequeuedImage.image.hashCode());
646 lastDequeuedImage.image.close();
647 lastDequeuedImage =
null;
651 lastDequeuedImage = r;
652 lastReaderDequeuedFrom = reader;
655 pruneImageReaderQueue();
662 if (!CLEANUP_ON_MEMORY_PRESSURE) {
666 createNewReader =
true;
669 private void releaseInternal() {
674 private void cleanup() {
675 synchronized (lock) {
676 for (PerImageReader pir : perImageReaders.values()) {
677 if (lastReaderDequeuedFrom == pir) {
678 lastReaderDequeuedFrom =
null;
682 perImageReaders.clear();
683 if (lastDequeuedImage !=
null) {
684 lastDequeuedImage.image.close();
685 lastDequeuedImage =
null;
687 if (lastReaderDequeuedFrom !=
null) {
688 lastReaderDequeuedFrom.close();
689 lastReaderDequeuedFrom =
null;
691 imageReaderQueue.clear();
695 @TargetApi(API_LEVELS.API_33)
696 private
void waitOnFence(Image
image) {
698 SyncFence fence =
image.getFence();
699 fence.awaitForever();
700 }
catch (IOException e) {
705 private void maybeWaitOnFence(Image
image) {
712 if (
Build.VERSION.SDK_INT >= API_LEVELS.API_33) {
718 ignoringFence =
true;
719 Log.w(TAG,
"ImageTextureEntry can't wait on the fence on Android < 33");
737 unregisterTexture(
id);
746 if (requestedWidth ==
width && requestedHeight ==
height) {
750 this.createNewReader =
true;
751 this.requestedHeight =
height;
752 this.requestedWidth =
width;
757 return this.requestedWidth;
762 return this.requestedHeight;
767 PerImageReader pir = getActiveReader();
769 Log.
i(
TAG,
"" + pir.reader.hashCode() +
" returning surface to render a new frame.");
771 return pir.reader.getSurface();
777 long now = System.nanoTime();
778 if (lastScheduleTime != 0) {
779 long delta = now - lastScheduleTime;
780 Log.
v(
TAG,
"scheduleFrame delta=" + deltaMillis(delta));
782 lastScheduleTime = now;
784 scheduleEngineFrame();
788 @TargetApi(API_LEVELS.API_29)
789 public Image acquireLatestImage() {
790 PerImage r = dequeueImage();
794 maybeWaitOnFence(r.image);
798 private PerImageReader getActiveReader() {
799 synchronized (lock) {
800 if (createNewReader) {
801 createNewReader =
false;
803 ImageReader reader = createImageReader();
807 "" + reader.hashCode() +
" created w=" + requestedWidth +
" h=" + requestedHeight);
809 return getOrCreatePerImageReader(reader);
811 return imageReaderQueue.peekLast();
828 @TargetApi(API_LEVELS.API_33)
829 private ImageReader createImageReader33() {
830 final ImageReader.Builder builder =
new ImageReader.Builder(requestedWidth, requestedHeight);
832 builder.setMaxImages(MAX_IMAGES);
839 builder.setImageFormat(ImageFormat.PRIVATE);
841 builder.setUsage(HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
842 final ImageReader reader = builder.build();
846 @TargetApi(API_LEVELS.API_29)
847 private ImageReader createImageReader29() {
848 final ImageReader reader =
849 ImageReader.newInstance(
854 HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
858 private ImageReader createImageReader() {
859 if (
Build.VERSION.SDK_INT >= API_LEVELS.API_33) {
860 return createImageReader33();
861 }
else if (
Build.VERSION.SDK_INT >= API_LEVELS.API_29) {
862 return createImageReader29();
864 throw new UnsupportedOperationException(
865 "ImageReaderPlatformViewRenderTarget requires API version 29+");
871 ignoringFence =
true;
876 synchronized (lock) {
877 return imageReaderQueue.size();
884 synchronized (lock) {
885 for (PerImageReader reader : imageReaderQueue) {
886 r += reader.imageQueue.size();
895 implements TextureRegistry.ImageTextureEntry, TextureRegistry.ImageConsumer {
896 private static final String
TAG =
"ImageTextureRegistryEntry";
897 private final long id;
898 private boolean released;
899 private boolean ignoringFence =
false;
921 unregisterTexture(
id);
930 synchronized (
this) {
931 toClose = this.image;
935 if (toClose !=
null) {
936 Log.
e(
TAG,
"Dropping PlatformView Frame");
940 scheduleEngineFrame();
944 @TargetApi(API_LEVELS.API_33)
945 private
void waitOnFence(Image image) {
947 SyncFence fence = image.getFence();
948 fence.awaitForever();
949 }
catch (IOException e) {
954 @TargetApi(API_LEVELS.API_29)
955 private
void maybeWaitOnFence(Image image) {
962 if (Build.VERSION.SDK_INT >= API_LEVELS.API_33) {
968 ignoringFence =
true;
969 Log.w(TAG,
"ImageTextureEntry can't wait on the fence on Android < 33");
973 @TargetApi(API_LEVELS.API_29)
976 synchronized (
this) {
1033 flutterJNI.onSurfaceWindowChanged(
surface);
1036 flutterJNI.onSurfaceCreated(
surface);
1049 flutterJNI.onSurfaceWindowChanged(
surface);
1074 flutterJNI.onSurfaceDestroyed();
1080 if (isDisplayingFlutterUi) {
1081 flutterUiDisplayListener.onFlutterUiNoLongerDisplayed();
1084 isDisplayingFlutterUi =
false;
1100 if (!viewportMetrics.validate()) {
1105 "Setting viewport metrics\n"
1107 + viewportMetrics.width
1109 + viewportMetrics.height
1112 + viewportMetrics.viewPaddingLeft
1114 + viewportMetrics.viewPaddingTop
1116 + viewportMetrics.viewPaddingRight
1118 + viewportMetrics.viewPaddingBottom
1121 + viewportMetrics.viewInsetLeft
1123 + viewportMetrics.viewInsetTop
1125 + viewportMetrics.viewInsetRight
1127 + viewportMetrics.viewInsetBottom
1129 +
"System Gesture Insets - L: "
1130 + viewportMetrics.systemGestureInsetLeft
1132 + viewportMetrics.systemGestureInsetTop
1134 + viewportMetrics.systemGestureInsetRight
1136 + viewportMetrics.systemGestureInsetRight
1138 +
"Display Features: "
1139 + viewportMetrics.displayFeatures.size());
1141 int[] displayFeaturesBounds =
new int[viewportMetrics.displayFeatures.size() * 4];
1142 int[] displayFeaturesType =
new int[viewportMetrics.displayFeatures.size()];
1143 int[] displayFeaturesState =
new int[viewportMetrics.displayFeatures.size()];
1144 for (
int i = 0; i < viewportMetrics.displayFeatures.size(); i++) {
1145 DisplayFeature displayFeature = viewportMetrics.displayFeatures.get(i);
1146 displayFeaturesBounds[4 * i] = displayFeature.
bounds.left;
1147 displayFeaturesBounds[4 * i + 1] = displayFeature.
bounds.top;
1148 displayFeaturesBounds[4 * i + 2] = displayFeature.
bounds.right;
1149 displayFeaturesBounds[4 * i + 3] = displayFeature.
bounds.bottom;
1154 flutterJNI.setViewportMetrics(
1155 viewportMetrics.devicePixelRatio,
1156 viewportMetrics.width,
1157 viewportMetrics.height,
1158 viewportMetrics.viewPaddingTop,
1159 viewportMetrics.viewPaddingRight,
1160 viewportMetrics.viewPaddingBottom,
1161 viewportMetrics.viewPaddingLeft,
1162 viewportMetrics.viewInsetTop,
1163 viewportMetrics.viewInsetRight,
1164 viewportMetrics.viewInsetBottom,
1165 viewportMetrics.viewInsetLeft,
1166 viewportMetrics.systemGestureInsetTop,
1167 viewportMetrics.systemGestureInsetRight,
1168 viewportMetrics.systemGestureInsetBottom,
1169 viewportMetrics.systemGestureInsetLeft,
1170 viewportMetrics.physicalTouchSlop,
1171 displayFeaturesBounds,
1172 displayFeaturesType,
1173 displayFeaturesState);
1179 return flutterJNI.getBitmap();
1184 flutterJNI.dispatchPointerDataPacket(
buffer, position);
1189 flutterJNI.registerTexture(textureId, textureWrapper);
1192 private void registerImageTexture(
1193 long textureId, @NonNull TextureRegistry.ImageConsumer imageTexture) {
1194 flutterJNI.registerImageTexture(textureId, imageTexture);
1197 private void scheduleEngineFrame() {
1198 flutterJNI.scheduleFrame();
1202 private void markTextureFrameAvailable(
long textureId) {
1203 flutterJNI.markTextureFrameAvailable(textureId);
1207 private void unregisterTexture(
long textureId) {
1208 flutterJNI.unregisterTexture(textureId);
1213 return flutterJNI.getIsSoftwareRenderingEnabled();
1218 flutterJNI.setAccessibilityFeatures(
flags);
1223 flutterJNI.setSemanticsEnabled(enabled);
1228 int nodeId,
int action, @Nullable ByteBuffer
args,
int argsPosition) {
1229 flutterJNI.dispatchSemanticsAction(nodeId,
action,
args, argsPosition);
1334 this.encodedValue = encodedValue;
1363 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
static const uint8_t buffer[]
void Log(const char *format,...) SK_PRINTF_LIKE(1
Build(configs, env, options)