alanb's picture

Help problem with changing to version 0.9-8.1 from 0.3.14 and GLWidget

Hi,
I have been using a very old version of opentk (0.3.14) with GLWidget for quite a while in some software Im writing. Yesterday I hit a bug in this old version so decided to upgrade. I downloaded 0.9.8.1 and built it ok on opensuse 11.1. Thought everything would be ok if I just swapped the dlls in my project, I renamed the imports to OpenTK.Graphics.OpenGL; etc and built my project. No compilation errors. However when I run I get

Quote:

Unhandled Exception: System.InvalidOperationException: No GraphicsContext available in the calling thread.
at OpenTK.Graphics.GL.LoadAll () [0x00000]

So could someone tell me how things have changed or how to solve my problem? Looking at your example code I also suspect that GL.LoadAll() is deprecated but again dont know what the changes are. I saw your example using windows forms + GLcontrol. However I have a lot of time invested in my Gtk based project so swapping over to windows forms would be impossible. I am not writing a major 3D game or anything like that I just want a Gdk window to display some simple 3D paths so have not spent any time on openGL other than to learn the basics. I am prepared to change over to GLControl if that can be embedded in a Gtk Widget so my question is can it? I use monodevelop and tried browsing the opentk dll but couldnt see any reference to the control. Also monodevelop reported nothing available when I tried to add items to the toolbox from opentk.dll and opentk.utilities.dll.
I would rather progress than go back to the earlier versions so any help would be appreciated.
Thanks
Alan


Comments

Comment viewing options

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

Your code should work fine after a simple change: add a call to GraphicsContext.CreateDummyContext() as soon as you create the OpenGL context through your Gtk Widget.

OpenTK 0.9.8 is much stricter than 0.3.14 regarding OpenGL. One of the changes is that it now requires the creation of an OpenGL context prior to the use of OpenGL commands. Since you are creating the context from an external library, you need to inform OpenTK of the context's existence - hence the CreateDummyContext() method.

Another change is that the debug version of OpenTK.dll will now check for OpenGL errors automatically. This is *very* useful while developing an application, as it makes debugging much easier. Just remember to switch to the release OpenTK.dll when distributing your application (the debug version carries a hefty performance hit).

Edit: as part of the ongoing documentation effort, I have added a page that explains how to use external contexts with OpenTK.

alanb's picture

