Efficient 2D drawing in Android - java

I have searched for quite a few hours and have not been able to find a concise definite andswer to my question. I have an application where I need to draw a sports field (including all pitch lines) to the screen. So far, I have extended the SurfaceView and pretty much copied the rest of the LunarLander demo as well.
All the data the application requires to draw the pitch to the correct dimensions is being received from a socket which works fine too. However, at the minute in the onDraw() function, I am drawing all lines each frame which is causing a fairly slow framerate in the emulator (e.g. ~10fps). Here is my onDraw() function:
#Override
public void onDraw(Canvas canvas) {
canvas.drawARGB(255,0,144,0);
canvas.drawLine(canvas, getFirstLine(), mPaint);
canvas.drawRect(canvas, getFirstRect(), mPaint);
canvas.drawRect(canvas, getSecondRect(), mPaint);
...
canvas.drawRect(canvas, getSecondRect(), mPaint);
drawAnimatedObjects();
}
I then draw circles and different positions over this background. My question is how do I make this more efficient? Is there a way that I can draw the lines at the application initialisation and not have to redraw them every frame?
Thanks for any help.

You should definitely be caching any canvas drawing which will not change to a bitmap at initialization time and then draw that bitmap in onDraw(). That will help render times a lot. Something like:
Bitmap mField = null;
void init()
{
mField = new Bitmap(...dimensions...);
Canvas c = new Canvas(mField);
c.drawRect(...);
...
}
void onDraw(Canvas c)
{
c.drawBitmap(mField);
}

Is your sports field static or does is scroll over the screen?
If it is static, you should consider to build it once and save it as an image which than will be redrawn each time.
Another thing: The emulator is very slow in comparison with modern devices. You should check your performance at least on a G1 or later to verify the real performance.

Related

Full Width Graphics Cause Major Lag

I'm creating a game with processing java. I optimized the game as much as I could making sure the image textures are very small, drawing only certain portions of the map, etc., and the game runs consistently at 60 FPS. However, when I want to draw an image across the entire screen, for an example, as a tinted overlay (as seen from the image below)
the FPS significantly decreases, going from 60 FPS to around 40 FPS. The same happens if I use a fullscreen graphic, like a rect(0, 0, width, height) the FPS will still decrease when the graphic is quite large, spanning the width of the entire screen. Literally something as simple as the code below causes lag.
PImage fullscreenImg;
void setup() {
size(displayWidth, displayHeight);
fullscreenImg = loadImage("img.png");
}
void draw() {
image(fullScreenImg, 0, 0, width, width);
}
Here's a video of the lag happening when a full width image is displayed (the FPS goes from ~30 to ~20): https://www.youtube.com/watch?v=bjKFIgb2fII
I've tried to solve this problem by using the get() function, or reducing the resolution of the image (which just causes the image to be more pixely), and none of it works; the FPS still stays at around 40. Is there any way to make an image that has a very wide width, in my case, covering the entire screen, without decreasing the FPS? Am I doing something wrong?
Thanks for any help!
Make sure you are not loading the image during the draw() method. All of your images should load in your setup() function, and should be stored in a variable (memory), before the main loop executes. Otherwise the image will have to be pulled from the disk every time the game loops, which takes much longer than pulling from memory.
Hopefully this helps, I would recommend posting code samples rather than screenshots of the game (although the game looks very nice), otherwise it is a bit hard to diagnose the issue.

LibGDX - setProjectionMatrix() "hiding" a Sprite

