libobs-d3d11: Rebuild device and assets if device removed/reset

Due to an NVIDIA driver bug with the Windows 10 Anniversary Update,
there are an increasingly large number of reports of "Device Removed"
errors and TDRs.  When this happens, OBS stops outputting all data
because all graphics functions are failing, and it appears to just
"freeze up" for users.

To temporarily alleviate this issue while waiting for it to be fixed,
the D3D subsystem can be rebuilt when that happens, all assets can be
reloaded to ensure that it can continue functioning (with a minor hiccup
in playback).

To allow rebuilding the entire D3D subsystem, all objects that contain
D3D references must be part of a linked list (with a few exceptions) so
we can quickly traverse them all whenever needed, and all data for those
resources (static resources primarily, such as shaders, textures, index
buffers, vertex buffers) must be stored in RAM so they can be recreated
whenever needed.

Then if D3D reports a "device removed" or "device reset" error, all D3D
references must first be fully released with no stray references; the
linked list must be fully traversed until all references are released.
Then, the linked list must once again be traversed again, and all those
D3D objects must be recreated with the same data and descriptors (which
are now saved in each object).  Finally, all states need to be reset.

After that's complete, the device is able to continue functioning almost
as it was before, although the output to recording/stream may get a few
green frames due to texture data being reset.

This will temporarily alleviate the "Device Removed" issue while waiting
for a fix from NVIDIA.
master
jp9000 2016-11-03 07:41:23 -07:00
parent 5eb0c4ec83
commit 8e8834f109
4 changed files with 349 additions and 1 deletions

View File

