PSP 게임을 웹어셈블리로 변환해 브라우저에서 실행
원제 God of War on PSP, recompiled to WebAssembly and running in the browser
195 포인트댓글 104
Key Point
PSP 게임을 에뮬레이터 없이 브라우저에서 실행 가능하게 하는 재컴파일 기술의 구현 방식과 최적화 과정을 보여주며, 향후 다양한 레트로 게임의 웹 이식 가능성을 시사한다.
핵심 요약
- PSP 게임의 MIPS 머신 코드를 사전에 C++로 변환한 후 웹어셈블리로 컴파일하고, PSP 운영체제와 그래픽 칩을 웹GL2로 구현한 환경에서 브라우저에서 실행하는 기술이다.
- 첫 번째 타이틀 '갓 오브 워: 체인 오브 올림푸스'는 부팅부터 메뉴, 컷신, 전투까지 노트북에서 Chrome과 Firefox로 초당 60프레임으로 구동되며, PSP 해상도의 최대 4배인 1440×816에서 실행되고 휴대폰 터치 제어도 지원한다.
- 음악, 음성, 효과음은 작동하며 영화는 현재 스킵된다.
- 두 번째 타이틀 '갓 오브 워: 고스트 오브 스파르타'는 DRM 해독, 몇 개의 시스템 콜, 라이팅 수정만으로 구동되며 성능 최적화 없이도 기본 해상도의 3배에서 매초 55~60프레임으로 실행된다.
- 게임 데이터는 포함되지 않으며, 사용자가 소유한 디스크 이미지를 가져가서 저장소의 스크립트로 웹 페이지로 변환한다.
- 재컴파일 방식으로 PSPRecomp가 실행 파일을 분석해 함수를 찾아내고 16KiB 단위의 게스트 코드를 C++로 변환한다.
- PSP 커널은 스레드, 세마포어, 이벤트 플래그, 메모리 파티션, HTTP Range 요청으로 스트리밍되는 파일 시스템, 컨트롤러, 오디오 출력, 저장 데이터를 고수준 에뮬레이션으로 구현한다.
- 그래픽 처리 장치(GE)는 디스플레이 리스트를 디코딩하고 꼭짓점 형식, 스킨닝, 라이팅, 텍스처 생성, 클리핑, 역면 제거를 거쳐 WebGL로 렌더링하며, 프레임버퍼는 VRAM 위치별로 WebGL 렌더 타겟으로 변환되어 GPU에 유지된다.
- PSP처럼 그래픽 칩이 한 프레임의 디스플레이 리스트를 처리하는 동안 CPU가 다음을 준비하도록, GE와 WebGL 컨텍스트는 OffscreenCanvas가 있는 워커 스레드에서 실행되며 명시적 동기화 호출에만 대기한다.
- 사운드는 PSP의 음성 신디사이저를 재구현한 32개 ADPCM 음성(피치와 ADSR 엔벨로프 포함), FFmpeg 디코더로 디코딩된 ATRAC3+ 스트림, 그리고 AudioWorklet으로 믹싱된다.
- 초기 빌드는 초당 6프레임이었으나, 게임이 프레임버퍼를 수직 귀선 간격을 기다리지 않고 교환해 화면에 도달하는 매 프레임마다 약 8프레임을 그렸으므로, 수직 귀선 내 두 번 교환을 다음 귀선까지 대기하게 하니 작업이 8배 감소했다.
- WebAssembly에서 std::chrono 시계 읽기가 성능을 저하시켰고, 프로파일링이 활성화될 때만 performance.now()를 읽도록 변경했으며, Firefox의 4MB 인덱스 버퍼 공유로 초당 3프레임 저하가 각 드로우마다 작은 버퍼로 해결되었고, 모바일에서는 조각 단위 쓰기가 GPU 드라이버 대기를 유발했다.
- PSP의 스텐실 버퍼가 프레임버퍼 알파 채널에 있어서 전체 화면 패스로 미러링하면 초당 6천만 픽셀이 소비되었으나, 실제로 변경된 영역만 추적해 약 7백만 픽셀로 감소시켰다.
- 두 번째 게임은 ZIP 파일에서 별도 스크립트나 재컴파일 없이 구동되었으나, PSP의 DRM 플래그가 있는 파일을 읽으므로 KIRK 암호화 엔진의 AES-128 복호화를 구현했으며, 하늘이 하얀 문제는 전역 주변 빛의 알파 불투명도를 라이팅 코드에 추가해 해결했다.
- 게임을 이식하려면 git, CMake, Ninja, C++20 컴파일러, Python 3이 필요하며 Linux에서 테스트되었고, 게임당 약 2GB 디스크 공간이 필요하며 port.sh 스크립트가 몇 분 내에 ISO를 웹 페이지로 변환한다.
- 소매 디스크의 실행 파일은 암호화되어 있어 DecEboot나 pspdecrypt 같은 복호화 도구나 PPSSPP 게임 실행 중 덤프로 평문 ELF를 얻어야 한다.