Thread safe tips?

I have a separate thread for my gameloop locked at 150hz, and VSync turned on so draw() runs at the monitors refresh rate. This works great for keeping the gameplay stable, responsive, with smooth graphics.

I'm a java noob, I've tried many different mixes of using synchronized (), volatile, CopyOnWriteArrayList, I can't seem to avoid getting random crashes on my gamethread. Things like modifying an arraylist or pshape in the gameloop and accessing it for drawing seem to cause problems. The errors vary but it's usually complaining about not finding a vertex or arraylist entry it expected to find or java.util.ConcurrentModificationException for arraylists in the gameloop, EmpyStackException when using resetMatrix() on a pshape from inside my draw loop. Sometimes it manifests as drawing corrupted versions of my PShapes.

What's the recommended method to go about doing this and keeping it safe? Sometimes I can play my game for 10+ minutes without a problem, sometimes it will lock up right away several times in a row.

https://indielm.itch.io/platformbattler here's the game, it was made very quick for a jam so I'd need to clean up the code before sharing it.

Tagged:

Answers

  • My best advice to you at this stage is not too use threading. Either use delta time (change between frames) or update logic multiple times per draw(). Threading is hard! You'll get a lot of advice on here that might not work correctly, and likely won't fix your issue, and probably won't perform any better than doing this single threaded in the first place. Don't prematurely optimize as they say - get it working properly, then figure out whether you have a performance issue.

    One problem you have is that with the issues you're talking about you'd probably want to synchronize your update()? and draw() methods, because otherwise you'll end up drawing in the middle of changing state. But if you just do that, they're effectively waiting for each other, and therefore you get no benefit from threading, just the overhead.

    You want to draw the state at the end of completing the game loop before the game loop starts calculating the next state. There are various approaches to doing that in parallel, but they all involve needing a way to calculate the updates in one step while drawing the previous - eg. perhaps two logic states A & B, with the ability to update A while drawing B, and swapping to draw A while updating B.

    Note also that depending on the renderer, doing things with PShape, etc in a different thread might cause issues no matter what you do.

  • edited April 2018

    The only way to get a consistant timed game loop I have found is with a thread.

    Using system.nanoTime inside draw() to attempt to time a game updates, even if draw() is running at 1000 fps, has significantly inconsistent speed.

    The top left shows a graph of FPS over time. My game loop runs a very steady 150FPS while the draw loop varries more than one would expect. This is with vsync being set with pgl.gl.setSwapInterval(); Without VSync, the draw framerate varies even more wildly.

    frameRate() seems to be more of a suggestion than a reliable setting. For my computer frameRate(60) actually produces 62FPS, and it's different on other computers.

    I am interested in figuring out how to do it properly, I'm trying to make a reusable template for game prototyping, not just fix this game I posted.

  • Which is why you either run as many updates as required to get to the current frame rendering time, or use delta time (the actual ms time between frames) to calculate how much to apply your logic updates. Using threads gives you nothing over that, and unless well designed means you're just drawing part of one logic frame and part of the next, which is even worse.

    Also check out the Java Gaming forum - there are often discussions around this sort of thing there, including some people making professional games in Java. You'll likely get exactly the same advice! :-)

  • edited April 2018

    Alright I give, I switched over to a threadless fixed rate interpolation implementation like described here http://www.java-gaming.org/topics/game-loops/24220/view.html It wasn't as hard as I thought and seems to be smooth enough.

    I still would rather have a high frequency fixed gameloop in a separate thread and figure out how to manage threads properly.

  • It's only worth having threads if you have a task that can be properly parallelized. So, maybe a useful learning exercise, but you need to work out what bits of your design are dependent on each other, and which can happen independently.

    So, if you're doing lots of calculations in your updates, it might be worth parallelizing those, but they all have to complete before you draw()

    It's only worth parallelizing update() and draw() if you can design in such a way that update() can change data at the same time as draw() is rendering data (the cause of your earlier problems). One way is to have two complete state models - both update() and draw() can read from one model at the same time, while update() is writing to the other model - at the end of draw() you synchronize and switch the models, and so on.

    There's lots of resources online around parallelizing game loops. Good to get your head around, if not easy reading. :-)

  • So, I know, this is not my discussion, but I have to ask, because I used this many times and don't know, whether it is a good idea or not.

    I have 3 Game class instances drawGame, calcutationGame and updateGame. After calculationGame has finished calculating next state of the game, updateGame copies it, unless drawGame is currently using the updateGame. In that case it waits and then copies it. After drawGame has finished drawing, it copies updateGame into itself. The calcutations are usually very complex, but there are not many variables that need to be copied. The calculationGame and drawGame run in parallel and the updateGame is there only so that the two don't have to wait for each other.

    So my question is am I doing it right? Do I get any benefit from doing it this way?

Sign In or Register to comment.