[개요]
ThorVG 채택 사례 중, LibreScoot 을 조사하기로 했습니다.
평소 모빌리티 SW 산업에 관심이 있기도 했고,
간단한 AI 브리핑 결과
- GPU가 없는 임베디드 환경
- ThorVG 의 동적 링킹 ( Godot 등 다른 대표적인 사례 들은 전부 정적 링킹을 한다고 하더라구요 )
이라는 점에서 ThorVG 의 경량성에 대해 집중하여 알아볼 수 있겠다 생각해서 채택했습니다 .
[ LibreScoot 이란 ? ]
LibreScoot은 독일 스타트업 unu가 개발하던 unu Scooter Pro(전기 스쿠터) 용도의 오픈소스 펌웨어 입니다. unu가 2023년 사업을 철수하며 수 천 대의 스쿠터가 클라우드 서비스 단절로 이용하지 못할 상황에 처하자 오픈소스 커뮤니티가 이 하드웨어를 계속 구동시키기 위해 개발하고 보급한 펌웨어 SW 입니다.
unu Scooter Pro 의 하드웨어 구성은 아래와 같습니다.
- MDB (Main Dashboard Board) — NXP i.MX6UL. 차량 제어 로직(배터리, 알람, ECU, GPS 등)을 담당.
- DBC (Dashboard Computer) — NXP i.MX6DL. 480×480 원형 터치스크린이 붙은, 사용자에게 보이는 컴퓨터.
- 개발 보드 — Raspberry Pi 4 (librescoot-dbc-rpi4).
주목할 점: DBC의 i.MX6DL에는 사용 가능한 GPU가 없습니다. Boot Splash 부터 Dash Board UI까지 전부 CPU로 벡터를 그려야 하는데요, 이 제약이 ThorVG 채택으로 이어졌다고 보여집니다.
프로젝트는 Yocto Scarthgap 기반, 라이선스는 CC BY-NC-SA 4.0
[ ThorVG adoptation point in LibreScoot ]
LibreScoot 조직 저장소 56개 중 ThorVG 를 직접적으로 사용하고 있는 곳은 아래와 같습니다.
저장소 │ 언어│
librescoot/boot-animation │ C │
[ Analysis on boot-animation ]
저장소는 네 개의 파일로 구성되어 있습니다.
librescoot/boot-animation/ ├── main.c # 전체 구현 ├── Makefile # 크로스 컴파일 빌드 ├── README.md └── LICENSE
- 프로젝트 요약 :
“Lottie boot animation renderer for /dev/fb0 using ThorVG”.
스쿠터에 전원이 들어오면, 리눅스 커널이 로드되고 초기 userspace가 올라오는 순간부터, DashBoard UI(scootui-qt)가 준비되기 전까지의 시간 동안 원형 터치스크린에 로티 애니메이션을 재생한다.
- 빌드
Makefile CFLAGS += -O2 -Wall LDLIBS += -lthorvg -lstdc++ -lm -lpthread CC ?= gcc ARM_CC ?= arm-linux-gnueabihf-gcc
build-host: $(CC) $(CFLAGS) main.c $(LDLIBS) -o boot-animation build-arm: $(ARM_CC) $(CFLAGS) main.c $(LDLIBS) -o boot-animation dist: $(ARM_STRIP) boot-animation
- ThorVG는 -lthorvg로 동적 링크.
- ThorVG C API 를 사용함에도 -lstdc++ 가 포함되는데, 이는 ThorVG 가 C++ 로 개발되었다 보니 C++라이브러리가 함께 링크되는 것으로 생각됩니다.
- main.c
ThorVG C API(thorvg_capi.h) 따라가기
0. 디스플레이 하드웨어 bpp 읽어 오기
int bpp = vinfo.bits_per_pixel;
- bpp = 16 / 32 에 따라 argb 변환이 이루어 집니다.
1. 디스플레이 하드웨어 프레임버퍼 매핑
int fb_fd = open("/dev/fb0", O_RDWR);
void *fb_mmap = mmap(NULL, fb_size,
PROT_READ | PROT_WRITE, MAP_SHARED,
fb_fd, 0);
- 실제 디스플레이 하드웨어에 연결되는 버퍼 fb_mmap 을 할당합니다.
2. ThorVG 엔진 초기화
if (tvg_engine_init(0) != TVG_RESULT_SUCCESS) {
fprintf(stderr, "tvg_engine_init failed\n");
goto cleanup_fb;
}
- Boot Splash 는 가볍고 재생 시간이 짧기에 스레드 풀 초기화 오버로드를 없애기 위해 싱글 스레드로 최소화 하는 모습을 볼 수 있습니다.
3 . 캔버스 생성 및 타겟 버퍼 바인딩
Tvg_Canvas canvas = tvg_swcanvas_create(TVG_ENGINE_OPTION_DEFAULT);
uint32_t *argb_buf = calloc(width * height, sizeof(uint32_t));
if (tvg_swcanvas_set_target(canvas, argb_buf,
width, width, height,
TVG_COLORSPACE_ARGB8888)
!= TVG_RESULT_SUCCESS) { /* error */ }
- TVG_COLORSPACE_ARGB8888 로 ThorVG 렌더링 결과물을 담는 버퍼 argb_buf 를 할당합니다. bpp = 16 이라 프레임버퍼 fb_mmap 자체가 RGB565일 수 있어도, ThorVG에는 항상 32bit로 그리게 하고 argb_to_rgb565 으로 나중에 변환하네요. 아마 알파값 및 색 정밀도를 보장하기 위함이라고 생각합니다.
4. 로티 애니메이션 로드
Tvg_Animation anim = tvg_animation_new();
Tvg_Paint picture = tvg_animation_get_picture(anim);
if (tvg_picture_load(picture, lottie_path) != TVG_RESULT_SUCCESS)
{ /* error */ }
5. 프레임 루프
tvg_canvas_add(canvas, picture);
for (float frame = 0; frame < total_frames && !quit; frame += frame_step) {
tvg_animation_set_frame(anim, frame);
tvg_canvas_update(canvas);
tvg_canvas_draw(canvas, true);
tvg_canvas_sync(canvas);
...
if (bpp == 16) {
argb_to_rgb565(argb_buf, (uint16_t *)fb_mmap,
width * height);
} else {
memcpy(fb_mmap, argb_buf, width * height * 4);
}
}
- 캔버스와 애니메이션 매핑
- Canvas 가 애니메이션 렌더링 수행
- argb_buf -> fb_mmap 변환 : 16bpp면 RGB565 변환, 32bpp면 그대로 memcpy.
6. bpp =16 이면 RGB565 변환
static void argb_to_rgb565(const uint32_t *src,
uint16_t *dst, int count) {
for (int i = 0; i < count; i++) {
uint32_t px = src[i];
uint8_t r = (px >> 16) & 0xFF;
uint8_t g = (px >> 8) & 0xFF;
uint8_t b = px & 0xFF;
dst[i] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}
}
- Abstract Data Flow
ThorVG Canvas -> (rendering) -> argb_buf -> ( converting if needed ) -> fb_mmap -> Display H/W -> Display
ThorVG 채택 포인트
1. GPU 없음 : 앞서 언급했듯이, 출력 하드웨어가 GPU를 지원하지 않습니다. Skia나 Cairo의 GPU 백엔드, Qt Quick의 SG 렌더러는 모두 GPU Based. ThorVG의 CPU Based 렌더링이 필요했다고 추정됩니다.
2. Lottie 및 SVG 지원 : rlottie도 가능하지만 rlottie는 lottie 전용이라 향후 SVG/벡터 UI 요소로 확장 시 별도 라이브러리가 합니다. ThorVG 하나로 lottie + SVG + 벡터 그래픽스를 모두 다룰 수 있어, 크기 면에서 유리합니다.
3. 바이너리 크기 : ThorVG는 의 경량 크기는 이점
4. C API 지원 : boot-animation 의 main.c가 순수 C로 개발되었는데요 thorvg_capi.h가 덕분에 C++ 런타임을 고려하지 않고 ThorVG API 사용이 가능했습니다.
LibreScoot 에서의 특이점
LibreScoot 에서는 ThorVG 를 동적 링킹하고 있는데요, 이는 다른 Godot/LVGL/dotLottie 의 정적 링킹 방식과는 차별화된 방식이라고 합니다. 그 사유는 아래와 같이 추측됩니다. LibreScoot 에서 ThorVG 참조 지점은 1. boot-animation 2. Qt 입니다. ( 단, Qt 는 버전에 따라 참조할 수도 안할수도 있음 ) 따라서 이들 각각이 모두 ThorVG 를 정적링킹해 들고 있는 것 보다는, Yocto가 시스템 전역에 하나의 libthorvg.so 를 로드해두고, 이들 각각이 공유하는 구조로 동작하는 쪽이 전체 바이너리 크기 절감에 보다 유리합니다.
댓글
Discussion 원문