Threads, games and running on all CPU's

[quote=“princec,post:40,topic:55527”]
I’ve never used a profiler before. Interesting. Googling MaskBlit tells me that it’s something to do with image drawing. My guess was that it was because I was calling paint directly in my main loop. I’ll pulled that out and using -Xprof I now get this:

Flat profile of 30.14 secs (2590 total ticks): AWT-EventQueue-0

  Interpreted + native   Method
 52.0%     0  +    26    sun.awt.windows.WInputMethodDescriptor.getNativeAvailableLocales
  8.0%     0  +     4    sun.awt.windows.WInputMethod.createNativeContext
  6.0%     0  +     3    sun.awt.windows.WComponentPeer.nativeHandleEvent
  6.0%     0  +     3    sun.awt.windows.WInputMethod.getConversionStatus
  4.0%     0  +     2    com.sun.media.sound.DirectAudioDevice.nOpen
  2.0%     0  +     1    com.sun.media.sound.DirectAudioDeviceProvider.nGetNumDevices
  2.0%     0  +     1    com.sun.media.sound.PortMixerProvider.nGetNumDevices
  2.0%     0  +     1    sun.awt.windows.WGlobalCursorManager.findHeavyweightUnderCursor
  2.0%     1  +     0    sun.awt.Win32GraphicsConfig.getDevice
  2.0%     0  +     1    sun.awt.windows.WInputMethod.getOpenStatus
  2.0%     1  +     0    sun.awt.windows.WComponentPeer.handleJavaFocusEvent
 88.0%     2  +    42    Total interpreted

PS I haven’t updated the download yet as there’s still something taking up a lot of CPU that I don’t understand. Any idea what getNativeAvailableLocales is or where abouts I should look>

That’s the event queue thread, which according to jvisualvm has negligible cpu impact.

Here’s the tree from ‘Thread-2:’

The culprits are fillPolygon() and sleep(), although sleep() is probably going to be very low or nothing on processors that have trouble keeping up with the polygon filling. (Mine does not)

I’m glad profiling came up as a topic. I wasn’t aware if you actually had something working / proof of concept yet with the initial discussion. On quick further inspection w/ a decompile revealed instantly not best practices… For instance in the drawsea method:


   private void drawSea(Graphics graphics) {
        int i = 0;
        int i2 = (int) this.VarVal[8];
        int i3 = ScreenWidth / ((int) this.VarVal[8]) + 1;
        graphics.setColor(new Color(0, 10, 255, (int) this.VarVal[5]));
        int i4 = 0;
        while (i4 < i3) {
            graphics.fillPolygon(new int[]{i4 * i2, (i4 + 1) * i2, (i4 + 1) * i2, i4 * i2}, new int[]{this.WaterY[i4], this.WaterY[i4 + 1], ScreenHeight, ScreenHeight}, 4);
            i4++;
        }
        graphics.setColor(new Color(0, 0, 0));
        while (i < i3) {
            graphics.drawLine(i * i2, this.WaterY[i], (i + 1) * i2, this.WaterY[i + 1]);
            i++;
        }
    }

Never create objects especially in your rendering loop. That goes for “new Color”, but most importantly in even tighter loops ala the while statement and all of the anonymous array creation. In this case the data inside fillPolygon; you are creating two new arrays repeatedly and they are constant length (4). Simply create these arrays once outside of the method and populate them each time through the loop then call fillPolygon… Tons of GC thrashing will occur with this code.

I don’t have to look at the rest of the code to know there are likely many things to fix and given this initial jaunt you shouldn’t even consider multithreading at this point as you are far from optimum in the “naive” / test implementation.

Since your water is always filling the bottom of the screen (a continuous blob), try building and drawing one big polygon instead of lots of little ones, or one polygon down to the lowest highest WaterY value, and filling in the rest down to the bottom with a fast fillRect(). And yeah, reuse an existing array instead of allocating a new one each time.

@BurntPizza… Hah… Was just about to post this too… :stuck_out_tongue_winking_eye: Knowing the iterations through the loop “(ScreenWidth / ((int) this.VarVal[8]) + 1;)” One can create one large int[] for x & y and then your loop becomes filling the data into the large int[] and then a single call to fillPolygon…

Edit… Also use a single drawPolyline call via filling up an existing large int array. Taking a look at your current game this can run at 60FPS single threaded no problems w/ my guess of around ~20-30% CPU utilization even on low powered boxes probably not exceeding 50% of a single core. As long as you do the proper calculation to have a large enough array for x/y you can reuse these arrays between fillPolygon / drawPolyline.

For kicks I looked at the rest of your code… Looks more or less fine. You’ll want to get rid of new object creation (mostly Color) and bare string creation in methods like drawPlayerInfo / drawOtherInfo (make a cache of strings as necessary). Another thing you can try is to use the VolatileImage API. At least back in my Java2D heyday I used VolatileImages to great effect. This looks like an adequate tutorial: http://content.gpwiki.org/index.php/Java:Tutorials:VolatileImage So you’ll need to change your findImage method and do a little housekeeping in the render loop. Also make your backing drawing buffer a VolatileImage. It’d be interesting to see where performance is at after these changes. And then we can look at the sea calculations and see if there is a way to speed them up if even necessary. It seemingly is grid oriented, so it should be able to devise some sort of fork / join process right in the main game loop.

I don’t think VolatileImage is much better than BufferedImage these days. Of course one can test it, but I don’t remember getting anything out of it last time I tried.

These are not the same things, and it pays to understand the difference. In OpenGL terminology it’s loosely equivalent to the difference between a texture and an FBO (which IIRC is how the GL pipeline backs them). There’s not much point in using a VolatileImage for sprites, etc. as you’ll just be trying to replicate the caching layer already built into BufferedImage.

On the other hand, as @Catharsis says, use a VolatileImage for a back buffer, or use BufferStrategy to manage one for you. Never use a BufferedImage as a backing buffer - rendering into a BufferedImage is never accelerated.

TL;DR use a BufferedImage for assets, use a VolatileImage where you need an off-screen accelerated render target.

Y’all really should be using JavaFX these days and forget all that stuff…

Cas :slight_smile:

This is a long post - sorry. I understand some of the points in the posts above and have implemented many - with varying success. The great news is that frame rates have gone from 20FPS to just over 30 which makes the game playable on the laptop but I still don’t have sound. I’ll try to summarise where I am:


[b]Not creating things in paint[/b] makes sense and I've fixed the code.
Going from [b]multiple large polygons[/b] to multiple smaller polygons with a large fillRect to paint the rest of the sea underneath the waves. I'm not sure why this should improve things but I've done it and it works fine. I've yet to try doing it all in just one call but I'll try this later. I should of kept records of frame rates etc so I could see what improved things and what doesn't.
I tried to call [b]drawPolyLine [/b]but ran into a compiler error I didn't understand (it was late though):
neptune.java:1706: error: cannot find symbol
                og.drawPolyLine( WaterSurfaceX, WaterSurfaceY, w );
                  ^
  symbol:   method drawPolyLine(int[],int[],int)
  location: variable og of type Graphics
1 error

[b]Sound[/b] is still an issue for me. I'm calling very basic stuff (as detailed here) and it's definitely not working on slower processors. I assume it's not threaded internally. I wanted to try TinySound but, in all honesty, I've never used any external libraries and I'm having a right pain trying to work out how I do it. I downloaded the zip. Unzipped it and placed it in a local directory under my main program dir called "kuusisto" - I wasn't sure what to call it or whether I should just have the plain jar in the same dir as my program. I tried setting the CLASSPATH with [i]set CLASSPATH=E:\Dropbox\Projects\neptune\kuusisto\tinysound-1.1.1.jar;%CLASSPATH%[/i] and get the following error:
E:\Dropbox\Projects\neptune>javac neptune.java

E:\Dropbox\Projects\neptune>jar -cfm neptune.jar Manifest.txt *.class *.png *.wav

E:\Dropbox\Projects\neptune>java -jar -Dsun.java2d.noddraw=true neptune.jar
Exception in thread "main" java.lang.NoClassDefFoundError: kuusisto/tinysound/TinySound
        at neptune.main(neptune.java:387)
Caused by: java.lang.ClassNotFoundException: kuusisto.tinysound.TinySound
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 1 more

E:\Dropbox\Projects\neptune>echo %CLASSPATH%
E:\Dropbox\Projects\neptune\kuusisto\tinysound-1.1.1.jar;E:\Dropbox\Projects\neptune\tiny\tinysound-1.1.1.jar;%CLASSPATH%

So it compiled fine but blew up at run time in main() where I call TinySound.init(); If I can get it to work will TinySound offer volume and panning?


I don't understand why [b]bufferedImages[/b] are better or what a volatile Image is. I'm afraid there were too many acronyms and not enough simple words for me to make sense of things. I'm happy to try both but I'd prefer to know why I'm doing it. Is the idea to use BufferedImage's for the sprites and VolatileImage's for the sea and sky?
Thanks for the help so far and thanks for trying to understand this post (I did try to format it but this was the best I could do).

Mike

Don’t touch VolatileImage. Just stick to BufferedImage as it will automatically promote itself behind the scenes into a VolatileImage-alike if it figures out that’s how you’re using it.

Also, why would you be using -Dsun.java2d.noddraw=true? No need for it. Just let Java figure it out.

… or just forget all this crap and use JavaFX. Srsly.

Cas :slight_smile:

er … did you read what I wrote above (read-only vs read-write!) :point:

I’m looking forward to when JavaFX (or Java 8 for that matter!) becomes officially included in an Ubuntu LTS - woot, only 6 more months! :emo:

With my last game I found that java didn’t draw anything in full screen mode unless I had this option. The screen just changed it’s resolution and then stayed black. I just tried running this game without the option and I’m staring at a black screen listening to the game play. I never understood why this happened :frowning:

I understand it’s better to use JavaFX but it’s something new to learn and I can’t even get TinySound to work at the moment :frowning: I’m aiming to write an arcade game for each planet in the system so maybe I’ll try JavaFX for the next one :slight_smile:

Aye, just adding a +1 as it were!

Cas :slight_smile:

That depends! What are you drawing the sea and sky on to? Where is that Graphics object from? If you’re getting the graphics object from a BufferedImage then all rendering is in software - you could potentially get a big speed up from switching to VolatileImages. OTOH, if you’re drawing into a Graphics provided by the screen or a component then that should already be backed by a VolatileImage or otherwise accelerated.

However, if you’re using -Dsun.java2d.noddraw=true you’re going to switch off most of the acceleration capability anyway. You need to work out what’s going on there first! I vaguely recall a similar issue with FSEM. Try getting it all working in a windowed version first, and possibly consider faking full screen on Windows.

I suggest just having a little go at it as an experiment… you’ll be surprised.

Cas :slight_smile:

Except we were not saying the same thing! :wink: You said to never touch VolatileImage. I’m saying if you ever call getGraphics() on a BufferedImage as part of your rendering loop, you should be switching to a VolatileImage. Quite a lot of people seem to misinterpret managed images as meaning that the graphics of a BufferedImage is hardware accelerated - it isn’t!

Where I’ll +1 you back is, if you’re at all bothered about the performance of your rendering loop, you shouldn’t be using Java2D! ;D

Aha I get you, dead right.

Cas :slight_smile:

@mike_bike_kite

[quote]I’m not sure why this should improve things but I’ve done it and it works fine. I’ve yet to try doing it all in just one call but I’ll try this later. I should of kept records of frame rates etc so I could see what improved things and what doesn’t.
[/quote]
Less draw calls the better. I don’t have any specific implementation details behind the scenes with where accelerated Java2D is at these days, but I’d gather the fillPolygon method is generally expensive in regard to setting up the data and drawing (via Direct3D, etc.). Doing it once versus a 100 or 1000 times should make a difference.

You are correct to measure and only accept a change if things improve. Just be careful what you are measuring at times as micro-benchmarks are not always accurate.

[quote]I tried to call drawPolyLine but ran into a compiler error I didn’t understand (it was late though)
[/quote]
No capital L… drawPolylinehttp://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#drawPolyline(int[],%20int[],%20int)

