Sunday, June 14, 2009

Out of sync and upside down: Windows mouse pointer acceleration (Enhance pointer precision) is partially broken

Windows mouse handling ("Enhance pointer precision" option) has always been partially broken in Vista and XP due to a simple design error which is only now fixed in Windows 7.

The "Enhance pointer precision" code uses a calculation that should have included:
× ScreenResolution(DPI) ÷ MouseBusUpdateRate.
The (incorrect) calculation actually used is:
× ScreenRefreshRate ÷ ScreenResolution(DPI).
"Out of sync and upside down."

The Enhance pointer precision checkbox

Windows has long provided a "mouse pointer acceleration" feature that determines how fast the on-screen mouse pointer (cursor) moves in response to movements of the mouse.
(In Windows XP and Windows Vista, mouse pointer acceleration is turned on using the Control Panel>Mouse>Pointer Options>"Enhance pointer precision" checkbox.)

If acceleration is enabled, when the mouse moves slowly, the pointer is moved at a lower ratio, for example 1 count ('dot') of mouse movement might correspond to 1 pixel or less of pointer movement. But if the mouse moves faster, the system responds by accelerating the movement of the pointer, using a higher ratio, for example 1 count of mouse movement then might correspond to 2 or more pixels of pointer movement.

Starting with Windows XP and continuing through Windows Vista and onto Windows 7 RC1, Windows implements mouse pointer acceleration in a different way than in previous versions of Windows.

The new implementation made important improvements in how acceleration works, and in most cases works well enough.
Some (including myself) consider it essential to enable mouse pointer acceleration [link to Coding Horror blog].

HOWEVER, there is a simple conceptual error in its design, which has been carried into the implementation.

There is a large possibility that the effects of the error were found during testing and an attempt was made to fix it.
BUT that fix did not correct the error, and instead merely masked its effects AND INVERTED and UNDID the intended behaviour.

RIGHT NOW, for millions of people, movement of the mouse pointer on their screens is being controlled by an algorithm which includes that conceptual error and "fix error".

The broken behaviour affects users running Windows XP or Windows Vista.
It was present in the first release of Windows XP in October 2001 and continues through Windows Vista SP2.
The conceptual error and the "fix error" have finally been fixed in Windows 7.

What is the broken behaviour?

The broken behaviour can be very very subtle, which may explain why it has not been more widely recognized as a problem and why it has taken so long for it to be fixed. The behaviour is:

  • How far the on-screen mouse pointer moves in response to on-mouse-pad mouse movement depends on the monitor refresh rate (higher refresh rates cause larger pointer movements). But the monitor refresh rate should have no effect.
  • Increasing the display DPI setting (Large size vs. Normal size in the Control Panel) causes the mouse pointer to move fewer on-screen pixels in response to on-mouse-pad mouse movement. But an increased DPI setting should cause the pointer to move a larger number of on-screen pixels, not fewer.

How should it work?

By reading what Microsoft have written about Windows XP mouse acceleration, and some investigation, we can explain how the acceleration should have worked.

Microsoft intended that the velocity (speed) of the on-screen pointer (measured in inches per second) would depend upon the velocity of the on-mouse-pad mouse (also measured in inches per second).

They defined an acceleration formula that translates the velocity of the on-mouse-pad mouse to the corresponding velocity of the on-screen pointer.

The algorithm works like so:

  1. During a time period, measure the count of mouse movements (also known as 'dots' or Mickeys).
  2. Convert the mouse movement count into a mouse velocity.
  3. Using the acceleration formula, calculate the corresponding pointer velocity.
  4. Convert the pointer velocity into a number of pixels of pointer movement.

Step 4 is where the conceptual error was made and also the "fix error".

To turn the calculated pointer velocity into a number of pixels of mouse movement involves some simple formulae.

Velocity = Distance
Time

If the "Time" term corresponds to a regular periodic interval, such as the time period between mouse updates, or the time period between each screen refresh, then we can also use this:

Rate = 1
Time

For example, a regular time period of 0.2 seconds corresponds to a rate of 5 times a second (5 Hz).

Combining the two formulae, we get:

Velocity = Distance × Rate

If something is moving 2 inches every 0.2 seconds (5 Hz) we can use the Velocity = Distance/Time formula to calculate Velocity=10 inch/s, OR we can use the Velocity=Distance×Rate formula to also calculate 10 inch/s.

