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
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
modules/renderers/directx11/src/DirectX11Renderer.cpp— TransformWindowToLogical / TransformLogicalToWindow; no RegisterForWindowmodules/renderers/directx12/src/DirectX12Renderer.cpp— TransformWindowToLogical / TransformLogicalToWindow; no RegisterForWindowmodules/renderers/freedirect/src/FreeDirectRenderer.cpp— TransformWindowToLogical (ComputeLetterbox); 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 called
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.
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