Add the ability to recover from a removed device (TDR or driver removal) to the D3D11 code path.

TRAC #22411

Signed-off-by: Geoff Lang
Signed-off-by: Shannon Woods
Author: Jamie Madill

git-svn-id: https://angleproject.googlecode.com/svn/branches/dx11proto@1857 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index 7fec8d5..be2a92a 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -2092,7 +2092,7 @@
 
 GLenum Context::getResetStatus()
 {
-    if (mResetStatus == GL_NO_ERROR)
+    if (mResetStatus == GL_NO_ERROR && !mContextLost)
     {
         // mResetStatus will be set by the markContextLost callback
         // in the case a notification is sent
@@ -2103,6 +2103,8 @@
 
     if (mResetStatus != GL_NO_ERROR)
     {
+        ASSERT(mContextLost);
+
         if (mRenderer->testDeviceResettable())
         {
             mResetStatus = GL_NO_ERROR;
diff --git a/src/libGLESv2/renderer/Image11.cpp b/src/libGLESv2/renderer/Image11.cpp
index 41dc670..df9965b 100644
--- a/src/libGLESv2/renderer/Image11.cpp
+++ b/src/libGLESv2/renderer/Image11.cpp
@@ -401,15 +401,22 @@
 {
     createStagingTexture();
 
-    HRESULT result = D3DERR_INVALIDCALL;
+    HRESULT result = E_FAIL;
 
     if (mStagingTexture)
     {
         ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
         result = deviceContext->Map(mStagingTexture, 0, D3D11_MAP_WRITE, 0, map);
-        ASSERT(SUCCEEDED(result));
 
-        mDirty = true;
+        // this can fail if the device is removed (from TDR)
+        if (d3d11::isDeviceLostError(result))
+        {
+            mRenderer->notifyDeviceLost();
+        }
+        else if (SUCCEEDED(result))
+        {
+            mDirty = true;
+        }
     }
 
     return result;
diff --git a/src/libGLESv2/renderer/RenderTarget11.cpp b/src/libGLESv2/renderer/RenderTarget11.cpp
index 37d777b..e485a68 100644
--- a/src/libGLESv2/renderer/RenderTarget11.cpp
+++ b/src/libGLESv2/renderer/RenderTarget11.cpp
@@ -238,7 +238,7 @@
         return;
     }
 
-    HRESULT result = D3DERR_INVALIDCALL;
+    HRESULT result = E_FAIL;
     
     if (width > 0 && height > 0)
     {
diff --git a/src/libGLESv2/renderer/Renderer11.cpp b/src/libGLESv2/renderer/Renderer11.cpp
index f47f11a..bafbe6c 100644
--- a/src/libGLESv2/renderer/Renderer11.cpp
+++ b/src/libGLESv2/renderer/Renderer11.cpp
@@ -107,45 +107,7 @@
 
 Renderer11::~Renderer11()
 {
-    releaseDeviceResources();
-
-    if (mDxgiFactory)
-    {
-        mDxgiFactory->Release();
-        mDxgiFactory = NULL;
-    }
-
-    if (mDxgiAdapter)
-    {
-        mDxgiAdapter->Release();
-        mDxgiAdapter = NULL;
-    }
-
-    if (mDeviceContext)
-    {
-        mDeviceContext->ClearState();
-        mDeviceContext->Flush();
-        mDeviceContext->Release();
-        mDeviceContext = NULL;
-    }
-
-    if (mDevice)
-    {
-        mDevice->Release();
-        mDevice = NULL;
-    }
-
-    if (mD3d11Module)
-    {
-        FreeLibrary(mD3d11Module);
-        mD3d11Module = NULL;
-    }
-
-    if (mDxgiModule)
-    {
-        FreeLibrary(mDxgiModule);
-        mDxgiModule = NULL;
-    }
+    release();
 }
 
 Renderer11 *Renderer11::makeRenderer11(Renderer *renderer)
@@ -170,6 +132,8 @@
         return EGL_NOT_INITIALIZED;
     }
 
+    // create the D3D11 device
+    ASSERT(mDevice == NULL);
     PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(mD3d11Module, "D3D11CreateDevice");
 
     if (D3D11CreateDevice == NULL)
@@ -1775,11 +1739,18 @@
 {
     bool isLost = false;
 
-    // TODO
-    //UNIMPLEMENTED();
+    // GetRemovedReason is used to test if the device is removed
+    HRESULT result = mDevice->GetDeviceRemovedReason();
+    isLost = d3d11::isDeviceLostError(result);
 
     if (isLost)
     {
+        // Log error if this is a new device lost event
+        if (mDeviceLost == false)
+        {
+            ERR("The D3D11 device was removed: 0x%08X", result);
+        }
+
         // ensure we note the device loss --
         // we'll probably get this done again by notifyDeviceLost
         // but best to remember it!
@@ -1797,30 +1768,106 @@
 
 bool Renderer11::testDeviceResettable()
 {
-    HRESULT status = D3D_OK;
+    // determine if the device is resettable by creating a dummy device
+    PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(mD3d11Module, "D3D11CreateDevice");
 
-    // TODO
-    UNIMPLEMENTED();
-
-    switch (status)
+    if (D3D11CreateDevice == NULL)
     {
-      case D3DERR_DEVICENOTRESET:
-      case D3DERR_DEVICEHUNG:
-        return true;
-      default:
         return false;
     }
+
+    D3D_FEATURE_LEVEL featureLevel[] =
+    {
+        D3D_FEATURE_LEVEL_11_0,
+        D3D_FEATURE_LEVEL_10_1,
+        D3D_FEATURE_LEVEL_10_0,
+    };
+
+    ID3D11Device* dummyDevice;
+    D3D_FEATURE_LEVEL dummyFeatureLevel;
+    ID3D11DeviceContext* dummyContext;
+
+    HRESULT result = D3D11CreateDevice(NULL,
+                                       D3D_DRIVER_TYPE_HARDWARE,
+                                       NULL,
+                                       #if defined(_DEBUG)
+                                       D3D11_CREATE_DEVICE_DEBUG,
+                                       #else
+                                       0,
+                                       #endif
+                                       featureLevel,
+                                       sizeof(featureLevel)/sizeof(featureLevel[0]),
+                                       D3D11_SDK_VERSION,
+                                       &dummyDevice,
+                                       &dummyFeatureLevel,
+                                       &dummyContext);
+
+    if (!mDevice || FAILED(result))
+    {
+        return false;
+    }
+
+    dummyContext->Release();
+    dummyDevice->Release();
+
+    return true;
+}
+
+void Renderer11::release()
+{
+    releaseDeviceResources();
+
+    if (mDxgiFactory)
+    {
+        mDxgiFactory->Release();
+        mDxgiFactory = NULL;
+    }
+
+    if (mDxgiAdapter)
+    {
+        mDxgiAdapter->Release();
+        mDxgiAdapter = NULL;
+    }
+
+    if (mDeviceContext)
+    {
+        mDeviceContext->ClearState();
+        mDeviceContext->Flush();
+        mDeviceContext->Release();
+        mDeviceContext = NULL;
+    }
+
+    if (mDevice)
+    {
+        mDevice->Release();
+        mDevice = NULL;
+    }
+
+    if (mD3d11Module)
+    {
+        FreeLibrary(mD3d11Module);
+        mD3d11Module = NULL;
+    }
+
+    if (mDxgiModule)
+    {
+        FreeLibrary(mDxgiModule);
+        mDxgiModule = NULL;
+    }
 }
 
 bool Renderer11::resetDevice()
 {
-    releaseDeviceResources();
+    // recreate everything
+    release();
+    EGLint result = initialize();
 
-    // TODO
-    UNIMPLEMENTED();
+    if (result != EGL_SUCCESS)
+    {
+        ERR("Could not reinitialize D3D11 device: %08X", result);
+        return false;
+    }
 
-    // reset device defaults
-    initializeDevice();
     mDeviceLost = false;
 
     return true;
diff --git a/src/libGLESv2/renderer/Renderer11.h b/src/libGLESv2/renderer/Renderer11.h
index 8642958..d5fe318 100644
--- a/src/libGLESv2/renderer/Renderer11.h
+++ b/src/libGLESv2/renderer/Renderer11.h
@@ -196,6 +196,7 @@
     void initializeDevice();
     void releaseDeviceResources();
     int getMinorShaderModel() const;
+    void release();
 
     RenderStateCache mStateCache;
 
diff --git a/src/libGLESv2/renderer/SwapChain11.cpp b/src/libGLESv2/renderer/SwapChain11.cpp
index a3e8a7a..f9e1a3b 100644
--- a/src/libGLESv2/renderer/SwapChain11.cpp
+++ b/src/libGLESv2/renderer/SwapChain11.cpp
@@ -536,7 +536,23 @@
 
     // Draw
     deviceContext->Draw(4, 0);
-    mSwapChain->Present(mSwapInterval, 0);
+    result = mSwapChain->Present(mSwapInterval, 0);
+
+    if (result == DXGI_ERROR_DEVICE_REMOVED)
+    {
+        HRESULT removedReason = device->GetDeviceRemovedReason();
+        ERR("Present failed: the D3D11 device was removed: 0x%08X", removedReason);
+        return EGL_CONTEXT_LOST;
+    }
+    else if (result == DXGI_ERROR_DEVICE_RESET)
+    {
+        ERR("Present failed: the D3D11 device was reset from a bad command.");
+        return EGL_CONTEXT_LOST;
+    }
+    else if (FAILED(result))
+    {
+        ERR("Present failed with error code 0x%08X", result);
+    }
 
     // Unbind
     static ID3D11ShaderResourceView *const nullSRV = NULL;
diff --git a/src/libGLESv2/renderer/TextureStorage11.cpp b/src/libGLESv2/renderer/TextureStorage11.cpp
index b75d94e..9a3db01 100644
--- a/src/libGLESv2/renderer/TextureStorage11.cpp
+++ b/src/libGLESv2/renderer/TextureStorage11.cpp
@@ -235,7 +235,13 @@
 
         HRESULT result = device->CreateTexture2D(&desc, NULL, &mTexture);
 
-        if (FAILED(result))
+        // this can happen from windows TDR
+        if (d3d11::isDeviceLostError(result))
+        {
+            mRenderer->notifyDeviceLost();
+            gl::error(GL_OUT_OF_MEMORY);
+        }
+        else if (FAILED(result))
         {
             ASSERT(result == E_OUTOFMEMORY);
             ERR("Creating image failed.");