2

Recently, I've found a bug in my 3D application that uses DirectX 9. The bug is that everytime I alt-tab or ctrl-alt-delete, the program freezes or seems to do nothing. I've looked around and found out that I need to reset my device by using IDirect3DDevice9::TestCooperativeLevel and IDirect3DDevice9::Reset. But what I'm struggling on at the moment is that (as far as I know) before I Reset the device, I need to release the resouce in D3DPOOL_MANAGE before I reset the device and reallocate the resource back to it after the Reset? How do I do this? Below is the code I've got for the reset of my device (in the IsDeviceLost() function).

DirectX.h

#pragma once
#pragma comment(lib, "d3d9.lib")

#include<d3d9.h>
#include<d3dx9math.h>

#include"Window.h"
#include"Vertex.h"
#include"Shader.h"

#define VERTEXFORMAT (D3DFVF_XYZ | D3DFVF_TEX1) //Flags for the Flexible Vertex Format (FVF)

class Shader;

class DirectX
{
public:
    DirectX(std::string windowTitle, int x, int y, unsigned int windowWidth, unsigned int windowHeight, bool fullscreen);
    virtual ~DirectX();

    static LPDIRECT3D9 direct3D;
    static LPDIRECT3DDEVICE9 device;
    static IDirect3DVertexDeclaration9* vertexDec;
    static ID3DXEffect* currentShaderEffect;

    void CheckShaderVersion();
    bool IsDeviceLost();

protected:
    D3DPRESENT_PARAMETERS direct3DPresPara;

    Window *window;
    unsigned int width;
    unsigned int height;
    D3DXMATRIX projMatrix;

private:
    void Initialize(bool fullscreenMode);
};

Direct.cpp

#include"DirectX.h"

LPDIRECT3D9 DirectX::direct3D = NULL;
LPDIRECT3DDEVICE9 DirectX::device = NULL;
IDirect3DVertexDeclaration9* DirectX::vertexDec = NULL;
ID3DXEffect* DirectX::currentShaderEffect = NULL;

DirectX::DirectX(std::string windowTitle, int x, int y, unsigned int windowWidth, unsigned int windowHeight, bool fullscreen)
{
    width = windowWidth;
    height = windowHeight;

    window = new Window(windowTitle.c_str(), windowWidth, windowHeight, x, y, fullscreen);

    Initialize(fullscreen);

    D3DXMatrixPerspectiveFovLH( &projMatrix,
                                D3DXToRadian(45),
                                (float)width/(float)height,
                                1.0f,
                                15000.0f);

    //device->SetTransform(D3DTS_PROJECTION, &projMatrix);
}

DirectX::~DirectX()
{
    direct3D->Release();
    device->Release();
    vertexDec->Release();

    delete vertexDec;
    delete currentShaderEffect;
    delete window;
}

void DirectX::Initialize(bool fullscreenMode)
{
    direct3D = Direct3DCreate9(D3D_SDK_VERSION);

    ZeroMemory(&direct3DPresPara, sizeof(direct3DPresPara));

    switch(fullscreenMode)
    {
        case true:
            direct3DPresPara.Windowed = false;
        break;
        case false:
            direct3DPresPara.Windowed = true;
    }

    direct3DPresPara.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //turns off VSync, comment this line of code to turn VSync back on
    direct3DPresPara.SwapEffect = D3DSWAPEFFECT_DISCARD;
    direct3DPresPara.hDeviceWindow = window->GetHandle();
    direct3DPresPara.BackBufferFormat = D3DFMT_X8R8G8B8;
    direct3DPresPara.BackBufferWidth = width;
    direct3DPresPara.BackBufferHeight = height;
    direct3DPresPara.EnableAutoDepthStencil = TRUE;
    direct3DPresPara.AutoDepthStencilFormat = D3DFMT_D16;

    direct3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window->GetHandle(), D3DCREATE_SOFTWARE_VERTEXPROCESSING, &direct3DPresPara, &device);

    D3DVERTEXELEMENT9 vertexElement[] = {   {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
                                            {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
                                            D3DDECL_END()};

    device->CreateVertexDeclaration(vertexElement, &vertexDec);

    //currentShaderEffect = 0;
}

void DirectX::CheckShaderVersion()
{
    D3DCAPS9 caps;

    device->GetDeviceCaps(&caps);

    bool stop = false;
    int i = 6;
    while(!stop)
    {
        if(caps.VertexShaderVersion < D3DVS_VERSION(i, 0))
        {
            i--;
        }
        else
        {
            std::cout << "you are using shader model " << i << std::endl;
            stop = true;
        }
    }

    //std::cout << caps.VertexShaderVersion << std::endl;
    //std::cout << D3DVS_VERSION(3, 0) << std::endl;
}

bool DirectX::IsDeviceLost()
{
    HRESULT result = device->TestCooperativeLevel();

    if(result == D3DERR_DEVICELOST)
    {
        Sleep(20); //Sleep for a little bit then try again.
        return true;
    }
    else if(result == D3DERR_DRIVERINTERNALERROR)
    {
        MessageBox(0, "Internal Driver Error. The program will now exit.", 0, 0);
        PostQuitMessage(0);
        return true;
    }
    else if(result == D3DERR_DEVICENOTRESET)
    {
        device->Reset(&direct3DPresPara);
        return false;
    }
    else
    {
        return false;
    }
}

Any help or advice will be greatly appreciated. Thanks

Danny
  • 9,199
  • 16
  • 53
  • 75
  • `D3DPOOL_MANAGED` actually doesn't require all that much work around this issue. The simplest way is to just create the things that will be invalidated on the fly so they're already destroyed and recreated when this happens. – chris Nov 14 '13 at 14:35

1 Answers1

3

You don't need to release resources in D3DPOOL_MANAGED. You need to release resources in D3DPOOL_DEFAULT.

If you're using D3DX objects (ID3DXFont, ID3DXMesh, ID3DXSprite), they typically have "OnLostDevice" and "OnResetDevice" methods. You should call those methods in appropriate situations.

If an object does not have any methods for dealing with "DeviceLost" state, and they should be released, simply destroy the object before resetting the device, and reload it afterwards.

Please note that D3DPOOL_MANAGED objects are reloaded automatically by the driver and don't need any "help" from you to deal with reset/devicelost. YOu do need to take care about all other objects.

In addition to what I said/wrote, you should obviously read DirectX SDK documentation. They cover lost devices in their documentation, and DirectX examples normally survive device reset just fine.

SigTerm
  • 26,089
  • 6
  • 66
  • 115
  • Thanks for the info. I don't use any D3DX Objects. In fact, at the moment, I have wrote my own OBJMesh and Heightmap class. Does this mean I'll have to delete any OBJMesh or models from the program and re-initialize it's data such as it's vertex buffer, textures etc? – Danny Nov 14 '13 at 14:57
  • @Danny: If I remember correctly, both meshes and textures can be placed into D3DPOOL_MANAGED, unless they have D3DUSAGE_DYNAMIC flag (in which case they can be only placed into POOL_DEFAULT). SO if you place meshes/textures into managed pool, you don't need to do anything. If I remember correctly, Shaders cannot be placed into managed pool and should be released and recreated. Also if you're unsure what prevents you from resetting the device, you could install debug DirectX and see debug output from within MSVC> – SigTerm Nov 14 '13 at 15:07
  • Shaders and also vertex declarations don't need to be released on device lost. – cdoubleplusgood Nov 15 '13 at 08:36