You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 15:45:14