2013-10-05

OpenGL and OpenTK Fun

I've been working on updating my OpenGL code to use OpenGL ES 2.0 (instead of 1.1). I've run into a number of problems doing this, many of which are exasperated because I don't if my problems are related to my OpenGL code or the underlying libraries I'm using (OpenTK and the PowerVR OpenGL ES implementation for Windows).

To try to diagnose the problems I tried to start with a simple OpenGL ES 2.0 example. Unfortunately, all of the examples I found were either for native OpenGL ES (not via .NET) or specifically from Xamarin for iOS or Android, where I wanted a simple OpenTK example. The OpenTK distribution actually includes one very simple example (just showing an empty window with a colored fill), but as I mentioned in my previous post, the latest official OpenTK release is from 2010 and has some issues.

I searched around and found what appears to be the most active development branch of OpenTK on GitHub at https://github.com/andykorth/opentk. I downloaded that branch and tried to run the same example, but continued to run into problems.

At first I got a System.PlatformNotSupportedException.
System.PlatformNotSupportedException at OpenTK.Platform.Factory.UnsupportedPlatform.CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, Boolean directRendering, Int32 major, Int32 minor, GraphicsContextFlags flags) in opentk\Source\OpenTK\Platform\Factory.cs:line 171
   at OpenTK.Graphics.GraphicsContext..ctor(GraphicsMode mode, IWindowInfo window, Int32 major, Int32 minor, GraphicsContextFlags flags) in opentk\Source\OpenTK\Graphics\GraphicsContext.cs:line 134
   at OpenTK.GameWindow..ctor(Int32 width, Int32 height, GraphicsMode mode, String title, GameWindowFlags options, DisplayDevice device, Int32 major, Int32 minor, GraphicsContextFlags flags, IGraphicsContext sharedContext) in opentk\Source\OpenTK\GameWindow.cs:line 222
   at OpenTK.GameWindow..ctor(Int32 width, Int32 height, GraphicsMode mode, String title, GameWindowFlags options, DisplayDevice device, Int32 major, Int32 minor, GraphicsContextFlags flags) in opentk\Source\OpenTK\GameWindow.cs:line 180
   at Examples.Tutorial.SimpleES20Window..ctor(GraphicsContextFlags flags) in opentk\Source\Examples\OpenGLES\2.0\SimpleWindow20.cs:line 26
   at Examples.Tutorial.SimpleES20Window.Main() in opentk\Source\Examples\OpenGLES\2.0\SimpleWindow20.cs:line 112
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

This led me down a little side road of trying to confirm that I had the PowerVR OpenGL ES library properly installed. After confirming it was, as far as I could tell, I stepped through the code in the debugger and got to the Factory constructor in opentk\Source\OpenTK\Platform\Factory.cs.

While stepping through I kept finding that Egl.Egl.IsSupported was false. That code for IsSupported is below.

Oh great, a swallowed exception, maybe that would tell me something helpful. Stepping through the code again and examining the exception showed me a System.BadImageFormatException.

System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
   at OpenTK.Platform.Egl.Egl.GetCurrentContext()
   at OpenTK.Platform.Egl.Egl.get_IsSupported() in opentk\Source\OpenTK\Platform\Egl\Egl.cs:line 316 

I'm sure some web searching would find it, but I've run into this problem in the past and remembered it had to do with incompatible binary formats. For example, if you have a .NET program compiled for x64 and try to load an 32bit x86 native DLL. I checked the OpenTK solution's build configuration, and indeed, it was set to "AnyCPU". I changed it to x86 and tried again and the above exception went away, but it still didn't work.

Next I got an entirely unhelpful System.Collections.Generic.KeyNotFoundException. With the below stack-trace.

System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at OpenTK.Graphics.GraphicsContext.get_CurrentContext() in opentk\Source\OpenTK\Graphics\GraphicsContext.cs:line 344
   at OpenTK.Graphics.GraphicsContext.LoadAll() in opentk\Source\OpenTK\Graphics\GraphicsContext.cs:line 512
   at OpenTK.GameWindow..ctor(Int32 width, Int32 height, GraphicsMode mode, String title, GameWindowFlags options, DisplayDevice device, Int32 major, Int32 minor, GraphicsContextFlags flags, IGraphicsContext sharedContext) in opentk\Source\OpenTK\GameWindow.cs:line 220
   at OpenTK.GameWindow..ctor(Int32 width, Int32 height, GraphicsMode mode, String title, GameWindowFlags options, DisplayDevice device, Int32 major, Int32 minor, GraphicsContextFlags flags) in opentk\Source\OpenTK\GameWindow.cs:line 180
   at Examples.Tutorial.SimpleES20Window..ctor(GraphicsContextFlags flags) in opentk\Source\Examples\OpenGLES\2.0\SimpleWindow20.cs:line 26
   at Examples.Tutorial.SimpleES20Window.Main() in opentk\Source\Examples\OpenGLES\2.0\SimpleWindow20.cs:line 112
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Well, for a pretty low-level media platform this isn't very encouraging as it doesn't describe anything about the real problem and is clearly happening deep in the library. After a lot of debugging I finally found the below bit of code in opentk\Source\OpenTK\Graphics\GraphicsContext.cs.

I missed it the first few times I was stepping through the code, but the comment says it all: Note: this property will not function correctly when both desktop and EGL contexts are available in the same process. This scenario is very unlikely to appear in practice. In my case, since I'm running on Windows, I do actually have both desktop and EGL available. I didn't feel like digging into the code to fix this so that both contexts could actually exist simultaneously, but I remembered the Factory class that's I'd looked into above and made an easier fix. I did this in opentk\Source\OpenTK\Platform\Factory.cs in the constructor. My updated version is below.

You can compare to the original version above, but basically I made it such that the library first tries to see if EGL is available, and if so, uses it. If it is not available then the desktop version is instantiated. You can see that the Default context isn't instantiated unless the EGL contexts don't work.

After all of this I could run the sample program and was rewarded with a 800x600 blue window. Woohoo! Ok, it's not that exciting, but at least it was running and I can now try to get my OpenGL ES 2.0 code working.

Next: Premultiplied alpha