Android: Make lcd updates synchronous, doesn't make it faster but smoother (no updates are skipped) and guaranteed to be glitch free.
test_fps can also now report reasonable numbers: ~62 fps for both 1/1 and 1/4 updates (was 300-400 previously).

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28728 a1c6a512-1295-4272-9138-f99709370657
diff --git a/android/src/org/rockbox/RockboxFramebuffer.java b/android/src/org/rockbox/RockboxFramebuffer.java
index 8c99725..1841556 100644
--- a/android/src/org/rockbox/RockboxFramebuffer.java
+++ b/android/src/org/rockbox/RockboxFramebuffer.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -38,6 +39,7 @@
 public class RockboxFramebuffer extends View
 {
     private Bitmap btm;
+    private Rect rect;
     private ByteBuffer native_buf;
     private MediaButtonReceiver media_monitor;
     private final DisplayMetrics metrics;
@@ -52,6 +54,7 @@
         setFocusableInTouchMode(true);
         setClickable(true);
         btm = Bitmap.createBitmap(lcd_width, lcd_height, Bitmap.Config.RGB_565);
+        rect = new Rect();
         native_buf = native_fb;
         media_monitor = new MediaButtonReceiver(c);
         media_monitor.register();
@@ -64,22 +67,11 @@
 
     public void onDraw(Canvas c) 
     {
-        c.drawBitmap(btm, 0.0f, 0.0f, null);
-    }
-
-    @SuppressWarnings("unused")
-    private void java_lcd_update()
-    {
+        /* can't copy a partial buffer :( */
         btm.copyPixelsFromBuffer(native_buf);
-        postInvalidate();
-    }
-    
-    @SuppressWarnings("unused")
-    private void java_lcd_update_rect(int x, int y, int w, int h)
-    {
-        /* can't copy a partial buffer */
-        btm.copyPixelsFromBuffer(native_buf);
-        postInvalidate(x, y, x+w, y+h);
+        c.getClipBounds(rect);
+        c.drawBitmap(btm, rect, rect, null);
+        post_update_done();
     }
 
     @SuppressWarnings("unused")
@@ -152,6 +144,7 @@
         return view_config.getScaledTouchSlop();
     }
 
+    private native void post_update_done();
     private native void set_lcd_active(int active);
     private native void touchHandler(boolean down, int x, int y);
     private native boolean buttonHandler(int keycode, boolean state);
diff --git a/firmware/target/hosted/android/lcd-android.c b/firmware/target/hosted/android/lcd-android.c
index f4ef7b5..2bc6d04 100644
--- a/firmware/target/hosted/android/lcd-android.c
+++ b/firmware/target/hosted/android/lcd-android.c
@@ -23,6 +23,7 @@
 #include <jni.h>
 #include "config.h"
 #include "system.h"
+#include "kernel.h"
 #include "lcd.h"
 
 extern JNIEnv *env_ptr;
@@ -31,16 +32,18 @@
 
 static jclass RockboxFramebuffer_class;
 static jobject RockboxFramebuffer_instance;
-static jmethodID java_lcd_update;
-static jmethodID java_lcd_update_rect;
+static jmethodID postInvalidate1;
+static jmethodID postInvalidate2;
 
 static bool display_on;
 static int dpi;
 static int scroll_threshold;
+static struct wakeup lcd_wakeup;
 
 void lcd_init_device(void)
 {
     JNIEnv e = *env_ptr;
+    wakeup_init(&lcd_wakeup);
     RockboxFramebuffer_class = e->FindClass(env_ptr,
                                             "org/rockbox/RockboxFramebuffer");
     /* instantiate a RockboxFramebuffer instance
@@ -71,13 +74,13 @@
                                                buf);
 
     /* cache update functions */
-    java_lcd_update      = (*env_ptr)->GetMethodID(env_ptr,
+    postInvalidate1      = (*env_ptr)->GetMethodID(env_ptr,
                                                    RockboxFramebuffer_class,
-                                                   "java_lcd_update",
+                                                   "postInvalidate",
                                                    "()V");
-    java_lcd_update_rect = (*env_ptr)->GetMethodID(env_ptr,
+    postInvalidate2 = (*env_ptr)->GetMethodID(env_ptr,
                                                    RockboxFramebuffer_class,
-                                                   "java_lcd_update_rect",
+                                                   "postInvalidate",
                                                    "(IIII)V");
 
     jmethodID get_dpi    = e->GetMethodID(env_ptr,
@@ -96,22 +99,44 @@
     display_on = true;
 }
 
+/* the update mechanism is asynchronous since
+ * onDraw() must be called from the UI thread
+ * 
+ * The Rockbox thread calling lcd_update() has to wait
+ * for the update to complete, so that it's synchronous,
+ * and we need to notify it (we could wait in the java layer, but
+ * that'd block the other Rockbox threads too)
+ * 
+ * That should give more smoonth animations
+ */
 void lcd_update(void)
 {
     /* tell the system we're ready for drawing */
     if (display_on)
-        (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, java_lcd_update);
+    {
+        (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, postInvalidate1);
+        wakeup_wait(&lcd_wakeup, TIMEOUT_BLOCK);
+    }
 }
 
-void lcd_update_rect(int x, int y, int height, int width)
+void lcd_update_rect(int x, int y, int width, int height)
 {
     if (display_on)
     {
-        (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, java_lcd_update_rect,
-                                   x, y, height, width);
+        (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, postInvalidate2,
+                                  (jint)x, (jint)y, (jint)x+width, (jint)y+height);
+        wakeup_wait(&lcd_wakeup, TIMEOUT_BLOCK);
     }
 }
 
+JNIEXPORT void JNICALL
+Java_org_rockbox_RockboxFramebuffer_post_1update_1done(JNIEnv *e, jobject this)
+{
+    (void)e;
+    (void)this;
+    wakeup_signal(&lcd_wakeup);
+}
+
 bool lcd_active(void)
 {
     return display_on;