Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (524)
Games in Android Showcase (127)
games submitted by our members
Games in WIP (592)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Best way to reuse a texture  (Read 448 times)
0 Members and 1 Guest are viewing this topic.
Offline Gibbo3771
« Posted 2014-02-17 17:15:58 »

Hi,

Im in the process of porting my Falling Rocks game to android and I am having some FPS issues, I have discovered that this is due to the way my objects are created.

My FPS on desktop is 60 solid but FPS on a Galaxy S4 and Tab 3 are around 35-40, which is low considering the power of these devices and the simplicity of the game.

My game creates 300 new objects every minute on normal and does 600/minute on hard, this means that my game does a minimum of 300 File I/O operations a minute.

This seems to be the problem, now I thought "heh, i'll just create an assets class with a bunch of static fields and reuse the sprites", well that did not work out well because my objects have a random size every spawn, also after 5 seconds they start to blink to let you know they are about to expire. Which is obviously not working because when 1 sprite flashes they all flash.

However when I did this my FPS was 60 solid on both devices.

What can I do to reduce these File I/O operations? I thought object pooling but that means I would have to load in like , 30 of every texture into memory and store them, then borrow and put back constantly. Might end up hogging all the memory or making the GC go crazy?

Solution

Baring in mine I am using the LibGDX library but that should not make a difference.

Basically create a static reference to the texture/sprite somewhere like so:

1  
2  
3  
public class AssetLoader{

public static final Sprite EMERALD = spriteLoader("data/img/gems/emerald.png");


}

Then when creating a new object, you can use the following constructor in the Sprite class:

1  
2  
3  
4  
/** Creates a sprite that is a copy in every way of the specified sprite. */
   public Sprite (Sprite sprite) {
      set(sprite);
   }


So I done this:

1  
setSprite(AssetLoader.EMERALD);


The set sprite method declares a new sprite, rather than taking the object reference itself, it copes it. So don't use
1  
this.sprite = sprite;


Instead use
1  
this.sprite = new Sprite(sprite);

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline opiop65

JGO Kernel


Medals: 159
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #1 - Posted 2014-02-17 17:19:06 »

Load the texture in statically, but don't create the actual rock statically. If you don't have those activities separated, then you need to do so. Make a rock class that holds a reference to a texture. Then, when you create that rock object, copy the texture data, but don't actually load it back in from the file system. This should be fairly easy to do.

Edit:
Longarmx explained it much better than I did.

Offline Longarmx
« Reply #2 - Posted 2014-02-17 17:21:04 »

Load 1 spritesheet texture at the beginning and then pull TextureRegions from that texture to draw.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Gibbo3771
« Reply #3 - Posted 2014-02-17 17:25:50 »

Load the texture in statically, but don't create the actual rock statically. If you don't have those activities separated, then you need to do so. Make a rock class that holds a reference to a texture. Then, when you create that rock object, copy the texture data, but don't actually load it back in from the file system. This should be fairly easy to do.

Edit:
Longarmx explained it much better than I did.

So:

1  
Sprite sprite = new Sprite(AssetLoader.ROCK);


This will create a new sprite using that data?

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline opiop65

JGO Kernel


Medals: 159
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #4 - Posted 2014-02-17 17:28:48 »

