The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: zemtex on January 30, 2011, 12:00:35 AM

Title: RawInputs organization
Post by: zemtex on January 30, 2011, 12:00:35 AM
I've uset GetRawInputData and it should be catched in the WM_INPUT message.

But now i'm implementing GetRawInputBuffer to reduce amount of WM_INPUT messages retrieved. Mice equipment today is very high resolution and can easily flood my program with WM_INPUT messages, so I figure using a buffered version of RawInput is the best solution.

My question is, where should I process it, microsoft sample use GetRawInputBuffer using a private windows message. Can I catch it in the peekmessage loop and just dump the WM_INPUT message from the windows procedure handler?

(Btw, there was not a single hit on "RawInput" or "GetRawInputBuffer" on this site, about time someone started such a thread :8))
Title: Re: RawInputs organization
Post by: qWord on January 30, 2011, 01:42:57 AM
Well, there is no need for buffered input, because you can handle the WM_INPUT message. The buffered method needs a timer or some other event, that let you read the input.
Quote from: zemtex on January 30, 2011, 12:00:35 AMMice equipment today is very high resolution and can easily flood my program with WM_INPUT messages, so I figure using a buffered version of RawInput is the best solution.
why? - for both methods you must process the same number of event at a time.

qWord
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 01:49:44 AM
If you move your mouse 500 pixels across with a 5700 dpi mouse and press 3-4 keys at the same time, it will generate events at the same pace, thats true, but the WM_INPUT version will rerun and fill in a message structure for each event over and over, while the buffered variant will only need to process it once without having the message resent.

If you use the buffered variant you will have code that runs and loops undisturbed, if you use the other variant you will run code, parse a long msg structure again and this adds overhead.
Title: Re: RawInputs organization
Post by: qWord on January 30, 2011, 01:54:34 AM
maybe your underrated the speed of today's computers  :bg
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 01:57:58 AM
There is no doubt that a modern computer can handle both ways, I even doubt you would notice any difference. Thats not the case, the case is when you combine the methods with demanding game-code, what will be the result then.

