Implemented recovering from a lost device by context recreation.
TRAC #13222
Singed-off-by: Daniel Koch

Author:    Nicolas Capens <nicolas@transgaming.com>

git-svn-id: http://angleproject.googlecode.com/svn/trunk@406 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libEGL/Display.cpp b/src/libEGL/Display.cpp
index 5aede45..43cbf32 100644
--- a/src/libEGL/Display.cpp
+++ b/src/libEGL/Display.cpp
@@ -17,7 +17,8 @@
 
 #include "libEGL/main.h"
 
-#define REF_RAST 0   // Can also be enabled by defining FORCE_REF_RAST in the project's predefined macros
+#define REF_RAST 0        // Can also be enabled by defining FORCE_REF_RAST in the project's predefined macros
+#define ENABLE_D3D9EX 1   // Enables use of the IDirect3D9Ex interface, when available
 
 namespace egl
 {
@@ -77,7 +78,7 @@
     // Use Direct3D9Ex if available. Among other things, this version is less
     // inclined to report a lost context, for example when the user switches
     // desktop. Direct3D9Ex is available in Windows Vista and later if suitable drivers are available.
-    if (Direct3DCreate9ExPtr && SUCCEEDED(Direct3DCreate9ExPtr(D3D_SDK_VERSION, &mD3d9ex)))
+    if (ENABLE_D3D9EX && Direct3DCreate9ExPtr && SUCCEEDED(Direct3DCreate9ExPtr(D3D_SDK_VERSION, &mD3d9ex)))
     {
         ASSERT(mD3d9ex);
         mD3d9ex->QueryInterface(IID_IDirect3D9, reinterpret_cast<void**>(&mD3d9));
@@ -102,6 +103,8 @@
             return error(EGL_BAD_ALLOC, false);
         }
 
+        ASSERT(SUCCEEDED(result));
+
         if (mDeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0))
         {
             terminate();
@@ -183,13 +186,6 @@
 
             mConfigSet.mSet.insert(configuration);
         }
-
-        if (!createDevice())
-        {
-            terminate();
-
-            return false;
-        }
     }
 
     if (!isInitialized())
@@ -199,6 +195,11 @@
         return false;
     }
 
+    static const TCHAR windowName[] = TEXT("AngleHiddenWindow");
+    static const TCHAR className[] = TEXT("STATIC");
+
+    mDeviceWindow = CreateWindowEx(WS_EX_NOACTIVATE, className, windowName, WS_DISABLED | WS_POPUP, 0, 0, 1, 1, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
+
     return true;
 }
 
@@ -314,28 +315,7 @@
 
 bool Display::createDevice()
 {
-    static const TCHAR windowName[] = TEXT("AngleHiddenWindow");
-    static const TCHAR className[] = TEXT("STATIC");
-
-    mDeviceWindow = CreateWindowEx(WS_EX_NOACTIVATE, className, windowName, WS_DISABLED | WS_POPUP, 0, 0, 1, 1, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
-
-    D3DPRESENT_PARAMETERS presentParameters = {0};
-
-    // The default swap chain is never actually used. Surface will create a new swap chain with the proper parameters.
-    presentParameters.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
-    presentParameters.BackBufferCount = 1;
-    presentParameters.BackBufferFormat = D3DFMT_UNKNOWN;
-    presentParameters.BackBufferWidth = 1;
-    presentParameters.BackBufferHeight = 1;
-    presentParameters.EnableAutoDepthStencil = FALSE;
-    presentParameters.Flags = 0;
-    presentParameters.hDeviceWindow = mDeviceWindow;
-    presentParameters.MultiSampleQuality = 0;
-    presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;
-    presentParameters.PresentationInterval = convertInterval(mMinSwapInterval);
-    presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
-    presentParameters.Windowed = TRUE;
-
+    D3DPRESENT_PARAMETERS presentParameters = getPresentParameters();
     DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_NOWINDOWCHANGES;
 
     HRESULT result = mD3d9->CreateDevice(mAdapter, mDeviceType, mDeviceWindow, behaviorFlags | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE, &presentParameters, &mDevice);
@@ -377,6 +357,40 @@
 
 EGLContext Display::createContext(EGLConfig configHandle, const gl::Context *shareContext)
 {
+    if (!mDevice)
+    {
+        if (!createDevice())
+        {
+            return NULL;
+        }
+    }
+    else if (FAILED(mDevice->TestCooperativeLevel()))   // Lost device
+    {
+        D3DPRESENT_PARAMETERS presentParameters = getPresentParameters();
+        HRESULT result;
+        
+        do
+        {
+            Sleep(0);   // Give the graphics driver some CPU time
+
+            result = mDevice->Reset(&presentParameters);
+        }
+        while (result == D3DERR_DEVICELOST);
+
+        if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DEVICEREMOVED || result == D3DERR_DRIVERINTERNALERROR)
+        {
+            return error(EGL_BAD_ALLOC, (EGLContext)NULL);
+        }
+
+        ASSERT(SUCCEEDED(result));
+
+        // Restore any surfaces that may have been lost
+        for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
+        {
+            (*surface)->resetSwapChain();
+        }
+    }
+
     const egl::Config *config = mConfigSet.get(configHandle);
 
     gl::Context *context = glCreateContext(config, shareContext);
@@ -395,6 +409,14 @@
 {
     glDestroyContext(context);
     mContextSet.erase(context);
+
+    if (mContextSet.empty() && mDevice && FAILED(mDevice->TestCooperativeLevel()))   // Last context of a lost device
+    {
+        for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
+        {
+            (*surface)->release();
+        }	
+    }
 }
 
 bool Display::isInitialized()
@@ -461,6 +483,14 @@
 
 IDirect3DDevice9 *Display::getDevice()
 {
+    if (!mDevice)
+    {
+        if (!createDevice())
+        {
+            return NULL;
+        }
+    }
+
     return mDevice;
 }
 
@@ -541,4 +571,26 @@
 
     return result != D3DERR_NOTAVAILABLE;
 }
+
+D3DPRESENT_PARAMETERS Display::getPresentParameters()
+{
+    D3DPRESENT_PARAMETERS presentParameters = {0};
+
+    // The default swap chain is never actually used. Surface will create a new swap chain with the proper parameters.
+    presentParameters.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
+    presentParameters.BackBufferCount = 1;
+    presentParameters.BackBufferFormat = D3DFMT_UNKNOWN;
+    presentParameters.BackBufferWidth = 1;
+    presentParameters.BackBufferHeight = 1;
+    presentParameters.EnableAutoDepthStencil = FALSE;
+    presentParameters.Flags = 0;
+    presentParameters.hDeviceWindow = mDeviceWindow;
+    presentParameters.MultiSampleQuality = 0;
+    presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;
+    presentParameters.PresentationInterval = convertInterval(mMinSwapInterval);
+    presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
+    presentParameters.Windowed = TRUE;
+
+    return presentParameters;
 }
+}
\ No newline at end of file
diff --git a/src/libEGL/Display.h b/src/libEGL/Display.h
index 8b85efd..4d4f4db 100644
--- a/src/libEGL/Display.h
+++ b/src/libEGL/Display.h
@@ -68,6 +68,9 @@
 
   private:
     DISALLOW_COPY_AND_ASSIGN(Display);
+
+    D3DPRESENT_PARAMETERS getPresentParameters();
+
     const HDC mDc;
 
     HMODULE mD3d9Module;
