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


Generated on Sat Jun 27 2020 05:00:31 for ResidualVM by doxygen 1.7.1
curved edge   curved edge