SPINON

SPINON / 모바일 렌더링 설계

모바일 GPU 렌더러 구현 계획

프레임워크가 만든 UI 변경을 Rust의 공통 트리와 배치 결과에 연결하고, Android·iOS의 GPU 표면에 일관된 화면으로 표시하는 순서입니다. 픽셀을 그리는 렌더러와 React·Vue·Svelte의 갱신 어댑터를 분리해 책임과 검증 기준을 정리합니다.

설계 초안 · 미구현모바일 일반 UI: GPU 출력웹: 브라우저 DOM·CSS제품 지원 상태는 공식 대장이 기준

전체 통과 조건을 이 페이지에 정리했습니다. 공식 구현 상태와 API 명세는 별도 문서가 기준입니다.

이미 정한 구조

  • 모바일의 일반 UI는 태그마다 네이티브 뷰를 만들지 않고 GPU로 그립니다.
  • 웹 빌드는 브라우저 DOM·CSS를 사용합니다. 동일 소스 목표가 렌더러까지 같다는 뜻은 아닙니다.
  • Rust 코어가 UI 트리·스타일·레이아웃과 플랫폼 중립 렌더 데이터를 담당합니다.
  • 각 프레임워크 어댑터가 자기 반응성·갱신 알고리즘을 소유합니다. React Fiber를 Rust에서 다시 만들지 않습니다.

실험으로 정할 항목

  • GPU API·라이브러리와 iOS·Android 백엔드 구성
  • 유지형 장면 스냅샷과 표시 명령 목록의 정확한 자료 구조
  • 문자 모양 만들기·글리프 래스터화·폰트 자원 경계
  • JS·레이아웃·UI·GPU 제출의 스레드 소유권과 취소 규칙
  • 접근성 노드와 IME 입력을 OS에 연결하는 구체 방식

렌더링 경계

React 어댑터와 GPU 렌더러는 서로 다른 계층입니다
계층입력 → 출력소유 책임소유하지 않는 것
프레임워크 어댑터React·Vue·Svelte 변경 → 공통 호스트 작업훅·상태·반응성, 해당 프레임워크의 갱신 및 이벤트 핸들러 수명GPU 자원, Rust 포인터, 화면 합성
Rust 문서·UI 코어호스트 작업 → 검증된 트리 revision안정 노드 ID, 자식 순서, 변경 커밋, 스타일 입력, 오류 의미React Fiber나 V8 객체의 소유권
스타일·레이아웃트리·스타일 값 → 계산된 스타일·박스지원 CSS 계산, 측정 제약, 좌표·크기와 무효화 원인GPU 프레임 제출
spinon-render일관된 트리·배치 snapshot → 플랫폼 중립 표시 데이터페인트 속성, 그리기 순서, 클립·변환, hit-test 데이터, 자원 참조JS 객체와 플랫폼 UI 객체
플랫폼 렌더 호스트표시 데이터 → GPU 제출·화면 프레임GPU 장치·표면 수명, 자원 업로드·해제, 표시 결과와 오류 보고컴포넌트 상태와 프레임워크 reconciliation
OS 기능 연결터치·IME·접근성 요청 ↔ 노드 ID입력 좌표 변환, 의미 노드, 텍스트 편집 세션, 플랫폼 생명주기일반 UI를 네이티브 뷰로 다시 그리기

핵심 구분: React 쪽 “renderer/host adapter”는 React가 만든 변경을 스피논 노드 작업으로 바꾸는 연결기입니다. GPU 렌더러는 커밋된 공통 트리와 레이아웃 결과를 픽셀로 바꿉니다. React용 연결기를 추가해도 GPU 렌더러가 React 전용이 되는 것은 아닙니다.

구현 순서와 통과 조건

새 공개 API를 미리 선언하지 않고 내부 계약부터 고정합니다
00

GPU·레이아웃·스레드 위험 검증

R06·R08·R10·R13 실험을 Android와 iOS 실기기에서 진행합니다. GPU 표면, 네이티브 프레임 시계에 따른 commit-to-present, 최소 화면, 텍스트 측정, 입력, 회전·백그라운드 복귀를 재현하고 후보 backend의 빌드 크기·지원 기기·복구 비용을 기록합니다.

통과: 두 플랫폼에서 네이티브 프레임 예약으로 commit-to-present를 확인하고, 재현 가능한 로그·화면 증거와 backend 선택 근거를 남깁니다. 성능 우위는 별도 비교 전까지 주장하지 않습니다.
01

트리·커밋·화면 표시 계약

