NVIDIAGameWorks / RayTracingDenoiser

NVIDIA Ray Tracing Denoiser
Other
504 stars 46 forks source link

Resource State Transition Error (REBLUR - Diffuse) #40

Closed kvnnap closed 1 year ago

kvnnap commented 2 years ago

Hi,

I am experiencing issues with the resource state of the output texture passed to the denoiser. The DirectX debug layer breaks with ERROR RESOURCE_MANIPULATION #527: RESOURCE_BARRIER_BEFORE_AFTER_MISMATCH. It seems as if the current state of the resource is different from the barrier being issued. This error is not deterministic, it sometimes occurs at frame 2, frame 3, frame 6, etc.

All input textures are in D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE and the output texture is in D3D12_RESOURCE_STATE_UNORDERED_ACCESS before executing the denoiser (with method nrd::Method::REBLUR_DIFFUSE). I used ID3D12DebugCommandList to verify this is the case. The NRI mapping of the textures is shown below:

nri::TextureD3D12Desc textureDesc = {};

// SRVs
textureDesc.d3d12Resource = *in_mv;
NRI->CreateTextureD3D12(*nriDevice, textureDesc, (nri::Texture*&)entryDescs[0].texture);
entryDescs[0].nextAccess = nri::AccessBits::SHADER_RESOURCE;
entryDescs[0].nextLayout = nri::TextureLayout::SHADER_RESOURCE;

.
.
.
// UAV
textureDesc.d3d12Resource = *out_diff_radiance_hitdist;
NRI->CreateTextureD3D12(*nriDevice, textureDesc, (nri::Texture*&)entryDescs[4].texture);
entryDescs[4].nextAccess = nri::AccessBits::SHADER_RESOURCE_STORAGE;
entryDescs[4].nextLayout = nri::TextureLayout::GENERAL;

I am using the NRD integration code and have compiled NRD (3.4.1.0) and NRI (1.84.0.0) DLLs for use with my renderer (running on 2080 Ti).

Part of the call stack:

NRI.dll!nri::CommandBufferD3D12::PipelineBarrier(const nri::TransitionBarrierDesc * transitionBarriers, const nri::AliasingBarrierDesc * aliasingBarriers, nri::BarrierDependency dependency) Line 526
    at C:\Users\kvnna\source\repos\NRI\Source\D3D12\CommandBufferD3D12.cpp(526)
NRI.dll!CmdPipelineBarrier(nri::CommandBuffer & commandBuffer, const nri::TransitionBarrierDesc * transitionBarriers, const nri::AliasingBarrierDesc * aliasingBarriers, nri::BarrierDependency dependency) Line 43
    at C:\Users\kvnna\source\repos\NRI\Source\D3D12\CommandBufferD3D12.hpp(43)
CandelaDXR.exe!NrdIntegration::Dispatch(nri::CommandBuffer & commandBuffer, nri::DescriptorPool & descriptorPool, const nrd::DispatchDesc & dispatchDesc, const std::array<NrdIntegrationTexture,34> & userPool, bool enableDescriptorCaching) Line 512
    at C:\Users\kvnna\source\repos\kvnnap\CandelaDXR\CandelaDXR\NVIDIA\NRDIntegration.hpp(512)
CandelaDXR.exe!NrdIntegration::Denoise(unsigned int consecutiveFrameIndex, nri::CommandBuffer & commandBuffer, const nrd::CommonSettings & commonSettings, const std::array<NrdIntegrationTexture,34> & userPool, bool enableDescriptorCaching) Line 406
    at C:\Users\kvnna\source\repos\kvnnap\CandelaDXR\CandelaDXR\NVIDIA\NRDIntegration.hpp(406)

Error:

