LeOpArD
LeOpArD

Reputation: 498

Strange thing in D3D9 2D animation -- high FPS while still not smooth

The animation is quite simple: display a picture full-screen and move the picture out of the screen horizontally in 1 second, just like the slide show switching effect in MS PowerPoint.

I'm using the D3D9 surface to realize the animation, because I want the program to be compatible with Windows XP and I might also need some 3D effects of displaying pictures.

When I turn on the VSYNC(d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT), the fps stays at 60 but I can still see the picture moving discontinuously(very obvious). When I turn off the VSYNC, the fps stays around 1600, the picture moves more smoothly(but still lags a little).

The strange part for both cases is that I can see a zig-zag border of the picture and rupture in the picture:

    ##########
    ##########
     #########
     #########
      ########

I have no exprience in either DX or 2D-animation, so I need your help.

The key part of the code is as follows:

D3DPRESENT_PARAMETERS d3dpp = {0};
d3dpp.BackBufferCount       = 3; 
d3dpp.Windowed              = TRUE; 
d3dpp.SwapEffect            = D3DSWAPEFFECT_DISCARD; 
d3dpp.BackBufferFormat      = D3DFMT_UNKNOWN;
d3dpp.PresentationInterval  = D3DPRESENT_INTERVAL_IMMEDIATE; //VSYNC off
//  d3dpp.PresentationInterval  = D3DPRESENT_INTERVAL_DEFAULT; //VSYNC

......
if(FAILED(D3DXLoadSurfaceFromFile(g_pSurface, NULL, NULL, PICPATH, NULL, D3DX_FILTER_NONE, 0, NULL)))
    return E_FAIL;
......
 while(1)
{
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        if (msg.message == WM_QUIT)
            break ;
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
    }
     else DxRender();
}

VOID DxRender() 
{ 

LPDIRECT3DSURFACE9 pBackBuffer = NULL;
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 ); 

static int offs = 0;
static float StartTime = timeGetTime() * 0.001f;
float CurTime = timeGetTime() * 0.001f;
offs = CurTime - StartTime * g_cxClient / ANIMATION_TIME_S;
// ANIMATION_TIME_S = 1.0f
if(offs >= g_cxClient)
{
    StartTime = CurTime;
    offs -= g_cxClient;
}


RECT srcrect1 = {0,0,g_cxClient-1-offs,g_cyClient-1}; 
POINT dstpt1 = {offs,0};

if( SUCCEEDED( g_pd3dDevice->BeginScene() ) ) 
{ 

    if(FAILED(g_pd3dDevice->GetBackBuffer(0,0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer)))
    {
        MessageBox(NULL, TEXT("GetBackBuffer"), TEXT("Error"), MB_OK); 
    }
    g_pd3dDevice->UpdateSurface(g_pSurface, &srcrect1, pBackBuffer, &dstpt1);
    ShowFPS();

    g_pd3dDevice->EndScene(); 
} 

g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); 
    if(pBackBuffer != NULL)
        pBackBuffer->Release();
} 

Upvotes: 0

Views: 1069

Answers (2)

zdd
zdd

Reputation: 8727

I have a solution which use the world matrix to translate the texture, the key steps as below

  • Create texuture from file
  • Display the texute
  • Update the the world matrix
    • if elapsed time less then 2 seconds, display the texture
    • else translate the texture to right until it out of screen, the translate matrix was based on the time, you can just upate the matrix._41 filed.

full code as below, you should replace the texture file with your local file to see the effect, please see the code in function SetupMatrix for details of the translation.

#include <d3dx9.h>

LPDIRECT3D9             g_pD3D              = NULL ; // Used to create the D3DDevice
LPDIRECT3DDEVICE9       g_pd3dDevice        = NULL ; // Our rendering device
LPDIRECT3DVERTEXBUFFER9 g_pVB               = NULL ; // Vertex buffer
LPDIRECT3DTEXTURE9      g_pTexture          = NULL ; // Texture

D3DXMATRIX g_worldMatrix ;

float                   g_ShowTimeInterval  = 2.0f;  // How long will the picture displays.
float                   g_totalShowTime     = 0.0f;  // Time elapsed since the picture start to display.

#define SAFE_RELEASE(P) if(P){ P->Release(); P = NULL;}

struct Vertex
{
    float x, y, z ; // Vertex position
    float u, v ;    // Texture coordinates
};

