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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user