Sign in to follow this  

[java] Optimizing a Tile Based Engine

This topic is 4355 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Well after many trials and tribulations. I've got a map loaded that was created in Tile Studio and displayed on the screen. I'm running my game in an 800 x 600 window. When I saw the map displayed triumph was mine! I began to wonder how efficient it was. I quickly made some code to display the frame rate. To my disgust I was only getting 50 frames per second! How can this be? I had realized java was sluggish in some aspects. However, I'm supposedly using hardware acceleration I thought to myself. I find it silly that something so simple could be so painfully slow on my system. P4 2.4ghz 512 MB RDRAM RADEON 9700 I'm asking if anyone sees any inherently inefficent methods in my code. I'll do my best to explain the bits and pieces: Here's where I first call the draw map function in my game loop. asge is a wrapper class for graphics. It simplifies my main game loop, especially if I decide to reimplement the graphics engine. mainMap is a Map object. More on that later...
asge.draw(mainMap, 0 , 0);
Here's the ASGE object's draw function. It's simple enough just passes a graphics object and some coordinates to the map object.
    public void draw(Map map, int x, int y)
    {
        map.draw(g, x , y); //draw the map starting at x and y
    }
Here's the meat and potatoes, the map objects draw function:
    public void draw(Graphics g, int xStart, int yStart)
    {
        //find what tiles will be visible starting in the top left of screen
        startXTile = xStart / 32;
        startYTile = yStart / 32;
        //now we need to get the remainder of this in order to offset these tiles the proper amount
        x_off = startXTile & 31;
        y_off = startYTile & 31;
        
        for(int x = startXTile; x < screenTileWidth; x++)
        {
            for(int y = startYTile; y < screenTileHeight; y++)
            {
                tiles.drawTile(mapData[height * y + x],g, x * 32 + x_off, y * 32 + y_off);
                //System.out.println("mapData[" + (height * y + x) + "]: " + mapData[height * y + x]); 
            }
        }
    }

It only draws enough tiles to fill the screen. Well you may wonder if there's a bottleneck in this tiles object you just spotted. Here ya go:
    public void drawTile(int id, Graphics g, int x, int y)
    {
        allTiles[id].draw(g, x ,y);
        //System.out.println("id: " + id); //debug
    }

Now I fill this allTiles[] array in this fashion:
        while(line != null)
        {
            Image image = null; //image object to store what we load from a file
            System.out.println("Line: " + line);
            try
            {
                image = ImageIO.read(getClass().getResource(line)); //load the image file to image
            }
            catch(IOException e)
            {
                System.err.println("Couldn't load " + line);
            }
            Sprite tileSprite = new Sprite(image);
            addTile(tileSprite);
            System.out.println("Added sprite " + line);
            
            line = tileConfigReader.getNextLine(); //contains filename
        }

    }

It's my understanding that image objects will automatically be accelerated in Java 1.5. Maybe I'm incorrect? I've been looking into and nothing has turned up on Sun's site so far. Thanks in advance, -Kris

Share this post


Link to post
Share on other sites
Are you creating an image out of all the tiles, and then drawing that to the screen? ie. are you allowing the image to be blitted to the screen at once?

And are you only drawing the parts that changed?

Share this post


Link to post
Share on other sites
Quote:
Original post by Argus2
Are you creating an image out of all the tiles, and then drawing that to the screen? ie. are you allowing the image to be blitted to the screen at once?

And are you only drawing the parts that changed?


I'm drawing the the graphics buffer, g, in my ASGE object. To display it to the screen I flip the buffer.

I've considered drawing a very large map segment, then just copying the displayable part of that to the graphics buffer. As the player reaches the edge of the very large map segment, I'd create another one in memory, so they would seamlessly be displayed. That may be a bad explanation. Basically I'd draw it in chunks rather than tiles.

I redraw the screen each time, disregarding whether it has changed or not. The idea behind this is that because I'm going to have the camera constantly follow the player the only time there will not be a change is when the player stops. The player will only stop in non-critical moments so I've seen no use in implementing the feature yet. I'm more concerned about the frame rate during battle. Where there should be constant movement.


-Kris

Share this post


Link to post
Share on other sites
Quote:
Original post by darookie
Here's what I found. I'm not a Java expert so that link might be useless to you.

Hope it helps anyway,
Pat.


