mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Use placeholder surface to configure OpenGL and frame processors.
The placeholder surface is either EGL_NO_SURFACE or a 1x1 pbuffer depending on whether the device supports EGL_KHR_surfaceless_context. PiperOrigin-RevId: 438541846
This commit is contained in:
parent
bd257d24ed
commit
839f342e55
@ -147,7 +147,11 @@ public final class GlUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether creating a GL context with {@value #EXTENSION_SURFACELESS_CONTEXT} is possible.
|
* Returns whether the {@value #EXTENSION_SURFACELESS_CONTEXT} extension is supported.
|
||||||
|
*
|
||||||
|
* <p>This extension allows passing {@link EGL14#EGL_NO_SURFACE} for both the write and read
|
||||||
|
* surfaces in a call to {@link EGL14#eglMakeCurrent(EGLDisplay, EGLSurface, EGLSurface,
|
||||||
|
* EGLContext)}.
|
||||||
*/
|
*/
|
||||||
public static boolean isSurfacelessContextExtensionSupported() {
|
public static boolean isSurfacelessContextExtensionSupported() {
|
||||||
if (Util.SDK_INT < 17) {
|
if (Util.SDK_INT < 17) {
|
||||||
@ -207,6 +211,52 @@ public final class GlUtil {
|
|||||||
EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ);
|
EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and focuses a new {@link EGLSurface} wrapping a 1x1 pixel buffer.
|
||||||
|
*
|
||||||
|
* @param eglContext The {@link EGLContext} to make current.
|
||||||
|
* @param eglDisplay The {@link EGLDisplay} to attach the surface to.
|
||||||
|
*/
|
||||||
|
@RequiresApi(17)
|
||||||
|
public static void focusPlaceholderEglSurface(EGLContext eglContext, EGLDisplay eglDisplay) {
|
||||||
|
int[] pbufferAttributes =
|
||||||
|
new int[] {
|
||||||
|
EGL14.EGL_WIDTH, /* width= */ 1,
|
||||||
|
EGL14.EGL_HEIGHT, /* height= */ 1,
|
||||||
|
EGL14.EGL_NONE
|
||||||
|
};
|
||||||
|
EGLSurface eglSurface =
|
||||||
|
Api17.createEglPbufferSurface(
|
||||||
|
eglDisplay, EGL_CONFIG_ATTRIBUTES_RGBA_8888, pbufferAttributes);
|
||||||
|
focusEglSurface(eglDisplay, eglContext, eglSurface, /* width= */ 1, /* height= */ 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and focuses a new {@link EGLSurface} wrapping a 1x1 pixel buffer, for HDR rendering
|
||||||
|
* with Rec. 2020 color primaries and using the PQ transfer function.
|
||||||
|
*
|
||||||
|
* @param eglContext The {@link EGLContext} to make current.
|
||||||
|
* @param eglDisplay The {@link EGLDisplay} to attach the surface to.
|
||||||
|
*/
|
||||||
|
@RequiresApi(17)
|
||||||
|
public static void focusPlaceholderEglSurfaceBt2020Pq(
|
||||||
|
EGLContext eglContext, EGLDisplay eglDisplay) {
|
||||||
|
int[] pbufferAttributes =
|
||||||
|
new int[] {
|
||||||
|
EGL14.EGL_WIDTH,
|
||||||
|
/* width= */ 1,
|
||||||
|
EGL14.EGL_HEIGHT,
|
||||||
|
/* height= */ 1,
|
||||||
|
EGL_GL_COLORSPACE_KHR,
|
||||||
|
EGL_GL_COLORSPACE_BT2020_PQ_EXT,
|
||||||
|
EGL14.EGL_NONE
|
||||||
|
};
|
||||||
|
EGLSurface eglSurface =
|
||||||
|
Api17.createEglPbufferSurface(
|
||||||
|
eglDisplay, EGL_CONFIG_ATTRIBUTES_RGBA_1010102, pbufferAttributes);
|
||||||
|
focusEglSurface(eglDisplay, eglContext, eglSurface, /* width= */ 1, /* height= */ 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If there is an OpenGl error, logs the error and if {@link #glAssertionsEnabled} is true throws
|
* If there is an OpenGl error, logs the error and if {@link #glAssertionsEnabled} is true throws
|
||||||
* a {@link GlException}.
|
* a {@link GlException}.
|
||||||
@ -486,6 +536,19 @@ public final class GlUtil {
|
|||||||
return eglSurface;
|
return eglSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DoNotInline
|
||||||
|
public static EGLSurface createEglPbufferSurface(
|
||||||
|
EGLDisplay eglDisplay, int[] configAttributes, int[] pbufferAttributes) {
|
||||||
|
EGLSurface eglSurface =
|
||||||
|
EGL14.eglCreatePbufferSurface(
|
||||||
|
eglDisplay,
|
||||||
|
getEglConfig(eglDisplay, configAttributes),
|
||||||
|
pbufferAttributes,
|
||||||
|
/* offset= */ 0);
|
||||||
|
checkEglException("Error creating surface");
|
||||||
|
return eglSurface;
|
||||||
|
}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
public static void focusRenderTarget(
|
public static void focusRenderTarget(
|
||||||
EGLDisplay eglDisplay,
|
EGLDisplay eglDisplay,
|
||||||
|
@ -45,7 +45,6 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
|
|
||||||
@ -68,8 +67,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
private static final String THREAD_NAME = "Transformer:FrameProcessorChain";
|
private static final String THREAD_NAME = "Transformer:FrameProcessorChain";
|
||||||
|
|
||||||
private final boolean enableExperimentalHdrEditing;
|
private final boolean enableExperimentalHdrEditing;
|
||||||
private final EGLDisplay eglDisplay;
|
private @MonotonicNonNull EGLDisplay eglDisplay;
|
||||||
private final EGLContext eglContext;
|
private @MonotonicNonNull EGLContext eglContext;
|
||||||
/** Some OpenGL commands may block, so all OpenGL commands are run on a background thread. */
|
/** Some OpenGL commands may block, so all OpenGL commands are run on a background thread. */
|
||||||
private final ExecutorService singleThreadExecutorService;
|
private final ExecutorService singleThreadExecutorService;
|
||||||
/** Futures corresponding to the executor service's pending tasks. */
|
/** Futures corresponding to the executor service's pending tasks. */
|
||||||
@ -152,16 +151,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
this.enableExperimentalHdrEditing = enableExperimentalHdrEditing;
|
this.enableExperimentalHdrEditing = enableExperimentalHdrEditing;
|
||||||
this.frameProcessors = ImmutableList.copyOf(frameProcessors);
|
this.frameProcessors = ImmutableList.copyOf(frameProcessors);
|
||||||
|
|
||||||
try {
|
|
||||||
eglDisplay = GlUtil.createEglDisplay();
|
|
||||||
eglContext =
|
|
||||||
enableExperimentalHdrEditing
|
|
||||||
? GlUtil.createEglContextEs3Rgba1010102(eglDisplay)
|
|
||||||
: GlUtil.createEglContext(eglDisplay);
|
|
||||||
} catch (GlUtil.GlException e) {
|
|
||||||
throw TransformationException.createForFrameProcessorChain(
|
|
||||||
e, TransformationException.ERROR_CODE_GL_INIT_FAILED);
|
|
||||||
}
|
|
||||||
singleThreadExecutorService = Util.newSingleThreadExecutor(THREAD_NAME);
|
singleThreadExecutorService = Util.newSingleThreadExecutor(THREAD_NAME);
|
||||||
futures = new ConcurrentLinkedQueue<>();
|
futures = new ConcurrentLinkedQueue<>();
|
||||||
pendingFrameCount = new AtomicInteger();
|
pendingFrameCount = new AtomicInteger();
|
||||||
@ -218,9 +207,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
try {
|
try {
|
||||||
// Wait for task to finish to be able to use inputExternalTexId to create the SurfaceTexture.
|
// Wait for task to finish to be able to use inputExternalTexId to create the SurfaceTexture.
|
||||||
singleThreadExecutorService
|
singleThreadExecutorService
|
||||||
.submit(
|
.submit(this::createOpenGlObjectsAndInitializeFrameProcessors)
|
||||||
() ->
|
|
||||||
createOpenGlObjectsAndInitializeFrameProcessors(outputSurface, debugSurfaceView))
|
|
||||||
.get();
|
.get();
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
throw TransformationException.createForFrameProcessorChain(
|
throw TransformationException.createForFrameProcessorChain(
|
||||||
@ -248,6 +235,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
inputSurface = new Surface(inputSurfaceTexture);
|
inputSurface = new Surface(inputSurfaceTexture);
|
||||||
|
|
||||||
|
futures.add(
|
||||||
|
singleThreadExecutorService.submit(
|
||||||
|
() -> createOpenGlSurfaces(outputSurface, debugSurfaceView)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -344,16 +335,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the OpenGL textures, framebuffers, surfaces, and initializes the {@link
|
* Creates the OpenGL surfaces.
|
||||||
* GlFrameProcessor GlFrameProcessors}.
|
|
||||||
*
|
*
|
||||||
* <p>This method must by executed on the same thread as {@link #processFrame()}, i.e., executed
|
* <p>This method should only be called after {@link
|
||||||
* by the {@link #singleThreadExecutorService}.
|
* #createOpenGlObjectsAndInitializeFrameProcessors()} and must be called on the background
|
||||||
|
* thread.
|
||||||
*/
|
*/
|
||||||
@EnsuresNonNull("eglSurface")
|
private void createOpenGlSurfaces(Surface outputSurface, @Nullable SurfaceView debugSurfaceView) {
|
||||||
private Void createOpenGlObjectsAndInitializeFrameProcessors(
|
|
||||||
Surface outputSurface, @Nullable SurfaceView debugSurfaceView) throws IOException {
|
|
||||||
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
||||||
|
checkStateNotNull(eglDisplay);
|
||||||
|
|
||||||
if (enableExperimentalHdrEditing) {
|
if (enableExperimentalHdrEditing) {
|
||||||
// TODO(b/209404935): Don't assume BT.2020 PQ input/output.
|
// TODO(b/209404935): Don't assume BT.2020 PQ input/output.
|
||||||
@ -369,7 +359,32 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
GlUtil.getEglSurface(eglDisplay, checkNotNull(debugSurfaceView.getHolder()));
|
GlUtil.getEglSurface(eglDisplay, checkNotNull(debugSurfaceView.getHolder()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GlUtil.focusEglSurface(eglDisplay, eglContext, eglSurface, outputWidth, outputHeight);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the OpenGL textures and framebuffers, and initializes the {@link GlFrameProcessor
|
||||||
|
* GlFrameProcessors}.
|
||||||
|
*
|
||||||
|
* <p>This method should only be called on the background thread.
|
||||||
|
*/
|
||||||
|
private Void createOpenGlObjectsAndInitializeFrameProcessors() throws IOException {
|
||||||
|
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
||||||
|
|
||||||
|
eglDisplay = GlUtil.createEglDisplay();
|
||||||
|
eglContext =
|
||||||
|
enableExperimentalHdrEditing
|
||||||
|
? GlUtil.createEglContextEs3Rgba1010102(eglDisplay)
|
||||||
|
: GlUtil.createEglContext(eglDisplay);
|
||||||
|
|
||||||
|
if (GlUtil.isSurfacelessContextExtensionSupported()) {
|
||||||
|
GlUtil.focusEglSurface(
|
||||||
|
eglDisplay, eglContext, EGL14.EGL_NO_SURFACE, /* width= */ 1, /* height= */ 1);
|
||||||
|
} else if (enableExperimentalHdrEditing) {
|
||||||
|
// TODO(b/209404935): Don't assume BT.2020 PQ input/output.
|
||||||
|
GlUtil.focusPlaceholderEglSurfaceBt2020Pq(eglContext, eglDisplay);
|
||||||
|
} else {
|
||||||
|
GlUtil.focusPlaceholderEglSurface(eglContext, eglDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
inputExternalTexId = GlUtil.createExternalTexture();
|
inputExternalTexId = GlUtil.createExternalTexture();
|
||||||
Size inputSize = inputSizes.get(0);
|
Size inputSize = inputSizes.get(0);
|
||||||
@ -389,13 +404,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
/**
|
/**
|
||||||
* Processes an input frame.
|
* Processes an input frame.
|
||||||
*
|
*
|
||||||
* <p>This method must by executed on the same thread as {@link
|
* <p>This method should only be called on the background thread.
|
||||||
* #createOpenGlObjectsAndInitializeFrameProcessors(Surface,SurfaceView)}, i.e., executed by the
|
|
||||||
* {@link #singleThreadExecutorService}.
|
|
||||||
*/
|
*/
|
||||||
@RequiresNonNull({"inputSurfaceTexture", "eglSurface"})
|
@RequiresNonNull("inputSurfaceTexture")
|
||||||
private void processFrame() {
|
private void processFrame() {
|
||||||
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
||||||
|
checkStateNotNull(eglSurface);
|
||||||
|
checkStateNotNull(eglContext);
|
||||||
|
checkStateNotNull(eglDisplay);
|
||||||
|
|
||||||
if (frameProcessors.isEmpty()) {
|
if (frameProcessors.isEmpty()) {
|
||||||
GlUtil.focusEglSurface(eglDisplay, eglContext, eglSurface, outputWidth, outputHeight);
|
GlUtil.focusEglSurface(eglDisplay, eglContext, eglSurface, outputWidth, outputHeight);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user