gecko/mobile/android/base/gfx/GfxInfoThread.java
Brian Nicholson 2d98f0cf71 Bug 850487 - More code cleanup (@Overrides and unused imports). r=kats
--HG--
extra : rebase_source : 376574e0c41b91c16a6be335584a4a61768bb4a9
2013-03-13 13:20:57 -07:00

220 lines
7.8 KiB
Java

/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import android.opengl.GLES20;
import android.util.Log;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
public class GfxInfoThread extends Thread {
private static final String LOGTAG = "GfxInfoThread";
private static GfxInfoThread sInstance;
private String mData;
private GfxInfoThread() {
}
public static void startThread() {
if (sInstance == null) {
sInstance = new GfxInfoThread();
sInstance.start();
}
}
public static boolean hasData() {
// This should never be called before startThread(), so if
// sInstance is null here, then we know the thread was created,
// ran to completion, and getData() was called. Therefore hasData()
// should return true. If sInstance is not null, then we need to
// check if the mData field on it is null or not and return accordingly.
// Note that we keep a local copy of sInstance to avoid race conditions
// as getData() may be called concurrently.
GfxInfoThread instance = sInstance;
if (instance == null) {
return true;
}
synchronized (instance) {
return instance.mData != null;
}
}
public static String getData() {
// This should be called exactly once after startThread(), so we
// know sInstance will be non-null here
String data = sInstance.getDataImpl();
sInstance = null;
return data;
}
private synchronized void error(String msg) {
Log.e(LOGTAG, msg);
mData = "ERROR\n" + msg + "\n";
notifyAll();
}
private void eglError(EGL10 egl, String msg) {
error(msg + " (EGL error " + Integer.toHexString(egl.eglGetError()) + ")");
}
private synchronized String getDataImpl() {
if (mData != null) {
return mData;
}
Log.w(LOGTAG, "We need the GfxInfo data, but it is not yet available. " +
"We have to wait for it, so expect abnormally long startup times. " +
"Please report a Mozilla bug.");
try {
while (mData == null) {
wait();
}
} catch (InterruptedException e) {
Log.w(LOGTAG, "Thread interrupted", e);
Thread.currentThread().interrupt();
}
Log.i(LOGTAG, "GfxInfo data is finally available.");
return mData;
}
@Override
public void run() {
// initialize EGL
EGL10 egl = (EGL10) EGLContext.getEGL();
EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
eglError(egl, "eglGetDisplay failed");
return;
}
int[] returnedVersion = new int[2];
if (!egl.eglInitialize(eglDisplay, returnedVersion)) {
eglError(egl, "eglInitialize failed");
return;
}
// query number of configs
int[] returnedNumberOfConfigs = new int[1];
int EGL_OPENGL_ES2_BIT = 4;
int[] configAttribs = new int[] {
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE
};
String noES2SupportMsg = "Maybe this device does not support OpenGL ES2?";
if (!egl.eglChooseConfig(eglDisplay,
configAttribs,
null,
0,
returnedNumberOfConfigs))
{
eglError(egl, "eglChooseConfig failed to query OpenGL ES2 configs. " +
noES2SupportMsg);
return;
}
// get the first config
int numConfigs = returnedNumberOfConfigs[0];
if (numConfigs == 0) {
error("eglChooseConfig returned zero OpenGL ES2 configs. " +
noES2SupportMsg);
return;
}
EGLConfig[] returnedConfigs = new EGLConfig[numConfigs];
if (!egl.eglChooseConfig(eglDisplay,
configAttribs,
returnedConfigs,
numConfigs,
returnedNumberOfConfigs))
{
eglError(egl, "eglChooseConfig failed (listing OpenGL ES2 configs). " +
noES2SupportMsg);
return;
}
EGLConfig eglConfig = returnedConfigs[0];
// create a ES 2.0 context
int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int[] contextAttribs = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
EGLContext eglContext = egl.eglCreateContext(eglDisplay,
eglConfig,
EGL10.EGL_NO_CONTEXT,
contextAttribs);
if (eglContext == EGL10.EGL_NO_CONTEXT) {
eglError(egl, "eglCreateContext failed to create a OpenGL ES2 context" +
noES2SupportMsg);
return;
}
// create a surface, necessary to make the context current. Hopefully PBuffers
// are well supported enough. Are there other kinds of off-screen surfaces in
// Android EGL anyway?
int[] surfaceAttribs = new int[] {
EGL10.EGL_WIDTH, 16,
EGL10.EGL_HEIGHT, 16,
EGL10.EGL_NONE
};
EGLSurface eglSurface = egl.eglCreatePbufferSurface(eglDisplay,
eglConfig,
surfaceAttribs);
if (eglSurface == EGL10.EGL_NO_SURFACE) {
eglError(egl, "eglCreatePbufferSurface failed");
return;
}
// obtain GL strings, store them in mDataQueue
if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
eglError(egl, "eglMakeCurrent failed");
return;
}
{
int error = egl.eglGetError();
if (error != EGL10.EGL_SUCCESS) {
error("EGL error " + Integer.toHexString(error));
return;
}
}
String data =
"VENDOR\n" + GLES20.glGetString(GLES20.GL_VENDOR) + "\n" +
"RENDERER\n" + GLES20.glGetString(GLES20.GL_RENDERER) + "\n" +
"VERSION\n" + GLES20.glGetString(GLES20.GL_VERSION) + "\n";
{
int error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
error("OpenGL error " + Integer.toHexString(error));
return;
}
}
// clean up after ourselves. This is especially important as some Android devices
// have a very low limit on the global number of GL contexts.
egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
egl.eglDestroySurface(eglDisplay, eglSurface);
egl.eglDestroyContext(eglDisplay, eglContext);
// intentionally do not eglTerminate: maybe this will make the next eglInitialize faster?
// finally send the data. Notice that we've already freed the EGL resources, so that they don't
// remain there until the data is read.
synchronized (this) {
mData = data;
notifyAll();
}
}
}