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

Commit d259a52

Browse files
Reenable HardwareBuffer backed Android Platform Views on SDK >= 29 (#44790)
- Fix a bug in the SDK < 33 ImageReader construction code path. - Fix a bug that resulted in references to Images produced by a closed ImageReader. - Fix an order of operations bug in ImageReaderPlatformViewRenderTarget release/finalizer code path. - Enable HardwareBuffer backed Android Platform Views on SDK >= 29 Manually tested on device rotating and shutting down the app.
1 parent 5019d6d commit d259a52

File tree

5 files changed

+34
-33
lines changed

5 files changed

+34
-33
lines changed

shell/platform/android/hardware_buffer_external_texture.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ void HardwareBufferExternalTexture::Paint(PaintContext& context,
3939
flutter::DlCanvas::SrcRectConstraint::kStrict // enforce edges
4040
);
4141
} else {
42-
FML_LOG(WARNING) << "No DlImage available.";
42+
FML_LOG(WARNING)
43+
<< "No DlImage available for HardwareBufferExternalTexture to paint.";
4344
}
4445
}
4546

shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ public void run() {
322322

323323
@Keep
324324
final class ImageTextureRegistryEntry implements TextureRegistry.ImageTextureEntry {
325+
private static final String TAG = "ImageTextureRegistryEntry";
325326
private final long id;
326327
private boolean released;
327328
private Image image;
@@ -356,8 +357,10 @@ public void pushImage(Image image) {
356357
if (toClose != null) {
357358
toClose.close();
358359
}
359-
// Mark that we have a new frame available.
360-
markTextureFrameAvailable(id);
360+
if (image != null) {
361+
// Mark that we have a new frame available.
362+
markTextureFrameAvailable(id);
363+
}
361364
}
362365

363366
@Override

shell/platform/android/io/flutter/plugin/platform/ImageReaderPlatformViewRenderTarget.java

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,24 @@
1111
import android.view.Surface;
1212
import io.flutter.view.TextureRegistry.ImageTextureEntry;
1313

14-
@TargetApi(26)
14+
@TargetApi(29)
1515
public class ImageReaderPlatformViewRenderTarget implements PlatformViewRenderTarget {
1616
private ImageTextureEntry textureEntry;
17-
private boolean mustRecreateImageReader = false;
1817
private ImageReader reader;
1918
private int bufferWidth = 0;
2019
private int bufferHeight = 0;
2120
private static final String TAG = "ImageReaderPlatformViewRenderTarget";
2221

2322
private void closeReader() {
2423
if (this.reader != null) {
24+
// Push a null image, forcing the texture entry to close any cached images.
25+
textureEntry.pushImage(null);
26+
// Close the reader, which also closes any produced images.
2527
this.reader.close();
2628
this.reader = null;
2729
}
2830
}
2931

30-
private void recreateImageReaderIfNeeded() {
31-
if (!mustRecreateImageReader) {
32-
return;
33-
}
34-
mustRecreateImageReader = false;
35-
closeReader();
36-
this.reader = createImageReader();
37-
}
38-
3932
private final Handler onImageAvailableHandler = new Handler();
4033
private final ImageReader.OnImageAvailableListener onImageAvailableListener =
4134
new ImageReader.OnImageAvailableListener() {
@@ -55,9 +48,12 @@ protected ImageReader createImageReader33() {
5548
// Allow for double buffering.
5649
builder.setMaxImages(3);
5750
// Use PRIVATE image format so that we can support video decoding.
58-
// TODO(johnmccutchan): Should we always use PRIVATE here? It may impact our ability
59-
// to read back texture data. If we don't always want to use it, how do we decide when
60-
// to use it or not? Perhaps PlatformViews can indicate if they may contain DRM'd content.
51+
// TODO(johnmccutchan): Should we always use PRIVATE here? It may impact our
52+
// ability to read back texture data. If we don't always want to use it, how do we
53+
// decide when to use it or not? Perhaps PlatformViews can indicate if they may contain
54+
// DRM'd content.
55+
// I need to investigate how PRIVATE impacts our ability to take screenshots or capture
56+
// the output of Flutter application.
6157
builder.setImageFormat(ImageFormat.PRIVATE);
6258
// Hint that consumed images will only be read by GPU.
6359
builder.setUsage(HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
@@ -68,29 +64,31 @@ protected ImageReader createImageReader33() {
6864

6965
@TargetApi(29)
7066
protected ImageReader createImageReader29() {
71-
return ImageReader.newInstance(
72-
bufferWidth, bufferHeight, ImageFormat.PRIVATE, 2, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
73-
}
74-
75-
@TargetApi(26)
76-
protected ImageReader createImageReader26() {
77-
return ImageReader.newInstance(bufferWidth, bufferHeight, ImageFormat.PRIVATE, 2);
67+
final ImageReader reader =
68+
ImageReader.newInstance(
69+
bufferWidth,
70+
bufferHeight,
71+
ImageFormat.PRIVATE,
72+
2,
73+
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
74+
reader.setOnImageAvailableListener(this.onImageAvailableListener, onImageAvailableHandler);
75+
return reader;
7876
}
7977

8078
protected ImageReader createImageReader() {
8179
if (Build.VERSION.SDK_INT >= 33) {
8280
return createImageReader33();
8381
} else if (Build.VERSION.SDK_INT >= 29) {
8482
return createImageReader29();
85-
} else {
86-
return createImageReader26();
8783
}
84+
throw new UnsupportedOperationException(
85+
"ImageReaderPlatformViewRenderTarget requires API version 29+");
8886
}
8987

9088
public ImageReaderPlatformViewRenderTarget(ImageTextureEntry textureEntry) {
91-
if (Build.VERSION.SDK_INT < 26) {
89+
if (Build.VERSION.SDK_INT < 29) {
9290
throw new UnsupportedOperationException(
93-
"ImageReaderPlatformViewRenderTarget requires API version 26+");
91+
"ImageReaderPlatformViewRenderTarget requires API version 29+");
9492
}
9593
this.textureEntry = textureEntry;
9694
}
@@ -127,17 +125,16 @@ public long getId() {
127125
}
128126

129127
public void release() {
128+
closeReader();
130129
// textureEntry has a finalizer attached.
131130
textureEntry = null;
132-
closeReader();
133131
}
134132

135133
public boolean isReleased() {
136134
return this.textureEntry == null;
137135
}
138136

139137
public Surface getSurface() {
140-
recreateImageReaderIfNeeded();
141138
return this.reader.getSurface();
142139
}
143140
}

shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -968,8 +968,7 @@ private void unlockInputConnection(@NonNull VirtualDisplayController controller)
968968

969969
private static PlatformViewRenderTarget makePlatformViewRenderTarget(
970970
TextureRegistry textureRegistry) {
971-
// TODO(johnmccutchan): Enable ImageReaderPlatformViewRenderTarget for public use.
972-
if (false) {
971+
if (Build.VERSION.SDK_INT >= 29) {
973972
final TextureRegistry.ImageTextureEntry textureEntry = textureRegistry.createImageTexture();
974973
return new ImageReaderPlatformViewRenderTarget(textureEntry);
975974
}

shell/platform/android/surface_texture_external_texture.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ void SurfaceTextureExternalTexture::Paint(PaintContext& context,
6565
flutter::DlCanvas::SrcRectConstraint::kStrict // enforce edges
6666
);
6767
} else {
68-
FML_LOG(WARNING) << "No DlImage available.";
68+
FML_LOG(WARNING)
69+
<< "No DlImage available for SurfaceTextureExternalTexture to paint.";
6970
}
7071
}
7172

0 commit comments

Comments
 (0)