zeromus's picture

GLControl Vsync property (and others) vs current context

I'm using Windows and OpenTK 2014-01-15

It seems I need to ensure that a GLControl's context is current before I set the VSync property on it, otherwise the WGL call will go to the wrong context. None of this is much of a surprise, but it seems semantically that when I set a VSync property on a control (Which could only be implementing that through its owned context) or even directly on the context, the code should be ensuring that the correct context gets changed.

It looks, from the code, that Platform.MacOS might not suffer from this problem: the AGL_SWAP_INTERVAL gets set on a specific handle, which would suggest that it gets set on the correct context, not the current context. Therefore, this might be a difference in behaviours between the platforms; however, I haven't tested it.

I recommend that OpenTK contexts platform implementations change the context when necessary, set the properties, and then change it back to whatever it was before. I can submit a patch which does this, if you wish. On Windows, at least.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

It is not just vsync - all GLControl properties and events are tied to the current context. When using multiple contexts, the current context is not necessarily the one returned by GLControl.Context.

I do not think it is possible to abstract context management in a reliable fashion. Consider the following scenario:

// main thread
var a = new GLControl();
var b = new GLControl();
// thread #1
a.MakeCurrent();
// main thread
a.Context.SwapInterval = 1;

In the last line, a.Context is not current so it will not be affected.

Following your suggestion, OpenTK would have to yank a.Context from thread #1 to the main thread, call wglSwapInterval, then move the context back to thread #1. Two difficulties with this:

  • yanking the context would immediately crash any OpenGL code running on thread #1
  • (AFAIK) it is not possible to call wglMakeCurrent() as a cross-thread operation

I feel it should be the responsibility of the application to ensure GLControl.Context is current before performing any OpenGL-related operations. In that sense, it is more a matter of documentation rather than code.

Of course, if you can think of a way to make this work in a reliable fashion it might be worth a try.

zeromus's picture

The problem of which context is current in various GLControl niceties is not something I had got as far as analyzing. I had sort of expected that GLControl would be making sure it was using its context for any work it did with lots of paranoid MakeCurrent calls. Otherwise, I'm I don't understand why it exists*. But lets talk about the contexts.

I had forgotten about the difficulty posed by different threads. Your scenario is what I was describing. True, in order to do that, the potential must exist for a context to get yanked to another thread momentarily.

I too would be satisfied with docs describing this violation of what seems semantically obvious.

Here are some other thoughts:

1. The GLContext's properties could throw an exception if the context isn't current already. It might be hard to guarantee this atomically, but what I'm about to propose has similar issues and way more work:

2. You could track which context is current on each thread. Then continue 'yanking' the context from thread #1, which may work sometimes, but if it can't be yanked because the tracking shows that it was left as current on thread #1, throw an exception. In this case, you could be redefining the OpenTK Context semantics to say "A context can't be active on more than one thread at a time". This is likely already to be the case in every platform, to a some degree, so codifying it shouldn't be harmful.

* The comment on the GLControl.Context property says `Gets an interface to the underlying GraphicsContext used by this GLControl. ` But apparently, it barely makes use of it. I can understand the appeal of having a context per window, and if you want to draw to a window, use its context. Of course, WGL would let you use one context to draw to any window, but that's not a very nice or portable function to assume. So the comment should read `Gets an interface to a GraphicsContext you should use when drawing to this GLControl or otherwise changing its display surface properties"

You see, this property's current comment complements the GLContext shenanigans in making me think the context management is automagic. If the comments are 'Do it yourself: MakeCurrent early ,and MakeCurrent often' then there would never be any doubt about it.

the Fiddler's picture

I have updated the GLControl documentation to clarify which operations require a current context and added a warning message when accessing VSync or GrabScreenshot() on a non-current context.

GLControl.Context returns the context associated with a GLControl handle. This association is established when the GLControl handle is created and does not change unless the handle is destroyed and recreated. A context may or may not be current on any given thread, depending on the state of the application - the current context can be accessed via GraphicsContext.CurrentContext. Most OpenGL operations implicitly affect the current context.

Throwing an exception when accessing VSync of a non-current context would fit the WinForms model of throwing exceptions for cross-thread operations. However, it would also break existing applications - I do not feel the benefit would outweigh the cost in this case.