To tell the truth, I was tempted to use the easiest variant. But I don't go for cheap solutions just because the other is harder to implement. Thats a bad habit.
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 02:15:22 AM
Btw, do you know of a good way to implement the buffered variant, are you absolutely sure you have to use a timer, can you not parse the structures in the message loop?
Title: Re: RawInputs organization
Post by: jj2007 on January 30, 2011, 09:21:46 AM
You checked this (http://msdn.microsoft.com/en-us/library/ms645595%28v=vs.85%29.aspx), I suppose?

QuoteGetRawInputBuffer Function

Performs a buffered read of the raw input data.
Syntax
Copy

UINT WINAPI GetRawInputBuffer(
  __out_opt  PRAWINPUT pData,
  __inout    PUINT pcbSize,
  __in       UINT cbSizeHeader
);


Parameters

pData [out, optional]

    Type: PRAWINPUT

    A pointer to a buffer of RAWINPUT structures that contain the raw input data. If NULL, the minimum required buffer, in bytes, is returned in *pcbSize.
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 05:12:39 PM
Been readin all the ms docs yesterday, almost any sublink from that page is purple as i've clicked almost all of them and gone deeper into the topic. I spent about 6 hours reading on the subject.

I've already implemented the non-buffered variant and to tell the truth, its not that bad, the amount of messages it generates is really not as bad as I thought and it shouldnt produce a problem on performance.
Title: Re: RawInputs organization
Post by: qWord on January 30, 2011, 06:17:22 PM
hi,
I've also played a bit with it and end up in an bug in WOW64. However, after some fix it works: look for the line
Quote.if [edi].RAWINPUT.data.keyboard.VKey+8 == VK_J
the displacement of 8 is normally not needed - but win7-x64 seems to use wrong struct definition (WIN64) for the RAWINPUTHEADER-struct.(?)

include masm32rt.inc

.686p
.mmx
.xmm

RAWINPUTDEVICE struct
    usUsagePage USHORT  ?
    usUsage     USHORT  ?
    dwFlags     DWORD   ?
    hwndTarget  HWND    ?
RAWINPUTDEVICE ends

RAWINPUTHEADER  struct
    dwType  DWORD   ?
    dwSize  DWORD   ?
    hDevice HANDLE  ?
    wParam  WPARAM  ?
RAWINPUTHEADER  ends
PRAWINPUTHEADER typedef ptr RAWINPUTHEADER

RAWMOUSE struct
    usFlags WORD    ?
    union
        ulButtons   DWORD   ?
        struct
            usButtonFlags   WORD    ?
            usButtonData    WORD    ?
        ends
    ends
    ulRawButtons        DWORD   ?
    lLastX              SDWORD  ?
    lLastY              SDWORD  ?
    ulExtraInformation  DWORD   ?
RAWMOUSE ends
PRAWMOUSE typedef ptr RAWMOUSE

RAWKEYBOARD struct
    MakeCode            WORD    ?
    Flags               WORD    ?
    Reserved            WORD    ?
    VKey                WORD    ?
    Message             DWORD   ?
    ExtraInformation    DWORD   ?
RAWKEYBOARD ends
PRAWKEYBOARD typedef ptr RAWKEYBOARD

RAWHID struct
    dwSizeHid   DWORD ?
    dwCount     DWORD ?
    bRawData    BYTE 1 dup (?)
RAWHID ends
PRAWHID typedef ptr RAWHID

RAWINPUT struct
    header  RAWINPUTHEADER  <>
    union data
        mouse       RAWMOUSE    <>
        keyboard    RAWKEYBOARD <>
        hid         RAWHID      <>
    ends
RAWINPUT ends
PRAWINPUT typedef ptr RAWINPUT

RawInputThread proto hWnd:HWND

.data?
    hInstance                   HINSTANCE   ?
    dwThreadID                  DWORD       ?
    hCloseevent                 HANDLE      ?
    hwnd                        HWND        ?
.code

WndProc proto hWnd:HWND,uMgs:UINT,wParam:WPARAM,lParam:LPARAM

main proc
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG

    mov hInstance,rv(GetModuleHandle,0)
    mov wc.hInstance,eax
    mov wc.cbSize,SIZEOF wc
    mov wc.style,CS_HREDRAW or CS_VREDRAW or CS_SAVEBITS
    mov wc.lpfnWndProc,OFFSET WndProc
    mov wc.cbClsExtra,0
    mov wc.cbWndExtra,0
    mov wc.hIcon,rv(LoadIcon,0,IDI_APPLICATION)
    mov wc.hIconSm,eax
    mov wc.hCursor,rv(LoadCursor,0,IDC_ARROW)
    mov wc.lpszMenuName,0
    mov wc.hbrBackground,rv(GetStockObject,WHITE_BRUSH);
    mov wc.lpszClassName,chr$("Win32Wnd")
    invoke RegisterClassEx,ADDR wc
    mov ebx,ASM(mov edi,rv(GetSystemMetrics,SM_CXSCREEN))
    mov esi,rv(GetSystemMetrics,SM_CYSCREEN)
    shr ebx,1
    shr eax,1
    shr edi,2
    shr esi,2
    mov esi,rv(CreateWindowEx,0,wc.lpszClassName,"Win32Wnd",WS_VISIBLE or WS_SYSMENU or WS_MAXIMIZEBOX or WS_MINIMIZEBOX or WS_SIZEBOX,edi,esi,ebx,eax,0,0,hInstance,0)
    mov hwnd,eax
    invoke UpdateWindow,esi
   
    .while 1
        invoke GetMessage,ADDR msg,0,0,0
        .break .if !eax || eax == -1
        invoke TranslateMessage,ADDR msg
        invoke DispatchMessage,ADDR msg     
    .endw

    xor eax,eax
    ret
   
main endp

WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

    .if uMsg == WM_CLOSE
        invoke SetEvent,hCloseevent
        invoke PostQuitMessage,0
    .elseif uMsg == WM_CREATE       
        mov hCloseevent,rv(CreateEvent,0,1,0,0)     
        invoke CreateThread,0,0,OFFSET RawInputThread,hCloseevent,0,ADDR dwThreadID
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
   
    xor eax,eax
    ret
   
WndProc endp



RawInputThread proc hCloseEvent:HANDLE
LOCAL rid:RAWINPUTDEVICE
LOCAL hWndDummy:HWND
LOCAL cbSize:DWORD
LOCAL msg:MSG

    mov hWndDummy,rv(CreateWindowEx,0,"button",0,0,0,0,1,1,0,0,hInstance,0)

    mov rid.usUsagePage,1
    mov rid.usUsage,6
    mov rid.dwFlags,0
    m2m rid.hwndTarget,hWndDummy
    invoke RegisterRawInputDevices,ADDR rid,1,SIZEOF RAWINPUTDEVICE

    .while 1
        invoke MsgWaitForMultipleObjects,1,ADDR hCloseEvent,0,INFINITE,QS_RAWINPUT
        .if eax == WAIT_OBJECT_0+1
            .break .if rv(GetRawInputBuffer,0,ADDR cbSize,SIZEOF RAWINPUTHEADER)
            mov eax,cbSize
            imul eax,eax,16
            mov cbSize,eax
            .break .if !ASM(mov edi,alloc(cbSize))
            .while 1
                .if ASM( mov esi,rv(GetRawInputBuffer,edi,ADDR cbSize,SIZEOF RAWINPUTHEADER)) != -1 && eax
                    mov ebx,edi
                    push esi
                    .while esi
                        .if [edi].RAWINPUT.header.dwType == RIM_TYPEKEYBOARD
                            .if [edi].RAWINPUT.data.keyboard.VKey+8 == VK_J   ; !!!  fix for wow64
                                fn MessageBox,hwnd,"sdfsdf",0,0
                            .endif
                        .endif
                        ; align 4 needed !
                        add edi,SIZEOF RAWINPUT + 3
                        and edi,-4
                        dec esi
                    .endw
                    pop esi
                .else
                    .break
                .endif
            .endw
            .while 1
                .break .if !rv(PeekMessage,ADDR msg,0,WM_INPUT,WM_INPUT,PM_REMOVE)
            .endw
            free edi           
        .else
            .break 
        .endif
    .endw
    invoke DestroyWindow,hWndDummy
   
    ret
   
RawInputThread endp
end main
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 09:30:51 PM
The RAWMOUSE structure is bad unless you add 2 bytes at the beginning. It is off by 2 bytes using win32.

The windows documentation says that RawMouse usFlags is a word sized, but I need to add 2 bytes for the adressing to be correct.
Title: Re: RawInputs organization
Post by: qWord on January 30, 2011, 09:33:04 PM
well, the struct RAWINPUT must be/is aligned to 4 - have you checked this?
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 09:34:30 PM
I was about to say so, windows aligns the structure.
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 10:03:31 PM
I had my structures defined individually, because of that I had to align the RawMouse structure individually for it to work, so I simply just merged all structures and nested them.


RawInput struct 4

   header   RawInputHeader {}
   UNION data
      STRUCT mouse
         usFlags         word ?
         UNION
            ulButtons   dword ?
            STRUCT
               usButtonFlags   word ?
               usButtonData   word ?
            ENDS
         ENDS
         ulRawButtons      dword ?
         lLastX         dword ?
         lLastY         dword ?
         ulExtraInformation   dword ?
      ENDS

      STRUCT keyboard
         MakeCode      word ?
         Flags         word ?
         Reserved      word ?
         VKey         word ?
         Message         dword ?
         ExtraInformation   dword ?
      ENDS

      STRUCT hid
         dwSizeHid   dword ?
         dwCount      dword ?
         bRawData   byte ?
      ENDS
   ENDS

RawInput ends
Title: Re: RawInputs organization
Post by: qWord on January 30, 2011, 10:06:49 PM
have you tested my program on your machine? (after removing the +8)
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 10:13:47 PM
No I dont have win64 here.
Title: Re: RawInputs organization
Post by: qWord on January 30, 2011, 10:18:38 PM
it is an 32Bit application - after removing the displacement of 8 it should work (theoretically :-))

