diff options
Diffstat (limited to 'backends/platform')
-rw-r--r-- | backends/platform/android/README.build | 28 | ||||
-rw-r--r-- | backends/platform/android/android.cpp | 84 | ||||
-rw-r--r-- | backends/platform/android/org/inodes/gus/scummvm/ScummVM.java | 129 |
3 files changed, 116 insertions, 125 deletions
diff --git a/backends/platform/android/README.build b/backends/platform/android/README.build index 0aa5c6eefd..3d1cf433a7 100644 --- a/backends/platform/android/README.build +++ b/backends/platform/android/README.build @@ -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 diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp index 105561e595..f6af0fcff5 100644 --- a/backends/platform/android/android.cpp +++ b/backends/platform/android/android.cpp @@ -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 diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java index 4ce8a85f30..d39aa363ef 100644 --- a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java +++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java @@ -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; - - public void surfaceCreated(SurfaceHolder holder) { + }; + 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) { 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); |