上传 2D 或 3D 纹理数据类似于上传 1D 数据,不同之处在于应用程序需要更加关注与行间距相关的数据对齐方式。 缓冲区可以独立并发地从图形管道的多个部分使用,并且非常灵活。
通过缓冲区上传纹理数据
应用程序必须通过 ID3D12GraphicsCommandList::CopyTextureRegion 或 ID3D12GraphicsCommandList::CopyBufferRegion 上传数据。 纹理数据比其他资源数据更可能较大、被反复访问,并且更能从非线性内存布局的改进缓存一致性中受益。 在 D3D12 中使用缓冲区时,只要满足内存对齐要求,应用程序就可以完全控制与复制资源数据相关的数据放置和排列。
此示例详细说明了应用程序如何在将 2D 数据转换为 1D 后再放置到缓冲区中。 对于 mipmap 2D 方案,应用程序可以离散地平展每个子资源并快速使用 1D 子分配算法,或者使用更复杂的 2D 子分配技术来最大程度地减少视频内存利用率。 第一种方法应更频繁地使用,因为它更简单。 将数据打包到磁盘或跨网络时,第二种方法可能很有用。 在任一情况下,应用程序仍必须为每个子资源调用复制 API。
// Prepare a pBitmap in memory, with bitmapWidth, bitmapHeight, and pixel format of DXGI_FORMAT_B8G8R8A8_UNORM.
//
// Sub-allocate from the buffer for texture data.
//
D3D12_SUBRESOURCE_FOOTPRINT pitchedDesc = { 0 };
pitchedDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
pitchedDesc.Width = bitmapWidth;
pitchedDesc.Height = bitmapHeight;
pitchedDesc.Depth = 1;
pitchedDesc.RowPitch = Align(bitmapWidth * sizeof(DWORD), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
//
// Note that the helper function UpdateSubresource in D3DX12.h, and ID3D12Device::GetCopyableFootprints
// can help applications fill out D3D12_SUBRESOURCE_FOOTPRINT and D3D12_PLACED_SUBRESOURCE_FOOTPRINT structures.
//
// Refer to the D3D12 Code example for the previous section "Uploading Different Types of Resources"
// for the code for SuballocateFromBuffer.
//
SuballocateFromBuffer(
pitchedDesc.Height * pitchedDesc.RowPitch,
D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT
);
D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTexture2D = { 0 };
placedTexture2D.Offset = m_pDataCur – m_pDataBegin;
placedTexture2D.Footprint = pitchedDesc;
//
// Copy texture data from DWORD* pBitmap->pixels to the buffer
//
for (UINT y = 0; y < bitmapHeight; y++)
{
UINT8 *pScan = m_pDataBegin + placedTexture2D.Offset + y * pitchedDesc.RowPitch;
memcpy( pScan, &(pBitmap->pixels[y * bitmapWidth]), sizeof(DWORD) * bitmapWidth );
}
//
// Create default texture2D resource.
//
D3D12_RESOURCE_DESC textureDesc { ... };
CComPtr<ID3D12Resource> texture2D;
d3dDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, &textureDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&texture2D) );
//
// Copy heap data to texture2D.
//
commandList->CopyTextureRegion(
&CD3DX12_TEXTURE_COPY_LOCATION( texture2D, 0 ),
0, 0, 0,
&CD3DX12_TEXTURE_COPY_LOCATION( m_spUploadHeap, placedTexture2D ),
nullptr );
请注意帮助程序结构的用法 CD3DX12_HEAP_PROPERTIES 和 CD3DX12_TEXTURE_COPY_LOCATION,以及 CreateCommittedResource 和 CopyTextureRegion 的方法。
复制
D3D12 方法使应用程序能够替换 D3D11 UpdateSubresource、 CopySubresourceRegion 和资源初始数据。 单个包含一定数量行主纹理数据的3D子资源可能位于缓冲区资源中。 CopyTextureRegion 可以将该纹理数据从缓冲区复制到具有未知纹理布局的纹理资源,反之亦然。 应用程序应首选这种类型的技术来填充经常访问的 GPU 资源,方法是在 UPLOAD 堆中创建大型缓冲区,同时在没有 CPU 访问权限的 DEFAULT 堆中创建经常访问的 GPU 资源。 这种技术有效地支持离散 GPU 及其大量 CPU 不可访问的内存,而不会经常损害 UMA 体系结构。
请注意以下两个常量:
const UINT D3D12_TEXTURE_DATA_PITCH_ALIGNMENT = 256;
const UINT D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT = 512;
除非 D3D12_FEATURE_DATA_D3D12_OPTIONS13::UnrestrictedBufferTextureCopyPitchSupported 为 TRUE,否则每个子资源的行间距必须与 D3D12_TEXTURE_DATA_PITCH_ALIGNMENT(256)的倍数对齐,并且每个子资源的起始偏移必须与 D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT(512)的倍数对齐。
如果行间距小于 D3D12_TEXTURE_DATA_PITCH_ALIGNMENT(256),这是纹理的最小 mip 层的典型情况,则行间距对齐后,D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT(512)的子资源偏移对齐可能会导致额外的填充,这超出了通过行间距对齐所添加的填充。
- D3D12_SUBRESOURCE_FOOTPRINT
- D3D12_PLACED_SUBRESOURCE_FOOTPRINT
- D3D12_TEXTURE_COPY_LOCATION
- D3D12_TEXTURE_COPY_TYPE
- ID3D12Device::GetCopyableFootprints
- ID3D12GraphicsCommandList::CopyResource
- ID3D12GraphicsCommandList::CopyTextureRegion
- ID3D12GraphicsCommandList::CopyBufferRegion
- ID3D12GraphicsCommandList::CopyTiles
- ID3D12CommandQueue::UpdateTileMappings
映射和取消映射
映射和取消映射可以安全地由多个线程调用。 对 Map 的第一次调用为资源分配 CPU 虚拟地址范围。 对 Unmap 的最后一次调用会解除分配 CPU 虚拟地址范围。 CPU 虚拟地址通常返回到应用程序。
每当通过读回堆中的资源在 CPU 和 GPU 之间传递数据时,必须使用Map 和 Unmap,以支持所有支持 D3D12 的系统。 将范围保持得尽可能紧密可以提高需要范围的系统上的效率(请参阅 D3D12_RANGE)。
调试工具的性能受益于在所有 映射 / Unmap 调用中准确使用范围,以及当不再需要 CPU 修改时,应用程序主动取消映射资源。
D3D12 不支持使用 Map (具有 DISCARD 参数集)重命名资源的 D3D11 方法。 应用程序必须自行实现资源重命名。 所有 映射 调用都隐式NO_OVERWRITE和多线程调用。 应用程序负责确保在使用 CPU 访问数据之前完成命令列表中包含的任何相关 GPU 工作。 对 Map 的 D3D12 调用不会隐式刷新任何命令缓冲区,也不会阻止等待 GPU 完成工作。 因此,在某些情况下,映射 和 取消映射 操作甚至可能被优化掉。
缓冲区对齐方式
缓冲区对齐限制:
- 线性子资源复制必须与D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT(512)字节对齐(行间距与D3D12_TEXTURE_DATA_PITCH_ALIGNMENT(256)字节对齐)。
- 常量数据读取必须从堆的起始位置开始,并且是 256 字节的倍数(即仅从与 256 字节对齐的地址读取)。
- 索引数据的读取必须是索引数据类型大小的整数倍(即仅从自然对齐至该数据的地址进行读取)。
- ID3D12GraphicsCommandList::ExecuteIndirect 数据必须来自 4 的倍数的偏移量(即仅来自 DWORD 对齐的地址)。