c++ 将来自网络摄像头的帧渲染为DirectX 11纹理

toe95027  于 2022-12-05  发布在  其他
关注(0)|答案(1)|浏览(170)

我在使用内存中的网络摄像头帧缓冲区中的图像数据更新DirectX 11纹理时遇到了困难。我设法从缓冲区中的单个帧创建了一个纹理,但由于缓冲区被下一帧覆盖,纹理没有更新。因此,我只剩下一张快照图像,而不是我所追求的实时流。
我正在尝试使用Map/Unmap方法来更新ID 3D 11 Texture 2D资源,因为这应该比使用UpdateSubresource方法更有效。我还没有设法让这两种方法都起作用。我是DirectX的新手,我只是找不到关于如何完成此操作的好解释。
在此处创建纹理:

bool CreateCamTexture(ID3D11ShaderResourceView** out_srv, RGBQUAD* ptrimg, int* image_width, int* image_height)
{

    ZeroMemory(&desc, sizeof(desc));
    desc.Width = *image_width;
    desc.Height = *image_height;
    desc.MipLevels = 1;
    desc.ArraySize = 1;
    desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc.SampleDesc.Count = 1;
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

    std::cout << ptrimg << std::endl;
    subResource.pSysMem = ptrimg;
    subResource.SysMemPitch = desc.Width * 4;
    subResource.SysMemSlicePitch = 0;
    g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);

    ZeroMemory(&srvDesc, sizeof(srvDesc));
    srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    srvDesc.Texture2D.MipLevels = desc.MipLevels;
    srvDesc.Texture2D.MostDetailedMip = 0;
    g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, out_srv);
    if (pTexture != NULL) {
        pTexture->Release();
    }
    else
    {
        std::cout << "pTexture is NULL ShaderResourceView not created" << std::endl;
    }

    return true;
}
bool CreateDeviceD3D(HWND hWnd)
{
    // Setup swap chain
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 2;
    sd.BufferDesc.Width = 0;
    sd.BufferDesc.Height = 0;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = hWnd;
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    UINT createDeviceFlags = 0;
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
    D3D_FEATURE_LEVEL featureLevel;
    const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };

尝试Map/取消Map纹理:

void UpdateCamTexture() {
    D3D11_MAPPED_SUBRESOURCE mappedResource;

    ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));
    
    g_pd3dDeviceContext->Map(
        pTexture,
        0, //0,
        D3D11_MAP_WRITE_DISCARD,
        0,
        &mappedResource);
    memcpy(mappedResource.pData, listener_instance.pImgData, sizeof(listener_instance.pImgData));
    //  Reenable GPU access to the vertex buffer data.
    g_pd3dDeviceContext->Unmap(pTexture, 0);
    std::cout << "texture updated" << std::endl;
}

我没有得到一个错误,图像只是黑色。我没有调试层启用虽然。

v09wglhw

v09wglhw1#

在指针listener_instance.pImgData上调用sizeof并不是你想要的,因为它返回的是指针类型的大小(在x64架构上是8),而不是指针所指向的数组的大小。用图像数据的大小(以字节为单位)调用memcpy也不是完全正确的解决方案。更多细节请参见here。我将从那里复制答案,以防它被删除。
马克西姆斯·米尼穆斯的回答是:
检查从贴图调用返回的间距-您假设它是width * 4(对于32位RGBA),但它可能不是(特别是如果您的纹理不是2的幂或它的宽度不是4的倍数)。
如果间距等于宽度 * 格式中的字节数,则一次只能对整个块执行memcpy操作。否则,必须一次对一行执行memcpy操作。
示例代码,请原谅C-isms:
假定src和dst是32位RGBA数据

unsigned *src; // this comes from whatever your input is
unsigned *dst = (unsigned *) msr.pData; // msr is a D3D11_MAPPED_SUBRESOURCE derived from ID3D11DeviceContext::Map

宽度和高度来自ID3D11Texture2D::GetDesc

for (int i = 0; i < height; i++)
{
    memcpy (dst, src, width * 4); // copy one row at a time because msr.RowPitch may be != (width * 4)
    dst += msr.RowPitch >> 2; // msr.RowPitch is in bytes so for 32-bit data we divide by 4 (or downshift by 2, same thing)
    src += width; // assumes pitch of source data is equal to width * 4
}

当然,你也可以包括一个测试,如果(msr.RowPitch == width * 4)为真,那么就对整个过程做一个memcpy测试。

相关问题