#define VertexFVF D3DFVF_XYZ | D3DFVF_TEX1

HRESULT InitD3D( HWND hWnd )
{
    DWORD ScreenW = 0;
    DWORD ScreenH = 0;

    DEVMODE devMode ;
    devMode.dmSize = sizeof(devMode) ;
    DWORD iModeNum = 0 ;
    DWORD r = 1 ;

    while(r != 0)
    {
        r = EnumDisplaySettings(NULL, iModeNum, &devMode) ;
        // Store the maximum resolution currently
        if(devMode.dmPelsWidth >= ScreenW && devMode.dmPelsHeight >= ScreenH)
        {
            ScreenW = devMode.dmPelsWidth ;
            ScreenH = devMode.dmPelsHeight ;
        }

        //OutputModeInfo(iModeNum, devMode) ;
        iModeNum++ ;
    }

    // Create the D3D object.
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );

    d3dpp.Windowed               = FALSE;
    d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD; 
    d3dpp.BackBufferWidth        = ScreenW; 
    d3dpp.BackBufferHeight       = ScreenH;
    d3dpp.BackBufferFormat       = D3DFMT_X8R8G8B8; 

    // Create device
    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
        &d3dpp, &g_pd3dDevice ) ) )
    {
        MessageBoxA(NULL, "Create D3D9 device failed!", "Error", 0) ;
        return E_FAIL;
    }

    // Disable lighting, since we didn't specify color for vertex
    g_pd3dDevice->SetRenderState( D3DRS_LIGHTING , FALSE );  

    D3DXMatrixIdentity(&g_worldMatrix);

    // Create Texture
    HRESULT hr ;
    hr = D3DXCreateTextureFromFile(g_pd3dDevice, "../Common/Media/chessboard.jpg", &g_pTexture) ;
    if (FAILED(hr))
    {
        MessageBoxA(NULL, "Create Texture failed!", "Error", 0) ;
    }

    return S_OK;
}

// Prepare vertex buffer
void InitVB()
{
    Vertex Quad[] = 
    {
        {-5.0f,  5.0f, 0,    0,    0},  // 1
        { 5.0f,  5.0f, 0, 1.0f,    0},  // 2
        {-5.0f, -5.0f, 0,    0, 1.0f},  // 4
        { 5.0f, -5.0f, 0, 1.0f, 1.0f},  // 3
    } ;

    // Create vertex buffer
    HRESULT hr ;
    hr = g_pd3dDevice->CreateVertexBuffer(6 * sizeof(Vertex), D3DUSAGE_WRITEONLY, 
        VertexFVF, D3DPOOL_MANAGED, &g_pVB, NULL) ;
    if (FAILED(hr))
    {
        MessageBoxA(NULL, "Create vertex buffer failed!", "Error", 0) ;
    }

    // Copy data
    Vertex* v ;
    g_pVB->Lock(0, 0, (void**)&v, 0) ;
    memcpy(v, Quad, 6 * sizeof(Vertex)) ;
    g_pVB->Unlock() ;
}

VOID Cleanup()
{
    SAFE_RELEASE(g_pTexture) ;

    SAFE_RELEASE(g_pVB) ;

    SAFE_RELEASE(g_pd3dDevice) ;

    SAFE_RELEASE(g_pD3D) ;
}

void SetupMatrix(float timeDelta)
{
    g_totalShowTime += timeDelta;
    if(g_totalShowTime > g_ShowTimeInterval)
    {
        g_worldMatrix._41 += timeDelta * 10;
        g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix) ;
    }
    else
    {
        D3DXMatrixTranslation(&g_worldMatrix, 0.0f, 0.0f, 0.0f) ;
        g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix) ;
    }

    // set view
    D3DXVECTOR3 eyePt(0.0f, 0.0f, -15.0f) ;
    D3DXVECTOR3 upVec(0.0f, 1.0f, 0.0f) ;
    D3DXVECTOR3 lookCenter(0.0f, 0.0f, 0.0f) ;

    D3DXMATRIX view ;
    D3DXMatrixLookAtLH(&view, &eyePt, &lookCenter, &upVec) ;
    g_pd3dDevice->SetTransform(D3DTS_VIEW, &view) ;

    // set projection
    D3DXMATRIX proj ;
    D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI / 4, 1.0f, 1.0f, 1000.0f) ;
    g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &proj) ;
}

