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.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -93,6 +95,10 @@ public class EglRenderer implements VideoSink {
|
|||||||
|
|
||||||
protected final String name;
|
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`.
|
// `eglThread` is used for rendering, and is synchronized on `threadLock`.
|
||||||
private final Object threadLock = new Object();
|
private final Object threadLock = new Object();
|
||||||
@GuardedBy("threadLock") @Nullable private EglThread eglThread;
|
@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
|
* 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
|
* 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.
|
* @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.
|
* 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) {
|
private void swapBuffersOnRenderThread(final VideoFrame frame, long swapBuffersStartTimeNs) {
|
||||||
synchronized (threadLock) {
|
synchronized (threadLock) {
|
||||||
if (eglThread != null) {
|
if (eglThread == null) {
|
||||||
eglThread.scheduleRenderUpdate(
|
return;
|
||||||
runsInline -> {
|
}
|
||||||
if (!runsInline) {
|
EglThread.RenderUpdate renderUpdate =
|
||||||
if (eglBase == null || !eglBase.hasSurface()) {
|
runsInline -> {
|
||||||
return;
|
if (!runsInline) {
|
||||||
}
|
if (eglBase == null || !eglBase.hasSurface()) {
|
||||||
eglBase.makeCurrent();
|
return;
|
||||||
}
|
}
|
||||||
|
eglBase.makeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
if (usePresentationTimeStamp) {
|
if (usePresentationTimeStamp) {
|
||||||
eglBase.swapBuffers(frame.getTimestampNs());
|
eglBase.swapBuffers(frame.getTimestampNs());
|
||||||
} else {
|
} else {
|
||||||
eglBase.swapBuffers();
|
eglBase.swapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var listener : renderListeners) {
|
for (var listener : renderListeners) {
|
||||||
listener.onRender(System.nanoTime());
|
listener.onRender(System.nanoTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (statisticsLock) {
|
synchronized (statisticsLock) {
|
||||||
renderSwapBufferTimeNs += (System.nanoTime() - swapBuffersStartTimeNs);
|
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 android.os.Message;
|
||||||
import androidx.annotation.GuardedBy;
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.webrtc.EglBase.EglConnection;
|
import org.webrtc.EglBase.EglConnection;
|
||||||
|
|
||||||
/** EGL graphics thread that allows multiple clients to share the same underlying EGLContext. */
|
/** 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 HandlerWithExceptionCallbacks handler;
|
||||||
private final EglConnection eglConnection;
|
private final EglConnection eglConnection;
|
||||||
private final RenderSynchronizer renderSynchronizer;
|
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 boolean renderWindowOpen = true;
|
||||||
|
|
||||||
private EglThread(
|
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
|
* 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.
|
* 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.
|
* 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) {
|
public void scheduleRenderUpdate(RenderUpdate update) {
|
||||||
if (renderWindowOpen) {
|
if (renderWindowOpen) {
|
||||||
update.update(/* runsInline = */true);
|
update.update(/* runsInline = */ true);
|
||||||
} else {
|
} else {
|
||||||
pendingRenderUpdates.add(update);
|
pendingRenderUpdatesQueued.add(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,10 +223,14 @@ public class EglThread implements RenderSynchronizer.Listener {
|
|||||||
handler.post(
|
handler.post(
|
||||||
() -> {
|
() -> {
|
||||||
renderWindowOpen = true;
|
renderWindowOpen = true;
|
||||||
for (RenderUpdate update : pendingRenderUpdates) {
|
for (RenderUpdate update : pendingRenderUpdates.values()) {
|
||||||
update.update(/* runsInline = */false);
|
update.update(/* runsInline = */ false);
|
||||||
}
|
}
|
||||||
pendingRenderUpdates.clear();
|
pendingRenderUpdates.clear();
|
||||||
|
for (RenderUpdate update: pendingRenderUpdatesQueued) {
|
||||||
|
update.update(/* runsInline = */ false);
|
||||||
|
}
|
||||||
|
pendingRenderUpdatesQueued.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user