Advertisement Jump to content
Sign in to follow this  

[D3D12] Unbinding RTV and DSV

This topic is 971 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts



I am running into issues while trying to unbind RTVs and DSV after rendering G-Buffer.


According to Microsoft D3D12 multithreading sample, in order to unbind SRV you just need to bind a descriptor from the descriptor heap which has not been initialized by one of the Create*View functions.


I am doing pretty much the same thing for RTVs and DSV. I allocate contiguous descriptor range in the shader invisible RTV/DSV descriptor heaps but do not call CreateRenderTargetView and CreateDepthStencilView on them.


Since, my RTVs are located in contiguous range, I am able to call OMSetRenderTargets with a pointer to the first descriptor.

D3D12_CPU_DESCRIPTOR_HANDLE rtvHeapStart = pResources->m_RTVHeapStart;
D3D12_CPU_DESCRIPTOR_HANDLE dsvHeapStart = pResources->m_DSVHeapStart;
pCommandList->OMSetRenderTargets(3, &rtvHeapStart, TRUE, &dsvHeapStart);

  And to unbind them I am setting the pointer to the uninitialized descriptor ranges.

D3D12_CPU_DESCRIPTOR_HANDLE nullRTVHeapStart = pEnv->m_NullRTVHeapStart;
D3D12_CPU_DESCRIPTOR_HANDLE nullDSVHeapStart = pEnv->m_NullDSVHeapStart;
pCommandList->OMSetRenderTargets(3, &nullRTVHeapStart, TRUE, &nullDSVHeapStart);

But the debug layer runtime gives me the following errors:


D3D12 ERROR: ID3D12CommandList::OMSetRenderTargets: Specified CPU descriptor handle (ptr=0x00D8A310) has not been initialized. Render Target View descriptor handles must be initialized before being referenced by Command List API's. [ EXECUTION ERROR #646: INVALID_DESCRIPTOR_HANDLE]


D3D12 ERROR: ID3D12CommandList::OMSetRenderTargets: Specified CPU descriptor handle (ptr=0x00D89770) has not been initialized. Depth Stencil View descriptor handles must be initialized before being referenced by Command List API's. [ EXECUTION ERROR #646: INVALID_DESCRIPTOR_HANDLE]


How do I handle this correctly?

Many thanks



Share this post

Link to post
Share on other sites

Adam, many thanks for your prompt response! I was really hoping to get input from you :-)

After going through the documentation, which I have fully missed, I am wondering regarding the following:

  • Is it sufficient to have NULL descriptor on per-resource type level? That is, you have a NULL SRV for texture 2D with 4 mips and you would like to unbind SRV for texture 2D with 6 mips using it. WIll it work for textures with different multisample count or format as well?
  • Or does it have to be "really" on per-resource level? To unbind SRV with 4 mips, you need to use dedicated NULL SRV with 4 mips to ensure that the behaviour will not be hardware-dependent?
Edited by _void_

Share this post

Link to post
Share on other sites

It's sufficient for the type to match (Buffer, 1D, 2D, 3D), so don't worry about mip count, but the documentation isn't clear on whether 2DArray, Cube, CubeArray and 2DMS count differently to plain old 2D. I'll check with the team.

Share this post

Link to post
Share on other sites

Adam, does this mean that I am able to unbind Tex2D SRV with Tex2D NULL UAV descriptor or Tex2D DSV with Tex2D NULL RTV descriptor as long as they have the same resource type (Tex2D)?


Is it alright to create one NULL descriptor using Create*View function and populate the rest of the NULL descriptors in the heap by its copy, using CopyDescriptor function, to be able to unbind a range of descriptors?


Thanks again!

Edited by _void_

Share this post

Link to post
Share on other sites

