r200 dri seems to have some issues when an application renders from 2 threads
to the same window with 2 different contexts.
According to the glXSwapBuffers manpage it is ok to render to one window with
2 contexts. I have experienced issues with the glDrawBuffer setting when doing
that. I will attach a small test application which shows the bug.
(sorry if this bug report appeared 2 times - I had a system crash when I filed
it the first time, I couldn't find the bug, so I assumed it wasn't filed and
Created attachment 7414 [details]
Test app to show the bug
This app creates a window and two glx contexts and starts 2 threads which use
the contexts to render to the window. The threads are only hackily
synchronised, but it should work, to my knowledge.
The program should draw a blue quad and a red primitive, with the red primitive
beeing partially covered by the quad. The problem is that the quad flickers as
if it was drawn to the front buffer. The flickering goes away if I move the
glDrawBuffers(GL_BACK); statements into the rendering loop in both threads.
The depth buffer seems to be shared properly between the contexts, and the
glColor state seems to work too.
The background is implementing multithreaded Direct3D for Wine. Direct3D
applications can do rendering from any thread, while a gl context is only valid
for one thread. Because of that a d3d app using differnt threads will just
crash with the current wine code.
We can't easilly take away the context from the thread it is assigned to
because we have no control over the threads, so we hope to be able to create
one context for each thread that does the rendering and share textures, VBOs,
shaders, ..., between the contexts and to render to the same window. Locking in
the d3d libs will make sure that only one thread renders at the same time.
Sorry, I forgot to mention, the version identification from glxinfo is:
OpenGL vendor string: Tungsten Graphics, Inc.
OpenGL renderer string: Mesa DRI R200 20060602 AGP 4x x86/MMX/SSE2 TCL
OpenGL version string: 1.3 Mesa 6.5.1
Have you enabled pageflip? It seems to work just fine here without pageflip, but
not with pageflip enabled.
I think the issue here is that in all of the radeon drivers (didn't look at
others), the radeon/r200PageFlip function which will finally get called upon a
glxSwapBuffers call updates the (per-context) hw state to adjust for the new
offsets. But since the drawable ultimately is bound to 2 contexts the other one
will still use the old values.
Good point, yes pageflip is enabled.
It seems using different contexts to render to the same window is a bit jerky
in general. The nvidia driver seems to share the color attribute between both
contexts, and fglrx just crashes :-/ . Is that supposed to work? The
glXSwapBuffers manpage suggests that, but I haven't found anything else.
Maybe I can persuade our dict^H^H^H^H project maintainer to accept a
SetThreadContext hack to take the rendering context away from the thread
(In reply to comment #4)
> Good point, yes pageflip is enabled.
> It seems using different contexts to render to the same window is a bit jerky
> in general. The nvidia driver seems to share the color attribute between both
> contexts, and fglrx just crashes :-/ . Is that supposed to work? The
> glXSwapBuffers manpage suggests that, but I haven't found anything else.
From quickly glancing at it, I would just say the same, but I'm no expert in
that area. Nevertheless, this looks like genuine driver bugs to me.
> Maybe I can persuade our dict^H^H^H^H project maintainer to accept a
> SetThreadContext hack to take the rendering context away from the thread
> owning it...
Well, I guess if it really doesn't work with pretty much any driver, you might
need to do that even if what you do is fully legal. I'd fix it for the radeon
drivers but I currently can't quite see how you'd notice the other contexts
upon a drawable change, as the __DRIdrawablePrivate struct only has a pointer to
a single context. Calling glXMakeCurrent in the drawing loops should work around
that problem (as that should make sure the hw buffer offsets get updated, just
as putting glDrawBuffers(GL_BACK) in there does), though again I don't think
this is needed by the spec.
The underlying problem is that per-drawable state is tracked per context, which
is unfortunately quite widespread in DRI drivers currently, especially related
to buffer swaps (and vsync, ...).
Mass version move, cvs -> git
Does this work better now? FWIW, the Mesa demos repository contains a demo src/xdemos/corender which seems to work perfectly here with both the classic and Gallium r300 drivers.
Indeed a single drawable may have serveral OpenGL render contexts on it, which also may be active in multiple threads at the same time. This is a common setup to allow for such things like uploading texture or buffer object data, while other threads are blocking, say in glXSwapBuffers. Blocking glXSwapBuffers on V-Sync may give up to 1/50th (say if someone slightly missed a previous frame's retrace deadline) of free CPU and GPU time, which is worth using.
But as soon as you go about drawing to the same drawable from multiple render contexts you're begging for trouble. Effectively the target framebuffer is a mutually exclusive, but shared resource, so the driver constantly has to switch locks, and of course the whole rendering process (sending geometry, etc.) is just one big race condition. The bug submitter's test case is just a perfect example for such a race condition.
Now I see, that the original application is D3D9 multithreaded rendering emulation. Personally I never experienced (native) D3D9 multithreaded rendering usefull for something else like generating render-to-texture content. Anyway: It is right that OpenGL contexts can be used from only one thread at a time, BUT THEY ARE NOT BOUND TO A SPECIFIC THREAD! Once can unbind the context from one thread, and rebind it to another:
glXMakeContextCurrent(display, drawable, drawable, NULL)
signal_thread(thread_B, "context released")
glXMakeContextCurrent(display, drawable, drawable, context);
It may also be worthwile for the D3D9 emulator to use a PBuffer drawable (maybe in combination with a FBO, featuring depth texture attachments); instead of glXSwapBuffer at the and of each thread signal that each threads composition layer has been finished and only the last finishing thread should then collect all intermediary pictures and combine them into a composition, implementing things like depth testing and the like there.