【问题标题】:DX12, How to implement a standard texture class?DX12,如何实现标准纹理类?
【发布时间】:2025-12-16 11:50:01
【问题描述】:

我正在研究引擎的纹理类,但遇到了一个问题。

我们的 API 支持不同的操作,例如更新纹理的某些纹素以及从纹理中读取数据。

此纹理可以作为 UAV 或 SRV 绑定到管道。它也可以创建为 RTV 或 DSV。

我应该如何处理纹理的创建和更新?我应该将所有纹理创建为 UPLOAD 堆吗?这将是最标准的解决方案,因为我可以轻松地读写数据,但这也意味着更少的 GPU 带宽。

我还可以检测我是从文件创建纹理还是程序纹理,在第一种情况下,我会将纹理上传到 DEFAULT 堆。

你怎么看?

谢谢!

【问题讨论】:

  • 您可能想看看DirectX Tool Kit for DX12,尤其是DescriptorHeapResourceUploadBatch
  • 嗨@ChuckWalbourn,是的,您的代码对理解某些事情非常有帮助,谢谢!

标签: textures directx-12


【解决方案1】:

您将无法在上传堆中创建纹理,在尝试解决这些问题时,您将面临一个永恒的验证错误循环。这是因为纹理在内存中没有使用线性表示。但至少对您而言,复制和更新操作是您最关心的问题。

当你想在 CPU 和 GPU 之间进行传输时,你需要在上传或回读堆中创建一个缓冲区,使用 GetCopyableFootprints 知道如何在其中存储/读取图像并调用 CopyTextureRegion 来执行实际复制。如果你想支持部分更新和读取,这是几页代码,但是这个操作很简单。

在那之后,许多考虑因素会影响你的纹理类,你想如何抽象描述符堆中的视图,你想支持部分驻留的保留纹理,你想支持内存别名,你如何管理生命周期gpu 在更新/销毁它们、流式传输等时使用的纹理。没有完美的解决方案,这完全取决于您的需求。

【讨论】:

  • 嗨@galop1n,假设我想实现一个 ReadTexel() 方法,我的想法是创建一个 READBACK 资源,然后复制DEFAULT 资源中的数据。但是,ID3D12GraphicsCommandList::CopyTextureRegion() 将在 ReadTexel() 返回后很久才执行,我该如何处理?也许我应该有一个 CopyCommandList 并且我应该将这个命令发送到那里以便我能够在 ReadTexel() 方法中执行它?谢谢!
  • @Nacho ReadTexel 不是推荐的操作,除非真的有必要并且永远不应该同步 GPU 和 CPU!如果它位于从 CPU 内存创建的资源上,则只需标记您的图像以不丢弃内存并支付额外的存储价格。如果它是 GPU 生成的表面的 ReadTexel,那么您必须将其视为 std::future。然后以异步方式触发复制,等待操作完成后再读回它。您可以在执行CopyTextureRegion 后插入ID3D12Fence 并等待它达到正确的值。
  • 是的,我发现这一定是一项非常昂贵的手术!很有趣,因为在 Dx11 中你可以轻松地做到这一点,所以我猜驱动程序内部会做很多事情。我喜欢你对未来的态度,但这意味着我将在相当长的一段时间内没有结果。你觉得有一个单独的 CopyCmdList 怎么样?
  • 同样的建议适用于 D3D11,您只需等待几帧或使用查询等待完成,以便不同步 CPU 和 GPU。而关于使用复制队列,你可以而且应该,但这也意味着在复制之前使用栅栏来等待生成的表面,它不会更快地得到结果。