They need to match at the level of the view dimension. Example code:

    static_assert(_countof(m_NullSRVs) == _countof(m_NullUAVs) &&
                  _countof(m_NullSRVs) == D3D10_SB_RESOURCE_DIMENSION_TEXTURECUBEARRAY+1,
                  "Size of the NULL view arrays has changed.");
        NullSRVDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
        NullSRVDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        NullUAVDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
        switch (i)
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
                NullSRVDesc.Buffer.FirstElement = 0;
                NullSRVDesc.Buffer.NumElements = 0;
                NullSRVDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
                NullSRVDesc.Buffer.StructureByteStride = 0;
                NullUAVDesc.Buffer.FirstElement = 0;
                NullUAVDesc.Buffer.NumElements = 0;
                NullUAVDesc.Buffer.StructureByteStride = 0;
                NullUAVDesc.Buffer.CounterOffsetInBytes = 0;
                NullUAVDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D;
                NullSRVDesc.Texture1D.MipLevels = 1;
                NullSRVDesc.Texture1D.MostDetailedMip = 0;
                NullSRVDesc.Texture1D.ResourceMinLODClamp = 0.0f;
                NullUAVDesc.Texture1D.MipSlice = 0;
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1DARRAY;
                NullSRVDesc.Texture1DArray.MipLevels = 1;
                NullSRVDesc.Texture1DArray.ArraySize = 1;
                NullSRVDesc.Texture1DArray.MostDetailedMip = 0;
                NullSRVDesc.Texture1DArray.FirstArraySlice = 0;
                NullSRVDesc.Texture1DArray.ResourceMinLODClamp = 0.0f;
                NullUAVDesc.Texture1DArray.ArraySize = 1;
                NullUAVDesc.Texture1DArray.MipSlice = 0;
                NullUAVDesc.Texture1DArray.FirstArraySlice = 0;
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
                NullSRVDesc.Texture2D.MipLevels = 1;
                NullSRVDesc.Texture2D.MostDetailedMip = 0;
                NullSRVDesc.Texture2D.PlaneSlice = 0;
                NullSRVDesc.Texture2D.ResourceMinLODClamp = 0.0f;
                NullUAVDesc.Texture2D.MipSlice = 0;                
                NullUAVDesc.Texture2D.PlaneSlice = 0;
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
                NullSRVDesc.Texture2DArray.MipLevels = 1;
                NullSRVDesc.Texture2DArray.ArraySize = 1;
                NullSRVDesc.Texture2DArray.MostDetailedMip = 0;
                NullSRVDesc.Texture2DArray.FirstArraySlice = 0;
                NullSRVDesc.Texture2DArray.PlaneSlice = 0;
                NullSRVDesc.Texture2DArray.ResourceMinLODClamp = 0.0f;
                NullUAVDesc.Texture2DArray.ArraySize = 1;
                NullUAVDesc.Texture2DArray.MipSlice = 0;
                NullUAVDesc.Texture2DArray.FirstArraySlice = 0;
                NullUAVDesc.Texture2DArray.PlaneSlice = 0;
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_UNKNOWN;
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_UNKNOWN;
                NullSRVDesc.Texture2DMSArray.ArraySize = 1;
                NullSRVDesc.Texture2DMSArray.FirstArraySlice = 0;
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
                NullSRVDesc.Texture3D.MipLevels = 1;
                NullSRVDesc.Texture3D.MostDetailedMip = 0;
                NullSRVDesc.Texture3D.ResourceMinLODClamp = 0.0f;
                NullUAVDesc.Texture3D.WSize = 1;
                NullUAVDesc.Texture3D.MipSlice = 0;
                NullUAVDesc.Texture3D.FirstWSlice = 0;
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_UNKNOWN;
                NullSRVDesc.TextureCube.MipLevels = 1;
                NullSRVDesc.TextureCube.MostDetailedMip = 0;
                NullSRVDesc.TextureCube.ResourceMinLODClamp = 0.0f;
                NullSRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
                NullUAVDesc.ViewDimension = D3D12_UAV_DIMENSION_UNKNOWN;
                NullSRVDesc.TextureCubeArray.MipLevels = 1;
                NullSRVDesc.TextureCubeArray.NumCubes = 1;
                NullSRVDesc.TextureCubeArray.MostDetailedMip = 0;
                NullSRVDesc.TextureCubeArray.First2DArrayFace = 0;
                NullSRVDesc.TextureCubeArray.ResourceMinLODClamp = 0.0f;

        if (NullSRVDesc.ViewDimension != D3D12_SRV_DIMENSION_UNKNOWN)
            m_NullSRVs[i] = m_SRVAllocator.AllocateHeapSlot();
            m_pDevice12->CreateShaderResourceView(nullptr, &NullSRVDesc, m_NullSRVs[i]);

        if (NullUAVDesc.ViewDimension != D3D12_UAV_DIMENSION_UNKNOWN)
            m_NullUAVs[i] = m_UAVAllocator.AllocateHeapSlot();
            m_pDevice12->CreateUnorderedAccessView(nullptr, nullptr, &NullUAVDesc, m_NullUAVs[i]);

The D3D10_SB_* enums come from driver-level defines, you can find it if you search around.


And yes, to answer your question, you can create the NULL descriptor once, and copy it from non-shader-visible descriptor heaps to multiple locations in a shader visible heap to unbind a range of descriptors.

Edited by Jesse Natalie

Share this post

Link to post
Share on other sites

Jesse Natalie, now I understand :-) Thank you for the code and clarifying the things!?

Share this post

Link to post
Share on other sites

What's your use-case for unbinding a descriptor anyway? Do you ever need a shader to read from a texture that doesn't exist?

Share this post

Link to post
Share on other sites

Hodgman, you write into G-Buffer RTVs and during the shading pass you read G-Buffer content through SRVs. In D3D11 you have to unbind them before that.

I guess, the need for explicit resource unbinding is applicable to D3D12 as well.

Edited by _void_

Share this post

Link to post
Share on other sites

No, you don't need to unbind them; you need to add a resource barrier to transition them from RTV to SRV.

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using, you agree to our community Guidelines, Terms of Use, and Privacy Policy. is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!