6
\$\begingroup\$

I'm writing a small game using OpenGL.

I'm implementing basic networking in this game and I'm facing a problem.

I have a thread in my client socket class that check for available data, when there are data I raise an event like this :

immutable int len = this.m_socket.receive(data);

if(len > 0)
{
    this.m_onDataEvent(data);
}

Then on my game class, I have a function that handle and parse data like this :

switch(msgId)
{
    case ProtocolID.CharacterData:

        // Load terrain with opengl, character model....

Im not able to call opengl functions because my opengl context is created from a different thread.

But I really don't know how I can solve this problem, I tried Google but it's really hard to find a solution.

I'm using D programming language if it can help.

\$\endgroup\$

2 Answers 2

10
\$\begingroup\$

You're right on track with using the context only from a single thread. You can only use an OpenGL context from one thread at a time, but you can do everything else in another thread.


The simplest way to take advantage would be to do all the loading as you normally would, but send some kind of "event" back to your main thread to actually call the OpenGL upload with everything already prepared.


Once you have that working you can take it a step further by introducing asynchronous DMA mappings using a functions like glMapBuffer. In this case, you will still pass an "event" to your render thread to do the OpenGL call, but instead of waiting for the upload, you will receive a pointer where OpenGL wants the data, and it doesn't care what thread puts it there.

It might go like this: You get your network event, send a "buffer request event" to your main render thread. Still working from the network thread, continue loading / preparing your data and then when it's ready for upload, wait for the glMapBuffer pointer back from the OpenGL thread, copy the data into it and then send ... another ... event back to your renderer to tell it "The data is there, do glUnmapBuffer and set up other GL stuff now"

\$\endgroup\$
2
  • 2
    \$\begingroup\$ You can share OpenGL buffer objects between contexts and assign each thread its own context. That allows you to create and manage buffer objects without any interaction with the drawing thread, but it has one significant problem -- each context has its own unsynchronized command stream. You would have to insert fence sync objects into the command queue or call glFinish (...) and wait for the worker threads to finish writing data before using the data in the drawing thread. Mapping the memory and modifying it in the worker thread like you suggest is by far the simpler thing to implement. \$\endgroup\$ Commented May 29, 2014 at 23:38
  • \$\begingroup\$ Not to mention driver bugs galore, I am aware of the multiple context approach but after evaluating it several times while developing realtime engines I've found it to be more trouble than it's worth! \$\endgroup\$
    – MickLH
    Commented May 30, 2014 at 2:18
4
\$\begingroup\$

As it was mentioned by others, it is a safer play to keep the GL context to a single thread and communicate with it via producer/consumer buffers, events, etc. The OpenGL API wasn't really developed with treading in mind.

But I think it is worth mentioning that you can move your existing context between threads. Every windowing system that supports OpenGL has a proper method for it:

Windows: wglMakeCurrent()

MacOS: NSOpenGLContext

OpenGL ES / EGL: eglMakeCurrent()

All the above allow you to bind a context to a thread and then call GL functions normally from that thread.

wglMakeCurrent was historically buggy, I haven't used it for a long time, so I'm not sure how it stands today, but I have found that NSOpenGLContext and eglMakeCurrent work very well.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .