ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

ResidualVM.java

Go to the documentation of this file.
00001 package org.residualvm.residualvm;
00002 
00003 import android.util.Log;
00004 import android.content.res.AssetManager;
00005 import android.view.SurfaceHolder;
00006 import android.media.AudioFormat;
00007 import android.media.AudioManager;
00008 import android.media.AudioTrack;
00009 
00010 import javax.microedition.khronos.opengles.GL10;
00011 import javax.microedition.khronos.egl.EGL10;
00012 import javax.microedition.khronos.egl.EGLConfig;
00013 import javax.microedition.khronos.egl.EGLContext;
00014 import javax.microedition.khronos.egl.EGLDisplay;
00015 import javax.microedition.khronos.egl.EGLSurface;
00016 
00017 import java.io.File;
00018 import java.util.LinkedHashMap;
00019 
00020 public abstract class ResidualVM implements SurfaceHolder.Callback, Runnable {
00021     final protected static String LOG_TAG = "ResidualVM";
00022     final private AssetManager _asset_manager;
00023     final private Object _sem_surface;
00024 
00025     private EGL10 _egl;
00026     private EGLDisplay _egl_display = EGL10.EGL_NO_DISPLAY;
00027     private EGLConfig _egl_config;
00028     private EGLContext _egl_context = EGL10.EGL_NO_CONTEXT;
00029     private EGLSurface _egl_surface = EGL10.EGL_NO_SURFACE;
00030 
00031     private SurfaceHolder _surface_holder;
00032     private AudioTrack _audio_track;
00033     private int _sample_rate = 0;
00034     private int _buffer_size = 0;
00035 
00036     private String[] _args;
00037 
00038     final private native void create(AssetManager asset_manager,
00039                                         EGL10 egl, EGLDisplay egl_display,
00040                                         AudioTrack audio_track,
00041                                         int sample_rate, int buffer_size);
00042     final private native void destroy();
00043     final private native void setSurface(int width, int height);
00044     final private native int main(String[] args);
00045 
00046     // pause the engine and all native threads
00047     final public native void setPause(boolean pause);
00048     final public native void enableZoning(boolean enable);
00049     // Feed an event to ResidualVM.  Safe to call from other threads.
00050     final public native void pushEvent(int type, int arg1, int arg2, int arg3,
00051                                         int arg4, int arg5, int arg6);
00052 
00053     // Callbacks from C++ peer instance
00054     abstract protected void getDPI(float[] values);
00055     abstract protected void displayMessageOnOSD(String msg);
00056     abstract protected void openUrl(String url);
00057     abstract protected boolean isConnectionLimited();
00058     abstract protected void setWindowCaption(String caption);
00059     abstract protected void showVirtualKeyboard(boolean enable);
00060     abstract protected String[] getSysArchives();
00061 
00062     public ResidualVM(AssetManager asset_manager, SurfaceHolder holder) {
00063         _asset_manager = asset_manager;
00064         _sem_surface = new Object();
00065 
00066         holder.addCallback(this);
00067     }
00068 
00069     // SurfaceHolder callback
00070     final public void surfaceCreated(SurfaceHolder holder) {
00071         Log.d(LOG_TAG, "surfaceCreated");
00072 
00073         // no need to do anything, surfaceChanged() will be called in any case
00074     }
00075 
00076     // SurfaceHolder callback
00077     final public void surfaceChanged(SurfaceHolder holder, int format,
00078                                         int width, int height) {
00079         // the orientation may reset on standby mode and the theme manager
00080         // could assert when using a portrait resolution. so lets not do that.
00081         if (height > width) {
00082             Log.d(LOG_TAG, String.format("Ignoring surfaceChanged: %dx%d (%d)",
00083                                             width, height, format));
00084             return;
00085         }
00086 
00087         Log.d(LOG_TAG, String.format("surfaceChanged: %dx%d (%d)",
00088                                         width, height, format));
00089 
00090         // store values for the native code
00091         // make sure to do it before notifying the lock
00092         // as it leads to a race condition otherwise
00093         setSurface(width, height);
00094 
00095         synchronized(_sem_surface) {
00096             _surface_holder = holder;
00097             _sem_surface.notifyAll();
00098         }
00099     }
00100 
00101     // SurfaceHolder callback
00102     final public void surfaceDestroyed(SurfaceHolder holder) {
00103         Log.d(LOG_TAG, "surfaceDestroyed");
00104 
00105         synchronized(_sem_surface) {
00106             _surface_holder = null;
00107             _sem_surface.notifyAll();
00108         }
00109 
00110         // clear values for the native code
00111         setSurface(0, 0);
00112     }
00113 
00114     final public void setArgs(String[] args) {
00115         _args = args;
00116     }
00117 
00118     final public void run() {
00119         try {
00120             initAudio();
00121             initEGL();
00122 
00123             // wait for the surfaceChanged callback
00124             synchronized(_sem_surface) {
00125                 while (_surface_holder == null)
00126                     _sem_surface.wait();
00127             }
00128         } catch (Exception e) {
00129             deinitEGL();
00130             deinitAudio();
00131 
00132             throw new RuntimeException("Error preparing the ResidualVM thread", e);
00133         }
00134 
00135         create(_asset_manager, _egl, _egl_display,
00136                 _audio_track, _sample_rate, _buffer_size);
00137 
00138         int res = main(_args);
00139 
00140         destroy();
00141 
00142         deinitEGL();
00143         deinitAudio();
00144 
00145         // On exit, tear everything down for a fresh restart next time.
00146         System.exit(res);
00147     }
00148 
00149     final private void initEGL() throws Exception {
00150         _egl = (EGL10)EGLContext.getEGL();
00151         _egl_display = _egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
00152 
00153         int[] version = new int[2];
00154         _egl.eglInitialize(_egl_display, version);
00155 
00156         int[] num_config = new int[1];
00157         int[] config_attrib_list = {
00158                 EGL10.EGL_RENDERABLE_TYPE, 4, // ES2
00159                 EGL10.EGL_RED_SIZE, 5,
00160                 EGL10.EGL_GREEN_SIZE, 6,
00161                 EGL10.EGL_BLUE_SIZE, 5,
00162                 EGL10.EGL_NONE
00163         };
00164         _egl.eglChooseConfig(_egl_display, config_attrib_list, null, 0, num_config);
00165 
00166         final int numConfigs = num_config[0];
00167 
00168         if (numConfigs <= 0)
00169             throw new IllegalArgumentException("No EGL configs");
00170 
00171         EGLConfig[] configs = new EGLConfig[numConfigs];
00172         _egl.eglChooseConfig(_egl_display, config_attrib_list, configs, numConfigs, num_config);
00173 
00174         _egl_config = chooseEglConfig(configs);
00175         int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
00176         int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2,
00177                               EGL10.EGL_NONE };
00178         _egl_context = _egl.eglCreateContext(_egl_display, _egl_config,
00179                                              EGL10.EGL_NO_CONTEXT, attrib_list);
00180 
00181         if (_egl_context == EGL10.EGL_NO_CONTEXT)
00182             throw new Exception(String.format("Failed to create context: 0x%x",
00183                                                 _egl.eglGetError()));
00184     }
00185 
00186     // Callback from C++ peer instance
00187     final protected EGLSurface initSurface() throws Exception {
00188         _egl_surface = _egl.eglCreateWindowSurface(_egl_display, _egl_config,
00189                                                     _surface_holder, null);
00190 
00191         if (_egl_surface == EGL10.EGL_NO_SURFACE)
00192             throw new Exception(String.format(
00193                     "eglCreateWindowSurface failed: 0x%x", _egl.eglGetError()));
00194 
00195         _egl.eglMakeCurrent(_egl_display, _egl_surface, _egl_surface,
00196                             _egl_context);
00197 
00198         GL10 gl = (GL10)_egl_context.getGL();
00199 
00200         Log.i(LOG_TAG, String.format("Using EGL %s (%s); GL %s/%s (%s)",
00201                         _egl.eglQueryString(_egl_display, EGL10.EGL_VERSION),
00202                         _egl.eglQueryString(_egl_display, EGL10.EGL_VENDOR),
00203                         gl.glGetString(GL10.GL_VERSION),
00204                         gl.glGetString(GL10.GL_RENDERER),
00205                         gl.glGetString(GL10.GL_VENDOR)));
00206 
00207         return _egl_surface;
00208     }
00209 
00210     // Callback from C++ peer instance
00211     final protected void deinitSurface() {
00212         if (_egl_display != EGL10.EGL_NO_DISPLAY) {
00213             _egl.eglMakeCurrent(_egl_display, EGL10.EGL_NO_SURFACE,
00214                                 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
00215 
00216             if (_egl_surface != EGL10.EGL_NO_SURFACE)
00217                 _egl.eglDestroySurface(_egl_display, _egl_surface);
00218         }
00219 
00220         _egl_surface = EGL10.EGL_NO_SURFACE;
00221     }
00222 
00223     final private void deinitEGL() {
00224         if (_egl_display != EGL10.EGL_NO_DISPLAY) {
00225             _egl.eglMakeCurrent(_egl_display, EGL10.EGL_NO_SURFACE,
00226                                 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
00227 
00228             if (_egl_surface != EGL10.EGL_NO_SURFACE)
00229                 _egl.eglDestroySurface(_egl_display, _egl_surface);
00230 
00231             if (_egl_context != EGL10.EGL_NO_CONTEXT)
00232                 _egl.eglDestroyContext(_egl_display, _egl_context);
00233 
00234             _egl.eglTerminate(_egl_display);
00235         }
00236 
00237         _egl_surface = EGL10.EGL_NO_SURFACE;
00238         _egl_context = EGL10.EGL_NO_CONTEXT;
00239         _egl_config = null;
00240         _egl_display = EGL10.EGL_NO_DISPLAY;
00241         _egl = null;
00242     }
00243 
00244     final private void initAudio() throws Exception {
00245         _sample_rate = AudioTrack.getNativeOutputSampleRate(
00246                                     AudioManager.STREAM_MUSIC);
00247         // Maximum supported resampler rate (see LinearRateConverter)
00248         if (_sample_rate >= 131072)
00249             _sample_rate = 131071;
00250         _buffer_size = AudioTrack.getMinBufferSize(_sample_rate,
00251                                     AudioFormat.CHANNEL_CONFIGURATION_STEREO,
00252                                     AudioFormat.ENCODING_PCM_16BIT);
00253 
00254         // ~50ms
00255         int buffer_size_want = (_sample_rate * 2 * 2 / 20) & ~1023;
00256 
00257         if (_buffer_size < buffer_size_want) {
00258             Log.w(LOG_TAG, String.format(
00259                 "adjusting audio buffer size (was: %d)", _buffer_size));
00260 
00261             _buffer_size = buffer_size_want;
00262         }
00263 
00264         Log.i(LOG_TAG, String.format("Using %d bytes buffer for %dHz audio",
00265                                         _buffer_size, _sample_rate));
00266 
00267         _audio_track = new AudioTrack(AudioManager.STREAM_MUSIC,
00268                                     _sample_rate,
00269                                     AudioFormat.CHANNEL_CONFIGURATION_STEREO,
00270                                     AudioFormat.ENCODING_PCM_16BIT,
00271                                     _buffer_size,
00272                                     AudioTrack.MODE_STREAM);
00273 
00274         if (_audio_track.getState() != AudioTrack.STATE_INITIALIZED)
00275             throw new Exception(
00276                 String.format("Error initializing AudioTrack: %d",
00277                                 _audio_track.getState()));
00278     }
00279 
00280     final private void deinitAudio() {
00281         if (_audio_track != null)
00282             _audio_track.stop();
00283 
00284         _audio_track = null;
00285         _buffer_size = 0;
00286         _sample_rate = 0;
00287     }
00288 
00289     private static final int[] s_eglAttribs = {
00290         EGL10.EGL_CONFIG_ID,
00291         EGL10.EGL_BUFFER_SIZE,
00292         EGL10.EGL_RED_SIZE,
00293         EGL10.EGL_GREEN_SIZE,
00294         EGL10.EGL_BLUE_SIZE,
00295         EGL10.EGL_ALPHA_SIZE,
00296         EGL10.EGL_CONFIG_CAVEAT,
00297         EGL10.EGL_DEPTH_SIZE,
00298         EGL10.EGL_LEVEL,
00299         EGL10.EGL_MAX_PBUFFER_WIDTH,
00300         EGL10.EGL_MAX_PBUFFER_HEIGHT,
00301         EGL10.EGL_MAX_PBUFFER_PIXELS,
00302         EGL10.EGL_NATIVE_RENDERABLE,
00303         EGL10.EGL_NATIVE_VISUAL_ID,
00304         EGL10.EGL_NATIVE_VISUAL_TYPE,
00305         EGL10.EGL_SAMPLE_BUFFERS,
00306         EGL10.EGL_SAMPLES,
00307         EGL10.EGL_STENCIL_SIZE,
00308         EGL10.EGL_SURFACE_TYPE,
00309         EGL10.EGL_TRANSPARENT_TYPE,
00310         EGL10.EGL_TRANSPARENT_RED_VALUE,
00311         EGL10.EGL_TRANSPARENT_GREEN_VALUE,
00312         EGL10.EGL_TRANSPARENT_BLUE_VALUE
00313     };
00314 
00315     final private class EglAttribs extends LinkedHashMap<Integer, Integer> {
00316         public EglAttribs(EGLConfig config) {
00317             super(s_eglAttribs.length);
00318 
00319             int[] value = new int[1];
00320 
00321             for (int i : s_eglAttribs) {
00322                 _egl.eglGetConfigAttrib(_egl_display, config, i, value);
00323 
00324                 put(i, value[0]);
00325             }
00326         }
00327 
00328         private int weightBits(int attr, int size) {
00329             final int value = get(attr);
00330 
00331             int score = 0;
00332 
00333             if (value == size || (size > 0 && value > size))
00334                 score += 10;
00335 
00336             // penalize for wasted bits
00337             score -= value - size;
00338 
00339             return score;
00340         }
00341 
00342         public int weight() {
00343             int score = 10000;
00344 
00345             if (get(EGL10.EGL_CONFIG_CAVEAT) != EGL10.EGL_NONE)
00346                 score -= 1000;
00347 
00348             // less MSAA is better
00349             score -= get(EGL10.EGL_SAMPLES) * 100;
00350 
00351             // Must be at least 565, but then smaller is better
00352             score += weightBits(EGL10.EGL_RED_SIZE, 5);
00353             score += weightBits(EGL10.EGL_GREEN_SIZE, 6);
00354             score += weightBits(EGL10.EGL_BLUE_SIZE, 5);
00355             score += weightBits(EGL10.EGL_ALPHA_SIZE, 0);
00356             score += weightBits(EGL10.EGL_DEPTH_SIZE, 0);
00357             score += weightBits(EGL10.EGL_STENCIL_SIZE, 0);
00358 
00359             return score;
00360         }
00361 
00362         public String toString() {
00363             String s;
00364 
00365             if (get(EGL10.EGL_ALPHA_SIZE) > 0)
00366                 s = String.format("[%d] RGBA%d%d%d%d",
00367                                     get(EGL10.EGL_CONFIG_ID),
00368                                     get(EGL10.EGL_RED_SIZE),
00369                                     get(EGL10.EGL_GREEN_SIZE),
00370                                     get(EGL10.EGL_BLUE_SIZE),
00371                                     get(EGL10.EGL_ALPHA_SIZE));
00372             else
00373                 s = String.format("[%d] RGB%d%d%d",
00374                                     get(EGL10.EGL_CONFIG_ID),
00375                                     get(EGL10.EGL_RED_SIZE),
00376                                     get(EGL10.EGL_GREEN_SIZE),
00377                                     get(EGL10.EGL_BLUE_SIZE));
00378 
00379             if (get(EGL10.EGL_DEPTH_SIZE) > 0)
00380                 s += String.format(" D%d", get(EGL10.EGL_DEPTH_SIZE));
00381 
00382             if (get(EGL10.EGL_STENCIL_SIZE) > 0)
00383                 s += String.format(" S%d", get(EGL10.EGL_STENCIL_SIZE));
00384 
00385             if (get(EGL10.EGL_SAMPLES) > 0)
00386                 s += String.format(" MSAAx%d", get(EGL10.EGL_SAMPLES));
00387 
00388             if ((get(EGL10.EGL_SURFACE_TYPE) & EGL10.EGL_WINDOW_BIT) > 0)
00389                 s += " W";
00390             if ((get(EGL10.EGL_SURFACE_TYPE) & EGL10.EGL_PBUFFER_BIT) > 0)
00391                 s += " P";
00392             if ((get(EGL10.EGL_SURFACE_TYPE) & EGL10.EGL_PIXMAP_BIT) > 0)
00393                 s += " X";
00394 
00395             switch (get(EGL10.EGL_CONFIG_CAVEAT)) {
00396             case EGL10.EGL_NONE:
00397                 break;
00398 
00399             case EGL10.EGL_SLOW_CONFIG:
00400                 s += " SLOW";
00401                 break;
00402 
00403             case EGL10.EGL_NON_CONFORMANT_CONFIG:
00404                 s += " NON_CONFORMANT";
00405 
00406             default:
00407                 s += String.format(" unknown CAVEAT 0x%x",
00408                                     get(EGL10.EGL_CONFIG_CAVEAT));
00409             }
00410 
00411             return s;
00412         }
00413     };
00414 
00415     final private EGLConfig chooseEglConfig(EGLConfig[] configs) {
00416         EGLConfig res = configs[0];
00417         int bestScore = -1;
00418 
00419         Log.d(LOG_TAG, "EGL configs:");
00420 
00421         for (EGLConfig config : configs) {
00422             EglAttribs attr = new EglAttribs(config);
00423 
00424             // must have
00425             if ((attr.get(EGL10.EGL_SURFACE_TYPE) & EGL10.EGL_WINDOW_BIT) == 0)
00426                 continue;
00427 
00428             int score = attr.weight();
00429 
00430             Log.d(LOG_TAG, String.format("%s (%d)", attr.toString(), score));
00431 
00432             if (score > bestScore) {
00433                 res = config;
00434                 bestScore = score;
00435             }
00436         }
00437 
00438         if (bestScore < 0)
00439             Log.e(LOG_TAG,
00440                     "Unable to find an acceptable EGL config, expect badness.");
00441 
00442         Log.d(LOG_TAG, String.format("Chosen EGL config: %s",
00443                                         new EglAttribs(res).toString()));
00444 
00445         return res;
00446     }
00447 
00448     static {
00449         // For grabbing with gdb...
00450         final boolean sleep_for_debugger = false;
00451         if (sleep_for_debugger) {
00452             try {
00453                 Thread.sleep(20 * 1000);
00454             } catch (InterruptedException e) {
00455             }
00456         }
00457 
00458         System.loadLibrary("residualvm");
00459     }
00460 }


Generated on Sat Mar 16 2019 05:01:51 for ResidualVM by doxygen 1.7.1
curved edge   curved edge