aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
authorPo Lu2023-07-13 18:17:59 +0800
committerPo Lu2023-07-13 18:17:59 +0800
commitae9f1a075c3a5f5bd0425828b6144f97265d8794 (patch)
tree4d22cbafc2915f5bd6fe9f2279a5316240337998 /java
parent140755f2cfe6a39f643ab0a9ca2d81b0ed470ae7 (diff)
downloademacs-ae9f1a075c3a5f5bd0425828b6144f97265d8794.tar.gz
emacs-ae9f1a075c3a5f5bd0425828b6144f97265d8794.zip
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.
Diffstat (limited to 'java')
-rw-r--r--java/AndroidManifest.xml.in5
-rw-r--r--java/org/gnu/emacs/EmacsDialog.java11
-rw-r--r--java/org/gnu/emacs/EmacsNative.java5
-rw-r--r--java/org/gnu/emacs/EmacsSurfaceView.java61
4 files changed, 46 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/>. -->
77 @ANDROID_SHARED_USER_ID@ 77 @ANDROID_SHARED_USER_ID@
78 android:extractNativeLibs="true"> 78 android:extractNativeLibs="true">
79 79
80 <!-- See EmacsSurfaceView.onDraw for why hardware acceleration is
81 disabled. -->
82
83 <activity android:name="org.gnu.emacs.EmacsActivity" 80 <activity android:name="org.gnu.emacs.EmacsActivity"
84 android:launchMode="singleInstance" 81 android:launchMode="singleInstance"
85 android:windowSoftInputMode="adjustResize" 82 android:windowSoftInputMode="adjustResize"
86 android:exported="true" 83 android:exported="true"
87 android:hardwareAccelerated="false"
88 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"> 84 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
89 <intent-filter> 85 <intent-filter>
90 <action android:name="android.intent.action.MAIN" /> 86 <action android:name="android.intent.action.MAIN" />
@@ -177,7 +173,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. -->
177 <activity android:name="org.gnu.emacs.EmacsMultitaskActivity" 173 <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
178 android:windowSoftInputMode="adjustResize" 174 android:windowSoftInputMode="adjustResize"
179 android:exported="true" 175 android:exported="true"
180 android:hardwareAccelerated="false"
181 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/> 176 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
182 177
183 <activity android:autoRemoveFromRecents="true" 178 <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
274 } 274 }
275 } 275 }
276 276
277 /* Make sure the dialog is hardware accelerated. Hardware
278 acceleration is disabled for dialogs by default, because they
279 aren't enabled in EmacsActivity either. */
280
281 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
282 {
283 flag = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
284 window = dialog.getWindow ();
285 window.addFlags (flag);
286 }
287
288 return dialog; 277 return dialog;
289 } 278 }
290 279
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
249 public static native void blitRect (Bitmap src, Bitmap dest, int x1, 249 public static native void blitRect (Bitmap src, Bitmap dest, int x1,
250 int y1, int x2, int y2); 250 int y1, int x2, int y2);
251 251
252 /* Increment the generation ID of the specified BITMAP, forcing its
253 texture to be re-uploaded to the GPU. */
254
255 public static native void notifyPixelsChanged (Bitmap bitmap);
256
252 static 257 static
253 { 258 {
254 /* Older versions of Android cannot link correctly with shared 259 /* 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
42 /* The complete buffer contents at the time of the last draw. */ 42 /* The complete buffer contents at the time of the last draw. */
43 private Bitmap frontBuffer; 43 private Bitmap frontBuffer;
44 44
45 /* Whether frontBuffer has been updated since the last call to
46 `onDraw'. */
47 private boolean bitmapChanged;
48
45 /* Canvas representing the front buffer. */ 49 /* Canvas representing the front buffer. */
46 private Canvas bitmapCanvas; 50 private Canvas bitmapCanvas;
47 51
@@ -105,6 +109,9 @@ public final class EmacsSurfaceView extends View
105 bitmap.getWidth (), 109 bitmap.getWidth (),
106 bitmap.getHeight ()); 110 bitmap.getHeight ());
107 } 111 }
112
113 /* See the large comment inside `onDraw'. */
114 bitmapChanged = true;
108 } 115 }
109 116
110 private void 117 private void
@@ -176,27 +183,41 @@ public final class EmacsSurfaceView extends View
176 onDraw (Canvas canvas) 183 onDraw (Canvas canvas)
177 { 184 {
178 /* Paint the view's bitmap; the bitmap might be recycled right 185 /* Paint the view's bitmap; the bitmap might be recycled right
179 now. 186 now. */
180
181 Hardware acceleration is disabled in AndroidManifest.xml to
182 prevent Android from uploading the front buffer to the GPU from
183 a separate thread. This is important for two reasons: first,
184 the GPU command queue uses a massive amount of memory (dozens
185 of MiB) to upload bitmaps to the GPU, regardless of how much of
186 the bitmap has actually changed.
187
188 Secondly, asynchronous texturization leads to race conditions
189 when a buffer swap occurs before the front buffer is fully
190 uploaded to the GPU. Normally, only slight and tolerable
191 tearing should result from this behavior, but Android does not
192 properly interlock the ``generation ID'' used to avoid
193 texturizing unchanged bitmaps with the bitmap contents,
194 consequentially leading to textures in an incomplete state
195 remaining in use to the GPU if a buffer swap happens between
196 the image data being uploaded and the ``generation ID'' being
197 read. */
198 187
199 if (frontBuffer != null) 188 if (frontBuffer != null)
200 canvas.drawBitmap (frontBuffer, 0f, 0f, uiThreadPaint); 189 {
190 /* The first time the bitmap is drawn after a buffer swap,
191 mark its contents as having changed. This increments the
192 ``generation ID'' used by Android to avoid uploading buffer
193 textures for unchanged bitmaps.
194
195 When a buffer swap takes place, the bitmap is initially
196 updated from the Emacs thread, resulting in the generation
197 ID being increased. If the render thread is texturizing
198 the bitmap while the swap takes place, it might record the
199 generation ID after the update for a texture containing the
200 contents of the bitmap prior to the swap, leaving the
201 texture tied to the bitmap partially updated.
202
203 Android never calls `onDraw' if the render thread is still
204 processing the bitmap. Update the generation ID here to
205 ensure that a new texture will be uploaded if the bitmap
206 has changed.
207
208 Uploading the bitmap contents to the GPU uses an excessive
209 amount of memory, as the entire bitmap is placed into the
210 graphics command queue, but this memory is actually shared
211 among all other applications and reclaimed by the system
212 when necessary. */
213
214 if (bitmapChanged)
215 {
216 EmacsNative.notifyPixelsChanged (frontBuffer);
217 bitmapChanged = false;
218 }
219
220 canvas.drawBitmap (frontBuffer, 0f, 0f, uiThreadPaint);
221 }
201 } 222 }
202}; 223};