D3D12 ERROR: ID3D12CommandList::ResourceBarrier: Before state (0x8: D3D12_RESOURCE_STATE_UNORDERED_ACCESS) of resource (0x000001FC972A13E0:'out_diff_radiance_hitdist') (subresource: 0) specified by transition barrier does not match with the current resource state (0x40: D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE) (assumed at first use) [ RESOURCE_MANIPULATION ERROR #527: RESOURCE_BARRIER_BEFORE_AFTER_MISMATCH]
D3D12: **BREAK** enabled for the previous message, which was: [ ERROR RESOURCE_MANIPULATION #527: RESOURCE_BARRIER_BEFORE_AFTER_MISMATCH ]
Exception thrown at 0x00007FFCB8A8474C (KernelBase.dll) in CandelaDXR.exe: 0x0000087A (parameters: 0x0000000000000001, 0x00000059CD548F40, 0x00000059CD54AD20).
Unhandled exception at 0x00007FFCB8A8474C (KernelBase.dll) in CandelaDXR.exe: 0x0000087A (parameters: 0x0000000000000001, 0x00000059CD548F40, 0x00000059CD54AD20).

Additionally, if I perform resource state assertions prior to calling the denoiser I get the following:

NRI.dll!nri::PipelineLayoutD3D12::SetDescriptorSetsImpl<0>(ID3D12GraphicsCommandList & graphicsCommandList, unsigned int baseIndex, unsigned int setNum, const nri::DescriptorSet * const * descriptorSets, const unsigned int * offsets) Line 130
    at C:\Users\kvnna\source\repos\NRI\Source\D3D12\PipelineLayoutD3D12.h(130)
NRI.dll!nri::PipelineLayoutD3D12::SetDescriptorSets(ID3D12GraphicsCommandList & graphicsCommandList, bool isGraphics, unsigned int baseIndex, unsigned int setNum, const nri::DescriptorSet * const * descriptorSets, const unsigned int * offsets) Line 156
    at C:\Users\kvnna\source\repos\NRI\Source\D3D12\PipelineLayoutD3D12.h(156)
NRI.dll!nri::CommandBufferD3D12::SetDescriptorSets(unsigned int baseIndex, unsigned int setNum, const nri::DescriptorSet * const * descriptorSets, const unsigned int * offsets) Line 293
    at C:\Users\kvnna\source\repos\NRI\Source\D3D12\CommandBufferD3D12.cpp(293)
NRI.dll!CmdSetDescriptorSets(nri::CommandBuffer & commandBuffer, unsigned int baseSlot, unsigned int descriptorSetNum, const nri::DescriptorSet * const * descriptorSets, const unsigned int * dynamicConstantBufferOffsets) Line 53
    at C:\Users\kvnna\source\repos\NRI\Source\D3D12\CommandBufferD3D12.hpp(53)
CandelaDXR.exe!NrdIntegration::Dispatch(nri::CommandBuffer & commandBuffer, nri::DescriptorPool & descriptorPool, const nrd::DispatchDesc & dispatchDesc, const std::array<NrdIntegrationTexture,34> & userPool, bool enableDescriptorCaching) Line 515
    at C:\Users\kvnna\source\repos\kvnnap\CandelaDXR\CandelaDXR\NVIDIA\NRDIntegration.hpp(515)
CandelaDXR.exe!NrdIntegration::Denoise(unsigned int consecutiveFrameIndex, nri::CommandBuffer & commandBuffer, const nrd::CommonSettings & commonSettings, const std::array<NrdIntegrationTexture,34> & userPool, bool enableDescriptorCaching) Line 406
    at C:\Users\kvnna\source\repos\kvnnap\CandelaDXR\CandelaDXR\NVIDIA\NRDIntegration.hpp(406)
D3D12 ERROR: CGraphicsCommandList::SetComputeRootDescriptorTable: Resource state (0x8: D3D12_RESOURCE_STATE_UNORDERED_ACCESS) (assumed at previous call to AssertResourceState) of resource (0x000002277F1C6BE0:'out_diff_radiance_hitdist') (subresource: 0) is invalid for use as a NON_PIXEL_SHADER_RESOURCE.  Expected State Bits (all): 0x40: D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, Assumed Actual State: 0x8: D3D12_RESOURCE_STATE_UNORDERED_ACCESS, Missing State: 0x40: D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE. This validation is being enforced at bind during command list recording because the descriptor for this resource in the root signature is declared as DATA_STATIC_WHILE_SET_AT_EXECUTE. [ EXECUTION ERROR #538: INVALID_SUBRESOURCE_STATE]
D3D12: **BREAK** enabled for the previous message, which was: [ ERROR EXECUTION #538: INVALID_SUBRESOURCE_STATE ]
Exception thrown at 0x00007FFCB8A8474C (KernelBase.dll) in CandelaDXR.exe: 0x0000087A (parameters: 0x0000000000000001, 0x0000003D400F8250, 0x0000003D400FA030).
Unhandled exception at 0x00007FFCB8A8474C (KernelBase.dll) in CandelaDXR.exe: 0x0000087A (parameters: 0x0000000000000001, 0x0000003D400F8250, 0x0000003D400FA030).

I am not sure why this is happening and I hope someone can help. Please do let me know should you require any further information.

dzhdanNV commented 2 years ago

Are you using modified state after invoking denoising?

// User inputs / outputs are not mipmapped, thus only 1 entry is needed.
// "TextureTransitionBarrierDesc::texture" is used to store the resource.
// "TextureTransitionBarrierDesc::next..." are used to represent current state of the subresource.
struct NrdIntegrationTexture
{
    nri::TextureTransitionBarrierDesc* subresourceStates;
    nri::Format format;
};
kvnnap commented 2 years ago

Yes, after calling the denoiser I add transition barriers if the state changed (see sync states section below). However, this code never gets to execute - the state is not changing at all. The only time that the state changes is when I examine the call stack after the break issued by the debug layer, but when that happens the denoiser function has not yet returned. I slightly refactored the code for readability.

The following is the part of code where I invoke the denoiser per frame.

struct DenoiserResource
{
  Resource* resource;
  D3D12_RESOURCE_STATES requiredState;
  nrd::ResourceType resourceType;
  nri::Format format;
};
// .
// .
nri::CommandBufferD3D12Desc cmdDesc = {};
cmdDesc.d3d12CommandList = pCurrentCommandList.Get();
cmdDesc.d3d12CommandAllocator = nullptr; // rendererResources->commandQueue->getCommandAllocator(pCurrentCommandList); // Not needed for NRD integration layer

nri::CommandBuffer* cmdBuffer = nullptr;
NRI->CreateCommandBufferD3D12(*nriDevice, cmdDesc, cmdBuffer);

// Wrap required textures
constexpr int N = 5;
nri::TextureTransitionBarrierDesc entryDescs[N] = {};
nri::Format entryFormat[N] = {};
DenoiserResource denoiserResources[N] = {
  { in_mv,                     D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE, nrd::ResourceType::IN_MV,                     nri::Format::RGBA32_SFLOAT },
  { in_normal_roughness,       D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE, nrd::ResourceType::IN_NORMAL_ROUGHNESS,       nri::Format::RGBA32_SFLOAT },
  { in_view_z,                 D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE, nrd::ResourceType::IN_VIEWZ,                  nri::Format::R32_SFLOAT    },
  { in_diff_radiance_hitdist,  D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE, nrd::ResourceType::IN_DIFF_RADIANCE_HITDIST,  nri::Format::RGBA32_SFLOAT },
  { out_diff_radiance_hitdist, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,    nrd::ResourceType::OUT_DIFF_RADIANCE_HITDIST, nri::Format::RGBA32_SFLOAT }
};

for (uint32_t i = 0; i < N; i++)
{
  // You need to specify the current state of the resource here, after denoising NRD can modify
  // this state. Application must continue state tracking from this point.
  // Useful information:
  //    SRV = nri::AccessBits::SHADER_RESOURCE, nri::TextureLayout::SHADER_RESOURCE
  //    UAV = nri::AccessBits::SHADER_RESOURCE_STORAGE, nri::TextureLayout::GENERAL
  auto& denResource = denoiserResources[i];
  nri::TextureD3D12Desc textureDesc = {};
  textureDesc.d3d12Resource = *denResource.resource;
  NRI->CreateTextureD3D12(*nriDevice, textureDesc, (nri::Texture*&)entryDescs[i].texture);
  entryDescs[i].nextAccess = GetResourceState(denResource.requiredState);
  entryDescs[i].nextLayout = entryDescs[i].nextAccess == nri::AccessBits::SHADER_RESOURCE ? nri::TextureLayout::SHADER_RESOURCE : nri::TextureLayout::GENERAL;
}

// Currently there is no animation, so passing same matrices for previous and current in the common settings
nrd::CommonSettings commonSettings{};
auto camera = rendererResources->camera;
memcpy(&commonSettings.worldToViewMatrix, &camera->getViewMatrix(), sizeof(commonSettings.worldToViewMatrix));
memcpy(&commonSettings.worldToViewMatrixPrev, &camera->getViewMatrix(), sizeof(commonSettings.worldToViewMatrixPrev));
memcpy(&commonSettings.viewToClipMatrix, &camera->getPerspectiveMatrix(), sizeof(commonSettings.viewToClipMatrix));
memcpy(&commonSettings.viewToClipMatrixPrev, &camera->getPerspectiveMatrix(), sizeof(commonSettings.viewToClipMatrixPrev));

nrd::ReblurSettings settings{};
settings.enableReferenceAccumulation = true;

NRD->SetMethodSettings(nrd::Method::REBLUR_DIFFUSE, &settings);

// Populate the user pool
NrdUserPool userPool = {};
for (uint32_t i = 0; i < N; i++)
{
  auto& denResource = denoiserResources[i];
  NrdIntegrationTexture tex{};
  tex.format = denResource.format;
  tex.subresourceStates = &entryDescs[i];
  NrdIntegration_SetResource(userPool, denResource.resourceType, tex);
}

//bool isUAV = pDbgCmdList->AssertResourceState(*out_diff_radiance_hitdist, 0, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
NRD->Denoise(currentBackBufferIndex, *cmdBuffer, commonSettings, userPool);
//isUAV = pDbgCmdList->AssertResourceState(*out_diff_radiance_hitdist, 0, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

// Sync states
for (uint32_t i = 0; i < N; i++)
{
  auto& denResource = denoiserResources[i];
  auto reqState = GetResourceState(denResource.requiredState);
  auto reqLayout = reqState == nri::AccessBits::SHADER_RESOURCE ? nri::TextureLayout::SHADER_RESOURCE : nri::TextureLayout::GENERAL;
  if (entryDescs[i].nextAccess != reqState || entryDescs[i].nextLayout != reqLayout)
  {
      denResource.resource->rewriteState(GetResourceState(entryDescs[i].nextAccess));
      denResource.resource->transistionBarrier(pCurrentCommandList, denResource.requiredState);
  }
}

//bool isUAV = pDbgCmdList->AssertResourceState(*out_diff_radiance_hitdist, 0, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
for (uint32_t i = 0; i < N; i++)
  NRI->DestroyTexture(*(nri::Texture*&)entryDescs[i].texture);

NRI->DestroyCommandBuffer(*cmdBuffer);

I am currently using these functions to translate between DirectX and NRI states;

D3D12_RESOURCE_STATES GetResourceState(nri::AccessBits accessBits)
{
    D3D12_RESOURCE_STATES result{};
    if (accessBits & nri::AccessBits::SHADER_RESOURCE)
        result |= D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
    if (accessBits & nri::AccessBits::SHADER_RESOURCE_STORAGE)
        result |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
    if (result == 0)
        throw std::runtime_error("Cannot convert nri::AccessBits");
    return result;
}

nri::AccessBits GetResourceState(D3D12_RESOURCE_STATES d3dResourceState)
{
    nri::AccessBits result{};
    if (d3dResourceState & D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE)
        result |= nri::AccessBits::SHADER_RESOURCE;
    if (d3dResourceState & D3D12_RESOURCE_STATE_UNORDERED_ACCESS)
        result |= nri::AccessBits::SHADER_RESOURCE_STORAGE;
    if (result == nri::AccessBits::UNKNOWN)
        throw std::runtime_error("Cannot convert D3D12_RESOURCE_STATES");
    return result;
}
kvnnap commented 1 year ago

Found the issue. The problem here is that enableDescriptorCaching (the fifth parameter of NRD->Denoise) is passed with default value true. However, the texture wrappers are being created per frame using NRI->CreateTextureD3D12 and hence enableDescriptorCaching needs to be false. I was also getting erroneous visual output due to this. Passing enableDescriptorCaching as false in this case solves the issue.

dzhdanNV commented 1 year ago

Thanks for not giving up. Since you are not the only one bumping into this, I will clarify enableDescriptorCaching even better (in the doc and code) in the upcoming update.