Closed dcb-areva closed 7 years ago
Can you clarify the meaning of "open/close" window? Do you dispose it? Or do you simply show/hide it? Are you running on different threads each time a window is displayed?
The NullReferenceException
is telling me that no OpenGL procedure pointers are loaded.
Just tried to run application by opening and closing two windows. Runs flawlessly. What the H/W you running on? Maybe an Optimus system (NV+Intel)?
Yes, I dispose the GlControl in the StopProcessing() method. The thread that calls StartProcessing() and StopProcessing() remains running in the process until I close the application. Note that I can induce the exception when first opening the window (which calls StartProcessing()) by calling Gl.Initialize() in the constructor. I suppose that I can avoid the exception by not disposing the GlControl. The problem is I will still likely encounter the exception when I open a second window (the application has that capability). Is there a way to force loading of OpenGL procedures in conjunction with Gl.Initialize()?
Disclaimer: This is inherited code. I would not have designed the form to operate in this fashion. I am moving to OpenGL.Net from OpenTK which causes random exceptions in the CLR on GC.
From: Luca Piccioni [mailto:notifications@github.com] Sent: Monday, August 14, 2017 11:19 AM To: luca-piccioni/OpenGL.Net Cc: BIGLER Don (IB); Author Subject: Re: [luca-piccioni/OpenGL.Net] Encounter NullReferenceException Second Time Gl.Control Window is Opened (#58)
Can you clarify the meaning of "open/close" window? Do you dispose it? Or do you simply show/hide it? Are you running on different threads each time a window is displayed?
The NullReferenceException is telling me that no OpenGL procedure pointers are loaded.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/luca-piccioni/OpenGL.Net/issues/58#issuecomment-322219498, or mute the threadhttps://github.com/notifications/unsubscribe-auth/Adkl-UHlIMSUoMgganqDaJYnDACjUkvbks5sYGVZgaJpZM4O2apX.
There's a Gl.BindAPI()
method that loads function pointers based on the context current on the calling thread. Call it before creation the new window.
But it shouldn't necessary, since this is called automatically the GlControl at handle creation time. I suspect there is something wrong elsewhere.
I already tried calling Gl.BindApi() before creating the GlControl. I still get the same exception. In this case the first time the window is opened since I have to call Gl.Initialize() first in order to get the KhronosVersion and Extensions objects. That appears to be the key to solving the issue. I don’t understand why the OpenGL bindings are not available after calling Gl.Initialize() since Gl.Initialize() is called automatically without setting the OPENGL_NET_INIT env variable to NO.
Gl.Initialize
is executed only once, and it is described in the wiki pages. Using GlControl is not necessary to call it explicitly.
To overcome this issue, try to set Wgl.CurrentExtensions.SwapControl_EXT
to false (after calling Gl.Initialize
explicitly). This will leave the swap interval as default (V-Sync enabled on double buffered visual configurations). In practice, it avoids to to call that specific API.
Which version of OpenGL.Net you are running on?
I’ll try setting Wgl.CurrentExtensions.SwapControl_EXT to false.
I’m using version 0.5.2 which appears to be the latest on NuGet.
Sorry for the typo, but you should set Wgl.CurrentExtensions.SwapControl_EXT
.
Actually Gl.CurrentExtensions.SwapControl_EXT does not exist in version 0.5.2. At least the in the version I downloaded from NuGet. Does the NuGet package require updating?
Sorry for the typo, but you should set Wgl.CurrentExtensions.SwapControl_EXT.
Unfortunately that didn’t work either. I end up getting an exception at the next OpenGL function call.
Good (even if it is not yet solved, this proves that the problem is elsewhere). Maybe I found the problem, since I've encountered it some days ago.
Can you check whether Wgl.GetCurrentContext()
is returning a value different from IntPtr.Zero
after GlControl disposition? If it is, that's the cause. Because this code:
public override bool MakeCurrent(IntPtr ctx)
{
// Avoid actual call to wglMakeCurrent if it is not necessary
// Efficient on simple/nominal applications
IntPtr currentContext = Wgl.GetCurrentContext(), currentDc = Wgl.GetCurrentDC();
if (ctx == currentContext && _DeviceContext == currentDc)
return (true);
// Base implementation
return (base.MakeCurrent(ctx));
}
protected override bool MakeCurrentCore(IntPtr ctx)
{
return (Wgl.MakeCurrent(_DeviceContext, ctx));
}
Probably that "Efficient on simple/nominal applications" becomes "Buggy on strange/complex applications" ... -.-
Another hypothesis. Windows are run on different threads?
Wgl.GetCurrentContext()returns IntPtr.Zero after calling Dispose on the GlControl. That said, the disposition occurs in a different thread than the thread on which the GlControl was created. StartProcessing() calls Application.Run() on the form that contains the GlControl UserControl. Application.Run() spawns a new thread and calls containerForm_Load(). The GlControl is created in that method. I tried creating the GlControl directly in StartProcessing() but I get the same result.
What do you mean “Windows are run on different threads”? The second time I try to open the form the attempt is made on a new thread.
(Probably) I found the source of the problem. Actually the library has never been tested for multi-threading, even if it is ready to support it. What you need is to load WGL procedures; to do so, you need the Wgl.BindAPI(), but actually is private.
I think you could execute it via reflection at the moment. I'll provide some pattern to support multiple threads.in the next days
Being said this, probably the problem is caused by a missing call to Wgl.BindAPI()
(a private method). You must know that all procedure on KhronoApi
derived classes (indeed including Wgl
), have a [ThreadStatic]
attribute.
Indeed, each thread using any method must load procedures dynamically. Wgl
procedures are thread static too. That's responsibility of Wgl.BindAPI()
; this method is called only in two places:
Wgl
static constructorsWgl.MakeCurrent
Despite all the reasoning above is correct, I cannot explain why only the Wgl.SwapIntervalEXT is failing (and not the GL context creation itself). I'll try to reproduce it locally using an unit test.
By the way, for what is worth, throw away that crazy design with a single-window-at-time-with-multithreading with a safe and fast single-ui-thread design i.e. what this answer explain quite well.
It also doesn’t explain why the form functions properly the first time it is opened but not the second. Perhaps that makes sense to you at this point.
I’ll try using Wgl.MakeCurrent().
Unfortunately it will not be easy to change the current design. Also the current code does use Invoke/InvokeRequired as explained in the thread.
Actually I’m not certain how I can reach Wgl.BindApi() with a call to Wgl.MakeCurrent(). It requires that newContext is not IntPtr.Zero.
Something like using System.Reflection:
MethodInfo bindApiMethod = typeof(Wgl).GetMethod("BindAPI", BindingFlags.NonPublic | BindingFlags.Static);
bindApiMethod.Invoke(null, null);
Ok, I've unit tested the multi-threading UI using WinForms control, as you can see from the commit referenced above. As I expected, it has thrown a NullReferenceException
when access to wglCreateContextAttribsARB
(the first WGL procedure used by GlControl [*]), and this happens on the slower thread (because the other get execution of static constructors).
Now the unit test works as expected. I hope this will fix you issue. In the other case, you can continue this issue.
[*] The first because my system does not support wglSwapIntervalEXT procedure! Eureka!
Great! Will you be releasing a new version on nuget?
I will test the reflection solution tomorrow.
I tried using the code below. I still get the same exception when opening the window the second time. As you noted, it’s strange that the exception occurs at SwapIntervalEXT instead of at Gl context creation. It’s almost as if some but not all of the OpenGL calls are initialized. I wonder if there is a static variable that becomes initialized when the window is first opened and remains initialized after disposition such that it does not allow the full initialization of all of the OpenGL functions.
Quite strange. At this point I suggest to clone the master branch, and test application against it. It is alrwady patched for your case.
Another check: did you call the Wgl.BindAPI method on the new thread, isn't it?
I tested the application against the latest in Master. Your latest commit seems to have fixed the problem. I’d like to build against the NuGet package instead of building from source so I would appreciate an update at your earliest convenience. Thank you
Indeed I found the bug: the commit fixes the multi-threading issues you found.
Essentially WGL function pointers doesn't must be ThreadStatic
, and they requires to be initialized only at Gl initialization time. Then, WGL function pointers are shared across all threads.
This will be included in the NuGet package v0.6.0-beta3 that I'll release soon.
I am using OpenGL.Net to display a video stream using the OpenGL.Net.WinForms GlControl. When I open the form window the first time everything works great. However the second time I open the window I get the NullReferenceException shown in the screen capture. I'm not sure why I am encountering the exception. Is it a problem in my code (shown) or a bug in OpenGL.Net? Thanks!
` using System; using System.Diagnostics; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.Threading; using System.Windows.Forms; using System.Runtime.InteropServices;
using OpenGL;
`