Skip to content

Commit

Permalink
Reenable HardwareBuffer backed Android Platform Views on SDK >= 29 (f…
Browse files Browse the repository at this point in the history
…lutter#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.
  • Loading branch information
johnmccutchan authored Aug 17, 2023
1 parent 5019d6d commit d259a52
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 33 deletions.
3 changes: 2 additions & 1 deletion shell/platform/android/hardware_buffer_external_texture.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ void HardwareBufferExternalTexture::Paint(PaintContext& context,
flutter::DlCanvas::SrcRectConstraint::kStrict // enforce edges
);
} else {
FML_LOG(WARNING) << "No DlImage available.";
FML_LOG(WARNING)
<< "No DlImage available for HardwareBufferExternalTexture to paint.";
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ public void run() {

@Keep
final class ImageTextureRegistryEntry implements TextureRegistry.ImageTextureEntry {
private static final String TAG = "ImageTextureRegistryEntry";
private final long id;
private boolean released;
private Image image;
Expand Down Expand Up @@ -356,8 +357,10 @@ public void pushImage(Image image) {
if (toClose != null) {
toClose.close();
}
// Mark that we have a new frame available.
markTextureFrameAvailable(id);
if (image != null) {
// Mark that we have a new frame available.
markTextureFrameAvailable(id);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,24 @@
import android.view.Surface;
import io.flutter.view.TextureRegistry.ImageTextureEntry;

@TargetApi(26)
@TargetApi(29)
public class ImageReaderPlatformViewRenderTarget implements PlatformViewRenderTarget {
private ImageTextureEntry textureEntry;
private boolean mustRecreateImageReader = false;
private ImageReader reader;
private int bufferWidth = 0;
private int bufferHeight = 0;
private static final String TAG = "ImageReaderPlatformViewRenderTarget";

private void closeReader() {
if (this.reader != null) {
// Push a null image, forcing the texture entry to close any cached images.
textureEntry.pushImage(null);
// Close the reader, which also closes any produced images.
this.reader.close();
this.reader = null;
}
}

private void recreateImageReaderIfNeeded() {
if (!mustRecreateImageReader) {
return;
}
mustRecreateImageReader = false;
closeReader();
this.reader = createImageReader();
}

private final Handler onImageAvailableHandler = new Handler();
private final ImageReader.OnImageAvailableListener onImageAvailableListener =
new ImageReader.OnImageAvailableListener() {
Expand All @@ -55,9 +48,12 @@ protected ImageReader createImageReader33() {
// Allow for double buffering.
builder.setMaxImages(3);
// Use PRIVATE image format so that we can support video decoding.
// TODO(johnmccutchan): Should we always use PRIVATE here? It may impact our ability
// to read back texture data. If we don't always want to use it, how do we decide when
// to use it or not? Perhaps PlatformViews can indicate if they may contain DRM'd content.
// TODO(johnmccutchan): Should we always use PRIVATE here? It may impact our
// ability to read back texture data. If we don't always want to use it, how do we
// decide when to use it or not? Perhaps PlatformViews can indicate if they may contain
// DRM'd content.
// I need to investigate how PRIVATE impacts our ability to take screenshots or capture
// the output of Flutter application.
builder.setImageFormat(ImageFormat.PRIVATE);
// Hint that consumed images will only be read by GPU.
builder.setUsage(HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
Expand All @@ -68,29 +64,31 @@ protected ImageReader createImageReader33() {

@TargetApi(29)
protected ImageReader createImageReader29() {
return ImageReader.newInstance(
bufferWidth, bufferHeight, ImageFormat.PRIVATE, 2, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
}

@TargetApi(26)
protected ImageReader createImageReader26() {
return ImageReader.newInstance(bufferWidth, bufferHeight, ImageFormat.PRIVATE, 2);
final ImageReader reader =
ImageReader.newInstance(
bufferWidth,
bufferHeight,
ImageFormat.PRIVATE,
2,
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
reader.setOnImageAvailableListener(this.onImageAvailableListener, onImageAvailableHandler);
return reader;
}

protected ImageReader createImageReader() {
if (Build.VERSION.SDK_INT >= 33) {
return createImageReader33();
} else if (Build.VERSION.SDK_INT >= 29) {
return createImageReader29();
} else {
return createImageReader26();
}
throw new UnsupportedOperationException(
"ImageReaderPlatformViewRenderTarget requires API version 29+");
}

public ImageReaderPlatformViewRenderTarget(ImageTextureEntry textureEntry) {
if (Build.VERSION.SDK_INT < 26) {
if (Build.VERSION.SDK_INT < 29) {
throw new UnsupportedOperationException(
"ImageReaderPlatformViewRenderTarget requires API version 26+");
"ImageReaderPlatformViewRenderTarget requires API version 29+");
}
this.textureEntry = textureEntry;
}
Expand Down Expand Up @@ -127,17 +125,16 @@ public long getId() {
}

public void release() {
closeReader();
// textureEntry has a finalizer attached.
textureEntry = null;
closeReader();
}

public boolean isReleased() {
return this.textureEntry == null;
}

public Surface getSurface() {
recreateImageReaderIfNeeded();
return this.reader.getSurface();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,7 @@ private void unlockInputConnection(@NonNull VirtualDisplayController controller)

private static PlatformViewRenderTarget makePlatformViewRenderTarget(
TextureRegistry textureRegistry) {
// TODO(johnmccutchan): Enable ImageReaderPlatformViewRenderTarget for public use.
if (false) {
if (Build.VERSION.SDK_INT >= 29) {
final TextureRegistry.ImageTextureEntry textureEntry = textureRegistry.createImageTexture();
return new ImageReaderPlatformViewRenderTarget(textureEntry);
}
Expand Down
3 changes: 2 additions & 1 deletion shell/platform/android/surface_texture_external_texture.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ void SurfaceTextureExternalTexture::Paint(PaintContext& context,
flutter::DlCanvas::SrcRectConstraint::kStrict // enforce edges
);
} else {
FML_LOG(WARNING) << "No DlImage available.";
FML_LOG(WARNING)
<< "No DlImage available for SurfaceTextureExternalTexture to paint.";
}
}

Expand Down

0 comments on commit d259a52

Please sign in to comment.