@ -14,6 +14,7 @@ set(libobs-d3d11_SOURCES
d3d11-texture2d.cpp
d3d11-vertexbuffer.cpp
d3d11-duplicator.cpp
d3d11-rebuild.cpp
d3d11-zstencilbuffer.cpp)
set(libobs-d3d11_HEADERS

View File

@ -0,0 +1,313 @@
/******************************************************************************
Copyright (C) 2016 by Hugh Bailey <obs.jim@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "d3d11-subsystem.hpp"
inline void gs_vertex_buffer::Rebuild()
{
uvBuffers.clear();
uvSizes.clear();
BuildBuffers();
}
inline void gs_index_buffer::Rebuild(ID3D11Device *dev)
{
HRESULT hr = dev->CreateBuffer(&bd, &srd, &indexBuffer);
if (FAILED(hr))
throw HRError("Failed to create buffer", hr);
}
inline void gs_texture_2d::Rebuild(ID3D11Device *dev)
{
HRESULT hr;
if (isShared) {
hr = dev->OpenSharedResource((HANDLE)(uintptr_t)sharedHandle,
__uuidof(ID3D11Texture2D), (void**)&texture);
if (FAILED(hr))
throw HRError("Failed to open shared 2D texture", hr);
} else {
hr = dev->CreateTexture2D(&td,
data.size() ? srd.data() : nullptr,
&texture);
if (FAILED(hr))
throw HRError("Failed to create 2D texture", hr);
}
hr = dev->CreateShaderResourceView(texture, &resourceDesc, &shaderRes);
if (FAILED(hr))
throw HRError("Failed to create resource view", hr);
if (isRenderTarget)
InitRenderTargets();
}
inline void gs_zstencil_buffer::Rebuild(ID3D11Device *dev)
{
HRESULT hr;
hr = dev->CreateTexture2D(&td, nullptr, &texture);
if (FAILED(hr))
throw HRError("Failed to create depth stencil texture", hr);
hr = dev->CreateDepthStencilView(texture, &dsvd, &view);
if (FAILED(hr))
throw HRError("Failed to create depth stencil view", hr);
}
inline void gs_stage_surface::Rebuild(ID3D11Device *dev)
{
HRESULT hr = dev->CreateTexture2D(&td, nullptr, &texture);
if (FAILED(hr))
throw HRError("Failed to create staging surface", hr);
}
inline void gs_sampler_state::Rebuild(ID3D11Device *dev)
{
HRESULT hr = dev->CreateSamplerState(&sd, state.Assign());
if (FAILED(hr))
throw HRError("Failed to create sampler state", hr);
}
inline void gs_vertex_shader::Rebuild(ID3D11Device *dev)
{
HRESULT hr;
hr = dev->CreateVertexShader(data.data(), data.size(), nullptr, &shader);
if (FAILED(hr))
throw HRError("Failed to create vertex shader", hr);
hr = dev->CreateInputLayout(layoutData.data(), (UINT)layoutData.size(),
data.data(), data.size(), &layout);
if (FAILED(hr))
throw HRError("Failed to create input layout", hr);
if (constantSize) {
hr = dev->CreateBuffer(&bd, NULL, &constants);
if (FAILED(hr))
throw HRError("Failed to create constant buffer", hr);
}
for (gs_shader_param &param : params) {
param.nextSampler = nullptr;
param.curValue.clear();
gs_shader_set_default(&param);
}
}
inline void gs_pixel_shader::Rebuild(ID3D11Device *dev)
{
HRESULT hr;
hr = dev->CreatePixelShader(data.data(), data.size(), nullptr,
&shader);
if (FAILED(hr))
throw HRError("Failed to create pixel shader", hr);
if (constantSize) {
hr = dev->CreateBuffer(&bd, NULL, &constants);
if (FAILED(hr))
throw HRError("Failed to create constant buffer", hr);
}
for (gs_shader_param &param : params) {
param.nextSampler = nullptr;
param.curValue.clear();
gs_shader_set_default(&param);
}
}
inline void gs_swap_chain::Rebuild(ID3D11Device *dev)
{
HRESULT hr = device->factory->CreateSwapChain(dev, &swapDesc, &swap);
if (FAILED(hr))
throw HRError("Failed to create swap chain", hr);
Init();
}
inline void SavedBlendState::Rebuild(ID3D11Device *dev)
{
HRESULT hr = dev->CreateBlendState(&bd, &state);
if (FAILED(hr))
throw HRError("Failed to create blend state", hr);
}
inline void SavedZStencilState::Rebuild(ID3D11Device *dev)
{
HRESULT hr = dev->CreateDepthStencilState(&dsd, &state);
if (FAILED(hr))
throw HRError("Failed to create depth stencil state", hr);
}
inline void SavedRasterState::Rebuild(ID3D11Device *dev)
{
HRESULT hr = dev->CreateRasterizerState(&rd, &state);
if (FAILED(hr))
throw HRError("Failed to create rasterizer state", hr);
}
const static D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
};
void gs_device::RebuildDevice()
try {
ID3D11Device *dev = nullptr;
HRESULT hr;
blog(LOG_WARNING, "Device Remove/Reset! Rebuilding all assets...");
/* ----------------------------------------------------------------- */
gs_obj *obj = first_obj;
while (obj) {
switch (obj->obj_type) {
case gs_type::gs_vertex_buffer:
((gs_vertex_buffer*)obj)->Release();
break;
case gs_type::gs_index_buffer:
((gs_index_buffer*)obj)->Release();
break;
case gs_type::gs_texture_2d:
((gs_texture_2d*)obj)->Release();
break;
case gs_type::gs_zstencil_buffer:
((gs_zstencil_buffer*)obj)->Release();
break;
case gs_type::gs_stage_surface:
((gs_stage_surface*)obj)->Release();
break;
case gs_type::gs_sampler_state:
((gs_sampler_state*)obj)->Release();
break;
case gs_type::gs_vertex_shader:
((gs_vertex_shader*)obj)->Release();
break;
case gs_type::gs_pixel_shader:
((gs_pixel_shader*)obj)->Release();
break;
case gs_type::gs_duplicator:
((gs_duplicator*)obj)->Release();
break;
case gs_type::gs_swap_chain:
((gs_swap_chain*)obj)->Release();
break;
}
obj = obj->next;
}
for (auto &state : zstencilStates)
state.Release();
for (auto &state : rasterStates)
state.Release();
for (auto &state : blendStates)
state.Release();
context->ClearState();
context.Release();
device.Release();
adapter.Release();
factory.Release();
/* ----------------------------------------------------------------- */
InitFactory(adpIdx);
uint32_t createFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
hr = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN,
nullptr, createFlags, featureLevels,
sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL),
D3D11_SDK_VERSION, &device, nullptr, &context);
if (FAILED(hr))
throw HRError("Failed to create device", hr);
dev = device;
obj = first_obj;
while (obj) {
switch (obj->obj_type) {
case gs_type::gs_vertex_buffer:
((gs_vertex_buffer*)obj)->Rebuild();
break;
case gs_type::gs_index_buffer:
((gs_index_buffer*)obj)->Rebuild(dev);
break;
case gs_type::gs_texture_2d:
((gs_texture_2d*)obj)->Rebuild(dev);
break;
case gs_type::gs_zstencil_buffer:
((gs_zstencil_buffer*)obj)->Rebuild(dev);
break;
case gs_type::gs_stage_surface:
((gs_stage_surface*)obj)->Rebuild(dev);
break;
case gs_type::gs_sampler_state:
((gs_sampler_state*)obj)->Rebuild(dev);
break;
case gs_type::gs_vertex_shader:
((gs_vertex_shader*)obj)->Rebuild(dev);
break;
case gs_type::gs_pixel_shader:
((gs_pixel_shader*)obj)->Rebuild(dev);
break;
case gs_type::gs_duplicator:
((gs_duplicator*)obj)->Start();
break;
case gs_type::gs_swap_chain:
((gs_swap_chain*)obj)->Rebuild(dev);
break;
}
obj = obj->next;
}
curRenderTarget = nullptr;
curZStencilBuffer = nullptr;
curRenderSide = 0;
memset(&curTextures, 0, sizeof(curTextures));
memset(&curSamplers, 0, sizeof(curSamplers));
curVertexBuffer = nullptr;
curIndexBuffer = nullptr;
curVertexShader = nullptr;
curPixelShader = nullptr;
curSwapChain = nullptr;
zstencilStateChanged = true;
rasterStateChanged = true;
blendStateChanged = true;
curDepthStencilState = nullptr;
curRasterState = nullptr;
curBlendState = nullptr;
curToplogy = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
for (auto &state : zstencilStates)
state.Rebuild(dev);
for (auto &state : rasterStates)
state.Rebuild(dev);
for (auto &state : blendStates)
state.Rebuild(dev);
} catch (const char *error) {
bcrash("Failed to recreate D3D11: %s", error);
} catch (HRError error) {
bcrash("Failed to recreate D3D11: %s (%08lX)",
error.str, error.hr);
}

