D3D11: Added code to handle GPU-device-removed scenarios
authorDavid Ludwig <dludwig@pobox.com>
Sun, 23 Mar 2014 13:48:16 -0400
changeset 866807dce0097f29
parent 8667 fb44c438a3e5
child 8669 62dfa45e5e08
D3D11: Added code to handle GPU-device-removed scenarios

These scenarios can happen when a GPU is switched, its driver updated, or in
some virtual machines (such as Parallels) are suspended and then resumed. In
these cases, all GPU resources will already be lost, and it's up to the app to
recover.

For now, SDL's D3D11 renderer will handle this by freeing all GPU resources,
including all textures, and then sending a SDL_RENDER_TARGETS_RESET event.
It's currently up to an app to intercept this event, destroy all of its
textures, then recreate them from scratch.
src/render/direct3d11/SDL_render_d3d11.c
     1.1 --- a/src/render/direct3d11/SDL_render_d3d11.c	Sun Mar 23 09:44:04 2014 -0700
     1.2 +++ b/src/render/direct3d11/SDL_render_d3d11.c	Sun Mar 23 13:48:16 2014 -0400
     1.3 @@ -846,9 +846,10 @@
     1.4  }
     1.5  
     1.6  static void
     1.7 -D3D11_DestroyRenderer(SDL_Renderer * renderer)
     1.8 +D3D11_ReleaseAll(SDL_Renderer * renderer)
     1.9  {
    1.10      D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata;
    1.11 +    SDL_Texture *texture = NULL;
    1.12  
    1.13      if (data) {
    1.14          SAFE_RELEASE(data->dxgiFactory);
    1.15 @@ -879,6 +880,29 @@
    1.16          if (data->hDXGIMod) {
    1.17              SDL_UnloadObject(data->hDXGIMod);
    1.18          }
    1.19 +
    1.20 +        data->swapEffect = (DXGI_SWAP_EFFECT) 0;
    1.21 +        data->rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
    1.22 +        data->currentRenderTargetView = NULL;
    1.23 +        data->currentRasterizerState = NULL;
    1.24 +        data->currentBlendState = NULL;
    1.25 +        data->currentShader = NULL;
    1.26 +        data->currentShaderResource = NULL;
    1.27 +        data->currentSampler = NULL;
    1.28 +    }
    1.29 +
    1.30 +    /* Release all textures */
    1.31 +    for (texture = renderer->textures; texture; texture = texture->next) {
    1.32 +        D3D11_DestroyTexture(renderer, texture);
    1.33 +    }
    1.34 +}
    1.35 +
    1.36 +static void
    1.37 +D3D11_DestroyRenderer(SDL_Renderer * renderer)
    1.38 +{
    1.39 +    D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata;
    1.40 +    D3D11_ReleaseAll(renderer);
    1.41 +    if (data) {
    1.42          SDL_free(data);
    1.43      }
    1.44      SDL_free(renderer);
    1.45 @@ -1463,7 +1487,15 @@
    1.46              DXGI_FORMAT_UNKNOWN,
    1.47              0
    1.48              );
    1.49 -        if (FAILED(result)) {
    1.50 +        if (result == DXGI_ERROR_DEVICE_REMOVED) {
    1.51 +            /* If the device was removed for any reason, a new device and swap chain will need to be created. */
    1.52 +            D3D11_HandleDeviceLost(renderer);
    1.53 +
    1.54 +            /* Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method 
    1.55 +             * and correctly set up the new device.
    1.56 +             */
    1.57 +            goto done;
    1.58 +        } else if (FAILED(result)) {
    1.59              WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGISwapChain::ResizeBuffers", result);
    1.60              goto done;
    1.61          }
    1.62 @@ -1537,7 +1569,7 @@
    1.63      D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata;
    1.64      HRESULT result = S_OK;
    1.65  
    1.66 -    /* FIXME: Need to release all resources - all textures are invalid! */
    1.67 +    D3D11_ReleaseAll(renderer);
    1.68  
    1.69      result = D3D11_CreateDeviceResources(renderer);
    1.70      if (FAILED(result)) {
    1.71 @@ -1551,6 +1583,14 @@
    1.72          return result;
    1.73      }
    1.74  
    1.75 +    /* Let the application know that the device has been reset */
    1.76 +    {
    1.77 +        /* TODO/FIXME: consider adding a new SDL event to indicate that the entire rendering device has been reset, not just render targets! */
    1.78 +        SDL_Event event;
    1.79 +        event.type = SDL_RENDER_TARGETS_RESET;
    1.80 +        SDL_PushEvent(&event);
    1.81 +    }
    1.82 +
    1.83      return S_OK;
    1.84  }
    1.85