Sign in to follow this  
Sirus20x6

Large delay in wm_input

Recommended Posts

Sirus20x6    122
I've written a small program in directx that just displays a model I've made in blender, and shows the x and y pos of the mouse that I get from wm_input. It takes the x coord and feeds it into a rotation of the model. The problem I'm having is that there is a huge delay in between the mouse movement and what happens on the screen. The model seems to move very, very smoothly, but if I move the mouse back and forth several times and then stop it takes the model several seconds stop wiggling as well as for the x and y coords to settle. I capture the mouse with this case WM_INPUT: { UINT dwSize = 40; static BYTE lpb[40]; GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)); RAWINPUT* raw = (RAWINPUT*)lpb; if (raw->header.dwType == RIM_TYPEMOUSE) { xPosRelative = raw->data.mouse.lLastX; yPosRelative = raw->data.mouse.lLastY; } break; } i process the input with just a few lines of code to give me a more static number absx += xPosRelative; absy += yPosRelative; if (absx >= 640 || absx <= -640){absx = 0;} if (absy >= 480 || absy <= -480){absy = 0;} xPosRelative = 0; yPosRelative = 0; I render some text with this std::wstring stemp = s2ws(itos(absx)); LPCWSTR result = stemp.c_str(); SetRect( &rc, 10, 450, 0, 0 ); g_pFont->DrawText( NULL, result , -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f )); SetRect( &rc, 50, 450, 0, 0 ); g_pFont->DrawText( NULL, L"Xpos", -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f )); SetRect( &rc, 110, 450, 0, 0 ); stemp = s2ws(itos(absy)); result = stemp.c_str(); g_pFont->DrawText( NULL, result , -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f )); SetRect( &rc, 150, 450, 0, 0 ); g_pFont->DrawText( NULL, L"Ypos", -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f )); and finally I do the rotation of the model with this line D3DXMatrixRotationY(&matRotateY, (absx/100.0f)); any clues? do I need buffered input or a seperate thread for input?

Share this post


Link to post
Share on other sites
Molle85    172
you have to handle all input before rendering...


MSG msg;
while( 1 )
{
if( PeekMessage( ... ) )
{
//handle input
...
}
else
{
//render, app update
...
}
}

Share this post


Link to post
Share on other sites
Drigovas    509
Yes, but you still have your input bound to your render code. When you push a key, you handle the input, and then you render. Given this example, lets say you rapidly mash a whole bunch of keys really fast. Each input message will result in a render, and thus there will be a delay between each key getting handled that is at least the time it takes you to render something. As an alternative, if you soaked up all the keys pressed, then rendered, you wouldn't have this bound.

It's unlikely that you can even press keys that fast, but seeing this sort of structure in your input code is a pretty decent hint that you have these same sorts of problems elsewhere, which would definitely show up as a problem, or rather a slowdown.

Share this post


Link to post
Share on other sites
Sirus20x6    122
I'm kind of at a loss. I'm looking over a few tutorials on wm_input as well as msdn, but none of them seem quite complete. anyone know of any really good resources?

Share this post


Link to post
Share on other sites
Drigovas    509
Here is a way to look at it. When you check for input, you read in ALL of the input, keep reading in input until there is no input left to read, and logically make the changes that this input will result in. For example, if each key on the keyboard corresponds to a unit moving, then move the unit when you read a key is pressed.

Don't render any of the units. Don't render anything at all. Just update your game data.

When you're totally done reading in the input, then render everything in one bang. Repeat this cycle as needed. If you go to read input, and there is no input, just go straight to the render step.

Share this post


Link to post
Share on other sites
Sirus20x6    122
well I'm pretty new to both Directx and windows gui programming so I'm pretty much hacking together tutorials and snippits from msdn. The part that I seem to be having trhoubles with now is that peekmesage needs MSG which is in:

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){
...

if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;

TranslateMessage(&msg);
DispatchMessage(&msg);
}
...
}

but my wm_input code relies on LPARAM which is in this sub:
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){

UINT dwSize = 40;
static BYTE lpb[40];

GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
lpb, &dwSize, sizeof(RAWINPUTHEADER));

RAWINPUT* raw = (RAWINPUT*)lpb;

if (raw->header.dwType == RIM_TYPEMOUSE)
{
xPosRelative = raw->data.mouse.lLastX;
yPosRelative = raw->data.mouse.lLastY;
}
}



