[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);