In the case of moving the mouse pointer, what is the appropriate "Time" and "Rate" to use?

This is a key question, and where the conceptual error occured.
The correct time interval is the interval that occurs between successive pointer position updates and (identically) the correct rate is the rate at which the pointer position is updated.
In Windows, the mouse position is updated every time mouse movement counts are sent from the mouse to the PC.
The correct rate to use is the Mouse Bus Update Rate.
A moving mouse connected via a PS/2 bus typically sends movement counts 100 times each second (100 Hz).
A moving mouse connected via a USB bus typically sends movement counts 125 times each second (125 Hz).

How to measure "Distance" for the on-screen pointer?

Windows stores and uses an assumed screen resolution, in Dots Per Inch (DPI) in the Control Panel>Display Properties>Settings>Advanced dialog.
That Screen Resolution (DPI) can be used like so:

DistancePointer = PixelsPointer
ScreenResolution

Putting all of the formula together, we get this formula for pointer velocity:

VPointer = PixelsMouseBusUpdate × MouseBusUpdateRate
ScreenResolution

The formula above is the formula that SHOULD have been used.
At this point, the team within Microsoft designing and implementing the mouse acceleration got confused.
This confusion is documented in Microsoft's website here:
Pointer Ballistics for Windows XP

The section titled "Relating to Physical Units" indicates that an incorrect formula for Vpointer was used:

VPointer = PixelsMouseBusUpdate × ScreenUpdateRate
ScreenResolution

Compared to the correct formula, note that ScreenUpdateRate is used where MouseBusUpdateRate should have been used instead.

Using ScreenUpdateRate in the formula would have been appropriate IF the pointer position was updated every screen refresh.
HOWEVER, the pointer position IS NOT updated every screen refresh; it is updated on every mouse bus update.

Of course, it could have been that the Microsoft team had only been imprecise with their formula.
HOWEVER, the example in that section also shows that they have used their incorrect formula: a correct examination of their example gives a physical to virtual gain of 5, NOT the 3 that they calculate.

Of course, it could have been that the Microsoft team had only been imprecise with their formula, AND also clumsy with their example.
HOWEVER, examination of the actual mouse behaviour in Windows XP and Windows Vista shows that they DO have the incorrect ScreenUpdateRate term in their algorithm, AND WORSE!

Considering again the correct formula for Vpointer and rearranging and solving for Pixels:

PixelsMouseBusUpdate = VPointer × ScreenResolution
MouseBusUpdateRate

Let's examine this formula.
Suppose a monitor having a high resolution (a high DPI, in other words, a small pixel size/pitch).
In order to move the mouse pointer a given physical distance on this high resolution monitor (measured in inches), we would have to move the pointer a LARGER number of pixels, because each pixel is smaller. Note that the formula above MULTIPLIES by ScreenResolution, which will have the desired effect of calculating a LARGER number of SMALLER pixels.

What formula is used by Windows XP and Windows Vista?

Despite the confused formula and example on the Microsoft Pointer Ballistics webpage, one would hope that the correct PixelsMouseBusUpdate formula is used, as above.
One might suspect that a formula that had ScreenUpdateRate instead of MouseBusUpdateRate is used.
However, the formula used is:

PixelsMouseBusUpdate = VPointer × ScreenUpdateRate
ScreenResolution

Note: rather than multiplying by ScreenResolution, the formula divides by ScreenResolution.
Rather than divide by ScreenUpdateRate (or MouseBusUpdateRate) the formula instead multiplies by ScreenUpdateRate.

Consider again the monitor having a high resolution (a small pixel size/pitch).
Rather than compensating for the small pixel size, the formula used amplifies the effect of a small pixel size: For a high resolution monitor, Windows XP and Windows Vista move the mouse pointer a SMALLER number of SMALLER pixels which is undesirable.

Also undesirable is that how far and how fast Windows XP and Windows Vista move the mouse pointer, depends upon the screen refresh rate.
(The Microsoft team put screen refresh rate in their formula because they wanted to REMOVE any possible effect that screen refresh rate might have had on mouse pointer movement, but instead they ADDED an undesirable effect.)

How could this problem have remained unfixed for so long?

Consider a typical monitor with a refresh rate of 75 Hz and 96 DPI, and a typical mouse with a USB bus update rate of 125 Hz.
The correct PixelsMouseBusUpdate formula calculates ScreenResolution/MouseBusUpdateRate = 96/125 = 0.768.
The "out of sync and upside down" formula that Windows XP and Windows Vista use calculates ScreenUpdateRate/ScreenResolution = 75/96 = 0.78125.
These two numbers differ by less than 2%.