View File

@ -1432,8 +1432,14 @@ void device_clear(gs_device_t *device, uint32_t clear_flags,
void device_present(gs_device_t *device)
{
HRESULT hr;
if (device->curSwapChain) {
device->curSwapChain->swap->Present(0, 0);
hr = device->curSwapChain->swap->Present(0, 0);
if (hr == DXGI_ERROR_DEVICE_REMOVED ||
hr == DXGI_ERROR_DEVICE_RESET) {
device->RebuildDevice();
}
} else {
blog(LOG_WARNING, "device_present (D3D11): No active swap");
}

View File

@ -265,6 +265,8 @@ struct gs_vertex_buffer : gs_obj {
uvBuffers.clear();
}
inline void Rebuild();
gs_vertex_buffer(gs_device_t *device, struct gs_vb_data *data,
uint32_t flags);
};
@ -290,6 +292,8 @@ struct gs_index_buffer : gs_obj {
void InitBuffer();
inline void Rebuild(ID3D11Device *dev);
inline void Release() {indexBuffer.Release();}
gs_index_buffer(gs_device_t *device, enum gs_index_type type,
@ -304,6 +308,8 @@ struct gs_texture : gs_obj {
ComPtr<ID3D11ShaderResourceView> shaderRes;
D3D11_SHADER_RESOURCE_VIEW_DESC resourceDesc = {};
inline void Rebuild(ID3D11Device *dev);
inline gs_texture(gs_texture_type type, uint32_t levels,
gs_color_format format)
: type (type),
@ -354,6 +360,8 @@ struct gs_texture_2d : gs_texture {
void InitRenderTargets();
void BackupTexture(const uint8_t **data);
inline void Rebuild(ID3D11Device *dev);
inline void Release()
{
texture.Release();
@ -389,6 +397,8 @@ struct gs_zstencil_buffer : gs_obj {
void InitBuffer();
inline void Rebuild(ID3D11Device *dev);
inline void Release()
{
texture.Release();
@ -414,6 +424,8 @@ struct gs_stage_surface : gs_obj {
gs_color_format format;
DXGI_FORMAT dxgiFormat;
inline void Rebuild(ID3D11Device *dev);
inline void Release()
{
texture.Release();
@ -428,6 +440,8 @@ struct gs_sampler_state : gs_obj {
D3D11_SAMPLER_DESC sd = {};
gs_sampler_info info;
inline void Rebuild(ID3D11Device *dev);
inline void Release() {state.Release();}
gs_sampler_state(gs_device_t *device, const gs_sampler_info *info);
@ -515,6 +529,8 @@ struct gs_vertex_shader : gs_shader {
bool hasTangents;
uint32_t nTexUnits;
inline void Rebuild(ID3D11Device *dev);
inline void Release()
{
shader.Release();
@ -558,6 +574,8 @@ struct gs_pixel_shader : gs_shader {
ComPtr<ID3D11PixelShader> shader;
vector<unique_ptr<ShaderSampler>> samplers;
inline void Rebuild(ID3D11Device *dev);
inline void Release()
{
shader.Release();
@ -592,6 +610,8 @@ struct gs_swap_chain : gs_obj {
void Resize(uint32_t cx, uint32_t cy);
void Init();
inline void Rebuild(ID3D11Device *dev);
inline void Release()
{
target.Release();
@ -637,6 +657,8 @@ struct SavedBlendState : BlendState {
ComPtr<ID3D11BlendState> state;
D3D11_BLEND_DESC bd;
inline void Rebuild(ID3D11Device *dev);
inline void Release()
{
state.Release();
@ -692,6 +714,8 @@ struct SavedZStencilState : ZStencilState {
ComPtr<ID3D11DepthStencilState> state;
D3D11_DEPTH_STENCIL_DESC dsd;
inline void Rebuild(ID3D11Device *dev);
inline void Release()
{
state.Release();
@ -725,6 +749,8 @@ struct SavedRasterState : RasterState {
ComPtr<ID3D11RasterizerState> state;
D3D11_RASTERIZER_DESC rd;
inline void Rebuild(ID3D11Device *dev);
inline void Release()
{
state.Release();
@ -804,6 +830,8 @@ struct gs_device {
void UpdateViewProjMatrix();
void RebuildDevice();
gs_device(uint32_t adapterIdx);
~gs_device();
};