使用DirectStorage加载纹理到D3D12_TEXTURE2D的报错求助
错误原因分析
这个错误的核心是你分配的DirectStorage目标缓冲区大小不足以容纳D3D12纹理子资源的对齐后数据,具体原因包括:
- 手动计算缓冲区大小时,仅按纹理像素的原始尺寸(宽×高×单像素字节数)累加,忽略了D3D12对纹理行间距(RowPitch)和子资源布局的强制内存对齐要求——GPU要求纹理行间距必须是
D3D12_TEXTURE_DATA_PITCH_ALIGNMENT(通常是256或4096字节)的倍数,这会导致每个子资源的实际占用空间比原始像素数据大。 - 使用
DSTORAGE_REQUEST_DESTINATION_MULTIPLE_SUBRESOURCES时,DirectStorage会按照D3D12标准的子资源布局写入数据,但你创建的缓冲区总大小没有匹配这个布局的总开销,尤其是多mipmap层级的纹理,每个mip的子资源都有独立的对齐开销,总大小是所有子资源对齐后大小的总和,而非原始像素总和。 - D3D12MA分配缓冲区时,可能错误地设置了资源大小参数,导致实际分配的内存小于纹理子资源布局所需的总空间。
解决方法
1. 用官方API计算准确的纹理子资源总大小
绝对不要手动计算纹理内存大小,必须使用D3D12CalcSubresourceFootprints函数获取符合D3D12对齐要求的子资源布局,并累加得到总缓冲区大小:
// 从DirectXTex获取的纹理元数据生成D3D12资源描述 D3D12_RESOURCE_DESC texDesc = {}; texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; texDesc.Width = metadata.width; texDesc.Height = metadata.height; texDesc.MipLevels = metadata.mipLevels; texDesc.Format = metadata.format; texDesc.SampleDesc.Count = 1; texDesc.SampleDesc.Quality = 0; texDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; texDesc.Flags = D3D12_RESOURCE_FLAG_NONE; UINT numSubresources = D3D12CalcSubresourceCount( texDesc.MipLevels, texDesc.ArraySize, texDesc.SampleDesc.Count ); D3D12_SUBRESOURCE_FOOTPRINT footprints[128]; // 按需调整数组大小 UINT rowCounts[128]; UINT64 rowSizesInBytes[128]; UINT64 totalBufferSize = D3D12CalcSubresourceFootprints( &texDesc, 0, // 起始子资源索引 numSubresources, 0, // 缓冲区基地址偏移 footprints, rowCounts, rowSizesInBytes );
用计算出的totalBufferSize作为D3D12MA分配缓冲区的大小。
2. 匹配DirectStorage请求的子资源目标布局
设置DSTORAGE_REQUEST_DESTINATION_MULTIPLE_SUBRESOURCES时,需为DSTORAGE_REQUEST::Destinations数组填充每个子资源的偏移和大小,这些值必须和D3D12CalcSubresourceFootprints返回的footprints[i].Offset和footprints[i].SizeInBytes完全对应:
DSTORAGE_REQUEST request = {}; request.DestinationType = DSTORAGE_REQUEST_DESTINATION_MULTIPLE_SUBRESOURCES; request.DestinationsCount = numSubresources; request.Destinations = new DSTORAGE_SUBRESOURCE_DESTINATION[numSubresources]; for (UINT i = 0; i < numSubresources; ++i) { request.Destinations[i].Offset = footprints[i].Offset; request.Destinations[i].Size = footprints[i].SizeInBytes; request.Destinations[i].Destination = dstBuffer; // 你的DirectStorage目标缓冲区 }
3. 检查D3D12MA的缓冲区分配参数
分配DirectStorage目标缓冲区时,确保资源描述的Width(缓冲区字节数)设置为totalBufferSize,并满足DirectStorage的资源要求:
D3D12_RESOURCE_DESC bufferDesc = {}; bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; bufferDesc.Width = totalBufferSize; bufferDesc.Height = 1; bufferDesc.DepthOrArraySize = 1; bufferDesc.MipLevels = 1; bufferDesc.Format = DXGI_FORMAT_UNKNOWN; bufferDesc.SampleDesc.Count = 1; bufferDesc.SampleDesc.Quality = 0; bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; bufferDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; // DirectStorage要求 D3D12MA::ALLOCATION_DESC allocDesc = {}; allocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; // 或根据需求用DEFAULT堆 ComPtr<D3D12MA::Allocation> alloc; ComPtr<ID3D12Resource> dstBuffer; allocator->CreateResource( &allocDesc, &bufferDesc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, // DirectStorage写入前的状态 nullptr, &alloc, IID_PPV_ARGS(&dstBuffer) );
4. 验证CopyTextureRegion的参数正确性
执行纹理拷贝时,源的PlacedFootprint必须严格使用D3D12CalcSubresourceFootprints返回的对应子资源数据,禁止手动修改偏移或行间距:
for (UINT i = 0; i < numSubresources; ++i) { D3D12_TEXTURE_COPY_LOCATION srcLoc = {}; srcLoc.pResource = dstBuffer.Get(); srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; srcLoc.PlacedFootprint = footprints[i]; // 注意:这里的Offset要和缓冲区的基地址偏移一致,若缓冲区是Placed资源需调整,Upload堆则直接用footprints的Offset D3D12_TEXTURE_COPY_LOCATION dstLoc = {}; dstLoc.pResource = texResource.Get(); // 你的目标Texture2D dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; dstLoc.SubresourceIndex = i; commandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr); }
内容的提问来源于stack exchange,提问作者Shark_Bladder
相关产品推荐
相关产品推荐

