[英]c++ Directx11 capture screen and save to file
我在將 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;
}
}
我究竟做錯了什么?
首先,您需要明確檢查所有返回 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 Kit中ScreenGrab模塊中執行此處理的代碼:
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 Tool Kit而不是舊的
D3DX11
庫。 所有版本的 D3DX 都已棄用,舊版 DirectX SDK 本身也是如此(請參閱MSDN )。 有許多容易獲得的替代品。
除了 MSAA 問題之外,您可能還會遇到D3DX11
選擇 WIC 格式的問題。 根據您的渲染目標格式和渲染,它可能會用全 0 alpha 通道寫出圖像,這可能會導致“空白”輸出圖像。 DirectX Tool Kit ScreenGrab 模塊使您能夠明確指定輸出格式,並因此默認嘗試使用非 alpha 輸出文件格式。
不使用舊版D3DX11
另一個原因:它從未針對 DXGI 1.1 格式更新,因此即使底層 WIC 容器文件格式支持,它也不支持寫出 BGRA 格式資源,如DXGI_FORMAT_B8G8R8A8_UNORM
或DXGI_FORMAT_B8G8R8A8_UNORM
。 如果上面代碼中的渲染目標是DXGI_FORMAT_B8G8R8A8_UNORM
而不是DXGI_FORMAT_R8G8B8A8_UNORM
那么D3DX11
將失敗,而 ScreenGrab 將正常工作。
我有沒有提到D3DX11
舊了,並且自 2009 年以來沒有對其進行任何修復?
以下是ScreenGrab的一些示例用法:
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" );
}
在設置所有值之前,您的描述已聲明但未初始化為任何內容。 它的MiscFlags
成員中可能有垃圾,會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();
}
要寫入 MSAA 緩沖區(交換鏈中的后台緩沖區),需要使用“ResolveSubresource”而不是“CopyResource”。
這是一個簡單的代碼段:
D3D11_TEXTURE2D_DESC desc;
HRESULT hr = S_OK;
ID3D11Resource* pSurface = nullptr;
// get pointer to back-texture from the swap chain referred from the pointer IDXGISwapChain* g_pswapChain
hr=g_pswapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pSurface));
if (FAILED(hr) || pSurface==nullptr)
{
// Throw Error
}
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.Width = 640; // i specified hard-coded the window size 640x480 in CreateWindowA
desc.Height = 480;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BindFlags = 0;
desc.CPUAccessFlags = 0; // data directly will be copied from GPU to a file
desc.Usage = D3D11_USAGE_DEFAULT;
ID3D11Texture2D* pTexture = nullptr;
// g_pdevice is pointer to device ID3D11Device
hr = g_pdevice->CreateTexture2D(&desc, nullptr, &pTexture);
if (pTexture)
{
// instead of CopyResource(pTexture, pSurface), g_pd3dContext pointer to context ID3D11DeviceContext
g_pd3dContext->ResolveSubresource(pTexture, 0, pSurface, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
// save rendered image as bmp-file
hr = D3DX11SaveTextureToFileA(g_pd3dContext, pTexture, D3DX11_IFF_BMP, "test.bmp");
pTexture->Release();
}
pSurface->Release();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.