I understand in principle what you are saying however practice is another matter as I did not write the GLWidget code. I have looked through the GLWidget code and tried putting GraphicsContext.CreateDummyContext() in several places but I still get the same error message. Perhaps you could suggest where, as I see no place which has an explicit call to CreateContext(). I have taken some liberties with the GLWidget code included below in that I have deleted all the windows stuff leaving just the X part. When I know where to put the call to CreateDummyContext() I will reinstate the complete version.
Thanks
Alan

 public class GLWidget : DrawingArea, IDisposable
    {
        static PlatformID platformID = System.Environment.OSVersion.Platform;
 
        bool doubleBuffer = true;
        public new bool DoubleBuffered
        {
            get { return doubleBuffer; }
            set { doubleBuffer = value; }
        }
 
        byte colorBits = 24;
        public byte ColorBits
        {
            get { return colorBits; }
            set { colorBits = value; }
        }
 
        byte alphaBits = 0;
        public byte AlphaBits
        {
            get { return alphaBits; }
            set { alphaBits = value; }
        }
 
        byte depthBits = 32;
        public byte DepthBits
        {
            get { return depthBits; }
            set { depthBits = value; }
        }
 
        byte stencilBits = 0;
        public byte StencilBits
        {
            get { return stencilBits; }
            set { stencilBits = value; }
        }
 
        HandleRef renderingContextHandle;
        public HandleRef RenderingContextHandle
        {
            get { return renderingContextHandle; }
        }
 
        Gdk.Visual visual = null;
        static GLWidget globalSharedContextWidget = null;	
        GLWidget sharedContextWidget;
 
        public GLWidget()
			 : this(null)
        {
			if( globalSharedContextWidget == null)
			{
				globalSharedContextWidget = this;
			}
			else
			{
				sharedContextWidget = globalSharedContextWidget;
			}
        }
 
        public GLWidget(GLWidget sharedContextWidget)
        {
            base.DoubleBuffered = false;
            Realized += new EventHandler(HandleRealized);
			ExposeEvent += new ExposeEventHandler(HandleExposeEvent);
 
            this.sharedContextWidget = sharedContextWidget;
 
        }
 
        public override void Dispose()
        {
            base.Dispose();
 
            glXDestroyContext(gdk_x11_display_get_xdisplay(visual.Screen.Display.Handle), renderingContextHandle);
        }
 
        void HandleRealized(object sender, EventArgs eventArgs)
        {
           int[] attributeList = new int[24];
           int attributeIndex = 0;
 
           attributeList[attributeIndex++] = GLX_RGBA;
           if (doubleBuffer) attributeList[attributeIndex++] = GLX_DOUBLEBUFFER;
 
           attributeList[attributeIndex++] = GLX_RED_SIZE;
           attributeList[attributeIndex++] = 1;
 
           attributeList[attributeIndex++] = GLX_GREEN_SIZE;
           attributeList[attributeIndex++] = 1;
 
           attributeList[attributeIndex++] = GLX_BLUE_SIZE;
           attributeList[attributeIndex++] = 1;
 
           if (alphaBits != 0)
           {
               attributeList[attributeIndex++] = GLX_ALPHA_SIZE;
               attributeList[attributeIndex++] = 1;
           }
 
           if (depthBits != 0)
           {
              attributeList[attributeIndex++] = GLX_DEPTH_SIZE;
              attributeList[attributeIndex++] = 1;
           }
 
           if (stencilBits != 0)
           {
              attributeList[attributeIndex++] = GLX_STENCIL_SIZE;
              attributeList[attributeIndex++] = 1;
           }
 
           attributeList[attributeIndex++] = GLX_NONE;
 
           IntPtr xDisplay = gdk_x11_display_get_xdisplay(Screen.Display.Handle);
           IntPtr visualIntPtr = IntPtr.Zero;
 
           try
           {
               visualIntPtr = glXChooseVisual(xDisplay, Screen.Number, attributeList);
           }
           catch (DllNotFoundException e)
           {
               throw new Exception("OpenGL dll not found!", e);
           }
           catch (EntryPointNotFoundException enf)
           {
               throw new Exception("Glx entry point not found!", enf);
           }
 
           if (visualIntPtr == IntPtr.Zero)
           {
             throw new Exception("Visual");
           }
 
           XVisualInfo xVisualInfo = (XVisualInfo)Marshal.PtrToStructure(visualIntPtr, typeof(XVisualInfo));
 
           IntPtr xRenderingContext = IntPtr.Zero;
 
 
 
           if (sharedContextWidget != null)
           {
               GLWidget primaryWidget = sharedContextWidget;
               while (primaryWidget.sharedContextWidget != null) primaryWidget = primaryWidget.sharedContextWidget;
 
               if (primaryWidget.RenderingContextHandle.Handle != IntPtr.Zero)
               {
                  xRenderingContext = glXCreateContext(xDisplay, visualIntPtr, primaryWidget.RenderingContextHandle, true);
               }
                else
               {
                   xRenderingContext = glXCreateContext(xDisplay, visualIntPtr, new HandleRef(null, IntPtr.Zero), true);
                   this.sharedContextWidget = null;
                   primaryWidget.sharedContextWidget = this;
               }
           }
           else
           {
                  xRenderingContext = glXCreateContext(xDisplay, visualIntPtr, new HandleRef(null, IntPtr.Zero), true);
           }
 
 
           if (xRenderingContext == IntPtr.Zero)
           {
              throw new Exception("Unable to create rendering context");
           }
 
           renderingContextHandle = new HandleRef(this, xRenderingContext);
           visual = (Gdk.Visual)GLib.Object.GetObject(gdk_x11_screen_lookup_visual(Screen.Handle, xVisualInfo.visualid));
 
           if (visualIntPtr != IntPtr.Zero)
           {
               XFree(visualIntPtr);
           }
	  OpenTK.Graphics.GraphicsContext.CreateDummyContext(); ********* I thought here but it doesnt seem to make a difference *********************88 
        }
 
		void HandleExposeEvent(object o, ExposeEventArgs args)
		{
			MakeCurrent();
 
			OnRender();
 
			SwapBuffers();
		}
 
		public event EventHandler Render;
		public virtual void OnRender()
		{
			if( Render != null )
			{
				try
				{
					Render(this, EventArgs.Empty);
				}
				catch( NullReferenceException ) { }
			}
		}
 
        public bool MakeCurrent()
        {
           return glXMakeCurrent(gdk_x11_display_get_xdisplay(GdkWindow.Display.Handle), gdk_x11_drawable_get_xid(GdkWindow.Handle), renderingContextHandle);
        }
 
        public void SwapBuffers()
        {
           glXSwapBuffers(gdk_x11_display_get_xdisplay(GdkWindow.Display.Handle), gdk_x11_drawable_get_xid(GdkWindow.Handle));
        }
 
 
        #region X
 
		const string linux_libgl_name = "libGL.so.1";
		const string linux_libx11_name = "libX11.so.6";
		const string linux_libgdk_x11_name = "libgdk-x11-2.0.so.0";
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libx11_name)]
        static extern void XFree(IntPtr handle);
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libx11_name)]
        internal static extern uint XVisualIDFromVisual(IntPtr visual);
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libgl_name)]
        static extern IntPtr glXCreateContext(IntPtr display, IntPtr visualInfo, HandleRef shareList, bool direct);
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libgl_name)]
        static extern IntPtr glXChooseVisual(IntPtr display, int screen, int[] attr);
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libgl_name)]
        static extern void glXDestroyContext(IntPtr display, HandleRef ctx);
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libgl_name)]
        static extern bool glXMakeCurrent(IntPtr display, uint xdrawable, HandleRef ctx);
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libgl_name)]
        static extern void glXSwapBuffers(IntPtr display, uint drawable);
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libgdk_x11_name)]
        static extern uint gdk_x11_drawable_get_xid(IntPtr d);
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libgdk_x11_name)]
        static extern IntPtr gdk_x11_display_get_xdisplay(IntPtr d);
 
        //[SuppressUnmanagedCodeSecurity, DllImport(linux_libgdk_x11_name)]
        //static extern IntPtr gdk_x11_visual_get_xvisual(IntPtr d);
 
        [SuppressUnmanagedCodeSecurity, DllImport(linux_libgdk_x11_name)]
        static extern IntPtr gdk_x11_screen_lookup_visual(IntPtr screen, uint xvisualid);
 
        const int GLX_NONE = 0;
        const int GLX_USE_GL = 1;
        const int GLX_BUFFER_SIZE = 2;
        const int GLX_LEVEL = 3;
        const int GLX_RGBA = 4;
        const int GLX_DOUBLEBUFFER = 5;
        const int GLX_STEREO = 6;
        const int GLX_AUX_BUFFERS = 7;
        const int GLX_RED_SIZE = 8;
        const int GLX_GREEN_SIZE = 9;
        const int GLX_BLUE_SIZE = 10;
        const int GLX_ALPHA_SIZE = 11;
        const int GLX_DEPTH_SIZE = 12;
        const int GLX_STENCIL_SIZE = 13;
        const int GLX_ACCUM_RED_SIZE = 14;
        const int GLX_ACCUM_GREEN_SIZE = 15;
        const int GLX_ACCUM_BLUE_SIZE = 16;
        const int GLX_ACCUM_ALPHA_SIZE = 17;
 
        [StructLayout(LayoutKind.Sequential)]
        private struct XVisualInfo
        {
            public IntPtr visual;
            public uint visualid;
            public int screen;
            public int depth;
            public int c_class;
            public uint red_mask;
            public uint blue_mask;
            public uint green_mask;
            public int colormap_size;
            public int bits_per_rgb;
        }
        #endregion
    }
