DirectX12创建2D纹理渲染全黑 触发GPU资源状态不兼容错误
问题定位与修复方案
1. 触发GPU验证报错的直接原因
报错明确指出:绑定为SRV的纹理资源依然处于D3D12_RESOURCE_STATE_COPY_DEST状态,不满足像素着色器读取SRV所需的资源状态要求。
- 根因:你在纹理上传流程中录制的资源过渡(从COPY_DEST转为PIXEL_SHADER_RESOURCE)操作,从未被提交到GPU队列执行。大量实现中会复用渲染用的命令列表对象,若录制完上传、过渡命令后没有执行命令队列提交、栅栏同步流程,这些命令就会一直停留在CPU端的录制缓冲区,不会在GPU端执行。到绘制阶段时,纹理依然停留在创建时设置的COPY_DEST初始状态,自然无法被着色器读取。
- 正确做法:将纹理上传流程和每帧渲染的命令列表录制流程解耦:
- 初始化阶段录制独立的上传命令列表,完成数据拷贝、资源状态转换后,立即提交给命令队列,通过栅栏等待GPU完成所有上传操作后再关闭命令列表
- 确认录制的资源过渡操作在命令提交顺序上,先于后续的绘制绑定操作执行。
2. 即使解决资源状态问题也会导致全黑的隐藏Bug
2.1 子资源内存跨度计算完全错误
你的代码中手动计算了子资源的Pitch参数:
subresource.RowPitch = m_Height * m_BytesPerPixel; subresource.SlicePitch = subresource.RowPitch * m_Width;
存在两处核心错误:
- 逻辑颠倒:行跨度(RowPitch)是单条图像行的内存长度,应该为
m_Width * m_BytesPerPixel,你错将高度纳入行跨度计算;切片跨度(SlicePitch)是整个子资源的总内存长度,应该为行跨度乘以图像高度,你错将宽度纳入切片跨度计算。 - 不满足内存对齐要求:D3D12规定纹理的行跨度必须对齐到256字节边界,手动计算的数值不满足该规则,会导致上传的像素数据错位,采样得到全黑或错误内容。
- 正确做法:调用
GetCopyableFootprints时传入预分配的D3D12_PLACED_SUBRESOURCE_FOOTPRINT数组,从返回的Footprint结构中读取对齐后的实际行跨度、切片跨度,替换手动计算的错误值。
2.2 纹理格式与输入图像数据不匹配
你硬编码设置纹理格式为DXGI_FORMAT_R16G16B16A16_UNORM,但stb_image默认加载的是8位每通道的图像数据(每像素4字节,对应格式为DXGI_FORMAT_R8G8B8A8_UNORM)。16位每通道的纹理要求每个像素占8字节,格式不匹配会导致着色器采样时读取到错误的内存区域,输出全黑。
- 正确做法:根据stb_image返回的
m_BytesPerPixel动态匹配纹理格式,不要硬编码高位宽格式。
2.3 SRV描述符堆属性配置遗漏
检查你创建m_SRVHeap时的Flags参数,必须设置D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE。如果未开启该标记,描述符堆的内容是无法被GPU着色器访问的,哪怕绑定流程完全正确,也会读不到SRV内容。
修复后验证要点
- 初始化阶段上传纹理后,通过栅栏确认GPU执行完上传、状态转换操作后,再开始进入渲染循环
- 确保纹理上传的命令提交顺序早于第一帧的绘制调用
- 确认SRV堆是可被着色器访问的堆类型
- 检查像素着色器的纹理寄存器映射与根签名、SRV描述符表的寄存器编号完全一致
内容的提问来源于stack exchange,提问作者Amir
相关产品推荐
相关产品推荐

