This CL adds a way to extract the underlying android.opengl.EGLContext and javax.microedition.khronos.egl.EGLContext for EglBase14 and EglBase10 respectively. The reason is that clients can't be expected to use only WebRTC's OpenGL code and might need to integrate with their own GL code. Bug: None Change-Id: Ie00a564de45a090683542a52005da7e43c586ced Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/127888 Commit-Queue: Magnus Jedvert <magjed@webrtc.org> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org> Cr-Commit-Position: refs/heads/master@{#27205}
283 lines
9.7 KiB
Java
283 lines
9.7 KiB
Java
/*
|
|
* Copyright 2015 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
package org.webrtc;
|
|
|
|
import android.annotation.TargetApi;
|
|
import android.graphics.SurfaceTexture;
|
|
import android.opengl.EGL14;
|
|
import android.opengl.EGLConfig;
|
|
import android.opengl.EGLContext;
|
|
import android.opengl.EGLDisplay;
|
|
import android.opengl.EGLExt;
|
|
import android.opengl.EGLSurface;
|
|
import android.os.Build;
|
|
import android.support.annotation.Nullable;
|
|
import android.view.Surface;
|
|
import org.webrtc.EglBase;
|
|
|
|
/**
|
|
* Holds EGL state and utility methods for handling an EGL14 EGLContext, an EGLDisplay,
|
|
* and an EGLSurface.
|
|
*/
|
|
@SuppressWarnings("ReferenceEquality") // We want to compare to EGL14 constants.
|
|
@TargetApi(18)
|
|
class EglBase14Impl implements EglBase14 {
|
|
private static final String TAG = "EglBase14";
|
|
private static final int EGLExt_SDK_VERSION = Build.VERSION_CODES.JELLY_BEAN_MR2;
|
|
private static final int CURRENT_SDK_VERSION = Build.VERSION.SDK_INT;
|
|
private EGLContext eglContext;
|
|
@Nullable private EGLConfig eglConfig;
|
|
private EGLDisplay eglDisplay;
|
|
private EGLSurface eglSurface = EGL14.EGL_NO_SURFACE;
|
|
|
|
// EGL 1.4 is supported from API 17. But EGLExt that is used for setting presentation
|
|
// time stamp on a surface is supported from 18 so we require 18.
|
|
public static boolean isEGL14Supported() {
|
|
Logging.d(TAG,
|
|
"SDK version: " + CURRENT_SDK_VERSION
|
|
+ ". isEGL14Supported: " + (CURRENT_SDK_VERSION >= EGLExt_SDK_VERSION));
|
|
return (CURRENT_SDK_VERSION >= EGLExt_SDK_VERSION);
|
|
}
|
|
|
|
public static class Context implements EglBase14.Context {
|
|
private final EGLContext egl14Context;
|
|
|
|
@Override
|
|
public EGLContext getRawContext() {
|
|
return egl14Context;
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings("deprecation")
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
public long getNativeEglContext() {
|
|
return CURRENT_SDK_VERSION >= Build.VERSION_CODES.LOLLIPOP ? egl14Context.getNativeHandle()
|
|
: egl14Context.getHandle();
|
|
}
|
|
|
|
public Context(android.opengl.EGLContext eglContext) {
|
|
this.egl14Context = eglContext;
|
|
}
|
|
}
|
|
|
|
// Create a new context with the specified config type, sharing data with sharedContext.
|
|
// |sharedContext| may be null.
|
|
public EglBase14Impl(EGLContext sharedContext, int[] configAttributes) {
|
|
eglDisplay = getEglDisplay();
|
|
eglConfig = getEglConfig(eglDisplay, configAttributes);
|
|
eglContext = createEglContext(sharedContext, eglDisplay, eglConfig);
|
|
}
|
|
|
|
// Create EGLSurface from the Android Surface.
|
|
@Override
|
|
public void createSurface(Surface surface) {
|
|
createSurfaceInternal(surface);
|
|
}
|
|
|
|
// Create EGLSurface from the Android SurfaceTexture.
|
|
@Override
|
|
public void createSurface(SurfaceTexture surfaceTexture) {
|
|
createSurfaceInternal(surfaceTexture);
|
|
}
|
|
|
|
// Create EGLSurface from either Surface or SurfaceTexture.
|
|
private void createSurfaceInternal(Object surface) {
|
|
if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) {
|
|
throw new IllegalStateException("Input must be either a Surface or SurfaceTexture");
|
|
}
|
|
checkIsNotReleased();
|
|
if (eglSurface != EGL14.EGL_NO_SURFACE) {
|
|
throw new RuntimeException("Already has an EGLSurface");
|
|
}
|
|
int[] surfaceAttribs = {EGL14.EGL_NONE};
|
|
eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttribs, 0);
|
|
if (eglSurface == EGL14.EGL_NO_SURFACE) {
|
|
throw new RuntimeException(
|
|
"Failed to create window surface: 0x" + Integer.toHexString(EGL14.eglGetError()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void createDummyPbufferSurface() {
|
|
createPbufferSurface(1, 1);
|
|
}
|
|
|
|
@Override
|
|
public void createPbufferSurface(int width, int height) {
|
|
checkIsNotReleased();
|
|
if (eglSurface != EGL14.EGL_NO_SURFACE) {
|
|
throw new RuntimeException("Already has an EGLSurface");
|
|
}
|
|
int[] surfaceAttribs = {EGL14.EGL_WIDTH, width, EGL14.EGL_HEIGHT, height, EGL14.EGL_NONE};
|
|
eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, eglConfig, surfaceAttribs, 0);
|
|
if (eglSurface == EGL14.EGL_NO_SURFACE) {
|
|
throw new RuntimeException("Failed to create pixel buffer surface with size " + width + "x"
|
|
+ height + ": 0x" + Integer.toHexString(EGL14.eglGetError()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Context getEglBaseContext() {
|
|
return new Context(eglContext);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasSurface() {
|
|
return eglSurface != EGL14.EGL_NO_SURFACE;
|
|
}
|
|
|
|
@Override
|
|
public int surfaceWidth() {
|
|
final int widthArray[] = new int[1];
|
|
EGL14.eglQuerySurface(eglDisplay, eglSurface, EGL14.EGL_WIDTH, widthArray, 0);
|
|
return widthArray[0];
|
|
}
|
|
|
|
@Override
|
|
public int surfaceHeight() {
|
|
final int heightArray[] = new int[1];
|
|
EGL14.eglQuerySurface(eglDisplay, eglSurface, EGL14.EGL_HEIGHT, heightArray, 0);
|
|
return heightArray[0];
|
|
}
|
|
|
|
@Override
|
|
public void releaseSurface() {
|
|
if (eglSurface != EGL14.EGL_NO_SURFACE) {
|
|
EGL14.eglDestroySurface(eglDisplay, eglSurface);
|
|
eglSurface = EGL14.EGL_NO_SURFACE;
|
|
}
|
|
}
|
|
|
|
private void checkIsNotReleased() {
|
|
if (eglDisplay == EGL14.EGL_NO_DISPLAY || eglContext == EGL14.EGL_NO_CONTEXT
|
|
|| eglConfig == null) {
|
|
throw new RuntimeException("This object has been released");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void release() {
|
|
checkIsNotReleased();
|
|
releaseSurface();
|
|
detachCurrent();
|
|
EGL14.eglDestroyContext(eglDisplay, eglContext);
|
|
EGL14.eglReleaseThread();
|
|
EGL14.eglTerminate(eglDisplay);
|
|
eglContext = EGL14.EGL_NO_CONTEXT;
|
|
eglDisplay = EGL14.EGL_NO_DISPLAY;
|
|
eglConfig = null;
|
|
}
|
|
|
|
@Override
|
|
public void makeCurrent() {
|
|
checkIsNotReleased();
|
|
if (eglSurface == EGL14.EGL_NO_SURFACE) {
|
|
throw new RuntimeException("No EGLSurface - can't make current");
|
|
}
|
|
synchronized (EglBase.lock) {
|
|
if (!EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
|
|
throw new RuntimeException(
|
|
"eglMakeCurrent failed: 0x" + Integer.toHexString(EGL14.eglGetError()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Detach the current EGL context, so that it can be made current on another thread.
|
|
@Override
|
|
public void detachCurrent() {
|
|
synchronized (EglBase.lock) {
|
|
if (!EGL14.eglMakeCurrent(
|
|
eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)) {
|
|
throw new RuntimeException(
|
|
"eglDetachCurrent failed: 0x" + Integer.toHexString(EGL14.eglGetError()));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void swapBuffers() {
|
|
checkIsNotReleased();
|
|
if (eglSurface == EGL14.EGL_NO_SURFACE) {
|
|
throw new RuntimeException("No EGLSurface - can't swap buffers");
|
|
}
|
|
synchronized (EglBase.lock) {
|
|
EGL14.eglSwapBuffers(eglDisplay, eglSurface);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void swapBuffers(long timeStampNs) {
|
|
checkIsNotReleased();
|
|
if (eglSurface == EGL14.EGL_NO_SURFACE) {
|
|
throw new RuntimeException("No EGLSurface - can't swap buffers");
|
|
}
|
|
synchronized (EglBase.lock) {
|
|
// See
|
|
// https://android.googlesource.com/platform/frameworks/native/+/tools_r22.2/opengl/specs/EGL_ANDROID_presentation_time.txt
|
|
EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, timeStampNs);
|
|
EGL14.eglSwapBuffers(eglDisplay, eglSurface);
|
|
}
|
|
}
|
|
|
|
// Return an EGLDisplay, or die trying.
|
|
private static EGLDisplay getEglDisplay() {
|
|
EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
|
if (eglDisplay == EGL14.EGL_NO_DISPLAY) {
|
|
throw new RuntimeException(
|
|
"Unable to get EGL14 display: 0x" + Integer.toHexString(EGL14.eglGetError()));
|
|
}
|
|
int[] version = new int[2];
|
|
if (!EGL14.eglInitialize(eglDisplay, version, 0, version, 1)) {
|
|
throw new RuntimeException(
|
|
"Unable to initialize EGL14: 0x" + Integer.toHexString(EGL14.eglGetError()));
|
|
}
|
|
return eglDisplay;
|
|
}
|
|
|
|
// Return an EGLConfig, or die trying.
|
|
private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] configAttributes) {
|
|
EGLConfig[] configs = new EGLConfig[1];
|
|
int[] numConfigs = new int[1];
|
|
if (!EGL14.eglChooseConfig(
|
|
eglDisplay, configAttributes, 0, configs, 0, configs.length, numConfigs, 0)) {
|
|
throw new RuntimeException(
|
|
"eglChooseConfig failed: 0x" + Integer.toHexString(EGL14.eglGetError()));
|
|
}
|
|
if (numConfigs[0] <= 0) {
|
|
throw new RuntimeException("Unable to find any matching EGL config");
|
|
}
|
|
final EGLConfig eglConfig = configs[0];
|
|
if (eglConfig == null) {
|
|
throw new RuntimeException("eglChooseConfig returned null");
|
|
}
|
|
return eglConfig;
|
|
}
|
|
|
|
// Return an EGLConfig, or die trying.
|
|
private static EGLContext createEglContext(
|
|
@Nullable EGLContext sharedContext, EGLDisplay eglDisplay, EGLConfig eglConfig) {
|
|
if (sharedContext != null && sharedContext == EGL14.EGL_NO_CONTEXT) {
|
|
throw new RuntimeException("Invalid sharedContext");
|
|
}
|
|
int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
|
|
EGLContext rootContext = sharedContext == null ? EGL14.EGL_NO_CONTEXT : sharedContext;
|
|
final EGLContext eglContext;
|
|
synchronized (EglBase.lock) {
|
|
eglContext = EGL14.eglCreateContext(eglDisplay, eglConfig, rootContext, contextAttributes, 0);
|
|
}
|
|
if (eglContext == EGL14.EGL_NO_CONTEXT) {
|
|
throw new RuntimeException(
|
|
"Failed to create EGL context: 0x" + Integer.toHexString(EGL14.eglGetError()));
|
|
}
|
|
return eglContext;
|
|
}
|
|
}
|