首先,您需要明确地检查所有返回 HRESULTs 的函数的返回代码。
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
返回 void,您需要在使用它之前检查格式是否支持 D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE
。下面是 ScreenGrab 模块中 DirectX Tool Kit 的处理代码:
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 )
{
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) )
{
pStaging = pTexture;
}
else
{
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格式,因此它不支持写出BGRA格式资源,例如
DXGI_FORMAT_B8G8R8A8_UNORM
或
DXGI_FORMAT_B8G8R8A8_UNORM
,即使底层WIC容器文件格式支持它们。如果你在上面的代码中的渲染目标是
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) )
{
hr = SaveWICTextureToFile( context.Get(), backBufferTex.Get(), GUID_ContainerFormatPng, L"SCREENSHOT.PNG");
hr = SaveWICTextureToFile( context.Get(), backBufferTex.Get(), GUID_ContainerFormatJpeg, L"SCREENSHOT.JPG" );
hr = SaveWICTextureToFile( context.Get(), backBufferTex.Get(), GUID_ContainerFormatBmp, L"SCREENSHOT.BMP" );
hr = SaveWICTextureToFile( context.Get(), backBufferTex.Get(), GUID_ContainerFormatBmp, L"SCREENSHOT.BMP", &GUID_WICPixelFormat16bppBGR565 );
hr = SaveWICTextureToFile( context.Get(), backBufferTex.Get(), GUID_ContainerFormatTiff, L"SCREENSHOT.TIF" );
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 );
});
hr = SaveDDSTextureToFile( context.Get(), backBufferTex.Get(), L"SCREENSHOT.DDS" );
}