3
\$\begingroup\$

My Win32/DirectX 11.1 project requires that I render the mouse cursor myself rather than use the system cursor. This is easy enough; however, I've found that there is a human-perceptible latency when updating the position. I can demonstrate this in a simple test harness when I leave the system cursor on -- in this setup, my rendered cursor lags slightly behind the system cursor as I drag the mouse, catching up when I stop moving it. It is not a major effect, but it is non-zero, and reproduces on more than one system with different hardware (both CPU and GPU).

My test harness is stripped down as much as possible to avoid confounding variables. It merely creates a window and listens for WM_MOUSEMOVE in the winproc. When this message is received, it renders the cursor at the reported position. Position is passed via a constant buffer to my shaders; the vertex shader operates on a triangle strip with 4 vertices / 2 triangles which makes a quad and the pixel shader just produces a fixed color, resulting in a basic rectangle for my cursor shape.

I have also tried not handling WM_MOUSEMOVE, but instead polling the cursor position with GetCursorPos/ScreenToClient and rendering in a tight loop. This also reproduces the slight latency, but at least allows me to get a sense of FPS; this loop runs at over 4000hz on my machine, which is obviously well beyond the capacity of human perception. From this I believe the issue is not in the renderer, so I haven't tried other approaches like using a vertex buffer instead of a constant buffer.

If it is not in the renderer, my next guess is that I am experiencing some sort of input latency. For example, if the system cursor is rendered prior to (or racing with) updating the position reported by GetCursorPos I could experience this lag. I don't know this to be the case, so I am just speculating here. To help eliminate that variable I also tried using the raw input APIs (despite being usually counter-indicated for mice); this continued to exhibit the slight latency.

So: can anyone help me mitigate this latency? My desired end state is that I cannot perceive a difference in position between my rendered cursor and the system cursor when moving the mouse. Some additional constraints: I have to stay in user space (no rootkit solutions) and I'd really like to stick with published APIs if possible.

If it isn't possible (for example, if Windows is making use of APIs inaccessible to me), I'd like to at least understand why, if anyone knows.

To forestall requests for my source: unfortunately, I cannot share my code due to it being considered proprietary (yes, even the test harness...I don't make the rules).

Thanks!

\$\endgroup\$
7
  • \$\begingroup\$ The hardware cursor draws as a much faster rate than your rendering. As such, it pretty much always lags. \$\endgroup\$ Aug 10, 2022 at 18:20
  • 1
    \$\begingroup\$ But even if the hardware cursor draws faster, should that really matter when I can draw at over 4000 fps? I don't need the time delta to be 0, just small enough that a human cannot tell the difference. \$\endgroup\$ Aug 11, 2022 at 0:59
  • \$\begingroup\$ Your render time is not actually 4000 fps, or the cursor wouldn't be lagging. In the end, your render speed is limited by the refresh rate. \$\endgroup\$ Aug 11, 2022 at 16:53
  • \$\begingroup\$ Why can't you just use SetCursor() to customize the appearance of the hardware cursor? \$\endgroup\$
    – Adam
    Aug 22, 2023 at 0:20
  • \$\begingroup\$ @Adam because the reaction of the application would suffer lag, even if the pointer was correct, and this may or may not be acceptable depending on the application. Looking around for this type of problem, I saw someone mentioned having this issue for a drag-and-drop gesture where the application lagging made it feel "elastic-y" and not a good user experience. Edit:typos \$\endgroup\$ Aug 22, 2023 at 1:10

1 Answer 1

0
\$\begingroup\$

I ran into the exact same thing. Although it couldn't be helped much in windowed mode, in fullscreen, using the new DXGI SetMaximumFrameLatency function really helped:

    IDXGIDevice1* pDXGIDevice1;
    pD3DDevice->QueryInterface(__uuidof(IDXGIDevice1), (void**)&pDXGIDevice1);
    pDXGIDevice1->SetMaximumFrameLatency(1);

The default latency is 3, and I could feel it in fullscreen. Setting it to 10 confirmed that it DOES work. :)

I'll be looking for solutions in windowed mode in the meantime; it could be related to device settings or maybe the swap chain...

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .