• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Evil Steve

Size/move loop and delay in DefWindowProc

21 posts in this topic

I'm trying to start a timer when the user starts to drag the window, so I can render a frame on the WM_TIMER message, to prevent the stall that happens when the user drags the window. I added some logging and found that DefWindowProc() for WM_SYSCOMMAND (With a wParam of 0xf012, which doesn't seem to be a recognised SC_* value) and WM_NCLBUTTONDOWN takes a considerable time to return (Over 500ms). With some more logging, it seems that WM_NCLBUTTONDOWN comes first, and calling DefWindowProc causes a WM_SYSCOMMAND message to be sent (So it's recursing into the window proc, hence the delay on both messages). The call stack at the time if I break with F12 isn't very exciting:
ntdll.dll!_KiFastSystemCallRet@0() 	
user32.dll!_NtUserMessageCall@28()  + 0xc bytes	
user32.dll!_RealDefWindowProcWorker@20()  - 0x79d6 bytes	
user32.dll!_RealDefWindowProcW@16()  + 0x27 bytes	
uxtheme.dll!DoMsgDefault()  + 0x29 bytes	
uxtheme.dll!OnDwpSysCommand()  + 0x29 bytes	
uxtheme.dll!_ThemeDefWindowProc()  + 0x61a8 bytes	
uxtheme.dll!_ThemeDefWindowProcW@16()  + 0x18 bytes	
user32.dll!_DefWindowProcW@16()  + 0x815 bytes	
>	Tutorial02.exe!D3DWindow::WndProc(unsigned int uMsg=274, unsigned int wParam=61458, long lParam=5636612)  Line 227 + 0x1b bytes	C++
Tutorial02.exe!D3DWindow::StaticWndProc(HWND__ * hWnd=0x000a06fa, unsigned int uMsg=274, unsigned int wParam=61458, long lParam=5636612)  Line 169 + 0x1a bytes	C++
user32.dll!_InternalCallWinProc@20()  + 0x23 bytes	
user32.dll!_UserCallWinProcCheckWow@32()  - 0xddcf bytes	
user32.dll!_DispatchClientMessage@20()  + 0x4b bytes	
user32.dll!___fnDWORD@4()  + 0x24 bytes	
ntdll.dll!_KiUserCallbackDispatcher@12()  + 0x2e bytes	
user32.dll!_NtUserMessageCall@28()  + 0xc bytes	
user32.dll!_RealDefWindowProcWorker@20()  - 0x79d6 bytes	
user32.dll!_RealDefWindowProcW@16()  + 0x27 bytes	
uxtheme.dll!DoMsgDefault()  + 0x29 bytes	
uxtheme.dll!OnDwpNcLButtonDown()  + 0x32 bytes	
uxtheme.dll!_ThemeDefWindowProc()  + 0x61a8 bytes	
uxtheme.dll!_ThemeDefWindowProcW@16()  + 0x18 bytes	
user32.dll!_DefWindowProcW@16()  + 0x815 bytes	
Tutorial02.exe!D3DWindow::WndProc(unsigned int uMsg=161, unsigned int wParam=2, long lParam=5636612)  Line 227 + 0x1b bytes	C++
Tutorial02.exe!D3DWindow::StaticWndProc(HWND__ * hWnd=0x000a06fa, unsigned int uMsg=161, unsigned int wParam=2, long lParam=5636612)  Line 169 + 0x1a bytes	C++
user32.dll!_InternalCallWinProc@20()  + 0x23 bytes	
user32.dll!_UserCallWinProcCheckWow@32()  + 0xb3 bytes	
user32.dll!_DispatchMessageWorker@8()  + 0xe6 bytes	
user32.dll!_DispatchMessageW@4()  + 0xf bytes	
Tutorial02.exe!WinMain(HINSTANCE__ * hInstance=0x009f0000, HINSTANCE__ * __formal=0x00000000, HINSTANCE__ * __formal=0x00000000, HINSTANCE__ * __formal=0x00000000)  Line 22 + 0xf bytes	C++
Tutorial02.exe!__tmainCRTStartup()  Line 578 + 0x35 bytes	C
Tutorial02.exe!WinMainCRTStartup()  Line 403	C
kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes	
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes	
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes	

Obviously returning 0 from WM_NCLBUTTONDOWN doesn't cause WM_SYSCOMMAND to be sent, and no delay, but it also prevents the window from being moved. So, does anyone know what's going on here and if there's any way of stopping it? Cheers, Steve
0

Share this post


Link to post
Share on other sites
How complex are your renders and what kind of delay are you putting on your timer? If you have a relatively simple render and a relatively low update frequency, then you might want to just start the timer up when the window is created and let it run.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
How complex are your renders and what kind of delay are you putting on your timer? If you have a relatively simple render and a relatively low update frequency, then you might want to just start the timer up when the window is created and let it run.
Currently the render is very basic (A single triangle), and the timer is set to 0ms, so just "as fast as possible".

However, this is for a tutorial / article on D3D rendering, and I'd like to do things as "correctly" as possible, even if it means leaving the 500ms lag.

Also, this is D3D (In case that's not obvious from the call stack function names [smile]), so running rendering in another thread is out of the question.

EDIT: And I'm curious about what exactly is going on under the hood here.

[Edited by - Evil Steve on January 14, 2009 3:33:36 PM]
0

Share this post


Link to post
Share on other sites
What kind of stall are you referring to? To my knowledge the window will NEVER update while it's being dragged. Your program will keep executing though. Have you considered throwing your timer code into the main loop instead of relying on your window procedure? I recommend QueryPerformanceCounter() if you do. 64-bit calculations... Nummy. Also, what is your definition of "correctly?"
0

Share this post


Link to post
Share on other sites
There are some other things that cause about a 500ms stall, such as grabbing a window title bar. In fact, now that I think about it, grabbing a window title bar causes WM_SYSCOMMAND with a wparam value of 0xf012 which is in fact (SC_MOVE + HTCAPTION).

EDIT: You may find Spy++ will give you useful information about what messages are occuring. To me it is worlds easier than a call stack for windows message monitoring.
0

Share this post


Link to post
Share on other sites
This is a guess, but I'm thinking DefWindowProc is waiting to see whether you're about to double-click or not. Try setting your double-click delay really slow or really fast to see if that affects things.

If that's the case, then there's probably not a lot you can do about it (but maybe make a note in your tutorial or something [smile]).
0

Share this post


Link to post
Share on other sites
Thanks for the replies.

Quote:
Original post by kittycat768
What kind of stall are you referring to? To my knowledge the window will NEVER update while it's being dragged. Your program will keep executing though. Have you considered throwing your timer code into the main loop instead of relying on your window procedure? I recommend QueryPerformanceCounter() if you do. 64-bit calculations... Nummy. Also, what is your definition of "correctly?"
For the stall, DefWindowProc doesn't return for over 500ms. It's as if it's an extremely expensive function call. Putting a timer in the main loop wouldn't help, and for now GetTickCount() is accurate enough (You don't need to render at full speed when the user is moving the window).
My definition of "correctly" is to render in the main loop, not from a timer handler - the timer handler isn't called all that frequently as far as I'm aware.

Quote:
Original post by lordikon
There are some other things that cause about a 500ms stall, such as grabbing a window title bar. In fact, now that I think about it, grabbing a window title bar causes WM_SYSCOMMAND with a wparam value of 0xf012 which is in fact (SC_MOVE + HTCAPTION).

EDIT: You may find Spy++ will give you useful information about what messages are occuring. To me it is worlds easier than a call stack for windows message monitoring.
Ah, I didn't notice that the wParam can include HTCAPTION, thanks. It's the WM_SYSCOMMAND handling in DefWindowProc that causes the 500ms stall - that's what I'm trying to fix or work around.

Quote:
Original post by Codeka
This is a guess, but I'm thinking DefWindowProc is waiting to see whether you're about to double-click or not. Try setting your double-click delay really slow or really fast to see if that affects things.

If that's the case, then there's probably not a lot you can do about it (but maybe make a note in your tutorial or something [smile]).
I don't think that's it, I always have my double click delay set to as fast as possible, and that should be less than 500ms. I'll give that a try when I get a chance though.

Cheers,
Steve
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
My definition of "correctly" is to render in the main loop, not from a timer handler - the timer handler isn't called all that frequently as far as I'm aware.

You could try starting a timer at window creation and calling InvalidateRect() in the timer handler, which will cause WM_PAINT messages to be generated even when your window is being dragged.
0

Share this post


Link to post
Share on other sites
Could this be because you've got a Direct3d device open inside the window?

Does that stall also happen without D3D? It might be because you're actually losing the device.

If it is because of D3D, could it be the debug runtimes?
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Codeka
This is a guess, but I'm thinking DefWindowProc is waiting to see whether you're about to double-click or not. Try setting your double-click delay really slow or really fast to see if that affects things.
Setting my double click speed to extremely slow still takes the same time for DefWindowProc to return, so that doesn't seem to be related.

Quote:
Original post by SiCrane
You could try starting a timer at window creation and calling InvalidateRect() in the timer handler, which will cause WM_PAINT messages to be generated even when your window is being dragged.
Hmm, it's possible - but still seems a bit ugly to me. This is beginning to look like the only option though...

Quote:
Original post by Endurion
Could this be because you've got a Direct3d device open inside the window?
Disabling D3D completely (Commenting out any reference to any D3D interface) still yields the same problem.
0

Share this post


Link to post
Share on other sites
This problem happens even if you create a timer at window-creation. I've always wondered why..
It doesn't happen if you start dragging the window right away, moving the mouse already when you click. It also pauses if you hold down the button on the minimize button for example. I think all such things are handled in some loop that doesn't return, except for that once the window starts moving it returns to allow for interactive moving.
Don't know why it waits 500 ms before returning unless the mouse moves.. the reply about double-clicking sounds plausible to me..
It also freezes if you hold down the right mouse-button in the title bar.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Hmm, it's possible - but still seems a bit ugly to me. This is beginning to look like the only option though...


I create the timer the first time WM_MOVING is reached, and remove the timer when WM_EXITSIZEMOVE is reached. This doesn't help with the delay though. There is about a 500ms delay between the time the user clicks the title bar and I receive SC_MOVE + HTCAPTION. During this time the window stops updating.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by lordikon
I create the timer the first time WM_MOVING is reached, and remove the timer when WM_EXITSIZEMOVE is reached. This doesn't help with the delay though. There is about a 500ms delay between the time the user clicks the title bar and I receive SC_MOVE + HTCAPTION. During this time the window stops updating.
Yep, it's that 500ms delay that I'm trying to avoid.
0

Share this post


Link to post
Share on other sites
I'm also fairly interested in this solution. I wish I could help, but after some research I found no answers either.
0

Share this post


Link to post
Share on other sites
So, it appears you've been having this problem for awhile. :D

http://www.gamedev.net/community/forums/topic.asp?topic_id=440341
0

Share this post


Link to post
Share on other sites
Quote:
Original post by lordikon
So, it appears you've been having this problem for awhile. :D

http://www.gamedev.net/community/forums/topic.asp?topic_id=440341
Yeah, I found that post while Googling, but decided not to necro the thread [smile]
0

Share this post


Link to post
Share on other sites
Here is a quick workaround. Note that this has the little quirk of setting the cursor position to the middle of the window's caption. Also, if the app has other windows and he starts dragging them, the problem remains. Also, the delay will remain if the window is sizable and the user [EDIT: referred to in the previous sentence as "he"] clicks on the borders to resize it. Also, this will effectively disable double-clicking the title bar to maximize the window. Plus possibly a few more things I haven't thought about yet.


case WM_NCLBUTTONDOWN:
if( SendMessage( hWnd, WM_NCHITTEST, wParam, lParam ) == HTCAPTION )
{
SetTimer( hWnd, 1000, 0, NULL );
SendMessage( hWnd, WM_SYSCOMMAND, SC_MOVE, 0 );
return 0;
}
break;

case WM_TIMER:
Render();
break;

case WM_EXITSIZEMOVE:
KillTimer( hWnd, 1000 );
break;






But personally I would recommend against messing with this because, well, you're just nitpicking one of Windows' insignificant quirks. That's not patriotic!

[Edited by - hikikomori-san on January 15, 2009 10:47:56 AM]
0

Share this post


Link to post
Share on other sites
Just found this: http://developer.popcap.com/forums/showthread.php?p=19636

They mention the same delay problem.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by lordikon
Just found this: http://developer.popcap.com/forums/showthread.php?p=19636

They mention the same delay problem.
Yes, I've been through Google. I've found plenty of reports of the problems, but no solutions.

Quote:
Original post by hikikomori-san
Here is a quick workaround. Note that this has the little quirk of setting the cursor position to the middle of the window's caption. Also, if the app has other windows and he starts dragging them, the problem remains. Also, the delay will remain if the window is sizable and the user [EDIT: referred to in the previous sentence as "he"] clicks on the borders to resize it. Also, this will effectively disable double-clicking the title bar to maximize the window. Plus possibly a few more things I haven't thought about yet.
Hmm, I'll give that a go later tonight, thanks.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by hikikomori-san
Here is a quick workaround. Note that this has the little quirk of setting the cursor position to the middle of the window's caption. Also, if the app has other windows and he starts dragging them, the problem remains. Also, the delay will remain if the window is sizable and the user [EDIT: referred to in the previous sentence as "he"] clicks on the borders to resize it. Also, this will effectively disable double-clicking the title bar to maximize the window. Plus possibly a few more things I haven't thought about yet.
This seems to work pretty well, with a bit of an ajustment:

LRESULT D3DWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static bool sbMoving = false;
static POINT sptOffset;

switch(uMsg)
{
case WM_NCLBUTTONDOWN:
if(SendMessage(m_hWnd, WM_NCHITTEST, wParam, lParam) == HTCAPTION)
{
POINT ptCursor;
RECT rcWnd;
GetWindowRect(m_hWnd, &rcWnd);
GetCursorPos(&ptCursor);
sptOffset.x = ptCursor.x - rcWnd.left;
sptOffset.y = ptCursor.y - rcWnd.top;

SetCapture(m_hWnd);
sbMoving = true;
return 0;
}
break;

case WM_NCLBUTTONUP:
case WM_LBUTTONUP:
sbMoving = false;
ReleaseCapture();
break;

case WM_MOUSEMOVE:
case WM_NCMOUSEMOVE:
if(sbMoving)
{
POINT ptCursor;
RECT rcWnd;
GetWindowRect(m_hWnd, &rcWnd);
GetCursorPos(&ptCursor);
SetWindowPos(m_hWnd, NULL, rcWnd.left+(ptCursor.x-(rcWnd.left+sptOffset.x)),
rcWnd.top+(ptCursor.y-(rcWnd.top+sptOffset.y)), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
}
break;
}

return DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}


I dread to think what horrible bugs this will cause though, so I think I'm just going to use the method with the 500ms delay for this, and add the above method as an alternative.
Another issue is that the user can still access the system menu of the window (Which will cause a stall), and select "Move" from there - which will stall. The second issue can be fixed with the timer as before, but probably not the stall when selecting the system menu.

Thanks for all the replies,
Steve
0

Share this post


Link to post
Share on other sites
It's down to a 500ms wait on an event when there are no mouse, keyboard, or sync messages in the queue. The event seems to be signalled when there are input messages available. There's also lots of resource locking which is mostly block-until-available so 500ms would be a lower bound to the stall. Lots of calls to user mode as well for hooking purposes so there's a few context switches to account for too.

The "highlights" from WM_SYSCOMMAND to WM_MOVING:

xxxSysCommand-
Calls some hook back in UM
xxxIsDragging-
GetKeyState(VK_LBUTTON)-
Just checks some bits, nothing heavy
Sets mouse capture to window-
Locks and unlocks the window
Checks if the thread's impersonation token or the process token is restricted
Checks user has WINSTA_WRITEATTRIBUTES permission on the Window Station
xxxInternalGetMessage (..., WM_MOUSEFIRST, WM_XBUTTONDBLCLK..)-
if any mouse action
Calls mouse hooks -> back to UM
Removes WM_LBUTTONUP from Queue
Calls mouse hooks again -> back to UM
seems it loops around couple of times
otherwise
Exits without doing anything of note
xxxInternalGetMessage(..., WM_QUEUESYNC, WM_QUEUESYNC)
if no message-
xxxInternalGetMessage(..., WM_KEYFIRST, WM_KEYLAST)
if no message-
xxxSleepThread(7, 500, 1)
KeClearEvent()
KeWaitForSingleObject(ClearedEvent, -500ms) // seems to be for input
end if
Releases mouse capture on window-
Locks and unlocks the window again
Puts WM_MOUSEMOVE in message queue
Calls window proc with WM_CAPTURECHANGED (0x0215)
returns whether the message x/y pos was within a specified rect (if there was a message)
if dragging
xxxSetWindowPos(PWND, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) -
BeginDeferWindowPos(1)
DeferWindowPos()
EndDeferWindowPosEx()
Z-Order checking stuff for a few windows
Call app WndProc with WM_WINDOWPOSCHANGING
Updates the previous top most window if there was one
Locks screen and blits the valid bits
Unlocks the screen
Sends any WM_WINDOWPOSCHANGED messages
returns
not checked beyond here for this case
returns
end if
xxxMoveSize-
Clips cursor to area
Call app WndProc with WM_GETMINMAXINFO
Draw the drag rectange-
SetWindowPos(PVOID, 0, x, y, width, height, (SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOZORDER))-
Locks screen and blits the valid bits to the new pos
Unlocks the screen
Updates relevant Windows
Fakes a mouse move
Call app WndProc with WM_ENTERSIZEMOVE
Locks Capture Window
Show Cursor
GetMessage(wnd, no filter)-
Scan's system queue for all messages-
GetNextSysMessage-
WM_MOUSEMOVE posted (prob due to fake move above)
Mouse hooks called
WM_MOUSEMOVE consumed (deleted from queue)
Hooks called again
Updates drag rect-
Calls WndProc with WM_MOVING
Redraws the rect
Wait for messages

0

Share this post


Link to post
Share on other sites
Hello all,

I know this is a very late reply but I felt I might be able to help people searching for answers to this question by adding some information.
What happens in the 'stall' is that DefWindowProc takes over complete control. You enter DefWindowProc and it doesn't return until the move is completed.
During that time, DefWindowProc will send a lot of WM_* messages to your own window procedure (including move updates), one important one being WM_PAINT (and I'm guessing only if your system settings are such that the windows are drawn during move and resize operations). So, you can draw and therefore update your window during the stalls by responding to WM_PAINT with a scene draw.
The bad news is that all your regular game code, starting with your message pump and ending with the normal process & draw loops, don't get called during the entire operation.
Testing with a couple of other applications, I think I may have found the one compelling reason for applications to supply their own customised title bar with system menu: so as to avoid DefWindowProc hanging on non-client messages (NC_* window messages).
It seems then that the solution is either to remove all the non-client regions from your window (by setting the appropriate window styles) and implementing your own move/resize and title bar.
Or, you somehow use multi-threading to keep the core going and the window updated; somehow having your gui be pretty much a stub thread while the 'real' thread does all the work (not sure how easy this is to do...).

Hopefully, this can still help someone.
Best Regards,
MicroVirus
0

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  
Followers 0