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


Generated on Sat May 18 2019 05:01:18 for ResidualVM by doxygen 1.7.1
curved edge   curved edge