void SetupTexture()
{
    // Create Texture
    HRESULT hr ;
    hr = D3DXCreateTextureFromFile(g_pd3dDevice, "../Common/Media/crate.jpg", &g_pTexture) ;
    if (FAILED(hr))
    {
        MessageBoxA(NULL, "Create Texture failed!", "Error", 0) ;
    }

    // Setup texture
    g_pd3dDevice->SetTexture(0, g_pTexture) ;
    g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
    g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
    g_pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU,  D3DTADDRESS_WRAP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV,  D3DTADDRESS_WRAP );
}

void RenderQuad()
{
    SetupTexture();

    // Set stream source
    g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(Vertex) );
    g_pd3dDevice->SetFVF(VertexFVF) ;
    g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2) ;

}

VOID Render(float timeDelta)
{
    SetupMatrix(timeDelta) ;

    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0, 1.0f, 0 );

    // Begin the scene
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
        RenderQuad() ;

        // End the scene
        g_pd3dDevice->EndScene();
    }

    // Present the back-buffer contents to the display
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
    case WM_KEYDOWN:
        {
            switch( wParam )
            {
            case VK_ESCAPE:
                SendMessage( hWnd, WM_CLOSE, 0, 0 );
                break ;
            default:
                break ;
            }
        }
        break ;

    case WM_DESTROY:
        Cleanup();
        PostQuitMessage( 0 );
        return 0;
    }

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

INT WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR szCmdLine, int iCmdShow)
{
    WNDCLASSEX winClass ;

    winClass.lpszClassName = "ScreenQuad";
    winClass.cbSize        = sizeof(WNDCLASSEX);
    winClass.style         = CS_HREDRAW | CS_VREDRAW;
    winClass.lpfnWndProc   = MsgProc;
    winClass.hInstance     = hInstance;
    winClass.hIcon         = NULL ;
    winClass.hIconSm       = NULL ;
    winClass.hCursor       = LoadCursor(NULL, IDC_ARROW) ; // to avoid busy cursor
    winClass.hbrBackground = NULL ;
    winClass.lpszMenuName  = NULL ;
    winClass.cbClsExtra    = 0;
    winClass.cbWndExtra    = 0;

    RegisterClassEx (&winClass) ;  

    HWND hWnd = CreateWindowEx(NULL,  
        winClass.lpszClassName,     // window class name
        "ScreenQuad",               // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        32,                         // initial x position
        32,                         // initial y position
        600,                        // initial window width
        600,                        // initial window height
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL) ;                     // creation parameters

    // Create window failed
    if(hWnd == NULL)
    {
        MessageBoxA(hWnd, "Create Window failed!", "Error", 0) ;
        return -1 ;
    }

    // Initialize Direct3D
    if( SUCCEEDED(InitD3D(hWnd)))
    { 
        InitVB() ;

        // Show the window
        ShowWindow( hWnd, SW_SHOWDEFAULT );
        UpdateWindow( hWnd );

        // Enter the message loop
        MSG    msg ; 
        ZeroMemory( &msg, sizeof(msg) );
        PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );

        static DWORD lastTime = timeGetTime();

        while (msg.message != WM_QUIT)  
        {
            if( PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) != 0)
            {
                TranslateMessage (&msg) ;
                DispatchMessage (&msg) ;
            }
            else // Render the game if there is no message to process
            {
                DWORD currentTime = timeGetTime();
                float timeDelta = (currentTime - lastTime) * 0.001f;

                Render(timeDelta) ;

                lastTime = currentTime;
            }
        }
    }

    UnregisterClass(winClass.lpszClassName, hInstance) ;
    return 0;
}

Upvotes: 0

Gnietschow
Gnietschow

Reputation: 3180

That your animation isn't smooth can be caused by an insufficient resolution of timeGetTime(). This can cause these problems (see MSDN of timeGetTime()), because at high FPS a frame can be shorter than a millisecond. You can try QueryPerformanceCounter() to get a timer with a higher frequency (MSDN to QueryPerformanceCounter()). Then it shouldn't lag. I can't imagine, why there are zig-zags at 60 FPS, but with D3DPRESENT_INTERVAL_IMMEDIATE ruptures are normal, because the screen cannot keep up refreshing.

Upvotes: 1

Related Questions