Drop direct use of (private) libEGL.so and call EGL functions via Java.

This removes the final private library - we should be completely above
board now.

svn-id: r50677
This commit is contained in:
Angus Lees 2010-07-05 13:06:58 +00:00
parent d4ad7b8a6d
commit 330e94feed
4 changed files with 116 additions and 125 deletions

View file

@ -2,9 +2,8 @@ Building the ScummVM Android port
================================= =================================
You will need these things to build: You will need these things to build:
1. Android EGL headers and library 1. Android SDK
2. Android SDK 2. An arm-oe-linux-androideabi GCC toolchain(*)
3. An arm-oe-linux-androideabi GCC toolchain(*)
In the example commands, we are going to build against the Android 1.5 In the example commands, we are going to build against the Android 1.5
native ABI (but using the Android 1.6 SDK tools). Other version native ABI (but using the Android 1.6 SDK tools). Other version
@ -18,21 +17,13 @@ you have some other prefix variation.
In detail: In detail:
1. Android EGL headers and library 1. Android SDK
You can build these from the full Android source, but it is far easier Download the SDK from http://developer.android.com/ and install
to just download the 3 Android EGL headers from here: somewhere. You will need both the API level 8 (aka Android 2.2) and
http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=opengl/include/EGL;hb=HEAD API level 3 (aka Android 1.5) platforms.
(copy them to a directory called "EGL" somewhere)
... and grab libEGL.so off an existing phone/emulator: 2. arm-*-linux-androideabi GCC toolchain
adb pull /system/lib/libEGL.so /tmp
2. Android SDK
Download and install somewhere.
3. arm-*-linux-androideabi GCC toolchain
You have several choices for toolchains: You have several choices for toolchains:
@ -95,11 +86,6 @@ Then build ScummVM:
export ANDROID_TOP=<root of built Android source> export ANDROID_TOP=<root of built Android source>
EGL_INC="-I<location of EGL/ header directory>"
EGL_LIBS="-L<location of libEGL.so>"
CPPFLAGS="$EGL_INC" \
LDFLAGS="-g $EGL_LIBS" \
./configure --backend=android --host=android --enable-zlib #and any other flags ./configure --backend=android --host=android --enable-zlib #and any other flags
make scummvm.apk make scummvm.apk

View file

