Detect and surface host device loss instead of spinning silently
D3D11Window ignored the HRESULTs from Present, ResizeBuffers, and CreateRenderTargetView, so a host-side TDR / driver reset / GPU hang left the render loop presenting to a dead device forever with no error. Now note_device_loss() inspects those HRESULTs; on DXGI_ERROR_DEVICE_REMOVED/RESET it captures GetDeviceRemovedReason() and sets device_lost(). The main loop checks it after render_frame, shows a MessageBox with the reason, and stops cleanly. Per the agreed scope this is detect-surface-halt, not full device re-creation (which would have to re-init ImGui + the capture pipeline) -- that's future work. Not unit-testable (TDR isn't deterministically reproducible); fixed by inspection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -169,12 +169,28 @@ bool D3D11Window::create_device()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11Window::note_device_loss(HRESULT hr)
|
||||
{
|
||||
if (hr != DXGI_ERROR_DEVICE_REMOVED && hr != DXGI_ERROR_DEVICE_RESET)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// GetDeviceRemovedReason gives the specific cause (HUNG / driver internal / removed); a plain
|
||||
// RESET may report S_OK there, so fall back to the originating error in that case.
|
||||
const HRESULT reason = device_ ? device_->GetDeviceRemovedReason() : hr;
|
||||
device_lost_reason_ = (reason != S_OK) ? reason : hr;
|
||||
device_lost_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D11Window::create_render_target()
|
||||
{
|
||||
ComPtr<ID3D11Texture2D> back_buffer;
|
||||
if (SUCCEEDED(swap_chain_->GetBuffer(0, IID_PPV_ARGS(back_buffer.GetAddressOf()))))
|
||||
{
|
||||
device_->CreateRenderTargetView(back_buffer.Get(), nullptr, rtv_.ReleaseAndGetAddressOf());
|
||||
const HRESULT hr =
|
||||
device_->CreateRenderTargetView(back_buffer.Get(), nullptr, rtv_.ReleaseAndGetAddressOf());
|
||||
note_device_loss(hr); // a removed device surfaces here too; the render loop checks device_lost()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +206,11 @@ void D3D11Window::handle_resize(UINT width, UINT height)
|
||||
return;
|
||||
}
|
||||
release_render_target();
|
||||
swap_chain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0);
|
||||
const HRESULT hr = swap_chain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0);
|
||||
if (note_device_loss(hr))
|
||||
{
|
||||
return; // device gone; the render loop will see device_lost() and stop
|
||||
}
|
||||
create_render_target();
|
||||
}
|
||||
|
||||
@@ -238,7 +258,8 @@ void D3D11Window::render_frame(const RenderCallback& render, UINT sync_interval)
|
||||
|
||||
// sync_interval 1 (default) vsyncs to the monitor; 0 presents immediately so the
|
||||
// caller can pace the flip itself (frame-sync to the game's published frames).
|
||||
swap_chain_->Present(sync_interval, 0);
|
||||
const HRESULT hr = swap_chain_->Present(sync_interval, 0);
|
||||
note_device_loss(hr); // a TDR/driver reset on the host surfaces here; the render loop halts on it
|
||||
}
|
||||
|
||||
void D3D11Window::request_screenshot(std::wstring path)
|
||||
|
||||
Reference in New Issue
Block a user