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:
1. Android EGL headers and library
2. Android SDK
3. An arm-oe-linux-androideabi GCC toolchain(*)
1. Android SDK
2. An arm-oe-linux-androideabi GCC toolchain(*)
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
@ -18,21 +17,13 @@ you have some other prefix variation.
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
to just download the 3 Android EGL headers from here:
http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=opengl/include/EGL;hb=HEAD
(copy them to a directory called "EGL" somewhere)
Download the SDK from http://developer.android.com/ and install
somewhere. You will need both the API level 8 (aka Android 2.2) and
API level 3 (aka Android 1.5) platforms.
... and grab libEGL.so off an existing phone/emulator:
adb pull /system/lib/libEGL.so /tmp
2. Android SDK
Download and install somewhere.
3. arm-*-linux-androideabi GCC toolchain
2. arm-*-linux-androideabi GCC toolchain
You have several choices for toolchains:
@ -95,11 +86,6 @@ Then build ScummVM:
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
make scummvm.apk

View file

@ -41,7 +41,6 @@
#include <GLES/gl.h>
#include <GLES/glext.h>
#include <EGL/egl.h>
#include <android/log.h>
#include "common/archive.h"
@ -166,12 +165,11 @@ private:
jmethodID MID_getPluginDirectories;
jmethodID MID_setupScummVMSurface;
jmethodID MID_destroyScummVMSurface;
jmethodID MID_swapBuffers;
int _screen_changeid;
EGLDisplay _egl_display;
EGLSurface _egl_surface;
EGLint _egl_surface_width;
EGLint _egl_surface_height;
int _egl_surface_width;
int _egl_surface_height;
bool _force_redraw;
@ -223,6 +221,10 @@ public:
virtual void initBackend();
void addPluginDirectories(Common::FSList &dirs) const;
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 void setFeatureState(Feature f, bool enable);
@ -303,8 +305,6 @@ public:
OSystem_Android::OSystem_Android(jobject am)
: _back_ptr(0),
_egl_display(EGL_NO_DISPLAY),
_egl_surface(EGL_NO_SURFACE),
_screen_changeid(0),
_force_redraw(false),
_game_texture(NULL),
@ -369,6 +369,7 @@ bool OSystem_Android::initJavaHooks(JNIEnv* env, jobject self) {
FIND_METHOD(getPluginDirectories, "()[Ljava/lang/String;");
FIND_METHOD(setupScummVMSurface, "()V");
FIND_METHOD(destroyScummVMSurface, "()V");
FIND_METHOD(swapBuffers, "()Z");
#undef FIND_METHOD
@ -581,6 +582,7 @@ int OSystem_Android::getGraphicsMode() const {
}
void OSystem_Android::setupScummVMSurface() {
ENTER("setupScummVMSurface");
JNIEnv* env = JNU_GetEnv();
env->CallVoidMethod(_back_ptr, MID_setupScummVMSurface);
if (env->ExceptionCheck())
@ -588,37 +590,8 @@ void OSystem_Android::setupScummVMSurface() {
// 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();
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 ;)
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
@ -664,7 +637,6 @@ void OSystem_Android::setupScummVMSurface() {
}
void OSystem_Android::destroyScummVMSurface() {
_egl_surface = EGL_NO_SURFACE;
JNIEnv* env = JNU_GetEnv();
env->CallVoidMethod(_back_ptr, MID_destroyScummVMSurface);
// Can't use OpenGLES functions after this
@ -745,8 +717,11 @@ void OSystem_Android::updateScreen() {
glPushMatrix();
if (_shake_offset != 0) {
// This is the only case where _game_texture doesn't
if (_shake_offset != 0 ||
(!_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.
glClearColorx(0, 0, 0, 1 << 16);
glClear(GL_COLOR_BUFFER_BIT);
@ -759,16 +734,6 @@ void OSystem_Android::updateScreen() {
_game_texture->drawTexture(0, 0,
_egl_surface_width, _egl_surface_height);
} 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();
glScalex(xdiv(_egl_surface_width, _focus_rect.width()),
xdiv(_egl_surface_height, _focus_rect.height()),
@ -831,14 +796,11 @@ void OSystem_Android::updateScreen() {
CHECK_GL_ERROR();
if (!eglSwapBuffers(_egl_display, _egl_surface)) {
EGLint error = eglGetError();
warning("eglSwapBuffers exited with error 0x%x", error);
// Some errors mean we need to reinit GL
if (error == EGL_CONTEXT_LOST) {
destroyScummVMSurface();
setupScummVMSurface();
}
JNIEnv* env = JNU_GetEnv();
if (!env->CallBooleanMethod(_back_ptr, MID_swapBuffers)) {
// Context lost -> need to reinit GL
destroyScummVMSurface();
setupScummVMSurface();
}
}
@ -1365,6 +1327,12 @@ static void ScummVM_enableZoning(JNIEnv* env, jobject self, jboolean 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[] = {
{ "create", "(Landroid/content/res/AssetManager;)V",
(void*)ScummVM_create },
@ -1381,6 +1349,8 @@ const static JNINativeMethod gMethods[] = {
(void*)ScummVM_setConfManString },
{ "enableZoning", "(Z)V",
(void*)ScummVM_enableZoning },
{ "setSurfaceSize", "(II)V",
(void*)ScummVM_setSurfaceSize },
};
JNIEXPORT jint JNICALL

View file

@ -12,7 +12,10 @@ import android.util.Log;
import android.view.Surface;
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.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
@ -30,11 +33,11 @@ import java.util.concurrent.Semaphore;
public class ScummVM implements SurfaceHolder.Callback {
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 {}
private long nativeScummVM; // native code hangs itself here
boolean scummVMRunning = false;
boolean scummVMRunning = false;
private native void create(AssetManager am);
@ -54,49 +57,49 @@ public class ScummVM implements SurfaceHolder.Callback {
destroy();
}
// Surface creation:
// GUI thread: create surface, release lock
// ScummVM thread: acquire lock (block), read surface
//
// Surface deletion:
// GUI thread: post event, acquire lock (block), return
// ScummVM thread: read event, free surface, release lock
//
// In other words, ScummVM thread does this:
// acquire lock
// setup surface
// when SCREEN_CHANGED arrives:
// destroy surface
// release lock
// back to acquire lock
static final int configSpec[] = {
// Surface creation:
// GUI thread: create surface, release lock
// ScummVM thread: acquire lock (block), read surface
//
// Surface deletion:
// GUI thread: post event, acquire lock (block), return
// ScummVM thread: read event, free surface, release lock
//
// In other words, ScummVM thread does this:
// acquire lock
// setup surface
// when SCREEN_CHANGED arrives:
// destroy surface
// release lock
// back to acquire lock
static final int configSpec[] = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 5,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 0,
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
EGL10.EGL_NONE,
};
EGL10 egl;
EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY;
EGLConfig eglConfig;
EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
Semaphore surfaceLock = new Semaphore(0, true);
SurfaceHolder nativeSurface;
};
EGL10 egl;
EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY;
EGLConfig eglConfig;
EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
Semaphore surfaceLock = new Semaphore(0, true);
SurfaceHolder nativeSurface;
public void surfaceCreated(SurfaceHolder holder) {
public void surfaceCreated(SurfaceHolder holder) {
nativeSurface = holder;
surfaceLock.release();
}
}
public void surfaceChanged(SurfaceHolder holder, int format,
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
// Disabled while I debug GL problems
//pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
public void surfaceDestroyed(SurfaceHolder holder) {
pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
try {
surfaceLock.acquire();
@ -104,10 +107,10 @@ public class ScummVM implements SurfaceHolder.Callback {
Log.e(this.toString(),
"Interrupted while waiting for surface lock", e);
}
}
}
// Called by ScummVM thread (from initBackend)
private void createScummVMGLContext() {
// Called by ScummVM thread (from initBackend)
private void createScummVMGLContext() {
egl = (EGL10)EGLContext.getEGL();
eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] version = new int[2];
@ -126,10 +129,11 @@ public class ScummVM implements SurfaceHolder.Callback {
eglContext = egl.eglCreateContext(eglDisplay, eglConfig,
EGL10.EGL_NO_CONTEXT, null);
}
}
// Called by ScummVM thread
protected void setupScummVMSurface() {
// Called by ScummVM thread
static private boolean _log_version = true;
protected void setupScummVMSurface() {
try {
surfaceLock.acquire();
} catch (InterruptedException e) {
@ -140,10 +144,30 @@ public class ScummVM implements SurfaceHolder.Callback {
eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig,
nativeSurface, null);
egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
}
// Called by ScummVM thread
protected void destroyScummVMSurface() {
GL10 gl = (GL10)eglContext.getGL();
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) {
egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
@ -152,17 +176,28 @@ public class ScummVM implements SurfaceHolder.Callback {
}
surfaceLock.release();
}
}
public void setSurface(SurfaceHolder holder) {
public void setSurface(SurfaceHolder holder) {
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
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, String value);
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.
final public native void pushEvent(Event e);
@ -180,10 +215,10 @@ public class ScummVM implements SurfaceHolder.Callback {
protected void showVirtualKeyboard(boolean enable) {}
protected String[] getSysArchives() { return new String[0]; }
protected String[] getPluginDirectories() { return new String[0]; }
protected void initBackend() throws AudioSetupException {
protected void initBackend() throws AudioSetupException {
createScummVMGLContext();
initAudio();
}
}
private static class AudioThread extends Thread {
final private int buf_size;
@ -240,7 +275,7 @@ public class ScummVM implements SurfaceHolder.Callback {
break;
} else if (ret != len) {
Log.w(LOG_TAG, String.format(
"Short audio write. Wrote %dB, not %dB",
"Short audio write. Wrote %dB, not %dB",
ret, buf.length));
// Buffer is full, so yield cpu for a while
Thread.sleep(100);

2
configure vendored
View file

@ -2515,7 +2515,7 @@ case $_backend in
# -lgcc is carefully placed here - we want to catch
# all toolchain symbols in *our* libraries rather
# 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"
add_line_to_config_mk 'PLUGIN_LDFLAGS += $(LDFLAGS) -Wl,-shared,-Bsymbolic'