Load and dispose images with Libgdx - android

I made an android app using libgdx but I have a problem of latency. After a few games, the application become very slow and I don't really know why.
I think it's maybe because the loading of images and when I dispose them but I am not really sure.
This is my code :
I have a class Cell and in this one I load and dispose images:
TextureAtlas dotsAtlas = game.getAssets().get("dots.pack", TextureAtlas.class);
TextureRegion textureRegionRed = dotsAtlas.findRegion("red");
ImageButtonStyleRed = new ImageButton.ImageButtonStyle();
ImageButtonStyleRed.imageUp = new TextureRegionDrawable(textureRegionRed);
public void disposeCell(){
dotsAtlas.dispose();
}
And in the class GameScreen, I have :
public void dispose(){
skin.dispose();
stage.dispose();
batch.dispose();
backgroundSprite.getTexture().dispose();
generatorFont.dispose();
othersAtlas.dispose();
Cell.disposeCell();
}
Do I use Atlas,textures and dispose functions correctly to load images ?

Related

Low frame rate when drawing full screen drawable on canvas

The app I'm developing is a Flappy Bird clone.
I'm using a surfaceView object in which I have a gameThread and inside of its run method I draw the various components of the game on the canvas.
Everything runs smoothly as long as I just draw Rects to represent the objects, but as soon as I added the first Drawables i noticed a little bit of a loss in smoothness. If I try to draw the background as a Drawable the game suffers very significant frame rate loss.
What I tried:
Using png and all different kinds of bitmap as assets
Resizing the asset to fit the canvas perfectly, thus avoiding a rescale
None of this had any tangible effect.
Basically:
If I only use drawRect: 60fps
If I draw the back with drawRect and the other components with drawable.draw(canvas): 57fps
If I draw everything (background included) with drawable.draw(canvas): 15fps
Somewhat relevant code:
public class CannonView extends SurfaceView
implements SurfaceHolder.Callback {
private CannonThread cannonThread; // controls the game loop
private Drawable background;
// constructor
public CannonView(Context context, AttributeSet attrs) {
super(context, attrs); // call superclass constructor
getHolder().addCallback(this);
background= ResourcesCompat.getDrawable(getResources(), R.drawable.background, null);
}
public void newGame() {
background.setBounds(0,0, getScreenWidth(),getScreenHeight());
}
public void drawGameElements(Canvas canvas) {
background.draw(canvas);
}
public void stopGame() {
if (cannonThread != null)
cannonThread.setRunning(false); // tell thread to terminate
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!dialogIsDisplayed) {
newGame(); // set up and start a new game
cannonThread = new CannonThread(holder); // create thread
cannonThread.setRunning(true); // start game running
cannonThread.start(); // start the game loop thread
}
}
private class CannonThread extends Thread {
private SurfaceHolder surfaceHolder; // for manipulating canvas
private boolean threadIsRunning = true; // running by default
// initializes the surface holder
public CannonThread(SurfaceHolder holder) {
surfaceHolder = holder;
setName("CannonThread");
}
// changes running state
public void setRunning(boolean running) {
threadIsRunning = running;
}
// controls the game loop
#Override
public void run() {
Canvas canvas = null; // used for drawing
while (threadIsRunning) {
try {
// get Canvas for exclusive drawing from this thread
canvas = surfaceHolder.lockCanvas(null);
synchronized(surfaceHolder) {
drawGameElements(canvas);
}
}
finally {
if (canvas != null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
It seems apparent that the dominant cause of the low frame rate is background.draw(). Switching to a Bitmap improves this somewhat, probably since it cached the output of draw(), and because it can be used with Canvas functions that are guaranteed not to need scaling (e.g., drawBitmap( Bitmap, float, float, Paint))
You also found that switching to RGB_565 as an intermediate format improves performance quite a bit, presumably because it throws away the alpha. (Otherwise, I would've expected this to be somewhat slower, b/c the format has to be converted back to RGBA_8888 as it's blitted into the SurfaceView.)
It's also apparent that Android won't let you go over 60fps. This is almost certainly because lockCanvas() takes part in a triple buffering scheme that throttles the drawing rate, to prevent you from submitting frames that could never be displayed (due to your device's fixed screen refresh rate of 60Hz).
This leaves the question of why you don't get a full 60fps, but something close to it. If drawGameElements() takes the same amount of time to run each time, and it's less than 16ms, then lockCanvas() should be throttling you, and no frames should ever get dropped (60fps continuously). It seems likely that there is a burble in the thread scheduler or something, and every so often, the CannonThread does not execute quickly enough to provide the frame before the triple-buffering scheme needs to page-flip. In this event, the frame must be delayed until the next screen refresh. You might try increasing CannonThread's thread priority, removing any extra processing in drawGameElements() that doesn't absolutely need to happen on CannonThread, or closing other apps running on your device.
As mentioned, OpenGL is the standard way of getting max sprite performance for games like these, because it is able to offload many operations to hardware. You may be approaching the performance limit of a drawBitmap()-based game.

unity3d - Separate UI Thread for Android

In my VR app for Android Smartphones (I think it doesn't matter whether it is a VR app or not) I download 6 big images from Street View and built a skybox out of it. During this process the whole app incl. environment and UI freezes. After ~10sec the process is down and Street View is there.
How can I seperate the main processing from UI? So the phone is working, but should not freeze. This is a common web problem, but how can I solve this in Unity for Android?
Thank you!
code:
private byte[] GetStreetviewTexture(string url) {
WWW www = new WWW(url);
while (!www.isDone) ;
if (!string.IsNullOrEmpty(www.error))
{
Debug.LogWarning("Unable to DL texture: " + www.error);
}
else
{
bytes = www.texture.EncodeToPNG();
}
return bytes;
}
You probably want to use an IEnumerator to thread it with a Unity Coroutine. If you're already using this, which I'm assuming you actually are - could it be a performance issue that freezes the device? If so, check the Unity profiler and/or Unity Remote. Coroutines are, like comments suggest, not a Thread - but emulates a threaded behaviour.
Basic Coroutine code:
void SomeMethod() {
StartCoroutine(Threaded());
}
IEnumerator Threaded() {
// Do something
yield return new WaitForSeconds(3f);
}
Check out the WWW class and how to make an IEnumerator wait for download to complete
Update responding to OP's follow-up question:
How to tell when a Coroutine is done and get a value
Update responding to OPs follow-up follow-up question:
This is a simplified example of your current logic/flow. NOTE: THIS DOES NOT WORK. Do not use this code as an example for how it's supposed to be done, I just want to illustrate the differences in how you should approach your problem.
void ButtonClicked() {
SetTexture()
}
void SetTexture() {
Texture texture = GetTexture()
Object.texture = texture;
}
Texture GetTexture() {
Texture texture;
StartCoroutine(DownloadTexture((textureCallback) => {
texture = textureCallback;
}));
return texture;
}
IEnumerator DownloadTexture(Action<Texture> callbackTexture)
{
WWW www = new WWW(URL);
yield return www;
callback(www.texture);
}
Not only does this not work, because a Coroutine is run asyncronously with the rest of the code, but it's not a good approach for this task.
Instead of doing
Button click -> set texture -> start download -> error setting texture -> download finished
you want:
Button click -> download texture -> wait for it to be done -> set texture
Like this:
void ButtonClick() {
StartCoroutine(DownloadTexture((callbackTexture) => {
SetTexture(callbackTexture); // Will run SetTexture when Coroutine DownloadTexture is completed.
}));
}
IEnumerator DownloadTexture(Action<Texture> callbackTexture)
{
WWW www = new WWW(URL);
yield return www;
callback(www.texture);
}
void SetTexture(Texture texture) {
object.texture = texture;
}
Note that this is just flow-code. Your code will look different, using byte[] instead of Texture and I don't know if you're using a Button to start the texture-setting. This can be any sort of trigger/starting point.

libgdx: asset manager block screen ui

I use AssetManager to load all assets in my game.
I have problem with displaying loading progress or instead progress I try to set only background colour using this code in my LoadingScreen render method.
Gdx.gl.glClearColor(0.431f, 0.792f, 0.808f, 0xff / 255.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Everiting loads well but, while loading I get black screen instead colour set in glClearColor. The same happend when implementing loading bar like this example
Here part of Assests class
public class Assets implements Disposable, AssetErrorListener {
public static final Assets instance = new Assets();
.
.
public void init (AssetManager assetManager) {
this.assetManager = assetManager;
// set asset manager error handler
assetManager.setErrorListener(this);
// load texture atlas
assetManager.load(Constants.TEXTURE_ATLAS_OBJECTS, TextureAtlas.class);
.
.
TextureAtlas atlas = assetManager.get(Constants.TEXTURE_ATLAS_OBJECTS);
.
}
.
}
Here is simple code of my LoadingScreen:
#Override
public void show() {
manager = new AssetManager();
Assets.instance.init(manager);
}
public void render(float deltaTime) {
Gdx.gl.glClearColor(0.431f, 0.792f, 0.808f, 0xff / 255.0f);//light blue
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (manager.update()) {
ScreenTransition transition = ScreenTransitionFade.init(0.5f);
game.setScreen(new HomeScreen(game), transition);
}
}
Here I expected light blue empty screen to show while loading assets, but I get black screen.
I guess some code blocks main thread but I don't know how to fix this.
Thanks for help
Make sure you know when Screen.show() and Screen.render(float delta) are called:
Screen.show(): is called when you call Game.setScreen().
Screen.render(float delta): is called continuously after Screen.show() is finished. Hence during the loading Screen.show() is not yet finished and the default background (black) is shown.
To avoid that a separate thread would be nice. Here comes AssetManager which loads stuff asynchronously. Now we only have use it properly:
Call AssetManager.load(<your assets>) before you call Game.setScreen(<Loading Screen>). LoadingScreen.render(float delta) is then called directly and you see your background. To check if the loading is done you can use AssetManager.update(), which returns true if done and false else.

Load Bitmap to variable in new thread, OpenGL result a white image

I'm trying to load images in new thread to reduce lags in UI thread:
public class MyGame extends Activity implements Game, Renderer {
...
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
super.onSurfaceCreated(gl, config);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
if(firstTimeCreate) {
Settings.load(getFileIO());
// Load bitmaps and save to variable Assets.backgroundRegions
Assets.load(this);
firstTimeCreate = false;
} else {
Assets.reload();
}
}
});
t.start();
}
...
}
The problem is when I trying to draw images as soon as the images is loaded, I only get a white images, no error message. This is the method I use to render background (this method run in a loop)
public void renderBackgrounds() {
if (Assets.backgroundRegions.size() > 0) {
batcher.beginBatch(Assets.backgroundRegions.get(0).texture);
batcher.drawSprite(
Assets.backgroundRegions.get(0).position,
Assets.backgroundRegions.get(0)
);
batcher.endBatch();
} // else { background is not loaded yet }
}
The weird thing is when I press Home button and open my app again, then background images are displayed all correctly. It's just like there are "white-images cached version" of all the loaded images, and Android just don't clear the cache until I re-create the Activity, maybe, I don't know.
If I remove the new thread implementation in onSurfaceCreated like this:
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
super.onSurfaceCreated(gl, config);
if(firstTimeCreate) {
Settings.load(getFileIO());
Assets.load(this);
firstTimeCreate = false;
} else {
Assets.reload();
}
}
...then everything works fine except the UI thread is extremely lag.
I have read some posts about loading Bitmaps in new thread (e.g. this and this), they load Bitmap and assign the variable directly to the View (ImageView), but in my case I save the Bitmap to variables and use OpenGL to render. I don't know if this is one of the reasons or not.
There are the possible reasons I could think about:
All loaded images are cached wrong (somehow, I don't know) and I only get white images until I re-create the Activity.
Something wrong with multithreading and the variable I have used to store bitmap data (backgroundRegions).
I did things in wrong way when use OpenGL and Multithreading together, I don't know much about OpenGL, the part with OpenGL is taken from a small framework.

Android: OpenGL reloading textures

I am trying to add a loading screen into my app as it takes some time to load off of the textures. This is what I was doing before...
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
super.onSurfaceCreated(gl, config);
if(firstTimeCreate) {
load(); //load settings
Assets.LoadTextures(this);
firstTimeCreate = false;}
else {
//When screen is resumed....
Assets.reloadTextures();}
This way after the app was resumed the else statement would take effect and reload everything...I tried to adapt it to this
else {
//When screen is resumed....
Thread aThread = new Thread()
{
public void run(){
boolean once = true;
while(once)
{
Assets.reloadTexutres();
once = false;
}
}
};
aThread.start();
}
However it just seems now that OpenGL cant bind the textures as the screen is white. What is going wrong by adding a thread to this method? Do I need to wait for all the textures to load before I let OpenGL start to try and render, if so how can I load one and then present a loading screen until the rest are done?
Thanks
OpenGL ES context is assigned to a thread. So, if you want to create context in one thread and use it in another thread, you should call eglMakeCurrent(...) function.

Categories

Resources