3

Setup: I have implemented a native (read JNI) mechanism to copy pixels from a Bitmap object, to native memory. This is done by malloc() uint23_t array in native memory and later using memcpy() to copy pixels to/from Bitmap's native pointer. This works well and have been tested. Pixels are successfully saved in native memory from a Bitmap object, and copied back to a Bitmap object, and visible on screen. Its pretty fast in copying, up to order of several milliseconds for fairly large bitmaps. But extremely slow in rendering it.

Intention: The above was done to break free of heap limit on default android Bitmaps (refer to https://stackoverflow.com/a/1949205/1531054). There would be only 1 Java Bitmap object acting as buffer between native memory and target canvas.

Save a shape:

  1. clear Buffer Bitmap.
  2. Draw shape on Bitmap.
  3. Copy pixels to native memory, and save the memory pointer.
  4. Clear Buffer Bitmap.

So, any number of shapes can be saved to native memory, without running into heap size limits. This works.

Later when need to draw a shape (say in onDraw()):

  1. clear Buffer Bitmap.
  2. Copy pixels from native memory, to Buffer Bitmap, using the saved memory pointer.
  3. Draw Buffer Bitmap on canvas.
  4. Clear Buffer Bitmap.
  5. Repeat again for next shape.

Problem When quickly drawing many shapes from memory, The Buffer Bitmap sorts of lags. Basically we're doing

clear bitmap -> load pixels from memory onto it -> draw it on view canvas

in Quick succession inside onDraw(), only the latest shape's pixels are drawn onto canvas. It appears as if:

  1. The internal canvas.drawBitmap() is asynchronous and copies pixels off the bitmap later sometimes.
  2. Android's Bitmaps have some hidden caching mechanism.

Has anyone run into such trouble before ? Or has some insight regarding this ?

I know one can get native skia lib's canvas instance in JNI and draw on it, but this is a non standard way.

Community
  • 1
  • 1
S.D.
  • 29,290
  • 3
  • 79
  • 130

2 Answers2

2

In recent Android versions (3.0 and on, which is the majority of devices), pixels use regular Java memory heap. With the introduction of hardware acceleration, bitmaps are drawn asynchronously, and there is a caching system that manages bitmaps loaded as textures to the GPU. Therefore the hack you are trying to do will probably degrade performance on new devices. If you need more memory, try to use largeHeap="true" in your manifest.

yoah
  • 7,180
  • 2
  • 30
  • 30
  • I do agree. Also `up to order of several milliseconds for fairly large bitmaps` in question is actually not fast at all, taking into account that you have only 16ms to keep up with 60fps. – Dmitry Zaytsev Oct 09 '14 at 15:54
  • @DmitryZaitsev `memcpy` is around 3ms-8ms for 1000x1000 px of data to native and back. The bottleneck is `Bitmap` on Java side. Its too slow to copy pixels on java side. – S.D. Oct 09 '14 at 19:20
  • @yoah You are correct. It boils down to `canvas.drawBitmap(int[],..)` versus `canvas.drawBitmap(bitmap,..)` . The first isn't cached but is very slow, the later is very fast but cached hence useless for my intentions. I see GL textures being created/destroyed every time. Though I don't get the rationale of why android guys went with this kind of implementation. Anyways, thanks for your answer :). – S.D. Oct 10 '14 at 04:59
1

On relatively new Androids (from 3.0 if I recall correctly) with hardware acceleration canvas.drawBitmap method does not actually draws anything (as well as dispatchDraw, draw, and onDraw). Instead, it creates record in display list which:

  1. Might be cached for an indefinite amount of time.
  2. Might (and will be) drawn in the future, not right away. It is not exactly asynchronous as for now, it just executed later in the same thread.

Those two points, I think are the answer to your question.

Alternatively, you can disable hardware acceleration for your view/window and see if your approach is working.

For further reading:

http://android-developers.blogspot.de/2011/03/android-30-hardware-acceleration.html

http://developer.android.com/guide/topics/graphics/hardware-accel.html#model

Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146
  • I think you are right about texture caching and display list. When I use an `int[]` (pixels) instead of a `Bitmap` as a buffer, all shapes are drawn correctly. – S.D. Oct 09 '14 at 19:16