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 устройства и найдите сообщения об ошибках или предупреждениях.

В частности, если ваш буфер цепочки подкачки является ресурсом MSAA, он не сможет получить какие-либо данные. Вы должны явно использовать ResolveSubresource прежде чем делать копию. В свою очередь с ResolveSubresource возвращает пустоту, вам нужно проверить формат поддерживает D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE перед его использованием. Вот код в Screengrab модуль в Набор инструментов DirectX что делает эту обработку:

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). Есть ряд легко доступных замены.

В дополнение к проблеме MSAA у вас могут возникнуть проблемы с D3DX11Выбор форматов WIC. В зависимости от целевого формата рендеринга и рендеринга, это может быть запись изображения с альфа-каналом со всеми 0, что может привести к «пустому» выходному изображению. Набор инструментов DirectX Модуль ScreenGrab дает вам возможность явно указать выходной формат и по умолчанию пытается использовать не-альфа формат выходного файла по этой причине.

Еще одна причина не использовать наследство 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) )
{
// 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" );
}
7

Другие решения

Ваше описание объявляется, но ничего не инициализируется перед установкой всех ваших значений. это 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();
}
2

По вопросам рекламы [email protected]