pouët.net

multiple opengl windows on windows

category: code [glöplog]
I'm trying to get a few OpenGL 3.x windows, sharing the same context on windows. At the moment I have 2 windows, only one of those windows renders something, and whenever I move a window from a screen to another the window that wasn't rendering starts rendering, and the other one stops.

I create the shared contexts with
Code:hglrc[n_windows] = wglCreateContextAttribsARB(hdc[n_windows], hglrc[0], ctxattribs);


and the main loop has something like this

Code:for(unsigned i = 0; i < n_windows; ++i) { wglMakeCurrent(hdc[i], hglrc[i]); glClearColor((float)rand() / RAND_MAX, (float)rand() / RAND_MAX, (float)rand() / RAND_MAX, 0.0f); glClear(GL_COLOR_BUFFER_BIT); SwapBuffers(hdc[i]); }


Any hints?
added on the 2013-04-23 21:12:48 by xernobyl xernobyl
Does each window have its own thread and message pump?

(My code keeps the winmain thread separate from my rendering thread using the same funcs, not tried multiple windows yet though)
added on the 2013-04-23 23:11:31 by Canopy Canopy
Scali has written about this stuff on his blog anongst other places, if ther's one guy who knows, it may be him.
I've tried 2 threads each one for each window, and it looked the same. HelloWorld: you mean this dude https://scalibq.wordpress.com ?
added on the 2013-04-23 23:30:41 by xernobyl xernobyl
xernobyl: Well, the reason you're getting the problem is because you're applying the "main loop" concept where you shouldn't.

Don't.

Spawn one thread for each context, do a wglMakeCurrent() in the entry-point once, and then just do the rendering. If you need to synchronize which window renders when, use events/semaphores/critical sections/whatever (but don't go the spinlock route ;-) ). And then after you're done, feel free to wglSwapBuffers() from whichever context you want system FBO's back buffer flipped with the front buffer.
added on the 2013-04-23 23:37:39 by kbi kbi
Yes, this blog.
At least in this post:
http://scalibq.wordpress.com/2012/05/25/the-story-of-multi-monitor-rendering/
On the asm forum I lurked on, I think he mentioned that he had put some code on sourceforge.
@kbi thanks. I need to reengineer some things then.
added on the 2013-04-24 00:41:09 by xernobyl xernobyl
In my experience there's no reason you need different threads for different windows. You can (and should) have one message loop for all your windows. That's why messages arrive with one window handle hWnd, so you can route the thing to whoever needs it.

I usually have a window with the visuals, and a window with the GUI (which is OpenGL too and does render visuals too). One single context renders to both windows, which allows them to render from the same texture (one window renders the visuals, and the other window uses the same FBO to render visuals AND overlap all sort of GL-made GUI on top.

The only thing you need to do is to wglMakeCurrent() your context to the window you want to render too, before rendering. So:

Init:
hA = createWindow( "A" );
hB = createWindow( "B" );
gl = createContext( from hA or hB, doesnt matter );


AppLoop:

while()
{
GetMessages(); // for hA and hB

doCPUAnimations();

wglMakeCurrent( hA, gl );
render awesome stuff
wglSwapBuffer( hA );

wglMakeCurrent( hB, gl );
render awesome stuff
wglSwapBuffer( hB );
}


added on the 2013-04-24 01:38:19 by iq iq
[ So basically, unlisten to kbi! Make your life easy. You don't need threads, spinlocks or message passing. You don't even need more than one GL context ]
added on the 2013-04-24 01:40:44 by iq iq
I do admit that all API requests eventually land as commands in the same message buffer and are serialized one way or another in the end. However I found separate threads handling separate windows to be more convenient to tackle. Also, assuming we can accept a bit more complex design, we do not run into stuttering if a lot of window messages start to put pressure on the message loop.

iq, agreed that your design is cleaner, simpler to understand, therefore heavily recommended. a problem I see with your approach though is that if you block the application loop by, say, moving your window around, the contents will not update (or will update in irregular intervals, which could or might not be a problem depending on your needs). If you bombard the window with messages caused by windows moving over your window area, you might experience slow-downs. And that's something that xernobyl wanted to avoid, as far as I get it.
added on the 2013-04-24 07:04:30 by kbi kbi
Oh, and for the many GL contexts part - they are sometimes a friend :) Like animated loading screens - these could benefit from the instrument, unless you get access to API that provide you with some sort of asynchronous transfers..
added on the 2013-04-24 07:05:28 by kbi kbi
correct.

i'm just a lazy programmer, that's it :)
added on the 2013-04-24 11:05:38 by iq iq
iq / kbl

i think my previous post about a thread/pump per window is driven by my experience with simultaneous gang (multi) flashing of devices attached to USB and parallel ports which transferred anywhere between a couple meg and a gig or so of data, which obviously blocked quite heavily at points, especially with parallel where timing is more critical.

