From 3bd60b09c1b58a2a75591d89a806d6f0d1b1221c Mon Sep 17 00:00:00 2001 From: Angus Lees Date: Mon, 25 Oct 2010 08:50:16 +0000 Subject: ANDROID: Don't trust eglChooseConfig and refilter/sort results manually It seems some Android versions and devices (eg Droid) don't implement eglChooseConfig according to spec and the first result isn't the best choice. Implement our own filtering / scoring to workaround this. svn-id: r53808 --- .../android/org/inodes/gus/scummvm/ScummVM.java | 123 ++++++++++++++++++++- 1 file changed, 118 insertions(+), 5 deletions(-) diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java index d39aa363ef..6986f3988d 100644 --- a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java +++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java @@ -23,6 +23,8 @@ import javax.microedition.khronos.egl.EGLSurface; import java.io.File; import java.util.concurrent.Semaphore; +import java.util.Map; +import java.util.LinkedHashMap; // At least in Android 2.1, eglCreateWindowSurface() requires an @@ -109,6 +111,51 @@ public class ScummVM implements SurfaceHolder.Callback { } } + // For debugging + private static final Map attribs; + static { + attribs = new LinkedHashMap(); + attribs.put("CONFIG_ID", EGL10.EGL_CONFIG_ID); + attribs.put("BUFFER_SIZE", EGL10.EGL_BUFFER_SIZE); + attribs.put("RED_SIZE", EGL10.EGL_RED_SIZE); + attribs.put("GREEN_SIZE", EGL10.EGL_GREEN_SIZE); + attribs.put("BLUE_SIZE", EGL10.EGL_BLUE_SIZE); + attribs.put("ALPHA_SIZE", EGL10.EGL_ALPHA_SIZE); + //attribs.put("BIND_TO_RGB", EGL10.EGL_BIND_TO_TEXTURE_RGB); + //attribs.put("BIND_TO_RGBA", EGL10.EGL_BIND_TO_TEXTURE_RGBA); + attribs.put("CONFIG_CAVEAT", EGL10.EGL_CONFIG_CAVEAT); + attribs.put("DEPTH_SIZE", EGL10.EGL_DEPTH_SIZE); + attribs.put("LEVEL", EGL10.EGL_LEVEL); + attribs.put("MAX_PBUFFER_WIDTH", EGL10.EGL_MAX_PBUFFER_WIDTH); + attribs.put("MAX_PBUFFER_HEIGHT", EGL10.EGL_MAX_PBUFFER_HEIGHT); + attribs.put("MAX_PBUFFER_PIXELS", EGL10.EGL_MAX_PBUFFER_PIXELS); + //attribs.put("MAX_SWAP_INTERVAL", EGL10.EGL_MAX_SWAP_INTERVAL); + //attribs.put("MIN_SWAP_INTERVAL", EGL10.EGL_MIN_SWAP_INTERVAL); + attribs.put("NATIVE_RENDERABLE", EGL10.EGL_NATIVE_RENDERABLE); + attribs.put("NATIVE_VISUAL_ID", EGL10.EGL_NATIVE_VISUAL_ID); + attribs.put("NATIVE_VISUAL_TYPE", EGL10.EGL_NATIVE_VISUAL_TYPE); + attribs.put("SAMPLE_BUFFERS", EGL10.EGL_SAMPLE_BUFFERS); + attribs.put("SAMPLES", EGL10.EGL_SAMPLES); + attribs.put("STENCIL_SIZE", EGL10.EGL_STENCIL_SIZE); + attribs.put("SURFACE_TYPE", EGL10.EGL_SURFACE_TYPE); + attribs.put("TRANSPARENT_TYPE", EGL10.EGL_TRANSPARENT_TYPE); + attribs.put("TRANSPARENT_RED_VALUE", EGL10.EGL_TRANSPARENT_RED_VALUE); + attribs.put("TRANSPARENT_GREEN_VALUE", EGL10.EGL_TRANSPARENT_GREEN_VALUE); + attribs.put("TRANSPARENT_BLUE_VALUE", EGL10.EGL_TRANSPARENT_BLUE_VALUE); + } + private void dumpEglConfig(EGLConfig config) { + int[] value = new int[1]; + for (Map.Entry entry : attribs.entrySet()) { + egl.eglGetConfigAttrib(eglDisplay, config, + entry.getValue(), value); + if (value[0] == EGL10.EGL_NONE) + Log.d(LOG_TAG, entry.getKey() + ": NONE"); + else + Log.d(LOG_TAG, String.format("%s: %d", + entry.getKey(), value[0])); + } + } + // Called by ScummVM thread (from initBackend) private void createScummVMGLContext() { egl = (EGL10)EGLContext.getEGL(); @@ -125,10 +172,75 @@ public class ScummVM implements SurfaceHolder.Callback { EGLConfig[] configs = new EGLConfig[numConfigs]; egl.eglChooseConfig(eglDisplay, configSpec, configs, numConfigs, num_config); - eglConfig = configs[0]; + + if (false) { + Log.d(LOG_TAG, + String.format("Found %d EGL configurations.", numConfigs)); + for (EGLConfig config : configs) + dumpEglConfig(config); + } + + // Android's eglChooseConfig is busted in several versions and + // devices so we have to filter/rank the configs again ourselves. + eglConfig = chooseEglConfig(configs); + if (false) { + Log.d(LOG_TAG, + String.format("Chose EGL config from %d possibilities.", numConfigs)); + dumpEglConfig(eglConfig); + } eglContext = egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, null); + if (eglContext == EGL10.EGL_NO_CONTEXT) + throw new RuntimeException("Failed to create context"); + } + + private EGLConfig chooseEglConfig(EGLConfig[] configs) { + int best = 0; + int bestScore = -1; + int[] value = new int[1]; + for (int i = 0; i < configs.length; i++) { + EGLConfig config = configs[i]; + int score = 10000; + egl.eglGetConfigAttrib(eglDisplay, config, + EGL10.EGL_SURFACE_TYPE, value); + if ((value[0] & EGL10.EGL_WINDOW_BIT) == 0) + continue; // must have + + egl.eglGetConfigAttrib(eglDisplay, config, + EGL10.EGL_CONFIG_CAVEAT, value); + if (value[0] != EGL10.EGL_NONE) + score -= 1000; + + // Must be at least 555, but then smaller is better + final int[] colorBits = {EGL10.EGL_RED_SIZE, + EGL10.EGL_GREEN_SIZE, + EGL10.EGL_BLUE_SIZE, + EGL10.EGL_ALPHA_SIZE}; + for (int component : colorBits) { + egl.eglGetConfigAttrib(eglDisplay, config, + component, value); + if (value[0] >= 5) + score += 10; // boost if >5 bits accuracy + score -= value[0]; // penalize for wasted bits + } + + egl.eglGetConfigAttrib(eglDisplay, config, + EGL10.EGL_DEPTH_SIZE, value); + score -= value[0]; // penalize for wasted bits + + if (score > bestScore) { + best = i; + bestScore = score; + } + } + + if (bestScore < 0) { + Log.e(LOG_TAG, "Unable to find an acceptable EGL config, expect badness."); + return configs[0]; + } + + return configs[best]; } // Called by ScummVM thread @@ -137,12 +249,13 @@ public class ScummVM implements SurfaceHolder.Callback { try { surfaceLock.acquire(); } catch (InterruptedException e) { - Log.e(this.toString(), - "Interrupted while waiting for surface lock", e); + Log.e(LOG_TAG, "Interrupted while waiting for surface lock", e); return; } eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, nativeSurface, null); + if (eglSurface == EGL10.EGL_NO_SURFACE) + Log.e(LOG_TAG, "CreateWindowSurface failed!"); egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); GL10 gl = (GL10)eglContext.getGL(); @@ -302,8 +415,8 @@ public class ScummVM implements SurfaceHolder.Callback { if (buf_size < 0) { int guess = AUDIO_FRAME_SIZE * sample_rate / 100; // 10ms of audio Log.w(LOG_TAG, String.format( - "Unable to get min audio buffer size (error %d). Guessing %dB.", - buf_size, guess)); + "Unable to get min audio buffer size (error %d). Guessing %dB.", + buf_size, guess)); buf_size = guess; } Log.d(LOG_TAG, String.format("Using %dB buffer for %dHZ audio", -- cgit v1.2.3