zahirtezcan's picture

IIRC CreateDummyContext method requires an active context on the current thread. So, calling such method should be no problem after MakeCurrent call.

JTalton's picture

I'll see what I can work out and get the GLWidget site updated with relevant information. It will be a few days.

JTalton's picture

It's been a few days. Still have not found the time. :(

JTalton's picture

After creating a GLContext using the GLWidget code and making the context current, I call CreateDummyContext and get

System.NullReferenceException: Object reference not set to an instance of an object.
at OpenTK.Platform.Windows.Wgl.GetCurrentContext()
at OpenTK.Platform.Windows.WinFactory.b__0()
at OpenTK.Graphics.GraphicsContext.CreateDummyContext()

On a related note, I would like to get GLWidget to create a context using the underlying context code that OpenTK uses.
Related Post: http://www.opentk.com/node/903
I used MonoDevelop on Windows and I am not seeing the issue I previously mentioned. I will have to test some things.

objarni's picture

Isn't this related to http://www.opentk.com/node/993 ?

the Fiddler's picture

Any chance for a stack trace from a debug version of OpenTK? It might help.

There are two ways to make GLWidget work with OpenTK directly: either make it depend on OpenTK or create a third, "glue" assembly that acts as the bridge between OpenTK and GLWidget. The actual code will resemble EGL: you'll need to fill a WinWindowInfo, X11WindowInfo or CarbonWindowInfo struct and pass it to the GraphicsContext constructor (along with any other parameters you wish to set, e.g. OpenGL version).These structs hold platform-specific information that is used to contsruct the context (a window handle and a DC for WinWindowInfo, a window handle, display connection and visual for X11WindowInfo etc). GLWidget should already have this information available, so the actual implementation should be relatively simple.

In both cases, we'll need to make a few changes in OpenTK to expose some internal types to the world - let me take a look.

Edit @objarni: good memory, this is the issue. I'll track this down.

the Fiddler's picture

Ok, the necessary classes and enums are now public (SVN trunk). This code can be changed as needed to accommodate the GLWidget - please let me know if you need any help with this task. I am going to hunt down the GetCurrentContext bug now.

the Fiddler's picture

Ok, this issue should be fixed now.