I am trying to draw a Sprite but it just appears for a frame when I first run my program, and then immediately disappears. I am working with a Stage, a Tiled map and two Batches, which I hope is not a problem.
It pretty much looked like the sprite was hiding behind something else, but I made completely sure that nothing was being drawn after it. So after a long time messing around, I found out that deleting the setProjectionMatrix() method "solved" my problem, since the sprite showed perfectly somehow.
I don't understand the reason why this happened at all, and I don't want to just delete the method and have sprites following the camera around, so:
Why or how would a setProjectionMatrix() method "hide" a Sprite? Is it altering the order in which my sprites are drawing? And most importantly, how do I fix it?
Here's my render method:
public void render(float delta) {
Render.cleanScreen(); //Render is a class i made with useful static stuff, like the Batch i am using.
//This method is pretty much just a Gdx.gl.glClearColor() method.
tmr.setView(camera); // tileMapRenderer
tmr.render();
b2dr.render(world, camera.combined); // Box2DDebugRenderer
stage.act();
stage.draw();
Render.batch.begin();
sprite2.draw(Render.batch); //The sprite i want to draw
Render.batch.end();
hudBatch.begin();
sprite1.draw(hudBatch); //This works fine
hudBatch.end();
Render.batch.setProjectionMatrix(camera.combined);
Gdx.input.setInputProcessor(stage);
}
Edit: Someone asked for extra info, so:
• This is what my game looks like with the setProjectionMatrix (after the first frame when the Sprite dissapears) and this is what it looks when i delete it. You can clearly see the red square(the Sprite).
• I am currently using a FitViewport.
Stage.draw() calls apply() on the Stage's Viewport, but never restores the OpenGL Viewport to what it was before. I'm guessing that your stage uses FitViewport, so it is cropping part of the screen. If you want different viewport behavior for stuff you draw outside your stage, you should create a separate viewport for that, and call viewport.apply() before you start rendering the other stuff.
Side note: if you are lazy loading anything that uses native memory (like SpriteBatch and Texture, things implementing Disposable) and storing a reference in a static variable (your Render class), make sure you dispose all of it and null it out in your game's destroy() method. Otherwise, on Android you will be leaking that stuff and it will fail to work when you reopen your game.
Another side note: In my opinion, the Sprite class should not be used unless you have hundreds of them and they aren't moving much (in which case it might have slightly better performance). Sprite is a weird conflation of an asset (a TextureRegion) and game state data. As a result, it can create design issues that you have to work around, such as when you want to animate it or flip it. It is better to create your own GameObject class that references a TextureRegion (a single instance of which can be shared by many elements), and has its own variables for position, color, rotation, etc. Then draw it with the appropriate SpriteBatch.draw method using those properties. In my game, I have a wrapper interface around TextureRegion that can alternately wrap Animation<TextureRegion> so my GameObject class can use either and it can be swapped easily. This makes the code more flexible to change.
So... I just realized my coworker accidentally deleted the viewport.update() method.
#Override
public void resize(int width, int height) {
viewport.update(width, height);
}

Is LibGDX SpriteBatch draw() smart enough not to redraw 100% exactly same sprite at each render() call?

Is LibGDX SpriteBatch draw() smart enough not to redraw 100% exactly same sprite at each render() call?
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (shallFadeOut) { // becomes true when sound message finishes
doFadingOut();
showNextScreen();
dispose();
} else {
batch.begin();
// introSprite is a static image - it never changes a pixel (splash screen)
// I need to paint it once and then just wait until sound message finishes
// DON'T NEED TO REDRAW IT EVERY render() cycle 60 times/sec (~60 fpm)
introSprite.draw(batch);
batch.end();
}
}
In OpenGL, you typically redraw the whole screen on every frame. It doesn’t make sense to wonder if the batch is smart enough to avoid redrawing the same thing, because it has to redraw it to prevent it from disappearing.
If you want to avoid redrawing anything on the screen for a while to save battery on the device, you can use GDX.graphics.setContinuousRendering(false) but that means your render() method will stop getting called so you must set it back to true using a timer or input callback.
You could alternatively use a Boolean to decide whether to clear the screen with glClear and draw stuff, but under the hood, LibGDX will still be requesting OpenGL to copy the screen buffer data between the back buffer and the screen buffer.
Not that drawing a single sprite is super trivial and probably not worth a second of thought about optimizing it.
I am afraid that I do not think any accounting is done by the Batch between batching render steps to see if a texture was already loaded and is ready to be rendered, but the texture itself might.
The batching is intended for cases where you are drawing duplicates of the same thing in the same render step so that you are not unnecessarily context switching between sprites and having to reload data to your graphics card.
The batch is as I am sure you know defined by the begin and end calls you are using, and it depends on you actually rendering all of the sprites of a particular type one after the other without jumping to render something else. This can be seen int he batch draw code here, but for readability the relevant lines are below:
Texture texture = region.texture;
if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) {
flush();
}
To take advantage of the batch - you should be grouping the rendering of all your sprite types, and between steps, the batch does not offer any efficiencies by design as far as I can tell.
But, between batching steps or game loops, when a texture is bound, it uses a fixed target and handle as can be seen here. This means that if all you are drawing is a single sprite for several render loops, libgdx should be using your memory and graphics card as efficiently as can be expected.

