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

Display Duplication API帧缓冲区对齐异常求助:1366分辨率每行少2字节

解决Display Duplication API捕获非4倍数分辨率时的BGRA转RGB对齐问题及向量化优化

问题核心分析

当捕获1366x768这类水平分辨率非4倍数的桌面时,BGRA帧缓冲区出现每行少2字节的错位问题,本质是Display Duplication API返回的纹理存在硬件层面的隐式对齐——即使RowPitch等于output.width * 4,实际纹理的行数据边界未必与逻辑分辨率完全匹配。你的转换代码依赖RowPitch计算行偏移,但忽略了硬件对非对齐分辨率做的字节级调整,导致后续行的像素读取错位。

修复对齐问题的步骤

1. 确认纹理的实际物理宽度

不要直接使用桌面分辨率作为output.width,从捕获到的ID3D11Texture2D中获取真实的纹理参数:

D3D11_TEXTURE2D_DESC texDesc;
capturedTexture->GetDesc(&texDesc);
UINT actualWidth = texDesc.Width;
UINT actualRowPitch = texDesc.Width * 4; // BGRA格式下的行字节数

这里的actualWidth是硬件对齐后的纹理宽度,需以此为基准计算行偏移。

2. 修正行偏移计算

如果actualWidth大于逻辑桌面宽度,每行处理完后,源指针需要跳过(actualWidth - output.width) * 4字节;若RowPitch等于output.width * 4但仍有错位(如你的1366像素场景),说明硬件在每行末尾填充了2字节,需手动调整:

unsigned char* dstPtr = output.RGBOut;
const unsigned char* srcPtr = static_cast<unsigned char*>(mappedResource.pData);
UINT skipBytes = mappedResource.RowPitch - output.width * 4;
// 针对宽度模4余2的场景,手动补充偏移
if (skipBytes == 0 && (output.width % 4) == 2) {
    skipBytes = 2;
}

for(int y = 0; y < output.height; ++y){
    for(int x = 0; x < output.width; ++x){
        dstPtr[0] = srcPtr[0];
        dstPtr[1] = srcPtr[1];
        dstPtr[2] = srcPtr[2];
        srcPtr += 4;
        dstPtr += 3;
    }
    srcPtr += skipBytes;
}

3. 更可靠的方案:用D3D11视图裁剪逻辑分辨率

创建着色器资源视图(SRV),仅裁剪到逻辑桌面分辨率,确保读取时只获取有效区域的数据:

D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Texture2D.MostDetailedMip = 0;

ID3D11ShaderResourceView* srv = nullptr;
device->CreateShaderResourceView(capturedTexture, &srvDesc, &srv);

后续通过该SRV读取数据,可彻底规避对齐带来的额外字节干扰。

向量化优化方案

手动循环难以向量化,推荐两种高效方式:

1. 使用SSE/AVX SIMD指令集

利用CPU的SIMD单元批量处理像素,提升转换速度:

#include <smmintrin.h> // SSE4.1

for(int y = 0; y < output.height; ++y){
    __m128i* srcSIMD = reinterpret_cast<__m128i*>(const_cast<unsigned char*>(srcPtr));
    __m128i* dstSIMD = reinterpret_cast<__m128i*>(dstPtr);
    int x = 0;
    // 批量处理4个BGRA像素转RGB
    for(; x < output.width - 3; x +=4){
        __m128i bgra = _mm_loadu_si128(srcSIMD);
        // 提取B、G、R通道,丢弃Alpha
        __m128i bg = _mm_shuffle_epi8(bgra, _mm_set_epi8(-1,-1,-1,-1, 2,1,0,-1, 6,5,4,-1, 10,9,8,-1));
        __m128i r = _mm_shuffle_epi8(bgra, _mm_set_epi8(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 14,13,12,-1));
        __m128i rgb0 = _mm_or_si128(bg, r);
        // 写入12字节RGB数据
        _mm_storeu_si128(dstSIMD, rgb0);
        srcSIMD++;
        dstSIMD = reinterpret_cast<__m128i*>(reinterpret_cast<unsigned char*>(dstSIMD)+12);
    }
    // 处理剩余不足4个的像素
    for(; x < output.width; x++){
        dstPtr[0] = srcPtr[0];
        dstPtr[1] = srcPtr[1];
        dstPtr[2] = srcPtr[2];
        srcPtr +=4;
        dstPtr +=3;
    }
    srcPtr += skipBytes;
}

2. 使用D3D11 Compute Shader硬件加速

将转换操作放到GPU执行,自动处理对齐并利用并行计算能力:

  1. 编写Compute Shader代码:
Texture2D<float4> inputTex : register(t0);
RWTexture2D<float3> outputTex : register(u0);

[numthreads(16,16,1)]
void CS(uint3 dispatchThreadID : SV_DispatchThreadID)
{
    float4 bgra = inputTex[dispatchThreadID.xy];
    outputTex[dispatchThreadID.xy] = float3(bgra.b, bgra.g, bgra.r);
}
  1. CPU端调度执行:
// 设置输入输出资源
context->CSSetShaderResources(0, 1, &srv);
context->CSSetUnorderedAccessViews(0, 1, &uav, nullptr);
// 计算线程组数量
UINT groupX = (output.width + 15) / 16;
UINT groupY = (output.height + 15) / 16;
context->Dispatch(groupX, groupY, 1);
// 读取转换后的RGB数据
context->Map(outputTexture, 0, D3D11_MAP_READ, 0, &mappedOutput);

内容的提问来源于stack exchange,提问作者CommanderLake

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:43:18