Might the Microsoft team have jumped directly to the incorrect ScreenUpdateRate ÷ ScreenResolution calculation and then not realised the mistake?

Might they have started with ScreenResolution ÷ ScreenUpdateRate and realised something was wrong and then "fixed" it by inverting the numerator and divisor?

We may never know.

A silly flash toy...

The real reason why mouse acceleration is broken? [link]


24 comments:

Dae said...

Could you please elaborate on what's Pixels_MouseUpdateRate? I didn't understand this in the Microsoft article either, and there they just called it "Mickey" for some reason...

Additionally, why is mouse resolution not used in the new formula? I.e. on my Razer mouse I've set 500 DPI. I believe it matters too.

Thanks!

Mark Cranness said...

Pixels_MouseBusUpdate means the number of pixels on the screen that the pointer moves in each (or any) particular Mouse Bus Update time period.

I would write Microsoft's first formula (which I didn't discuss above) as:
V_Mouse = MouseCount_MouseBusUpdate * (MouseBusUpdateRate / MouseResolution)

Microsoft use the term 'Mickey', which is short-hand for 'mouse movement count', i.e. the count of how many dots or points sent by the mouse to the computer during any Mouse Bus Update time period.

In Microsoft's example (the 'Relating to Physical Units' paragraph) they are assuming a system with no acceleration applied, where one dot or point moved by the on-mouse-pad mouse translates to one pixel moved by the on-screen pointer.
I.E. in the Microsoft formula: Mickey = MouseCount_MouseBusUpdate = Pixels_MouseBusUpdate

Microsoft do use the mouse resolution in their first (V_Mouse) formula, but they call it 'Pointer Resolution' for some confusing reason.
I didn't use mouse resolution in my formula because I was only discussing the second (V_Pixels) formula which is the formula that has problems.

BTW, In XP, Vista and Windows 7, Micrsoft hard-code the mouse bus update rate to ~120 and hard-code the mouse resolution to ~400. They do this probably because there is no standard API for mouse driver developers to use to let Windows know what the actual DPI and update rate are.

Anonymous said...

Mark,

I agree with you that Microsoft made an error when they used ScreenUpdateRate instead of BusUpdateRate in their formula for V_pointer. However, I don't see how you come to the conclusion that their calculation is "upside down." In the "What formula is used by Windows XP and Windows Vista?" section, you wrote that Microsoft uses this formula:

Pointer = V × ScreenUpdateRate / ScreenResolution

Where did you get that? I could not find any such information in Microsoft's article you quoted.

Please note also that the screen resolution Microsoft uses is not the DPI setting in the Display Properties. They seem to be using the screen dimensions in pixels in combination with an assumed physical size of the screen in inches to estimate the screen resolution. I doubt that the OS actually queries the monitor properties to find out the screen size in inches.

It has been my observation that the DPI setting in the Display Properties does not work the way you describe. In Windows XP, instead of making each pixel smaller, increasing the DPI makes each inch larger. It seems to me that this setting is supposed to be used in combination with the screen size in pixels to adjust the size of the inch on the screen so that it is closest to the actual inch. Then this DPI setting would be close to the actual screen resolution and could be used in the calculation of pointer movement. I cannot find an equivalent of this DPI setting in Windows Vista. Perhaps, Microsoft eliminated it.

Mark Cranness said...

> Pointer = V × ScreenUpdateRate / ScreenResolution ... Where did you get that?

That formula is not in the Microsoft article.
It was found by testing how actual mouse input caused the pointer to move (including a program that simulates pointer movement according to an algorithm using that formula, and the simulated pointer matches the actual Windows pointer pixel for pixel, and other direct technical ways).

I say that the XP and Vista calculation is "upside down" because they used the UpdateRate/ScreenResolution formula when they should have used ScreenResolution/UpdateRate.

The Microsoft article does describe DPI in terms of screen physical size and resolution, but the actual Windows mouse algorithm uses the control panel display DPI setting.

When I said 'a high DPI, in other words, a small pixel size/pitch', I was not being as clear as I should have been. I did not mean that a larger DPI makes each pixel smaller.
I meant that if you have a monitor with a small pixel size, you would/could set a high DPI so that UI elements appear at a better size.

