muhkuh's picture

WinGLContext.MakeCurrent only works for windows used to create an opengl context

Project:The Open Toolkit library
Version:1.0-2010-10-06
Component:Code
Category:bug report
Priority:normal
Assigned:Unassigned
Status:open
Description

OpenTK.Platform.Windows.MakeCurrent only works when the window given was used to create an opengl context. For other windows MakCurrent fails and throws GraphicsContextException.

The reason for this is that wglMakeCurrent requires that the pixel format of the given GDI device context is compatible to the pixel format of the window the opengl context was created with. I fixed this adding two lines in OpenTK.Platform.Windows.WinGLContext :

        public override void MakeCurrent(IWindowInfo window)
        {
            bool success;
 
            if (window != null)
            {
                if (((WinWindowInfo)window).WindowHandle == IntPtr.Zero)
                    throw new ArgumentException("window", "Must point to a valid window.");
 
                if (0 == Functions.GetPixelFormat((window as WinWindowInfo).WindowHandle))	//fix: 	Check if window has a pixel format set
                    SetGraphicsModePFD(Mode, window as WinWindowInfo);				//	and set a fitting pixel format if not.
 
                success = Wgl.Imports.MakeCurrent(((WinWindowInfo)window).DeviceContext, Handle.Handle);
            }
            else
                success = Wgl.Imports.MakeCurrent(IntPtr.Zero, IntPtr.Zero);
 
            if (!success)
                throw new GraphicsContextException(String.Format(
                    "Failed to make context {0} current. Error: {1}", this, Marshal.GetLastWin32Error()));
 
        }

I also added this to OpenTK.Platform.Windows.Functions:

        [DllImport("gdi32.dll")]
        internal static extern int GetPixelFormat(IntPtr deviceContext);

Comments

Comment viewing options

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

#1

What is the use case of calling MakeCurrent on a window that is not prepared for OpenGL rendering?

MakeCurrent works this way by design. OpenTK strives to be platform-independent and this hack is very platform-specific. Unless MakeCurrent can be made to work in a reasonably similar fashion on Linux, Mac OS X and EGL, I do not believe this should be added to the library.

On the other hand, I do not wish to limit potentially valid uses, which is why I am asking for clarification - there might be a better way to achieve your goal.

muhkuh's picture

#2

Thank you for your quick response. I'll try to be a specific as I can.

First, this is not really a platform specific hack. It fixes an issue that is only present on win32. The X11 implementation has no problems rendering to another window as long as this window has the same or a compatbile visual as the context. Only on win32 this works in this way that a window needs a special call to be OpenGL capable. But I must admit that I have not very much experience with OSX.

My use case is a GTK# app using a docking library and an opengl widget. The problem is that GTK handles native windows completely transparent. A widget can exist without having a native window when it's not currently displayed on screen. Furthermore the native window might change. For instance this happens when docking/undocking a widget. The old native window get's destroyed and a new one is created. When the widget is OpenGL accelerated the only thing possible is to bind the opengl context to the new hardware window. The alternative would be to rewrite GTK and/or the docking library or to recreate the opengl context every time.

But there are other use cases when you need to bind an existing opengl context to a different window. One example is if you want to render to multiple windows. Of course you can create multiple opengl contexts for this. But under some circumstances this might create more problems than it solves. Data shareing between those contexts is required. There are some drivers that have horrible bugs with this. Furthermore not all objects are shared - FBOs are not for example. Supporting multiple contexts makes the underlying engine more complicated. For instance if you just need one window for your target platform but the game editor that should obviously run the same engine needs multiple views (top, left, front, perspective) of the scene. It might be easier to take the existing engine that only supports a single context and use it to render to one view of the scene after another if performance doesn't matter so much.

Another issue is that you don't always have the luxury to create your own window. If you want to use opengl inside a toolkit like GTK the window is already there. Fortunately this works on almost all platforms:
1. Microsoft Windows
You can call SetPixelFormat on almost all windows making them OpenGL capable

2. X11
X11 visuals used for windows of a screen are usually compatible to GLX visuals abtained by glxChooseVisual and the like.

3. MacOS
Don't have a mac, but because GLWidget also works OSX there seems to be a solution there too.

I think the fix I provided is quite safe.
If you pass in a window handle that has no pixel format set you probably want the context to be bound to that window (otherwise you would not call this method). The fix will make it work. If you pass in a window that already has a pixel format set the fix does nothing. The performance overhead of calling GetPixelFormat is irrelevant I think because MakeCurrent is nothing that get's called a thousand times a frame.

Edit:
I also tried to do this outside of OpenTK. But this would require access to the WinWIndowsInfo which is not public.

muhkuh's picture

#3

I tried a lot of things to solve the problem in a different way. If I'm not overlooking something there is currently no clean way to use OpenTK under windows with a single opengl context and multiple windows.

With X11 I can query the visual id from the graphics mode created by OpenTK using the Index property. I can use this visual ID to create an X11 window with this visual and create an IWIndowInfo from it using CreateX11WindowInfo.

Under win32 things are different. A window is not directly bound to an opengl context. Instead a window device context has to be created and made opengl renderable by calling SetPixelFormat. The problem is that although I can create an IWindowInfo instance from an existing HWND handle by calling CreateWindowsWindowInfo I have no access to the HDC because is is created internally. I cannot cast IWindowInfo to WinWindowInfo because WinWindowInfo is not public. So there is no direct way to get SetPixelFormat called on the HDC.

I can think of these solutions:
1. I can modify the window class of the windows Gtk (Gdk) creates. If I add CS_OWNDC to the class style there is only a single device context for the window. So I can call GetDC and get the same HDC as OpenTK and call SetPixelFormat on it. But I'd have to switch it back after creating the window because I do not know what influence this could have on Gtk. Not really a nice solution. It's more a workaround. This is what I'd call a hack.

2. Create a dummy opengl context and supply the new window info to make sure SetPixelFormat is called in the process. Throw away the second context then. This also sucks IMHO.

3. Allow to supply a user created DC in CreateWindowsWindowInfo that I can call SetPixelFormat on. OpenTK should make sure it doesn't release it in Dispose in this case. It could work but would actually move more platform specific stuff out of OpenTK and into my application.

4. Allow to supply a pixel format to CreateWindowsWindowInfo. In this case WinWindowsInfo has to make sure it calls SetPixelFormat when the DC is created. This seems to be the cleanest solution to me. It matches the X11 implementation where the visual is part of X11WindowInfo. But to make this backward compatible to existing code WinGLContext would have to set a pixel format in case it was not supplied. This aspect also leads to 5. - more or less.

5. Change OpenTK using the code above. The user cannot explicitely set the desired pixel format this way but it will implicitely be done when the window is first used. This is by far the easiest thing to do and I don't see any real drawbacks.

6. Use reflection to get the HDC from WinWindowInfo. This is what I'm doing now but it's a hack too.

To sum up: For most platforms it is possible to create a window outside of OpenTK and make sure it fits to the opengl context. Only on win32 this is currently not possible because the device context of a window is handled by OpenTK internally and this is the one that needs to be opengl renderable and fit to the opengl context.

muhkuh's picture

#4

Could anyone of the developers please look at this? I offer to do the coding but I need at least a short reaction on what solution should be implemented.