diff options
| author | Po Lu | 2023-07-13 18:17:59 +0800 |
|---|---|---|
| committer | Po Lu | 2023-07-13 18:17:59 +0800 |
| commit | ae9f1a075c3a5f5bd0425828b6144f97265d8794 (patch) | |
| tree | 4d22cbafc2915f5bd6fe9f2279a5316240337998 /java | |
| parent | 140755f2cfe6a39f643ab0a9ca2d81b0ed470ae7 (diff) | |
| download | emacs-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.in | 5 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsDialog.java | 11 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsNative.java | 5 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsSurfaceView.java | 61 |
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 | }; |