see the attachment: a msgbox should appear, when pressing 'j'
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 10:34:14 PM
Ok I will look into it, I thought it was the single raw read variant, Didnt pay too much attention. :bdg
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 10:42:15 PM
Why not use the main window as the target and fetch it in peekmessage? I read somewhere that using a dummy window is quite common. But why is it?

To be able to run the main loop undisturbed?
Title: Re: RawInputs organization
Post by: qWord on January 30, 2011, 10:49:42 PM
window-related message are only post to the thread that create the window - that the whole reason.
now, did the programm work?
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 10:51:45 PM
Yes works wonderfully
Title: Re: RawInputs organization
Post by: zemtex on January 30, 2011, 11:21:35 PM
The only thing I would add is RIDEV_NOLEGACY to the flags when creating rawinput to get rid of mouse and key legacy messages.
I would also avvoid allocating memory, the buffer doesnt go above 40 bytes for keyboard and mouse raw inputs, you can declare that
buffer. Even worse is to use globalalloc.

Other than that it looks good to me. I will copy your philosophy, but code my own code. I have my own codestyle so i'm sure you understand
that I need to code it myself. But like I said, I have studied the philosophy behind the code and will copy some of that.

:U
Title: Re: RawInputs organization
Post by: qWord on January 30, 2011, 11:43:42 PM
Quote from: zemtex on January 30, 2011, 11:21:35 PMI would also avoid allocating memory, the buffer doesnt go above 40 bytes for keyboard and mouse raw inputs, you can declare that
buffer.
That's dangerous - what is if windows collect two or more events and send them to you in a bunch? (for this case the function is designed)
Title: Re: RawInputs organization
Post by: zemtex on January 31, 2011, 12:28:56 AM
If you use GetRawInputBuffer you should allocate using heap allocation (not globalalloc) but if you use GetRawInputData you should use a local declared buffer of 40 bytes.

