Before i get to the question i need to clarify some things. I'm a novice so don't be too harsh on me if i make some stupid mistake :) Also English is not my native language :)
I spent the last two days trying to understand the extreme input lag(or at least i thought so at first) i get when using vsync (D3DPRESENT_INTERVAL_DEFAULT or D3DPRESENT_INTERVAL_ONE) or when having low fps(in the case below 240fps) in my simple 3d application. I read a lot of articles and posts in different forums and reached a conclusion that it's kind of normal because the specifics or vsync and buffering. Still i'm not sure that that much lag is normal. To be more precise it's not exactly input lag - more like something to do with presentation lag. I'm reading the book of Frank Luna about DirectX 9.0, so i modified the source of chapter one to show you what i mean. It's pretty basic stuff, but is perfect for demonstration. The idea is simple - you move the mouse around the window and a mouse texture is drawn at the current position of the mouse every frame... The two cursors should overlap perfectly... Or at least i want it to be that way, but it isn't. The DirectX drawn mouse always lags behind the Windows cursor and it's quite annoying for me. One more important thing - i'm using Windows 7 and disabling Aero almost fixes the problem. I don't have a Windows XP PC to test there. I was planning on doing a simple DirectX GUI Library to use in my future education projects, but when i came across this problem i just wonder what will dragging look like... I remember seeing some commercial games behave the same way with vsync enabled - can't recall any names at the moment. Still, the majority of them don't behave that way. I just don't believe that you have to render at least 240 frames for smooth feeling even if you don't have vsync enabled.
To be honest i tried some solutions, but none of them gave me satisfactory results except disabling Aero - but i don't think that's the solution(I'm still interested why is that working at all). I also tried decreasing render ahead and even disabling it completely - still no improvement. I'm posting the source as i don't see an option for attaching files.
FILENAME: d3dUtility.h//////////////////////////////////////////////////////////////////////////////////////////////////// // File: d3dUtility.h// // Author: Frank Luna (C) All Rights Reserved//// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 //// Desc: Provides utility functions for simplifying common tasks.// //////////////////////////////////////////////////////////////////////////////////////////////////#ifndef __d3dUtilityH__#define __d3dUtilityH__#include <d3dx9.h>#include <string>namespace d3d{ bool InitD3D( HINSTANCE hInstance, // [in] Application instance. int width, int height, // [in] Backbuffer dimensions. bool windowed, // [in] Windowed (true)or full screen (false). D3DDEVTYPE deviceType, // [in] HAL or REF IDirect3DDevice9** device);// [out]The created device. int EnterMsgLoop( bool (*ptr_display)(float timeDelta)); LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); static HWND handle; HWND GetHandle(); template<class T> void Release(T t) { if( t ) { t->Release(); t = 0; } } template<class T> void Delete(T t) { if( t ) { delete t; t = 0; } }}#endif // __d3dUtilityH__
FILENAME: d3dUtility.cpp//////////////////////////////////////////////////////////////////////////////////////////////////// // File: d3dUtility.cpp// // Author: Frank Luna (C) All Rights Reserved//// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 //// Desc: Provides utility functions for simplifying common tasks.// //////////////////////////////////////////////////////////////////////////////////////////////////#include "d3dUtility.h"bool d3d::InitD3D( HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9** device){ // // Create the main application window. // WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)d3d::WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = "Direct3D9App"; if( !RegisterClass(&wc) ) { ::MessageBox(0, "RegisterClass() - FAILED", 0, 0); return false; } HWND hwnd = 0; DWORD styles = WS_CAPTION | WS_SYSMENU | WS_EX_TRANSPARENT; RECT screenRect = {300, 300, 300 + width, 300 + height}; AdjustWindowRect(&screenRect, styles, false); hwnd = ::CreateWindow("Direct3D9App", "Direct3D9App", WS_CAPTION | WS_SYSMENU | WS_EX_TRANSPARENT, screenRect.left, screenRect.top, screenRect.right - screenRect.left, screenRect.bottom - screenRect.top, 0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/); if( !hwnd ) { ::MessageBox(0, "CreateWindow() - FAILED", 0, 0); return false; } handle = hwnd; ::ShowWindow(hwnd, SW_SHOW); ::UpdateWindow(hwnd); // // Init D3D: // HRESULT hr = 0; // Step 1: Create the IDirect3D9 object. IDirect3D9* d3d9 = 0; d3d9 = Direct3DCreate9(D3D_SDK_VERSION); if( !d3d9 ) { ::MessageBox(0, "Direct3DCreate9() - FAILED", 0, 0); return false; } // Step 2: Check for hardware vp. D3DCAPS9 caps; d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps); int vp = 0; if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; else vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; // Step 3: Fill out the D3DPRESENT_PARAMETERS structure. D3DPRESENT_PARAMETERS d3dpp; d3dpp.BackBufferWidth = width; d3dpp.BackBufferHeight = height; d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; d3dpp.BackBufferCount = 1; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.MultiSampleQuality = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = hwnd; d3dpp.Windowed = windowed; d3dpp.EnableAutoDepthStencil = true; d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; d3dpp.Flags = 0; d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // Step 4: Create the device. hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, // primary adapter deviceType, // device type hwnd, // window associated with device vp, // vertex processing &d3dpp, // present parameters device); // return created device if( FAILED(hr) ) { // try again using a 16-bit depth buffer d3dpp.AutoDepthStencilFormat = D3DFMT_D16; hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, deviceType, hwnd, vp, &d3dpp, device); if( FAILED(hr) ) { d3d9->Release(); // done with d3d9 object ::MessageBox(0, "CreateDevice() - FAILED", 0, 0); return false; } } d3d9->Release(); // done with d3d9 object return true;}HWND d3d::GetHandle(){ return handle;}int d3d::EnterMsgLoop( bool (*ptr_display)(float timeDelta) ){ MSG msg; ::ZeroMemory(&msg, sizeof(MSG)); static float lastTime = (float)timeGetTime(); while(msg.message != WM_QUIT) { if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } else { float currTime = (float)timeGetTime(); float timeDelta = (currTime - lastTime); ptr_display(timeDelta); lastTime = currTime; } } return msg.wParam;}
FILENAME: d3dinit.cpp//////////////////////////////////////////////////////////////////////////////////////////////////// // File: d3dinit.cpp// // Author: Frank Luna (C) All Rights Reserved//// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 //// Desc: Demonstrates how to initialize Direct3D, how to use the book's framework// functions, and how to clear the screen to black. Note that the Direct3D// initialization code is in the d3dUtility.h/.cpp files.// //////////////////////////////////////////////////////////////////////////////////////////////////#include "d3dUtility.h"//// Globals//IDirect3DDevice9* Device = 0;LPDIRECT3DTEXTURE9 texture = 0;LPD3DXSPRITE sprite = 0; //// Framework Functions//bool Setup(){ // Nothing to setup in this sample. HRESULT r1 = D3DXCreateTextureFromFile(Device, "C:\\Users\\Armion\\Desktop\\cursor.png", &texture); HRESULT r2 = D3DXCreateSprite(Device, &sprite); return true;}void Cleanup(){ // Nothing to cleanup in this sample. texture->Release(); sprite->Release();}bool Display(float timeDelta){ if( Device ) // Only use Device methods if we have a valid device. { // Instruct the device to set each pixel on the back buffer black - // D3DCLEAR_TARGET: 0x00000000 (black) - and to set each pixel on // the depth buffer to a value of 1.0 - D3DCLEAR_ZBUFFER: 1.0f. static float totalTime = 0; static int frames = 0; totalTime += timeDelta; frames++; HWND handle = d3d::GetHandle(); if (totalTime > 1000) { char strFrames[10]; itoa(frames, strFrames, 10); char strFps[16]; strcpy(strFps, "FPS: "); strcat(strFps, strFrames); frames = 0; totalTime -= 1000; ::SetWindowTextA(handle, strFps); } Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0); Device->BeginScene(); POINT coords; ::GetCursorPos(&coords); ::ScreenToClient(handle, &coords); RECT client; GetClientRect(handle, &client); D3DXVECTOR3 pos(coords.x, coords.y, 0); sprite->Begin(D3DXSPRITE_ALPHABLEND); HRESULT test = sprite->Draw(texture, 0, 0, &pos, 0xFFFFFFFF); sprite->End(); //::SetWindowText(hwnd, Device->EndScene(); // Swap the back and front buffers. Device->Present(0, 0, 0, 0); } return true;}//// WndProc//LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){ switch( msg ) { case WM_DESTROY: ::PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); break; } return ::DefWindowProc(hwnd, msg, wParam, lParam);}//// WinMain//int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd){ if(!d3d::InitD3D(hinstance, 640, 480, true, D3DDEVTYPE_HAL, &Device)) { ::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; } if(!Setup()) { ::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; } d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); return 0;}
Thanks in advance :)