2014-01-18 160 views
3

我有問題,它總是給了我黑色的圖像。 這是代碼:C++的DirectX11捕捉畫面並保存保存的Texture2D到文件到文件

HRESULT hr = SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast< void** >(&g_pSurface)); 
if(g_pSurface) 
{ 
    ID3D11Texture2D* pNewTexture = NULL; 

    D3D11_TEXTURE2D_DESC description; 
    g_pSurface->GetDesc(&description); 
    description.BindFlags = 0; 
    description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; 
    description.Usage = D3D11_USAGE_STAGING; 

    HRESULT hr = d3d11Device->CreateTexture2D(&description, NULL, &pNewTexture); 
    if(pNewTexture) 
    { 
     d3d11DevCon->CopyResource(pNewTexture, g_pSurface); 

     hr=D3DX11SaveTextureToFileA(d3d11DevCon, pNewTexture, D3DX11_IFF_BMP, "screen.bmp"); 
     return; 
    } 
} 

我在做什麼錯?

回答

4

首先,你必須明確地檢查所有的返回碼即返回的HRESULT

失敗的
HRESULT hr = SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), 
    reinterpret_cast< void** >(&g_pSurface)); 
if(SUCCEEDED(hr)) 
{ 
... 
    HRESULT hr = d3d11Device->CreateTexture2D(&description, NULL, &pNewTexture); 
    if(SUCCEEDED(hr)) 

另一種觀點是CopyResource它返回void,所以你不能探測出你的代碼問題的功能。相反,您需要啓用Direct3D DEBUG設備並查找任何ERROR或WARNING消息。

特別是,如果您的交換鏈緩衝器是MSAA的資源,這將無法獲得任何數據。在複製之前,您必須明確使用ResolveSubresource。反過來,由於ResolveSubresource返回一個空格,因此在使用它之前,您需要檢查支持D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE的格式。下面是執行此操作在DirectX Tool KitScreenGrab模塊中的代碼:

static HRESULT CaptureTexture(_In_ ID3D11DeviceContext* pContext, 
           _In_ ID3D11Resource* pSource, 
           _Inout_ D3D11_TEXTURE2D_DESC& desc, 
           _Inout_ ComPtr<ID3D11Texture2D>& pStaging) 
{ 
    if (!pContext || !pSource) 
     return E_INVALIDARG; 

    D3D11_RESOURCE_DIMENSION resType = D3D11_RESOURCE_DIMENSION_UNKNOWN; 
    pSource->GetType(&resType); 

    if (resType != D3D11_RESOURCE_DIMENSION_TEXTURE2D) 
     return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 

    ComPtr<ID3D11Texture2D> pTexture; 
    HRESULT hr = pSource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(pTexture.GetAddressOf())); 
    if (FAILED(hr)) 
     return hr; 

    assert(pTexture); 

    pTexture->GetDesc(&desc); 

    ComPtr<ID3D11Device> d3dDevice; 
    pContext->GetDevice(d3dDevice.GetAddressOf()); 

    if (desc.SampleDesc.Count > 1) 
    { 
     // MSAA content must be resolved before being copied to a staging texture 
     desc.SampleDesc.Count = 1; 
     desc.SampleDesc.Quality = 0; 

     ComPtr<ID3D11Texture2D> pTemp; 
     hr = d3dDevice->CreateTexture2D(&desc, 0, pTemp.GetAddressOf()); 
     if (FAILED(hr)) 
      return hr; 

     assert(pTemp); 

     DXGI_FORMAT fmt = EnsureNotTypeless(desc.Format); 

     UINT support = 0; 
     hr = d3dDevice->CheckFormatSupport(fmt, &support); 
     if (FAILED(hr)) 
      return hr; 

     if (!(support & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE)) 
      return E_FAIL; 

     for(UINT item = 0; item < desc.ArraySize; ++item) 
     { 
      for(UINT level = 0; level < desc.MipLevels; ++level) 
      { 
       UINT index = D3D11CalcSubresource(level, item, desc.MipLevels); 
       pContext->ResolveSubresource(pTemp.Get(), index, pSource, index, fmt); 
      } 
     } 

     desc.BindFlags = 0; 
     desc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE; 
     desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; 
     desc.Usage = D3D11_USAGE_STAGING; 

     hr = d3dDevice->CreateTexture2D(&desc, 0, pStaging.GetAddressOf()); 
     if (FAILED(hr)) 
      return hr; 

     assert(pStaging); 

     pContext->CopyResource(pStaging.Get(), pTemp.Get()); 
    } 
    else if ((desc.Usage == D3D11_USAGE_STAGING) && (desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) 
    { 
     // Handle case where the source is already a staging texture we can use directly 
     pStaging = pTexture; 
    } 
    else 
    { 
     // Otherwise, create a staging texture from the non-MSAA source 
     desc.BindFlags = 0; 
     desc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE; 
     desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; 
     desc.Usage = D3D11_USAGE_STAGING; 

     hr = d3dDevice->CreateTexture2D(&desc, 0, pStaging.GetAddressOf()); 
     if (FAILED(hr)) 
      return hr; 

     assert(pStaging); 

     pContext->CopyResource(pStaging.Get(), pSource); 
    } 

事實上,你應該使用的DirectX工具包,而不是傳統的D3DX11庫。 D3DX的所有版本都與傳統的DirectX SDK本身一樣被棄用(請參閱MSDN)。有一些容易獲得的replacements

除了MSAA問題,您可能會遇到D3DX11選擇WIC格式的問題。根據您的渲染目標格式和渲染,它可能會寫出一個全0 alpha通道的圖像,這可能會導致「空白」輸出圖像。該的DirectX工具包 ScreenGrab模塊,使您能夠明確地指定輸出格式,默認爲嘗試使用非字母輸出文件格式因爲這個原因的能力。

還有一個理由不使用傳統的D3DX11:這是從來沒有更新的DXGI 1.1格式,因此它不支持寫出BGRA格式的資源,如DXGI_FORMAT_B8G8R8A8_UNORMDXGI_FORMAT_B8G8R8A8_UNORM即使底層WIC容器文件格式支持他們。如果上面的代碼中的渲染目標DXGI_FORMAT_B8G8R8A8_UNORM而非DXGI_FORMAT_R8G8B8A8_UNORM然後D3DX11會失敗,而ScreenGrab將正常工作。

我提到D3DX11是瘋狂的老並沒有因爲年〜2009年向它提出的任何修正?

下面是ScreenGrab一些使用例子:如果表面是MSAA,或者如果它使用的是BGRA格式

ComPtr<ID3D11Texture2D> backBufferTex; 
hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferTex); 
if (SUCCEEDED(hr)) 
{ 
    // Write out the render target as a PNG 
    hr = SaveWICTextureToFile(context.Get(), backBufferTex.Get(), GUID_ContainerFormatPng, L"SCREENSHOT.PNG"); 

    // Write out the render target as JPG 
    hr = SaveWICTextureToFile(context.Get(), backBufferTex.Get(), GUID_ContainerFormatJpeg, L"SCREENSHOT.JPG"); 

    // Write out the render target as BMP 
    hr = SaveWICTextureToFile(context.Get(), backBufferTex.Get(), GUID_ContainerFormatBmp, L"SCREENSHOT.BMP"); 

    // Write out the render target as BMP and explicitly use a 16-bit format 
    hr = SaveWICTextureToFile(context.Get(), backBufferTex.Get(), GUID_ContainerFormatBmp, L"SCREENSHOT.BMP", &GUID_WICPixelFormat16bppBGR565); 

    // Write out the render target as a TIF 
    hr = SaveWICTextureToFile(context.Get(), backBufferTex.Get(), GUID_ContainerFormatTiff, L"SCREENSHOT.TIF"); 

    // Write out the render target as a TIF with explicit WIC codec properties 
    hr = SaveWICTextureToFile(context.Get(), backBufferTex.Get(), GUID_ContainerFormatTiff, L"SCREENSHOT.TIF", nullptr, 
           [&](IPropertyBag2* props) 
           { 
            PROPBAG2 options[2] = { 0, 0 }; 
            options[0].pstrName = L"CompressionQuality"; 
            options[1].pstrName = L"TiffCompressionMethod"; 

            VARIANT varValues[2]; 
            varValues[0].vt = VT_R4; 
            varValues[0].fltVal = 0.75f; 

            varValues[1].vt = VT_UI1; 
            varValues[1].bVal = WICTiffCompressionNone; 

            (void)props->Write(2, options, varValues); 
           }); 

    // Write out the render target as a DDS 
    hr = SaveDDSTextureToFile(context.Get(), backBufferTex.Get(), L"SCREENSHOT.DDS"); 
} 
1

你的描述聲明,但設置所有的值之前未初始化任何東西。它的MiscFlags成員可能會有垃圾,它會破壞你的創作。嘗試設置description.MiscFlags = 0;或首先清零整個描述。如果不這樣做,這裏是從我目前的圖書館之一,工程一個精簡的代碼示例:

試試這個:

HRESULT hr; 

ID3D11Resource* pSurface = nullptr; 
m_pRenderTargetView->GetResource(&pSurface); 

if (pSurface) 
{ 
    D3D11_TEXTURE2D_DESC desc; 
    ZeroMemory(&desc, sizeof(desc)); 
    desc.ArraySize = 1; 
    desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 
    desc.Width = clientWidth; 
    desc.Height = clientHeight; 
    desc.MipLevels = 1; 
    desc.SampleDesc.Count = 1; 
    desc.SampleDesc.Quality = 0; 
    desc.BindFlags = 0; 
    desc.CPUAccessFlags = 0; 
    desc.Usage = D3D11_USAGE_DEFAULT; 

    ID3D11Texture2D* pTexture = nullptr; 
    hr = m_pDevice->CreateTexture2D(&desc, nullptr, &pTexture); 
    if (pTexture) 
    { 
     m_pContext->CopyResource(pTexture, pSurface); 
     hr = D3DX11SaveTextureToFileA(m_pContext, pTexture, D3DX11_IFF_PNG, "ss.png"); 
     pTexture->Release(); 
    } 
    pSurface->Release(); 
} 
+0

您的代碼也將失敗。它使用了傳統的D3DX11庫。 –

+0

是的,我的回答與OP實際上試圖做/使用的內容非常接近,但沒有在代碼中引入太多變化,但我同意你的觀點並贊成你的回答。 – orfdorf