CNA-BUG-133: DIRECTX9, DIRECTX11 and METAL implement the window-to-logical transform but never register for their window, so mouse and touch input ignore it
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 c1c316b9; 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 three 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
c1c316b9(c1c316b9c7a846ce8002809c151fcd1af14942c9) - 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 DIRECTX9, DIRECTX11 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, SDL_GPU, WebGPU, FNA3D and the SDL renderer call RegisterForWindow.
Actual behaviour at TARGET
DirectX11Renderer.cpp, DirectX9Renderer.cpp (whose transform FULLSCREEN-002 added) 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
modules/renderers/directx11/src/DirectX11Renderer.cpp— TransformWindowToLogical / TransformLogicalToWindow; no RegisterForWindowmodules/graphics/include/CNA/Internal/Renderers/Common/IGraphicsRenderer.hpp— RegisterForWindow / GetForWindow and the registry commentmodules/input/src/Xna/Mouse.cpp— window_to_logical and logical_to_window fall back to the raw coordinatemodules/input/src/Internal/SdlInputBridge.cpp— to_logical_position, used for mouse motion and touchmodules/renderers/metal/src/MetalRenderer.mm— MetalRenderer::TransformWindowToLogical is implemented but RegisterForWindow is never calledmodules/renderers/directx9/src/DirectX9Renderer.cpp— TransformWindowToLogical / TransformLogicalToWindow (added by FULLSCREEN-002); no RegisterForWindow
Evidence
Checked by reading and by grepping every renderer source, including the Objective-C++ MetalRenderer.mm, for RegisterForWindow at c1c316b9; not executed (the Direct3D 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 DIRECTX9, DIRECTX11 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.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- Input: mouse
- Internals
- Input internals: mouse
- Deep dives
- Input model: logical coordinates
- Known issues
- Bug index