提取脏的RGB像素缓冲区数据DirectX

时间:2017-05-07 22:57:09

标签: c++ windows directx-11 dxgi

我正在使用Windows API中的桌面复制功能。 下面是访问下一帧的代码,并获取与前一帧相比有所变化的像素矩形。

//
// Get next frame and write it into Data
//
_Success_(*Timeout == false && return == DUPL_RETURN_SUCCESS)
DUPL_RETURN DUPLICATIONMANAGER::GetFrame(_Out_ FRAME_DATA* Data, _Out_ bool* Timeout)
{
IDXGIResource* DesktopResource = nullptr;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;

// Get new frame
HRESULT hr = m_DeskDupl->AcquireNextFrame(10000, &FrameInfo, &DesktopResource);
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
    *Timeout = true;
    return DUPL_RETURN_SUCCESS;
}
*Timeout = false;

if (FAILED(hr))
{
    return ProcessFailure(m_Device, L"Failed to acquire next frame in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
}

// If still holding old frame, destroy it
if (m_AcquiredDesktopImage)
{
    m_AcquiredDesktopImage->Release();
    m_AcquiredDesktopImage = nullptr;
}

// QI for IDXGIResource
hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&m_AcquiredDesktopImage));
DesktopResource->Release();
DesktopResource = nullptr;
if (FAILED(hr))
{
    return ProcessFailure(nullptr, L"Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER", L"Error", hr);
}

// Get metadata
if (FrameInfo.TotalMetadataBufferSize)
{
    // Old buffer too small
    if (FrameInfo.TotalMetadataBufferSize > m_MetaDataSize)
    {
        if (m_MetaDataBuffer)
        {
            delete [] m_MetaDataBuffer;
            m_MetaDataBuffer = nullptr;
        }
        m_MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize];
        if (!m_MetaDataBuffer)
        {
            m_MetaDataSize = 0;
            Data->MoveCount = 0;
            Data->DirtyCount = 0;
            return ProcessFailure(nullptr, L"Failed to allocate memory for metadata in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
        }
        m_MetaDataSize = FrameInfo.TotalMetadataBufferSize;
    }

    UINT BufSize = FrameInfo.TotalMetadataBufferSize;

    // Get move rectangles
    hr = m_DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(m_MetaDataBuffer), &BufSize);
    if (FAILED(hr))
    {
        Data->MoveCount = 0;
        Data->DirtyCount = 0;
        return ProcessFailure(nullptr, L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
    }
    Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);

    BYTE* DirtyRects = m_MetaDataBuffer + BufSize;
    BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;

    // Get dirty rectangles
    hr = m_DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize);
    if (FAILED(hr))
    {
        Data->MoveCount = 0;
        Data->DirtyCount = 0;
        return ProcessFailure(nullptr, L"Failed to get frame dirty rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
    }
    Data->DirtyCount = BufSize / sizeof(RECT);

    Data->MetaData = m_MetaDataBuffer;
}

Data->Frame = m_AcquiredDesktopImage;
Data->FrameInfo = FrameInfo;

//Here I would like to access pixel data from Data->Frame. A buffer of RGBA pixel

return DUPL_RETURN_SUCCESS;
}

这是Frame_Data结构

typedef struct _FRAME_DATA
{
ID3D11Texture2D* Frame;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
_Field_size_bytes_((MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT)) + (DirtyCount * sizeof(RECT))) BYTE* MetaData;
UINT DirtyCount;
UINT MoveCount;
} FRAME_DATA;

是否可以访问已从Data->Frame

修改的像素缓冲区数据

以下是我访问数据的代码:

BYTE* DISPLAYMANAGER::GetImageData(ID3D11Texture2D* texture2D, D3D11_TEXTURE2D_DESC Desc)
{
if (texture2D != NULL)
{
    D3D11_TEXTURE2D_DESC description;
    texture2D->GetDesc(&description);
    description.BindFlags = 0;
    description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
    description.Usage = D3D11_USAGE_STAGING;
    description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;

    ID3D11Texture2D* texTemp = NULL;
    HRESULT hr = m_Device->CreateTexture2D(&description, NULL, &texTemp);
    if (FAILED(hr))
    {
        if (texTemp)
        {
            texTemp->Release();
            texTemp = NULL;
        }
        return NULL;
    }
    m_DeviceContext->CopyResource(texTemp, texture2D);

    D3D11_MAPPED_SUBRESOURCE mapped;
    unsigned int subresource = D3D11CalcSubresource(0, 0, 0);
    hr = m_DeviceContext->Map(texTemp, subresource, D3D11_MAP_READ_WRITE, 0, &mapped);
    if (FAILED(hr))
    {
        texTemp->Release();
        texTemp = NULL;
        return NULL;
    }

    unsigned char *captureData = new unsigned char[Desc.Width * Desc.Height * 4];
    RtlZeroMemory(captureData, Desc.Width * Desc.Height * 4);
    const int pitch = mapped.RowPitch;
    unsigned char *source = static_cast<unsigned char*>(mapped.pData);
    unsigned char *dest = captureData;
    for (int i = 0; i < Desc.Height; i++) {
        memcpy(captureData, source, Desc.Width * 4);
        source += pitch;
        captureData += Desc.Width * 4;
    }
    for (int i = 0; i < Desc.Width * Desc.Height * 4; i++) {
        //trace(L"Pixel[%d] = %x\n", i, dest[i]);
    }

    m_DeviceContext->Unmap(texTemp, 0);
    return dest;
}
else
    return NULL;
}

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

对于单独的像素访问,您通过复制API获取的纹理不一定是CPU可访问的。要读取纹理数据,您可能需要创建可映射的分段纹理并在那里复制获得的纹理。然后进行映射,您将获得指向实际数据的指针。请注意,这通常不是一个对性能友好的操作。

您还可以在其他答案中找到相关信息:

How to work with pixels using Direct2D

  

对于那些绝对需要进行CPU像素操作但仍需要大幅度加速的时候,您可以管理自己的可映射D3D11纹理。例如,如果要从CPU异步操作纹理资源,则可以使用分段纹理。

Transferring textures across adapters in DirectX 11

  

...使用ID3D11DeviceContext :: CopyResource将其复制到临时资源(在同一设备上创建)。然后,我使用Read ...

映射该暂存资源