1

I'm struggling to find the best way to draw smoothly as possible some pre-calculated trajectories of physical objects on a custom SurfaceView. So the scenario is like this:

  • I have a 100k entries SQLite database that stores x,y positions of about 10 objects.
  • I have to animate my objects using the data provided by the database while drawing trajectories.
  • I have to be able to interact with the SurfaceView zomming and panning appropriately through touch events.

What strategy is the best?

  • (a) Pre loading data and store them in 200k entries float arrays.

    • (a1) Then, at every draw(Canvas canvas) call extract the segment of the array with data from the initial time to the actual time, construct a Path with these points and draw it.
    • (a2) Same as (a1) but instead of using Path, call the Canvas.drawLines() function. That function require a float array with repetition of points so that the end point of a line will be the start point of the next line. This function may be faster than drawPath() but the array must be constructed at every iteration...
    • (a3) Otherwise, save drawings in a Bitmap so that at every iteration I have to draw only the entire Bitmap + the last segment of the trajectories. Only when the Matrix change redraw all stuff with the new Matrix on the Bitmap. This slow down only when touch events happens to change the Matrix requesting the entire redrawing.
  • (b) Live read data from database with a concurrent Thread. Then, save in a float array only the positions read. This may be a little more heavy because of the additional thread and live querying but I have to handle only smaller arrays of float (only the part I'm interested in and not all hystory).

    • (b1) Redraw all at every draw(Canvas canvas) call as in (a1) using Path
    • (b2) Redraw all at every draw(Canvas canvas) call as in (a2) using drawLines()
    • (b3) Save in a Bitmap to optimize drawings as in (a3)

Options (a) are better because I can smoothly draw lines using all points in database while with options (b) if the process slow down the live updating system can skip some points making the drawing less smooth. Options (b) are better bacause of the simplicity and readability of code.

I only tested options (b) but I'm wondering if I have to give a try to options (a). Keep in mind that with the trajectories growing up the draw process becomes very slow so optimization is necessary. The Bitmap strategy can solve partially the problem with long trails but I can't have a fluid experience when touch events happens...

fadden
  • 51,356
  • 5
  • 116
  • 166
bluePhlavio
  • 537
  • 5
  • 18
  • You have two interrelated problems: (1) determining what to draw; (2) doing the actual drawing. Drawing just the new stuff to a persistent off-screen Bitmap will make the drawing much faster (as would using a custom View instead of a SurfaceView Surface -- hardware acceleration). Pre-loading the data will reduce latency, if you can spare the memory for it. Using a separate thread to read from the DB is only helpful if you need to animate while loading data. You said you want the "best way", but you haven't told us what you're optimizing for -- render speed, latency, mem use, ...? – fadden May 03 '16 at 00:12
  • @fadden Ok for me the "best way" is the way that minimizes the time of a single iteration of updating, calculating and drawing. I'm using a Custom SurfaceView, why would be better to not use it? I read from the DB because I'm live updating positions while animating. I don't know if reading from a 200k array is faster than querying a DB? – bluePhlavio May 03 '16 at 06:27
  • 1
    Canvas rendering to a Surface is not hardware-accelerated, so [custom Views](http://developer.android.com/training/custom-views/index.html) can be faster when drawing lots. I'm not sure what you're doing with the database and the arrays so you may just have to try it and time it. [systrace](http://developer.android.com/tools/debugging/systrace.html) with custom tags can be very helpful, especially when you have more than one thread working and want to see the interactions. – fadden May 03 '16 at 15:46
  • @fadden Thanks. You mean that even if I call Canvas.drawLines() that is theoretically hardware-accelerated on a Custom SurfaceView the GPU is not used? I have to use a simple Custom View to take advantage of it? It seems a little strange to me because I thought that SurfaceView was optimized for drawing stuff... – bluePhlavio May 03 '16 at 15:53
  • 1
    Canvas rendering on a Surface isn't hardware-accelerated; see e.g. [this section](https://source.android.com/devices/graphics/architecture.html#canvas) in the graphics arch doc. Surfaces allow you to render off of the main thread, independent of the View UI, but if you want fast rendering on a modern device you need to use OpenGL ES. That means using a custom View with hardware acceleration, drawing directly with GLES, or using a 3rd-party graphics library that does the GLES stuff for you. – fadden May 03 '16 at 16:00
  • @fadden What if I override the draw method of a simple Custom View and call draw from a separate thread? Can I access hardware-acceleration without OpenGL ES? – bluePhlavio May 03 '16 at 16:07
  • 1
    For a custom View, draw calls must be made from the main UI thread. The rendering happens as part of the invalidate/refresh cycle. If you want to distribute your processing across threads, the typical approach would be to do all of your computation on a worker thread, generate a draw list, and then `postInvalidate()`. With hardware acceleration the actual rendering should happen quickly. If you maintain two draw lists, you can draw (or re-draw) from list A while you're generating list B, allowing the computation to overlap. – fadden May 03 '16 at 17:28

0 Answers0