Skip to content

ZeroFG: geração de quadros (Zero e ReallyZero) com presenter independente, e SGSR1 Edge Direction - #121

Merged
phforner0 merged 3 commits into
phforner0:mainfrom
hy300leosquizz-ctrl:zerofg-1.1
Nov 9, 2026
Merged

phforner0 merged 3 commits into
phforner0:mainfrom
hy300leosquizz-ctrl:zerofg-1.1

Conversation

@hy300leosquizz-ctrl

Copy link
Copy Markdown
Contributor

Traz o ZeroFG para o Xendroid+. Ele gera o quadro do meio entre dois quadros do jogo, e a tela mostra o dobro do ritmo do jogo: 30 fps vira 60, 60 vira 120. Vem desligado por padrão, com dois modos:

  • Zero: o motor de qualidade. Reconstrói a cena em camadas: superfícies, o objeto em movimento mais próximo com o próprio recorte e uma camada de tela para o HUD. Tudo isso é conferido contra uma imagem de fluxo denso. É o melhor quando a câmera anda.
  • ReallyZero: o motor econômico. Fluxo óptico denso com uma passada de limpeza; custa uma fração do Zero e fica perto dele com a câmera parada.

O motor é a release pública v1.1.0 do ZeroFG (Apache-2.0), copiada arquivo por arquivo no primeiro commit, com a procedência em zerofg/SOURCE.md. O README do ZeroFG explica como os motores funcionam, estágio por estágio, e compara com o LSFG 3.1 e o win-fg clipe a clipe.

Placar no mesmo laboratório, com a mesma régua para todos. Conta-se o melhor dos dois modos contra o melhor das duas configurações do LSFG:

Clipes ZeroFG à frente Empate LSFG à frente Maior vantagem Maior desvantagem
Quadro inteiro, passo 2 25 15 2 8 +5,55 dB −0,58 dB
Só pixels em movimento 6 5 0 1 +5,70 dB −0,12 dB
Quadro inteiro, passo 4 5 4 0 1 +0,35 dB −0,22 dB

O win-fg fica atrás em todos os clipes das três tabelas. No Adreno 840, o ReallyZero custa 0,45 / 0,94 / 1,62 ms por quadro (640×448 / 1280×896 / 1920×1344), de 2,1 a 2,6 vezes menos que o LSFG com fluxo cheio.

Como liga e desliga

  • Configurações → Frame generation (ZeroFG): Off, ReallyZero ou Zero.
  • No jogo, menu Frame generation: as mesmas três opções, com o Manter / Desfazer / Tornar global de sempre.
  • Recomendado: pôr o limite de fps do jogo no ritmo dele (30 ou 60). Os quadros do jogo chegam uniformes, e os gerados também. A descrição da opção nas Configurações e na lista ao vivo do jogo diz isso.

Troca com o jogo rodando. O renderer desconecta e drena a sessão anterior, devolve a janela e monta o modo novo; nada é destruído no callback da UI do Android. Só um motor de frame generation roda por vez: escolher outro desliga o ZeroFG, e escolher o ZeroFG para o outro. O menu mostra separados o modo pedido e a saída que está de fato rodando.

Requisitos: Vulkan 1.3 com synchronization2 e os storage formats, mais o handoff de imagem e fence do Android (AHardwareBuffer e sync fd). Tudo isso é negociado quando o aparelho suporta, mesmo com o ZeroFG desligado, para ele poder ser ligado depois sem reiniciar o jogo. Aparelho que não qualifica continua na apresentação nativa, e o piso de Vulkan do Plus não muda.

O que muda no apresentador

O presenter independente do ZeroFG

