Latência

Por que o Dish parece um fio.

Latência é a única métrica que importa em um controle. O Dish foi construído em torno dela desde o primeiro dia.

O orçamento de latência

Um controle Xbox com fio faz polling a 250 Hz no Windows. Isso significa que o seu jogo pode ler um botão apertado até 4 ms depois que aconteceu, só pela taxa de polling. Empilhe um frame de 60 fps em cima disso e o pior caso dedo a pixel em um controle com fio chega em torno de 20 ms.

O Dish foi construído para adicionar quase nada a esse orçamento em uma LAN Wi-Fi 6 normal. Para onde cada milissegundo vai:

Estágio Tempo típico
Evento de toque / botão chega no SO < 1 ms
Dish monta + criptografa pacote de 12 bytes < 0.1 ms
Envio UDP + tempo de ar Wi-Fi + recebimento UDP 1–4 ms (5 GHz)
Satellite verifica + injeta via ViGEmBus < 0.5 ms
Jogo faz polling do novo estado do controle 0–4 ms (depende do jogo)
Frame renderizado no monitor 0–16 ms (depende dos fps)
Ponta a ponta (típico) ~6–25 ms

Para referência, controles Bluetooth normalmente adicionam 8 a 15 ms em cima de seus equivalentes com fio. O pior caso do Dish em uma LAN saudável é mais ou menos o melhor caso de um controle Bluetooth.

Como mantemos isso enxuto

  • Sem fila, sem runtime assíncrono. O handler do evento de entrada chama sendto inline. Sem fila produtor/consumidor, sem hop de event loop, sem Combine, sem coroutine de Kotlin. A thread de entrada é a thread de rede.
  • UDP puro. Sem TCP, sem WebSockets, sem gRPC, sem QUIC, nem mesmo NWConnection em plataformas Apple. Sockets POSIX puros para que possamos definir IP_TOS nós mesmos.
  • Marcação DSCP EF. Pacotes de saída são marcados com classe DSCP EF (0xB8). Roteadores e pontos de acesso com suporte a QoS os colocam à frente do tráfego em massa.
  • Pacotes minúsculos. 12 bytes de payload, cerca de 50 bytes no fio depois dos cabeçalhos UDP, IP e 802.11. Um frame de Wi-Fi.
  • Zero alocações no hot path. Buffers são de pilha ou pré-alocados. Nenhuma pausa de GC pode esticar um envio de pacote.
  • Injeção direta no kernel. O Satellite chama DeviceIoControl direto no ViGEmBus. Sem marshalling de DLL, sem IPC, sem ida e volta de serviço. O hot path do receptor são três syscalls com zero alocações: recvfrom()memcpy()DeviceIoControl().
  • Thread de recebimento de tempo crítico. O Satellite fixa sua thread de recebimento UDP em THREAD_PRIORITY_TIME_CRITICAL no Windows e em afinidade ao core 0, para que uma aba de navegador descontrolada não consiga esfomear os seus inputs.

O que come latência na prática

Todo relato de "Dish parece travado" que vimos rastreia para uma dessas. Nenhuma é culpa do Dish, todas têm solução:

  • Wi-Fi 2.4 GHz. Interferência de micro-ondas, congestão dos vizinhos, teto de banda mais baixo. Solução: coloque seu celular e seu PC gamer em 5 GHz ou 6 GHz.
  • Power-save de Wi-Fi no Android. Alguns celulares Android atrasam pacotes de saída em 30 a 100 ms quando a tela fica parada. O Dish mantém um heartbeat a cada 2 segundos: pequeno o bastante para ser de graça no rádio, frequente o bastante para impedir que o SO coloque o chip Wi-Fi para dormir.
  • Repetidores mesh. Cada hop adiciona 1 a 3 ms. Coloque o Satellite em um nó que esteja cabeado no roteador principal se possível.
  • Dock USB-C com Ethernet em um hub Thunderbolt. Alguns docks baratos fazem buffer. Use o Wi-Fi nativo do notebook ou um adaptador passthrough.
  • 120 Hz vs 60 Hz. Um monitor de 120 Hz reduz pela metade a pior fatia de frame time do orçamento. 8 ms de graça.

Quer medir você mesmo?

A UI web do Satellite em localhost:9877 mostra RTT ao vivo por conexão, os últimos microssegundos de loop, o pico de microssegundos de loop, contadores de sucesso e falha de submissão e contagens de descartes por replay. A página /debug é a leitura completa de performance do receptor. Se você ver qualquer coisa acima de 10 ms de RTT no mesmo Wi-Fi, alguma outra coisa na sua rede está agindo de forma estranha. Abra uma issue com um screenshot da página de debug e te ajudaremos a investigar.