]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve workaround for partial texture updates on Android
authorPo Lu <luangruo@yahoo.com>
Thu, 13 Jul 2023 10:17:59 +0000 (18:17 +0800)
committerPo Lu <luangruo@yahoo.com>
Thu, 13 Jul 2023 10:17:59 +0000 (18:17 +0800)
* 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
java/org/gnu/emacs/EmacsDialog.java
java/org/gnu/emacs/EmacsNative.java
java/org/gnu/emacs/EmacsSurfaceView.java
src/android.c

index f2aede7369c535f4e1990fcda56cf5077a1189d4..e79fb4e46e7c5c12ce957c8d71e1c92d6df9dd6c 100644 (file)
@@ -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"
index 42455ed78f8fa956f6b5dda322fa4cebf5ab2a68..e4ed2271741d61d788f7230a576eeb1b70b3afae 100644 (file)
@@ -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;
   }
 
index 5b8b0f1eae51c85d5b0c34f91a93bb65c58d9204..1331539879a6ac1f161db1a679ca72bb98d8db1f 100644 (file)
@@ -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
index 54fe70e1634a882a0265f6326ec176a2cfde9ff5..c47696b35c07f17d70c90c1a6f2a187549a8f6ec 100644 (file)
@@ -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);
+      }
   }
 };
index 5eb6f65419c6f7ef88892966c5ce7a2bb3346eb6..f8ad78a2b39157445439147b915d335f7c8f25eb 100644 (file)
@@ -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);