sp0wner
Junior Newbie
|
 |
«
Posted
2012-08-22 19:11:32 » |
|
I am developing a full screen exclusive mode isometric tile game from scratch -i.e. I don't use a game engine- and I have a problem when it comes to drawing "BufferedImages" and "Images" on screen. (I am using active rendering) The default FPS value is set to 80, but when the map tiles are drawn on screen the FPS value drops at 60. The map size (=array's size) at the moment is 100 tiles (10 width * 10 height). If I change that to 100*100=1000 tiles then the FPS value drops to 0 and the game cannot be played. The code I use to load buffered images is the following: BufferedImage image= ImageIO.read(new File(file)); while the code to load Images is the following: Image image= ImageIO.read(new File(file)); where file is a string containing the path to the image. The size of the tile-image that it loaded is ~3KB. Do you have any idea why does the FPS value drop? Or a different way to suggest on how to draw images without occuring problems to the FPS value? Thanks in advance, sp0wner
|
|
|
|
|
teletubo
|
 |
«
Reply #1 - Posted
2012-08-22 19:13:28 » |
|
You should provide the code you're using to draw your images if you want to know what is wrong with it.
|
|
|
|
sp0wner
Junior Newbie
|
 |
«
Reply #2 - Posted
2012-08-22 19:38:37 » |
|
There are many draw functions used in the game, so here I am posting the one I think is the most responsible one. The code to draw the "floor" of the map is the following: public void draw(Graphics g){ Graphics2D g2d = (Graphics2D) g;
//draw Floor for (int i = 0; i < floorAl.size(); i++){ g2d.drawImage(floorAl.get(i).getImage(), null, floorAl.get(i).getX(), floorAl.get(i).getY()); } } floorAl is an ArrayList<Tile>, where all the Tile objects representing the floor are contained along with the their x,y positions and their BufferedImages. The Tile class makes use of another class called BufferedImageLoader which is responsible for loading any BufferedImage passed as a parameter to the constructor.
|
|
|
|
|
Games published by our own members! Check 'em out!
|
|
davedes
|
 |
«
Reply #3 - Posted
2012-08-22 20:04:30 » |
|
Java2D is slow; you will probably not be able to achieve (reliable) full-screen performance using active rendering (i.e. clearing the screen every frame).
Instead I would suggest LibGDX, Slick2D, RabbitEngine, or straight LWJGL.
e.g. Rendering a full-screen tiled map (32x32 tiles) on my iMac (3.06 GHz Intel Core 2 Duo; NVIDIA GeForce 9400) leads to ~25 FPS at 1920x1080. The same runs at ~150 FPS using Slick.
|
|
|
|
65K
|
 |
«
Reply #4 - Posted
2012-08-22 20:24:41 » |
|
Impossible to spot without the complete source. Maybe incompatible image formats. Use a profiler. In any case, 10x10 tiles is really nothing. I got a frame rate of 1400 when drawing 16x12=192 tiles using volatile images and Java2d.
|
|
|
|
davedes
|
 |
«
Reply #5 - Posted
2012-08-22 20:33:38 » |
|
Impossible to spot without the complete source. Maybe incompatible image formats. Use a profiler. In any case, 10x10 tiles is really nothing. I got a frame rate of 1400 when drawing 16x12=192 tiles using volatile images and Java2d.
The OP says the problem occurs with 100x100 tiles. By today's standards that isn't much, but it's too much for Java2D's standards. I'd love to see a self-contained example using volatile images. Could you post some code?
|
|
|
|
65K
|
 |
«
Reply #6 - Posted
2012-08-22 21:00:38 » |
|
Okay, I just took the written 1000 tiles, not 10000. Which sums up to a lot for 60fps indeed. I would use bigger tiles in any case... I don't have an extractable example, but you have to take care to create compatible images, not to mess with pixels and get images unmanaged.
|
|
|
|
Ultroman
|
 |
«
Reply #7 - Posted
2012-08-22 21:16:39 » |
|
You really shouldn't keep BufferedImages in your Tiles, but rather a String-identifier. Then have a HashMap of BufferedImages with a String as key, so you can do something like: g2d.drawImage(imageMap.get(floorAl.get(i).getImageString()), null, floorAl.get(i).getX(), floorAl.get(i).getY());
I don't know if that'll help your FPS, though. Can you post your Tile-class in the pastebin and provide a link? Are you making sure that the images you load are compatible with the graphicscontext?
|
- Jonas
|
|
|
matheus23
|
 |
«
Reply #8 - Posted
2012-08-22 21:55:43 » |
|
Or even better:
have something like a "manager"-class managing all the Images (so for example "ImageManager").
Then the ImageManager holds a hash-map of all the Images you have in your res/ folder (or whatever), and then puts them into a HashMap<String, BufferedImage>. The String (the Key), will have the name of the Image loaded (for example "grass.png" / "dirt.png" / "characters/mike.png" or whatever).
Then in the constructor of the Tile you ask the ImageManager for a reference of the Image, called (for example) "grass.png", and save it in a member field.
Tl;Dr: same as Ultroman, but you won't need to "load" the BufferedImage from the HashMap everytime you want to draw it, which is bad (or slow), because it needs to "search" through the map with an algorithm. The more Images you have, the longer it will take to get it from the HashMap. Just take a reference to the Image, which is then actually stored in another class and only loaded once.
|
|
|
|
loom_weaver
|
 |
«
Reply #9 - Posted
2012-08-23 00:10:12 » |
|
The OP says the problem occurs with 100x100 tiles. By today's standards that isn't much, but it's too much for Java2D's standards.
100x100 tiles is nothing even for Java2D. That being said we'd need to see the code.
|
|
|
|
|
Games published by our own members! Check 'em out!
|
|
|
|
Ultroman
|
 |
«
Reply #11 - Posted
2012-08-23 01:20:56 » |
|
same as Ultroman, but you won't need to "load" the BufferedImage from the HashMap everytime you want to draw it, which is bad (or slow), because it needs to "search" through the map with an algorithm. The more Images you have, the longer it will take to get it from the HashMap. Just take a reference to the Image, which is then actually stored in another class and only loaded once.
What is the difference from your method to mine, except for the dedicated class? My suggestion was essentially the same: "have a HashMap of BufferedImages with a String as key". I've done somewhat what you said for my game, and put my static HashMap of images in a ResourceHolder class, so I can access it from every class that has a reference to the ResourceHolder, like my Creator-class and my draw-method in the Game-class. Do you mean you give the Tile-class a BufferedImage as a parameter in the constructor from the Tile-class? So you're linking the image to the Tile-class instead of looking it up every update? Just to clarify; this could come off as a whiney post, but I'd just love to know how much I can gain from changing to your proposed method compared to mine. I was under the impression HashMap lookups were pretty instantaneous.
|
- Jonas
|
|
|
sp0wner
Junior Newbie
|
 |
«
Reply #12 - Posted
2012-08-23 04:33:51 » |
|
I think I found a temporary solution to my problem, although any other ideas are more than welcomed. My solution is to load images as ImageIcons. I tried to load a 100x100 map at 80 fps/ups and the results were the game to run at 50 fps and 80 ups.
|
|
|
|
|
Ultroman
|
 |
«
Reply #13 - Posted
2012-08-23 04:54:27 » |
|
I'm checking, updating animations on, and drawing around 3-500 40x40px tiles at a steady 60fps, and this only takes up about a millisecond per frame/update. There must be something terribly wrong in your code. Are you sure you're not reloading all images each draw or update or something?
I'd recommend doing some timing-debugging on the different parts of your gameloop, to figure out what is taking so much time. Time every event, like update(), draw() and whatever you have.
|
- Jonas
|
|
|
|
|
StumpyStrust
|
 |
«
Reply #15 - Posted
2012-08-23 07:30:34 » |
|
That could be it but even then 1k images should not be that slow. Make sure you are loading images by getting compatible images from the Graphics Environment. You can also specify whether you want transparency or not. Java2D does some software rendering which means that as you scale up screen size you don't get fill rate limited. Also, when it comes to storing images, there is nothing wrong with having a BufferedImage reference in a class. It is good to have a resource management system but keeping a reference is not bad either. You should not deal with ImageIcons. You need to properly load everything. 1 2 3 4 5 6
| GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice gs = ge.getDefaultScreenDevice(); GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage bimage = gc.createCompatibleImage(100, 100, Transparency.OPAQUE); |
|
|
|
|
|
65K
|
 |
«
Reply #16 - Posted
2012-08-23 08:04:56 » |
|
So, of which number are we talking ? 100x100 = 10000*60 = 600000 tiles per second ?
Anyway, check your fps calculation.
Also, I would disable Swing's repainting (setIgnoreRepaint) and not extend JPanel and not make it runnable.
|
|
|
|
davedes
|
 |
«
Reply #17 - Posted
2012-08-23 08:43:27 » |
|
The OP says the problem occurs with 100x100 tiles. By today's standards that isn't much, but it's too much for Java2D's standards.
100x100 tiles is nothing even for Java2D. Please provide some Java2D code that will perform 100 * 100 tiles (e.g. 32x32 pixels or smaller) at 60 FPS on the majority of systems within the past couple years. Until then, no, 100x100 tiles is way too much to expect from Java2D.
|
|
|
|
Damocles
|
 |
«
Reply #18 - Posted
2012-08-23 09:21:24 » |
|
If the rendering is too slow, use more memory, eg: precalculate static tiles/backgrounds wich dont change.
If the rendered map image is too large, only rerender the part wich is visible + some border to enable a smooth scrolling.
The problem then is probably then the order of elements (eg dynamic elements must be obstructed by walls) There is a range of solutions. (like a Z Buffer, or a partial rendering) But basically, the floor-tiles can be drawn first anyway, as everything is ontop of them, they cannot obstruct anything.
|
|
|
|
StumpyStrust
|
 |
«
Reply #19 - Posted
2012-08-23 19:26:51 » |
|
I think that java2D does z culling under the hood. They create rectangles and wont draw the intersection of said rectangles. The key here is draw calls. Problem with tile based systems is that if you have 1000 tiles that need to be drawn that is 1k draw calls. Cut down on draw calls as much as possible. All games I have made run fairly well at 1-2k draw calls. fillShape() drawImage() once you pass 5k, just about any system will drop fps like crazy. Size of image has virtually no impact on performance on most systems that have a DGPU.
|
|
|
|
|
matheus23
|
 |
«
Reply #20 - Posted
2012-08-26 20:03:26 » |
|
same as Ultroman, but you won't need to "load" the BufferedImage from the HashMap everytime you want to draw it, which is bad (or slow), because it needs to "search" through the map with an algorithm. The more Images you have, the longer it will take to get it from the HashMap. Just take a reference to the Image, which is then actually stored in another class and only loaded once.
What is the difference from your method to mine, except for the dedicated class? My suggestion was essentially the same: "have a HashMap of BufferedImages with a String as key". I've done somewhat what you said for my game, and put my static HashMap of images in a ResourceHolder class, so I can access it from every class that has a reference to the ResourceHolder, like my Creator-class and my draw-method in the Game-class. Do you mean you give the Tile-class a BufferedImage as a parameter in the constructor from the Tile-class? So you're linking the image to the Tile-class instead of looking it up every update? Just to clarify; this could come off as a whiney post, but I'd just love to know how much I can gain from changing to your proposed method compared to mine. I was under the impression HashMap lookups were pretty instantaneous. Hmmm... Yes. The only difference between your method and mine is the constructor stuff, and removing the lookup from the render method. And I don't know how long a lookup in HashMap takes. I'm really not sure, but I'm sure a field "lookup" is much faster. And since rendering methods are called very very often, I thought that could be a huge difference... But I'm just checking how long a HashMap lookup would take... (I am also pretty sure that HashMap lookup time is dependent on the number of entrys. So it would take much longer if you'd have tons of sprites... and that'd be bad...). Could be possible that would fall into the "premature optimization" bag...
|
|
|
|
Ultroman
|
 |
«
Reply #21 - Posted
2012-08-26 20:23:47 » |
|
Hmmm... Yes. The only difference between your method and mine is the constructor stuff, and removing the lookup from the render method.
And I don't know how long a lookup in HashMap takes. I'm really not sure, but I'm sure a field "lookup" is much faster. And since rendering methods are called very very often, I thought that could be a huge difference... But I'm just checking how long a HashMap lookup would take... (I am also pretty sure that HashMap lookup time is dependent on the number of entrys. So it would take much longer if you'd have tons of sprites... and that'd be bad...).
Could be possible that would fall into the "premature optimization" bag...
The more I think about it, the more I know you're right  I'd hate to find out now that my teachers are full of it, and that HashMaps aren't instantaneous.
|
- Jonas
|
|
|
Ultroman
|
 |
«
Reply #22 - Posted
2012-08-26 20:36:18 » |
|
I'd hate to find out now that my teachers are full of it, and that HashMaps aren't instantaneous.
Well, seems they're halfway right...or something. This is a very interesting read on performance of lists, sets and maps.
|
- Jonas
|
|
|
jonjava
|
 |
«
Reply #23 - Posted
2012-08-26 21:48:57 » |
|
I know this problem very well. You should not draw all the tiles simultaneously, only the ones that are visible. This way you can easily get around 1k visible tiles without much fps drop with simple java2d. At least I don't get any FPS drop while filling the screen with pink 1px height tiles on my netbook in this applet: http://jonjavaweb.orgfree.com/TestApplet.html (press W to insert pink tiles). If you minimize the window where the applet is and maximize it again you get a glimpse of how and how fast the tiles are drawn on the screen. There should be around 50+ tiles per platform that are fit inside the screen + a few more extra to make sure you don't get undrawn tiles when moving the screen. So adding 20 pink tiles to each visible pillar/tile coordinate you should get around 20*50=1k tiles. Then again why you'd need that many 1 pixel height tiles ontop of each other is very rare and performace shouldn't be an issue if you stick to minimum of 8px height tiles, even on larger screen sizes. here's a post discussing a bit more in depth on how to draw only the visible tiles instead of ALL the tiles in the map: http://www.java-gaming.org/topics/drawing-isometric-tiles-inside-a-screen/24922/msg/212780/view.html[EDIT]: Also, make sure you don't load a seperate bufferedImage for every single tile in your game. Similar tiles should share the same bufferedImage. Take a look at this post for more info: http://www.java-gaming.org/topics/loading-multiple-images/24878/msg/212071/view.html
|
|
|
|
Quadro
Senior Newbie 
|
 |
«
Reply #24 - Posted
2012-09-06 03:34:28 » |
|
1) dont draw textures on tiles you can't see, probably the biggest thing here
2) don't overide the paint method(I'm assuming you're doing that, if not disregard this), run your painting in a thread, the performance increase is noticeable, on my machine cpu usage goes from 60 to 20
3) don't use a lot of transparent images
4) limit your framerate, it can mean the difference from your game using 20 cpu or 5, a human can't tell the difference from 100fps and 800fps
also check your code for slowdowns, I always monitor the FPS on games I'm making so the moment I do something where the frames jump from from somwhere over 1000 to 40 I can find what is causing it
|
|
|
|
|
JESTERRRRRR
|
 |
«
Reply #25 - Posted
2012-09-06 05:58:05 » |
|
I seem to remember having this problem, and solving it with Volatile images and bufferstrategy
|
|
|
|
|
|