如何创建DXGI_FORMAT_420_OPAQUE格式Texture2D并传入YUV420p数据
如何使用DXGI_FORMAT_420_OPAQUE纹理作为视频处理器输入并填充YUV420p数据
1. 创建DXGI_FORMAT_420_OPAQUE纹理
DXGI_FORMAT_420_OPAQUE属于平面格式,不支持在CreateTexture2D时指定初始数据,也无法通过UpdateSubresource更新内容。必须通过上传堆纹理暂存数据,再用CopyTextureRegion将数据复制到默认堆的目标纹理中(视频处理器仅支持默认堆资源)。
代码示例:创建目标纹理与上传堆纹理
// 视频分辨率示例:1920x1080 UINT width = 1920; UINT height = 1080; UINT yPlaneSize = width * height; UINT uvPlaneSize = (width / 2) * (height / 2) * 2; // UV平面每个像素占2字节 // 创建默认堆目标纹理(视频处理器输入用) D3D12_RESOURCE_DESC targetTexDesc = {}; targetTexDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; targetTexDesc.Width = width; targetTexDesc.Height = height; targetTexDesc.MipLevels = 1; targetTexDesc.ArraySize = 1; targetTexDesc.Format = DXGI_FORMAT_420_OPAQUE; targetTexDesc.SampleDesc.Count = 1; targetTexDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; targetTexDesc.Flags = D3D12_RESOURCE_FLAG_NONE; ID3D12Resource* pYUVTargetTex = nullptr; HRESULT hr = pDevice->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &targetTexDesc, D3D12_RESOURCE_STATE_COPY_DEST, // 初始状态设为复制目标 nullptr, IID_PPV_ARGS(&pYUVTargetTex) ); // 创建Y平面上传纹理(存储Y通道数据) D3D12_RESOURCE_DESC yUploadDesc = {}; yUploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; yUploadDesc.Width = width; yUploadDesc.Height = height; yUploadDesc.MipLevels = 1; yUploadDesc.Format = DXGI_FORMAT_R8_UNORM; yUploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; ID3D12Resource* pYUploadTex = nullptr; hr = pDevice->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &yUploadDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pYUploadTex) ); // 映射并填充Y平面数据 BYTE* pYData = nullptr; CD3DX12_RANGE readRange(0, 0); // 仅写入,不读取 hr = pYUploadTex->Map(0, &readRange, reinterpret_cast<void**>(&pYData)); if (SUCCEEDED(hr)) { memcpy(pYData, pYourYUVBuffer, yPlaneSize); // pYourYUVBuffer为YUV420p的Y数据起始指针 pYUploadTex->Unmap(0, nullptr); } // 创建UV平面上传纹理(存储UV通道数据) D3D12_RESOURCE_DESC uvUploadDesc = {}; uvUploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; uvUploadDesc.Width = width / 2; uvUploadDesc.Height = height / 2; uvUploadDesc.MipLevels = 1; uvUploadDesc.Format = DXGI_FORMAT_R8G8_UNORM; uvUploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; ID3D12Resource* pUVUploadTex = nullptr; hr = pDevice->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &uvUploadDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pUVUploadTex) ); // 映射并填充UV平面数据 BYTE* pUVData = nullptr; hr = pUVUploadTex->Map(0, &readRange, reinterpret_cast<void**>(&pUVData)); if (SUCCEEDED(hr)) { memcpy(pUVData, pYourYUVBuffer + yPlaneSize, uvPlaneSize); // UV数据位于Y数据之后 pUVUploadTex->Unmap(0, nullptr); }
复制数据到目标纹理
// 复制Y平面数据 pCmdList->CopyTextureRegion( &CD3DX12_TEXTURE_REGION(0, 0, 0, 0, 0, 0, width, height, 1), // 目标:Y平面(z=0) pYUVTargetTex, 0, &CD3DX12_TEXTURE_REGION(0, 0, 0, 0, 0, 0, width, height, 1), // 源:Y上传纹理 pYUploadTex, 0 ); // 复制UV平面数据 pCmdList->CopyTextureRegion( &CD3DX12_TEXTURE_REGION(0, 0, 1, 0, 0, 0, width/2, height/2, 1), // 目标:UV平面(z=1) pYUVTargetTex, 0, &CD3DX12_TEXTURE_REGION(0, 0, 0, 0, 0, 0, width/2, height/2, 1), // 源:UV上传纹理 pUVUploadTex, 0 ); // 将目标纹理状态转为视频处理器输入可用 pCmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition( pYUVTargetTex, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VIDEO_PROCESS_INPUT ));
2. 创建Y/UV平面的Shader Resource View(SRV)
按照MSDN要求,对同一纹理创建两个SRV,通过PlaneSlice参数区分Y/UV平面:
// 创建Y平面SRV D3D12_SHADER_RESOURCE_VIEW_DESC ySrvDesc = {}; ySrvDesc.Format = DXGI_FORMAT_R8_UNORM; // 单通道格式匹配Y平面 ySrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; ySrvDesc.Texture2D.MipLevels = 1; ySrvDesc.Texture2D.PlaneSlice = 0; // 指定Y平面 pDevice->CreateShaderResourceView(pYUVTargetTex, &ySrvDesc, hYSRVHandle); // hYSRVHandle为SRV堆中的句柄 // 创建UV平面SRV D3D12_SHADER_RESOURCE_VIEW_DESC uvSrvDesc = {}; uvSrvDesc.Format = DXGI_FORMAT_R8G8_UNORM; // 双通道格式匹配UV平面 uvSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; uvSrvDesc.Texture2D.MipLevels = 1; uvSrvDesc.Texture2D.PlaneSlice = 1; // 指定UV平面 pDevice->CreateShaderResourceView(pYUVTargetTex, &uvSrvDesc, hUVSRVHandle); // hUVSRVHandle为SRV堆中的句柄
3. 作为VideoProcessor输入完成格式转换
将SRV关联到视频处理器输入视图,调用VideoProcessorBlt转换为RGBA格式:
// 配置视频处理器输入参数 D3D12_VIDEO_PROCESS_INPUT_STREAM_ARGUMENTS inputArgs = {}; inputArgs.InputStream[0].Texture = pYUVTargetTex; inputArgs.InputStream[0].Subresource = 0; // 调用Blt转换为RGBA格式 pVideoProcessor->VideoProcessorBlt( pCmdList, pRGBATargetTex, // RGBA格式的渲染目标纹理 0, 1, &inputArgs );
关键注意事项
DXGI_FORMAT_420_OPAQUE纹理必须使用默认堆,视频处理器无法访问上传堆资源。- 必须通过
CopyTextureRegion传递数据,UpdateSubresource和初始数据参数均不支持该格式。 - SRV的
PlaneSlice参数是区分Y/UV平面的核心:0对应Y平面,1对应UV平面。
内容的提问来源于stack exchange,提问作者Grey
相关产品推荐
相关产品推荐

