Skip to content

Service COM during frame throttle so screen readers stay responsive#4761

Open
RealAmethyst wants to merge 1 commit into
TASEmulators:masterfrom
RealAmethyst:patch-1
Open

Service COM during frame throttle so screen readers stay responsive#4761
RealAmethyst wants to merge 1 commit into
TASEmulators:masterfrom
RealAmethyst:patch-1

Conversation

@RealAmethyst
Copy link
Copy Markdown

EmuHawk's frame throttle uses Thread.Sleep, which does not dispatch incoming COM calls. Screen readers
(NVDA, Narrator, etc.) read the window via UI Automation / MSAA — COM calls marshaled to the STA UI
thread — so during every frame's sleep the thread stops answering them. The result: all screen readers
become very laggy whenever EmuHawk is the foreground window, even though the app itself stays responsive.

This replaces the two Thread.Sleep calls in Throttle.cs with a small ResponsiveSleep that waits the same
duration via CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS, ...), which dispatches COM calls during the
wait (COM only — no window input/paint — so there's no reentrancy into the emulation loop). The Unix
path keeps Thread.Sleep, since CoWaitForMultipleHandles is Windows-only.

@YoshiRulz YoshiRulz linked an issue Jun 3, 2026 that may be closed by this pull request
Comment thread src/BizHawk.Client.EmuHawk/Throttle.cs
private static extern IntPtr CreateEventW(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, IntPtr lpName);

private const uint COWAIT_DISPATCH_CALLS = 0x8;
private static readonly IntPtr[] _responsiveSleepHandles = { CreateEventW(IntPtr.Zero, true, false, IntPtr.Zero) };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this will need OSTailoredCode.IsUnixHost ? [ default ] : to avoid crashing on startup.

/// </summary>
private static void ResponsiveSleep(int ms)
{
if (ms < 1) ms = 1;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both callers already pass in >= 1, so this line can be removed.

@Morilli
Copy link
Copy Markdown
Collaborator

Morilli commented Jun 3, 2026

I don't know how a screenreader is typically used, but I started NVDA to see if I can notice anything, and I don't see what you mean when you say "laggy". If anything, there should be a <15ms delay in response time which should be negligible.

@RealAmethyst
Copy link
Copy Markdown
Author

so without this fix it's like 10 seconds of lag if not more, this fixes that, this also happens on every other screen reader I tested. If you don't get that lag can you please inform me of what screen reader version you use?

@CasualPokePlayer
Copy link
Copy Markdown
Member

CasualPokePlayer commented Jun 4, 2026

I would be wary of trying to "pump COM messages" while sleeping. While technically the STA thread contract dictates this should be done, in practice this ends up causing more issues. This "fix" would also be incomplete, as this throttling is only applicable assuming Clock Throttle. Audio Throttle has sleep calls elsewhere and VSync Throttle / VSync Enabled are just impossible to "fix" here. I would also be skeptical about "very laggy" claims, the sleep calls are normally very short and message pumping occurs between them. Are you sure you didn't end up setting BizHawk to say 1% speed? (which case there will be big sleep calls, although in this case the UI would also just be laggy in the first place / this is a known issue)

Also, COWAIT_DISPATCH_CALLS would do nothing in this case (or rather it would be ignored), our thread is an STA thread, not an ASTA thread (ASTA is its own thing with UWP UI, it is not applicable to WinForms nor most applications). So in practice you are getting COWAIT_DEFAULT (0) behavior.

@RealAmethyst
Copy link
Copy Markdown
Author

Whenever I would open the emulator and try to use the UI it would be very lagy, this is also not just me, but every other blind user I've talked to has the exact same issue, this is before doing anything, not even loading a rom, as the UI is just slow with screenr eaders in general. It also does not matter what screen reader you use, same issue.

@YoshiRulz
Copy link
Copy Markdown
Member

Clearly there are more differences between Morilli's and my dev environments and a "real" user's environment, and it just hasn't occurred to any of us what those differences are. Do you use your PC with the screen off? Is the lag dependent on the display method (D3D vs GDI+)? Does Windows itself change if it's installed with a11y features enabled?

@RealAmethyst
Copy link
Copy Markdown
Author

I tried checking D3D vs GDI and no difference on my end, I have a monitor connected but it's not on most of the time, installing windows with accessibility stuff shouldn't change anything as it comes with the OS by default

@BlindGuyNW
Copy link
Copy Markdown

Just chiming in here as a fellow blind user to confirm that the lag is not imaginary :) IT's specifically just ambient as soon as you open the app with NVDA running. The slightly confusing thing is that the lag only occurs in screen reader response time, not in the app performance itself as far as I am aware.

@CasualPokePlayer
Copy link
Copy Markdown
Member

CasualPokePlayer commented Jun 5, 2026

To be clear, does this lag also occur for Audio Throttle and/or VSync Throttle and/or Vsync Enabled?

@RealAmethyst
Copy link
Copy Markdown
Author

I have no idea, as trying to turn those settings on is a pain due to screen reader lag, this happens without us changing any settings, just an program launch

@RealAmethyst
Copy link
Copy Markdown
Author

Tested more, the app itself is fast, no lag, games themselves has no lag, the screen reader itself lags, even when I try to do screen reader specific commands, when the emulator is in forground, it will lag, this is for all screen readers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

screen reader accessibility for UI

5 participants