1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
/* -*- 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();
}
}
}
|