Skip to main content
Up and Coming
March 20, 2019

wglDXRegisterObjectNV returns 0 but no error ( NV_DX_interop2 )

Has anyone successfully added NV_DX_interop2 functionality to a PC Win10 OVR SDK app with NVIDIA GTX 1080 GPU?  Just to be clear, my goal is to be able to execute arbitrary GL code and have it
write to the DX eye render targets, with the proper wglDXLockObjectsNV()
and wglDXUnlockObjectsNV() surrounds.


The problem I am having is that when I call

gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorbuffer,  gl_names[0], GL_RENDERBUFFER,  WGL_ACCESS_READ_WRITE_NV);

the handle is set to 0, but there is no error detected by GetLastError();  So, I have 2 renderbuffer objects, color and depth, that are derived from the OVR eye render targets/buffers.  The dxColorbuffer object appears correct, as does the dxDepthbuffer object.  The call succeeds on the dxDepthbuffer object and sets the handle to a non-null value.

I think I am doing all the required steps correctly, including:

gl_handleD3D = wglDXOpenDeviceNVFunc((void *)((OVR::Render::D3D11::RenderDevice*)pRender)->Device); 
glGenFramebuffers(2, gl_fbonames);
glGenRenderbuffers(2, gl_names);
gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorbuffer, gl_names[0],   GL_RENDERBUFFER,      WGL_ACCESS_READ_WRITE_NV);

... subsequent calls such as glBindFramebuffer(), etc. are present but not shown.

