Marathon Input Issues Fix : Concevoir des jeux PC capables de survivre aux conflits d'Overlay
Tout développeur de jeux multijoueurs compétitifs connaît cette sensation de voir un netcode parfaitement architecturé s'effondrer à cause d'un overlay Discord. Vous passez des mois à optimiser votre infrastructure serveur pour une latence inférieure à 30ms, pour finalement découvrir que les joueurs ratent leurs tirs parce qu'une application de streaming en arrière-plan étouffe le thread d'input de votre jeu.
C'est exactement ce qui se passe actuellement lors du Server Slam de Marathon, le prochain extraction shooter de Bungie. Les joueurs PC signalent un mouse lag sévère, des inputs perdus et des mouvements de caméra saccadés. Bungie a officiellement reconnu le problème, pointant du doigt les utilitaires tiers, les overlays de streamers et certaines méthodes de capture de fenêtre.
Si vous surveillez les forums, des milliers de joueurs cherchent désespérément un marathon input issues fix. La solution temporaire côté joueur est simple : streamer l'écran complet au lieu de capturer la fenêtre du jeu, et désactiver les overlays.
Mais pour les développeurs, cela pose une question d'architecture critique : pourquoi les apps de streaming et les overlays détournent-ils les inputs, et comment concevoir nos clients PC pour l'empêcher ?
Dans ce deep dive technique, nous analyserons la gestion de l'input sous Windows, pourquoi OBS Game Capture cause des problèmes de frame pacing, et comment découpler vos threads d'input.
La cause racine : Comment les Overlays détournent le Render Pipeline
Pour comprendre pourquoi le streaming ruine le mouse input, il faut regarder comment les jeux PC s'affichent à l'écran.
Lorsqu'un joueur utilise OBS ou Discord, ces applications injectent une DLL directement dans le processus du jeu. Cette DLL intercepte les appels API graphiques, notamment via un hook sur la fonction Present de DirectX ou vkQueuePresentKHR de Vulkan.
Le goulot d'étranglement du Swap Chain
En hookant le swap chain, l'overlay force le moteur à attendre pendant qu'il copie le frame buffer vers son propre espace mémoire avant de permettre l'affichage de la frame.
Si le CPU ou GPU est chargé par l'encodeur, cette copie stagne. Une frame censée prendre 16.6ms (à 60 FPS) prend soudainement 24ms. Comme la plupart des moteurs interrogent l'input Windows standard (WM_MOUSEMOVE) sur le thread principal juste avant le render tick, retarder le rendu retarde l'input polling.
Votre souris gaming 1000Hz est alors interrogée à des intervalles erratiques, ce qui donne une sensation de visée lourde ou flottante.
Fullscreen vs. Windowed Capture
Bungie a noté que les problèmes surviennent surtout en capture de fenêtre. Cela est dû à la gestion de la composition par le Windows Desktop Window Manager (DWM).
En Exclusive Fullscreen ou avec le DXGI Flip Model, le jeu bypass le DWM. Mais si une app de streaming force un hook de capture de fenêtre, elle force souvent le jeu en mode composité, ajoutant 1 à 3 frames de latence (16ms à 48ms) avant même que l'image n'atteigne l'écran.
Architecturer un Input imblocable : L'API Raw Input
Pour immuniser votre jeu, vous devez abandonner les message pumps Windows standards pour les inputs critiques. Se fier à WM_MOUSEMOVE est suicidaire pour un shooter compétitif.
Utilisez plutôt la Raw Input API (WM_INPUT) et déplacez l'input polling vers un thread dédié de haute priorité, totalement découplé de la render loop.
Implémenter le Raw Input en C++ (Win32)
Voici comment bypasser les files d'attente standards pour enregistrer les données raw de la souris :
// Initialisation Raw Input C++ Win32
void InitializeRawInput(HWND hwnd) {
RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = 0x02;
Rid[0].dwFlags = 0;
Rid[0].hwndTarget = hwnd;
if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) {
DWORD error = GetLastError();
LogWarning("Échec de l'enregistrement Raw Input. Erreur : %d", error);
}
}
// Dans votre WndProc
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_INPUT: {
UINT dwSize = 0;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
if (dwSize == 0) break;
LPBYTE lpb = new BYTE[dwSize];
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
RAWINPUT* raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE) {
int deltaX = raw->data.mouse.lLastX;
int deltaY = raw->data.mouse.lLastY;
InputManager::GetInstance().AccumulateMouseDelta(deltaX, deltaY);
}
delete[] lpb;
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Développeurs Unreal Engine : Désactiver le Smoothing
Unreal gère le Raw Input en interne, mais applique souvent un lissage qui aggrave le ressenti de lag. Désactivez-le dans le PlayerController :
void ACompetitivePlayerController::SetupInputComponent() {
Super::SetupInputComponent();
bEnableMouseSmoothing = false;
LocalPlayer->AspectRatioAxisConstraint = AspectRatio_MaintainYFOV;
}
Effet de cascade : Client Lag vs. Server Rollback
L'input lag n'est pas que local. Dans une architecture serveur autoritaire, si un overlay bloque la boucle du client, les paquets d'input arrivent en retard. Le serveur doit alors accélérer la simulation pour rattraper le retard. Si le délai est trop grand, la reconciliation force une correction brutale (rubberbanding), comme expliqué dans How To Fix Player Location Desync In Uefn And Unreal Engine Multiplayer.
Gestion côté serveur
Le serveur doit utiliser un buffer d'input pour absorber le jitter du client.
void ServerProcessClientInputs(ClientConnection* client) {
const int TARGET_BUFFER_SIZE = 2;
int currentBufferSize = client->InputQueue.Size();
if (currentBufferSize > TARGET_BUFFER_SIZE + 3) {
client->InputQueue.DropOldest(currentBufferSize - TARGET_BUFFER_SIZE);
}
if (!client->InputQueue.IsEmpty()) {
InputCommand cmd = client->InputQueue.Pop();
SimulatePlayerMovement(client->PlayerEntity, cmd);
}
}
Avec horizOn, déployez des serveurs optimisés avec protection contre le desync, permettant à vos équipes de se concentrer sur le client.
5 Best Practices pour l'Input PC
- Découpler l'Input du Rendu : Utilisez un thread haute priorité à 1000Hz.
- Forcer le DXGI Flip Model : Utilisez
DXGI_SWAP_EFFECT_FLIP_DISCARDpour bypasser le DWM. - Hardware Cursor : Utilisez
SetCursorde l'OS plutôt que des quads texturés. - Alerter sur les Injections : Détectez les DLL d'overlays et prévenez le joueur.
- Fixed Timestep Simulation : Liez la physique et l'input à un pas de temps fixe.
Conclusion
Le cas Marathon rappelle la complexité de l'écosystème PC. Pendant que les joueurs cherchent un marathon input issues fix, les développeurs doivent implémenter le Raw Input et découpler les threads. Pour le backend, utilisez horizOn pour gérer l'orchestration serveur et le desync.