How do I draw adjacent bitmaps to a canvas in Android?

I've spent all night trying to figure out why I can't draw a series of bitmaps completely adjacent to each other (with no gaps in between), using Android.
For context, I am building a spectrogram application which displays a vertical bitmap for each 'window' of audio data that comes in, providing the user with a heatmap of frequencies. At the moment I'm using pre-recorded audio so I can perform all my calculations before I have to display anything - I have an ArrayList of integer arrays, each of which represents one window's bitmap, which is drawn to a canvas using a timer thread.
I am aware that the approach below will ultimately break when the application tries to draw past the dimensions of the screen, but I am not worrying about that for now. The problem I would like to solve is that the below code results in a one-pixel (ish) gap between the drawn bitmaps, when I would actually like them to be absolutely adjacent.
This is the run() method for my timer thread:
public void run() {
Canvas c = null;
try {
c = sh.lockCanvas(null);
synchronized(sh) {
doDraw(c);
}
} finally {
if (c!=null) {
sh.unlockCanvasAndPost(c);
}
}
}
This is the doDraw() method which draws the bitmaps, and then skips along to the end of that drawn bitmap in order to draw the next one. It simply does so by incrementing the 'windowsDrawn' field:
private void doDraw(Canvas canvas) {
canvas.drawBitmap(spec.getBitmapWindow(windowsDrawn), 0, 1, windowsDrawn, 0, 1, h, false, null);
System.out.println("Windows drawn: "+windowsDrawn);
windowsDrawn++;
}
spec.getBitmapWindow(windowsDrawn) simply returns an integer array of pixel values for the vertical window to be drawn.
Here's a screenshot to show what I'm talking about.The image looks as if it is behind tiny prison bars and I would like to get rid of these.
Thanks!
I found out what I was doing wrong. The 'prison bars' effect was actually a side-effect of me writing incremental updates to the back-buffer, which was presumably being flipped every so often (hence losing some of my updates to the other buffer, giving the black vertical lines). The problem was solved by ensuring that I was instead writing to a buffer bitmap and then redrawing the entire frame each time. Incremental updates to the screen (like I was trying to do) are not allowed in Android.

How can I speed up my Java game's FPS?

I am making a Java game, and the game lags a lot when I paint out the graphics.
The way I am currently painting out the graphics is to make a BufferedImage, get the Graphics2D from it, then do a whole bunch of:
g2d.drawImage(loadedImages.getImage(),x,y,null);
After I print all of the images to the BufferedImage, I paint the BufferedImage to the screen.
There are a lot of images I paint to the BufferedImage. Is there a better way to do this that would speed up the painting time?
I do not know much about graphics and graphic cards. Should I use a graphics card? Should I paint directly to the screen? Should I use something entirely different than drawImage()?
The performance should be good if you're drawing a reasonable amount of images.
Make sure you're not creating new BufferedImages every time you draw to the screen. For example, you might have a Resources singleton in which you manage all of your images so that you only load and unload each image once.
If you really want more performance, you'll want to use OpenGL. See LWJGL, libgdx, or JOGL. You may also want to consider 2D graphics libraries like Slick.
I've found it useful to extend java.awt.Canvas and use
BufferStrategy bs;
void init() {
createBufferStrategy(2);// or 3 for triple-buffering
bs = getBufferStrategy();
}
and then the actual draw() method looks like
void draw() {
Graphics g = bs.getDrawGraphics();
g.drawImage(image, x, y, w, h, null);
// Draw more things here...
// You can also make calls like `myObject.draw(g)` and whatever
// you draw onto `g` within those calls will show up.
bs.show();
g.dispose();
}
This is what I use for drawing a lot of things. And then within each myObject.draw(g) call, there are sometimes multiple other similar calls all chained up. Most often my main draw() method has one or two for loops in it that just say for(Entity e: entities) e.draw(g); or something similar. Then the whole drawing chain is kicked off from there.

Categories