The display DPI can be used exactly as you describe to make a screen inch closer to an actual inch (but can also be set to whatever value provides the best view of text and UI elements).

This link describes how to change the DPI on Vista.

Nirsoft provide a tool that can be used to test how the mouse:pointer algorithm works!
NirCmd has a sub-command: 'sendmouse move x,y' that can be used to inject controlled mouse movements into Windows and then you could see what effect changing monitor refresh rate or display DPI has for yourself (ask for details).

Anonymous said...

Does this mean that, dpi translates to different resolution, for example:
If I use 1920x1080, will my pointer respond slower than at 800x600?

Mark Cranness said...

> Does this mean that, dpi translates to different resolution?

No. Changing your screen size does not change the Windows DPI and will not change how fast the pointer reponds.
The Windows DPI setting is separate from the screen size.
How to change the DPI on Windows 7.
How to change the DPI on Vista.
How to change the DPI on XP.

eltranced said...

not only is the problem at the tip of their noses they still havent made a fix for xp?

Mark Cranness said...

I also wonder why they have not made a fix for XP!
Sometimes I think I may make a fix myself, along the lines of Anir's WCAFIX...

Allan said...

Very informative and a good read. Thank you.

Anonymous said...

If I don't enable "enhance pointer precision", is cursor movement directly proportional to mouse movement? E.g., if I move my mouse 1/1000 inch then the cursor will move 1 pixel.

Mark Cranness said...

> If I don't enable "enhance pointer precision", is cursor movement directly proportional to mouse movement?

Yes, with "Enhance pointer precision" OFF (unchecked) pointer movement is directly proportional to mouse movement.

Although response linear and proportional, it is scaled according to the control panel "Pointer speed" slider.
In your example, if the slider is set to the middle 6/11 position, then a 1000 DPI mouse if moved 1/1000 inch would move the pointer 1 pixel.

Osmo Jaakkola said...
This comment has been removed by the author.
Meow Opre said...

Very useful. I'm also having fun changing my mouse cursor and could do the basic personalized mouse pointer . <3

Sami Niemi said...

Hey. I haven't used the mouse fix yet but I tried the mouse movement recorder and it didn't show a single red or green line. Does that mean I don't need the mouse fix at all?

Mark Cranness said...

> Does that mean I don't need the mouse fix at all?

Read my main post The MarkC Windows 8.1 + 8 + 7 Mouse Acceleration Fix and scroll down to "Does my game need a mouse fix?".

Carl Cahill said...

I've actually become quite attached to XP's flawed algorhtyhms lol you might think it's madness but how would i infact emulate it in Windows 10 with games that don't accept direct input is the extra challenge

Carl Cahill said...

I imagine you've accrued quite a lot of familiarity with the XP mouse code, you might be just the man to help me in my quest to best emulate it in Povaaks intercept driver or better still something that is capable of more accurately emulating it..

Thankfully Bologna also decimalised them

Below are the default values converted to decimal figures respectively:

SmoothMouseXCurve
00,00,00,00,00,00,00,00, <-- 0
15,6e,00,00,00,00,00,00, <-- 0.43001
00,40,01,00,00,00,00,00, <-- 1.25
29,dc,03,00,00,00,00,00, <-- 3.86001
00,00,28,00,00,00,00,00 <-- 40

SmoothMouseYCurve
00,00,00,00,00,00,00,00, <-- 0
b8,5e,01,00,00,00,00,00, <-- 1.37
cd,4c,05,00,00,00,00,00, <-- 5.30001
cd,4c,18,00,00,00,00,00, <-- 24.30001
00,00,38,02,00,00,00,00 <-- 568

but i'm not sure where to place the decimal values in the interaccel framework to replicate it's effect and Kovaaak is unsure, maybe your superior XP knowledge can shed some more light

Mark Cranness said...

Carl wrote:
> XP ... how would i infact emulate it in Windows 10 with games that don't accept direct input is the extra challenge

Windows 10 mouse response is exactly the same as XP, when XP was running @ 60Hz monitor refresh rate.

So the best way for games that don't use DirectInput or Raw Input is just use the standard Windows 10 curve.

In the registry the Windows 10 looks different than XP, but there is a different internal scaling inside the code:
XP has: mouse_movement × (60 / 96) × SmoothMouseYCurve,
Windows 10 has: mouse_movement × (96 / 120) × SmoothMouseYCurve.

