I am attempting to make a drawing/painting app using TextureView
on Android. I want to support a drawing surface of up to 4096x4096 pixels, which seems reasonable for my minimum target device (which I use for testing) which is a Google Nexus 7 2013 which has a nice quad core CPU and 2GB memory.
One of my requirements is that my view must be inside of a view that allows it to be zoomed in and out and panned, which is all custom code I have written (think UIScrollView from iOS).
I've tried using a regular View (not TextureView
) with OnDraw and performance was absolutely horrible - less than 1 frame a second. This happened even if I called Invalidate(rect)
with only the rect that changed. I tried turning off hardware acceleration for the view, but nothing rendered, I assume because 4096x4096 is too big for software.
I then tried using TextureView
and performance is a little better - about 5-10 frames per second (still terrible but better). The user draws into a bitmap, which is then later drawn into the texture using a background thread. I'm using Xamarin but hopefully the code makes sense to Java people.
private void RunUpdateThread()
{
try
{
TimeSpan sleep = TimeSpan.FromSeconds(1.0f / 60.0f);
while (true)
{
lock (dirtyRect)
{
if (dirtyRect.Width() > 0 && dirtyRect.Height() > 0)
{
Canvas c = LockCanvas(dirtyRect);
if (c != null)
{
c.DrawBitmap(bitmap, dirtyRect, dirtyRect, bufferPaint);
dirtyRect.Set(0, 0, 0, 0);
UnlockCanvasAndPost(c);
}
}
}
Thread.Sleep(sleep);
}
}
catch
{
}
}
If I change lockCanvas to pass null instead of a rect, performance is great at 60 fps, but the contents of the TextureView
flicker and get corrupted, which is disappointing. I would have thought it would simply be using an OpenGL frame buffer / render texture underneath or at least have an option to preserve contents.
Are there any other options short of doing everything in raw OpenGL in Android for high performance drawing and painting on a surface that is preserved in between draw calls?
UPDATE I ditched TextureView and now use an OpenGL view where I call glTexSubImage2D to update changed pieces of a render texture.
OLD ANSWER I ended up tiling TextureView in a 4x4 grid. Depending on the dirty rect each frame, I refresh the appropriate TextureView views. Any view that is not updated that frame I call Invalidate on.
Some devices, such as the Moto G phone have an issue where the double buffering is corrupted for one frame. You can fix that by calling lockCanvas twice when the parent view has it's onLayout called.