[quote]So it compiled fine but blew up at run time in main() where I call TinySound.init(); If I can get it to work will TinySound offer volume and panning?
[/quote]
You need to include it on the classpath when using javac and when running the main jar file. Not the system classpath ala %CLASSPATH%, but the runtime classpath when invoking javac and java. It’s the command line option -cp or -classpath. If you have the tinysound.jar in the same directory as the base of your source code you might be able to use:
javac -cp ./tinysound.jar neptune.java
java -cp ./tinysound.jar -jar neptune.jar

It might need to be:
java -cp ./neptune.jar;./tinysound.jar neptune

BTW instead of using javac perhaps use an IDE. My long time favorite is IntelliJ Idea and there is a free community edition: https://www.jetbrains.com/idea/download/

I know your code is all in a single file (we can talk about that later… ;P), but always compiling and running by command line is no fun… There are so many useful aspects of using an IDE for development especially when we get to refactoring your code… :stuck_out_tongue:


As far as the peanut gallery at JGO goes here re: volatile images I don’t think other folks were looking at the decompiled source code, but will give opinions… RE: here is the line in your init method…

this.og = this.OffscreenImage.getGraphics();

right after you create a BufferedImage for OffscreenImage. More or less the general advice you get around JGO is often “defensive” in nature. Last time I heavily worked with Java2D was from '02 to '08 and back then the VolatileImage API was the way to go. It’s not clear at which point, re: JDK 6/7/8, BufferedImages get accelerated more or what exactly is the promotion strategy or how translucent images are handled. Using VolatileImage API was very important for translucent images back in the day. If you are trying to support older computers with older versions of Java even Java 5 (1.5) then VolatileImages may be a win. Again measure. The code needed to load and verify VolatileImages is in the tutorial I linked to previously. It’s going to add a slightly finicky aspect to your render loop, but one worth measuring especially on any older machines you are testing on…

[quote]I don’t understand why bufferedImages are better or what a volatile Image is. I’m afraid there were too many acronyms and not enough simple words for me to make sense of things. I’m happy to try both but I’d prefer to know why I’m doing it. Is the idea to use BufferedImage’s for the sprites and VolatileImage’s for the sea and sky?
[/quote]
BufferedImages are better in general because there is less hand holding involved as a developer. By creating a volatile image you are explicitly creating an accelerated image on the GPU that isn’t managed. On some platforms like Windows this image can get stale and you need to reload it; all of this is described in the tutorial I linked to.

I’ve change the sea stuff so only one call is made to draw the sea all at once. I think the biggest benefit of this is that the array can be built completely outside the draw code.

Thanks for pointing out the drawPolyline mistake - I could of stared all day at that and never seen it :frowning:

I still can’t get this damn library to work (it isn’t the libraries fault, it’s mine for just not grokking it).


# 2 files in my directory
# neptune.jar
# tinysound-1.1.1.jar

# in the example code the developer does the following imports (assume I need them to?)
import kuusisto.tinysound.Music;
import kuusisto.tinysound.Sound;
import kuusisto.tinysound.TinySound;

# as well as the above I have the following in my main() method
TinySound.init();
Sound coin = TinySound.loadSound("doh.wav");
coin.play();
TinySound.shutdown();

# I tried the following to compile
javac -cp ./tinysound-1.1.1.jar neptune.java
jar -cfm neptune.jar Manifest.txt *.class *.png *.wav
java -cp ./tinysound-1.1.1.jar -jar -Dsun.java2d.noddraw=true neptune.jar

# got the following error when trying to run
E:\Dropbox\Projects\neptune>java -cp ./tinysound-1.1.1.jar -jar -Dsun.java2d.noddraw=true neptune.jar
Exception in thread "main" java.lang.NoClassDefFoundError: kuusisto/tinysound/TinySound
        at neptune.main(neptune.java:421)
Caused by: java.lang.ClassNotFoundException: kuusisto.tinysound.TinySound
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 1 more

Will try the image stuff after I get the sound working

Mike