O ZeroFG traz o próprio presenter, em vez de gerar dentro do presenter do Xenia. Foi escrito para ser portável: o mesmo presenter roda no XenDroid e no ARMSX2.

  • Um segundo dispositivo Vulkan (device B) na mesma GPU, ao lado do do Xenia (device A). O device A renderiza o jogo como sempre. Cada quadro novo é copiado para uma imagem AHardwareBuffer e passado ao device B com um sync fd (zerofg_device_handoff); o device B nunca espera na fila do A.
  • O device B é dono da janela enquanto o ZeroFG roda. Ele gera os quadros do meio e apresenta reais e gerados em ordem. O presenter do Xenia continua renderizando, mas entrega a superfície; e a recebe de volta no Off ou em qualquer falha.
  • Prioridade de GPU: o device B pede VK_QUEUE_GLOBAL_PRIORITY_HIGH. Com driver custom carregado pelo adrenotools, um gancho pequeno (kgsl_priority_hook.c) sobe a prioridade KGSL do contexto dele. Assim o trabalho curto do ZeroFG não fica na fila atrás de um jogo pesado.
  • Vblank do convidado: o VblankPacer do Plus continua. Com o ZeroFG ligado, um pull pode adiantar um evento de vblank, e paga com o seguinte; não adianta um segundo antes de pagar. Com o ZeroFG desligado, a grade nativa não muda.
  • Controles de imagem: escala, CAS, FSR, dither e filtro de cor do Plus rodam igual em quadros reais e gerados.

O ritmo (pacing)

Gerar o quadro do meio é metade do trabalho. A outra metade, a que mais custou, é mostrar cada quadro na hora certa sem atrasar o jogo. Não é perfeito, porque nada é, mas fica muito perto:

  • Segue o relógio real do jogo. O período do jogo é medido pelos horários de emissão dos quadros reais, não por um número fixo. Um calendário que ficou atrasado volta para uma folga pequena, e um adiantado drena pelo próprio ritmo. Isso sem pular quadro e sem tirar os reais de ordem (recuperação de fase).
  • Cada quadro na batida da tela. Cada present leva o horário pedido via VK_GOOGLE_display_timing, alinhado ao vsync do painel (quantizador de vsync), e o horário real de exibição volta pelo driver para a medição. A saída acompanha quando o sistema muda a taxa do painel, como o HyperOS baixando para 90 Hz.
  • Não segura o jogo. O jogo só espera o ZeroFG numa profundidade limitada de handoff. A espera do jogo pela própria GPU é medida à parte e nunca contada como freio do ZeroFG. Com a prioridade de GPU, a cópia de cada quadro com a GPU a 95–99 % caiu de 40–170 ms para 1,3 ms.
  • Vblank do convidado. Um quadro do jogo que atrasou pode puxar um vblank adiantado, pago com o seguinte, em vez de perder um refresh inteiro.

Medido com esse presenter no Adreno 840:

Jogo (host) Antes Com o pacing atual
Naruto, PPSSPP, 30 → 60 pares irregulares de 1–3 refreshes 2 · 2 uniforme
Resident Evil 4, ARMSX2 quadro do jogo na tela ~13 ms antes, 0–3 % dos gerados atrasados
Arkham City, XenDroid, limite 30 38 % dos intervalos em dois refreshes 79–89 %
Gears of War 3, Xendroid+, 30 → 60 ritmo uniforme, +46–48 ms sobre o Off na mediana
Halo 3, Xendroid+, limite 30 saída a 60, intervalos de 16,7 ms na mediana e no p90

Os contadores de invariantes do presenter (real descartado, real fora de ordem e outros) ficaram em zero em todas as janelas das sessões do Xendroid+.

Mudanças no presenter do Xenia

  • O IssueSwap registra a fronteira de present da fonte (RecordSourcePresent), que identifica cada quadro do jogo para o handoff e o ritmo.
  • Correção que vale também para a apresentação nativa: o Xenia nunca guardava o formato com que o pipeline de pintura da swapchain era criado. A checagem de formato então reconstruía o pipeline a cada pintura. Agora o formato fica guardado, e o pipeline só é reconstruído quando o formato da swapchain muda.
  • A entrega da janela e os votos de taxa de atualização seguem o dono da superfície, inclusive com saída para TV.
  • frame_stats.h guarda as estatísticas de cadência da fonte que o ritmo lê, com teste nativo.

SGSR1 Edge Direction

O filtro SGSR1 Edge Direction do XenDroid, com variante com dither, nas Configurações e no menu Imagem do jogo. Entra como valor 6 da escala: os valores existentes mantêm os índices e o filtro padrão não muda. O SPIR-V do SGSR1 e do SGSR1 + dither ficou idêntico byte a byte, e os dois shaders do Edge passam no spirv-val --target-env vulkan1.0.

Testado

  • Aparelho: Xiaomi 25102PCBEG (Adreno 840, Android 16), Turnip.
    • Gears of War 3, uma cena fixa, alternando Off → ReallyZero → Off → Zero → Off:
      • os quadros reais chegam à tela 46,5 ms (ReallyZero) e 47,8 ms (Zero) depois do Off, na mediana (Off: 26,8 ms);
      • o ritmo fica uniforme em 30 → 60;
      • a medida vai do IssueSwap ao tempo de apresentação informado pelo driver, não do controle até a luz da tela.
    • Halo 3 com limite de 30 fps, nos dois modos e trocando durante o jogo: saída a 60, com intervalos de 16,7 ms na mediana e no p90.
    • O build deste PR, Gears of War 3 em ReallyZero, ~4,5 min sem limite de fps:
      • depois do carregamento, saída a 60 com intervalos de 16,7 ms na mediana;
      • o calendário do presenter fica no ritmo do jogo;
      • zero espera do jogo pela própria GPU;
      • as quedas pontuais acompanham as quedas do próprio jogo (período de 36–38 ms).
    • O driver de fábrica da Qualcomm ainda não foi qualificado com o ZeroFG neste host; os testes foram no Turnip.
  • Build e testes: o APK de debug compila com JDK 21 e NDK 29.0.14206865. Os testes JVM cobrem o menu do jogo, Manter/Desfazer, o catálogo e o esquema das configurações e as strings em inglês e português. A checagem das declarações JNI e os testes nativos do vblank pacer e das estatísticas de quadro passam.
  • Em cima do main atual, com o System Link (Add System Link (LAN) play #115): o teste de inventário das configurações passa a contar também as duas opções de rede que ele trouxe. Elas entraram sem atualizar a contagem, e o terceiro commit corrige isso.
  • Outras GPUs: os motores foram medidos no Adreno 650 e numa Radeon Vega, no laboratório do ZeroFG. Mali e Xclipse não foram testados neste host. Os backends são escolhidos sozinhos, e o caminho Compat existe para eles.

Nota para jogos pesados: um jogo que se cadencia por vblank e está no limite da GPU (o Halo 3 nos trechos mais pesados) entrega quadros irregulares. Vem um atrasado e depois vários rápidos para recuperar, e o ZeroFG mostra alguns reais sem gerado entre eles. Pôr o limite de fps do jogo no ritmo dele (30 no Halo 3) deixa tudo uniforme. Está no docs/ZEROFG.md, com a leitura dos logs do xe.log.

Licença

O motor é Apache-2.0 (zerofg/LICENSE), e o THIRD-PARTY-NOTICES.md e a página de licenças do app ganham a linha dele. O presenter, o handoff e a cola com o Xenia vêm do mesmo projeto (Apache-2.0 na release pública do ZeroFG) e entram aqui como contribuição a este repositório. Os trechos derivados do Xenia mantêm o aviso BSD.

Adds ZeroFG frame generation: the screen shows twice the game's frame rate (30 → 60, 60 → 120).

  • ZeroFG frame generation with two modes, Zero (best picture) and ReallyZero (lowest cost), off by default.
  • Switch it from Settings or from the in-game Frame generation menu while you play.
  • Tip: set the game's frame rate limit to its own rate (30 or 60) for the most even motion.
  • New SGSR1 Edge Direction scaling filter.

🎞️ Frame generation

  • ZeroFG generates the frame halfway between two of the game's on its own Vulkan device and presents them in order. Zero rebuilds the scene as moving layers and leads when the camera moves; ReallyZero is a dense optical flow that costs under a millisecond per frame at 1280×896 on an Adreno 840.
  • Frame pacing follows the game's real clock and lines every frame up with the screen's refresh, so motion stays even.
  • Needs Vulkan 1.3; a device that does not qualify keeps the normal presentation.

✅ Tested on an Adreno 840 (Turnip) in Gears of War 3 and Halo 3.

🖼️ Image

  • SGSR1 Edge Direction, with an optional dither variant, in Settings and the in-game Image menu.

🛠️ Fixed

  • The swapchain paint pipeline is no longer rebuilt on every frame.

Chega o ZeroFG: a tela mostra o dobro do ritmo do jogo (30 → 60, 60 → 120).

  • Geração de quadros ZeroFG com dois modos, Zero (melhor imagem) e ReallyZero (menor custo), desligada por padrão.
  • Troque nas Configurações ou no menu Frame generation do jogo, sem sair da partida.
  • Dica: ponha o limite de fps do jogo no ritmo dele (30 ou 60) para o movimento mais uniforme.
  • Novo filtro de escala SGSR1 Edge Direction.

🎞️ Geração de quadros

  • O ZeroFG gera o quadro do meio entre dois quadros do jogo num dispositivo Vulkan próprio e apresenta tudo em ordem. O Zero reconstrói a cena em camadas e é o melhor quando a câmera anda; o ReallyZero é um fluxo óptico denso que custa menos de um milissegundo por quadro em 1280×896 num Adreno 840.
  • O ritmo segue o relógio real do jogo e alinha cada quadro com o refresh da tela, para o movimento ficar uniforme.
  • Precisa de Vulkan 1.3; aparelho que não qualifica continua com a apresentação normal.

✅ Testado num Adreno 840 (Turnip) no Gears of War 3 e no Halo 3.

🖼️ Imagem

  • SGSR1 Edge Direction, com variante com dither, nas Configurações e no menu Imagem do jogo.

🛠️ Corrigido

  • O pipeline de pintura da swapchain não é mais reconstruído a cada quadro.

🤖 Generated with Claude Code

hy300leosquizz-ctrl and others added 3 commits November 7, 2026 23:55
The frame-generation engine of ZeroFG's public release v1.1.0
(https://github.com/hy300leosquizz-ctrl/ZeroFG, tag v1.1.0, commit
2b7711e47680c25dd461110c182ee5dd4a637f99), blob for blob: public
headers, engine sources, shader sources, the compiled SPIR-V headers the
build uses (no shader tools needed) and the shader compiler script. The
host adapter in integration/xenia is the release's host/xendroid adapter.
SOURCE.md records the provenance and the mode mapping; LICENSE is the
engine's Apache-2.0. Nothing here is built until the next commit wires
it in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nter, and SGSR1 Edge Direction

ZeroFG generates the frame halfway between two of the game's, so the
screen shows twice the game's rate (30 -> 60, 60 -> 120). Off by
default; Zero (the layered quality engine) and ReallyZero (dense flow,
lowest cost) are chosen in Settings or in the in-game Frame generation
menu and switch while the game runs. Only one frame-generation engine
runs at a time.

- The ZeroFG Independent Presenter: a second Vulkan device on the same
  GPU (device B) takes each new game frame through an AHardwareBuffer +
  sync fd handoff, generates the middle frames and presents real and
  generated frames on the window while ZeroFG runs (main surface
  authority); Xenia's presenter gives the surface back to it and takes it
  back on Off or on failure. It paces from the game's real rate and keeps
  real frames in order. Device B asks for global priority HIGH, and with
  an adrenotools custom driver a KGSL hook raises its context priority.
- Xenia's presenter: IssueSwap records the Source present boundary; the
  swapchain paint pipeline now records the format it was created for, so
  it is no longer rebuilt on every paint (native presentation too);
  refresh-rate votes follow the window's owner; the Plus VblankPacer
  takes ZeroFG's guest vblank pull (one event ahead at most, repaid).
- Plus image controls run on real and generated frames alike.
- SGSR1 Edge Direction from XenDroid, with a dither variant, appended as
  scaling value 6; existing values and the default filter are unchanged.
- App: the setting, the in-game selector with Keep/Undo/Make global, a
  HUD readout that tells the requested mode from the running output, EN
  and pt-BR strings, tests; docs/ZEROFG.md.

Port by Zeromeia (ZeroFG's ChatGPT-powered collaborator), updated to the
v1.1.0 engine and reviewed by Zé Raio.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
System link (phforner0#115) added the Network section with Network|systemlink (a
switch) and Network|systemlink_peers (text) without updating the
inventory test: 176 entries, 120 switches, 3 text fields, and Network
after Kernel among the sections.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@phforner0
phforner0 merged commit 09edabe into phforner0:main Nov 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants