AcquireNextFrame无法工作(桌面复制API和D3D11)

3
我已经编写了一段代码,可以对桌面进行截图并映射为原始像素数据,但输出全为零。我不知道哪里出错了。在查看了许多在线的桌面复制 API 示例后,我没有发现它们与我的任何区别。
这是我的方法来初始化所有内容,没有引发任何错误。
BOOL init()
{
    CHECKHR(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &lDevice, &FeatureLevel, &lImmediateContext))
    IDXGIDevice* lDxgiDevice;
    CHECKHR(lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice)))
    IDXGIAdapter* lDxgiAdapter;
    CHECKHR(lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&lDxgiAdapter))
    lDxgiDevice->Release();
    IDXGIOutput* lDxgiOutput;
    CHECKHR(lDxgiAdapter->EnumOutputs(0, &lDxgiOutput))
    lDxgiAdapter->Release();
    CHECKHR(lDxgiOutput->GetDesc(&OutputDesc))
    IDXGIOutput1* lDxgiOutput1;
    CHECKHR(lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1)))
    lDxgiOutput->Release();
    CHECKHR(lDxgiOutput1->DuplicateOutput(lDevice, &lDeskDupl))
    lDxgiOutput1->Release();
    lDeskDupl->GetDesc(&OutputDuplDesc);
    D3D11_TEXTURE2D_DESC desc;
    desc.Width = OutputDuplDesc.ModeDesc.Width;
    desc.Height = OutputDuplDesc.ModeDesc.Height;
    desc.Format = OutputDuplDesc.ModeDesc.Format;
    desc.ArraySize = 1;
    desc.BindFlags = 0;
    desc.MiscFlags = 0;
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    desc.MipLevels = 1;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
    desc.Usage = D3D11_USAGE_STAGING;
    CHECKHR(lDevice->CreateTexture2D(&desc, NULL, &lDestImage))
    return TRUE;
}

我正在使用的CHECKHR宏已经由我测试过并且有效,只是为了澄清它不是问题所在。

这是我用来实际获取帧的代码:

int main()
{
    init();

    HRESULT hr;
    IDXGIResource* lDesktopResource;
    ID3D11Texture2D* lAcquiredDesktopImage;
    DXGI_OUTDUPL_FRAME_INFO FrameInfo;
    while (true)
    {
        if (SUCCEEDED(lDeskDupl->AcquireNextFrame(INFINITE, &FrameInfo, &lDesktopResource)))
        {
            break;
        }
    }
    hr = lDesktopResource->QueryInterface(IID_PPV_ARGS(&lAcquiredDesktopImage));
    if (FAILED(hr))
    {
        cout << "QueryInterface failed!" << endl;
        system("pause");
    }
    lDesktopResource->Release();
    lImmediateContext->CopyResource(lDestImage, lAcquiredDesktopImage);
    lAcquiredDesktopImage->Release();
    lDeskDupl->ReleaseFrame();
    D3D11_MAPPED_SUBRESOURCE resource;
    UINT subresource = D3D11CalcSubresource(0, 0, 0);
    hr = lImmediateContext->Map(lDestImage, subresource, D3D11_MAP_READ_WRITE, 0, &resource);
    if (FAILED(hr))
    {
        cout << "Map failed!" << endl;
        system("pause");
    }
    BYTE* pb = (BYTE*)(resource.pData);
    for (int i = 0; i < 2000000; i++)
    {
        cout << (int)pb[i] << endl;
    }
    system("pause");
    return 0;
}

所有发生的事情就是向控制台打印2000000个零。我是否遗漏了什么或者看不到什么?


我认为 D3D11CreateDevice 调用是错误的。你应该先使用 DXGI 获取适配器指针,然后将显式适配器作为第一个参数调用 D3D11CreateDevice。而不是反过来。 - Roman R.
根据Roman R的建议,我创建了另一个init方法,使用IDXGIFactory获取适配器,然后用它创建设备。但现在调用D3D11CreateDevice返回E_INVALIDARG。在IDXGIFactory :: EnumAdapters中使用0作为索引参数是否正确? - M. Alvarez
这是我的新init方法链接:https://pastebin.com/fAPyqWMa - M. Alvarez
新错误可能是由 D3D_DRIVER_TYPE_HARDWARE 引起的,您需要将其更改为 D3D_DRIVER_TYPE_UNKNOWN启用 D3D 调试层 来在调试输出窗口中启用提示。 - Roman R.
1个回答

3
首先,针对你要复制的特定适配器调用D3D11CreateDevice
BOOL init()
{
    IDXGIFactory* lDxgiFactory;
    CHECKHR(CreateDXGIFactory1(__uuidof(IDXGIFactory), (void**)&lDxgiFactory))
    IDXGIAdapter* lDxgiAdapter;
    CHECKHR(lDxgiFactory->EnumAdapters(0, &lDxgiAdapter))
    lDxgiFactory->Release();
    CHECKHR(D3D11CreateDevice(lDxgiAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &lDevice, &FeatureLevel, &lImmediateContext))
    IDXGIDevice* lDxgiDevice;
    CHECKHR(lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice)))
    //IDXGIAdapter* lDxgiAdapter;
    //CHECKHR(lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&lDxgiAdapter))
    lDxgiDevice->Release();
    IDXGIOutput* lDxgiOutput;
    CHECKHR(lDxgiAdapter->EnumOutputs(0, &lDxgiOutput))
    lDxgiAdapter->Release();
    CHECKHR(lDxgiOutput->GetDesc(&OutputDesc))
    IDXGIOutput1* lDxgiOutput1;
    CHECKHR(lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1)))
    lDxgiOutput->Release();
    CHECKHR(lDxgiOutput1->DuplicateOutput(lDevice, &lDeskDupl))
    // ...

那么,在AcquireNextFrame周围,您的代码并不十分准确。您需要等待实际帧并在跳过时在循环中执行ReleaseFrame

// ...
while (true)
{
    if (SUCCEEDED(lDeskDupl->AcquireNextFrame(INFINITE, &FrameInfo, &lDesktopResource)) && FrameInfo.LastPresentTime.QuadPart)
    {
        break;
    }
    lDeskDupl->ReleaseFrame();
}
// ...

1
非常感谢!这修复了init函数,但直到我在if语句中添加了“FrameInfo.LastPresentTime.QuadPart”才真正起作用。我真的不知道它是什么,但现在它可以工作了! - M. Alvarez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接