emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/android ae9f1a075c3 1/2: Improve workaround for partial texture


From: Po Lu
Subject: feature/android ae9f1a075c3 1/2: Improve workaround for partial texture updates on Android
Date: Thu, 13 Jul 2023 07:22:10 -0400 (EDT)

branch: feature/android
commit ae9f1a075c3a5f5bd0425828b6144f97265d8794
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Improve workaround for partial texture updates on Android
    
    * java/AndroidManifest.xml.in:
    * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Don't
    change hardware acceleration state.
    * java/org/gnu/emacs/EmacsNative.java (notifyPixelsChanged): New
    function.
    * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
    New field `bitmapChanged'.
    (copyToFrontBuffer): Signal that the bitmap has changed.
    (onDraw): If the bitmap has changed, increment the generation
    ID.
    * src/android.c (JNICALL): Implement new function.
---
 java/AndroidManifest.xml.in              |  5 ---
 java/org/gnu/emacs/EmacsDialog.java      | 11 ------
 java/org/gnu/emacs/EmacsNative.java      |  5 +++
 java/org/gnu/emacs/EmacsSurfaceView.java | 61 +++++++++++++++++++++-----------
 src/android.c                            | 17 +++++++++
 5 files changed, 63 insertions(+), 36 deletions(-)

diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in
index f2aede7369c..e79fb4e46e7 100644
--- a/java/AndroidManifest.xml.in
+++ b/java/AndroidManifest.xml.in
@@ -77,14 +77,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>. -->
               @ANDROID_SHARED_USER_ID@
               android:extractNativeLibs="true">
 
-    <!-- See EmacsSurfaceView.onDraw for why hardware acceleration is
-         disabled.  -->
-
     <activity android:name="org.gnu.emacs.EmacsActivity"
              android:launchMode="singleInstance"
              android:windowSoftInputMode="adjustResize"
              android:exported="true"
-             android:hardwareAccelerated="false"
              
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
       <intent-filter>
         <action android:name="android.intent.action.MAIN" />
@@ -177,7 +173,6 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>. -->
     <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
              android:windowSoftInputMode="adjustResize"
              android:exported="true"
-             android:hardwareAccelerated="false"
              
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
 
     <activity android:autoRemoveFromRecents="true"
diff --git a/java/org/gnu/emacs/EmacsDialog.java 
b/java/org/gnu/emacs/EmacsDialog.java
index 42455ed78f8..e4ed2271741 100644
--- a/java/org/gnu/emacs/EmacsDialog.java
+++ b/java/org/gnu/emacs/EmacsDialog.java
@@ -274,17 +274,6 @@ public final class EmacsDialog implements 
DialogInterface.OnDismissListener
          }
       }
 
-    /* Make sure the dialog is hardware accelerated.  Hardware
-       acceleration is disabled for dialogs by default, because they
-       aren't enabled in EmacsActivity either.  */
-
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
-      {
-       flag = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-       window = dialog.getWindow ();
-       window.addFlags (flag);
-      }
-
     return dialog;
   }
 
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index 5b8b0f1eae5..1331539879a 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -249,6 +249,11 @@ public final class EmacsNative
   public static native void blitRect (Bitmap src, Bitmap dest, int x1,
                                      int y1, int x2, int y2);
 
+  /* Increment the generation ID of the specified BITMAP, forcing its
+     texture to be re-uploaded to the GPU.  */
+
+  public static native void notifyPixelsChanged (Bitmap bitmap);
+
   static
   {
     /* Older versions of Android cannot link correctly with shared
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java 
b/java/org/gnu/emacs/EmacsSurfaceView.java
index 54fe70e1634..c47696b35c0 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -42,6 +42,10 @@ public final class EmacsSurfaceView extends View
   /* The complete buffer contents at the time of the last draw.  */
   private Bitmap frontBuffer;
 
+  /* Whether frontBuffer has been updated since the last call to
+     `onDraw'.  */
+  private boolean bitmapChanged;
+
   /* Canvas representing the front buffer.  */
   private Canvas bitmapCanvas;
 