I personally haven't ever used the AssetLoader in LibGDX, I just create TextureRegions from a large spritesheet. For example:
1  
2  
3  
public Mob(World world, int x, int y, int tx, int ty){
      texture = new TextureRegion(new Texture("data/tiles.png"), tx, ty, 16, 16);
      sprite = new Sprite(texture);

Where tx and ty indicate the position of the texture in the spritesheet, and 16 is the width and height of said TextureRegion. Now, this actually isn't good code because I create the spritesheet every time I load a new Mob (new Texture("data/tiles.png")), but it would be easy enough to fix that.

I'll take a look into the AssetLoader, sounds interesting.

Offline Gibbo3771
« Reply #5 - Posted 2014-02-17 17:31:41 »

I personally haven't ever used the AssetLoader in LibGDX, I just create TextureRegions from a large spritesheet. For example:
1  
2  
3  
public Mob(World world, int x, int y, int tx, int ty){
      texture = new TextureRegion(new Texture("data/tiles.png"), tx, ty, 16, 16);
      sprite = new Sprite(texture);

Where tx and ty indicate the position of the texture in the spritesheet, and 16 is the width and height of said TextureRegion. Now, this actually isn't good code because I create the spritesheet every time I load a new Mob (new Texture("data/tiles.png")), but it would be easy enough to fix that.

I'll take a look into the AssetLoader, sounds interesting.


Actually the asset loader is my own, it's nothing special, pretty standard and horrible.

However doing what you recommended works fine, it copies the data rather than use the actual data.

Here is my asset thingie:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
/**
 * Basic assets loader, used to load in textures or polygon information for
 * box2d
 *
 * @author Stephen Gibson
 */

public class AssetLoader {

   /* Gems */
   public static final Sprite EMERALD = spriteLoader("data/img/gems/emerald.png");
   public static final Sprite RUBY = spriteLoader("data/img/gems/ruby.png");
   public static final Sprite SHAPPIRE = spriteLoader("data/img/gems/sapphire.png");
   public static final Sprite DIAMOND = spriteLoader("data/img/gems/diamond.png");

   /* Rocks */
   public static final Sprite ROCK_1 = spriteLoader("data/img/rocks/rock1.png");
   public static final Sprite ROCK_2 = spriteLoader("data/img/rocks/rock2.png");
   public static final Sprite ROCK_3 = spriteLoader("data/img/rocks/rock3.png");
   public static final Sprite ROCK_4 = spriteLoader("data/img/rocks/rock4.png");
   public static final Sprite ROCK_5 = spriteLoader("data/img/rocks/rock5.png");

   /* Health pickups */
   public static final Sprite FIRST_AID_KIT = spriteLoader("data/img/health/firstaidkit.png");
   public static final Sprite MEDI_KIT = spriteLoader("data/img/health/medikit.png");

   /* Fonts */
   public static final BitmapFont INDICATOR_FONT = fontLoader("data/font/PriceDown38-White.fnt");

   /* Poly data */
   public static final BodyEditorLoader GEMS = bodyLoader("data/img/gems/gems");
   public static final BodyEditorLoader ROCKS = bodyLoader("data/img/rocks/rocks");
   public static final BodyEditorLoader HEALTH = bodyLoader("data/img/health/health");

   public AssetLoader() {
     
   }

   /**
    * Load a sprite from a file
    *
    * @param file
    *            the file location in storage
    * @return
    */

   private static Sprite spriteLoader(String file) {
      return new Sprite(new Texture(Gdx.files.internal(file)));

   }

   /**
    * Load a font from a file
    *
    * @param file
    *            the file location in storage
    * @return
    */

   private static BitmapFont fontLoader(String file) {
      return new BitmapFont(Gdx.files.internal(file));
   }

   /**
    * Load body information from a file
    *
    * @param file
    *            the file location in storage
    * @return
    */

   private static BodyEditorLoader bodyLoader(String file) {
      return new BodyEditorLoader(Gdx.files.internal(file));
   }


Like I said it doesn't do much and was just done dirty and quick, it gets the job done and increases performance so +1 lol.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline opiop65

JGO Kernel


Medals: 159
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #6 - Posted 2014-02-17 17:32:59 »

Oops, I was thinking of the AssetManager Wink Got me there! so your problem is fixed?

Offline Gibbo3771
« Reply #7 - Posted 2014-02-17 17:37:43 »

Oops, I was thinking of the AssetManager Wink Got me there! so your problem is fixed?

Running tests now, will let you know if the FPS has improved.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Gibbo3771
« Reply #8 - Posted 2014-02-17 17:42:33 »

60 FPS on S4 and Tab 3, 40 or so on Experia S and Desire HD, give or take.

Thanks, will update the OP in a mo for solution.

EDIT: Runs fking terrible on HTC Hero lol

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Troubleshoots

JGO Knight


Medals: 36
Exp: 7-9 months


Damn maths.


« Reply #9 - Posted 2014-02-17 17:52:33 »

Don't create a new texture for everything. Generating and binding textures is an expensive operation. Instead use TexturePacker to pack textures and load each sprite with a texture region.

You can run TexturePacker every time you run your application while in development.
1  
2  
TexturePacker.process("../Project-android/assets/textures", "../Project-android/assets/textures/packed",
      "sprites"); // I find it easiest to use a json file for the settings

Ignore the atlas file that's generated; it's only useful for Scene2D skins now. Instead, load the generated spritesheet as a texture and load each sprite with a texture region, like so:
1  
2  
3  
4  
5  
private static Texture sprites = new Texture(Gdx.files.internal("textures/packed/sprites.png"));

private static Sprite spriteLoader(int x, int y, int width, int height) {
      return new Sprite(new TextureRegion(sprites, x, y, width, height));
}

Once set up, all you have to do is put a new sprite in the assets folder and it'll automatically pack it into the spritesheet.

Why are all OpenGL tutorials written in Brainf**k?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Gibbo3771
« Reply #10 - Posted 2014-02-17 18:21:36 »

Don't create a new texture for everything. Generating and binding textures is an expensive operation. Instead use TexturePacker to pack textures and load each sprite with a texture region.

You can run TexturePacker every time you run your application while in development.
1  
2  
TexturePacker.process("../Project-android/assets/textures", "../Project-android/assets/textures/packed",
      "sprites"); // I find it easiest to use a json file for the settings

Ignore the atlas file that's generated; it's only useful for Scene2D skins now. Instead, load the generated spritesheet as a texture and load each sprite with a texture region, like so:
1  
2  
3  
4  
5  
private static Texture sprites = new Texture(Gdx.files.internal("textures/packed/sprites.png"));

private static Sprite spriteLoader(int x, int y, int width, int height) {
      return new Sprite(new TextureRegion(sprites, x, y, width, height));
}

Once set up, all you have to do is put a new sprite in the assets folder and it'll automatically pack it into the spritesheet.

Is the way I am doing it much more expensive?

Am I not just creating the texture once and then reusing the texture?

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 273
Exp: 5 years



« Reply #11 - Posted 2014-02-17 18:33:44 »

Quote
Am I not just creating the texture once and then reusing the texture?

No, you are using a separate Texture for each sprite, as shown by your AssetLoader code:
1  
2  
3  
4  
5  
6  
7  
<snip>

private static Sprite spriteLoader(String file) {
      return new Sprite(new Texture(Gdx.files.internal(file))); //<--- Texture() being called! Bad!
}

<snip>


Troubleshoots is replacing that new Texture() call with a new TextureRegion() call which is referencing a section in a spritesheet, which is the best way to do it, as there is only one texture sent the  GPU (the spritesheet).

TextureRegions are simply lightweight references to a piece of texture, and can be used as a substitute for individual Textures in most places.
Offline Gibbo3771
« Reply #12 - Posted 2014-02-17 18:59:08 »

Quote
Am I not just creating the texture once and then reusing the texture?

No, you are using a separate Texture for each sprite, as shown by your AssetLoader code:
1  
2  
3  
4  
5  
6  
7  
<snip>

private static Sprite spriteLoader(String file) {
      return new Sprite(new Texture(Gdx.files.internal(file))); //<--- Texture() being called! Bad!
}

<snip>


Troubleshoots is replacing that new Texture() call with a new TextureRegion() call which is referencing a section in a spritesheet, which is the best way to do it, as there is only one texture sent the  GPU (the spritesheet).

TextureRegions are simply lightweight references to a piece of texture, and can be used as a substitute for individual Textures in most places.

Hm, I only call that Texture() constructor once per sprite at run time so for assets of this size (like 10 sprites) this is ok right?

I do not want to pack all these things into a spritesheet if not entirely needed, I don't want to sound like I am being ignorant but just clearing up the facts, only way to learn.

Here is the constructor for the LibGDX sprite class that I am calling in order to set the sprite using the static reference:

1  
2  
3  
4  
/** Creates a sprite that is a copy in every way of the specified sprite. */
   public Sprite (Sprite sprite) {
      set(sprite);
   }


It does not seem to be creating a texture, appreciate the advice and have used sprite sheets before but didn't want to use it this time because I was expecting to use < 10 sprites for the entire game.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

toopeicgaming1999 (55 views)
2014-11-26 15:22:04

toopeicgaming1999 (48 views)
2014-11-26 15:20:36

toopeicgaming1999 (8 views)
2014-11-26 15:20:08

SHC (24 views)
2014-11-25 12:00:59

SHC (24 views)
2014-11-25 11:53:45

Norakomi (26 views)
2014-11-25 11:26:43

Gibbo3771 (23 views)
2014-11-24 19:59:16

trollwarrior1 (36 views)
2014-11-22 12:13:56

xFryIx (75 views)
2014-11-13 12:34:49

digdugdiggy (52 views)
2014-11-12 21:11:50
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!