Ive been trying to make a scrollable/zoomable app and everything has gone great except for drawing bitmaps. It is a very large image (6656 by 4096) that i have split into tiles. There is a rectangle array that the bitmaps are drawn to, and it detects what rectangle is in the top left corner so it can draw the bitmaps that will cover the user's viewable screen. My problem is this all lags when the app has to load the bitmaps into memory; Once they are loaded it isnt an issue. I started with 512 by 512 tiles, then went down to 128 by 128. although it helped, there still is some noticeable lag. I have been looking into surfaceView and wanted your opinions if i should stick with View, or use surfaceView to solve my lag.
If you derive your own SurfaceView you have several advantages.
Mainly because you can have all drawing logic in a seperate thread. This means that the ui won't wait for you (I'm assuming the lag is because the ui-thread is being blocked?).
SurfaceView's are also faster in nature.
I also find this overview on developer.android.com to be a good reference to choose drawing method.
Related
The Situation
I started developing for Android, and found that Android's way of handling layouts, animations etc. is not adequate for smooth touch feedback and real-time animations, especially before Android 4.0. So instead, I decided to use the game app approach: use a SurfaceView and define my own drawing code.
The Problem
After a few tests, I discovered that this method required too much CPU for a non-game app, which I believe is due to redrawing static elements 60 times per second.
The First Solution & Flaws
To solve this issue, I modified my code so that the app would redraw the screen (call postInvalidate) only if there were any changes to what should be drawn. This solution solved part of the issue, but the app still had to redraw static elements even if a small button moved a single pixel.
The Question: Possible Better Solution?
For a better solution, I considered how Android dealt with the problem; it had separate View's for every screen element. So I though, maybe I could have one SurfaceView for large, static, content elements and another for small, moving UI elements and achieve a similar effect. My question is, would this actually improve performance the way I described it above?
Thanks.
If you're using postInvalidate(), you should be using a custom View, not a SurfaceView. The whole point of using a SurfaceView is to have a separate layer that is independent of the View UI. If you're overriding onDraw(), you're drawing on the View part, not the Surface part, and just wasting the Surface.
All Views occupy a single layer, no matter how many you have. Each SurfaceView has a separate layer, so having a lot of them will become problematic. In practice you can have no more than three, because of Z-ordering limitations. (See the "multi-surface test" activity in Grafika for an example of three partially transparent SurfaceViews blended with the View UI.)
If you can't render fast enough to maintain 60 fps, you need to consider changing the way you render. Custom Views and OpenGL ES take advantage of hardware acceleration. Canvas rendering onto a SurfaceView Surface does not. On the plus side, you can down-size a SurfaceView's Surface and let the hardware scale it back up; this lets you limit the number of pixels you have to draw each frame, regardless of the display's resolution. (Blog, demo.) If you have a lot of static elements, the best approach may be to render to an off-screen Bitmap, and then just blit the Bitmap every frame.
One approach that will be very fast is to render all of the static elements onto the View part of the SurfaceView, taking care to keep the background transparent, and then render the animated parts on the Surface with GLES. You could use a second SurfaceView, but that adds an additional composition layer, which will degrade system performance if you exceed the number of overlay planes supported by the hardware.
For a deeper understanding of the way Android graphics work, take a look at the graphics architecture doc.
Flash Pro CC, AS3, Air for Android (v17), rendering mode GPU, stage quality.LOW, FPS: 60, testing device: an old Nexus One smartphone (Android 2.3.3).
The guides say that GPU makes rendering the bitmaps cheap, somehow i can't grasp how exactly it works.
So i have 49 separate bitmap squares covering the stage and one MovieClip in the middle with a bitmap inside tweened to move up and down (jumping ball). Pretty simple, right?
This is the view: http://i.stack.imgur.com/EKcJ6.png
All graphics are bitmaps (not vectors). Yet i get 55 fps (it varies arround 53-57).
Then i select all 49 squares and put them inside a symbol (MovieClip). Visually nothing changes. It seems to increase the FPS a tiny bit, the average fps is now ~57 (55-59).
Then i take the MovieClip (with all the squares inside it) and set cacheAsBitmap=true. Voila, now i have 60 fps!
What is happening in all 3 different cases? Why i need to put bitmaps into one MC and cache this MC as bitmap - aren't the squares already bitmaps?
I have also tried to make each square a MovieClip and cache it as bitmap, but i still got 55 fps.
Is it possible to keep squares separate at 60 fps?
In my real project i have many MovieClips on the stage (~100) but in most cases only one of them is animated at a time. Yet somehow it seems that the mere presence of other movieclips reduce the performance (fps). Obviously, i cannot put them all into one MC and cache it as bitmap as in the simplified example above.
How can i solve this, what should i do?
Thanks!
I think it relates to this best practice recommendation:
Limit the numbers of items visible on stage. Each item takes some time
to render and composite with the other items around it. When you no
longer want to display a display object, set its visible property to
false. Do not simply move it off stage, hide it behind another object,
or set its alpha property to 0. If the display object is no longer
needed at all, remove it from the stage with removeChild().
By putting all the bitmaps in a single container and setting cacheAsBitmap=true you are essentially turning them into a single bitmap as far as the compositor is concerned. This tends to be faster to composite than multiple individual bitmaps. Setting a bitmap to cacheAsBitmap=true (or in a single container with cacheAsBitmap=true) has no effect because it's already a bitmap.
Also note that GPU mode isn't recommended anymore, it was Adobe's first attempt at GPU accelerating the display and they basically gave up on that path in favor of the new Stage3D rendering pipeline. While GPU render mode can work really well when used just right, it can be somewhat unpredictable and confusing, so I would highly recommend you check out Stage3D.
Hope that helps.
I'm developing an Android game using Canvas element. I have many graphic elements (sprites) drawn on a large game map. These elements are drawn by standard graphics functions like drawLine, drawPath, drawArc etc.
It's not hard to test if they are in screen or not. So, if they are out of the screen, i may skip their drawing routines completely. But even this has a CPU cost. I wonder if Android Graphics Library can do this faster than I can?
In short, should I try to draw everything even if they are completely out of the screen coordinates believing Android Graphics Library would take care of them and not spend much CPU trying to draw them or should I check their drawing area rectangle myself and if they are completely out of screen, skip the drawing routines? Which is the proper way? Which one is supposed to be faster?
p.s: I'm targeting Android v2.1 and above.
From a not-entirely-scientific test I did drawing Bitmaps tiled across a greater area than the screen, I found that checking beforehand if the Bitmap was onscreen doesn't seem to make a considerable different.
In one test I set a Rect to the screen size and set another Rect to the position of the Bitmap and checked Rect.intersects() before drawing. In the other test I just drew the Bitmap. After 300-ish draws there wasn't a visible trend - some went one way, others went another. I tried the 300-draw test every frame, and the variation from frame to frame was much greater than difference between checked and unchecked drawing.
From that I think it's safe to say Android checks bounds in its native code, or you'd expect a considerable difference. I'd share the code of my test, but I think it makes sense for you to do your own test in the context of your situation. It's possible points behave differently than Bitmaps, or some other feature of your paint or canvas changes things.
Hope that help you (or another to stumble across this thread as I did with the same question).
I am creating an Android App that produces random images based on complex mathematical expressions. The color of a pixel depends on its location and the expression chosen. (I have seen many iPhone apps that produce "random art" but relatively few Android apps.)
It takes 5 to 15 seconds for the image to be drawn on a Nexus S dev phone.
To keep the UI thread responsive this seems like the purpose of the SurfaceView class. Almost all the examples of SurfaceView deal with animation, not a single complex image. Once the image is done being drawn / rendered I won't change it until the user
So, is SurfaceView the right component to use? If so, can I get a callback from the SurfaceView or its internal thread when it is done drawing when it is done rendering the image? The callback is so I know it is okay to switch the low resolution and blocky version of the image art with the high resolution one?
Is there an alternative to SurfaceView that is better for complex rendering of single images. (Not animation.)
Cheers!
If all you want to do is render a single complex image on another thread to keep the UI responsive, then after it's done rendering actually draw it, you might consider just doing this in the standard keep-work-off-the-UI-thread way by using something like an AsyncTask. It's not like you're doing compositing or anything that really is GPU-specific (unless as others have suggested you can offload the actual rendering calculations to the GPU).
I would at least experiment with simply building an array representing your pixels in an AsyncTask then when you're done, create a bitmap with it using setPixels and set the source of an ImageView to that bitmap.
If on the other hand you want your image to appear pixel by pixel, then maybe SurfaceView might be a reasonable choice, in which case it'll basically be an animation so you can follow other tutorials. There's some other setup, but the main thing would be to override onDraw and then you'll probably have to use Canvas.drawPoint to draw each pixel.
I'm tried to determine the "best" way to scroll a background comprised of tiled Bitmaps on an Android SurfaceView. I've actually been successful in doing so, but wanted to determine if there is a more efficient technique, or if my technique might not work on all Android phones.
Basically, I create a new, mutable Bitmap to be slightly larger than the dimensions of my SurfaceView. Specifically, my Bitmap accomodates an extra line of tiles on the top, bottom, left, and right. I create a canvas around my new bitmap, and draw my bitmap tiles to it. Then, I can scroll up to a tile in any direction simply by drawing a "Surfaceview-sized" subset of my background Bitmap to the SurfaceHolder's canvas.
My questions are:
Is there a better bit blit technique than drawing a background bitmap to the canvas of my SurfaceHolder?
What is the best course of action when I scroll to the edge of my background bitmap, and wish to shift the map one tile length?
As I see it, my options are to:
a. Redraw all the tiles in my background individually, shifted a tile length in one direction. (This strikes me as being inefficient, as it would entail many small Bitmap draws).
b. Simply make the background bitmap so large that it will encompass the entire scrolling world. (This could require an extremely large bitmap, yet it would only need to be created once.)
c. Copy the background bitmap, draw it onto itself but shifted a tile length in the direction we are scrolling, and draw the newly revealed row or column of tiles with a few individual bitmap draws. (Here I am making the assumption that one large bitmap draw is more efficient than multiple small ones covering the same expanse.)
Thank you for reading all this, and I would be most grateful for any advice.
I originally used a similar technique to you in my 'Box Fox' platformer game and RTS, but found it caused quite noticeable delays if you scroll enough that the bitmap needs to be redrawn.
My current method these games is similar to your Option C. I draw my tiled map layers onto a grid of big bitmaps (about 7x7) taking up an area larger than the screen. When the user scrolls onto the edge of this grid, I shift all the bitmaps in the grid over (moving the end bitmaps to the front), change the offset of grid, and then just redraw the new edge.
I'm not quite sure which is faster with software rendering (your Option C or my current method). I think my method maybe faster if you ever change to OpenGL rendering as you wouldn't have to upload as much texture data to the graphics card as the user scrolls.
I wouldn't recommend Option A because, as you suggest, the hundreds small bitmap draws for a tiled map kills performance, and it gets pretty bad with larger screens. Option B may not even be possible with many devices, as it's quite easy to get a 'bitmap size exceeds VM budget' error as the heap space limit is set quite low on many phones.
Also if you don't need transparency on your map/background try to use RGB_565 bitmaps, as it's quite a lot faster to draw in software, and uses up less memory.
By the way, I get capped at 60fps on both my phone and 10" tablet in my RTS with the method above, rendered in software, and can scroll across the map smoothly. So you can definitely get some decent speed out of the android software renderer. I have a 2D OpenGL wrapper built for my game but haven't yet needed to switch to it.
My solution in a mapping app relies on a 2 level cache, first tile objects are created with a bitmap and a position, these are either stored on disk or in a Vector (synching is important for me, multithreaded HTTP comms all over the place).
When I need to draw the background I detect the visible area and get a list of all the tiles I need (this is heavily optimised as it gets called so often) then either pull the tiles from memory or load from disk. I get very reasonable performance even on slightly older phones and nice smooth scrolling with no hiccups.
As a caveat, I allow tiles not to be ready and swap them with a loading image, I don't know if this would work for you, but if you have all the tiles loaded in the APK you should be fine.
I think one efficent way to do this would be to use canvas.translate.
On the first drawing the entire canvas would have to be filled with tiles. New android phones can do this easily and quickly.
When the backround is scrolled I would use canvas.translate(scrollX, scrollY), then I would draw individualy one by one tile to fill the gaps, BUT, I would use
canvas.drawBitmap(tileImage[i], fromRect, toRect, null) which would only draw the parts of the tiles that are needed to be shown, by setting fromRect and toRect to correspond to scrollX and scrollY.
So all would be done by mathematics and no new bitmaps would be created for the background - save some memory.
EDIT:
However there is a problem using canvas.translate with surfaceView, because it is double buffered and canvas.translate will translate only one buffer but not the second one at the same time, so this alternating of buffers would have to be taken into account when depending on surfaceView to preserve the drawn image.
I am using your original method to draw a perspective scrolling background. I came up with this idea entirely by accident a few days ago while messing around with an easy technique to do a perspective scrolling star field simulation. The app can be found here: Aurora2D.apk
Just tilt your device or shake it to make the background scroll (excuse the 2 bouncing sprites - they are there to help me with an efficient method to display trails). Please let me know if you find a better way to do it, since I have coded several different methods over the years and this one seems to be superior. Simply mail me if you want to compare code.