@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" />
<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"
/* 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;
bitmap.getWidth (),
bitmap.getHeight ());
}
+
+ /* See the large comment inside `onDraw'. */
+ bitmapChanged = true;
}
private void
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);
+ }
}
};
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);