↓Skip to main content
  1. Posts/

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:

SistemaCore
SNESsnes9x_libretro.so, snes9x2010_libretro.so
Genesis / Mega Drivegenesis_plus_gx_libretro.so, picodrive_libretro.so
PlayStation 1pcsx_rearmed_libretro.so
Game Boy / GBCgambatte_libretro.so
Game Boy Advancemgba_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 #

CamadaMudançaEfeito
FrontendAppImage do ES-DE substituindo o EmulationStation do RetroPieFrontend funcionando no Ubuntu 26.04 / GCC 15
Contexto GL do ES-DEMESA_GL_VERSION_OVERRIDE=3.3 + MESA_GLSL_VERSION_OVERRIDE=330Panfrost satisfaz o requisito GL 3.3 do ES-DE
Freeze Waylandunset WAYLAND_DISPLAY no launcher do ES-DERetroArch usa contexto X11; sem freeze silencioso
ES-DE + resolução VNCAguardar conexão VNC, então xrandr --auto antes do lançamentoResolução correta em cada conexão remota
Driver de vídeogl → vulkanMenor overhead de CPU por frame
Vídeo em threaddesligado → ligadoEmulador + renderizador em núcleos A76 separados
Profundidade do swapchain3 → 2Um frame a menos de buffering de display
Frame delaymanual → autoRetroArch auto-ajusta ao deadline de vsync
Hard syncdesligado → ligadoElimina fonte de lag CPU/GPU
Latência de áudio64 ms → 32 msReduz o delay de áudio à metade
Run-aheaddesligado → ligado (1 frame)~16 ms de redução de input lag
Afinidade de CPUnenhuma → taskset -c 4-7RetroArch permanece nos núcleos A76 big
Controle Xboxjoypad_autoconfig_dir apontado para perfis do RetroPieDetectado automaticamente ao iniciar
Cores de emuladorSymlinks do RetroPie para o diretório de cores do RetroArchES-DE consegue lançar todos os sistemas
Organização de ROMsScript bash → subpastas temáticas + atualização do gamelist.xmlBiblioteca 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.