[Study] janitor, janitor-web 분석 #128
juyonLee00
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.
Uh oh!
There was an error while loading. Please reload this page.
해커톤 전에 ThorVG의 기능을 활용한 프로젝트인 janitor, 웹으로 배포한 janitor-web의 코드를 함께 보면 코드 작성에 도움이 될 것 같아 해당 프로젝트들을 보게 되었습니다.
목차
1. thorvg.janitor
https://github.com/thorvg/thorvg.janitor
Janitor은 ThorVG의 렌더링 기능과 성능을 게임 형태로 보여주는 데모 프로젝트입니다. 적이 파괴될 때 다수의 파티클을 사용하며, 최대 25000개의Paint와 DropShadow, Blur 효과를 처리하는 장면이 성능 테스트의 핵심입니다.해당 코드는 핵심 콜백이
content(),update()로 이루어져 있습니다.content()는 초기 ThorVG 객체를 생성하고,update()는 매 프레임 상태를 갱신하며input()은 유저 입력을 처리합니다.이렇게 코드를 구조화함으로써 렌더링으로 인한 성능 부하를 재현할 때 코드를 쉽게 추적할 수 있습니다.
별 하나마다 독립된 Shape를 만드는 대신, 한 레이어의 별들을 하나의 Shape에 여러
appendRect()경로로 추가해 플레이어 위치에 따라 레이어별로 다른 이동량을 적용합니다. 이를 통해 별 하나마다Paint객체를 만들지 않고 한 Shape의 서브패스로 묶어 불필요한 Paint수를 줄일 수 있습니다.또한 레이어는 4개로 분리하고 각자 다른 변환을 적용함으로써 별 레이어에서는 Shape 단위 변환을, 격자와 경계 효과를 묶은 구역은 Scene 단위 변환을 적용합니다. 실제 성능 부하를 일으키는 폭발 파티클은 Paint를 많이 사용하고, 의미 없이 객체 수만 늘어나는 배경은 배치 처리하였습니다.
Enemy 기본 클래스는 이동, 위치, 회전, 충돌 반경을 관리하고 구체적인 적들 종류는 서로 다른 Shape 경로만 구현했습니다. Shape로 적들을 구별함으로써 ThorVG 렌더링 차이를 중심으로 확인하도록 하였습니다.
폭발은 하나당 6개의 파편 Shape와 80개의 flash shape으로 구성되며, 이 중 80개의 flash shape에는 Addictive Blend가 적용됩니다. 각 Shape와 flash Shape 둘 다 위치, 색상, 크기, 불투명도가 독립적으로 갱신되어 다수의 Paint가 서로 다른 Transform과 색상 상태를 갖습니다.
다음은 Pool에 보관되는 Paint의 수명 관리에 대한 내용입니다. 이 Paint는 현재 Sccne에서 제거되더라도 이후 다시 사용할 수 있어야 하므로 Enemy의 일부 객체는 생성 시 명시적으로
ref()를 호출하고, 실제 소유 객체가 파괴될 때unref()를 호출합니다. 이 방법을 통해 Scene에 포함된 기간과 cpp 게임 객체가 Paint를 보관하는 기간을 분리함으로써 Paint 소유권을 직접 보여줄 수 있습니다.다음은 지속적인 성능 변화 추세를 용이하게 만들기 위해 프레임 시간에 시간 기반 EMA를 적용하여 표시값을 부드럽게 보이도록 설정합니다. 비정상적인 값을 대비하기 위해 큰 프레임 간격은 최대 0.25초로 제한합니다.
추가로
assets.h에 포함된 메모리 데이터를 사용해 필요한 리소스를 로드함으로써 경로를 효율화하고 단일 실행 파일 중심으로 데모를 배포가능하게 하였습니다.2. thorvg.janitor-web
https://github.com/thorvg/thorvg.janitor-web
웹 버전은 네이티브 Janitor를
@thorvg/webcanvas위로 올린 프로젝트입니다. ThorVG에서 적용되는 Renderer들을 선택하여 렌더링이 가능하고 멀티스레드 렌더링도 실행 가능합니다.따라서 네이티브 Janitor의 동적 렌더링 부하를 브라우저에서 재현하면서 WebCanvas Renderer 선택, WASM 초기화, Worker 파일 배포와 cross-origin isolation 조건을 함께 검증합니다.
웹은
crossOriginIsolated값을 기준으로 해당 값이 false인 경우 요청된 스레드 값과 관계없이 스레드 수를 0으로 설정합니다.스레드 수가 1 이상이면
@thorvg/webcanvas의 threaded 배포 파일을 동적으로 가져오고, 0이면 일반 single-thread 초기화를 사용합니다. 그래서 COOP, COEP가 설정되지 않은 환경에서도 single-thread 버전으로 실행가능합니다.네이티브는 플레이어를 여러 Shape를 조합해 구성하지만, 웹 버전은
spaceship.json을TVG.Animation으로 로드합니다.Animation::info()에서 총 프레임과 재생 시간을 구하고, 플레이어가 이동 중일때만 시간 기반 진행률로 프레임을 계산합니다. 이것을 통해 WebCanvas의 Lottie 로드, Animation 정보 조회, 수동 프레임 제어와 Picture Transform을 함께 보여줍니다.게임 루프는
requestAnimationFrame()으로 진행됩니다. 각 콜백에서 현재 경과 시간을 계산하고, FPS를 갱신한 뒤update(),render()를 호출하고 다음 프레임을 다시 예약합니다.requestAnimationFrame()함수는 일반적으로 브라우저의 다음 repaint 직전에 콜백을 실행하고 디스플레이 갱신 주기에 맞춰서 호출됩니다. 그래서 대부분의 브라우저에서는 백그라운드 탭에서 호출이 일시 정지되거나 제한될 수 있습니다.웹에서도 적과 폭발 객체를 Pool 배열로 관리하지만 네이티브처럼 시작 시 폭발 객체를 미리 생성하지 않습니다. 웹에서는 Pool이 비었을 때 새 객체를 지연 생성하고, 이후부터는 반환된 객체를 재사용합니다.
이를 통해 네이티브와 유사한 재사용 수명 주기를 유지하며, 반복적인 객체 생성과 Javascript GC의 프레임 드롭을 방지할 수 있을 것이라 생각됩니다.
웹에 추가된 사운드는
AudioContext와AudioBufferMap을 사용하였고, 실제 재생 시에는AudioBufferSourceNode와 Gain Node를 생성하였습니다.그리고 리소스를 로드할 때 웹판은 번들러가 생성한 URL을 사용해 브라우저 요청으로 데이터를 가져옵니다.
3. 후기
원래는 해커톤 전에 웹 버전과 네이티브 버전의 차이를 분석하고 어느 버전으로 하는지 더 효율적일지를 고민해보려고 했습니다. 그런데 해커톤 기간에 cpp로 그래픽을 구현하기로 결정해버리고 구현하느라 글 작성이 늦어져 버렸습니다..그래서 글 작성이 늦어진 겸 겸사겸사 웹 버전 코드를 자세히 확인해보게 되었습니다. 해당 프로젝트를 보면서 프로젝트의 목적성에 맞는 기능을 어떻게 정할지에 대해 고민하게 되었고 웹 버전으로 다른 사이드 프로젝트를 해 보는 것에 대한 자신감이 조금 생긴 것 같습니다.
All reactions