用于光线追踪的 DXR 描述符堆管理

如何解决用于光线追踪的 DXR 描述符堆管理

在观看了有关 DXR 和 DX12 的视频和文档后,我仍然不确定如何管理 DX12 光线跟踪 (DXR) 的资源。

光栅化和光线追踪在资源管理方面有很大区别,主要区别在于光栅化有很多可以动态绑定的时间资源,而光线追踪需要所有资源 在投射光线时准备就绪。原因很明显,一条光线可以击中整个场景中的任何东西,所以在我们投射一条光线之前,我们需要准备好每个着色器、每个纹理、每个堆并填充数据。

到目前为止一切顺利。

我的第一个测试是将所有资源添加到单个堆中 - 基于一些 DXR 教程。这种方法的问题在于具有相同着色器但具有不同纹理的对象。我为我的单个命中组定义了 1 个着色器根签名,我必须在光线跟踪之前准备好。但是在创建根签名时,我们必须准确地告诉堆中的哪个位置对应纹理所在的SRV。由于堆中有许多具有不同位置的纹理,因此我需要为每个具有不同纹理的对象创建 1 个根签名。这当然不是首选,因为基于文档和常识,我们应该保持根签名量尽可能小。 因此,我放弃了这个测试。

我的第二种方法是为每个对象创建一个描述符堆,其中包含这个特定对象的所有本地描述符(纹理、常量等)。全局资源 = TLAS(顶级加速结构),输出和相机常量缓冲区全局保存在单独的堆中。在这方法中,我认为我误解了文档,认为我可以向根签名添加多个堆。在我写这篇文章时,我找不到将 2 个单独的堆添加到单个根签名的方法。如果可能的话,我很想知道怎么做,因此非常感谢您的帮助。

这里是我用于根签名的代码(使用 dx12 助手):

bool Pipelinestate::CreateHitSignature(Microsoft::WRL::ComPtr<ID3D12RootSignature>& signature)
{
    const auto device = RaytracingModule::GetInstance()->GetDevice();
    if (device == nullptr)
    {
        return false;
    }

    nv_helpers_dx12::RootSignatureGenerator rsc;

    rsc.AddRootParameter(D3D12_ROOT_ParaMETER_TYPE_SRV,0);  // "t0" vertices and colors

    // Add a single range pointing to the TLAS in the heap
    rsc.AddHeapRangesParameter({
        {2 /*t2*/,1,D3D12_DESCRIPTOR_RANGE_TYPE_SRV,1},/* 2nd slot of the first heap */
        {3 /*t3*/,3},/* 4nd slot of the first heap. Per-instance data */
    });

    signature = rsc.Generate(device,true);

    return signature.Get() != nullptr;
}

现在我的最后一个方法是创建一个包含所有必要资源的堆 -> 每个对象的 TLAS、CBV、SRV(纹理)等 = 每个对象有效的 1x 堆。同样,当我阅读文档时,不建议这样做,文档说明我们应该将资源分组到全局堆。在这一点上,我有一种感觉,我正在混合 DX12 和 DXR 文档和最佳实践,通过在 DXR 领域使用来自 DX12 的建议,这可能是错误的。

我还部分阅读了 Nvidia Falcor 源代码,它们似乎每个描述符类型有 1 个资源堆,有效地将描述符堆的数量限制为最少(完全有意义),但我没有发现根签名是如何创建的具有多个单独的堆。

我觉得我错过了这个谜团的最后一个谜题部分,然后才全部落到位并创造出美丽的图像。因此,如果有人能解释在 DXR 中应该如何处理资源管理(堆、描述符等),如果我们想要拥有许多不同资源的对象,那将对我有很大帮助。

所以提前致谢! 雅库布

解决方法

使用 DXR,您需要从着色器模型 6.2 开始,其中动态索引开始获得更多官方支持,而不仅仅是“最后一个描述符在看似超限的索引中自由泄漏”,这是 5.1 中的“秘密”方法

现在您拥有使用 type var[] : register(t4,1); 声明式语法的完整“无绑定”,您可以自由索引 var[1] 将访问寄存器 (t5,1) 等。
您可以在描述符表中设置寄存器范围,因此如果您有 100 个纹理,则可以跨越 100 个。
你甚至可以在数组变量之后声明其他资源,只要你记得跳转所有寄存器。但是使用不同的虚拟空间更容易:

float4 ambiance : register(b0,0);
Texture2D all_albedos[] : register(t0,1);
matrix4x4 world : register(b1,0);

现在您可以转到 t100 而不会干扰以下 space0 声明。
SM6 中取消了对寄存器值的限制。这是

最多支持的最大堆分配

所以 all_albedos[3400].Sample(..) 是一个完全可以接受的调用(前提是你的堆已经绑定了视图)。

不幸的是,在 DX12 中,它们让您感觉可以使用 CommandList::SetDescriptorHeaps 函数绑定多个堆,但如果您尝试,则会出现运行时错误:

D3D12 ERROR: ID3D12CommandList::SetDescriptorHeaps: pDescriptorHeaps[1] sets a descriptor heap type that appears earlier in the pDescriptorHeaps array.
Only one of any given descriptor heap type can be set at a time. [ EXECUTION ERROR #554: SET_DESCRIPTOR_HEAP_INVALID]

它具有误导性,因此不要相信方法名称中的复数 s
真的,如果我们有多个堆,那只是因为三重缓冲循环更新/使用情况,或者我想上传/着色器可见。只需将所有内容放在您的一个堆中,并根据需要让描述符表在其中索引。

描述符表是一个非常轻量级的元素,它只有 3 个整数。一个描述符开始,一个跨度和一个虚拟空间。只需使用它,如果您的场景中有 1000 个纹理,您就可以跨越 1000 个纹理。如果将材质 ID 嵌入到具有独特 UV(如光照贴图)的间接纹理中,则可以获得材质 ID。或者在顶点数据中,或者只是整个命中组(如果您设置为 1 个命中组 = 1 个对象)。您的命中组索引(由着色器中的系统值给出)将成为您的纹理索引。

,

HLSL 5.1 的动态索引可能是解决此问题的方法。

https://docs.microsoft.com/en-us/windows/win32/direct3d12/dynamic-indexing-using-hlsl-5-1

  • 通过动态索引,我们可以创建一个包含所有材质的堆,并为每个对象使用一个索引,该索引将在着色器中用于在运行时获取正确的材质
  • 因此,我们不需要多个相同类型的堆,因为无论如何这是不可能的。每种堆类型只能同时使用 1 个堆

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?