Skip to content

[BUG] - DirectX capture always falls back to BitBlt on non-English Windows #171

Description

@Opicka879

Summary

ScreenGrabberWindows detects DXGI_ERROR_WAIT_TIMEOUT by matching the English
exception message. On a localized Windows install the message is translated, the
when filter never matches, and the exception falls through to the next handler —
which calls SetCurrentOutputFaulted() and falls back to GDI BitBlt.

The result: on any non-English Windows, DirectX capture is effectively never used,
and the "DirectX" toggle in the viewer has no observable effect.

Environment

  • ControlR 0.27.6.0
  • Windows 10, Czech (cs-CZ) system locale
  • Agent running as a Windows service

Root cause

ControlR.DesktopClient.Windows/Services/ScreenGrabberWindows.cs:

catch (COMException ex) when (ex.Message.StartsWith("The timeout value has elapsed"))
{
    return CaptureResult.NoChanges(captureMode: DirectXCaptureMode);
}
catch (COMException ex)
{
    _dxOutputGenerator.SetCurrentOutputFaulted();
    _logger.LogWarningDeduped(
        "Failed to capture with DirectX, falling back to BitBlt. Display: {DisplayId}", ...);
    return CaptureResult.Fail(ex);
}

AcquireNextFrame is called with a 0 ms timeout, so DXGI_ERROR_WAIT_TIMEOUT
(0x887A0027) is returned whenever a new frame isn't already queued. That is the
common case and not an error condition.

On Czech Windows the message reads:

Hodnota časového limitu vypršela a prostředek není ještě k dispozici. (0x887A0027)

which does not start with "The timeout value has elapsed", so the filter fails.

Agent log

[WRN] Failed to capture with DirectX, falling back to BitBlt. Display: \\.\DISPLAY1
System.Runtime.InteropServices.COMException (0x887A0027): Hodnota časového limitu
vypršela a prostředek není ještě k dispozici. (0x887A0027)
   at Windows.Win32.Graphics.Dxgi.IDXGIOutputDuplication.AcquireNextFrame(...)
   at ControlR.DesktopClient.Windows.Services.ScreenGrabberWindows.GetDirectXCapture(...)

SetCurrentOutputFaulted() marks the output faulted for 10 seconds and disposes it,
so the duplication object is torn down and recreated in a loop for the whole session.

Impact

Measured on the same machine, same activity, before and after the fix:

FPS Agent CPU
Before 10–13 ~35 %
After 20–40 lower

Because BitBlt provides no dirty rectangles, GetDirtyRegions also falls back to a
full software pixel comparison of every frame, and the cursor-only fast path in
HandleCursorOnlyUpdate is never taken.

Suggested fix

Match on the HRESULT, which is locale-independent:

// DXGI_ERROR_WAIT_TIMEOUT = 0x887A0027
catch (COMException ex) when (ex.HResult == unchecked((int)0x887A0027))
{
    return CaptureResult.NoChanges(captureMode: DirectXCaptureMode);
}

We've been running this patch on Czech Windows and it resolves the issue.

Note

Three other message-text comparisons exist in the codebase, but none of them read an
OS-generated message, so they should not be affected:

  • ControlR.DesktopClient/Program.cs"RenderTimer" (Avalonia)
  • ControlR.Agent.Common/Services/AgentHubClient.cs"canceled by client" (SignalR)
  • ControlR.Web.Server/Hubs/AgentHub.cs"does not exist" (server runs in a Linux container)

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions