Gestire gli scenari di rimozione dei dispositivi in Direct3D 11
Questo argomento illustra come ricreare la catena di interfaccia del dispositivo Direct3D e DXGI quando la scheda grafica viene rimossa o reinizializzata.
In DirectX 9, le applicazioni potrebbero riscontrare una condizione di "dispositivo perso" in cui il dispositivo D3D entra in uno stato non operativo. Ad esempio, quando un'applicazione Direct3D 9 a schermo intero perde lo stato attivo, il dispositivo Direct3D diventa "perso"; eventuali tentativi di disegno con un dispositivo perso avranno esito negativo in modo invisibile all'utente. Direct3D 11 usa interfacce di dispositivi grafici virtuali, consentendo a più programmi di condividere lo stesso dispositivo grafico fisico ed eliminando tutte quelle condizioni per le quali le app perderebbero il controllo del dispositivo Direct3D. Tuttavia, rimane la possibilità che la disponibilità della scheda grafica cambi. Ad esempio:
- Il driver di grafica viene aggiornato.
- Il sistema passa da una scheda grafica di risparmio energia a una scheda grafica con prestazioni.
- Il dispositivo grafico smette di rispondere e viene reimpostato.
- Una scheda grafica viene fisicamente collegata o rimossa.
Quando si verificano tali circostanze, DXGI restituisce un codice di errore che indica che il dispositivo Direct3D deve essere reinizializzato e le risorse del dispositivo devono essere ricreate. Questa procedura dettagliata illustra come app e giochi Direct3D 11 possono rilevare e rispondere a qualsiasi circostanza in cui la scheda grafica viene reimpostata, rimossa o modificata. Gli esempi di codice provengono dal modello App DirectX 11 (Windows universale) fornito con Microsoft Visual Studio 2015.
Istruzioni
Passaggio 1:
Includere un controllo della presenza dell'errore di rimozione del dispositivo nel ciclo di rendering. Presentare il frame chiamando IDXGISwapChain::Present (o Present1 e così via). Controllare quindi se ha restituito DXGI_ERROR_DEVICE_REMOVED o DXGI_ERROR_DEVICE_RESET.
In primo luogo, il modello archivia il valore HRESULT restituito dalla swapchain DXGI:
HRESULT hr = m_swapChain->Present(1, 0);
Dopo avere eseguito tutte le altre operazioni per la presentazione del frame, il modello verifica la presenza dell'errore di rimozione del dispositivo. Se necessario, chiama un metodo per gestire la condizione rimossa del dispositivo:
// If the device was removed either by a disconnection or a driver upgrade, we
// must recreate all device resources.
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
HandleDeviceLost();
}
else
{
DX::ThrowIfFailed(hr);
}
Passaggio 2:
Includere anche un controllo della presenza dell'errore di rimozione del dispositivo quando risponde alle modifiche di dimensioni della finestra. Questo è un buon momento per verificare DXGI_ERROR_DEVICE_REMOVED o DXGI_ERROR_DEVICE_RESET per diversi motivi:
- Il ridimensionamento della swapchain richiede una chiamata all'adattatore DXGI sottostante, che può restituire l'errore di rimozione del dispositivo.
- L'app potrebbe essere stata spostata in un monitor collegato a un dispositivo grafico diverso.
- Quando un dispositivo grafico viene rimosso o reimpostato, la risoluzione del desktop cambia spesso, determinando una modifica delle dimensioni della finestra.
Il modello controlla il valore HRESULT restituito da ResizeBuffers:
// If the swap chain already exists, resize it.
HRESULT hr = m_swapChain->ResizeBuffers(
2, // Double-buffered swap chain.
static_cast<UINT>(m_d3dRenderTargetSize.Width),
static_cast<UINT>(m_d3dRenderTargetSize.Height),
DXGI_FORMAT_B8G8R8A8_UNORM,
0
);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
// If the device was removed for any reason, a new device and swap chain will need to be created.
HandleDeviceLost();
// Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
// and correctly set up the new device.
return;
}
else
{
DX::ThrowIfFailed(hr);
}
Passaggio 3:
Ogni volta che l'app riceve l'errore DXGI_ERROR_DEVICE_REMOVED, deve reinizializzare il dispositivo Direct3D e ricreare tutte le risorse dipendenti dal dispositivo. Rilasciare tutti i riferimenti alle risorse del dispositivo grafico create con il dispositivo Direct3D precedente; tali risorse non sono più valide ora, e tutti i riferimenti alla swapchain devono essere rilasciati prima di poterne creare uno nuovo.
Il metodo HandleDeviceLost rilascia la swapchain e notifica ai componenti dell'app di rilasciare le risorse del dispositivo:
m_swapChain = nullptr;
if (m_deviceNotify != nullptr)
{
// Notify the renderers that device resources need to be released.
// This ensures all references to the existing swap chain are released so that a new one can be created.
m_deviceNotify->OnDeviceLost();
}
A questo punto crea una nuova swapchain e reinizializza le risorse dipendenti dal dispositivo controllate dalla classe di gestione dei dispositivi:
// Create the new device and swap chain.
CreateDeviceResources();
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();
Dopo avere ristabilito il dispositivo e la swapchain, notifica ai componenti dell'app di reinizializzare le risorse dipendenti dal dispositivo:
// Create the new device and swap chain.
CreateDeviceResources();
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();
if (m_deviceNotify != nullptr)
{
// Notify the renderers that resources can now be created again.
m_deviceNotify->OnDeviceRestored();
}
Quando il metodo HandleDeviceLost viene chiuso, il controllo torna al ciclo di rendering, che continua a disegnare il fotogramma successivo.
Osservazioni:
Analisi delle cause di errore di rimozione del dispositivo
Ripetuti problemi con gli errori di dispositivo DXGI rimosso possono indicare che il codice grafico sta creando condizioni non valide durante una routine di disegno. Può anche indicare un errore hardware o un bug nel driver di grafica. Per analizzare la causa degli errori di rimozione del dispositivo, chiamare ID3D11Device::GetDeviceRemovedReason prima di rilasciare il dispositivo Direct3D. Questo metodo restituisce uno dei sei possibili codici di errore DXGI che indicano il motivo dell'errore di rimozione del dispositivo:
- DXGI_ERROR_DEVICE_HUNG: il driver di grafica ha smesso di rispondere a causa di una combinazione non valida di comandi grafici inviati dall'app. Se questo errore si verifica ripetutamente, è probabile che l'app abbia causato il blocco del dispositivo e che sia necessario procedere con il debug.
- DXGI_ERROR_DEVICE_REMOVED: il dispositivo grafico è stato rimosso fisicamente, è stato disattivato o si è verificato un aggiornamento del driver. Questo può accadere occasionalmente ed è normale; l'app o il gioco devono ricreare le risorse del dispositivo come descritto in questo argomento.
- DXGI_ERROR_DEVICE_RE edizione Standard T: il dispositivo grafico non ha avuto esito positivo a causa di un comando non valido. Se questo errore viene visualizzato ripetutamente, il codice potrebbe inviare comandi di disegno non validi.
- DXGI_ERROR_DRIVER_INTERNAL_ERROR: il driver di grafica ha rilevato un errore e ha reimpostato il dispositivo.
- DXGI_ERROR_INVALID_CALL: l'applicazione ha fornito dati di parametro non validi. Se viene visualizzato questo errore anche una sola volta, significa che il codice ha causato la condizione di dispositivo rimosso ed è necessario ricorrere a debugging.
- S_OK: restituito quando un dispositivo grafico è stato attivato, disattivato o reimpostato senza invalidare il dispositivo grafico corrente. Ad esempio, questo codice di errore può essere restituito se un'app usa Windows Advanced Rasterization Platform (WARP) e una scheda hardware diventa disponibile.
Il codice seguente recupererà il codice di errore DXGI_ERROR_DEVICE_REMOVED e lo stamperà nella console di debug. Inserire questo codice all'inizio del metodo HandleDeviceLost:
HRESULT reason = m_d3dDevice->GetDeviceRemovedReason();
#if defined(_DEBUG)
wchar_t outString[100];
size_t size = 100;
swprintf_s(outString, size, L"Device removed! DXGI_ERROR code: 0x%X\n", reason);
OutputDebugStringW(outString);
#endif
Per altri dettagli, vedere GetDeviceRemovedReason e DXGI_ERROR.
Test della gestione di rimozione dispositivo
Il prompt dei comandi per gli sviluppatori di Visual Studio supporta uno strumento da riga di comando "dxcap" per l'acquisizione e la riproduzione di eventi Direct3D correlati alla diagnostica grafica di Visual Studio. È possibile usare l'opzione della riga di comando "-forcetdr" mentre l'app è in esecuzione; questo forzerà un evento di rilevamento e ripristino del timeout GPU e attiverà DXGI_ERROR_DEVICE_REMOVED, consentendo di testare il codice di gestione degli errori.
Nota DXCap e le DLL di supporto sono installate in system32/syswow64 come parte degli strumenti di grafica per Windows 10, i quali non sono più distribuiti tramite Windows SDK. Sono invece forniti tramite la funzionalità Graphics Tools Feature on Demand, un componente del sistema operativo facoltativo che deve essere installato se si vogliono attivare e usare gli strumenti di grafica in Windows 10. Altre informazioni su come installare gli strumenti di grafica per Windows 10 sono disponibili qui: https://msdn.microsoft.com/library/mt125501.aspx#InstallGraphicsTools