views:

423

answers:

4

Hello, I'm wondering if anyone has/knows of any examples of networking code integrated with OpenGL.

Basically, I need to send position coordinates of something over a network to my OpenGL display... which would then draw the object at the correct position.

The problem I'm having is integrating my UDP code with the game. I basically have a loop which constantly updates/draws. I originally thought that I could have the UDP code in a separate thread and just update the shared coordinates. However, if my draw functions read position before the network code is done writing the new position then I have a race condition and invalid data... however I can't make the Update or Draw loops block...

I'm not sure if I should be using Synchronous or Asynchronous networking code here.

So, any tips on how I can solve this problem? Thanks

+2  A: 

Run network in separate thread, set a flag (atomic write) when new data has arrived and check that in update/draw, and synchronize only then. Then you can use a mutex for the shared data.

The sync part is likely to be very efficient vs. the rest of your code, so it shouldn't affect your draw loop much.

Marcus Lindblom
I like this for the simplicity and it may be what I go with :)
Polaris878
+9  A: 

I assume your game is real-time, since you wrote that you use UDP, and you haven't said otherwise. The general solution to this problem is:

  • To implement buffering of states from the network, which trades latency for stability.
  • To decouple the simulation from the rendering loop.
  • To interpolate between two or more states instead of treating the states as absolute.
  • To implement client-prediction.

    Buffering

    Buffering gives you increased stability, because the simulation will always lag behind the data you recieve. That way, you will never run out of data to use for the simulation on the receiving end. The natural consequence of this is a small increase in latency. With a good heuristic (right-sized buffer), the simulation will never catch up with the buffer, i.e the synchronization will never block unless the communication channel itself becomes unstable over a longer time period. That could be sudden change in bandwidth available, packet loss, severely increased latency, etc. You can also use a synchronization primitive which blocks on a timeout, say 1 ms. When a timeout occurs, you can use extrapolation instead of interpolation (see below), discard updating the missing states all together and only update the stuff handled by the client prediction (if any), or refuse to update anything until you get contact with the remote side again.

    Simulation-loop decoupling

    Your simulation should sample at a constant frequency. By decoupling simulation from rendering, you can render at a higher frequency than your simulation does for sampling. For example, your simulation can run at 30 fps, but you can still render as fast as your graphics card allows, say 300 fps. This means that you need to send less data over the network, but without sacrificing animation quality.

    Save bandwidth, interpolate between samples

    Smoothly interpolate between two samples instead of treating the data as absolute. And example could be player positions in a game. If you simple move or snap a player position using a vector directly, you would need some insanely high sampling frequency which would flood your network. By using linear or cubic interpolation between two or three points respectively, you get a smooth motion, but with very few sample points. This of course requires a point to interpolate to, so it will increase the latency with one delta time, i.e your time step.

    Summary

    EDIT: Ok, so how are these concepts directly related to your problem? Your problem is a design issue; you simply just feed the data directly to your simulation and rendering as they arrive on the wire. Using a mutex whose wait() function has a timeout, fixes your blocking issue, but not the data starvation. Some latency is better than frequent timeouts. This is why I recommend implementing buffering, and decrease the bandwidth usage to a sane level. Only send as much data you need to get a result which is good enough. Responsive-wise and aesthetic-wise.

For excellent insight on game network theory, visit the articles on Glenn Fiedler's blog :
Gaffer on games : networking for game-programmers
Gaffer on games : game-physics

Mads Elvheim
OMG I can't thank you enough... I was actually headed in the buffer direction before I started rethinking it again.
Polaris878
You're welcome :-)
Mads Elvheim
+2  A: 

For an example of real-world game that does this, you could of course browse through some suitable Quake sourcecode. Quake 3 Arena uses OpenGL, and uses UDP networking for its multiplayer.

unwind
+1  A: 

Mads' suggestions are very good, so I'll just add this. Along the lines of interpolation, it can be very valuable to do use client-side prediction. Synchronize position and velocity (and optionally acceleration) rather than just position. As an additional improvement, when an update arrives that indicates that the current position is in error, interpolate to the new correct position over a few frames rather than snapping directly to it.

That way the client can continue to predict object positions even if server updates are infrequent. If your objects move smoothly, then this can work very well. This is the method that most mutliplayer games use. Updates can be made only a few times per second (to conserve bandwidth) and variable network lag can be handled.

Alan