diff --git a/src/libEGL/Surface.cpp b/src/libEGL/Surface.cpp
index a5638d4..2dd608e 100644
--- a/src/libEGL/Surface.cpp
+++ b/src/libEGL/Surface.cpp
@@ -37,39 +37,51 @@
 
 Surface::~Surface()
 {
+    release();
+}
+
+void Surface::release()
+{
     if (mSwapChain)
     {
         mSwapChain->Release();
+        mSwapChain = NULL;
     }
 
     if (mBackBuffer)
     {
         mBackBuffer->Release();
+        mBackBuffer = NULL;
     }
 
     if (mRenderTarget)
     {
         mRenderTarget->Release();
+        mRenderTarget = NULL;
     }
 
     if (mDepthStencil)
     {
         mDepthStencil->Release();
+        mDepthStencil = NULL;
     }
 
     if (mFlipTexture)
     {
         mFlipTexture->Release();
+        mFlipTexture = NULL;
     }
 
     if (mFlipState)
     {
         mFlipState->Release();
+        mFlipState = NULL;
     }
 
     if (mPreFlipState)
     {
         mPreFlipState->Release();
+        mPreFlipState = NULL;
     }
 }
 
diff --git a/src/libEGL/Surface.h b/src/libEGL/Surface.h
index 5bc912c..ae13655 100644
--- a/src/libEGL/Surface.h
+++ b/src/libEGL/Surface.h
@@ -29,6 +29,9 @@
 
     ~Surface();
 
+    void release();
+    void resetSwapChain();
+
     HWND getWindowHandle();
     bool swap();
 
@@ -48,7 +51,6 @@
     IDirect3DSurface9 *mDepthStencil;
     IDirect3DTexture9 *mFlipTexture;
 
-    void resetSwapChain();
     bool checkForWindowResize();
 
     void applyFlipState(IDirect3DDevice9 *device);
diff --git a/src/libEGL/libEGL.cpp b/src/libEGL/libEGL.cpp
index 5ceb6ef..aedc669 100644
--- a/src/libEGL/libEGL.cpp
+++ b/src/libEGL/libEGL.cpp
@@ -825,7 +825,7 @@
         gl::Context *context = static_cast<gl::Context*>(ctx);
         IDirect3DDevice9 *device = display->getDevice();
 
-        if (!device || device->TestCooperativeLevel() != D3D_OK)
+        if (!device || FAILED(device->TestCooperativeLevel()))
         {
             return error(EGL_CONTEXT_LOST, EGL_FALSE);
         }
diff --git a/src/libGLESv2/Blit.cpp b/src/libGLESv2/Blit.cpp
index 00c878f..e964865 100644
--- a/src/libGLESv2/Blit.cpp
+++ b/src/libGLESv2/Blit.cpp
@@ -382,6 +382,11 @@
 
 IDirect3DTexture9 *Blit::copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &sourceRect)
 {
+    if (!surface)
+    {
+        return NULL;
+    }
+
     egl::Display *display = getDisplay();
     IDirect3DDevice9 *device = getDevice();
 
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index b68f5f1..9c1ada4 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -303,7 +303,10 @@
 
     setFramebufferZero(framebufferZero);
 
-    defaultRenderTarget->Release();
+    if (defaultRenderTarget)
+    {
+        defaultRenderTarget->Release();
+    }
 
     if (depthStencil)
     {
@@ -1585,6 +1588,12 @@
     }
 
     IDirect3DSurface9 *renderTarget = framebufferObject->getRenderTarget();
+
+    if (!renderTarget)
+    {
+        return false;   // Context must be lost
+    }
+
     IDirect3DSurface9 *depthStencil = NULL;
 
     unsigned int renderTargetSerial = framebufferObject->getRenderTargetSerial();
@@ -2098,6 +2107,12 @@
     }
 
     IDirect3DSurface9 *renderTarget = framebuffer->getRenderTarget();
+
+    if (!renderTarget)
+    {
+        return;   // Context must be lost, return silently
+    }
+
     IDirect3DDevice9 *device = getDevice();
 
     D3DSURFACE_DESC desc;
@@ -2396,6 +2411,11 @@
 
     IDirect3DSurface9 *renderTarget = framebufferObject->getRenderTarget();
 
+    if (!renderTarget)
+    {
+        return;   // Context must be lost, return silently
+    }
+
     D3DSURFACE_DESC desc;
     renderTarget->GetDesc(&desc);