- Adauto Meira/
- Posts/
- Orange Pi 5 Plus Configuração de Emulação: ES-DE, RetroArch, Cores e Organização de ROMs/
Orange Pi 5 Plus Configuração de Emulação: ES-DE, RetroArch, Cores e Organização de ROMs
Este post cobre a stack de emulação. A configuração base do SBC — problemas com a fonte de alimentação, ajuste de governor e a infraestrutura de acesso remoto VNC/SSH — é coberta no post complementar: Orange Pi 5 Plus: Diagnosticando Hard Resets e Configurando Acesso Remoto.
Capítulo 1: Frontend — ES-DE #
Por que não o EmulationStation? #
O RetroPie vem com sua própria build do EmulationStation, mas ela falha ao compilar no
Ubuntu 26.04 com GCC 15. O padrão C++ mudou e o código antigo do ES nunca declarou
CMAKE_CXX_STANDARD 11, fazendo os headers do Boost saírem com um hard error. O ES-DE
(EmulationStation Desktop Edition) é o fork ativamente mantido e distribui AppImages
pré-compilados para aarch64 — sem etapa de compilação.
Instalação #
ES-DE 3.4.1 baixado como um AppImage aarch64 (~127 MB). Colocado em
~/ES-DE_aarch64.AppImage e a entrada de autostart do RetroPie substituída por um
autostart do ES-DE em ~/.config/autostart/es-de.desktop.
Todos os lançamentos do ES-DE passam pelo ~/es-de-launch.sh (detalhado abaixo) para
que as variáveis de ambiente e a lógica de espera do VNC sejam aplicadas de forma
consistente, seja pelo autostart, menu de aplicativos ou terminal.
Correção do contexto GL: GLXBadFBConfig #
O ES-DE travou imediatamente no primeiro lançamento:
Error creating OpenGL context. Could not create GL context: GLXBadFBConfig
O ES-DE exige um contexto de perfil core do OpenGL 3.3. O driver Panfrost da Mesa no Mali-G610 só anuncia OpenGL 3.1 por padrão. Solução: dizer ao Panfrost para declarar 3.3 via variáveis de ambiente definidas no wrapper de lançamento.
Freeze do contexto Wayland #
Com o ES-DE rodando, lançar um jogo travava após cerca de 10 segundos com uma saída limpa e sem erro. Habilitar o log do RetroArch revelou:
GL context: "wayland"
Failed to specify monitor for fullscreen, letting compositor decide
Found display server: "null"
O RetroArch estava usando um contexto EGL Wayland mesmo com a sessão rodando em X11. O
compositor Wayland derrubava a janela fullscreen após alguns segundos. Desconfigurar
WAYLAND_DISPLAY antes de lançar força o RetroArch a usar um contexto EGL X11. Após
isso, o log mostrou GL context: egl_x e display server: x11 — jogos rodaram sem
travar.
Sincronização de resolução do ES-DE com VNC #
Ao rodar remotamente, o ES-DE iniciava antes que o cliente VNC se conectasse e enviasse
sua resolução de monitor. Ele travava na geometria inicial da sessão e transbordava a
tela. A solução: aguardar até detectar uma conexão VNC ativa na porta 5902, então
executar xrandr --auto para captar a resolução do cliente, e só então lançar o ES-DE.
O wrapper de lançamento completo #
~/es-de-launch.sh combinando as três correções:
#!/bin/bash
export MESA_GL_VERSION_OVERRIDE=3.3
export MESA_GLSL_VERSION_OVERRIDE=330
unset WAYLAND_DISPLAY
# No display VNC (:2), aguarda o cliente conectar e enviar sua resolução
if [ "$DISPLAY" = ":2" ]; then
until ss -tnp | grep -q "ESTAB.*:5902"; do
sleep 1
done
xrandr --auto
fi
~/ES-DE_aarch64.AppImage
Em um display físico (:0) o bloco if é ignorado e o ES-DE inicia imediatamente.
Capítulo 2: Ajuste do RetroArch #
Todos os cores neste build são renderizados por software: SNES, NES, GBA, Genesis, PS1, família Game Boy, FBNeo, MAME, PC Engine, Atari. Nenhum core usa renderização por hardware internamente, então todas as mudanças abaixo são seguras para toda a biblioteca.
Config: ~/.config/retroarch/retroarch.cfg
Driver de vídeo: GL → Vulkan #
video_driver = "vulkan"
Para cores renderizados por software, a GPU apenas exibe o frame final na tela. O Mali-G610 tem os drivers Mesa Panfrost (OpenGL) e panvk (Vulkan). O menor overhead de CPU do Vulkan significa mais tempo A76 para o emulador em vez do driver de display.
Vídeo em thread #
video_threaded = "true"
Desacopla a thread de renderização da thread de emulação. Em um SoC big.LITTLE, isso permite que emulador e renderizador rodem em núcleos A76 separados em vez de compartilhar um.
Profundidade do swapchain: 3 → 2 #
video_max_swapchain_images = "2"
Remove um frame de profundidade da fila. A placa suporta sem tearing.
Frame delay e hard sync #
video_frame_delay_auto = "true"
video_hard_sync = "true"
video_hard_sync força a CPU a aguardar a GPU terminar o frame anterior antes de
avançar — eliminando uma fonte de lag variável. video_frame_delay_auto auto-ajusta o
timing de submissão para comprimir o máximo possível até o deadline de vsync.
Latência de áudio: 64 ms → 32 ms #
audio_latency = "32"
Reduz o buffer de áudio à metade. A stack ALSA suporta sem underruns.
Run-ahead (1 frame) #
run_ahead_enabled = "true"
run_ahead_frames = "1"
run_ahead_secondary_instance = "true"
O run-ahead emula especulativamente um frame à frente, rebobina e envia o input para o frame correto. Efeito líquido: ~16 ms de redução de input lag a 60 Hz. O método de instância secundária é mais preciso que o método de save-state e compatível com todos os cores aqui.
Capítulo 3: Pinagem de Afinidade de CPU #
Os núcleos A76 big (cpu4–7) a 2,4 GHz lidam com toda a emulação. Os núcleos A55 little (cpu0–3) cuidam do OS e serviços em background. Pinar o RetroArch no cluster big impede o kernel de migrar uma thread de emulação quente para um núcleo lento mid-frame.
/usr/local/bin/retroarch é um wrapper (era anteriormente um symlink):
#!/bin/bash
exec taskset -c 4-7 /opt/retropie/emulators/retroarch/bin/retroarch "$@"
Para reverter para um symlink simples:
ln -sf /opt/retropie/emulators/retroarch/bin/retroarch /usr/local/bin/retroarch
Capítulo 4: Controle Xbox #
O controle Xbox One foi pareado via Bluetooth e estava visível para o OS via
jstest-gtk, mas o RetroArch mostrava “Xbox Wireless Controller not configured.”
O RetroArch procura perfis de autoconfig de joypad em
~/.config/retroarch/autoconfig/. Esse diretório não existia. Os perfis que vêm com o
RetroPie ficam em outro lugar. A solução foi apontar joypad_autoconfig_dir no
retroarch.cfg para o diretório de autoconfig do RetroPie. Na próxima inicialização, o
RetroArch encontrou o perfil do Xbox e configurou o controle automaticamente, sem
nenhum mapeamento manual de botões.
Capítulo 5: Cores de Emulador #
O RetroPie instala os cores em /opt/retropie/, mas a integração RetroArch do ES-DE
espera encontrá-los em ~/.config/retroarch/cores/. A solução é usar symlinks.
Aplicado para:
| Sistema | Core |
|---|---|
| SNES | snes9x_libretro.so, snes9x2010_libretro.so |
| Genesis / Mega Drive | genesis_plus_gx_libretro.so, picodrive_libretro.so |
| PlayStation 1 | pcsx_rearmed_libretro.so |
| Game Boy / GBC | gambatte_libretro.so |
| Game Boy Advance | mgba_libretro.so, vba_next_libretro.so |
Padrões recomendados: snes9x em vez de snes9x2010 (a versão 2010 é um snapshot
congelado de baixo consumo para o Raspberry Pi original — desnecessário no RK3588). Para
GBA, mgba é o mais preciso; vba_next é um fallback para casos extremos.
Capítulo 6: Organização de ROMs #
Com um grande diretório plano de ROMs, navegar no ES-DE se torna impraticável. Um
script bash move os arquivos para subpastas temáticas e atualiza os caminhos no
gamelist.xml para preservar o histórico de jogo.
#!/bin/bash
ROM_DIR=~/RetroPie/roms/snes
GAMELIST=~/RetroPie/roms/snes/gamelist.xml
declare -A CATEGORIES=(
["Franchise A Games (USA)"]="TitleA1|TitleA2|TitleA3"
["Franchise B Games (USA)"]="TitleB1|TitleB2"
["Genre Games (USA)"]="Keyword1|Keyword2|Keyword3"
["Action Games (USA)"]="." # catch-all
)
for FOLDER in "${!CATEGORIES[@]}"; do
PATTERN="${CATEGORIES[$FOLDER]}"
mkdir -p "$ROM_DIR/$FOLDER"
find "$ROM_DIR" -maxdepth 1 -name "*.zip" \
| grep -iE "$PATTERN" \
| while read -r ROM; do
BASENAME=$(basename "$ROM")
mv "$ROM" "$ROM_DIR/$FOLDER/$BASENAME"
sed -i "s|<path>./$BASENAME</path>|<path>./$FOLDER/$BASENAME</path>|g" \
"$GAMELIST"
done
done
Verifique o gamelist.xml para quaisquer caminhos de <image> ou <video> antes de
executar — se o scraping de metadados foi feito, esses também precisam de atualização.
Resumo #
| Camada | Mudança | Efeito |
|---|---|---|
| Frontend | AppImage do ES-DE substituindo o EmulationStation do RetroPie | Frontend funcionando no Ubuntu 26.04 / GCC 15 |
| Contexto GL do ES-DE | MESA_GL_VERSION_OVERRIDE=3.3 + MESA_GLSL_VERSION_OVERRIDE=330 | Panfrost satisfaz o requisito GL 3.3 do ES-DE |
| Freeze Wayland | unset WAYLAND_DISPLAY no launcher do ES-DE | RetroArch usa contexto X11; sem freeze silencioso |
| ES-DE + resolução VNC | Aguardar conexão VNC, então xrandr --auto antes do lançamento | Resolução correta em cada conexão remota |
| Driver de vídeo | gl → vulkan | Menor overhead de CPU por frame |
| Vídeo em thread | desligado → ligado | Emulador + renderizador em núcleos A76 separados |
| Profundidade do swapchain | 3 → 2 | Um frame a menos de buffering de display |
| Frame delay | manual → auto | RetroArch auto-ajusta ao deadline de vsync |
| Hard sync | desligado → ligado | Elimina fonte de lag CPU/GPU |
| Latência de áudio | 64 ms → 32 ms | Reduz o delay de áudio à metade |
| Run-ahead | desligado → ligado (1 frame) | ~16 ms de redução de input lag |
| Afinidade de CPU | nenhuma → taskset -c 4-7 | RetroArch permanece nos núcleos A76 big |
| Controle Xbox | joypad_autoconfig_dir apontado para perfis do RetroPie | Detectado automaticamente ao iniciar |
| Cores de emulador | Symlinks do RetroPie para o diretório de cores do RetroArch | ES-DE consegue lançar todos os sistemas |
| Organização de ROMs | Script bash → subpastas temáticas + atualização do gamelist.xml | Biblioteca navegável no ES-DE |
Uma Nota sobre Diagnóstico Assistido por IA #
Cada problema ao longo desta série de dois posts foi diagnosticado e resolvido em uma sessão com Claude (assistente de IA rodando localmente via Claude Code): a causa raiz da fonte de alimentação, a condição de corrida na reconexão VNC, o popup polkit, toda a configuração de roteamento de áudio, a falha de compilação do GCC 15, o mismatch de contexto GLX, o freeze do Wayland, o caminho de autoconfig faltando e o problema de timing de resolução do ES-DE.
Alguns parecem simples em retrospecto. O padrão de hard reset — sem kernel panic, sem log, acionado apenas por pressão na largura de banda de memória — é o tipo de coisa que parece um bug de kernel ou falha de hardware e te manda por longos caminhos errados por dias. O roteamento de áudio passou por várias iterações: streaming TCP pelo PipeWire, depois um sink de rede PulseAudio direto, antes de chegar na abordagem de PCM bruto pelo pipe SSH — cada uma com uma razão concreta para seguir em frente. O freeze do Wayland era uma saída limpa sem nenhuma mensagem de erro. O problema de resolução do ES-DE só aparecia em conexões VNC e apenas no primeiro lançamento, que é exatamente o tipo de bug de timing que desaparece quando você vai procurá-lo.
Ter um assistente que consegue ler logs, correlacionar resultados de stress tests, depurar simultaneamente no SBC e no lado do Windows, captar a falha de reinicialização do waveout que acontecia quando o pulseaudio.exe era reiniciado entre sessões, e sugerir o unset WAYLAND_DISPLAY ou o loop de polling de porta certos na hora muda a experiência completamente. Os problemas não ficaram mais simples — foram resolvidos mais rápido.