Thanks.












    57 replies

    Inspiring
    April 24, 2019
    You can resolve the MSAA texture to a single sample texture on the DX side. Not 100% sure, but this should be also very easy in GL to resolve, if not even easier than DX.

    I would suggest you technically disable MSAA on the OVR eye textures, since previously resolved MSAA textures should have about the same quality as passing them to OVR as real MSAA *

    The quality bump of not using MSAA at all is very dependent on your environment. It is mainly visible on outside edges of vertices. Since it is not very hard to cleanly resolve MSAA textures to single sample, I would not drop it completely.

    You can even use the ResolveSubresource method instead of CopyResource. This works for MSAA and single sample textures in most cases. This in in fact something I do in parts of my project, so I don't have to distinguish between MSAA and single sample texture sources.

    *) Since you can have access to sub-sample information of MSAA textures in shaders, it is technically possible to use a MSAA texture to have nice upscaling. You just have to know the spatial sample pattern used for subsamples - this is well known for DX at least. Some information as depth is shared between texels, but still the remaining MSAA information can be very helpfull. But I don't expect OVR to use techniques like this, and have not seen any information hinting it might be done. So my guess is you get the very same result from OVR when passing a resolved single sample texture as you would get passing MSAA textures. I guess they will only do the resolve for people not being capable of calling the resolve method.
    knchaffinAuthor
    Up and Coming
    April 24, 2019
    @thewhiteambit and anyone else who is trying NV_DX_interop

    Thanks for the guidance.  I now have 3 ways to accomplish NV_DX_interop2 with a shared DX11 color render target texture.  These steps allow successful wglDXRegisterObjectNV() calls as well as Context->CopyResource(), Context->CopySubresourceRegion() or Context->ResolveSubresource() (where appropriate).

    For me, these were the key necessary code steps in pseudo-code.  Some of this is specific to the Oculus World Demo App classes.  I've left out a lot of steps, but these are the ones that gave me trouble.  The shared depth buffer is not shown.  The GL side of rendering has not been tested.  Sorry for any typos.

    In this, MSAA="multi sample anti aliased",  SSAA="single sample anti aliased".
    ID3D11Texture2D* d3dColorbuffer;  // to be shared between DX and GL
    ID3D11Texture2D* d3dEyeRenderbuffer; // to be obtained from OVR
    D3D11_TEXTURE2D_DESC desc;
    ZeroMemory(&desc, sizeof(desc));
    // get an eye buffer DX texture and description to use as a template
    ovrTextureSwapChain oTSC = RenderTargets[0].pColorTex->Get_ovrTextureSet();
    ovr_GetTextureSwapChainBufferDX(Session, oTSC, 0, IID_ID3D11Texture2D, (void **)&
    d3dEyeRenderbuffer);
    d3dEyeRenderbuffer->GetDesc(&desc); //use this as a template for DX/GL render texture creation

    HANDLE m_hInteropDevice = NULL;
    // quite a few interop steps not shown
    m_hInteropDevice = wglDXOpenDeviceNVFunc((void *)deviceD3D11);

    GLuint m_gltextures[2] = { 0,0 }; // color and depth buffers as Texture2D
    HANDLE m_hInteropObjects[2] = { 0,0 }; // color and depth buffers as Texture2D
    glGenTextures(2, m_gltextures);

    //Method 1: OVR-SSAA , GL-SSAA
    MultisampleRequested(false); // this is an OculusWorldDemo parameter
    desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc.SampleDesc.Count = 1;
    desc.MiscFlags = 0;
    deviceD3D11->CreateTexture2D(&desc, NULL, &
    d3dColorbuffer);
    m_hInteropObjects[0] = wglDXRegisterObjectNV(m_hInteropDevice,
    d3dColorbuffer,
    m_gltextures[0],
    GL_TEXTURE_2D,
    WGL_ACCESS_READ_WRITE_NV);
    // GL can render to d3dColorbuffer once appropriately bound and set

    //
    IN OVR eye render code (e.g., RenderEyeView()),
    ID3D11Texture2D *pEyeTexture=nullptr;
    ovrTextureSwapChain oTSC = DrawEyeTargets[0]->pColorTex->Get_ovrTextureSet();
    int swapChainLength = 0;
    ovr_GetTextureSwapChainLength(Session, oTSC, &swapChainLength);
    int currentIndex = 0;
    ovr_GetTextureSwapChainCurrentIndex(Session, oTSC, &currentIndex);
    ovr_GetTextureSwapChainBufferDX(Session, oTSC, currentIndex, IID_ID3D11Texture2D, (void **)&pEyeTexture);
    deviceContextD3D11->CopyResource(pEyeTexture, dxColorbuffer);


    //Method 2: OVR-SSAA , GL-MSAA
    MultisampleRequested(false); // this is an OculusWorldDemo parameter
    desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc.SampleDesc.Count = 4;
    desc.MiscFlags = 0;
    deviceD3D11->CreateTexture2D(&desc, NULL, &
    d3dColorbuffer);
    m_hInteropObjects[0] = wglDXRegisterObjectNV(m_hInteropDevice,
    d3dColorbuffer,
    m_gltextures[0],
    GL_TEXTURE_2D,
    WGL_ACCESS_READ_WRITE_NV);
    // GL can render to d3dColorbuffer once appropriately bound and set


    // IN OVR eye render code (e.g., RenderEyeView()),
    ID3D11Texture2D *pEyeTexture=nullptr;
    ovrTextureSwapChain oTSC = DrawEyeTargets[0]->pColorTex->Get_ovrTextureSet();
    int swapChainLength = 0;
    ovr_GetTextureSwapChainLength(Session, oTSC, &swapChainLength);
    int currentIndex = 0;
    ovr_GetTextureSwapChainCurrentIndex(Session, oTSC, &currentIndex);
    ovr_GetTextureSwapChainBufferDX(Session, oTSC, currentIndex, IID_ID3D11Texture2D, (void **)&pEyeTexture);
    deviceContextD3D11->ResolveSubresource(pEyeTexture, 0, dxColorbuffer, 0,
    DXGI_FORMAT_R8G8B8A8_UNORM);


    //Method 3: OVR-MSAA , GL-MSAA
    MultisampleRequested(true); // this is an OculusWorldDemo parameter
    desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc.SampleDesc.Count = 4;

    desc.MiscFlags = 0;
    deviceD3D11->CreateTexture2D(&desc, NULL, &
    d3dColorbuffer);
    m_hInteropObjects[0] = wglDXRegisterObjectNV(m_hInteropDevice,
    d3dColorbuffer,
    m_gltextures[0],
    GL_TEXTURE_2D,
    WGL_ACCESS_READ_WRITE_NV);
    // GL can render to d3dColorbuffer once appropriately bound and set


    // IN OVR eye render code (e.g., RenderEyeView()),
    ID3D11Texture2D *pEyeTexture=nullptr;
    ovrTextureSwapChain oTSC = DrawEyeTargets[0]->pColorTex->Get_ovrTextureSet();
    int swapChainLength = 0;
    ovr_GetTextureSwapChainLength(Session, oTSC, &swapChainLength);
    int currentIndex = 0;
    ovr_GetTextureSwapChainCurrentIndex(Session, oTSC, &currentIndex);
    ovr_GetTextureSwapChainBufferDX(Session, oTSC, currentIndex, IID_ID3D11Texture2D, (void **)&pEyeTexture);
    deviceContextD3D11->CopyResource(pEyeTexture, dxColorbuffer);



    This topic  is too complex to write a recipe here.  The above should just be used as sticking-point guide.



































    knchaffinAuthor
    Up and Coming
    April 24, 2019
    I feel the need to write a retrospective summary here as this journey was long and convoluted.

    I decided to attempt to use NV_DX_interop2 in order to have a Windows VR app that could concurrently share a render target texture between DX and GL code.  When using the interop, it is necessary for the DX application to register the DX render target texture with the  created GL interop device via wglDXRegisterObjectNV() .  When I attempted to so register an OVR SDK/API created render target color texture, the registration would fail silently and the HANDLE hInteropObject return was NULL.

    Based on other forum members' suggestion that OVR textures could not be registered for interop and that I should create my own manually at the ID3D11Texture2D level, I began doing so.  But, I found that it was very difficult to create a texture that would register via wglRegisterObjectNV() and could also be copied to an OVR eye view render target texture via Context->CopyResource().  To make a long story shorter, the key was to create the manual DX texture based on the OVR eye view render target color texture "description" and then override two (or three) description members before creating the texture from the description using Device->CreateTexture2D(&desc, NULL, &d3dColorbuffer).

    The two overrides are:
    desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;  // OVR creates textures with DXGI_FORMAT_R8G8B8A8_TYPELESS

    and
    desc.MiscFlags  = 0;  //OVR creates textures with MiscFlags=2;

    If either of these are not overridden, wglDXRegisterObjectNV()) returns NULL.

    It is also important to understand the following:

    desc.SampleDesc.Count;  // in OVR, if MutltiSampleRequested= true, Count=4, otherwise Count=1. 
    This is important to set the Count  setting in the shared DX/GL render texture and the OVR created eye render target  swapchain textures such that the DX/GL manually created texture (rendered to by GL) can be copied to the OVR eye render target swapchain textures via Context->CopyResource() or Context->CopySubresourceRegion()  to copy SSAA to SSAA or MSAA to MSAA, or Context->ResolveSubresource() method to copy MSAA to SSAA.

    So, now I know why my interop registration was failing or my texture copy did nothing.

    Oh yeah, I have SRGB turned off in my app to avoid dealing with that complication.


    knchaffinAuthor
    Up and Coming
    April 28, 2019
    @thewhiteambit

    I have my NV_DX_interop2 code working with a single color texture and single depth texture being shared and the the GL rendered color texture being copied to the DX left eye view render target color texture as we have talked about before.  Before I expand this to support left and right eye views, I want to figure out how to blend the source and destination color and depth textures, rather than just doing the simple copy and replace CopyResource(), etc.

    As I am envisioning this, I could use a Compute Shader to do a per pixel comparision of the src and dst depth texture and determine how to combine the two color textures based on which occludes the other.  With that info, I would update the dst depth per pixel values and do a src and dst color pixel blending as desired.

    Does this make sense to you?  I believe you said elsewhere that you have done such Compute Shaders successfully.  If so, did you do them in an DX/GL interop environment and if so, were there any complications?  It's been a few years since I was working with complex HLSL parallel compute shaders, so I will have to dust off my toolkit.

    Both my shared color and depth textures are set up with binding flags of:
    desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
    So it seems this type of compute shader should work.

    For the time being I have these created as SSAA.  I will have to study up on how to do such a compute shader for MSAA textures.

    Thanks in advance.  It sure is nice to see GL and DX on screen at the same time:)



    Inspiring
    April 28, 2019
    @knchaffin
    yes, compute shaders are what you want to use for all that. I can confirm that I successfully used them on interop, depth and MSAA. In most cases they also have the huge advantage for VR to not use the full 3D pipeline and therefor work best in parallel without interrupting/blocking VR for to long.
    knchaffinAuthor
    Up and Coming
    April 29, 2019

    Thanks for the existence proof testimony.  All of my past Direct Compute work was done with Unity and C# scripts, so I have to realign my thinking to C++.  Just glancing at my old HLSL compute files inspires me to ask whether you were able to use ID3D11Texture2D render target/ shader resource textures directly in the compute shader, or did you have to mess with UAV's or similar?  Are the DX render target textures considered RWTextures inside of the shader?

    Thanks.

    knchaffinAuthor
    Up and Coming
    April 30, 2019
    no content.  can't delete.
    Inspiring
    April 30, 2019
    Of course its messing around with UAV. But can simply create them from ID3D11Texture2D when the texture was created with
    desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
    knchaffinAuthor
    Up and Coming
    April 30, 2019
    @thewhiteambit

    Thanks, I am using desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS on my manually created DX/GL shared texture.  It remains to be seen whether I can use the compute shader to "composite" the shared color and depth texture and the eye view render targets color and depth textures.  The compute shader needs to be able to R/W from both of these textures (plus the corresponding depth textures).  The eye view render target texture is set up by OVR to be bound to a RTV and SRV .  I don't think I can also bind it to a UAV I create, nor can I use desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS on that OVR texture.  The compute shader will probably have to use a mixture of UAV, RTV and SRV textures.

    I'm still wrapped up in relearning how to create HLSL compute shaders and then learning how to access them from C++.

    Thanks again for your info.  I feel better knowing I have selected the right path to get started on this task.

    Inspiring
    April 30, 2019
    @knchaffin
    Thats part of why I am so upset about OVR API layout. It is probably not possible and you need an extra texture for compositing that will be used for final copy to the OVR eye texture. This is completely unnecessary and (sorry) stupid behavior of the API. They used to have it a different style on (pre) 0.8 API. I have no clue why they did this without even having the option to provide the texture by yourself.