opentk / GLWpfControl

A fast native control for OpenTK 4.x + 3.x on WPF.
MIT License
196 stars 49 forks source link

Check for NV_DX_interop extension available and fallback mode #109

Open marcotod1410 opened 1 year ago

marcotod1410 commented 1 year ago

At this current time, this control only works if the NV_DX_interop is supported. If the extension is not supported, an AccessViolationException is thrown. This happened to me on a virtual machine where 3D acceleration was enabled via a virtual Mesa graphics card.

I've got the following questions:

NogginBops commented 1 year ago

I think we should fall back (or at least have the option to fall back) to a version that does that CPU roundtrip if interop is not supported. And making sure the user can query if they got a fast backend or a slow one.

NogginBops commented 5 months ago

Moving this to 4.3.1.

Krugpelke commented 4 months ago

I've had black screen issues with Intel UHD gpus, in those cases software fallback seems to solve the problem, but on some integrated gpus this AccessViolationException completely crashes my application and I've had no success try/catching this. It would be great if it would be possible to at least safely handle cases when this control fails to initialize.

NogginBops commented 4 months ago

@Krugpelke how would you like to handle the initialization error? Should we throw a more specific exception or should we have some other way of handling this?

Krugpelke commented 4 months ago

Personally, I'm completely fine with throwing an exception in how it's currently implemented in 4.3.1, it should work as long as NV_DX_interop is safely handled. Actually, disregard my previous comment about DxGlContext constructor crashing, I was thinking about 4.2.3, previously I would crash in DxGLFramebuffer constructor on var genHandle = Wgl.DXRegisterObjectNV(context.GlDeviceHandle, dxRenderTargetHandle, (uint)GLSharedTextureHandle, (uint)TextureTarget.Texture2D, WGL_NV_DX_interop.AccessReadWrite). In 4.3.1, however, the crash happens in DxGlContext Dispose on Wgl.DXCloseDeviceNV(DxDevice.Handle), this dispose is getting called from the finalizer for some reason, the exception for clarity is:

Exception thrown at 0x00007FFD2A857BCC (nvoglv64.dll) in Application.exe: 0xC0000005: Access violation reading location 0x000001E35169A5EE.
An unhandled exception of type 'System.AccessViolationException' occurred in OpenTK.Graphics.dll
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
NogginBops commented 2 months ago

With 67129a3 I've added a PlatformNotSupportedException when WGL_NV_DX_interop can't be found. We can implement the fallback mode in 4.3.3.

ArticadAlistair commented 1 month ago

I'm in the process of switching my prototype Windows (WPF) (.NET 8) app from using SharpGL to OpenTK 4, because everywhere I read suggests OpenTK is better. I now get an Access Violation exception when showing the window containing the GLWpfControl, and I believe it is related to this issue, judging from the above comments. The crash happens on this line in OpenTK.Wpf.GLWpfControlRenderer.ReallocateFramebufferIfNeeded():

this.DxInteropColorRenderTargetRegisteredHandle = OpenTK.Graphics.Wgl.Wgl.DXRegisterObjectNV(this._context.GLDeviceHandle, this.DxColorRenderTarget.Handle, (uint) this.GLSharedColorRenderbufferHandle, 36161U, WGL_NV_DX_interop.AccessReadWrite);

My machine only has Intel built-in graphics hardware, so I suspect I do not have NV_DX_interop support. It looks as though this is a known issue, with the only proposed solution being to throw a PlatformNotSupportedException. The trouble is, many of my users will be in the same position as me. This seems a major handicap to OpenTK, from my perspective, meaning I simply cannot use it. Is there not any better solution? There should at least be a warning (preferably in bold text) which people will see before they try to install or use the control. I've wasted a whole afternoon on this.

NogginBops commented 1 month ago

To print a list of all WGL extensions your GPU+driver combo supports you can use this code:

[DllImport("opengl32.dll")]
static extern IntPtr wglGetCurrentDC();

public static unsafe void PrintWGLExtensions()
{
    Wgl.LoadBindings(new GLFWBindingsContext());
    IntPtr dc = wglGetCurrentDC();
    string[] extensions = Wgl.Arb.GetExtensionsString(dc).Split(' ');
    for (int i = 0; i < extensions.Length; i++)
    {
        Console.WriteLine(extensions[i]);
    }
}

You will have to call this function after your control and OpenGL context has been created.

If your driver supports NV_DX_interop that will be one of the strings that this function prints out.

ArticadAlistair commented 1 month ago

Thanks. I have the GLview utility and can see that my driver doesn't support NV_DX_interop. Am I right in concluding I cannot use OpenTK at all?

NogginBops commented 1 month ago

Unfortunately not at the moment, no. I do think we should enable some sort of (slower) fallback mode in case this extension isn't supported.