혼합 요소·텍스트 순서가 필요한 렌더 경로의 트리 모델, 앱 루트, commit 실패·취소·오래된 revision 복구를 정합니다. 공개 단계는 중간에 끊지 않습니다. 취소가 공개 전에 이기면 이전 트리·revision을 유지하고, 공개가 먼저면 취소는 늦은 요청으로 처리합니다. JS 어댑터의 최종 결과도 실제 공개 상태와 일치해야 합니다.

통과: 공개 직전과 직후에 취소를 멈춰 둔 두 fixture에서 트리 상태·렌더 대상 revision·어댑터의 최종 결과가 일치하고, 부분 변경이 snapshot에 나타나지 않습니다.
02

플랫폼 중립 snapshot

spinon-render가 공개된 revision만 읽어 표시 명령·자원 표·hit-test 데이터를 만듭니다. 폰트 측정기와 이미지 제공자는 결정적 Rust 테스트에서 주입할 수 있게 둡니다.

통과: 같은 입력에서 revision·프레임 ID·명령·오류가 결정적이며, 플랫폼 호스트 객체가 필요하지 않습니다.
03

GPU 표면·좌표·기본 페인트

CSS 좌표와 OS 논리 좌표·물리 픽셀, 배율·안전 영역·반올림 규칙을 고정합니다. 스타일 결과는 입력 트리 revision을, 레이아웃 결과는 입력 트리·스타일 revision과 결과 revision을 보유합니다. tuple이 맞지 않는 늦은 결과는 버립니다.

통과: 네이티브 프레임 driver로 commit-to-present를 확인하고, 중첩·겹침과 stale 계산 fixture의 계산 상자·화면 순서·클립·터치 대상이 Android·iOS에서 맞습니다. 이 단계는 공개 JS requestAnimationFrame을 요구하지 않습니다.
04

텍스트·이미지와 비동기 자원

측정과 GPU 표시가 같은 shaping·fallback 결과를 사용합니다. 이미지 본래 크기 변화는 관련 레이아웃 조상과 paint·hit-test·접근성 bounds를 무효화합니다. 제거·교체·OTA 이후의 늦은 응답은 세대가 다르면 버립니다.

통과: 한글·라틴 측정·표시, 이미지 크기 변경·실패·늦은 응답, 표면 재생성 뒤 자원 업로드를 확인합니다.
05

입력·접근성·IME

입력과 OS 의미 노드는 같은 마지막 표시 frame revision과 변환을 사용합니다. OS 제스처 취소와 JS 이벤트 의미를 분리하고, 이벤트 순서·캡처·버블링·stopPropagation()·preventDefault()·포인터 취소·키보드 및 접근성 활성화·삭제 노드 대상·콜백 수명을 fixture로 고정합니다. 플랫폼 취소만으로 pointercancel을 가정하지 않습니다.

통과: 탭·취소·삭제 경합, 고정된 JS 이벤트 계약, 편집 세션, VoiceOver·TalkBack 탐색·활성화가 각 플랫폼에서 확인됩니다.
06

스크롤·제스처·JS 프레임 예약

U07·E06 범위에서 scroll offset, 중첩 제스처와 관성을 구현하고 공개 JS requestAnimationFrame·cancelAnimationFrame의 의미를 정합니다. OS 프레임 시계와 commit 때의 단발 렌더 예약은 00·03단계에서 먼저 제공합니다.

통과: 화면·hit-test·접근성 bounds가 스크롤 위치와 맞고, JS 프레임 예약과 중첩 스크롤·취소·비활성·복귀 동작을 각각 검증합니다.
07

GPU 자원·표면 복구

동시 프레임 제출 수와 역압력, 표면 획득 실패·대기·취소, 자원 참조 퇴역 시점을 정합니다. 글리프·이미지 캐시의 메모리 예산·축출·재업로드를 표면 세대와 연결합니다.

통과: 빠른 추가·삭제, 회전, 표면 손실, 장시간 실행·메모리 압박 뒤 stale handle·누수·오래된 성공 보고가 없습니다.
08

적합성·성능 비교

R05·S07·S09 측정은 공식 benchmark 규칙과 공통 fixture를 사용합니다. 웹·Android·iOS의 실제 기능 차이를 먼저 기록하고 동등한 시나리오만 비교합니다.

통과: 같은 실기기 조건의 원본 측정 자료가 남고, 기능 차이가 있으면 성능 순위를 매기지 않습니다.
09

OTA와 렌더 자원 세대 연결

