Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intel Xe graphics driver does not support depth/stencil buffer in d3d9 #149

Open
Xerxes004 opened this issue Dec 17, 2024 · 26 comments
Open

Comments

@Xerxes004
Copy link

Xerxes004 commented Dec 17, 2024

As mentioned in #138, Intel integrated graphics does not support MSAA. After some experimentation on a newer machine, the newer integrated graphics also do not support the call to NativeCreateDepthStencilSurface in this call:

public void CreateDepthStencilSurface(int width, int height, Format format, MultisampleType multisample, int multisampleQuality, bool discard, out IDirect3DSurface9 surfaceHandle, ref IntPtr sharedHandle)
{
NativeCreateDepthStencilSurface method = Marshal.GetDelegateForFunctionPointer<NativeCreateDepthStencilSurface>((*VTable)->CreateDepthStencilSurface);
int result = method(this, width, height, format, multisample, multisampleQuality, discard, out surfaceHandle, ref sharedHandle);
CheckHResult(result);
}

The example app crashes when switching to any tab with a GL control with an "INVALID ARGS" error.

Disabling the call to CreateDepthStencilSurface and checking for a null ptr in IDirect3DSurface9.Release fixes the issue, and the GL control renders properly.

On another, older machine running W10 + Intel i5-10400, the call does not throw an error BUT the depth buffer definitely does not work, and I have to create a new framebuffer + depth/stencil attachment for depth to be processed correctly.

From what I can gather, Intel no longer supports d3d9, and relies on D3D9On12 for the API. From what I can gather, the ball is out of Intel's court on this one.

Likely workaround:

Only use d3d9 render target surface, and use the OpenGL API to set up a new "default" framebuffer. This breaks the convention of having depth enabled by default, but it will only effect Intel integrated graphics users.

Device specs:

Intel(R) Core(TM) i9-14900K
Intel UHD Graphics 770 (31.0.101.4953)
Windows 11 Pro

@Xerxes004
Copy link
Author

Xerxes004 commented Dec 17, 2024

I updated my drivers to 32.0.101.6079, and now I have an exception in Wgl.DXRegisterObjectNV here:

DxInteropColorRenderTargetRegisteredHandle = Wgl.DXRegisterObjectNV(
_context.GLDeviceHandle,
DxColorRenderTarget.Handle,
(uint)GLSharedColorRenderbufferHandle,
(uint)RenderbufferTarget.Renderbuffer,
WGL_NV_DX_interop.AccessReadWrite);

Unchanged example app EXCEPT I'm building for net9.0-windows in both projects. OpenTK 4.9.3.

Error message building for netcore3.1:

Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Error message building for net9.0-windows:

Fatal error. 0xC0000005

@Xerxes004
Copy link
Author

I submitted a request to Intel to look into the WGL_NV_DX_interop extensions not being found properly with driver 32.0.101.6078. I'll update here if they give me any useful info.

@nick-boey
Copy link

I have run into the same issue on my computer, and have narrowed it down to two specific commits. It's definitely an issue with the Intel integrated graphics, as it works fine when using the dedicated NVIDIA graphics.

On version 4.3.3 it was crashing instantly, but I found it was working back in version 4.2.3. I went back through the commits and I found that it was running fine until this commit, at which point it just renders a black screen. I traced it down to this line in GLWpfControlRenderer.cs, which results in a GL_INVALID_OPERATION error.

GL.FramebufferRenderbuffer(
  FramebufferTarget.Framebuffer,
  FramebufferAttachment.ColorAttachment0,
  RenderbufferTarget.Renderbuffer,
  GLSharedColorRenderbufferHandle);

If I proceed forward through the commits it is only at this commit that it starts to crash instantly with a System.AccessViolationException at this line:

DxInteropColorRenderTargetRegisteredHandle = Wgl.DXRegisterObjectNV(
  _context.GLDeviceHandle,
  DxColorRenderTarget.Handle,
  (uint)GLSharedColorRenderbufferHandle,
  (uint)RenderbufferTarget.Renderbuffer,
  WGL_NV_DX_interop.AccessReadWrite);

Hardware specs:

OS Name Microsoft Windows 11 Enterprise
Processor Intel(R) Core(TM) Ultra 7 165H, 1400 Mhz, 16 Core(s), 22 Logical Processor(s)

Dedicated graphics
Name NVIDIA RTX 500 Ada Generation Laptop GPU
Driver Version 31.0.15.5305

Integrated graphics
Name Intel(R) Arc(TM) Pro Graphics
Adapter Type Intel(R) Arc(TM) Pro Graphics Family, Intel Corporation compatible
Driver Version 32.0.101.6078

@nick-boey
Copy link

The black screen in my case appears to be caused by Wgl.DXRegisterObjectNV returning a zero pointer when run on the Intel integrated graphics, but returning a valid pointer when run on NVIDIA graphics. It only causes the GL_INVALID_OPERATION when the GLSharedColorRenderbufferHandle is then used in the GL.FramebufferRenderbuffer call.

@Xerxes004
Copy link
Author

I saw the same thing you're seeing, @nick-boey. With Intel driver version 32, it seems that the WGL library tries and fails to call that extension function. I sent Intel a sample project located here. If you can, roll your Intel driver back to 31 and see if it fixes the WGL linking issue. If it does, you may still need to remove the GLWpfControl calls that set up the depth/stencil buffer.

@Xerxes004
Copy link
Author

Update: After some back and forth, I've finally made contact with an engineer at Intel. I've been told that this issue has been opened with the Intel driver team.

They told me it's internal case number 22020848268.

They want the ability to reproduce the issue with as "few layers as possible," so I may need to write some example code for them in C++.

@NogginBops
Copy link
Member

It's understandable to want as few layers as possible, but due to the DirectX context being created by WPF it's possible that it's not as easy as we'd want to create an "equivalent" context ourselves. But if this issue happens generally on intel drivers then it might be easier to reproduce the issue.

@Xerxes004
Copy link
Author

I have not heard anything new from Intel, but I am having the same issue with an Intel B570 graphics card, driver version 32.0.101.6262.

Fatal error. 0xC0000005
    at OpenTK.Graphics.Wgl.Wgl.DXRegisterObjectNV(IntPtr, IntPtr, UInt32, UInt32, OpenTK.Platform.Windows.WGL_NV_DX_interop)
    at OpenTK.Wpf.DxGLFramebuffer..ctor(OpenTK.Wpf.DxGlContext, Int32, Int32, Double, Double, OpenTK.Wpf.Interop.Format)
    at OpenTK.Wpf.GLWpfControlRenderer.SetSize(Int32, Int32, Double, Double, OpenTK.Wpf.Interop.Format)
    at OpenTK.Wpf.GLWpfControl.SetupRenderSize()

@Xerxes004
Copy link
Author

Out of curiosity, I went and ran the version 4.2.3 example, and it seems to work as expected. The same example crashes with the above error with 4.3.3. So it has to be something different between those two versions.

@NogginBops
Copy link
Member

I guess the biggest clue here is that 4.2.3 doesn't attempt to share the depth stencil buffer between DirectX and OpenGL. I've opened a PR here #151 that does the changes so that the depth buffer is hosted in OpenGL land instead of shared between OpenGL and DirectX. If you are able to test this PR that would be great @Xerxes004 @nick-boey

@Xerxes004
Copy link
Author

@NogginBops will test that first thing tomorrow morning. I really appreciate your help on this.

I stumbled upon another clue: If I change the "type" parameter of DXRegisterObjectNV to "Texture2D", it no longer crashes at that invocation:

DxInteropColorRenderTargetRegisteredHandle = Wgl.DXRegisterObjectNV(
    _context.GLDeviceHandle,
    DxColorRenderTarget.Handle,
    (uint)GLSharedColorRenderbufferHandle,
    (uint)TextureTarget.Texture2D,
    WGL_NV_DX_interop.AccessReadWrite);
if (DxInteropColorRenderTargetRegisteredHandle == IntPtr.Zero)
{
    Debug.WriteLine($"Could not register color render target. 0x{DXInterop.GetLastError():X8}");
}

I haven't gotten much further than this, but I'll drop any nuggets I find in here.

@NogginBops
Copy link
Member

I stumbled upon another clue: If I change the "type" parameter of DXRegisterObjectNV to "Texture2D", it no longer crashes at that invocation:

Did you also change the OpenGL object to be a texture?

@Xerxes004
Copy link
Author

I stumbled upon another clue: If I change the "type" parameter of DXRegisterObjectNV to "Texture2D", it no longer crashes at that invocation:

Did you also change the OpenGL object to be a texture?

No, I only changed that one param.

Another interesting thing to note is that if I call GL.IsFramebuffer on GLFramebufferHandle, the answer is false. Same with calling GL.IsRenderbuffer and GLSharedDepthRenderRenderbufferHandle.

@NogginBops
Copy link
Member

Weird... That seems like an Intel driver bug?

@Xerxes004
Copy link
Author

Weird... That seems like an Intel driver bug?

