Optionally overwrite instead of queueing render updates.
Here, we overwrite the pending frames per renderer. Bug: webrtc:351858995 Change-Id: I070219aec4e7be5f2b0c9f2371fe2c99af3e3920 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/358760 Reviewed-by: Zoé Lepaul <xalep@webrtc.org> Reviewed-by: Magnus Jedvert <magjed@webrtc.org> Reviewed-by: Fabian Bergmark <fabianbergmark@google.com> Commit-Queue: Zoé Lepaul <xalep@webrtc.org> Cr-Commit-Position: refs/heads/main@{#42741}
This commit is contained in:
parent
675986ec5f
commit
c7e2568457
@ -21,6 +21,8 @@ import java.nio.ByteBuffer;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -93,6 +95,10 @@ public class EglRenderer implements VideoSink {
|
||||
|
||||
protected final String name;
|
||||
|
||||
// An id to uniquely identify the renderer, used for when we're scheduling
|
||||
// frames for render.
|
||||
private Optional<UUID> id = Optional.empty();
|
||||
|
||||
// `eglThread` is used for rendering, and is synchronized on `threadLock`.
|
||||
private final Object threadLock = new Object();
|
||||
@GuardedBy("threadLock") @Nullable private EglThread eglThread;
|
||||
@ -214,6 +220,23 @@ public class EglRenderer implements VideoSink {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intializes this class with the given parameters.
|
||||
*
|
||||
* The new parameter here, `overwritePendingFrames` overwrites instead of
|
||||
* queueing frames when passing them to the synchronized renderer.
|
||||
*/
|
||||
public void init(
|
||||
EglThread eglThread,
|
||||
RendererCommon.GlDrawer drawer,
|
||||
boolean usePresentationTimeStamp,
|
||||
boolean overwritePendingFrames) {
|
||||
if (overwritePendingFrames) {
|
||||
id = Optional.of(UUID.randomUUID());
|
||||
}
|
||||
init(eglThread, drawer, usePresentationTimeStamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this class, sharing resources with `sharedContext`. The custom `drawer` will be used
|
||||
* for drawing frames on the EGLSurface. This class is responsible for calling release() on
|
||||
@ -444,7 +467,7 @@ public class EglRenderer implements VideoSink {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when a new video frame has been rendered.
|
||||
* Register a callback to be invoked when a new video frame has been rendered.
|
||||
*
|
||||
* @param listener The callback to be invoked. The callback will be invoked on the render thread.
|
||||
* It should be lightweight and must not call removeRenderListener.
|
||||
@ -611,30 +634,36 @@ public class EglRenderer implements VideoSink {
|
||||
|
||||
private void swapBuffersOnRenderThread(final VideoFrame frame, long swapBuffersStartTimeNs) {
|
||||
synchronized (threadLock) {
|
||||
if (eglThread != null) {
|
||||
eglThread.scheduleRenderUpdate(
|
||||
runsInline -> {
|
||||
if (!runsInline) {
|
||||
if (eglBase == null || !eglBase.hasSurface()) {
|
||||
return;
|
||||
}
|
||||
eglBase.makeCurrent();
|
||||
if (eglThread == null) {
|
||||
return;
|
||||
}
|
||||
EglThread.RenderUpdate renderUpdate =
|
||||
runsInline -> {
|
||||
if (!runsInline) {
|
||||
if (eglBase == null || !eglBase.hasSurface()) {
|
||||
return;
|
||||
}
|
||||
eglBase.makeCurrent();
|
||||
}
|
||||
|
||||
if (usePresentationTimeStamp) {
|
||||
eglBase.swapBuffers(frame.getTimestampNs());
|
||||
} else {
|
||||
eglBase.swapBuffers();
|
||||
}
|
||||
if (usePresentationTimeStamp) {
|
||||
eglBase.swapBuffers(frame.getTimestampNs());
|
||||
} else {
|
||||
eglBase.swapBuffers();
|
||||
}
|
||||
|
||||
for (var listener : renderListeners) {
|
||||
listener.onRender(System.nanoTime());
|
||||
}
|
||||
for (var listener : renderListeners) {
|
||||
listener.onRender(System.nanoTime());
|
||||
}
|
||||
|
||||
synchronized (statisticsLock) {
|
||||
renderSwapBufferTimeNs += (System.nanoTime() - swapBuffersStartTimeNs);
|
||||
}
|
||||
});
|
||||
synchronized (statisticsLock) {
|
||||
renderSwapBufferTimeNs += (System.nanoTime() - swapBuffersStartTimeNs);
|
||||
}
|
||||
};
|
||||
if (id.isPresent()) {
|
||||
eglThread.scheduleRenderUpdate(id.get(), renderUpdate);
|
||||
} else {
|
||||
eglThread.scheduleRenderUpdate(renderUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,8 +16,13 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.webrtc.EglBase.EglConnection;
|
||||
|
||||
/** EGL graphics thread that allows multiple clients to share the same underlying EGLContext. */
|
||||
@ -122,7 +127,10 @@ public class EglThread implements RenderSynchronizer.Listener {
|
||||
private final HandlerWithExceptionCallbacks handler;
|
||||
private final EglConnection eglConnection;
|
||||
private final RenderSynchronizer renderSynchronizer;
|
||||
private final List<RenderUpdate> pendingRenderUpdates = new ArrayList<>();
|
||||
// Pending render updates if they're overwritten per renderer.
|
||||
private final Map<UUID, RenderUpdate> pendingRenderUpdates = new HashMap<>();
|
||||
// Pending render updates if they're in a global queue.
|
||||
private final List<RenderUpdate> pendingRenderUpdatesQueued = new ArrayList<>();
|
||||
private boolean renderWindowOpen = true;
|
||||
|
||||
private EglThread(
|
||||
@ -188,12 +196,25 @@ public class EglThread implements RenderSynchronizer.Listener {
|
||||
* Schedules a render update (like swapBuffers) to be run in sync with other updates on the next
|
||||
* open render window. If the render window is currently open the update will run immediately.
|
||||
* This method must be called on the EglThread during a render pass.
|
||||
*
|
||||
* @param id a unique id of the renderer that scheduled this render update.
|
||||
*/
|
||||
public void scheduleRenderUpdate(UUID id, RenderUpdate update) {
|
||||
if (renderWindowOpen) {
|
||||
update.update(/* runsInline = */ true);
|
||||
} else {
|
||||
pendingRenderUpdates.put(id, update);
|
||||
}
|
||||
}
|
||||
|
||||
// The same as above, except that the ids are randomly generated for each frame.
|
||||
// So this essentially becomes a queue of frame updates.
|
||||
@Deprecated
|
||||
public void scheduleRenderUpdate(RenderUpdate update) {
|
||||
if (renderWindowOpen) {
|
||||
update.update(/* runsInline = */true);
|
||||
update.update(/* runsInline = */ true);
|
||||
} else {
|
||||
pendingRenderUpdates.add(update);
|
||||
pendingRenderUpdatesQueued.add(update);
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,10 +223,14 @@ public class EglThread implements RenderSynchronizer.Listener {
|
||||
handler.post(
|
||||
() -> {
|
||||
renderWindowOpen = true;
|
||||
for (RenderUpdate update : pendingRenderUpdates) {
|
||||
update.update(/* runsInline = */false);
|
||||
for (RenderUpdate update : pendingRenderUpdates.values()) {
|
||||
update.update(/* runsInline = */ false);
|
||||
}
|
||||
pendingRenderUpdates.clear();
|
||||
for (RenderUpdate update: pendingRenderUpdatesQueued) {
|
||||
update.update(/* runsInline = */ false);
|
||||
}
|
||||
pendingRenderUpdatesQueued.clear();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user