a hybrid would probably suffice. have the message pump be in the main application thread and spawn a thread per window purely for the rendering loop. to receive 'commands' (keyb/mouse etc) via the main app thread abstract them using a mutex protected fifo list. this stops messages from blocking and lets each rendering thread merrily loop without interruption. (window move/resize aside, if you have a control/script interface it might just plumb directly into that)
added on the 2013-04-24 11:09:12 by Canopy Canopy
kbi: But he is trying to solve a specific problem, not optimize his application. Your answer gave the impression that it would solve his problem, but it won't.
added on the 2013-04-24 13:51:31 by kusma kusma
kusma: you're right and that's exactly the reason for which I expanded what I had originally meant shortly after. If you ask me, the problem sounds implementation-dependent and I wouldn't dare to try to solve it without changing the design.
added on the 2013-04-24 14:53:25 by kbi kbi
This work? :

Init:
hA = createWindow( "A" );
hB = createWindow( "B" );
gl = createContext( from hA or hB, doesnt matter );


just before the "window loop":

kindof new thread() {
while()
{
doCPUAnimations();

wglMakeCurrent( hA, gl );
render awesome stuff
wglSwapBuffer( hA );

wglMakeCurrent( hB, gl );
render awesome stuff
wglSwapBuffer( hB );
}
}

while()
{
GetMessages(); // for hA and hB
}

I used this (with one window) and it doesn't block the animation when moving the window. It should be possible to do this with 2 or more window. I didn't use any things to controll any datas collision within the window and opengl thread, and it worked fine.

added on the 2013-04-24 16:31:35 by Romain337 Romain337
One problem, too many choices. If I want to have vsync on both windows I can't avoid having multiple threads, right?
added on the 2013-04-24 18:48:41 by xernobyl xernobyl
Sure you can. At a very likely cost of sacrificing performance, that is :)
added on the 2013-04-24 19:15:54 by kbi kbi
Main thread: message loop
Main window thread: gl update frame, gl draw to window, semaphore
per aux window thread: gl draw to window, semaphore

I'll try something like that, with the semaphore making sure the drawing to window parts are done in order. Does it make sense?
added on the 2013-04-24 21:38:49 by xernobyl xernobyl
yeah, now i'm home I can post my main loop and thread set up code.

hRenderThreadStarted = CreateEvent( NULL, TRUE, FALSE, NULL );
hRenderThreadFinished = CreateEvent( NULL, TRUE, FALSE, NULL );

CreateThread( NULL, 0, RenderThreadProc, &Params, 0, 0 );


WaitForSingleObject( hRenderThreadStarted, INFINITE );


DWORD dwMsgResult;
BOOL bPeekMessageResult;

bPeekMessageResult = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

if( bPeekMessageResult )
{
function_that_translates_input_does_input_fifo_stuff( &msg );

}

// this is the trick - it pumps windows messages unless there's input

dwMsgResult = MsgWaitForMultipleObjectsEx( 1, &hRenderThreadFinished, INFINITE, QS_ALLEVENTS, MWMO_INPUTAVAILABLE );

// and the while loop only quits when the hRenderThreadFinished event is signalled meaning you can nicely shutdown the threads in response to input elsewhere then signal this one event to terminate

}
while( dwMsgResult != WAIT_OBJECT_0 );


looking back over my code the BIG thing I remember not to miss is that the wglMakeCurrent has to be made per window RC from *inside* the rendering worker thread.


btw why vsync? what about delta timing? (i know this is a whole other can of worms, but i found it helped smoothness massively as between my various machines the vsync was in various states without me ever having touched the gfx settings)
added on the 2013-04-24 22:17:26 by Canopy Canopy
darn forgot the code tags...

and the matching 'do' for the 'while' sits below BOOL bPeekMessageResult;
added on the 2013-04-24 22:19:20 by Canopy Canopy
I just love how those simple design challenges gets addressed in so many different ways. I would personally go with what iq said.
The way I see it: single thread is a robuster design.
added on the 2013-04-24 22:24:45 by TLM TLM
it is. but is it faster? :)

releasing each thread to perform its calc's (if any) for the next frame instead of stalling til all windows have drawn *may* have benefits though. (depends upon whats in each window)
added on the 2013-04-24 22:35:30 by Canopy Canopy
I'm really not the expert, however, I think rendering is non-blocking asynchronous (up to the point where it stalls). So from my understand there is much sense in parallelizing the rendering commands.

I might be off here, again, I'm no expert.
added on the 2013-04-25 10:04:15 by TLM TLM
I did some googling, forcing async in GL is a 'bad thing' (tm) but, going down the lines of having worker threads for each window which can do their calc's while another thread is rendering is probably most efficient for really intensive stuff, but as you said less robust.

however, if there is a common set of data thats calculated once and rendered many times then iq's approach isn't going to be improved upon. if there are multiple sets of data, then threading those, and serialising GL will yield some improvement.






added on the 2013-04-25 11:34:57 by Canopy Canopy

login