This link did mention something that I did not realize and may potentially be very helpful. When I launch my game I may need to give the option "-Dsun.java2d.opengl=true"

I'll have to try that too.

-Kris

Share this post


Link to post
Share on other sites
Well I started up my graphics engine with the opengl arguments I posted earlier. It ran at a painfully slow frame rate. It couldnt have been faster than .5 fps. In fact, my timer read 0 fps.

When I closed the application I got this nasty error:

#
# An unexpected error has been detected by HotSpot Virtual Machine:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x692f81b7, pid=4000, tid=3516
#
# Java VM: Java HotSpot(TM) Client VM (1.5.0_04-b05 mixed mode)
# Problematic frame:
# C [atioglxx.dll+0x2f81b7]
#
# An error report file with more information is saved as hs_err_pid4000.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#


So I looked at the log file:

#
# An unexpected error has been detected by HotSpot Virtual Machine:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x692f81b7, pid=4000, tid=3516
#
# Java VM: Java HotSpot(TM) Client VM (1.5.0_04-b05 mixed mode)
# Problematic frame:
# C [atioglxx.dll+0x2f81b7]
#

--------------- T H R E A D ---------------

Current thread (0x0082a0b0): VMThread [id=3516]

siginfo: ExceptionCode=0xc0000005, reading address 0x0000816c

Registers:
EAX=0x00000000, EBX=0x00000000, ECX=0x13030b20, EDX=0x00000001
ESP=0x0abcfc5c, EBP=0x00000000, ESI=0x1d223480, EDI=0x1d223200
EIP=0x692f81b7, EFLAGS=0x00010206

Top of Stack: (sp=0x0abcfc5c)
0x0abcfc5c: 13030b48 00000000 13030b20 00000000
0x0abcfc6c: 693f280d 692f836f 1d223200 13030b20
0x0abcfc7c: 00000000 00000000 0abcfcc4 69000000
0x0abcfc8c: 00000001 13030b48 13030b20 692f856c
0x0abcfc9c: 00000002 692f6a23 692f6b27 693f4862
0x0abcfcac: 69000000 00000000 00000001 00000000
0x0abcfcbc: 0abcfcd8 00183140 0abcfce4 7c9011a7
0x0abcfccc: 69000000 00000000 00000001 00183140

Instructions: (pc=0x692f81b7)
0x692f81a7: 00 eb c8 33 c0 5d 59 c2 0c 00 53 56 8b 74 81 28
0x692f81b7: 8b 8d 6c 81 00 00 33 db 85 c9 89 74 24 10 7e 19


Stack: [0x0ab90000,0x0abd0000), sp=0x0abcfc5c, free space=255k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [atioglxx.dll+0x2f81b7]

[error occurred during error reporting, step 120, id 0xc0000005]

VM_Operation (0x1daaf808): exit, mode: safepoint, requested by thread 0x00842910


--------------- P R O C E S S ---------------

Java Threads: ( => current thread )
0x00842910 JavaThread "AWT-EventQueue-0" [_thread_blocked, id=1016]
0x00838170 JavaThread "AWT-Shutdown" [_thread_blocked, id=3164]
0x0082b360 JavaThread "Java2D Disposer" daemon [_thread_blocked, id=2748]
0x00830d80 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=2844]
0x0082fe70 JavaThread "CompilerThread0" daemon [_thread_blocked, id=2272]
0x0082c110 JavaThread "Finalizer" daemon [_thread_blocked, id=708]
0x0082cde0 JavaThread "Reference Handler" daemon [_thread_blocked, id=3844]
0x00822280 JavaThread "main" [_thread_blocked, id=2440]

Other Threads:
=>0x0082a0b0 VMThread [id=3516]

VM state:at safepoint (shutting down)

VM Mutex/Monitor currently owned by a thread: ([mutex/lock_event])
[0x008227d0/0x00001720] Threads_lock - owner thread: 0x0082a0b0

Heap
def new generation total 576K, used 475K [0x02b90000, 0x02c30000, 0x03070000)
eden space 512K, 91% used [0x02b90000, 0x02c04b38, 0x02c10000)
from space 64K, 13% used [0x02c20000, 0x02c22318, 0x02c30000)
to space 64K, 0% used [0x02c10000, 0x02c10000, 0x02c20000)
tenured generation total 1408K, used 893K [0x03070000, 0x031d0000, 0x06b90000)
the space 1408K, 63% used [0x03070000, 0x0314f7f0, 0x0314f800, 0x031d0000)
compacting perm gen total 8192K, used 5210K [0x06b90000, 0x07390000, 0x0ab90000)
the space 8192K, 63% used [0x06b90000, 0x070a6888, 0x070a6a00, 0x07390000)
No shared spaces configured.

Dynamic libraries:
0x00400000 - 0x0040c000 C:\Program Files\Java\jre1.5.0_04\bin\javaw.exe
0x7c900000 - 0x7c9b0000 C:\WINDOWS\system32\ntdll.dll
0x7c800000 - 0x7c8f4000 C:\WINDOWS\system32\kernel32.dll
0x77dd0000 - 0x77e6b000 C:\WINDOWS\system32\ADVAPI32.dll
0x77e70000 - 0x77f01000 C:\WINDOWS\system32\RPCRT4.dll
0x77d40000 - 0x77dd0000 C:\WINDOWS\system32\USER32.dll
0x77f10000 - 0x77f57000 C:\WINDOWS\system32\GDI32.dll
0x77c10000 - 0x77c68000 C:\WINDOWS\system32\MSVCRT.dll
0x76390000 - 0x763ad000 C:\WINDOWS\system32\IMM32.DLL
0x629c0000 - 0x629c9000 C:\WINDOWS\system32\LPK.DLL
0x74d90000 - 0x74dfb000 C:\WINDOWS\system32\USP10.dll
0x6d640000 - 0x6d7c9000 C:\Program Files\Java\jre1.5.0_04\bin\client\jvm.dll
0x76b40000 - 0x76b6d000 C:\WINDOWS\system32\WINMM.dll
0x5cd70000 - 0x5cd77000 C:\WINDOWS\system32\serwvdrv.dll
0x5b0a0000 - 0x5b0a7000 C:\WINDOWS\system32\umdmxfrm.dll
0x6d280000 - 0x6d288000 C:\Program Files\Java\jre1.5.0_04\bin\hpi.dll
0x76bf0000 - 0x76bfb000 C:\WINDOWS\system32\PSAPI.DLL
0x6d610000 - 0x6d61c000 C:\Program Files\Java\jre1.5.0_04\bin\verify.dll
0x6d300000 - 0x6d31d000 C:\Program Files\Java\jre1.5.0_04\bin\java.dll
0x6d630000 - 0x6d63f000 C:\Program Files\Java\jre1.5.0_04\bin\zip.dll
0x6d000000 - 0x6d167000 C:\Program Files\Java\jre1.5.0_04\bin\awt.dll
0x73000000 - 0x73026000 C:\WINDOWS\system32\WINSPOOL.DRV
0x774e0000 - 0x7761d000 C:\WINDOWS\system32\ole32.dll
0x5ed00000 - 0x5edcc000 C:\WINDOWS\system32\opengl32.dll
0x68b20000 - 0x68b40000 C:\WINDOWS\system32\GLU32.dll
0x73760000 - 0x737a9000 C:\WINDOWS\system32\DDRAW.dll
0x73bc0000 - 0x73bc6000 C:\WINDOWS\system32\DCIMAN32.dll
0x6d240000 - 0x6d27d000 C:\Program Files\Java\jre1.5.0_04\bin\fontmanager.dll
0x755c0000 - 0x755ee000 C:\WINDOWS\system32\msctfime.ime
0x69000000 - 0x694c0000 C:\WINDOWS\system32\atioglxx.dll
0x7c9c0000 - 0x7d1d5000 C:\WINDOWS\system32\shell32.dll
0x77f60000 - 0x77fd6000 C:\WINDOWS\system32\SHLWAPI.dll
0x773d0000 - 0x774d2000 C:\WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.2180_x-ww_a84f1ff9\comctl32.dll
0x5d090000 - 0x5d127000 C:\WINDOWS\system32\comctl32.dll
0x6d190000 - 0x6d1bf000 C:\Program Files\Java\jre1.5.0_04\bin\cmm.dll
0x6d3c0000 - 0x6d3df000 C:\Program Files\Java\jre1.5.0_04\bin\jpeg.dll
0x6d4c0000 - 0x6d4d3000 C:\Program Files\Java\jre1.5.0_04\bin\net.dll
0x71ab0000 - 0x71ac7000 C:\WINDOWS\system32\WS2_32.dll
0x71aa0000 - 0x71aa8000 C:\WINDOWS\system32\WS2HELP.dll
0x6d4e0000 - 0x6d4e9000 C:\Program Files\Java\jre1.5.0_04\bin\nio.dll

VM Arguments:
jvm_args: -Dsun.java2d.opengl=true
java_command: Game

Environment Variables:
CLASSPATH=C:\Program Files\Java\jre1.5.0_04\lib\ext\QTJava.zip
PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Program Files\ATI Technologies\ATI Control Panel;C:\j2sdk1.4.2_06\bin;C:\Program Files\QuickTime\QTSystemUSERNAME=Kris
OS=Windows_NT
PROCESSOR_IDENTIFIER=x86 Family 15 Model 2 Stepping 7, GenuineIntel



--------------- S Y S T E M ---------------

OS: Windows XP Build 2600 Service Pack 2

CPU:total 1 family 15, cmov, cx8, fxsr, mmx, sse, sse2, ht

Memory: 4k page, physical 523244k(73564k free), swap 1279756k(846388k free)

vm_info: Java HotSpot(TM) Client VM (1.5.0_04-b05) for windows-x86, built on Jun 3 2005 02:10:41 by "java_re" with MS VC++ 6.0



It looked to me like it may only be useful to a Sun developer. I posted it in hopes that someone will make some sort of heads or tails of it.

Share this post


Link to post
Share on other sites
The one thing you are missing is that you are blitting BufferedImages, which is what ImageIO returns. BufferedImages are painfully slow to blit, as a general rule.

After loading your BufferedImages, you need to convert them to screen native format. This means getting your component and calling createCompatibleImage on it to create an appropriate tile-sized image, blitting the tile to it, and then that's what you store in your tiles array, not the BufferedImages.

You'll find that these are about 5x as fast to blit.

You can get even cleverer by using VolatileImages but that's getting kinda fiddly and there's some tutorials floating around on Sun's site explaining them.

I hope also that you are blitting using a BufferStrategy rather than in some paint() command.

Cas :)

Share this post


Link to post
Share on other sites
You might want to try a different method for loading your images. ImageIcon has a getImage() method that returns a simple Image. So, you might want to try something like this:


URL url = getClass().getResource();
Image image = new ImageIcon(url).getImage();


- Rob

Share this post


Link to post
Share on other sites
In fact, to twiddle with your code a bit, we can get the whole process down to only three lines within the while loop:


while(line != null) {
URL url = getClass().getResource();
addTile(new Sprite(new ImageIcon(url).getImage());
line = tileConfigReader.getNextLine();
}


However, this doesn't handle error-checking. All you would have to do there is add a check for the returned Image being null.

- Rob

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
>BufferedImages are painfully slow to blit, as a general rule.

You mean unmanaged BufferedImages ;)

ImageIO returns managed images by default if your using 1.5+ (otherwise you need that copy step). The images should be either opaque or using bitmask transparency. Additionally the pixel size of the drawn images shouldn't exceed 2^16 pixels. (If you're using getSubimage you still reference the original image. So, instead create a new compatible image and draw a specific part of the bigger image over.)

I get about 280fps in 640x480x32 using 32x32 tiles (K7 500 + ati 9100). Well, I'm using lwjgl, but java2d should be able to reach about 66% of the speed on windows. Btw the java2d opengl pipeline is *slower* for this kind of stuff. Try -Dsun.java2d.noddraw=true instead.

Share this post


Link to post
Share on other sites
The general rule is that only 1.4 is available, so unmanaged buffered images are what you've got. So why not write code that works everywhere and simply blit the bufferedimages into compatible images and that'll be adequate for most tasks. For proper speed you can use VolatileImages but they are a bit of a pain to manage manually. You should in any case be using a VolatileImage with a BufferStrategy as a backbuffer.

The OpenGL pipeline is notoriously unreliable so relying on it again leaves you with the worst case scenario. The whole thing about using Java is this write-once-run-anywhere idea and that means you have to pick a lowest common denominator and aim for it. So that means no OpenGL pipeline.

Cas :)

Share this post


Link to post
Share on other sites

This topic is 4355 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this