R15·D02·D04 단계는 정식 OTA 설계가 검증한 불변 릴리스 snapshot을 소비합니다. release ID·manifest digest·binary runtime ID와 JS/CSS/폰트/이미지 해시를 프레임·자원 세대에 묶습니다. 렌더러는 digest를 불투명한 snapshot 식별자로만 쓰고, 생성·표준화·서명 검증 규칙은 OTA 계약에 둡니다.

통과: digest 계약을 R15/D02에서 정의한 뒤, 다른 snapshot이나 비호환 조합을 거부하고 정식 OTA 계약에 따라 이전 정상 릴리스로 복구합니다.

필수 공통 경합 시나리오

플랫폼 구현마다 같은 불변 조건을 확인합니다
시나리오확인할 불변 조건연결 ID
변환된 부모 안에서 겹치고 잘리는 버튼화면상 맨 위의 보이는 버튼과 hit-test·접근성 활성화 대상이 일치합니다.S04, S07, E03, R10
중첩 스크롤 중 OS 제스처 취소·관성 종료native gesture와 JS 이벤트 결과를 분리합니다. JS 의미는 이벤트 순서·캡처·버블링·대상·취소·기본 동작·콜백 수명 fixture로 고정된 경우에만 판정합니다.U07, E03, E06
작업 묶음 중 오류·취소·오래된 commit취소와 공개 중 먼저 선형화된 결과만 적용하며, 이전 공개 revision 또는 전체 새 revision만 표시합니다. JS 어댑터 결과도 공개 revision과 일치합니다.R03, R06, S01, S05
commit 취소와 공개 경합의 두 순서공개 직전 취소는 이전 트리·revision과 취소 결과를 유지하고, 공개 직후 취소는 새 revision과 성공 결과를 유지합니다. 어댑터 결과와 트리 상태가 같으며 부분 트리가 없습니다.R03, R06, S01
새 트리·스타일 뒤 이전 스타일·레이아웃 계산이 늦게 완료입력 revision 중 하나라도 frame tuple과 다르면 결과를 폐기하고, 화면·hit-test·접근성이 같은 수락 tuple을 사용합니다.R03, R10, E01, E03
이미지·글리프 업로드 중 노드 삭제와 표면 재생성제출 중 자원은 안전하게 유지하고 오래된 응답·handle은 버리며 새 표면 세대에서 다시 만듭니다.R08, R13, S11, E02, E07
OTA 뒤 이전 릴리스의 이미지·폰트 응답 도착활성 immutable snapshot의 release ID·manifest digest·runtime ID·JS/CSS/폰트/이미지 해시가 모두 맞는 자원만 새 프레임에 포함하고, 나머지는 거부·롤백합니다.R15, D02, D04

세부 계측은 공식 benchmark 계획을 따릅니다. 이 페이지는 별도의 측정 계약을 만들지 않습니다.

현재 체크리스트와의 연결

새 상태 ID를 만들지 않고 기존 항목을 구체화합니다
공식 항목이 계획에서 다루는 부분완료 증거의 중심
R03 · 공통 호스트 계약01–02루트·혼합 텍스트·commit 실패 복구·revision
R05 · 계측 계약08공식 benchmark 조건과 화면 표시 계측 자료
R06 · 스레드·소유권00–02, 05–07취소·경합·프레임과 자원 수명
R08 · GPU 출력 위험 실험00Android·iOS GPU 최소 화면, 텍스트·터치·IME·접근성 가능성 및 제약
R10·R13 · 레이아웃·복구 위험00–03, 07좌표·측정, 표면 재생성·자원 복구 근거
S02 · Taffy 레이아웃01–04공통 좌표 fixture와 텍스트 측정 경계
S04 · GPU 적용기01–03, 05변경 배치 → GPU 장면 → 플랫폼 입력의 첫 동작
S07 · 텍스트·터치·화면04–05폰트 측정·표시·hit test·접근성 이름의 기기 증거
S09 · 네 구현 비교08동등 시나리오의 실기기 측정
S11 · 표면·콜백 수명 복구00, 07회전·복귀·표면 재생성 뒤 ID·콜백·GPU 자원 복구
U07·E01–E0705–07스크롤·부분 갱신·GPU 자원·이벤트·제스처·프레임 예약
E01 · 부분 레이아웃 계산02, 06계산된 박스의 정확성과 변경된 범위만 다시 계산하는 근거
E02 · GPU 텍스트·이미지 품질04, 06글리프·이미지 자원, 클립·합성 품질과 메모리
E03 · 입력·접근성05hit test, IME, OS 의미 노드와 활성화 결과
E04–E05 · 프레임 배치·스케줄링06무효화 정확성, 제출 수, 지연, 프레임 누락, 자원 사용량
E06 · 제스처·애니메이션05–06입력 취소·동시 제스처와 프레임 진행 규칙
E07 · 장시간·오류 회귀00, 07반복 복구, 취소, 누수, 비정상 종료와 오류 진단
E08 · 시작 시간 최적화렌더러 범위 밖V8·번들 초기화 측정으로 별도 관리
R15·D02·D04 · 청크 OTA09릴리스 자원 일치·원자 활성화·롤백