BUT all of the standard SmoothMouseYCurve values in the registry are the XP values × 100 / 128, so it works out exactly the same.

If your XP monitor refresh rate was not 60 Hz, then an adjustment is needed.
If your XP mouse polling rate is different to your Windows 10 polling rate, then an adjustment is needed.

> ...emulate it in Povaaks intercept driver

This is the Windows XP curve you need to hit:
https://drive.google.com/open?id=0B_PnkMGDlD5nR1dSSlZIRFJSazg
(The title says Windows 8, but the curve is exactly the same for Windows XP@60Hz, Vista@60Hz, 8, 8.1 & 10, and very close for Windows 7.)

Having just now watched KovaaK's youtube video, I don't think it can quite match the same bumps and rounded curve shape as the XP curve, but you might get close enough?

Carl Cahill said...

Yeah it's funny in hindsight years later when i read your blog explaining how resolution and refresh rate come into the formula in xp, cos i had a monitor with the capability of 120hz even back in early xp but when i put it to 120 from 85, it totally ruined how the mouse felt, which makes a lot more sense now why it would, so i would purposely keep it at 85hz so my aim wouldn't be too erratic.

I used;

800x600 res in game
80 fov
500hz polling rate
85 refresh
85fps cap
400dpi
21" CRT
WPS at 1 space below default (so 5/11? :S )

Even such things as the modern widescreen monitors scaling the resolution 4:3 to fit seems to skew mouse curves

I've had a nightmare trying to emulate it, anything other than that feels terrible now, got years of muscle memory so specifically to this combination, i've been trying to emulate it for like 2 years with Povaaks driver fml with like 1,000 different permetations of values in each field, i'm out of my depth lol

It seems crazy that a lot of people used the enhanced pointer option in xp, without using your mouse fix and yet new games don't have an inbuilt option for it to cater for the amount of people that want specifically the enhanced point settings :| your tool which did regedits was good, seem to remember it allowed multiple levels of acceleration but i feel like the raw input of games like UT4 doesn't allow it to intercept unfortunately? it seems like it'd be quite popular

Not being able to keep the mouse settings i've always used in the advent of raw input has destroyed my skill competatively now, desperate to find a solution to get it back!:(

Mark Cranness said...

> XP @ 85Hz

If you are still using a 500 Hz mouse polling rate, then try bumping up your in-game sensitivity up by × 85 / 60 (= ×1.42), but leaving everything else the same as XP, ie Mouse at 400cpi and 5/11.

I should have asked at the start: did you have Enhance pointer precision on or off, but I it must have been on because changing the monitor refresh rate changed the sensitivity (120Hz would cause a slower mouse response).

Using FOV = 100 or so might make the centre part of a wide screen look the same as a 4:3 monitor.

Diego GAME said...

Can anyone tell me how I can emulate mouse movement like Windows XP + 144Hz on @Windows 7 + 144Hz?

Mark Cranness said...

> Windows XP + 144Hz on @Windows 7 + 144Hz?

A very custom mouse curve could do that, but far easier to increase your in-game sensitivity by (144 / 96) / (96 / 150) = × 2.34, which will have the same effect.
(This assumes you still have the same mouse polling rate on Windows 7 as you used on XP.)

Diego GAME said...

Unfortunately this does not work


- In Windows XP, the more Hz refresh the LCD, the faster the mouse moves, and you need to reduce the sensitivity of the mouse in the [control panel]


- In Windows 7, the more Hz refreshing the LCD, you do not have to do anything then I automatically sync the mouse to the fps indicator so it does not go too fast, so the mouse pointer at 144Hz "very smoothly moves on the screen"


The smooth movement of the pointer annoys me in windows 7 @144Hz (smoothly), because I am accustomed to XP @144Hz (no smooth), year after year searches the internet how to do it :(

Mark Cranness said...

> I am accustomed to XP @144Hz (no smooth)

I should have asked what mouse fixes if any you were using on XP?
CPL, or Cheese?
And what games you played.
And what control panel sensitivity you used in XP and what you now use in 7.

> In Windows XP, the more Hz refresh the LCD, the faster the mouse moves...

Yes that is true, which is why I was suggesting that for Windows 7 you increase your mouse sensitivity.
In Window XP: 144Hz increases mouse speed.
In Windows 7: 144Hz does not increase mouse speed.
So to get new Windows 7 to match old Windows XP, you need to increase mouse speed some way?