English · Português (Brasil)
God of War II HD (PS3, NPUA80491) running natively on Apple Silicon:
no emulator, no JIT. The PowerPC (PPU) and Cell SPU code is translated ahead
of time into C/C++, compiled with clang for arm64, and runs on a
reimplementation of the PS3 OS and libraries with Metal for graphics,
VideoToolbox for cutscenes and CoreAudio for sound.
This repository contains no game code and no game assets. Like Dusk, you bring your own legally dumped copy, and the setup kit decrypts, translates and builds it on your machine.
![]() |
![]() |
![]() |
![]() |
Captured from the native Metal renderer on an M-series Mac (1280×720 internal, MetalFX upscaled).
| Area | State |
|---|---|
| Boot, intro videos, logos | ✅ natural path, videos decoded by VideoToolbox |
| Main menu, New Game, saves | ✅ memory-card saves on disk |
| Rhodes gameplay (combat, HUD, particles, lighting) | ✅ 45–60 fps on Apple Silicon (Mac, Metal; measured numbers in Estado por plataforma e desempenho) |
| Colossus of Rhodes fight | 🟡 playable; the bronze drape on its shoulder is skinned with a sheared bone matrix (under investigation) |
| After the Colossus cutscene | ✅ fixed 2026-09-22: a lifted SPU program (spu1) was writing into the PPU code segment, which produced ICALL-BAD 0x80029F47; validated with 305 s of New Game → gameplay, 0 ICALL-BAD (log) |
| iOS (iPhone) and Android (tablet) | 🟡 experimental; see Estado por plataforma e desempenho (pt-BR) |
| Audio | 🟡 music/SFX through the SCREAM mixer SPU program; clock still partly host-driven |
| Controllers | ✅ DualShock 4 / DualSense / Xbox / MFi via GameController, keyboard + mouse |
The engineering log (every wall, the hypotheses refuted by measurement and the
fixes) lives in docs/ and in the engine repository.
EBOOT.ELF (your copy, decrypted)
│
├── PPU ── ppu_lifter.py ──► ~650 C++ chunks, 69k functions ──┐
│ (functions.json: curated function bounds, │
│ jump tables, mid-function entry points) │
│ ├─► clang -O1/-O2 arm64 ─► g2play
└── SPU ── spu_lifter.py ──► SPU jobs / SPURS policy modules ─┘ │
▼
ps3recomp runtime (MIT): LV2 syscalls, PPU threads,
lwmutex/lwcond, SPURS, FIOS, cellGcm → RSX FIFO walker
→ Metal backend (MSL from RSX VP/FP), cellVdec → VideoToolbox,
cellAudio → CoreAudio, cellPad → GameController
What makes a PS3 title hard to recompile, and what this port had to solve:
- Cell B.E.: one PPU plus up to six SPUs with their own ISA and 256 KB local store; SPU programs (SPURS workloads, job chains, policy modules) are lifted separately and scheduled on host threads.
- Indirect control flow: 64-bit PowerPC function descriptors (
.opd/TOC), vtables, computedbctrjump tables and mid-function entries, all of which the lifter must resolve statically. - RSX: the command FIFO (with CALL/JUMP, wait points and ring wraps) is walked on the host, and the NV40-class vertex/fragment programs are decompiled to Metal Shading Language.
- Faithfulness over shortcuts: fixes follow console behaviour (RPCS3 is used as a measurement oracle via its GDB stub); diagnostics are gated off by default.
GoW2 Recomp.app is a native SwiftUI launcher in the spirit of Dusk:
point it at your own EBOOT.ELF and USRDIR, pick graphics/audio/control
options, manage mods and play. Build it with:
launcher/macos/build_app.sh # -> ./GoW2 Recomp.app
- No Python, no interpreter: validation, config, autosave checks (SHA-256 per file) and mod import run in Swift inside the app.
- RPCS3-style patches: the launcher computes your executable's RPCS3 PPU
hash by streaming the ELF (for NPUA80491 01.00 it is
PPU-31e32090ea333902dbf322c24487bab7e8c8d0d1), reads RPCS3'spatch.yml(anchors, aliases, configurable values) or any.ymlyou import, and passes the enabled ones to the runtime (PS3_PATCH_FILE). Data patches (e.g. aspect ratio) take effect; code patches are flagged, because the code was already recompiled and would need a re-lift. - Design system in
launcher/macos/MASTER.md.
A Dusk-style kit: point it at your own game
folder (PS3_GAME, from RPCS3's dev_hdd0/game/NPUA80491 or your PS3) and
your license (UP9000-NPUA80491_00-GODOFWARIIHDUS00.rap), and it builds the
native binary on your Mac:
./kit/setup.sh /path/to/PS3_GAME # finds the .rap in RPCS3's exdata or ~/Downloads
./jogar_g2.shsetup.sh does the following:
- It decrypts
EBOOT.BINitself, the same way RPCS3 does (enginetools/unself). - It recompiles the PPU code with the pinned lifter, the patch scripts and a small delta.
- It recompiles the seven SPU programs.
- It extracts the movies from your
gow2.psarc. - It links
./g2play.
Every generated file is checked against the hashes in kit/. The result is
byte-identical to the build the project tests. Details are in
kit/README.md.
Controls: WASD move, mouse camera, Space/E/J/K = ✕/○/□/△, Enter = Start, F11 or Cmd+Enter = fullscreen, Esc releases the mouse.
| Project | Platform | Approach | Notable result |
|---|---|---|---|
| N64Recomp | Nintendo 64 (MIPS) | Static recompilation to C | Zelda 64: Recompiled and many more; modding framework |
| XenonRecomp | Xbox 360 (PowerPC) | Static recompilation to C++ | Unleashed Recompiled, the first 360 recomp playable start to finish |
| ReXGlue | Xbox 360 | Static recompilation | Tooling for 360 ports |
| PS2Recomp | PlayStation 2 (MIPS R5900) | Static recompilation | Early stage |
| psprecomp | PSP (Allegrex MIPS) | Static recompilation to C | Toolkit |
| ps3recomp | PlayStation 3 (PPU) | Static recompilation runtime | The base of this port's engine |
| This port | PlayStation 3 (PPU + SPU) | Static recompilation, PPU and SPU, native Metal | A commercial PS3 title in gameplay on macOS/arm64 |
| Dusk / Dusklight | GameCube | Decompilation (source rebuilt by hand) | Twilight Princess native port; you supply the ISO |
| RPCS3 | PlayStation 3 | Emulation (LLVM JIT/AOT) | The reference used here as an oracle |
Catalogs: Recompendium, decompilation & recompilation list.
Static recompilation sits between emulation and decompilation: like a decompilation it produces native code with no interpreter or JIT (so it can use native APIs such as Metal directly), but like an emulator it needs no hand-written game source. It only needs the original binary, which is why the user has to supply it.
The root holds only entry points; everything else lives in a folder with one
purpose. The full map (with the old root path of every moved script) is
scripts/README.md, and scripts/check_layout.sh keeps
the root clean.
gow2-recomp/
├── jogar_g2.sh rodar_gow2*.sh testar_fix.sh gow2_launcher.py # launchers
├── env_gow2.sh # environment recipe (sourced by launchers, smokes, iOS/Android builds)
├── build_macos.sh # Mac build; compiles boot_macos.cpp, gow2_boot.h, host_gow2_*, movie_eos_arm.*, gow2_overlay_provider.*
├── functions.json # curated PPU function bounds (truncation repairs, mid-function targets)
├── apply_all_patches.sh verify_lift.sh accept_relift.sh # lift pipeline entry points
├── <old paths> # deprecated wrappers to the moved scripts (until 2026-12-31)
├── recomp_mid_v2/ # hand-written glue: SPU registration, overrides, mid-asm hooks, patch_*.py (survive re-lifts)
├── config/gow2_recomp.toml # lifter configuration (hooks)
├── hooks/ # function overrides and mid-asm hook sources
├── kit/ # bring-your-own-game installer (kit/setup.sh) and its release tooling
├── launcher/ ios/ android/ # macOS launcher app, iOS app, Android build
├── scripts/
│ ├── smoke/ # boot/intro/Metal smoke runs
│ ├── lift/ # relift gates, promotion, bisect, patch helpers
│ ├── diag/ # Mac diagnostics on a running boot
│ └── archive/windows/ # the 2026 Windows bring-up scripts (machine-bound, not maintained)
├── tools/ # PKG/SELF extraction, RPCS3 source search
├── claude_runs/ # agent measurement runs (run for N s / until a capture, then sample)
├── tests/ lift_baseline/ # offline tests and the lift baseline
├── notes/ # dated investigation notes (+ artifacts/)
└── docs/img/ # README images
Excluded on purpose (see .gitignore): EBOOT.ELF, extracted/, *.psarc,
SPU images, lifted PPU/SPU sources, texture dumps and build output. Those are
derived from the game and are regenerated from your own copy.
God of War II is © Sony Interactive Entertainment. This project is not affiliated with or endorsed by Sony. It distributes no copyrighted game code or assets; you must own the game. The engine is MIT licensed (© sp00nznet and contributors).
O APK para Android é compilado localmente a partir do seu próprio dump e instalado somente no seu próprio aparelho; ele nunca é distribuído. As bibliotecas do FFmpeg dentro do app continuam sob a LGPL-2.1 e podem ser copiadas, modificadas e substituídas.
Port nativo de God of War II HD (PS3) para macOS/Apple Silicon por
recompilação estática: o código PowerPC e SPU do jogo é traduzido para
C/C++ e compilado para arm64, rodando sobre uma reimplementação do sistema do
PS3 com Metal, VideoToolbox e CoreAudio. Nenhum código ou arquivo do jogo é
distribuído: como no Dusk, você fornece sua própria cópia e o kit de
instalação (kit/setup.sh) descriptografa, traduz e compila tudo na sua máquina.
O APK para Android é compilado localmente a partir do seu próprio dump e instalado somente no seu próprio aparelho; ele nunca é distribuído. As bibliotecas do FFmpeg dentro do app continuam sob a LGPL-2.1 e podem ser copiadas, modificadas e substituídas.
Full technical history lives in the Claude project memory (ps3recomp-feasibility,
levas 13-17) and in ps3recomp/docs/.
Resumo das sessões de 2026-09-30 a 2026-10-02. Cada número diz onde foi medido e com quantas
corridas. Classes de evidência: in-boot no aparelho (jogo real no tablet ou no iPhone),
Mac (jogo real num MacBook; não vale para o tablet), offline/unit (testes sem o jogo) e
não exercitado. Os links apontam para o motor
(andrebrumdev/ps3recomp, ramo spurs-bringup).
| Plataforma | Backend | Provado | Não provado / em aberto |
|---|---|---|---|
| Mac (Apple Silicon) | Metal | in-boot: boot → menu → gameplay de Rodes; 44–52 fps p50 no gameplay num M5 na tomada antes do merge (3 corridas por braço, medição) | depois do merge de 2026-10-02 só houve 3 corridas com a máquina carregada (load 4,7–14): 26–42 fps, sem valor de comparação; o portão de ~45 fps com a máquina ociosa não foi refeito (merge) |
| iOS (iPhone 14) | Metal | in-boot em ramos anteriores: P2 de 2026-09-25 com p50 33 / p5 21 em 195 de 200 s de gameplay, 1 corrida, meta de p5 ≥ 25 não atingida (P2) | o spurs-bringup depois do merge não foi compilado nem rodado no iPhone |
| Android (Galaxy Tab S9 FE, Mali-G68) | Vulkan | in-boot: gameplay com toque virtual; p50 12 / p5 11 no APK padrão depois do merge (1 corrida); cinemática de abertura a 29–30 fps | a thread do RSX com fragmentos (PS3_RSX_FRAG) dá 17/15, mas continua desligada por padrão (revisão independente pendente) |
| Windows | D3D12 | histórico de bring-up (paredes A–G no CLAUDE.md do motor) |
não compilado nem exercitado nesta leva; lá PS3_RSX_FIFO, PS3_VDEC_ASYNC, PS3_MOVIE_IO e PS3_SPU1 continuam opt-in; a correção do recomp_mid_v2/build_d3d.sh (rsx_frame_notes.c no link) está só no gow2-recomp |
In-boot, APK do feat/gow2-android, tablet no USB, PS3_MUTE=1, 100 s de gameplay por
run_perf.sh --drive, salvo indicação. Ruído entre corridas de ~±1 fps, não caracterizado.
Gargalo medido: a thread principal (PPU + walker do RSX inline) a ~87–91 % de um núcleo; a GPU
fica ~9 % ocupada (GPU timing).
| Mudança | Resultado (p50 / p5) | n | Fonte |
|---|---|---|---|
Baseline → parar de reler memória Vulkan mapeada write-combined (rsx_core_index_fetch, 529f5962) |
9 / 8 → 11 / 10 | 1 por ponto | GPU timing |
+ as outras leituras mapeadas por draw (c0762b29, 929f607a) |
12 / 11 | 1 | idem |
Cache de pipeline persistente (551bc38e) |
pso_ms nos primeiros 60 s: 559 → 262 → 4 da 1.ª à 3.ª execução; fps 10/9 → 12/11 → 12/11 (a 1.ª execução é a mais lenta) |
1 série de 3 | idem |
Thread dedicada do RSX (PS3_RSX_THREAD=1) sozinha |
medianas 12 / 11 contra 12 / 11: sem ganho consistente; a Thread-2 cai ~15 pontos, a host:rsx gasta ~45 % de um núcleo e a PPU espera ~17 % de cada segundo no overflow |
3 pares | thread RSX |
Thread + fragmentos de 32 KB (PS3_RSX_FRAG=1, etapa 3) |
17 / 15 contra 13 / 11 (3 de 3 pares a favor; espera de overflow 156–207 → 0 ms/s); na ponta do merge, 17 / 15 em 1 corrida | 3 pares + 1 | FRAG |
| Interpretador SPU S0–S2 | CPU dos 6 workers SPURS −8 % (2368 → 2178 ms/s), interpretador −23 %, processo −5,4 %; fps igual (12 / 11); áudio sem regressão (3 pares com som) | PRE 5, S2 3 | SPU S0–S2 |
Dedup de comandos do replay Vulkan (PS3_VK_REPLAY_DEDUP, ligado por padrão) |
ON 12/11 e 12/11, OFF 12/10 e 11/10: inconclusivo, compatível com um ganho pequeno | 2 pares | GPU timing |
| Vídeo da cinemática de abertura | 29–30 fps estáveis com e sem som; os 5–6 fps de uma corrida anterior não se reproduziram (causa não identificada) | 2 | idem |
Medido sem ganho no tablet (não repetir sem motivo novo): número de workers SPURS (2, 3 ou 6:
9–10 fps, 1 corrida cada); afinidade nos núcleos grandes (PS3_ANDROID_BIG_CORES 1/2: 9/7 e 9/8;
o modo 3, PPU sozinha num núcleo, piora para 7/6); lift com -O2 (12/11, igual ao -O1, 2 corridas);
desvirtualização das chamadas indiretas (ON 12/11 e 12/11, OFF 12/11 e 13/12, 2 pares,
devirt);
a terceira leva de otimizações da thread principal (12/11 antes e depois); o dedup de
descritores/estado (acima, inconclusivo).
Jogo real, na tomada, janela oculta, mudo, 3 corridas por braço alternadas, janela de 60 s de gameplay (medição).
| Mudança | Resultado | Leitura |
|---|---|---|
| Interpretador SPU S0–S2 (Metal) | fps p50 48 [47..52] contra 44 [43..46]; workers −7 %, interpretador −12 %; áudio igual | ganho medido no Mac; no tablet o mesmo código não muda o fps |
| Thread do RSX (Vulkan via MoltenVK) | 58 [56..59] contra 46 [46..47] | o backend de produção do Mac é o Metal, e a thread recusa o Metal: não vale para o Mac distribuído |
PS3_RSX_DRAIN=1 PS3_GCM_FIFO_NOLOCK=1 (Metal) |
48 contra 48 | sem ganho; fica desligado |
| Desvirtualização das chamadas indiretas | −13 % de CPU por quadro (64,0 → 58,1 ms), 44 → 47 fps | medido no Mac; no tablet, sem ganho (devirt) |
A "regressão" de 10 fps no Mac de 2026-10-01 não era código: um binário de antes das promoções de ambiente rodou sem as 9 variáveis que ele ainda precisava (abaixo); o mesmo binário faz 48 fps com elas (bisect).
Conferidas no código do spurs-bringup. Sondas e experimentos ficam desligados por padrão.
| Variável | Padrão | O que faz | Estado |
|---|---|---|---|
PS3_RSX_THREAD=1 |
desligada | walker do FIFO do RSX numa thread própria (host:rsx) |
experimental; só com backend Vulkan (recusada no Metal, inexistente no Windows) |
PS3_RSX_FRAG=1 |
desligada | contexto padrão em fragmentos de 32 KB como a libgcm (etapa 3); só vale com PS3_RSX_THREAD=1 |
experimental; o maior ganho medido no tablet; revisão independente pendente |
PS3_TRACE_RSXT=1 |
desligada | sonda [RSXT]: tempos de walk/parse/draw, esperas, kicks |
sonda |
PS3_TRACE_SPU_INTERP=1 |
desligada | sonda [SPUISTAT] do interpretador SPU |
sonda; troca o laço rápido pelo instrumentado (spui_run_instr, ~56 ms/s mais caro no tablet): não mede o laço de produção |
PS3_SPU_INTERP=0 |
ligado (1) | volta a imagem 7 (mixer SCREAM) ao spu6 liftado | A/B |
PS3_TRACE_VDEC_PIPE=1 |
desligada | sonda [VDECPIPE], uma linha por segundo com filme tocando |
sonda |
PS3_VK_GPU_TIMING=1 |
desligada | tempo de GPU por passe ([GPUTIME]) |
sonda |
PS3_VK_PCACHE=0 |
ligado | =0 desliga o cache de pipeline em disco (precisa do diretório de cache) |
padrão ligado, provado no Mali-G68 |
PS3_VK_REPLAY_DEDUP=0 |
ligado | =0 desliga o dedup de comandos repetidos do replay Vulkan |
padrão ligado (ganho inconclusivo) |
PS3_VK_REPLAY_STATS=1 |
desligada | contadores [VKREPLAY] |
sonda |
PS3_ANDROID_BIG_CORES=1|2|3 |
desligada (0) | afinidade da PPU/SPU nos núcleos grandes | medido sem ganho; código mantido |
PS3_SPU_WORKERS=N |
nº de SPUs pedido pelo jogo | número de workers SPURS | medido sem ganho no tablet |
PS3_HOST_PROFILE=1 |
desligada | amostrador por SIGPROF ([HPROF]), só Linux/Android |
ferramenta de medição |
PS3_ICALL_DEVIRT=0 |
ligado | desliga os sítios desvirtualizados (só em lift construído com ICALL_DEVIRT=1, o padrão do build_macos.sh) |
A/B |
PS3_ICALL_DEVIRT_CHECK=1 |
desligada | recompara cada chamada desvirtualizada com o registro | diagnóstico |
PS3_RSX_DRAIN=1, PS3_GCM_FIFO_NOLOCK=1 |
desligadas | dreno extra do FIFO / walker sem o lock | medido sem ganho no Mac |
Variáveis do harness: PS3_MUTE=1 (sem som), PS3_TRACE_FPS=1 (linhas [FPS]),
PS3_WINDOW_HIDDEN=1 (janela oculta no Mac), PS3_TRACE_AUDIO=1 ([AUDIO]).
- Tablet:
bash games/gow2/android/run_perf.sh OUT --seconds 100 --drive --env PS3_MUTE=1 [--env PS3_HOST_PROFILE=1] [--env K=V ...].--drivechamaandroid/drive_gameplay.sh(toques reais poradbda tela inicial até o gameplay; para com um motivo em vez de girar);--timed N --drive-newgamedeixa a cinemática de abertura tocar (medição de vídeo). Oreport.mdsai doios/perf_report.py --tag ANDPERF. O script sempre apaga ogow2.override.envdo app e para o app ao sair. Osimpleperfnão funciona neste aparelho (cpu-cyclesnão suportado); usePS3_HOST_PROFILE=1. - Perfil por símbolo:
python3 tools/mobile/hprof_report.py gow2.log libmain.so --window S E --top 400 [--check](no motor). Recusa umlibmain.socom build-id diferente do log. - Mac:
claude_runs/run_metrics.sh BIN SHA256 LOG SECS [PS3_X=v ...]epython3 claude_runs/metrics_report.py LOG...; na tomada (pmset -g batt), mudo, sem outra instância do jogo aberta, e anotando a carga (uptime) no início. - Protocolo A/B: o mesmo binário/APK quando o braço for só uma variável; ≥ 3 pares
alternados; tablet no USB e Mac na tomada; mudo; dizer o n e as faixas (mín..máx); fps de
uma corrida só não é veredito. Antes de um A/B, a skill
changing-the-game-across-platforms. - Armadilha das 9 variáveis promovidas:
PS3_RSX_FIFO,PS3_VDEC_ASYNC,PS3_MOVIE_IO,PS3_SPU1,PS3_SPU6,PS3_GCM_CB,PS3_METAL_PER_DRAW_RT,PS3_METAL_PASS_MERGEePS3_METAL_GPU_DESWIZZLEviraram padrão no código (Mac/iOS; no Android as que valem lá). Oenv_gow2.she oclaude_runs/run_metrics.shdeste repositório não as exportam mais (gow2-recompb0a167d). Num ramo que contém as promoções isso é o certo; num binário de antes delas, essa receita roda o jogo em modo degradado (no Mac, 10 fps em vez de 48). Para medir um binário antigo, exporte as 9. No Windows,PS3_RSX_FIFO,PS3_VDEC_ASYNC,PS3_MOVIE_IOePS3_SPU1continuam lidas do ambiente.
O APK é compilado a partir do monorepo (motor + games/gow2), com este repositório como
GOW2_WORK (dados do jogo, recomp_macos_e435/, spu_lifted/):
cd ps3recomp # o monorepo (ou um worktree dele)
GOW2_WORK=../gow2-recomp bash games/gow2/android/build_android.sh # compila; última linha GOW2_ANDROID_APK=<apk>
bash tools/android/install_android.sh --serial <SERIAL> # só instala o build-android/gow2.apk
bash tools/android/install_android.sh --data --serial <SERIAL> # instala e copia EBOOT.ELF, USRDIR/ e movie_cache/- Pré-requisitos: rodar
./build_macos.sh recomp_macos_e435uma vez no Mac (aplica e confere os lifts SPU) etools/android/probe_transport.shuma vez com o aparelho. - Opções do lift:
LIFT_OPT=-O1(padrão;-O2medido sem ganho),FORCE_REBUILD_LIFT=1,ICALL_DEVIRT=0(lift sem desvirtualização; objetos em pasta separada). - Nas máquinas de desenvolvimento há dois atalhos locais, fora do git (
build-android/é ignorado):run_build2.shchama obuild_android.shacima e compila;run_install_apk.shchama oinstall_android.she só instala o APK que já existe. - O APK é compilado do seu próprio dump e instalado só no seu aparelho; nunca é distribuído. As bibliotecas do FFmpeg dentro do app continuam sob a LGPL-2.1 e podem ser copiadas, modificadas e substituídas.
- Mac:
./jogar_g2.sh(ou oGoW2 Recomp.app); build com./build_macos.sh(no Mac o padrão é Metal). Obuild_macos.shnão recompila a biblioteca do motor: rode antescmake --build build-macosnops3recomp. - iOS:
ios/build_ios.shassa oenv_gow2.shdeste repositório no app. Com o motor dospurs-bringup(que já tem as promoções) isso é coerente; com um motor anterior às promoções o app cairia no modo degradado descrito acima (leitura de código, não verificado no aparelho).
- gow2-recomp é o checkout físico de build/run: fica ao lado dos dados do jogo (não versionados) e dos launchers.
- ps3recomp (ramo
spurs-bringup) é o monorepo: o motor (runtime/,libs/,tools/) e uma cópia deste port emgames/gow2/. - A sincronização é por cópia, nos dois sentidos, conferida blob a blob. A última foi do
monorepo para cá em 2026-10-02 (Android, iOS, launcher, kit, testes:
port);
as anteriores foram daqui para o monorepo. O APK é compilado do
games/gow2do monorepo. Este README é igual nos dois lugares.
| Documento (motor) | Assunto |
|---|---|
| 2026-10-01 GPU timing e gameplay no Android | perfil da thread principal, leituras de memória mapeada, cache de pipeline, dedup, vídeo |
| 2026-10-01 desenho da thread do RSX | etapas 0–4, contrato de ordem e critérios de aborto |
| 2026-10-02 etapa 3 (fragmentos) | fragmentos de 32 KB, rodadas de revisão, mutantes |
| 2026-10-01 interpretador SPU | desenho do S0–S2 (sem JIT) |
| A/B thread do RSX, A/B FRAG, A/B SPU S0–S2 | medições no tablet |
| Mac com o ambiente certo, bisect do fps no Mac | medições no Mac e a armadilha das variáveis promovidas |
| Merge para o spurs-bringup | conflitos, matriz de paridade, verificação |
| PRs de Vulkan do upstream | leitura (sem execução) dos PRs #182/#192 contra o nosso backend |
Skill changing-the-game-across-platforms |
como levar uma mudança a Mac, iOS, Android e Windows com A/B e matriz de paridade |
- Revisão independente (Codex) da etapa 3 da thread do RSX: incompleta;
PS3_RSX_FRAGfica desligada por padrão até lá. Também falta comparar pixels com e sem FRAG. - iOS: o
spurs-bringupdepois do merge não foi verificado no iPhone. - Mac: o fps depois do merge não foi re-medido com a máquina ociosa.
- Windows: não compilado nesta leva; levar o
recomp_mid_v2/build_d3d.shdo gow2-recomp para o monorepo. - Tablet: o próximo ganho de fps está na thread principal (
vm_read*,ppu_rsv_*, decode de vértices, FIFO), não no SPU.
gow2_launcher.py (setup + mods + "Jogar" numa tela só) também repassa as
configurações do overlay para o jogo. Esta seção documenta o overlay em si
(controles, onde ficam as configurações, o que cada diagnóstico significa) e
como ele se encaixa no fluxo do launcher.
Importante: o overlay é uma UI do host. Ele abrir/renderizar na tela não
é prova de que o jogo (guest) está desenhando pixels reais — essa aceitação
é separada (ver a cadeia de paredes A-G no CLAUDE.md do motor). Um print com
o menu aberto inclui a UI por cima do frame do jogo.
- Abrir/fechar a central: botão PS/Home do controle (macOS, via
GameController), segure Select e aperte Start, ou F1 no teclado
(
toggle_key=2no arquivo troca para F2). A central sempre abre em "Voltar ao jogo" e o jogo continua rodando atrás dela, sem receber entrada. No Windows o botão PS/Home não é lido (XInput não o expõe): use Select+Start ou F1. - Navegação: esquerda/direita troca de cartão; baixo (ou X/Enter) entra no painel; cima/baixo percorre as linhas; X/Enter confirma; O/Esc volta um nível e, nos cartões, fecha. PS/Home, Select+Start e F1 fecham de qualquer lugar. Um botão que ainda estiver pressionado quando a central fecha só chega ao jogo depois de solto (o X que confirmou "Voltar ao jogo" não vira pulo; o Enter não vira pausa).
- Cartões: Voltar ao jogo · Tela (tela cheia, VSync, tamanho da janela) · Controles (zona morta 0–30 %, vibração do jogo, remapear botões, restaurar padrão) · Som (som do jogo, sons da interface, vibração da interface) · Desenvolvedor (FPS/tempo de quadro, draws do jogo, estado do WAD, shaders, Logs) · Sair (pede confirmação; as configurações já estão salvas).
- Remapear: escolha o botão do PS3 na lista e pressione o botão do controle que deve acioná-lo; o antigo dono troca de lugar (o mapa nunca fica com botões repetidos). Esc cancela; sem resposta em 5 s, cancela.
- Visual: no macOS o jogo fica desfocado e escurecido atrás da central
(Metal + MetalPerformanceShaders); no Windows só escurece. Texto em SF Pro
e ícones SF Symbols no macOS; Inter (OFL,
third_party/inter) e ícones geométricos no resto — ou se a fonte do sistema faltar. - Som do jogo (menu) e "Som ligado" (launcher,
PS3_MUTE) somam-se: o jogo só toca com os dois ligados. - Valores não medidos aparecem como "indisponível" — nunca como zero.
- O menu sempre inicia fechado.
Local padrão por usuário (mesma fórmula usada pelo launcher e pelo runtime):
- macOS:
~/Library/Application Support/ps3recomp/gow2/runtime-overlay.settings - Windows:
%APPDATA%\ps3recomp\gow2\runtime-overlay.settings - Linux:
$XDG_CONFIG_HOME/ps3recomp/gow2/runtime-overlay.settings(ou~/.config/ps3recomp/gow2/runtime-overlay.settingssemXDG_CONFIG_HOME)
Pode ser sobrescrito com a variável de ambiente PS3_OVERLAY_SETTINGS
(caminho explícito do arquivo). O gow2_launcher.py também aceita uma chave
opcional "overlay_settings" no user_config.json; vazia/ausente usa o
caminho padrão acima (configs antigas, sem essa chave, continuam funcionando
sem alteração). Se o arquivo estiver ausente, corrompido ou com um campo
inválido, o runtime cai de volta nos valores padrão desse campo (nunca
recusa iniciar por isso) — janela 1280x720, VSync ligado, mapeamento padrão.
Isso vale também para as chaves v2 (ui_sounds, ui_haptics, game_mute,
vibration): arquivos salvos por uma versão anterior do overlay, sem essas
chaves, carregam com os padrões (sons e vibração da interface ligados, som
do jogo ligado, vibração do jogo ligada).
Tela cheia e VSync têm uma fonte só: o arquivo de configurações do
overlay. O menu do jogo e o launcher SwiftUI (GoW2 Recomp.app) editam o
mesmo arquivo; o launcher não exporta mais PS3_FULLSCREEN/PS3_METAL_VSYNC
e passa o caminho em PS3_OVERLAY_SETTINGS. Na primeira execução do launcher
novo, um arquivo sem essas chaves recebe os valores que o launcher tinha
(padrão: ligados).
Um valor explícito ainda vence o arquivo: PS3_FULLSCREEN/
PS3_METAL_VSYNC definidos no ambiente de quem chamou (ou em "Avançado" no
launcher). O env_gow2.sh não força mais o VSync (só repassa o valor de quem
chamou). O jogar_g2.sh, o gow2_launcher.py e o launcher SwiftUI guardam o
valor do chamador antes de carregar o env_gow2.sh e removem a variável se
ele não definiu nada — nenhum padrão de script mascara o arquivo. O
jogar_g2.sh continua abrindo em tela cheia por padrão: se o arquivo
ainda não tem a chave fullscreen, ele grava fullscreen=1 antes de iniciar
(depois vale o que a central salvar; PS3_FULLSCREEN=0 ./jogar_g2.sh abre em
janela só naquela vez). Arquivos gravados antes desta mudança podem já ter
fullscreen=0: basta ligar Tela cheia na central uma vez.
Se o launcher SwiftUI não conseguir gravar o arquivo, ele mostra na tela "Não foi possível salvar as configurações: …" (com o motivo) em vez de fingir que o ajuste mudou.
| Ajuste | Metal (macOS) | D3D12 (Windows) |
|---|---|---|
| Tamanho de janela | aplica ao vivo | precisa reiniciar |
| Tela cheia | aplica ao vivo | precisa reiniciar (borderless no monitor primário, sem DPI awareness) |
| VSync | aplica ao vivo | aplica ao vivo |
A central marca cada ajuste que o backend atual não oferece como "indisponível", e cada um que só vale depois de reiniciar mostra "requer reiniciar" logo abaixo do nome — nenhum ajuste finge aplicar algo que só foi salvo.
No Windows, hoje só o caminho de bridge D3D12 do cellGcmSys.c inicializa o
overlay; não há ainda um provedor de diagnósticos de WAD/shader do GoW2
para Windows, então as linhas de WAD e de shader aparecem como
"indisponível" lá (o overlay em si funciona; só falta o adaptador de
diagnóstico, que hoje só existe para o boot_macos.cpp).
- FPS / tempo de frame: contagem real da apresentação do backend — só os flips do jogo (guest). Apresentações do filme/intro (que correm na thread de decodificação) não entram nessa mesma contagem.
- Draws do guest: número de draw calls do próprio jogo, replays contados
pelo backend — nunca inclui os draws do próprio menu do overlay. No Metal
com os render targets por draw (padrão do runtime) os registros de
clear e blit da mesma lista não entram na conta. No D3D12
esse número é limitado a
MAX_DRAWS=256por frame (registros reproduzidos, não o total real de draws se passar disso). - Shaders (hits/misses/compiles/failures): "compiles" tem definição diferente por backend — no Metal é o número de pares VS+FS (vertex+ fragment) compilados; no D3D12 é o número de compilações de fragment program bem-sucedidas. "Failures" conta decodificações/compilações que caíram para um caminho alternativo.
- Estado do WAD / filme: vem do caminho
movie_io(ligado quando o diretório movie_cache existe); WADs lidos por outro caminho (psarc/FIOS direto) não aparecem aqui. - "Indisponível" nunca é "zero": qualquer campo que o backend/provedor
ainda não mediu aparece como "indisponível" (unavailable), nunca como
0— um0no painel é sempre uma medição real de zero. - Log recente (aba Logs, separada da de Diagnóstico): um anel limitado em memória (64 entradas; a aba mostra o anel inteiro, a mais nova por último); sob contenção alguma linha pode ser descartada (o registro nunca bloqueia a thread de render/guest para garantir isso).
- Se o backend não conseguir criar o renderer do overlay (ou a textura de
fonte), o overlay se desliga sozinho para a sessão: F1/Select+Start não
fazem nada e nenhuma entrada é retida do jogo (aviso
overlay disabled: ...nostderr).
Arquivos user_config.json salvos antes do overlay continuam funcionando
sem qualquer alteração (a chave overlay_settings é opcional). Nenhum
comportamento de setup/extração/mods mudou.




