[과제] 3주차) ThorVG 활용사례 조사 김두람 #95
alpakaDurumi
started this conversation in
Blog
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
서론
LVGL(공식 리포)은 임베디드 시스템에서 사용되는 오픈소스 그래픽스 라이브러리로, 렌더링을 위한 여러 방식을 지원합니다: LVGL Docs - Draw Units
ThorVG는 그 중에서도 소프트웨어 렌더링을 사용할 시 벡터 그래픽을 렌더링하기 위해 사용됩니다. 이 글에서는 LVGL이 ThorVG를 어떤 방식으로 가져와서 통합했고, 실제로 어디에 사용하는지를 코드 기준으로 살펴보았습니다.
LVGL이 ThorVG를 선택한 이유
LVGL의 이슈를 살펴보면 2021년에 최초로 ThorVG에 대한 언급이 된 이후로, 여러 이슈 & PR에서 ThorVG의 도입에 대해 논의되었던 것으로 보입니다.
LVGL 포럼의 한 글의 댓글을 보면 2023년 즈음에 LVGL에 벡터 그래픽스 기능 추가가 마일스톤에 있었고, 어떤 라이브러리를 사용해야할지 고민하고 있었던 것 같습니다. hermet님께서 ThorVG의 경량성과 Lottie 지원 예정에 대해 말씀하시는 것도 보입니다. 그러다가 LVGL 측 개발자가 직접 ThorVG에 이슈를 작성해 LVGL에 ThorVG를 추가하고 싶다고 요청했습니다. 이는 임베디드 기기에서 돌아가기 위한 작은 바이너리 크기, Lottie 지원 등이 이유인 것으로 생각됩니다.
통합 방식
Note
LVGL은 타겟에 따라 여러 빌드 경로를 지원합니다. 이 글에서는 그 중 CMake 기반 빌드 흐름을 조사했습니다.
옵션 설정
LVGL Docs - Lottie를 보면, LVGL에서 Lottie를 사용하기 위해선
lv_conf.h에서LV_USE_THORVG_INTERNAL(포함된 ThorVG 사용) 또는LV_USE_THORVG_EXTERNAL(외부 링크 사용)을 활성화해야 하며, 또한 벡터 그래픽스이기 때문에LV_USE_VECTOR_GRAPHIC또한 활성화해야한다고 되어 있습니다.물론 LVGL에 있어 ThorVG는 선택 기능이기 때문에 활성화하지 않고 다른 벡터 그래픽스 백엔드를 사용할 수도 있습니다. 다만 백엔드가 하나도 없다면 에러가 발생합니다.
다시 돌아와서
lv_conf.h에 대해 살펴보겠습니다. 리포에는lv_conf.h가 보이지 않는데요, LVGL Docs - Overview를 보면 루트에 있는lv_conf_template.h를lv_conf.h라는 이름으로 복사한 후 필요한 만큼 옵션을 설정하면 되는 것임을 알 수 있습니다.lv_conf_template.h의 최상단에는 아래와 같이 설명되어있습니다.lv_conf_template.h에서 ThorVG와 관련된 옵션을 찾아보면:먼저 ThorVG가 활성화 되었을 때 스레드 스택 사이즈를 32KB 이상으로 설정하는 것을 권장하고 있습니다.
다음으로 벡터 그래픽스 활성화, ThorVG 활성화 부분입니다.
아래 부분은 VG-Lite 라는 Draw Unit을 ThorVG를 사용하여 시뮬레이션하는 기능과 관련된 옵션들입니다.
마지막으로는 Lottie 관련 옵션입니다.
CMake
CMakeLists.txt가 플랫폼에 따라 적절한.cmake파일을 include합니다.ESP 또는 MicroPython이 아니라고 가정하고
env_support/cmake/main.cmake를 보면 아래와 같은 ThorVG 관련 부분이 보입니다.INTERNAL이 켜져 있을 때
lvgl_thorvg라는 타겟을 생성하고 링크하는 것을 볼 수 있습니다. EXTERNAL의 경우는 사용자가 직접 링크해줘야 하는 것으로 보입니다.앞에
CONFIG_prefix가 붙은 변수들이 보이는데요, 방금 전lv_conf.h에서 설정했던 옵션들임을 알 수 있습니다. 다만 어떻게lv_conf.h에서 매크로로 설정한 옵션들이 CMake에 반영되는지가 궁금해서 알아본 결과, LVGL Docs - CMake를 보면 해당 변수들은-D로 수동으로 설정할 수도 있지만,LV_BUILD_SET_CONFIG_OPTS를 활성화하여 빌드 시스템이lv_conf_internal.h을 파싱하여 자동으로 변수들을 생성하게 할 수도 있다고 합니다.config.h
lv_conf.h에 설정된 변수들이 ThorVG 내부 코드들에는 어떻게 영향을 주는지는src/libs/thorvg/config.h를 보면 알 수 있습니다.방금 설정한
LV_USE_LOTTIE등을 그대로 ThorVG의 매크로의 값으로 사용하고, 결과적으로 ThorVG 내부는lv_conf.h에서 설정한 대로 동작하게 됩니다. 사용자가 ThorVG의 빌드 옵션을 알 필요 없이lv_conf.h만 설정하면 ThorVG의 내부 동작까지 제어하도록 설계되어 있습니다. 대신 버전은0.15.3에 고정되어 있는 것으로 보입니다.ThorVG 사용례
다음으로는 ThorVG가 실제로 어떻게 사용되는지 알아보았습니다. 먼저 ThorVG가 사용되는 파일 목록을 정리해보면 아래와 같습니다.
src/draw/sw/lv_draw_sw.csrc/draw/sw/lv_draw_sw_vector.csrc/draw/sw/lv_draw_sw_letter.csrc/widgets/lottie/lv_lottie.csrc/debugging/vg_lite_tvg/vg_lite_tvg.cpplv_draw_sw.c
line 17~23: C++ 헤더인
thorvg.h가 아니라 C API 바인딩인thorvg_capi.h를 씁니다. 또한 리포에 포함된 버전을 사용할 지, 또는 시스템에 설치된 버전을 사용할 지도 선택합니다.line 101~108: SW 렌더러를 초기화하는
lv_draw_sw_init()함수 내에서tvg_engine_init()을 호출하여 엔진을 초기화하며,LV_DRAW_SW_DRAW_UNIT_CNT에 따라 스레드 개수를 설정합니다.line 115~117: SW 렌더러를 해제하는
lv_draw_sw_deinit()함수 내에서 ThorVG 엔진을 해제합니다.line 423~427: 작업의 타입에 따라 분기하는
execute_drawing함수 내에서 벡터 그래픽 작업을 ThorVG에게 시키는 부분입니다.lv_draw_sw_vector()함수의 내용은lv_draw_sw_vector.c에 정의되어 있으며 바로 아래에서 다룹니다.lv_draw_sw_vector.c
LVGL의 벡터 그래픽 API를 ThorVG의 C API 바인딩으로 넘겨주는 역할을 합니다. 자체적인 렌더링 로직은 없고, 진입점 하나와 콜백 하나, 그리고 여러 변환 헬퍼들로 구성되어 있습니다.
진입점인
lv_draw_sw_vector()함수는 아래와 같은 흐름을 가집니다.콜백 함수
_task_draw_cb()은 LVGL과 렌더링 백엔드 간의 호환성을 위한 것입니다. LVGL은 벡터 그리기 명령을 즉시 실행하지 않고task_list에 쌓아둡니다. 이 리스트를 순회하는lv_vector_for_each_destroy_tasks()는lv_draw_vector.c의 공용 함수라 현재 어떤 백엔드를 사용하고 있는지 모릅니다. 그래서 "task 하나를 그리는 일"만 콜백으로 넘깁니다._task_draw_cb()는 task 하나를 받아tvg_shape_new()로 도형을 만들고 속성을 설정한 뒤tvg_canvas_push()로 캔버스에 등록합니다.파일의 나머지 내용은
lv_area_to_tvg(),lv_color_to_tvg(),lv_matrix_to_tvg()등의 헬퍼 함수로, LVGL의 구조체와 ThorVG 구조체 간 변환을 지원하는 함수입니다.lv_draw_sw_letter.c
FreeType이 폰트의 벡터 정보를 제공할 때 글자를 그리는 동작을 수행합니다. 방금 다룬
lv_draw_sw_vector()를 내부적으로 호출하여 ThorVG를 사용합니다.즉 ThorVG에 작업을 위임하는 함수는
lv_draw_sw_vector에 존재하고,lv_draw_sw.c와lv_draw_sw_letter.c두 곳에서 호출하는 구조입니다.lv_lottie.c
Lottie 애니메이션을 재생하는 위젯입니다. 앞의 두 파일과 달리 ThorVG의 렌더링 뿐만 아니라 Lottie 관련 기능까지 사용합니다.
아래 생성자는
tvg_animation_new()로 애니메이션 객체를,tvg_swcanvas_create()로 캔버스를 만듭니다. 동시에lv_anim_start()로 LVGL의 애니메이션도 시작합니다.사용할 애니메이션 파일 또는 데이터의 지정은
lv_lottie_set_src_data()와lv_lottie_set_src_file()로 수행합니다.lottie_update()함수는 프레임 번호v를 받아tvg_animation_set_frame()에 전달해 해당 프레임을 그립니다.vg_lite_tvg.cpp
LVGL은 VG-Lite라는 하드웨어 가속 백엔드를 사용하기도 합니다.
vg_lite_tvg.cpp는 ThorVG를 기반으로 VG-Lite를 시뮬레이션하는 구현체며, 디버깅 용도라고 합니다: LVGL Docs - VG-Lite SimulatorLV_USE_VG_LITE_THORVG가 활성화되면lv_vg_lite_utils.h를 비롯한 여러 파일들에서 아래와 같이 실제 드라이버 헤더 대신src/debugging/vg_lite_tvg/vg_lite.h를 include합니다.결론
LVGL은 기본적으로는 ThorVG를 외부 종속성으로 두지 않고 소스를 그대로 리포에 포함시킨 뒤, 빌드 옵션과 매크로를 사용자가 입맛에 맞게 설정하도록 해 뒀습니다. 여러 환경/케이스를 지원해야 하는 경우 어떻게 사용자에게 선택권을 줄 지에 대한 한 가지 방향성을 엿볼 수 있었습니다.
글의 앞부분에서도 보았듯이 LVGL은 ThorVG 말고도 다른 벡터 그래픽스 백엔드를 사용할 수도 있습니다만, LVGL 문서에서 언급되는 다른 대안인
VG-Lite,NemaVG,NanoVG는 모두 조사해본 결과 기본적으로 GPU 가속을 필수로 요구합니다. 반면 ThorVG는 완전한 SW 백엔드를 제공하기 때문에 선택 가능한 벡터 그래픽스 백엔드 사이에서 상당한 경쟁력을 가지는 것으로 보입니다. 여기에 특유의 경량성까지 더해져 임베디드를 타겟으로 하는 LVGL에 딱 맞는 라이브러리라고 생각이 듭니다.흥미롭게도 GPU를 요구하는
VG-Lite백엔드조차 GPU 없이 테스트할 때는 ThorVG로 구현한 시뮬레이터를 사용하는데요, 다소 특수한 사용례인 것 같습니다.또한
lv_draw_sw_vector.c에서도 보여지듯이, 외부 라이브러리를 프로젝트에 통합할 때 기존 시스템과 잘 어우러지게하는 계층이 필요하다는 것도 실감했습니다.All reactions