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

Request another frame in ImageReaderSurfaceProducer.dequeueImage if more images are pending in the queue #55944

Merged
merged 1 commit into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,10 @@ boolean canPrune() {
return imageQueue.isEmpty() && lastReaderDequeuedFrom != this;
}

boolean imageQueueIsEmpty() {
return imageQueue.isEmpty();
}

void close() {
closed = true;
if (VERBOSE_LOGS) {
Expand Down Expand Up @@ -630,6 +634,7 @@ void onImage(ImageReader reader, Image image) {

PerImage dequeueImage() {
PerImage r = null;
boolean hasPendingImages = false;
synchronized (lock) {
for (PerImageReader reader : imageReaderQueue) {
r = reader.dequeueImage();
Expand Down Expand Up @@ -679,6 +684,21 @@ PerImage dequeueImage() {
break;
}
pruneImageReaderQueue();
for (PerImageReader reader : imageReaderQueue) {
if (!reader.imageQueueIsEmpty()) {
hasPendingImages = true;
break;
}
}
}
if (hasPendingImages) {
// Request another frame to ensure that images are consumed until the queue is empty.
handler.post(
() -> {
if (!released) {
scheduleEngineFrame();
}
});
}
return r;
}
Expand Down Expand Up @@ -1245,7 +1265,8 @@ private void registerImageTexture(
flutterJNI.registerImageTexture(textureId, imageTexture);
}

private void scheduleEngineFrame() {
@VisibleForTesting
/* package */ void scheduleEngineFrame() {
flutterJNI.scheduleFrame();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -826,4 +826,43 @@ public void onSurfaceDestroyed() {}
// Verify.
latch.await();
}

@Test
public void ImageReaderSurfaceProducerSchedulesFrameIfQueueNotEmpty() throws Exception {
FlutterRenderer flutterRenderer = spy(engineRule.getFlutterEngine().getRenderer());
TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer();
FlutterRenderer.ImageReaderSurfaceProducer texture =
(FlutterRenderer.ImageReaderSurfaceProducer) producer;
texture.disableFenceForTest();
texture.setSize(1, 1);

// Render two frames.
for (int i = 0; i < 2; i++) {
Surface surface = texture.getSurface();
assertNotNull(surface);
Canvas canvas = surface.lockHardwareCanvas();
canvas.drawARGB(255, 255, 0, 0);
surface.unlockCanvasAndPost(canvas);
shadowOf(Looper.getMainLooper()).idle();
}

// Each enqueue of an image should result in a call to scheduleEngineFrame.
verify(flutterRenderer, times(2)).scheduleEngineFrame();

// Consume the first image.
Image image = texture.acquireLatestImage();
shadowOf(Looper.getMainLooper()).idle();

// The dequeue should call scheduleEngineFrame because another image
// remains in the queue.
verify(flutterRenderer, times(3)).scheduleEngineFrame();

// Consume the second image.
image = texture.acquireLatestImage();
shadowOf(Looper.getMainLooper()).idle();

// The dequeue should not call scheduleEngineFrame because the queue
// is now empty.
verify(flutterRenderer, times(3)).scheduleEngineFrame();
}
}