@@ -105,6 +109,9 @@ public final class EmacsSurfaceView extends View
                                bitmap.getWidth (),
                                bitmap.getHeight ());
       }
+
+    /* See the large comment inside `onDraw'.  */
+    bitmapChanged = true;
   }
 
   private void
@@ -176,27 +183,41 @@ public final class EmacsSurfaceView extends View
   onDraw (Canvas canvas)
   {
     /* Paint the view's bitmap; the bitmap might be recycled right
-       now.
-
-       Hardware acceleration is disabled in AndroidManifest.xml to
-       prevent Android from uploading the front buffer to the GPU from
-       a separate thread.  This is important for two reasons: first,
-       the GPU command queue uses a massive amount of memory (dozens
-       of MiB) to upload bitmaps to the GPU, regardless of how much of
-       the bitmap has actually changed.
-
-       Secondly, asynchronous texturization leads to race conditions
-       when a buffer swap occurs before the front buffer is fully
-       uploaded to the GPU.  Normally, only slight and tolerable
-       tearing should result from this behavior, but Android does not
-       properly interlock the ``generation ID'' used to avoid
-       texturizing unchanged bitmaps with the bitmap contents,
-       consequentially leading to textures in an incomplete state
-       remaining in use to the GPU if a buffer swap happens between
-       the image data being uploaded and the ``generation ID'' being
-       read.  */
+       now.  */
 
     if (frontBuffer != null)
-      canvas.drawBitmap (frontBuffer, 0f, 0f, uiThreadPaint);
+      {
+       /* The first time the bitmap is drawn after a buffer swap,
+          mark its contents as having changed.  This increments the
+          ``generation ID'' used by Android to avoid uploading buffer
+          textures for unchanged bitmaps.
+
+          When a buffer swap takes place, the bitmap is initially
+          updated from the Emacs thread, resulting in the generation
+          ID being increased.  If the render thread is texturizing
+          the bitmap while the swap takes place, it might record the
+          generation ID after the update for a texture containing the
+          contents of the bitmap prior to the swap, leaving the
+          texture tied to the bitmap partially updated.
+
+          Android never calls `onDraw' if the render thread is still
+          processing the bitmap.  Update the generation ID here to
+          ensure that a new texture will be uploaded if the bitmap
+          has changed.
+
+          Uploading the bitmap contents to the GPU uses an excessive
+          amount of memory, as the entire bitmap is placed into the
+          graphics command queue, but this memory is actually shared
+          among all other applications and reclaimed by the system
+          when necessary.  */
+
+       if (bitmapChanged)
+         {
+           EmacsNative.notifyPixelsChanged (frontBuffer);
+           bitmapChanged = false;
+         }
+
+       canvas.drawBitmap (frontBuffer, 0f, 0f, uiThreadPaint);
+      }
   }
 };
diff --git a/src/android.c b/src/android.c
index 5eb6f65419c..f8ad78a2b39 100644
--- a/src/android.c
+++ b/src/android.c
@@ -3133,6 +3133,23 @@ NATIVE_NAME (blitRect) (JNIEnv *env, jobject object,
   AndroidBitmap_unlockPixels (env, src);
 }
 
+JNIEXPORT void JNICALL
+NATIVE_NAME (notifyPixelsChanged) (JNIEnv *env, jobject object,
+                                  jobject bitmap)
+{
+  void *data;
+
+  /* Lock and unlock the bitmap.  This calls
+     SkBitmap->notifyPixelsChanged.  */
+
+  if (AndroidBitmap_lockPixels (env, bitmap, &data) < 0)
+    /* The return value is less than 0 if an error occurs.
+       Good luck finding this in the documentation.  */
+    return;
+
+  AndroidBitmap_unlockPixels (env, bitmap);
+}
+
 /* Forward declarations of deadlock prevention functions.  */
 
 static void android_begin_query (void);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]