/*
* Copyright (C) 2013 YIXIA.COM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.vov.vitamio;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;
import android.util.Log;
import android.view.Surface;
/**
* DON'T MODIFY THIS FILE IF YOU'RE NOT FAMILIAR WITH EGL, IT'S USED BY NATIVE CODE!!!
*/
public class EGL {
private EGL10 mEgl;
private EGLDisplay mEglDisplay;
private EGLSurface mEglSurface;
private EGLConfig mEglConfig;
private EGLContext mEglContext;
private EGLConfigChooser mEGLConfigChooser;
private EGLContextFactory mEGLContextFactory;
private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
public EGL() {
mEGLConfigChooser = new SimpleEGLConfigChooser();
mEGLContextFactory = new EGLContextFactory();
mEGLWindowSurfaceFactory = new EGLWindowSurfaceFactory();
}
public boolean initialize(Surface surface) {
start();
return createSurface(surface) != null;
}
public void release() {
destroySurface();
finish();
}
public void start() {
mEgl = (EGL10) EGLContext.getEGL();
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
int[] version = new int[2];
if (!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed");
}
mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
mEglContext = null;
throwEglException("createContext");
}
mEglSurface = null;
}
public GL createSurface(Surface surface) {
if (mEgl == null)
throw new RuntimeException("egl not initialized");
if (mEglDisplay == null)
throw new RuntimeException("eglDisplay not initialized");
if (mEglConfig == null)
throw new RuntimeException("mEglConfig not initialized");
if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
}
mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl, mEglDisplay, mEglConfig, surface);
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
return null;
}
throwEglException("createWindowSurface", error);
}
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
throwEglException("eglMakeCurrent");
}
GL gl = mEglContext.getGL();
return gl;
}
public boolean swap() {
if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
int error = mEgl.eglGetError();
switch (error) {
case EGL11.EGL_CONTEXT_LOST:
return false;
case EGL10.EGL_BAD_NATIVE_WINDOW:
Log.e("EglHelper", "eglSwapBuffers returned EGL_BAD_NATIVE_WINDOW. tid=" + Thread.currentThread().getId());
break;
case EGL10.EGL_BAD_SURFACE:
Log.e("EglHelper", "eglSwapBuffers returned EGL_BAD_SURFACE. tid=" + Thread.currentThread().getId());
return false;
default:
throwEglException("eglSwapBuffers", error);
}
}
return true;
}
public void destroySurface() {
if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
mEglSurface = null;
}
}
public void finish() {
if (mEglContext != null) {
mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
mEglContext = null;
}
if (mEglDisplay != null) {
mEgl.eglTerminate(mEglDisplay);
mEglDisplay = null;
}
}
private void throwEglException(String function) {
throwEglException(function, mEgl.eglGetError());
}
private void throwEglException(String function, int error) {
String message = String.format("%s failed: %x", function, error);
Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " " + message);
throw new RuntimeException(message);
}
private static class EGLWindowSurfaceFactory {
public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow) {
return egl.eglCreateWindowSurface(display, config, nativeWindow, null);
}
public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) {
egl.eglDestroySurface(display, surface);
}
}
private class EGLContextFactory {
private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attrib_list);
}
public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
if (!egl.eglDestroyContext(display, context)) {
Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
throw new RuntimeException("eglDestroyContext failed: ");
}
}
}
private abstract class EGLConfigChooser {
protected int[] mConfigSpec;
public EGLConfigChooser(int[] configSpec) {
mConfigSpec = filterConfigSpec(configSpec);
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] num_config = new int[1];
if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config)) {
throw new IllegalArgumentException("eglChooseConfig failed");
}
int numConfigs = num_config[0];
if (numConfigs <= 0) {
throw new IllegalArgumentException("No configs match configSpec");
}
EGLConfig[] configs = new EGLConfig[numConfigs];
if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config)) {
throw new IllegalArgumentException("eglChooseConfig#2 failed");
}
EGLConfig config = chooseConfig(egl, display, configs);
if (config == null) {
throw new IllegalArgumentException("No config chosen");
}
return config;
}
abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
private int[] filterConfigSpec(int[] configSpec) {
int len = configSpec.length;
int[] newConfigSpec = new int[len + 2];
System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
newConfigSpec[len + 1] = EGL10.EGL_NONE;
return newConfigSpec;
}
}
private class ComponentSizeChooser extends EGLConfigChooser {
protected int mRedSize;
protected int mGreenSize;
protected int mBlueSize;
protected int mAlphaSize;
protected int mDepthSize;
protected int mStencilSize;
private int[] mValue;
public ComponentSizeChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize) {
super(new int[]{EGL10.EGL_RED_SIZE, redSize, EGL10.EGL_GREEN_SIZE, greenSize, EGL10.EGL_BLUE_SIZE, blueSize, EGL10.EGL_ALPHA_SIZE, alphaSize, EGL10.EGL_DEPTH_SIZE, depthSize, EGL10.EGL_STENCIL_SIZE, stencilSize, EGL10.EGL_NONE});
mValue = new int[1];
mRedSize = redSize;
mGreenSize = greenSize;
mBlueSize = blueSize;
mAlphaSize = alphaSize;
mDepthSize = depthSize;
mStencilSize = stencilSize;
}
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
for (EGLConfig config : configs) {
int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
if ((d >= mDepthSize) && (s >= mStencilSize)) {
int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
if ((r == mRedSize) && (g == mGreenSize) && (b == mBlueSize) && (a == mAlphaSize)) {
return config;
}
}
}
return null;
}
private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue) {
if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
return mValue[0];
}
return defaultValue;
}
}
private class SimpleEGLConfigChooser extends ComponentSizeChooser {
public SimpleEGLConfigChooser() {
super(5, 6, 5, 0, 0, 0);
}
}
}