공식 완료 여부는 공식 구현 상태 대장만 기준으로 삼습니다. 이 페이지와 원문 초안은 상태 체크나 “지원 완료” 선언을 대신하지 않습니다.

네이티브 뷰 PoC: 공식 아키텍처에서 기존 PoC는 비교 기준으로 보존합니다. GPU 주 렌더링 경로의 대안으로 구현하지 않습니다.

구현 전에 확정할 결정

근거가 생기는 관문에 배치

GPU API와 backend R08 실험 뒤

공용 라이브러리 또는 플랫폼별 경로를 지원 기기, 앱 용량, 빌드·디버그 난이도, GPU 손실 복구, 실제 프레임 자료로 비교합니다. 현재 문서는 특정 API를 선택하지 않습니다.

유지형 장면과 표시 명령 02에서 최소 모델

첫 구현은 안정된 노드 ID를 가진 revision snapshot을 기준으로 삼고, 표시 명령은 결정적으로 만들 것을 제안합니다. dirty-region 최적화는 정확한 전체 프레임이 먼저 나온 뒤 측정합니다.

폰트 shaping·래스터화 04 전에 비교

플랫폼 폰트 서비스와 공통 래스터 backend를 비교합니다. 측정값과 그리는 글리프가 다르지 않아야 하며, 한글 조합·fallback·이모지·RTL의 지원 차이를 공개합니다.

스레드와 프레임 소유권 R06·06

UI 입력 처리와 JS 실행의 동기 경계, GPU queue 소유자, 취소·역압력·오류 전달을 실험합니다. 스레드/고루틴 선택을 렌더러 계약보다 먼저 고정하지 않습니다.

OS 접근성과 텍스트 입력 00·05

시각 요소를 네이티브 뷰로 바꾸지 않고 OS 접근성 인터페이스에 의미 트리를 노출하는 방향을 검증합니다. IME·선택·커서는 GPU 표시와 플랫폼 입력 세션 사이의 책임을 분리합니다.

렌더 명령의 공개 범위 공개 API 아님

표시 snapshot·GPU handle은 Rust 내부와 플랫폼 host 경계에 둡니다. 앱 작성자용 imperative drawing/WebGPU API는 별도 요구가 명세되기 전까지 이 계획에서 제공한다고 약속하지 않습니다.

검증 자료 기준

통과 판정은 소스·기기·측정 조건을 함께 남깁니다
영역기록할 값잘못된 완료 판정을 막는 규칙
결과 정확성revision, 계산 박스, 표시 명령, 이벤트 대상, 실패 이유트리 commit만으로 화면 표시 완료라 하지 않고 present 결과를 별도로 확인합니다.
시각 회귀Android·iOS별 캡처, OS·기기·배율, 글꼴·색 차이서로 다른 GPU·OS의 픽셀 일치를 전제하지 않습니다. 박스/명령 기대값과 플랫폼별 허용 오차를 함께 둡니다.
입력·접근성좌표·화면 revision·대상 노드·콜백 수명, 접근성 탐색 결과자동화된 Rust 테스트와 실제 기기의 IME·VoiceOver·TalkBack 결과를 분리해 보고합니다.
성능commit-to-present p50/p95, CPU layout/paint, GPU 시간, 누락 프레임, 메모리실기기·릴리스 조건·같은 UI 시나리오끼리 비교하고 시뮬레이터 수치를 섞지 않습니다.
수명·오류surface generation, 재생성 횟수, 자원 생성·해제, 복구 결과GPU 오류 뒤 오래된 프레임을 새 성공 프레임처럼 표시하지 않습니다.
정식 문서 위치: 구현 계획의 원문 후보는 로컬 renderer-plan.md입니다. 공식 저장소에 아직 반영되지 않았으며, 구현 계약은 별도 버전 있는 내부 명세와 공식 상태 ID에 연결합니다.