Allocating memory takes time, you could also measure the peak usage of memory during runtime to find a good sized buffer and then preallocate it.

Memory allocation is not such a big deal (usually), but we're dealing with directinput coding here so it is relevant, allocating memory should be avvoided if possible, especially in a RawInput handler, we want things to happen FAST and get the hell out of there.
Title: Re: RawInputs organization
Post by: zemtex on January 31, 2011, 12:36:59 AM
If your buffer is too small, catch an error if the returned required size is above your buffer size, you can avvoid the danger that way.
Title: Re: RawInputs organization
Post by: zemtex on January 31, 2011, 12:43:30 AM
Also beware when you declare RIDEV_NOLEGACY your window will not be taking mouse clicks or keyboard clicks, it works best for fullscreen programs. But then again, games that use RawInput is usually a full program  :dazzled:

If you dont declare it you will recieve multiple unneccesary WM_KEYUP WM_KEYDOWN and such useless messages that you wont process anyway.
Title: Re: RawInputs organization
Post by: zemtex on February 02, 2011, 06:49:39 PM
According to windows api documentation if you create a secondary dummy window and it gets focus, the main window will be in the background moving from priority 9 down to 7 and the rawinput handler window gets priority 9. Perhaps we ought to setfocus to the main window after creating the dummy window?

I've peeked into "Message-Only-Windows", I was hoping this could be used in replacement of creating a dummy "button" window. But I have my doubts it can be used for that. Anyone check it out: http://msdn.microsoft.com/en-us/library/ms632599%28v=vs.85%29.aspx#message_only
It looks like it is specially designed for this purpose.

Do you think this special message processing window can be used as a rawinputbuffer handler? The docs says such a window cannot be enumerated.  :snooty:

You can set the main window back to top priority by using SetForegroundWindow function

Btw, i've been thinking. Instead of using an event in your handler, you could kill the event and just use PostThreadMessage. That will eliminate some complexity and reduce overhead.
Title: Re: RawInputs organization
Post by: zemtex on February 03, 2011, 07:02:40 AM
I measured amount of messages retrieved by using a 5700 dpi mouse at full resolution and moving it as fast as possible, I measured the event count per second.

It peaked at 507 events per second (When moving mouse as fast as possible) I didnt output anything while measuring, I set a tickcounter every 1000 ms and output the result to the window caption text.

I couldnt get any higher number on any other dpi settings either. Thats nothing a computer cant handle. Thats not even a single event per millisecond. I have underestimated computers  :bdg
Title: Re: RawInputs organization
Post by: qWord on February 03, 2011, 07:37:23 AM
zemtex,
the question is why to use raw input. - the normal message processing (WM_MOUSEMOVE,...) is really enough for mouse and keyboard input. Also, as I notice, there are bugs in the implementation at least on Win7-x64.

qWord
Title: Re: RawInputs organization
Post by: zemtex on February 03, 2011, 08:45:40 AM
The good part of using legacy messages is that you dont have to deal with low level coding and you get pointer ballistics integrated in a single package.