[Study Note] Example 예제가 실제로 화면에 그려지기까지의 코드 흐름 추적하기. - 3 #54
jeongsunyong
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.
[Study Note] ThorVG 실행 흐름 분석 (3)
StudyShape::content()에서 호출한 네 개의 Public API가 ThorVG 내부에 어떤 상태를 만드는지 확인했다.이 시점의 ShapeImpl에는 사각형의 Geometry와 Appearance를 나타내는 backend 독립적인 렌더링 입력 데이터와, 이를 Scene에서 관리하기 위한 상태가 구성되어 있다.
RenderShape에는 사각형 Path와 빨간색 Fill이 저장되어 있고,Paint::Impl에는 Path | Color | Transform 변경 flag가 누적되어 있다. But 아직 Shape의renderer와 backend 전용 데이터인 rd는 nullptr이다.오늘은
content()가 반환된 이후, 이 Shape가 Software Renderer를 거쳐 SDL Surface의 실제 픽셀이 되기까지 추적해 보았다.1. 전체 Flow
Canvas가 Scene의 변경 사항을 확인하면
→ Software Renderer가 Shape를 그릴 수 있는 형태로 준비.
→ Rasterizer가 target buffer에 픽셀을 기록한다.
→ example이 완성된 target을 Window에 표시
여기서 어제 확인한 Paint와 RenderShape를 구성, canvas에 add하는것이 Scene구성 항목에 해당하는 부분이다.
오늘 범위는 SwShapeTask 생성 까지.
2.
Canvas::draw()StudyShape::content()가 반환되면 example 공통 실행부인Window::ready()가 첫 렌더링을 시작한다.content()와 draw()는 Canvas 객체를 통해 shape data 그릴 대상으로 연결한다고 보면 될것같다.
Example.h — Window::ready()
Canvas::draw()라는 이름만 보면 곧바로 target에 픽셀을 기록할 것 같지만, 내부에서는 먼저 Canvas의 상태를 확인한다.tvgCanvas.h — Canvas::Impl::draw()
Canvas::add()가 호출되면서 Canvas 상태는Painting이 된다. (상태를 바꾼다.)StudyShape가
Canvas::update()를 명시적으로 호출하지 않아도draw()가 먼저 update를 수행.새로 추가되거나 변경된 Shape를 먼저 처리하는 것이라 보면 될 것 같다. 처리된 결과를 그림
draw()는 내부적으로 Update, Render 두 작업을 묶는 상위 진입점이라고 볼 수 있을 것 같다.3. Canvas는 root Scene부터 순회
Canvas::Impl::update()는 identity matrix와 opacity 255를 시작값으로 root Scene의 update를 호출한다.tvgCanvas.h — Canvas::Impl::update()
여기서 Canvas가 자신에게 추가된 Shape를 직접 하나씩 처리하는 것은 아니다. Canvas 내부의 root Scene에서 출발한다.
SceneImpl::update()는 자신이 가진 Paint 목록을 순회하며 각 Paint의 update를 호출한다.tvgScene.h — SceneImpl::update()
StudyShape의 root Scene에는 Paint가 하나만 등록되어 있는 상태이다. (빨간색 사각형 1개만 등록)
그렇기 때문에
paints[0]인 빨간 사각형의 update가 호출된다.Scene 안에 여러 Paint가 있거나 Scene이 중첩되더라도 같은 방식으로 Paint tree를 순회할 수 있다.
Canvas는 root Scene을 기준으로 렌더링할 Paint tree를 관리하고, 그 tree의 변경 사항을 현재 Canvas에 연결된 Renderer로 전달하는 실행 흐름도 관리한다. (Shape를 담는 컨테이너정도보다 더 큰 개념이다)
4. Paint::Impl::update() : 모든 Paint의 공통 처리
Scene에서 자식 Paint의 update를 호출하면 먼저
Paint::Impl::update()에 들어간다.update에서는 Shape뿐 아니라 Scene, Picture, Text 등 Paint 계열에 공통으로 필요한 처리를 담당한다.
tvgPaint.cpp — Paint::Impl::update()
전체 구현에는 masking, clipping, composition 처리도 포함되어 있지만, 현재 StudyShape에는 단순 사각형이므로 해당 처리들이 포함되어있지 않다. 핵심 흐름만 보면
저번 확인에서
ShapeImpl의renderer는 아직nullptr이었다.이 값은
Paint::Impl::update()에서 처음으로 현재 Canvas의 Renderer를 가리키게 된다.SW 모드로 실행했으므로 실제 Renderer 객체는
SwRenderer다.그리고 Public API 호출로 누적한
Path | Color | Transformflag가 구체 Shape의 update에 전달된다. 처리가 끝나면renderFlag는None으로 초기화된다.Public API를 호출할 때마다 즉시 전체 Shape를 다시 계산하는 것이 아니라, 무엇이 바뀌었는지를 flag로 모아 두었다가 update 단계에서 Renderer에 전달하는 구조.
5.
ShapeImpl::update()— Software Renderer로 넘어가는 지점impl.rd = renderer->prepare( rs, impl.rd, transform, clips, opacity, flag, clipper );tvgShape.h — ShapeImpl::update()
여기서
rs는 RenderShape. 사각형 Path와 빨간색 Fill 정보가 들어 있다.renderer의 타입은 공통 인터페이스인RenderMethod*이지만, 현재 Canvas가 가진 실제 객체는SwRenderer다. 따라서renderer->prepare()를 호출하면 실제로SwRenderer::prepare()가 실행된다.이 지점이 ThorVG의 공통 Shape 처리에서 Software Renderer 구현으로 넘어가는 경계다.
6.
SwRenderer::prepare()—SwShapeTask생성SwRenderer::prepare()는 기존 backend 데이터가 있는지 확인한다.tvgSwRenderer.cpp — SwRenderer::prepare()
첫 draw에서는
Paint::Impl::rd가nullptr이므로 새로운SwShapeTask를 생성한다.update가 끝난 뒤

ShapeImpl의 상태는 다음과 같이 연결된다.RenderShape가 Software Renderer 전용 데이터로 바뀌는 것은 아니다. RenderShape는 그대로 유지되고, Software Renderer가 이를 참조하는 SwShapeTask를 만든다.
7. 정리
요약하면, 위와 같이 content()에서 만든 Shape는 Canvas의 root Scene에 저장되고,
이후 draw()가 같은 Scene을 순회하며 변경된 Shape를 현재 Renderer에 전달한다.
SW 모드에서는
ShapeImpl::update()의 renderer->prepare() 호출을 통해 SwRenderer로 넘어가고,RenderShape를 참조하는 SwShapeTask가 생성된다.
이번 글에서는 Shape가 Software Renderer의 작업으로 준비되는 지점까지 확인했다.
다음 글에서는
SwShapeTask가 실제로 실행된 뒤, 사각형이 SDL Surface의 픽셀로 기록되고 Window에 표시되는 과정을 이어서 확인할 예정이다.All reactions