In older versions of this repo there was an implementation that read the data fram OpenGL to the cpu and then uploaded it back to DX, so it shouldn't be too hard to bring parts of that code back. But I'm not sure how soon I can get to that, if someone else decided to have a go at it before me that would be fantastic.

ArticadAlistair commented 1 month ago

I have limited experience with OpenGL, and none with DirectX, otherwise I might offer to help. All I wanted was a C# wrapper for OpenGL that was reasonably future-proof. It looks like OpenTK has gone a bit too far into the future, and left the present behind. I don't know how representative my experience is, but if it is common, OpenTK may have shot itself in the foot.

NogginBops commented 1 month ago

Looking at the old renderer's source it's quite simple so I don't think it would be too much work to actually do the slower fallback method of rendering. https://github.com/opentk/GLWpfControl/blob/551b59ae02c846936db700de49c0bbf640821358/src/GLWpfControl/GLWpfControlRenderer.cs

If no one else wants to have a go at implementing this into the control I'll do it. But not sure how fast I can get to it.

But basically the change would mean checking for WGL_NV_DX_interop and switching the renderer to either use DX interop or do the pixel buffer object shuffle that is being done in the linked code above.

NogginBops commented 1 month ago

Looking at https://opengl.gpuinfo.org/listreports.php?extension=WGL_NV_DX_interop&option=not and filtering for "Windows" and "OpenGL" (exclusing OpenGLES) most of the GPUs that don't support WGL_NV_DX_interop are either software implementations, OpenGL over Vulkan translation layers, really old intel GPUs, and some odd AMD cards running MESA on windows.

Generally WGL_NV_DX_interop is generally well supported. What GPU and driver do you have?

ArticadAlistair commented 1 month ago

I have an Intel(R) UHD Graphics 770 (Forward Context 4.6). It is on a PC that is only 18 months old. The rest of the PC is fairly high spec. I don't need a fancy graphics card, not least because many of my customers don't either, so I need to know how well the program will work for them. When you say the "change would mean checking for WGL_NV_DX_interop and switching the renderer to either use DX interop or do the pixel buffer object shuffle", you've left me behind! Does this mean my machine would be able to use hardware acceleration on my OpenGL application if I'm only using OpenGL API calls?

NogginBops commented 1 month ago

Looking at reports for the UHD Graphics 770 you should expect WGL_NV_DX_interop to be supported: https://opengl.gpuinfo.org/displayreport.php?id=7677 (all UHD Graphics 770 reports report support for this extension).

Do you know what graphics driver you are running? The oldest driver version reported on the website is 30.0.101.1994 so I guess if you have something lower than that it could be possible that it doesn't have the extension.

Should be as simple as just installing the latest driver to get it working in that case.

ArticadAlistair commented 1 month ago

My most recent graphics driver update was this: Description: This download installs Intel® Graphics Driver 32.0.101.5972 (WHQL Certified) for Intel® Arc™ A-Series Graphics, Intel® Iris® Xe Graphics, and Intel® Core™ Ultra Processors with Intel® Arc™ Graphics. Version: 32.0.101.5972 Release date: August 23, 2024 Size: 884.42 MB More information here. It looks like a newer one is available. I'll get it now...

ArticadAlistair commented 1 month ago

Here's a possibly revealing development: the GLview Extensions Viewer utility does not show support for NV_DX_interop: image However, the code snippet posted above "To print a list of all WGL extensions your GPU+driver combo supports" does indicate support for NV_DX_interop: image I still get a crash within Wgl.DXRegisterObjectNV(): image hDevice = 0x000001a50afaf730 dxObject = 0x000001a503f33af8 name = 1 type = 36161 access = AccessReadWrite EntryPoints[21] = 0x00007ff83d80a4d0 If I try to put the 0x00007ff83d80a4d0 address (EntryPoints[21]) into the debugger's Dissasembly address box at this point, I get this: image Suggesting there is a different problem altogether. Any suggestions?

NogginBops commented 1 month ago

The GLview Extensions Viewer only shows you GL_ extensions and will not show you platform specific extensions for WGL (nor EGL, GLX etc).

So this seems to be a recurring issue with WGL_NV_DX_interop on intel (or generally integrated graphics), we've never really figured out if it's a bug on our end or if it's a intel graphics driver bug.

Can you show me the code you use to initialize your GLWpfControl?

ArticadAlistair commented 1 month ago

I created a minimalist app to initialise the control and then list the WGL extensions. Here is the WPF XAML: image And here is the code-behind: image As I'm a relative newbie, you also need to consider the possibility that I'm a moron who doesn't know what they're doing!

NogginBops commented 1 month ago

And just to make sure, what version of GLWpfControl are you running?

ArticadAlistair commented 1 month ago

4.3.2