CNA-BUG-133: DIRECTX11, DIRECTX12, FREEDIRECT and METAL implement the window-to-logical transform but never register for their window, so mouse and touch input ignore it

CNA snapshot 009d40f5  ·  Known Issues › Current bugs  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

Input looks renderers up by window id through IGraphicsRenderer::GetForWindow; these four renderers never call RegisterForWindow, so Mouse::GetState, Mouse::SetPosition and touch pass raw window coordinates through under a virtual resolution, letterbox or display scale.

Identifier
CNA-BUG-133
Category
Bug
Subsystem
Input
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Severity
Medium (a triage suggestion, not a project priority)
Evidence basis
Source-verified: read at TARGET, not executed
Tests touching this area
Yes: see Current tests
Affected contract
Mouse::GetState(), Mouse::SetPosition(int, int) and TouchPanel positions on the DIRECTX11, DIRECTX12, FREEDIRECT and METAL renderers

Expected behaviour

IGraphicsRenderer.hpp states the registry rule: “Renderers that implement coordinate conversion register themselves here so input can map platform window-client coordinates”. Vulkan, EasyGL, OpenGL4, SDL_GPU, WebGPU, FNA3D, the SDL renderer, GDI, Direct2D, Canvas and the DOM renderers call RegisterForWindow.

Actual behaviour at TARGET

DirectX11Renderer.cpp, DirectX12Renderer.cpp, FreeDirectRenderer.cpp and MetalRenderer.mm implement TransformWindowToLogical and TransformLogicalToWindow (presentation geometry, letterbox, display scale) but contain no RegisterForWindow call. window_to_logical and logical_to_window in Mouse.cpp and to_logical_position in SdlInputBridge.cpp find no renderer and return the window coordinate unchanged, so a game whose back buffer differs from the window size receives window pixels as game coordinates.

Source locations

Evidence

Checked by reading and by grepping every renderer source, including the Objective-C++ MetalRenderer.mm, for RegisterForWindow at 009d40f5; not executed (the Direct3D and FreeDirect renderers are Windows-only, METAL is Apple-only).

Focused reproduction

// Illustrative; not compiled or run. CNA_GRAPHICS_RENDERER=DIRECTX11,
// PreferredBackBuffer 800x480, window resized to 1600x960.
MouseState s = Mouse::GetState();
// Pointer at the window centre: expected (400, 240); actual (800, 480).

Current tests

MouseInputTests.cpp (CoordinateTransformPreservesLetterboxOffsetAndInverse) registers a test renderer by hand; IGraphicsRendererWindowRegistryTests.cpp tests the registry. No test checks that each renderer registers itself.

Regression test

A per-renderer check that constructing the renderer on a window makes GetForWindow(window) return it (the SDL_GPU constructor test already does this); the fix is to register after initialisation and unregister on destruction, as the other renderers do.

Blast radius

Games on DIRECTX11, DIRECTX12, FREEDIRECT or METAL whose window is not exactly the back-buffer size, including resizable and fullscreen-letterboxed windows: mouse, SetPosition and touch positions are wrong. Other renderers are unaffected.

Workaround

Keep the window at the back-buffer size, or scale mouse coordinates in the game.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

User guide
Input: mouse
Known issues
Bug index