@ -41,7 +41,6 @@
#include <GLES/gl.h> #include <GLES/gl.h>
#include <GLES/glext.h> #include <GLES/glext.h>
#include <EGL/egl.h>
#include <android/log.h> #include <android/log.h>
#include "common/archive.h" #include "common/archive.h"
@ -166,12 +165,11 @@ private:
jmethodID MID_getPluginDirectories; jmethodID MID_getPluginDirectories;
jmethodID MID_setupScummVMSurface; jmethodID MID_setupScummVMSurface;
jmethodID MID_destroyScummVMSurface; jmethodID MID_destroyScummVMSurface;
jmethodID MID_swapBuffers;
int _screen_changeid; int _screen_changeid;
EGLDisplay _egl_display; int _egl_surface_width;
EGLSurface _egl_surface; int _egl_surface_height;
EGLint _egl_surface_width;
EGLint _egl_surface_height;
bool _force_redraw; bool _force_redraw;
@ -223,6 +221,10 @@ public:
virtual void initBackend(); virtual void initBackend();
void addPluginDirectories(Common::FSList &dirs) const; void addPluginDirectories(Common::FSList &dirs) const;
void enableZoning(bool enable) { _enable_zoning = enable; } void enableZoning(bool enable) { _enable_zoning = enable; }
void setSurfaceSize(int width, int height) {
_egl_surface_width = width;
_egl_surface_height = height;
}
virtual bool hasFeature(Feature f); virtual bool hasFeature(Feature f);
virtual void setFeatureState(Feature f, bool enable); virtual void setFeatureState(Feature f, bool enable);
@ -303,8 +305,6 @@ public:
OSystem_Android::OSystem_Android(jobject am) OSystem_Android::OSystem_Android(jobject am)
: _back_ptr(0), : _back_ptr(0),
_egl_display(EGL_NO_DISPLAY),
_egl_surface(EGL_NO_SURFACE),
_screen_changeid(0), _screen_changeid(0),
_force_redraw(false), _force_redraw(false),
_game_texture(NULL), _game_texture(NULL),
@ -369,6 +369,7 @@ bool OSystem_Android::initJavaHooks(JNIEnv* env, jobject self) {
FIND_METHOD(getPluginDirectories, "()[Ljava/lang/String;"); FIND_METHOD(getPluginDirectories, "()[Ljava/lang/String;");
FIND_METHOD(setupScummVMSurface, "()V"); FIND_METHOD(setupScummVMSurface, "()V");
FIND_METHOD(destroyScummVMSurface, "()V"); FIND_METHOD(destroyScummVMSurface, "()V");
FIND_METHOD(swapBuffers, "()Z");
#undef FIND_METHOD #undef FIND_METHOD
@ -581,6 +582,7 @@ int OSystem_Android::getGraphicsMode() const {
} }
void OSystem_Android::setupScummVMSurface() { void OSystem_Android::setupScummVMSurface() {
ENTER("setupScummVMSurface");
JNIEnv* env = JNU_GetEnv(); JNIEnv* env = JNU_GetEnv();
env->CallVoidMethod(_back_ptr, MID_setupScummVMSurface); env->CallVoidMethod(_back_ptr, MID_setupScummVMSurface);
if (env->ExceptionCheck()) if (env->ExceptionCheck())
@ -588,37 +590,8 @@ void OSystem_Android::setupScummVMSurface() {
// EGL set up with a new surface. Initialise OpenGLES context. // EGL set up with a new surface. Initialise OpenGLES context.
_egl_display = eglGetCurrentDisplay();
_egl_surface = eglGetCurrentSurface(EGL_DRAW);
static bool log_version = true;
if (log_version) {
__android_log_print(ANDROID_LOG_INFO, LOG_TAG,
"Using EGL %s (%s); GL %s/%s (%s)",
eglQueryString(_egl_display, EGL_VERSION),
eglQueryString(_egl_display, EGL_VENDOR),
glGetString(GL_VERSION),
glGetString(GL_RENDERER),
glGetString(GL_VENDOR));
log_version = false; // only log this once
}
GLESTexture::initGLExtensions(); GLESTexture::initGLExtensions();
if (!eglQuerySurface(_egl_display, _egl_surface,
EGL_WIDTH, &_egl_surface_width) ||
!eglQuerySurface(_egl_display, _egl_surface,
EGL_HEIGHT, &_egl_surface_height)) {
JNU_ThrowByName(env, "java/lang/RuntimeException",
"Error fetching EGL surface width/height");
return;
}
__android_log_print(ANDROID_LOG_INFO, LOG_TAG,
"New surface is %dx%d",
_egl_surface_width, _egl_surface_height);
CHECK_GL_ERROR();
// Turn off anything that looks like 3D ;) // Turn off anything that looks like 3D ;)
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
@ -664,7 +637,6 @@ void OSystem_Android::setupScummVMSurface() {
} }
void OSystem_Android::destroyScummVMSurface() { void OSystem_Android::destroyScummVMSurface() {
_egl_surface = EGL_NO_SURFACE;
JNIEnv* env = JNU_GetEnv(); JNIEnv* env = JNU_GetEnv();
env->CallVoidMethod(_back_ptr, MID_destroyScummVMSurface); env->CallVoidMethod(_back_ptr, MID_destroyScummVMSurface);
// Can't use OpenGLES functions after this // Can't use OpenGLES functions after this
@ -745,8 +717,11 @@ void OSystem_Android::updateScreen() {
glPushMatrix(); glPushMatrix();
if (_shake_offset != 0) { if (_shake_offset != 0 ||
// This is the only case where _game_texture doesn't (!_focus_rect.isEmpty() &&
!Common::Rect(_game_texture->width(),
_game_texture->height()).contains(_focus_rect))) {
// These are the only cases where _game_texture doesn't
// cover the entire screen. // cover the entire screen.
glClearColorx(0, 0, 0, 1 << 16); glClearColorx(0, 0, 0, 1 << 16);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
@ -759,16 +734,6 @@ void OSystem_Android::updateScreen() {
_game_texture->drawTexture(0, 0, _game_texture->drawTexture(0, 0,
_egl_surface_width, _egl_surface_height); _egl_surface_width, _egl_surface_height);
} else { } else {
// Need to ensure any exposed out-of-bounds region doesn't go
// all hall-of-mirrors. If _shake_offset != 0, we've already
// done this above.
const Common::Rect
screen_bounds(_game_texture->width(), _game_texture->height());
if (!screen_bounds.contains(_focus_rect) && _shake_offset != 0) {
glClearColorx(0, 0, 0, 1 << 16);
glClear(GL_COLOR_BUFFER_BIT);
}
glPushMatrix(); glPushMatrix();
glScalex(xdiv(_egl_surface_width, _focus_rect.width()), glScalex(xdiv(_egl_surface_width, _focus_rect.width()),
xdiv(_egl_surface_height, _focus_rect.height()), xdiv(_egl_surface_height, _focus_rect.height()),
@ -831,14 +796,11 @@ void OSystem_Android::updateScreen() {
CHECK_GL_ERROR(); CHECK_GL_ERROR();
if (!eglSwapBuffers(_egl_display, _egl_surface)) { JNIEnv* env = JNU_GetEnv();
EGLint error = eglGetError(); if (!env->CallBooleanMethod(_back_ptr, MID_swapBuffers)) {
warning("eglSwapBuffers exited with error 0x%x", error); // Context lost -> need to reinit GL
// Some errors mean we need to reinit GL destroyScummVMSurface();
if (error == EGL_CONTEXT_LOST) { setupScummVMSurface();
destroyScummVMSurface();
setupScummVMSurface();
}
} }
} }
@ -1365,6 +1327,12 @@ static void ScummVM_enableZoning(JNIEnv* env, jobject self, jboolean enable) {
cpp_obj->enableZoning(enable); cpp_obj->enableZoning(enable);
} }
static void ScummVM_setSurfaceSize(JNIEnv* env, jobject self,
jint width, jint height) {
OSystem_Android* cpp_obj = OSystem_Android::fromJavaObject(env, self);
cpp_obj->setSurfaceSize(width, height);
}
const static JNINativeMethod gMethods[] = { const static JNINativeMethod gMethods[] = {
{ "create", "(Landroid/content/res/AssetManager;)V", { "create", "(Landroid/content/res/AssetManager;)V",
(void*)ScummVM_create }, (void*)ScummVM_create },
@ -1381,6 +1349,8 @@ const static JNINativeMethod gMethods[] = {
(void*)ScummVM_setConfManString }, (void*)ScummVM_setConfManString },
{ "enableZoning", "(Z)V", { "enableZoning", "(Z)V",
(void*)ScummVM_enableZoning }, (void*)ScummVM_enableZoning },
{ "setSurfaceSize", "(II)V",
(void*)ScummVM_setSurfaceSize },
}; };
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL

View file

@ -12,7 +12,10 @@ import android.util.Log;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLDisplay;
@ -30,11 +33,11 @@ import java.util.concurrent.Semaphore;
public class ScummVM implements SurfaceHolder.Callback { public class ScummVM implements SurfaceHolder.Callback {
private final static String LOG_TAG = "ScummVM.java"; private final static String LOG_TAG = "ScummVM.java";
private final int AUDIO_FRAME_SIZE = 2 * 2; // bytes. 16bit audio * stereo private final int AUDIO_FRAME_SIZE = 2 * 2; // bytes. 16bit audio * stereo
public static class AudioSetupException extends Exception {} public static class AudioSetupException extends Exception {}
private long nativeScummVM; // native code hangs itself here private long nativeScummVM; // native code hangs itself here
boolean scummVMRunning = false; boolean scummVMRunning = false;
private native void create(AssetManager am); private native void create(AssetManager am);
@ -54,49 +57,49 @@ public class ScummVM implements SurfaceHolder.Callback {
destroy(); destroy();
} }
// Surface creation: // Surface creation:
// GUI thread: create surface, release lock // GUI thread: create surface, release lock
// ScummVM thread: acquire lock (block), read surface // ScummVM thread: acquire lock (block), read surface
// //
// Surface deletion: // Surface deletion:
// GUI thread: post event, acquire lock (block), return // GUI thread: post event, acquire lock (block), return
// ScummVM thread: read event, free surface, release lock // ScummVM thread: read event, free surface, release lock
// //
// In other words, ScummVM thread does this: // In other words, ScummVM thread does this:
// acquire lock // acquire lock
// setup surface // setup surface
// when SCREEN_CHANGED arrives: // when SCREEN_CHANGED arrives:
// destroy surface // destroy surface
// release lock // release lock
// back to acquire lock // back to acquire lock
static final int configSpec[] = { static final int configSpec[] = {
EGL10.EGL_RED_SIZE, 5, EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 5, EGL10.EGL_GREEN_SIZE, 5,
EGL10.EGL_BLUE_SIZE, 5, EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 0, EGL10.EGL_DEPTH_SIZE, 0,
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
EGL10.EGL_NONE, EGL10.EGL_NONE,
}; };
EGL10 egl; EGL10 egl;
EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY; EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY;
EGLConfig eglConfig; EGLConfig eglConfig;
EGLContext eglContext = EGL10.EGL_NO_CONTEXT; EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
EGLSurface eglSurface = EGL10.EGL_NO_SURFACE; EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
Semaphore surfaceLock = new Semaphore(0, true); Semaphore surfaceLock = new Semaphore(0, true);
SurfaceHolder nativeSurface; SurfaceHolder nativeSurface;
public void surfaceCreated(SurfaceHolder holder) { public void surfaceCreated(SurfaceHolder holder) {
nativeSurface = holder; nativeSurface = holder;
surfaceLock.release(); surfaceLock.release();
} }
public void surfaceChanged(SurfaceHolder holder, int format, public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) { int width, int height) {
// Disabled while I debug GL problems // Disabled while I debug GL problems
//pushEvent(new Event(Event.EVENT_SCREEN_CHANGED)); //pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
} }
public void surfaceDestroyed(SurfaceHolder holder) { public void surfaceDestroyed(SurfaceHolder holder) {
pushEvent(new Event(Event.EVENT_SCREEN_CHANGED)); pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
try { try {
surfaceLock.acquire(); surfaceLock.acquire();
@ -104,10 +107,10 @@ public class ScummVM implements SurfaceHolder.Callback {
Log.e(this.toString(), Log.e(this.toString(),
"Interrupted while waiting for surface lock", e); "Interrupted while waiting for surface lock", e);
} }
} }
// Called by ScummVM thread (from initBackend) // Called by ScummVM thread (from initBackend)
private void createScummVMGLContext() { private void createScummVMGLContext() {
egl = (EGL10)EGLContext.getEGL(); egl = (EGL10)EGLContext.getEGL();
eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] version = new int[2]; int[] version = new int[2];
@ -126,10 +129,11 @@ public class ScummVM implements SurfaceHolder.Callback {
eglContext = egl.eglCreateContext(eglDisplay, eglConfig, eglContext = egl.eglCreateContext(eglDisplay, eglConfig,
EGL10.EGL_NO_CONTEXT, null); EGL10.EGL_NO_CONTEXT, null);
} }
// Called by ScummVM thread // Called by ScummVM thread
protected void setupScummVMSurface() { static private boolean _log_version = true;
protected void setupScummVMSurface() {
try { try {
surfaceLock.acquire(); surfaceLock.acquire();
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -140,10 +144,30 @@ public class ScummVM implements SurfaceHolder.Callback {
eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig,
nativeSurface, null); nativeSurface, null);
egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
}
// Called by ScummVM thread GL10 gl = (GL10)eglContext.getGL();
protected void destroyScummVMSurface() {
if (_log_version) {
Log.i(LOG_TAG, String.format("Using EGL %s (%s); GL %s/%s (%s)",
egl.eglQueryString(eglDisplay, EGL10.EGL_VERSION),
egl.eglQueryString(eglDisplay, EGL10.EGL_VENDOR),
gl.glGetString(GL10.GL_VERSION),
gl.glGetString(GL10.GL_RENDERER),
gl.glGetString(GL10.GL_VENDOR)));
_log_version = false; // only log this once
}
int[] value = new int[1];
egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, value);
int width = value[0];
egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, value);
int height = value[0];
Log.i(LOG_TAG, String.format("New surface is %dx%d", width, height));
setSurfaceSize(width, height);
}
// Called by ScummVM thread
protected void destroyScummVMSurface() {
if (eglSurface != null) { if (eglSurface != null) {
egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
@ -152,17 +176,28 @@ public class ScummVM implements SurfaceHolder.Callback {
} }
surfaceLock.release(); surfaceLock.release();
} }
public void setSurface(SurfaceHolder holder) { public void setSurface(SurfaceHolder holder) {
holder.addCallback(this); holder.addCallback(this);
} }
final public boolean swapBuffers() {
if (!egl.eglSwapBuffers(eglDisplay, eglSurface)) {
int error = egl.eglGetError();
Log.w(LOG_TAG, String.format("eglSwapBuffers exited with error 0x%x", error));
if (error == EGL11.EGL_CONTEXT_LOST)
return false;
}
return true;
}
// Set scummvm config options // Set scummvm config options
final public native static void loadConfigFile(String path); final public native static void loadConfigFile(String path);
final public native static void setConfMan(String key, int value); final public native static void setConfMan(String key, int value);
final public native static void setConfMan(String key, String value); final public native static void setConfMan(String key, String value);
final public native void enableZoning(boolean enable); final public native void enableZoning(boolean enable);
final public native void setSurfaceSize(int width, int height);
// Feed an event to ScummVM. Safe to call from other threads. // Feed an event to ScummVM. Safe to call from other threads.
final public native void pushEvent(Event e); final public native void pushEvent(Event e);
@ -180,10 +215,10 @@ public class ScummVM implements SurfaceHolder.Callback {
protected void showVirtualKeyboard(boolean enable) {} protected void showVirtualKeyboard(boolean enable) {}
protected String[] getSysArchives() { return new String[0]; } protected String[] getSysArchives() { return new String[0]; }
protected String[] getPluginDirectories() { return new String[0]; } protected String[] getPluginDirectories() { return new String[0]; }
protected void initBackend() throws AudioSetupException { protected void initBackend() throws AudioSetupException {
createScummVMGLContext(); createScummVMGLContext();
initAudio(); initAudio();
} }
private static class AudioThread extends Thread { private static class AudioThread extends Thread {
final private int buf_size; final private int buf_size;
@ -240,7 +275,7 @@ public class ScummVM implements SurfaceHolder.Callback {
break; break;
} else if (ret != len) { } else if (ret != len) {
Log.w(LOG_TAG, String.format( Log.w(LOG_TAG, String.format(
"Short audio write. Wrote %dB, not %dB", "Short audio write. Wrote %dB, not %dB",
ret, buf.length)); ret, buf.length));
// Buffer is full, so yield cpu for a while // Buffer is full, so yield cpu for a while
Thread.sleep(100); Thread.sleep(100);

2
configure vendored
View file

@ -2515,7 +2515,7 @@ case $_backend in
# -lgcc is carefully placed here - we want to catch # -lgcc is carefully placed here - we want to catch
# all toolchain symbols in *our* libraries rather # all toolchain symbols in *our* libraries rather
# than pick up anything unhygenic from the Android libs. # than pick up anything unhygenic from the Android libs.
LIBS="-Wl,-Bstatic $static_libs -Wl,-Bdynamic -lgcc $system_libs -lstdc++ -llog -lGLESv1_CM -lEGL" LIBS="-Wl,-Bstatic $static_libs -Wl,-Bdynamic -lgcc $system_libs -lstdc++ -llog -lGLESv1_CM"
DEFINES="$DEFINES -D__ANDROID__ -DANDROID_BACKEND -DREDUCE_MEMORY_USAGE" DEFINES="$DEFINES -D__ANDROID__ -DANDROID_BACKEND -DREDUCE_MEMORY_USAGE"
add_line_to_config_mk 'PLUGIN_LDFLAGS += $(LDFLAGS) -Wl,-shared,-Bsymbolic' add_line_to_config_mk 'PLUGIN_LDFLAGS += $(LDFLAGS) -Wl,-shared,-Bsymbolic'