I agree, RenderBuffer is explicitly allowed by the NV_DX_interop spec for a IDirect3DSurface9, and previous versions of the driver don't seem to have problems with The Gen* functions. The good news is I can probably give them more direct information, including a minimal reproducible example without having to involve Wgl.

I think their bug lies either in their implementation of wglDXSetResourceShareHandleNV, or in the IDirect3DSurface9 object created and returned by glfw.dll via Wgl. I think this because if I don't call it, then wglDXRegisterObjectNV succeeds. But if I do, it accesses memory out of range.

Very likely their interface for IDirect3DSurface9 is jacked up in some way when it comes to RenderBuffer registration. There were several functions in that interface with null pointers in the __Vtable. I'm fairly new to COM, but none of those functions should be IntPtr.Zero, right? I wonder if I could register my own COM interface for IDirect3DSurface9 and register that instead to see what it is they're trying to call.

I need to get this working, hell or high water, so I will try to use the registered Texture2D method that 4.2.3 was using and get back to you.

@Xerxes004
Copy link
Author

I guess the biggest clue here is that 4.2.3 doesn't attempt to share the depth stencil buffer between DirectX and OpenGL. I've opened a PR here #151 that does the changes so that the depth buffer is hosted in OpenGL land instead of shared between OpenGL and DirectX. If you are able to test this PR that would be great @Xerxes004 @nick-boey

This still crashes for me here

https://github.com/NogginBops/GLWpfControl/blob/8adec2c7779f11cbfd496d1ffde755dfda182456/src/GLWpfControl/GLWpfControlRenderer.cs#L198-L203

@NogginBops
Copy link
Member

This still crashes for me here

With or without MSAA?

@Xerxes004
Copy link
Author

This still crashes for me here

With or without MSAA?

Without MSAA; the Renderbuffer target is what makes it blow up.

I was able to work around all of the bugs using a Texture2D target, and forgoing the DX depth/stencil shared resource.

#152

I highly doubt this PR is something that GLWpfControl should take wholesale because it will break anyone's code who is relying on the depth/stencil attachment in the default framebuffer. But this PR represents code that works for what I need for my Intel application. It's a long story, but we rely on Intel's oneAPI for some critical functionality at my company, and the Intel GPUs work "out of the box" in that ecosystem. The same can't be said for D3D9 interop, apparently......

I am creating my own depth/stencil buffers and MSAA-enabled framebuffers because I need to have parts of my program drawn with MSAA, and parts without.

Please let me know if there's anything you need from me, I'm more than happy to contribute some Intel-specific code so that others can be un-stuck.

CC @nick-boey

@NogginBops
Copy link
Member

I guess we could switch to textures instead of renderbuffers, but then MSAA is not expected to work (but I guess it doesn't work anyway so it doesn't really matter).
We can keep the depth stencil buffer in OpenGL land as there is no reason for us to share it with DirectX afaik.

By doing this we should get something that works decently ok on intel GPUs while they hopefully fix their broken drivers.

@Xerxes004
Copy link
Author

Sounds good to me. I'm going to send Intel some more info and I'll keep you all posted.

@Xerxes004
Copy link
Author

@NogginBops I updated #152 with a possible solution to decide which "renderer" to use at runtime, based on passing the MSAA test. If the MSAA test fails, the "Texture2D" renderer is used. If it passes, the direct surface sharing is used. I kept the GLWpfControl and GLWpfControl settings interfaces the same, only internal interfaces were changed.

I tried several times to attach an MSAA renderbuffer to the Texture2D framebuffer, but it just leaves the screen blank. I opted to just skip MSAA entirely for that renderer, like you had before.

Cheers

@Xerxes004
Copy link
Author

I pushed a "fix" for the depth/stencil buffer not being honored in the shared surface framebuffer. I am setting up a separate framebuffer and blitting to it after the render pass. MSAA now works as well.

@Xerxes004
Copy link
Author

Good news, all:

Intel reached out and said they were able to root-cause the driver issue with their RENDERBUFFER registration. The public release of the fix is forthcoming.

@NogginBops
Copy link
Member

That's great! We'll still want the workaround for some time though, but we can switch on it at runtime.

@Xerxes004
Copy link
Author

That's great! We'll still want the workaround for some time though, but we can switch on it at runtime.

Good stuff. For anyone who wants access to the workaround, we are using this branch for our production code without any issues, so far. It needs some more massaging to squeeze out maximum performance (like skipping the blit if MSAA is off) but it works for what we need.

@NogginBops
Copy link
Member

I'll try and find some time to review #152 soon so we can get the workaround merged. From what I've seen so far it looks promising.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants