I'm writing a simple 2D game engine and rendering various objects to a locked SurfaceVew by calling Canvas.drawBitmap (all the same source bitmap but generally not with 1:1 source to destination scaling).
Everything appears to work as expected until there are a lot of calls per frame to drawBitmap. When this happens I occasionally see a stall (100mS or so) accompanied by an effect that looks as if several frames of rendering have occurred together, i.e. some objects will be drawn twice at two screen locations or a pair of objects moving at the same speed will appear to momentarily get closer together or further apart.
The application is structured as follows (simplified for the sake of example);
initialiseGameObjects();
while(quit==false)
{
processGameLogic(); // update object positions
Canvas c = surface_holder.lockCanvas();
if (c != null) {
drawGameObjects(c); // draw all objects
surface_holder.unlockCanvasAndPost(c);
}
}
As I understand it, the Canvas is a holder for draw requests which get executed when the Post is issued. Presumably there is a limit to the number of requests it can hold and I'm wondering if I'm exceeding this limit (I'm drawing up to a hundred small bitmaps depending on what's on screen at any one time) and inadvertently provoking some kind of flush and upsetting the double buffering in some way, although I've not managed to find any documentation to confirm or disprove this.
Does anyone have any idea what might be going on?
Regards,
Steve
You need to sychronize calls to canvas drawing. Check this example of a game driving thread: OnDraw() is not fired, nothing is drawn in surfaceView - Android
EDIT:
However I think it may be related to this problem:
Android SurfaceView/Canvas flickering after trying to clear it
Related
Currently I am doing app allowing user to draw. Simple think, just extend Canvas class and most of the thing is done.
That was my initial thinking and idea. But as the canvas is rather small because this is only what user see on the screen there is not much possible space to draw. Going through documentation I found translate() method allowing me to move canvas. What I did find out is when I move it, there is some kind of blank space just as you would move piece of paper. I understand that this is totally normal, as I've said before - canvas is only "the screen".
My question is - is there a possibility to make something like infinite canvas so you can make a huge painting and move everything around?
Before this question I was thinking about two things how something like this can be done:
Move all objects on canvas simultaneously - bad idea, because if you have a lot of them then the speed of moving is very bad.
Do something similar as it is done in ListView when you move it (or better see on the screen) only views that are on the screen together with one before and one after are loaded to memory and rest is uploaded dynamically when needed. I think this is the best option to achieve this goal.
EDIT:
Question/answer given by Kai showed me that it is worth to edit my question to clarify some of the things.
Basic assumptions about what can be done by user:
User is given opportunity to draw only circles and rectangles with some (around 80%) having drawable (bitmap) on them on canvas.
I assume that on all screens there will be maximum 500-800 rectangles or circles.
First of all thinking about infinity I was thinking about quite big number of screens - at least 30 on zoom 1x in each side. I just need to give my users bigger freedom in what they are doing.
On this screen everything can be done as on normal - draw, scale (TouchListener, ScaleListener, DoubleTapListener). When talking about scaling, there is another thing that has to be concerned and connected with "infinity" of canvas. When user is zooming out then screens, or more precise objects on the invisible "neighbours" should appear with proper scaling as you would zoom out camera in real life.
The other thing that I've just realised is possibility of drawing at small zoom level - that is on two or three screens and then zooming in - I suppose it should cut and recalculate it as a smaller part.
I would like to support devices at least from API 10 and not only high-end.
The question about time is the most crucial. I want everything to be as smooth as possible, so user wouldn't know that new canvas is being created each time.
I think it really depends on a number of things:
The complexity of this "infinite canvas": how "infinite" would it really be, what operations can be done on it, etc
The devices that you want to support
The amount of time/resource you wish to spend on it
If there are really not that many objects/commands to be drawn and you don't plan to support older/lower end phones, then you can get away with just draw everything. The gfx system would do the checking and only draws what would actually be shown, so you only waste some time to send commands pass JNI boundary to the gfx system and the associated rect check.
If you decided that you needs a more efficient method, you can store all the gfx objects' positions in 4 tree structures, so when you search the upper-left/upper-right/lower-left/lower-right "window" that the screen should show, it'll fast to find the gfx objects that intersects this window and then only draw those.
[Edit]
First of all thinking about infinity I was thinking about quite big
number of screens - at least 30 on zoom 1x in each side. I just need
to give my users bigger freedom in what they are doing.
If you just story the relative position of canvas objects, there's practically no limit on the size of your canvas, but may have to provide a button to take users to some point on canvas that they are familiar lest they got themselves lost.
When talking about scaling, there is another thing that has to be
concerned and connected with "infinity" of canvas. When user is
zooming out then screens, or more precise objects on the invisible
"neighbours" should appear with proper scaling as you would zoom out
camera in real life.
If you store canvas objects in a "virtual space", and using a "translation factor" to translate objects from virtual space to screen space then things like zoom-in/out would be quite trivial, something like
screenObj.left=obj.left*transFactor-offsetX;
screenObj.right=obj.right*transFactor-offsetX;
screenObj.top=obj.top*transFactor-offsetY;
screenObj.bottom=obj.bottom*transFactor-offsetY;
//draw screenObj
As an example here's a screenshot of my movie-booking app:
The lower window shows all the seats of a movie theater, and the upper window is a zoomed-in view of the same theater. They are implemented as two instances of the same SurfaceView class, besides user input handling, the only difference is that the upper one applies the above-mentioned "translation factor".
I assume that on all screens there will be maximum 500-800 rectangles
or circles.
It is actually not too bad. Reading your edit, I think a potentially bigger issue would be if an user adds a large number of objects to the same portion of your canvas. Then it wouldn't matter if you only draw the objects that are actually shown and nothing else - you'd still get bad FPS since the GPU's fill-rate is saturated.
So there are actually two potential sources of issues:
Too many draw commands (if drawing everything on canvas instead of just drawing visible ones)
Too many large objects in the same part of the screen (eats up GPU fill-rate)
The two issues requires very different strategy (1st one using tree structures to sort objects, 2nd one using dynamically generated Bitmap cache). Since how users use your app are likely to different than how you envisioned it to be, I would strongly recommend implementing the functions without the above optimizations, try to get as many people as possible to do testing, and then apply optimizations to each of the bottlenecks you encounter until the satisfactory performance is achieved.
[Edit 2]
Actually with just 500~800 objects, you can just calculate the position of all the objects, and then check to see if they are visible on screen, you don't even really need to use some fancy data structures like a tree with its own overheads.
I'm trying to incrementally draw a waveform on a canvas in a separate thread, i.e. in each loop of the thread draw only the portion recently acquired by the hardware without redrawing the whole curve.
I am not sure if I can rely on the canvas' contents retained between SurfaceHolder.unlockCanvasAndPost and SurfaceHolder.lockCanvas.
The documentation seems to be a bit ambiguous on that and my experiments give surprising results: the "old" contents is different for the canvas returned in two subsequent loops of the drawing thread. It looks as if two canvases were used alternately (but I checked - it is the same canvas but with different contents).
The simplest way which I found to reproduce this behaviour:
Import the LunarLander sample applicaton.
In the LunarView.run method insert sleep(500) instruction in the loop.
In the LunarView.doDraw method comment out the line:
canvas.drawBitmap(mBackgroundImage, 0, 0, null);, which erases the old contents.
Run the game and let the ship crash. You will see the intermediate positions of the ship but they will blink: odd and even positions will be shown alternately.
So, my question is: can we draw pictures adding changes only to the contents which has been drawn before and if so - is there any documented way of dealing with the strange behaviour described above?
The lunar landing does not cache.
It draws the background image each time (refreshing) and then draws the spaceship.
private void doDraw(Canvas canvas) {
// Draw the background image. Operations on the Canvas accumulate
// so this is like clearing the screen.
canvas.drawBitmap(mBackgroundImage, 0, 0, null);
Check your If clauses closesly. Maybe one is alternating true and false. This would cause things to draw and then not draw, in rapid succession.
Use Log if need be to output your booleans, to see if one is changing.
Failing this, this the following gaming loop instead.
http://blorb.tumblr.com/post/236799414/simple-java-android-game-loop
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).
So in my game my View gets drawn an inconsistent rates. Which in turn makes it glitchy. Ive been running into alot of problems with the invalidate(); meathod. Any simple ideas- Everywhere i look I get thrown up on by tons of intense code.
You haven't provided us with much information, specifically code.
A few things you could do are:
Set the initial frame rate to the lowest value you observe your application runs at, i.e., if currently set to 1/60, but the frame rate continuously dips to 1/30, set to 1/30 etc.
Rework your drawing calls to be more efficient.
Try to combine multiple transformations into a single transformation by multiplying matrices, i.e. if you need to scale, translate, and rotate, multiply those three matrices together and apply that single transformation to the vertices instead of applying three separate transformations.
Try not to iterate through entire lists/arrays if unnecessary.
Attempt to use the lowest level / most primitive structure possible for anything you have to process in the loop to avoid the overhead of unboxing.
[edit on 2012-08-27]
helpful link for fixing you timestep: http://gafferongames.com/game-physics/fix-your-timestep/
It sounds like your game loop doesn't take into account the actual time that has passed between iterations.
The problem is the assumption that there is a fixed amount of time between loop iterations. But this time can be variable depending on the number of objects in the scene, other processes on the computer, or even the computer itself.
This is a common, somewhat subtle, mistake in game programming, but it can easily be remedied. The trick is to store the time at the end of each draw loop and then take the difference of the last update with the current time at the start. Then you should scale all animations and game changes based on the actual elapsed time.
I've wrote more about this on my blog a while back here: http://laststop.spaceislimited.org/2008/05/17/programming-a-pong-clone-in-c-and-opengl-part-i/
Part II specifically covers this issue:
http://laststop.spaceislimited.org/2008/06/02/programming-pong-in-c-and-opengl-part-ii/
In my app I am drawing bus routes on top of a MapView. The routes have anywhere between a dozen and a few hundred GPS coordinates that describe the route that the bus takes.
The problem I'm having is that once I draw out all these lines panning/zooming the MapView is incredibly slow (even clicking the 'Back' button takes a minute to happen).
I'm not sure how relevant it is, but I put in some debug code then checked the logcat output and the MapView is repeatedly calling the draw() method of the Overlay whether anything has changed or not. This is happening several times a second and is causing a massive amount of garbage collection to happen (2-3 MB every second).
Does anyone have any ideas/suggestions for a method to try and speed this up?
I have only used ItemizedOverlay, not Overlay, so these suggestions are pure conjecture. Heck, I haven't even done much with the 2D graphics API.
Obviously, the best answer is to get it to stop calling draw() all the time. Try logging some stack traces and see if you can figure out what is triggering all of the draw() calls. For example, in the Android Google Groups recently, somebody noticed that Chronometer causes widgets in the same UI to be redrawn every second. While I can see you don't have a Chronometer, you might be able to figure out some root cause to the draw() calls that you can correct.
Assuming that does not help, I am guessing that the test for "whether anything has changed or not" is some combination of getLatitudeSpan(), getLongitudeSpan(), getZoomLevel(), and maybe other MapView methods. And, I am assuming that on every draw() you are iterating over your GPS points and drawing the route. If so, you could try:
When you really do draw, draw first to a Canvas backed by a Bitmap, then apply the Bitmap on the Canvas you are handed in draw(), and cache that Bitmap.
Track what combination of values were used in the last draw(), and if the next draw() is the same, just reuse the existing Bitmap. Else, go to step #1, making sure to release the Bitmap (or reuse it, if that's possible).
I am guessing that with graphics acceleration, blasting a Bitmap onto the Canvas is cheaper than iterating over the coordinates and drawing lines. And, by caching the Bitmap, you will save on garbage generation.
Anyway, just a thought.
There are two draw methods in the overlay class. One with 3 arguments and one with 4 arguments. You have to override the draw method with 3 arguments.
Overriding the method with 4 arguments will slow down your application. This is exactly, what happened to me. It seems, where are examples around in the internet with the same error.