Direct3D device lost

Hey there,

I'm not sure if it's a good idea to post this topic here, but this was the only place available on the site. It's about direct3d: how do you restore from a lost device? I tried to release the resources and then recreate them as soon as the device was ready again, but nothing shows on the screen once I put back the focus on the window.

Could any one help me?

Here the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include <windows.h>
#include <d3d9.h>

//windows variables
HWND hWnd;
//directx-functions
bool initData();
void releaseResources();
void changeDisplay();
bool testDevice();
//directx-variables
  IDirect3D9* pD3D=NULL;
  D3DPRESENT_PARAMETERS d3dpp;
  IDirect3DDevice9* pDevice;
  IDirect3DVertexBuffer9* vBufferObject;
  void*vBuffer;
  bool appWindowed=false;
  struct mvertex{float x,y,z,rhw;DWORD color;};
  const DWORD d3dfvf=D3DFVF_XYZRHW|D3DFVF_DIFFUSE;
  const mvertex data[]={//middelste driehoek
                      {0,0,1.0f,1.0f,0xFFFF0000},
                      {256,0,1.0f,1.0f,0xFF00FF00},
                      {128,256,1.0f,1.0f,0xFF0000FF},                                                             
                      };
  int countPrimitives=(sizeof(data)/sizeof(mvertex))/3;




bool InitD3D(){
    HRESULT hr;
    if((pD3D = Direct3DCreate9(D3D_SDK_VERSION))==NULL){
        return false;
    }
    D3DDISPLAYMODE d3ddm;
    pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
    ZeroMemory(&d3dpp,sizeof(d3dpp));
    d3dpp.Windowed=appWindowed;    
    d3dpp.BackBufferFormat = d3ddm.Format;
    d3dpp.BackBufferHeight=768;
    d3dpp.BackBufferWidth=1024;
    
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    if(FAILED(pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
    hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice))){
        return false;
    }
    initData();
    
    return true;
} 

void RenderD3DScene(){
    HRESULT hr;
    pDevice->Clear(0,NULL,D3DCLEAR_TARGET,0x00000000,1.0f,0);
    pDevice->BeginScene();
    
    hr=pDevice->SetStreamSource(0,vBufferObject,0,sizeof(mvertex));    
    pDevice->SetFVF(d3dfvf);
    pDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,countPrimitives);    
    pDevice->EndScene();
    pDevice->Present(NULL,NULL,NULL,NULL);
}

void changeDisplay()
{HRESULT hr;
 if(appWindowed)
  {appWindowed=false;
   
  }
 else
  {appWindowed=true;
  }
 d3dpp.Windowed=appWindowed;
 hr=pDevice->Reset(&d3dpp);
 if(FAILED(hr))
   {if(hr==D3DERR_DEVICELOST)
     {MessageBox(0,"D3DERR_DEVICELOST:device cannot be reset","DirectX ERROR",MB_OK);
     }
    else if(hr==D3DERR_DEVICENOTRESET)
     {MessageBox(0,"D3DERR_DEVICENOTRESET","DirectX ERROR",MB_OK);
     }
    else if(hr==D3DERR_DRIVERINTERNALERROR)
     {MessageBox(0,"D3DERR_INTERNALDRIVERERROR","DirectX ERROR",MB_OK);
     }
   }
}
bool testDevice()
{    HRESULT hr;
     hr=pDevice->TestCooperativeLevel();
     //device is lost, and cannot be reset at the moment (wait until the focus returns)
     if(hr==D3D_OK){return true;}
     if(hr == D3DERR_DEVICELOST)
          {return false;           
          }
     //device is lost, but the focus has returned and the device can now be reset
     else if(hr==D3DERR_DEVICENOTRESET)
          {//release the vertex-buffer on the video-driver
           releaseResources();
           InitD3D();               
           return true;
          }
     
     
}

bool initData()
{HRESULT hr;
 hr=pDevice->CreateVertexBuffer(sizeof(data),D3DUSAGE_WRITEONLY,d3dfvf,D3DPOOL_MANAGED,&vBufferObject,NULL);
 if(FAILED(hr)){MessageBox(hWnd,"pDevice->CreateVertexBuffer failed","DirectX ERROR",MB_OK);return false;}
 hr=vBufferObject->Lock(0,0,&vBuffer,0);
 if(FAILED(hr)){MessageBox(hWnd,"pDevice->Lock failed","DirectX ERROR",MB_OK);return false;}
 if(memcpy(vBuffer,data,sizeof(data))==false){MessageBox(hWnd,"memcpy failed","c++ error",MB_OK);return false;};
 hr=vBufferObject->Unlock();  
 if(FAILED(hr)){MessageBox(hWnd,"pDevice->Unlock failed","DirectX ERROR",MB_OK);return false;}
 return true;
}
void releaseResources()
{vBufferObject->Release();
 vBufferObject=NULL;
 vBuffer=NULL;
}

void MessageLoop(){
    MSG msg; 
    if(testDevice()==true){RenderD3DScene();}
    
    while(GetMessage(&msg,hWnd,0,0))
      {TranslateMessage(&msg); 
       DispatchMessage(&msg); 
      } 
}

LRESULT CALLBACK WndProc( HWND hWnd , UINT message , 
    WPARAM wParam , LPARAM lParam){
    switch(message){
    
    case WM_CLOSE:            
        PostQuitMessage(0); 
        return 0; 
        break;    
    case WM_RBUTTONDOWN:
         PostQuitMessage(0);
         break;
    case WM_LBUTTONDOWN:
         changeDisplay();
         break;
    
    default:
        return DefWindowProc(hWnd,message,wParam,lParam); 
        break;
    } 
    
}

bool CreateSimpWindow(int width, int height){

    // Create A Window Class Structure 

    WNDCLASSEX wc;
    wc.cbClsExtra = 0;
    wc.cbSize = sizeof(wc); 
    wc.cbWndExtra = 0; 
    wc.hbrBackground = NULL;
    wc.hCursor = NULL; 
    wc.hIcon = NULL; 
    wc.hIconSm = NULL; 
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "X3D"; 
    wc.lpszMenuName = NULL;
    wc.style = 0;

    // Register Window Class

    RegisterClassEx(&wc); 

    // Create Window 

    hWnd = CreateWindowEx(0, 
       "X3D", "X3D Direct3D Tutorial 1", 
       WS_OVERLAPPEDWINDOW|WS_VISIBLE, 0,0,width,height,
       NULL,NULL,wc.hInstance,0);

    return true;
}
 
 



INT WINAPI WinMain( HINSTANCE , HINSTANCE , LPSTR , INT ){

    if(!CreateSimpWindow(1024, 768))return 1;
       
    if(!InitD3D())return 2;     
    MessageLoop(); 
    
    return 0;
} 
At least this thing below solves a part of that problem


A lost driver can easily be restored, if you know what to do. There are two steps to this.

1. Detect when the application is re-entered with alt-tab.
2. Call
1
2
LPDIRECT3DDEVICE9 d3ddev;
d3ddev->reset(); 


1. Detect when the application is re-entered with alt-tab

Because this is a Windows activity, it involves Windows code, so let's head back to WinMain.cpp.

When the device is lost, the main loop of the game is still running in the background. The only thing that is skipped is the Present() function. This means that we can wait for a Windows message informing us that the game has been restored. The message to do this with is WM_ACTIVATEAPP.

Here is the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
        case WM_ACTIVATEAPP:
            {
                if(wParam)
                    ResetDevice();
            } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}

There are two unfamiliar pieces of code here. One is the wParam and how it is used, and the other is resetdevice().

resetdevice() is a function stored in Direct3D.cpp, so we'll cover that below.

wParam, the message parameter, returns false when you are leaving an application, and returns true when you are entering it. We want to reclaim the device when we are going back into the program, so we'll detect if it is true.

2. Call d3ddev->reset()

Let's take a look at what to put in resetdevice().
1
2
3
4
5
void ResetDevice()
{
    if(d3ddev)
        d3ddev->Reset(&d3dpp);
}

This is simple. If d3ddev exists, reset it.

But why the if? When will d3ddev ever not exist. Well, the WM_ACTIVATEAPP message is called when the program starts too, so we need to make sure that we've created d3ddev already. If not, Reset() would crash the program.

And what's up with that parameter? It is the Direct3D Presentation Parameters. Remember, it's global in Direct3D.cpp, so you have direct access to it.

Sprites

There is one catch. If you are using sprites, you need to prepare the sprite interface for the reset. Neglecting to do this will result in a loss of all your textures, and you will have to reload them.

The modified code is simple:
1
2
3
4
5
6
7
8
9
10
11
void ResetDevice()
{
    if(d3ddev)
    {
        d3dspt->OnLostDevice();

        d3ddev->Reset(&d3dpp);

        d3dspt->OnResetDevice();
    }
}

So what do these do?

OnLostDevice()

This tells Direct3DSprite that the device has been lost, and that all of the video memory it has claimed should be released.

OnResetDevice()

The tells Direct3DSprite that the device has been reset, and that all the memory can be re-acquired.

Fonts

This same thing also needs to be done with fonts:
1
2
3
4
5
6
7
8
9
10
11
12
13
void ResetDevice()
{
    if(d3ddev)
    {
        d3dspt->OnLostDevice();
        d3dfnt->OnLostDevice();

        d3ddev->Reset(&d3dpp);

        d3dspt->OnResetDevice();
        d3dfnt->OnResetDevice();
    }
}
Last edited on
why do you use this?
1
2
HRESULT hr; hr=pDevice->CreateVertexBuffer(sizeof(data),D3DUSAGE_WRITEONLY,d3dfvf,D3DPOOL_MANAGED,&vBufferObject,NULL);
 if(FAILED(hr)){MessageBox(hWnd,"pDevice->CreateVertexBuffer failed","DirectX ERROR",MB_OK);return false;}


instead of
1
2
3
4
5
6
7
8
9
10
11
12
13
if(FAILED(pDevice->CreateVertexBuffer(sizeof(data),
                                                              D3DUSAGE_WRITEONLY,
                                                              d3dfvf,
                                                              D3DPOOL_MANAGED,
                                                              &vBufferObject,
                                                              NULL)))
{
MessageBox(hWnd,
                       "pDevice->CreateVertexBuffer failed",
                       "DirectX ERROR",
                       MB_OK); 
 return false;
}
with all due respect, but your solution is not working. I read that all resources need to be released, then the device has to be reset, and then all resources need to be reallocated. That's why I changed your function "resetDevice" to this:

1
2
3
4
5
6
7
void resetDevice()
{if(pDevice)
       {releaseResources();
        pDevice->Reset(&d3dpp);
        initData();        
       }
}


but nothing is showing after the focus is returned. Strange! I even removed the HRESULT hr from the code as you suggested above. Or has it something to do with the releasing and reallocating of the resources? You showed me an example of how to handle sprites and fonts, but nothing about normal vertexbuffers.

thanks in advance
Try this one.. it draws the triangle than it restores using that d3ddev->Reset(&d3dpp);

It doesn't do any checks at all, but at least it does what you want. If you release the resources, you have to load them again.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>

// define the screen resolution
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600

// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")

// global declarations
LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev;    // the pointer to the device class
LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL;    // the pointer to the vertex buffer
D3DPRESENT_PARAMETERS d3dpp;


// function prototypes
void initD3D(HWND hWnd);    // sets up and initializes Direct3D
void render_frame(void);    // renders a single frame
void cleanD3D(void);    // closes Direct3D and releases memory
void init_graphics(void);    // 3D declarations

struct CUSTOMVERTEX {FLOAT X, Y, Z, RHW; DWORD COLOR;};
#define CUSTOMFVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"WindowClass";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL,
                          L"WindowClass",
                          L"Our Direct3D Program",
                          WS_OVERLAPPEDWINDOW,
                          0, 0,
                          SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

    ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);

    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if(msg.message == WM_QUIT)
            break;

        render_frame();
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;

			case WM_ACTIVATEAPP:
            {
                if(wParam)
					if(d3ddev)
                    d3ddev->Reset(&d3dpp);
            } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);


    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = false;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;

    // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    init_graphics();    // call the function to initialize the triangle
}


// this is the function used to render a single frame
void render_frame(void)
{
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();

        // select which vertex format we are using
        d3ddev->SetFVF(CUSTOMFVF);

        // select the vertex buffer to display
        d3ddev->SetStreamSource(0, v_buffer, 0, sizeof(CUSTOMVERTEX));

        // copy the vertex buffer to the back buffer
        d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

    d3ddev->EndScene();

    d3ddev->Present(NULL, NULL, NULL, NULL);
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    v_buffer->Release();    // close and release the vertex buffer
    d3ddev->Release();    // close and release the 3D device
    d3d->Release();    // close and release Direct3D
}


// this is the function that puts the 3D models into video RAM
void init_graphics(void)
{
    // create the vertices using the CUSTOMVERTEX struct
    CUSTOMVERTEX vertices[] =
    {
        { 400.0f, 62.5f, 0.5f, 1.0f, D3DCOLOR_XRGB(0, 0, 255), },
        { 650.0f, 500.0f, 0.5f, 1.0f, D3DCOLOR_XRGB(0, 255, 0), },
        { 150.0f, 500.0f, 0.5f, 1.0f, D3DCOLOR_XRGB(255, 0, 0), },
    };

    // create a vertex buffer interface called v_buffer
    d3ddev->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX),
                               0,
                               CUSTOMFVF,
                               D3DPOOL_MANAGED,
                               &v_buffer,
                               NULL);

    VOID* pVoid;    // a void pointer

    // lock v_buffer and load the vertices into it
    v_buffer->Lock(0, 0, (void**)&pVoid, 0);
    memcpy(pVoid, vertices, sizeof(vertices));
    v_buffer->Unlock();
}
Last edited on
hey that worked! Thanks a lot! Although I'm not exactly sure why tutorials keep claiming that all resources need to be freed and reallocated (http://www.drunkenhyena.com/cgi-bin/view_cpp_article.pl?chapter=2;article=10#Lost_Devices for example!). In this case nothing is lost at all. So you mean that only fonts and sprites need to be prepared for releasing and reallocating?
Other things that might need reloading are textures and meshes. I can't think of anyother now.


set your account so that you can revice PM, and send me one when you did that. I'll give you some usefull info there.
Last edited on
Topic archived. No new replies allowed.