Hi,
I have recently created an application which can decode video files and then render the videos to the screen. I have done that in DX11, but I now want to do it for OpenGL as well, but I am new to OpenGL (and generally to rendering), but have programmed for many years.
Basically my main thread does the rendering of all the objects, and the decoder is on another thread. I have a class RenderObject which represents anything that can be rendered, and derived from that I have a DoubleBufferSurface class, which has an "active surface" (the one being renderer by the rendering thread) and a "buffer surface" (the one the decoder writes its current frame to). Once the buffer surface is finished being updated, the pointer to the "activeSurface" changes to point to this surface and vice versa, so that the new videoframe will be rendered on the next frame.
This all seems to work well in DX11, but when I now started implementing it in OpenGL, I found that I cannot call any OpenGL functions on my thread (they all return errors).
So my question is: Is it easily possible to implement this for OpenGL (the decoder thread does no rendering, all it wants to do is write the data from the video to the buffer (or inactive) surface, by using glTexSubImage2D())? What are the steps necessary to make this work?
thanks,
Bjoern
Modifying textures in another thread [beginner's questions]
Hello thebjoern-
There are a number of ways to do this. You can google for OpenGL contexts to see some of them.
A very simple technique (which is what I do) is to keep a threadsafe queue of OpenGL requests. The main OpenGL thread will pull tasks off this queue and put them back on a finished queue every frame. Obviously this only works if the tasks themselves are pretty granular and contained.
Whenever your background thread needs to invoke any OpenGL calls, it can push a task on the queue, and then block until the OpenGL thread has completed the task for it.
This exact solution may or may not work for you, but I suspect the simplest solution will involve the main thread performing all OpenGL calls, and the background thread somehow feeding requests to the main thread.
-Thomas
There are a number of ways to do this. You can google for OpenGL contexts to see some of them.
A very simple technique (which is what I do) is to keep a threadsafe queue of OpenGL requests. The main OpenGL thread will pull tasks off this queue and put them back on a finished queue every frame. Obviously this only works if the tasks themselves are pretty granular and contained.
Whenever your background thread needs to invoke any OpenGL calls, it can push a task on the queue, and then block until the OpenGL thread has completed the task for it.
This exact solution may or may not work for you, but I suspect the simplest solution will involve the main thread performing all OpenGL calls, and the background thread somehow feeding requests to the main thread.
-Thomas
If you want to use the actual OpenGL call on another thread, you need to set the current OpenGL context on that thread. An OpenGL context can only be current on one thread at a time, so you need two contexts, one for each thread, and make them share textures (normally each context has separate texture IDs).
On Windows there's the wglShareLists function that does this. So you create two OpenGL contexts, call wglShareLists on them, then make one of them current on the rendering thread, and the other current on the thread that uses glTexSubImage2D.
On Windows there's the wglShareLists function that does this. So you create two OpenGL contexts, call wglShareLists on them, then make one of them current on the rendering thread, and the other current on the thread that uses glTexSubImage2D.
What I'd suggest doing for this (as I'm about to inplement video playback in my engine), and currently do for my animated textures. Is only have the frames decoded, and loaded into memory on one thread. And then signal to the OpenGL thread that that frame is ready to be added to the texture (using glSubTexImage*() calls). That way if if the decoding thread is stalled for whatever reason (takes longer than 1 frame to decode an image, etc) OpenGL will just keep rendering the currently loaded frame (and indeed since video framerate, and OpenGL framerate are likely to be different) this means that neither thread is stalled waiting for the other.
The other beauty of this situation is that the OpenGL Render Context remains with only 1 thread, you don't need to worry about wglMakeCurrent() calls (or equivalent OS functions) so you don't have to worry about losing track of that.
All in all, it works very well. Just ensure that the OpenGL and Decoding threads are accessing the 'status' indicator for the frame at the same time, which can be done via mutices, with a very small timeout to avoid stalling the threads.
The other beauty of this situation is that the OpenGL Render Context remains with only 1 thread, you don't need to worry about wglMakeCurrent() calls (or equivalent OS functions) so you don't have to worry about losing track of that.
All in all, it works very well. Just ensure that the OpenGL and Decoding threads are accessing the 'status' indicator for the frame at the same time, which can be done via mutices, with a very small timeout to avoid stalling the threads.
Hi,
thanks for your replies, I have played around with that today, and almost got there, except for the shared lists..
So what I am doing now on entering the decoding thread is first I create a new context:
m_hdc = m_window->GetHdc();
m_hrc = wglCreateContext(m_hdc);
Then I am setting the same pixel format as for my "main context":
SetPixelFormatGL(hdc);
Then make it current:
wglMakeCurrent(m_hdc, m_hrc);
And then finally I try to share the lists:
wglShareLists(mainContext->m_hrc, m_hrc)
Note that I have omitted the checking code here for brevity. The "mainContext" is the context that was created first on the rendering thread (ie the only thread that does actual rendering, as the decoder thread only just hopes to copy the new videoframe data to one of the textures (which is not actively used for rendering with glBindTexture() during the time)). And my problem is that the wglShareLists() function call always returns false, but I don't know what to change to make it work... any ideas?
Btw: As the decoder is created on demand by the application, it means that the mainContext in most cases has already created other textures and things (for the UI). Might this be the reason of the failure?
thanks,
Bjoern
thanks for your replies, I have played around with that today, and almost got there, except for the shared lists..
So what I am doing now on entering the decoding thread is first I create a new context:
m_hdc = m_window->GetHdc();
m_hrc = wglCreateContext(m_hdc);
Then I am setting the same pixel format as for my "main context":
SetPixelFormatGL(hdc);
Then make it current:
wglMakeCurrent(m_hdc, m_hrc);
And then finally I try to share the lists:
wglShareLists(mainContext->m_hrc, m_hrc)
Note that I have omitted the checking code here for brevity. The "mainContext" is the context that was created first on the rendering thread (ie the only thread that does actual rendering, as the decoder thread only just hopes to copy the new videoframe data to one of the textures (which is not actively used for rendering with glBindTexture() during the time)). And my problem is that the wglShareLists() function call always returns false, but I don't know what to change to make it work... any ideas?
Btw: As the decoder is created on demand by the application, it means that the mainContext in most cases has already created other textures and things (for the UI). Might this be the reason of the failure?
thanks,
Bjoern
You don't want to be creating a separate OpenGL context for the decoder thread. If you're going to go down the route of sharing the OpenGL context between the threads then just create it in the main thread, then call wglMakeCurrent() at the start of each thread's particular control of OpenGL. I however, would advise against this.
Since you are already using double buffering for the playback, I think the simplest approach is the one suggested AndyEsser. In the decode thread you would save the decoded frame to system memory and flag the main thread telling it that data is available. In the main thread you would check to see if data is ready and upload it to the texture accordingly. Simple and no need for multiple context.
Thanks for your replies.
The reason I wanted to go that route was that I already had a working system and class framework in place from when I first implemented it for DX11 (which seems to be quite friendly with threaded environments and allows me to create and modify resources on the other thread quite easily). So all I needed to do was to derive from those same base classes and create OpenGL specific implementations of them.
However, as you both seem to be so strictly against it, I might have to reconsider I suppose.
But can you please explain to me what the reason is that I should try to avoid multiple contexts at all cost?
P.S. in case you wonder why I want to make an OpenGL version of the renderer: I want to get it to work on Mac too...
Again thanks for your time and comments!
Bjoern
The reason I wanted to go that route was that I already had a working system and class framework in place from when I first implemented it for DX11 (which seems to be quite friendly with threaded environments and allows me to create and modify resources on the other thread quite easily). So all I needed to do was to derive from those same base classes and create OpenGL specific implementations of them.
However, as you both seem to be so strictly against it, I might have to reconsider I suppose.
But can you please explain to me what the reason is that I should try to avoid multiple contexts at all cost?
P.S. in case you wonder why I want to make an OpenGL version of the renderer: I want to get it to work on Mac too...
Again thanks for your time and comments!
Bjoern
Multiple Contexts don't work well.
If you want to use your decoder thread to upload the textures to OpenGL, you can. The way I'd suggest doing it is as such:
Decoder Thread:
Renderer Thread:
This way you're just using one context, however, it is entirely possible that the Renderer thread is half way through rendering the frame when the decoder frame calls wglMakeCurrent(); which means that the context will be taken away from the Renderer thread and cause the rest of the calls to OpenGL in that thread to fail.
I'm just advising you on the way I would this, and the same system I use for something similar.
If you want to use your decoder thread to upload the textures to OpenGL, you can. The way I'd suggest doing it is as such:
Decoder Thread:
// Add code to decode framewglMakeCurrent(); // Give control of the OpenGL Context to this threadglTexSubImage2d(); // Upload data to OpenGLwglMakeCurrent(NULL, NULL); // Release the OpenGL Context
Renderer Thread:
// At the beginning of every framewglMakeCurrent();// Render framewglMakeCurrent(NULL, NULL);
This way you're just using one context, however, it is entirely possible that the Renderer thread is half way through rendering the frame when the decoder frame calls wglMakeCurrent(); which means that the context will be taken away from the Renderer thread and cause the rest of the calls to OpenGL in that thread to fail.
I'm just advising you on the way I would this, and the same system I use for something similar.
Thanks for your replies.
So I suppose I will have to go and change the design a bit, so that just the decoding happens on the other thread, and the copying of the data will happen on the main rendering thread.
I had just hoped I could do it the same way as I did it for DX11, that way I could reuse more of the code...
anyway, thanks for your help,
Bjoern
So I suppose I will have to go and change the design a bit, so that just the decoding happens on the other thread, and the copying of the data will happen on the main rendering thread.
I had just hoped I could do it the same way as I did it for DX11, that way I could reuse more of the code...
anyway, thanks for your help,
Bjoern
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement