RP2350에서 문자열마다 JIT 코드를 찍어 픽셀당 6사이클로 비트맵 텍스트 그리기
- Raph Levien이 RP2350 기반 레트로 컴퓨터 vic-2350에서 프레임버퍼 대신 빔 레이싱을 택해 스캔라인마다 화면을 채우고, 픽셀당 약 6 사이클 예산 안에서 텍스트를 그리는 방법을 공개함
- Rust로는 성능이 안 나와서 Cortex-M33용으로 직접 만든 언어 symbolasm으로 렌더러를 작성했고, 문자열마다 최적화된 코드를 즉석에서 생성하는 JIT로 속도를 더 끌어올림
- 디스플레이 리스트를 더블 버퍼링하고 y0 기준으로 정렬해 두었다가, 스캔라인마다 활성 리스트에 객체를 넣고 빼면서 render/start/end 메서드를 호출하는 구조임
- 텍스트 객체는 문자열(문자당 1바이트), 폰트 포인터, 전경색과 배경색만 담고 있으며, 비트맵 폰트를 고른 이유는 효율과 함께 저해상도에서 흐릿한 안티에일리어싱보다 또렷하게 설계된 글꼴이 보기 좋기 때문임
- RP2350의 SRAM은 520kB뿐이라 프레임버퍼를 두면 해상도나 비트 깊이를 줄여야 하고 램을 앱에 남겨두기 어려워, 빔 레이싱이 낮은 지연을 얻는 대신 렌더러에 강한 성능 제약을 걸음
Hacker News 의견들
1996년에 출원된 US-6377261-B1 특허가 딱 이 얘기임. 글리프를 Load Immediate / Shift / OR to Memory 명령열로 컴파일해서 폰트 비트맵을 안 읽어도 되게 하는 방식이고, 예전에 Mac용 FrameMaker에 쓰였대.
그 특허 Figure 6이랑 8-12를 보면 재밌음. 문자마다 시프트 횟수를 미리 아는 JIT 함수를 여러 개 찍어두면 Shift 명령 자체를 없앨 수 있어서 최적화 여지가 더 생김.
DOS 게임 쪽에선 그걸 sprite compilation이라고 불렀음. 스프라이트 비트맵을 보고 화면에 그리는 어셈블리를 뽑아서 게임에 넣는 방식인데, 486도 없이 Tandy 1000 쓰던 시절엔 그게 답이었지.
나는 BASIC32라는 레트로 BASIC 인터프리터를 마이크로컨트롤러용으로 만드는데, 이 방식의 대가는 read를 포기하는 거임. 화면이 write-only가 돼서 픽셀 색 읽기나 flood fill 같은 게 안 됨. 그래서 그냥 프레임버퍼로 갔고, RP2350은 램이 KB 단위라 해상도와 비트뎁스를 확 낮춰야 하는데 ESP32-S3의 4MB나 8MB 램이면 훨씬 편해짐.
'race the beam'을 오늘 내 사전에 추가함. LLM이 안 쓴 글을 읽는 게 이렇게 반가울 줄 몰랐음.
빔 레이싱 자체를 다룬 MIT Press 책도 있음. 찾아보면 재밌을 듯.
CoreText 대체 구현을 만드는 중인데 비슷한 걸 했음. 내 코드는 JIT로 atlas에 렌더링하는 게 p100에서 CoreText가 JIT 없이 p50으로 렌더링하는 것보다 빠르고, exact cover 레퍼런스 대비 품질도 좋음.
dot-product kerning 트릭이 제일 계속 눈에 밟힘. 글리프 가장자리 프로파일을 비트벡터로 인코딩하고 겹치는 popcount로 자간을 정하는 발상이 문제를 진짜 이해해야 나오는 종류의 hack임. 다만 패킹이 많아져서 절약분보다 비용이 커지는 지점이 어디일지 궁금함.
Atari 8-bit 디스플레이 리스트랑 HP PCL 5e 인터프리터(짧은 래스터 밴드 버퍼) 만들던 때가 생각남. PCL 쪽은 객체를 최대 255픽셀 높이로 잘라서 역 max-y 순서로 그렸고, 빔이 아직 Y-buffer_height를 그리는 스캔라인을 지나야 그릴 수 있었음. Jay Miner가 만든 Antic 디스플레이 리스트는 정말 잘 만든 시스템이었음.
레이싱 더 빔은 128바이트 램 달린 6507을 쓰는 Atari VCS/2600 같은 램 빈곤 머신의 수명을 늘려준 기술이지. 램도 거의 없이 소프트웨어 조금으로 전자빔을 제어한다는 게 신기함.
Windows 3.1 BitBlt도 아마 JIT이었을 거임. src/dest op, bits-per-pixel, linear/planar 프레임버퍼 조합별로 템플릿에서 블리터 루틴을 만드는 방식이었을 거고. symbolasm도 흥미로워 보임.
벡터 폰트를 최대한 적은 바이트로 넣는 프로젝트를 했는데, Claude를 붙여서 ANS coder 쓰고 렌더 오류만 없으면 lossy해도 된다고 했더니 문자당 40바이트 정도로 됐음. 그런데 웹사이트들은 아직도 커스텀 폰트 몇백 KB씩 내려받게 함.