aboutsummaryrefslogtreecommitdiffstats
path: root/docs/design/002-symbol-format.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/design/002-symbol-format.md')
-rw-r--r--docs/design/002-symbol-format.md40
1 files changed, 24 insertions, 16 deletions
diff --git a/docs/design/002-symbol-format.md b/docs/design/002-symbol-format.md
index 3d9074b..253b37c 100644
--- a/docs/design/002-symbol-format.md
+++ b/docs/design/002-symbol-format.md
@@ -94,27 +94,35 @@ em² 대비 평균 2.6e-5, 최악 1.5e-4 차이다. SVG는 Adwaita 기호 아이
## GPU (Raspberry Pi 5 V3D 7.1, OpenGL ES 3.1, Mesa 26.2.2)
-`symbols/symbols-gl.c`는 파일의 점 표를 그대로 저장 버퍼 하나로 올리고, 심볼의 상자를 덮는 사각형(인스턴스 하나)의 픽셀마다
-`vega_coverage`와 같은 계산을 한다. 올릴 때 심볼마다 가로 띠 16개를 만들어 띠마다 걸치는 곡선(을 시작하는 점의 번호)을 적는다.
-픽셀은 제 행이 걸친 띠의 곡선만 읽고, 여러 띠에 걸친 곡선은 첫 띠에서만 센다. 띠 소속은 두 배 좌표의 정수 나눗셈이라 CPU와
-셰이더가 같게 가른다. 넓이는 행을 벗어난 곡선을 거르지 않고 왼쪽 곡선도 dy로 세야 하므로 띠는 가로로만 자른다.
+`symbols/symbols-gl.c`: 한 크기에서 쓸 심볼들을 CPU가 아틀라스(8비트 한 장)에 정확 넓이로 그리고(`vega_symbol_raster`), 글은
+그 아틀라스를 읽는 사각형(글리프마다 인스턴스 하나, 정수 픽셀의 펜 위치)으로 GPU가 합성한다. 크기가 계속 바뀌는 경우(확대
+애니메이션)는 그 크기의 아틀라스를 다시 그린다. 경로는 하나다.
-`vega-symbols gpu`로 잰 것(표면 없는 EGL 문맥, 8비트 빨강 대상):
+`vega-symbols gpu`로 잰 것(표면 없는 EGL 문맥). 심볼마다 제 칸의 펜 위치에 합성한 것을 읽어 같은 픽셀의 CPU 래스터와 댄다.
| | 12px | 16px | 32px | 64px |
|---|---|---|---|---|
-| CPU 참조와 평균 차이 / 최악 | 0.095 / 3 | 0.096 / 4 | 0.001 / 1 | 0.001 / 1 |
-| 1920×1080 한 쪽(글리프 수) | 19,779 | 11,029 | 2,698 | 647 |
-| 한 쪽 그리기 | 757 ms | 525 ms | 249 ms | 142 ms |
+| DejaVu ASCII: 합성과 CPU 래스터의 최악 차이 | 0 | 0 | 0 | 0 |
+| 아틀라스 채우기(95자, CPU 한 스레드) | 2.1 ms | 2.7 ms | 6.9 ms | 20.9 ms |
+| 1920×1080 한 쪽 합성(글리프 수) | 1.48 ms (20,212) | 1.07 ms (11,382) | 0.61 ms (2,805) | 0.44 ms (645) |
+| Noto 한글 11,172자: 채우기 / 한 쪽 합성 / 최악 | 0.68 s / 1.56 ms / 0 | 1.06 s / 1.45 ms / 1 | 1.83 s(24px) / 1.00 ms / 0 | |
-맞게 그리지만 매 프레임 직접 그리기에는 수십 배 느리다. 넓이 함수를 사소한 식으로 바꾸면 16px 쪽이 91 ms라서 시간의 83%가
-넓이 계산이다. 셰이더의 교차점 배열(`float ts[10]`, 동적 첨자와 삽입 정렬)을 레지스터 안의 정렬망(vec4 min/max)으로 바꾸면
-2.3배 빨라지지만(229 ms), 이 드라이버에서 프래그먼트 셰이더로 컴파일하면 몇몇 행 전체가 틀린 값을 낸다(최악 255). 같은 함수를
-계산 셰이더에서 돌리면 무작위 곡선 백만 개에서 CPU와 5.7e-6 안에서 같고, 프래그먼트의 `main` 전체를 계산 셰이더로 옮겨도 틀린
-픽셀이 없다. 그래서 프래그먼트 쪽은 정렬망 없이 두었다.
+이렇게 되기까지 GPU로 넓이를 계산하는 두 길을 쟀다. 둘 다 CPU보다 느렸다.
-다음: 크기마다 글리프를 한 번 계산 셰이더로 아틀라스에 그리고(여기서는 정렬망이 맞다), 글은 아틀라스를 읽는 사각형으로 그린다.
-UI의 글은 크기가 몇 가지뿐이므로 거의 모든 프레임이 텍스처 읽기다. 크기가 계속 바뀌는 경우(확대 애니메이션)만 직접 그리기를 쓴다.
+- **프래그먼트마다 직접.** 심볼의 상자를 덮는 사각형의 픽셀마다 띠(가로 16개, 올릴 때 CPU가 곡선을 나눠 적는다) 안의
+ 곡선의 넓이를 더했다. CPU 참조와 평균 0.001–0.1단계로 맞았지만 1080p 한 쪽이 142–757 ms였다. 시간의 83%가 넓이
+ 계산이었다. 교차점 배열(`float ts[10]`, 동적 첨자와 삽입 정렬)을 레지스터 안의 정렬망(vec4 min/max)으로 바꾸면 2.3배
+ 빨라졌지만 이 드라이버에서 몇몇 행 전체가 틀린 값을 냈다.
+- **계산 셰이더로 아틀라스.** 같은 정렬망 판이 정수 픽셀에 맞춘 격자에서(글리프의 기준선이 픽셀 경계와 겹친다) 틀렸다.
+ 기준선 바로 아래 행이 통째로 달랐다. 같은 곡선 입력을 C로 옮긴 판과, 넓이 함수 하나만 도는 계산 셰이더는 맞게 계산한다.
+ 그 함수를 띠의 반복문 안에 넣은 셰이더에서만 틀리므로 드라이버 컴파일러 쪽으로 본다(최소 재현은 아직 없다). 삽입 정렬 판은
+ 맞지만 16px 95자에 35 ms, 64px에 119 ms였다. CPU(Cortex-A76 한 코어)는 같은 일을 2.7 ms와 20.9 ms에 한다.
+
+그래서 넓이는 C 구현 하나로 계산하고, GPU는 합성만 한다. 셰이더에 넓이 계산이 없어 드라이버 결함에 걸릴 곳도 없다.
+GPU 계산이 빠른 장치에서 다시 잴 수 있도록 측정은 남긴다(이 문서와 커밋 기록).
+
+다음: 필요할 때 글리프를 더하는 아틀라스(화면에 나온 것만 굽기). 한글 한 글리프의 곡선은 약 54개이고 CPU 래스터는 픽셀마다
+모든 곡선을 본다. 행마다 걸치는 곡선만 보면 몇 배 빨라질 것이다.
## 도구
@@ -123,5 +131,5 @@ UI의 글은 크기가 몇 가지뿐이므로 거의 모든 프레임이 텍스�
호(`A`)는 4분의 1 바퀴 이하의 3차 곡선으로, 3차 곡선은 2차 곡선으로 나눈다. 칠해진 경로만 받는다. 변환, 불투명도, evenodd,
획, 경로 밖의 도형을 쓰는 아이콘은 이름을 대고 거부한다(빠뜨리고 그리면 틀린 그림이 된다). 해석은 여기서 한 번이다.
`compare`(FreeType과 픽셀 차이, 여는 시간, 윤곽 하나에 닿는 시간), `info`(심볼마다 점·곡선 수, 경계, 넓이), `render`(PGM),
-`gpu`(GPU와 CPU 참조의 차이, 한 쪽 그리는 시간)는
+`gpu`(GPU가 합성한 것과 CPU 래스터의 차이, 아틀라스를 채우고 한 쪽을 합성하는 시간)는
검증과 측정용이다. `tests/check.py`의 10번이 이것들로 형식을 지킨다.