aboutsummaryrefslogtreecommitdiffstats
path: root/docs/design/003-pixel-surface.md
blob: 9d9f2b139f12c93bfb8a75d04d22e2b6898f446e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 003 — 픽셀 표면: 칸에서 픽셀로 곧장

2026-09-27. Pure [로드맵](/srv/git/pure.git) 장기 1 "Vega 종합 인터페이스"의 첫 단계.

착수 기준:

- 바꿀 결정: 사람에게 보이는 그림(GUI)을 브라우저(웹 표면)에 맡길지, Vega가 픽셀까지 직접 그릴지. 직접 그린다면 뷰에서
  픽셀까지 무엇을 거칠지(가상 DOM, 씬 그래프를 두지 않고).
- 최소 확인: TUI가 이미 만드는 칸의 격자(`VegaGrid`, 보이는 칸만 평가한다)를 심볼 형식의 글리프로 그려, 같은 스크립트에서
  TUI의 글과 같은 프레임이 나오고 글자가 옆 칸으로 번지지 않는지 본다. 화면이 없는 Pi에서 이미지 파일로 확인한다.

## 한 것

`vega pixels MODULE --symbols FONT.vsym [--size PX] --grid WxH --out PREFIX < script`: TUI의 dump와 같은 스크립트를 받아 `show`마다
그 글을 쓰고 `PREFIX-N.ppm`에 그림을 쓴다. 경로는 짧다. 앱의 뷰가 칸(`vega_draw`, 화면에 든 칸만 평가)이 되고, 칸이 글리프가
되어 픽셀에 합성된다(`surfaces/pixels.c`). 중간에 문서나 장면 트리가 없다.

- 글리프는 크기마다 처음 쓸 때 한 번 정확 넓이로 굽는다(`vega_glyphs`, 심볼 파일의 곡선에서, [002](002-symbol-format.md)).
- 칸은 숫자 하나만큼 넓고 글꼴의 줄만큼 높다. 줄은 심볼 파일의 올림과 내림(3판에서 더한 헤더의 두 값)이다. 넓은 글자(한글,
  CJK)는 두 칸을 쓴다.
- 톤마다 색이 있고, 포커스 행은 배경이 다르다. 심볼에 없는 글자는 칸에 상자를 그린다.
- 글꼴은 차례로 채운다(`vega-symbols build --font A --codes … --font B --codes …`). 앞 글꼴에 없는 코드만 뒤 글꼴에서 가져온다.
  처음에는 한 CJK 글꼴(Noto Sans Mono CJK KR)만 썼는데, 동아시아 폭이 모호한 글자(화살표 등)를 그 글꼴은 전각으로 그려 한 칸인
  격자에서 옆 칸으로 번졌다. 검사가 그것을 잡았다. 라틴 고정폭(DejaVu Sans Mono) 다음에 한글을 채우면 번지지 않는다.

## 장치가 말한 것

Raspberry Pi 5. todo 예제에 한글 항목 넷, 48×12칸, 20px: 실행 전체(3.6 MB 심볼 파일 열기, 글리프 약 40개 굽기, 그리기,
PPM 쓰기)가 7 ms다. 192×45칸(1920×1350)은 프레임마다 12 ms다(그리기, 합성, 7.8 MB PPM을 `/dev/shm`에 쓰기).

## 디스플레이(KMS)

`vega display MODULE --symbols FONT.vsym [--size PX] [--card PATH] [--writeback WxH --out PREFIX] < script`: 같은 프레임을
디스플레이에 낸다(`surfaces/display.c`). 라이브러리 없이 커널의 ioctl로 한다. 연결된 커넥터를 찾아 그 모드를 CRTC에 걸고,
주 평면(primary plane)에 덤 버퍼 둘 중 하나를 atomic 커밋으로 넘긴다. 격자는 화면에 드는 만큼의 칸이다. 프레임은 캐시되는
메모리에 그린 뒤 덤 버퍼로 복사한다. 덤 버퍼는 write-combine 메모리여서 글리프를 섞으려고 그 메모리를 읽으면 느리다. 처음에는
그 버퍼에 바로 그렸고, 1080p 한 장에 약 370 ms가 들었다.

이 Pi(vc4)는 HDMI가 비어 있어도 writeback 커넥터가 둘 있다. 디스플레이 컨트롤러가 내보낸 프레임을 메모리로 되돌려 쓰는
출력이다. `--writeback`은 그 커넥터에 모드를 걸고, 프레임마다 돌아온 그림을 그린 것과 대 보고 PPM으로 쓴다. 1920×1080에서
프레임 하나가 20.6 ms다(합성, 복사, 커밋과 수직 동기 한 번(60 Hz, 16.7 ms), 캡처를 읽어 대 보고 6 MB PPM을 쓰기까지). 돌아온
그림은 늘 그린 것과 같았다(다른 픽셀 0).

## 확인

`tests/check.py` 11번과 12번. 12번은 writeback 커넥터가 있으면 같은 스크립트를 디스플레이로 돌린다. 프레임마다 돌아온 그림이
그린 것과 같고, 같은 격자의 픽셀 표면 그림과 픽셀 하나까지 같다(640×480에서 프레임 둘, 589,512 픽셀).

`tests/check.py` 11번. 같은 스크립트를 `tui --dump`와 `pixels`로 돌려 글이 같고, 그림에서 빈 칸은 그 행의 배경(포커스 행은 제
색)뿐이며 글자가 있는 칸에는 잉크가 있다(프레임 둘, 칸 1,024개).

## 다음

- 바뀐 칸만 다시 그리고 복사한다(TUI가 이미 칸마다 보인 것을 기억한다). 커밋을 기다리지 않고 페이지 넘김 이벤트로 맞춘다.
- 입력: evdev(키보드, 마우스, 터치). 칸의 격자가 있으니 클릭이 곧 행이다(`vega_hit`).
- 비례 글꼴의 줄: 칸이 아닌 글자 폭으로 놓는 표와 문단. 합성은 GPU로(아틀라스, [002](002-symbol-format.md)).