How would I go about getting things to mesh nicely?

Share this post


Link to post
Share on other sites
MJP    19791
You should be responding to WM_INPUT and other messages in your window procedure. This is the exact purpose of that function: to handle messages. The code where you pump the message queue (AKA call PeekMessage or GetMessage) should be in a different function, where it's in a loop that runs until your program is finished.

Share this post


Link to post
Share on other sites
Sirus20x6    122
I'm still lost. Here is what I have.

#ifndef WINVER
#define WINVER 0x0501
#endif

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif

#ifndef _WIN32_WINDOWS
#define _WIN32_WINDOWS 0x0410
#endif

#ifndef _WIN32_IE
#define _WIN32_IE 0x0600
#endif

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <commdlg.h>
#include <sstream>

#ifndef HID_USAGE_PAGE_GENERIC
#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
#endif
#ifndef HID_USAGE_GENERIC_MOUSE
#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
#endif

#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1

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

RAWINPUTDEVICE Rid[1];
LPDIRECT3D9 d3d; // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev; // the pointer to the device class
LPDIRECT3DVERTEXBUFFER9 t_buffer = NULL; // the pointer to the vertex buffer
LPDIRECT3DSURFACE9 z_buffer = NULL; // the pointer to the z-buffer
LPD3DXMESH meshSpaceship; // define the mesh pointer
D3DMATERIAL9* material; // define the material object
DWORD numMaterials; // stores the number of materials in the mesh
LPD3DXFONT pFontNew = NULL;
ID3DXFont* g_pFont = pFontNew;
RECT rc;
TCHAR g_strFont[LF_FACESIZE];
int absx = 0;
int absy = 0;
int xPosRelative;
int yPosRelative;
UINT dwSize = 40;
static BYTE lpb[40];

void initD3D(HWND hWnd);
void render_frame(void);
void cleanD3D(void);
void init_graphics(void);
void init_light(void);
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

std::string itos(int arg)
{
std::ostringstream buffer;
buffer << arg;
return buffer.str();
}

std::wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}

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 = (WNDPROC)WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"WindowClass1";

RegisterClassEx(&wc);

hWnd = CreateWindowEx(NULL,
L"WindowClass1",
L"3D Test",
WS_OVERLAPPEDWINDOW,
0, 0,
SCREEN_WIDTH, SCREEN_HEIGHT,
NULL,
NULL,
hInstance,
NULL);

ShowWindow(hWnd, nCmdShow);

Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
Rid[0].dwFlags = RIDEV_INPUTSINK;
Rid[0].hwndTarget = hWnd;
RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]));
initD3D(hWnd);

MSG msg;

while(TRUE)
{
DWORD starting_point = GetTickCount();

if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
// while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)){
absx += xPosRelative;
absy += yPosRelative;
if (absx >= 640 || absx <= -640){absx = 0;}
if (absy >= 480 || absy <= -480){absy = 0;}
xPosRelative = 0;
yPosRelative = 0;
// }
TranslateMessage(&msg);
DispatchMessage(&msg);
}

render_frame();

if(KEY_DOWN(VK_ESCAPE))
PostMessage(hWnd, WM_DESTROY, 0, 0);

while ((GetTickCount() - starting_point) < 25);
}
cleanD3D();

return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
case WM_INPUT:
{
GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
lpb, &dwSize, sizeof(RAWINPUTHEADER));
RAWINPUT* raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE)
{
xPosRelative = raw->data.mouse.lLastX;
yPosRelative = raw->data.mouse.lLastY;
}
break;
}
}

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


void render_frame(void)
{
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

d3ddev->BeginScene();

D3DXMATRIX matView;
D3DXMatrixLookAtLH(&matView,
&D3DXVECTOR3 (0.0f, 4.0f, 8.0f), // the camera position
&D3DXVECTOR3 (0.0f, 0.0f, 0.0f), // the look-at position
&D3DXVECTOR3 (0.0f, 1.0f, 0.0f)); // the up direction
d3ddev->SetTransform(D3DTS_VIEW, &matView); // set the view transform to matView

D3DXMATRIX matProjection;
D3DXMatrixPerspectiveFovLH(&matProjection,
D3DXToRadian(45),
SCREEN_WIDTH / SCREEN_HEIGHT,
1.0f,
100.0f);
d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);

//static float index = 0.0f; index+=0.13;
D3DXMATRIX matRotateY;
// D3DXMatrixRotationY(&matRotateY, index);
D3DXMatrixRotationY(&matRotateY, (absx/100.0f));
d3ddev->SetTransform(D3DTS_WORLD, &(matRotateY));

for(DWORD i = 0; i < numMaterials; i++)
{
d3ddev->SetMaterial(&material[i]);
meshSpaceship->DrawSubset(i);
}

std::wstring stemp = s2ws(itos(absx));
LPCWSTR result = stemp.c_str();
SetRect( &rc, 10, 450, 0, 0 );
g_pFont->DrawText( NULL, result , -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f ));
SetRect( &rc, 50, 450, 0, 0 );
g_pFont->DrawText( NULL, L"Xpos", -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f ));
SetRect( &rc, 110, 450, 0, 0 );
stemp = s2ws(itos(absy));
result = stemp.c_str();
g_pFont->DrawText( NULL, result , -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f ));
SetRect( &rc, 150, 450, 0, 0 );
g_pFont->DrawText( NULL, L"Ypos", -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f ));
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);

return;
}

...
...
...

Share this post


Link to post
Share on other sites
MJP    19791
Okay, let me break this down for you. This code right here:


while(TRUE)
{
DWORD starting_point = GetTickCount();

if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;

// while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)){
absx += xPosRelative;
absy += yPosRelative;
if (absx >= 640 || absx <= -640){absx = 0;}
if (absy >= 480 || absy <= -480){absy = 0;}
xPosRelative = 0;
yPosRelative = 0;
// }

TranslateMessage(&msg);
DispatchMessage(&msg);
}

render_frame();

if(KEY_DOWN(VK_ESCAPE))
PostMessage(hWnd, WM_DESTROY, 0, 0);

while ((GetTickCount() - starting_point) < 25);
}




This code has a few problems. In order:

1. You're pumping your messages wrong. Like others above have said, you're only pumping one message per frame of rendering. This is very bad, because in games rendering usually takes much much longer than it does to pump all of the messages in the queue. Quite often there will be many messages waiting in the queue every frame, whether they be input messages or notification messages or whatever else your window gets. This problem is made even worse by the fact that you're using a loop with GetTickCount to guarantee that your loop runs at ~30Hz...this means that at most your program can handle 30 messages a second. You need to have this:

MSG msg;
ZeroMemory( &msg, sizeof(msg) );

while(msg.message!=WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
updateGameLogic();
render();
}
}

2. You (generally)shouldn't be responding to messages here. Like I said before, this portion of code is where you should be "pumping" the message loop: you should be pulling it off the queue and giving it to DispatchMessage where it can send it off to your WindowProc (which is where you should actually respond to your message). Now you may be thinking "but if I'm always getting the message with "PeekMessage" in the main loop, why do I have to bother sending it to the window procedure"? This would be valid if PeekMessage/GetMessage were the only way to get a message, but they're not (your window procedure can get called directly in certain cases). The exception to this is the WM_QUIT message, which tells you when you should exit your loop and is never sent to the window procedure.

3. You don't destroy a window by posting a WM_DESTROY message. The message is just a notification telling you that the window is being destroyed, it's not the actual mechanism that causes destruction and triggers cleanup of window resources. Posting the message is like making sound of a gunshot but without actually firing the gun. Use DestroyWindow.

4. Using a timer to regulate a game loop is usually a bad idea, when you're working with 3D graphics hardware. If you want to limit the framerate, you should enable VSYNC so that you limit the framerate and prevent tearing. This will also allow for much better synchronization between the CPU and GPU. You also shouldn't use GetTickCount as a timer, it's notoriously inaccurate and insufficient for use in games. QueryPerformanceCounter or timeGetTime are better options.


Other things not related to that bit of code specifically...

-Don't cast function pointers unless you really know what you're doing (I'm talking about where you register your window class and provide a pointer to your window procedure). I know this is done in a lot of sample code and even in some of the default code Visual Studio generates, but it's very bad practice to do so as it can cover up compiler errors that will save you from very hard-to-find bugs.

-You don't need to call IDirect3DDevice0::Clear twice to clear both the back-buffer and the z-buffer, just OR together the two options like this:

d3ddev->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);


-If you're going to post long bits of code, use the "source" tags. You can read how to use them in the forum FAQ.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this