이 글은 아래 원문을 번역한 글로 의역이 있을 수 있습니다. 정확한 의미를 파악하고 싶으신 분은 원문을 참고해주시기 바랍니다.
원문: https://developer.chrome.com/blog/renderingng-architecture/
저작권 정보: https://developers.google.com/terms/site-policies
이 글은 크롬 렌더링 엔진에 있는 시리즈의 일부분이다. 렌더링NG, 주요 데이터 구조, VideoNG 및 LayoutNG에 대해 자세히 알아보려면 나머지 시리즈를 확인하십시오.
이전 게시물에서는 렌더링NG 아키텍처 목표와 주요 속성에 대한 개요를 제공했다. 이 게시물에는 구성 요소 조각의 설정 방법과 렌더링 파이프라인이 어떻게 구성 요소를 통과하는지 설명된다.
가장 높은 레벨에서 시작하여 그 아래로 드릴다운하는 렌더링 작업은 다음과 같다.
- 화면의 내용을 픽셀로 렌더링하십시오.
- 콘텐츠에 대한 시각적 효과를 한 상태에서 다른 상태로 애니메이션화하십시오.
- 입력에 응답하여 스크롤하십시오.
- 개발자 스크립트 및 기타 하위 시스템이 응답할 수 있도록 입력을 올바른 위치에 효율적으로 전달하십시오.
렌더링할 내용은 각 브라우저 탭에 대한 프레임 트리와 브라우저 UI이다. 그리고 터치 스크린, 마우스, 키보드 및 기타 하드웨어 장치의 원시 입력 이벤트 스트림(raw input events)이다.
각 프레임에는 다음이 포함된다.
- DOM 상태
- CSS
- 캔버스
- 이미지, 비디오, 글꼴 및 SVG와 같은 외부 리소스
프레임은 HTML 문서와 그 URL을 더한 것이다. 브라우저 탭에 로드된 웹 페이지에는 최상위 프레임과 최상위 문서에 포함된 각 iframe에 대한 하위 프레임, 그리고 그들의 반복적인 iframe 하위 항목이 있다.
시각 효과는 스크롤, 변환, 클립, 필터, 불투명도 또는 혼합과 같은 비트맵에 적용되는 그래픽 연산이다.
아키텍처 구성요소
렌더링NG에서 이러한 태스크는 여러 단계와 코드 구성요소에 걸쳐 논리적으로 분할된다. 그 구성 요소는 결국 그 스레드 내의 다양한 CPU 프로세스, 스레드 및 하위 구성 요소로 귀결된다. 각각은 모든 웹 콘텐츠에 대한 신뢰성, 확장 가능한 성능 및 확장성을 달성하는 데 중요한 역할을 한다.
렌더링 파이프라인 구조
렌더링은 여러 단계의 파이프라인에서 진행되며, 그 과정에서 부산물이(artifacts) 생성된다. 각 단계는 렌더링 내에서 하나의 잘 정의된 작업을 수행하는 코드를 나타낸다. 부산물은(artifacts) 단계의 입력 또는 출력인 데이터 구조물이다. 다이어그램에서 입력 또는 출력은 화살표로 표시된다.
이 게시물은 부산물(artifacts) 들에 대해 자세히 다루지 않을 것이다; 그것은 다음 게시물인 '렌더링NG에서 핵심 데이터 구조 및 역할'에서 논의될 것이다.
파이프라인 단계
위의 그래프에서 단계는 스레드 또는 프로세스를 실행하는 색상으로 표시된다.
- 녹색: 메인 스레드 프로세스 렌더링 (render process main thread)
- 노란색: 합성기 프로세스 렌더링 (render process compositor)
- 주황색: viz 프로세스 (viz process)
상황에 따라 여러 곳에서 실행할 수 있는 경우도 있어 두 가지 색상이 있는 것도 있다.
단계는 다음과 같다.
- 애니메이트: 선언적 타임라인에 따라 시간이 지남에 따라 계산 스타일을 변경하고 속성 트리를 변이시킨다.
- 스타일: CSS를 DOM에 적용하고 계산된 스타일을 생성한다.
- 레이아웃: 화면에서 DOM 요소의 크기와 위치를 결정하고 불변의 조각(fragment) 트리를 생성한다.
- 사전 페인팅: 속성 트리를 계산하고 필요에 따라 기존 디스플레이 목록과 GPU 텍스처 타일을 무효화함
- 스크롤: 속성 트리를 변경하여 문서 및 스크롤 가능한 DOM 요소의 스크롤 오프셋을 업데이트.
- 페인트: DOM에서 GPU 텍스처 타일을 래스터하는 방법을 설명하는 디스플레이 목록을 계산.
- 커밋: 속성 트리와 표시 목록을 합성기 스레드에 복사.
- Layerize: 독립 래스터라이징 및 애니메이션을 위해 디스플레이 목록을 복합 레이어 목록으로 분할.
- 래스터, 디코딩 및 paint worklets: 디스플레이 목록, 인코딩된 이미지 및 도장paint worklets코드를 각각 GPU 텍스처 타일로 변환.
- 활성화: 화면에 GPU 타일을 그리고 배치하는 방법과 시각적 효과를 나타내는 합성기 프레임을 만드십시오.
- Aggregate: 보이는 모든 합성기 프레임에서 합성기 프레임을 하나의 글로벌 합성기 프레임으로 결합.
- 그리기: GPU에서 집계된 합성기 프레임을 실행하여 화면에 픽셀을 생성.
렌더링 파이프라인의 단계는 필요하지 않은 경우 생략할 수 있다. 예를 들어 시각 효과와 스크롤의 애니메이션은 레이아웃,사전 페인팅 및 페인트를 건너뛸 수 있다. 도표에 애니메이션과 스크롤이 노랗고 초록색 점으로 표시된 것도 이 때문이다. 시각적 효과를 위해 배치, 사전 페인팅, 페인트 등을 생략할 수 있는 경우 합성기 스레드에서 전체를 실행하고 메인 스레드를 건너뛸 수 있다.
브라우저 UI 렌더링은 여기에 직접 설명되지는 않지만, 동일한 파이프라인의 단순화된 버전(사실상 구현은 코드의 대부분을 공유한다)이라고 생각할 수 있다. 비디오(직접 묘사되지 않음)는 일반적으로 프레임을 GPU 텍스처 타일로 디코딩하고 합성기 프레임과 그리기 단계에 연결하는 독립적인 코드를 통해 렌더링된다.
프로세스 및 스레드 구조
크롬 아키텍처에는 여기에 묘사된 것보다 더 많은 CPU 프로세스와 스레드가 있다. 이 도표는 렌더링에 중요한 도표에만 초점을 맞춘다.
CPU 프로세스
다중 CPU 프로세스를 사용하면 사이트 간 및 브라우저 상태 간 성능 및 보안 격리, GPU 하드웨어와의 안정성 및 보안 격리가 달성된다.
- 렌더링 프로세스는 단일 사이트 및 탭 조합에 대한 입력을 렌더링, 애니메이션, 스크롤 및 라우팅한다. 많은 렌더링 과정이 있다.
- 브라우저 프로세스는 브라우저 UI(URL 표시줄, 탭 제목 및 아이콘 포함)에 대한 입력을 렌더링, 애니메이션 및 라우팅하고 나머지 모든 입력을 적절한 렌더링 프로세스로 라우팅한다. 정확히 하나의 브라우저 프로세스가 있다.
- Viz 프로세스는 여러 렌더 프로세스와 브라우저 프로세스의 합성을 취합한다. 그것은 GPU를 사용하여 광을 내고 그린다. 정확히 하나의 Viz 프로세스가 있다.
서로 다른 사이트는 항상 서로 다른 렌더링 프로세스로 끝난다. (실제로는 데스크톱에서는 항상, 모바일에서는 가능하면. 아래에 "항상"이라고 쓰겠지만, 이 주의사항은 전체에 적용된다.)
동일한 사이트의 여러 브라우저 탭 또는 창은 탭이 관련되지 않는 한(하나는 다른 탭을 여는 경우) 서로 다른 렌더 프로세스를 거친다. 데스크탑의 강한 메모리 압력에 따라 크롬은 관련이 없더라도 동일한 사이트의 여러 탭을 동일한 렌더링 프로세스에 넣을 수 있다.
단일 브라우저 탭 내에서 서로 다른 사이트의 프레임은 항상 서로 다른 렌더 프로세스에 있지만 동일한 사이트의 프레임은 항상 동일한 렌더 프로세스에 있다. 렌더링의 관점에서, 다중 렌더 프로세스의 중요한 장점은 사이트 간 iframe과 탭이 서로 성능 격리를 달성한다는 것이다. 게다가, origin은 훨씬 더 많은 고립을 선택할 수 있다.
모든 크롬에는 정확히 하나의 Viz 프로세스가 있다. 결국, 보통 GPU와 화면은 한 개 밖에 없다. Viz를 자체 프로세스로 분리하는 것은 GPU 드라이버나 하드웨어 버그 발생 시 안정성에 도움이 된다. Vulkan과 같은 GPU API에도 중요한 보안격리에도 좋다. 일반적으로 보안에도 중요하다.
브라우저에는 많은 탭과 창이 있을 수 있고, 모두 그릴 브라우저 UI 픽셀이 있기 때문에, 여러분은 왜 브라우저 프로세스가 하나뿐인가?라고 생각할 수 있다. 그 이유는 한 번에 한 개만 집중되기 때문이다. 사실, 보이지 않는 브라우저 탭은 대부분 비활성화되고 모든 GPU 메모리를 삭제한다. 그러나 복잡한 브라우저 UI 렌더링 기능은 렌더링 프로세스(웹UI로 알려져 있음)에서도 점점 더 많이 구현되고 있다. 이것은 성능 격리를 위한 것이 아니라 크롬의 웹 렌더링 엔진의 사용 편의성을 이용하기 위한 것이다.
구형 안드로이드 기기에서는 WebView에서 사용할 때 렌더와 브라우저 프로세스가 공유된다(이는 일반적으로 안드로이드의 크롬에는 적용되지 않는다, WebView만 해당). WebView에서는 브라우저 프로세스도 임베딩 앱과 공유되고 WebView는 렌더 프로세스가 하나만 있다.
보호된 비디오 콘텐츠를 디코딩하는 유틸리티 프로세스도 가끔 있다. 이 과정은 위에서 설명되지 않는다.
스레드
스레드는 느린 작업, 파이프라인 병렬화 및 다중 버퍼링에도 불구하고 성능 격리 및 응답성을 달성하는 데 도움이 된다.
- 메인 스레드는 스크립트, 렌더링 이벤트 루프, 문서 라이프사이클, 히트 테스트, 스크립트 이벤트 디스패치 및 HTML, CSS 및 기타 데이터 형식의 구문을 실행한다.
- 메인 스레드 헬퍼는 인코딩이나 디코딩이 필요한 이미지 비트맵과 블롭 생성과 같은 작업을 수행한다.
- 웹 작업자는 OffscreenCanvas에 대한 스크립트 및 렌더링 이벤트 루프를 실행한다.
- Compositer 스레드는 입력 이벤트를 처리하고, 웹 콘텐츠의 스크롤 및 애니메이션을 수행하고, 웹 콘텐츠의 최적 레이어화를 계산하며, 이미지 디코딩, 페인트 작업 및 래스터 작업을 조정한다.
- 컴포지터 스레드 헬퍼는 Viz 래스터 작업을 조정하고 이미지 디코딩 작업, 워크렛 도장 및 폴백 래스터를 실행한다.
- 미디어, demuxer 또는 오디오 출력 스레드는 비디오 및 오디오 스트림을 디코딩, 처리 및 동기화한다. (비디오는 메인 렌더링 파이프라인과 병렬로 실행된다는 점을 기억하십시오.)
메인 스레드 및 컴포지터 스레드를 분리하는 것은 애니메이션의 성능 격리 및 메인 스레드 작업에서 스크롤하는 데 매우 중요하다.
동일한 사이트의 여러 탭 또는 프레임이 동일한 프로세스에 포함될 수 있지만 렌더 프로세스당 하나의 메인 스레드만 있을 수 있다. 그러나, 다양한 브라우저 API에서 수행되는 작업으로부터 성능 격리가 있다. 예를 들어, 캔버스 API의 이미지 비트맵과 블롭 생성은 메인 스레드 도우미 스레드에서 실행된다.
마찬가지로 렌더 프로세스당 합성자 스레드는 하나만 있다. 컴포지터 스레드의 정말 값비싼 작업은 모두 컴포지터 작업자 스레드나 Viz 공정에 위임되어 있으며, 이 작업은 입력 라우팅, 스크롤링 또는 애니메이션과 병행할 수 있기 때문에 일반적으로 단 한 가지뿐이라는 것은 문제가 되지 않는다. 컴포지터 작업자 스레드는 Viz 프로세스에서 실행되는 작업을 조정하지만, 어디서나 GPU 가속화는 드라이버 버그와 같이 크롬이 제어하지 않는 이유로 실패할 수 있다. 이러한 상황에서 작업자 스레드는 CPU의 예비 모드에서 작업을 수행한다.
컴포지터 작업자 스레드 수는 장치의 기능에 따라 달라진다. 예를 들어, 데스크톱은 일반적으로 더 많은 스레드를 사용하게 되는데, 이는 모바일 장치보다 CPU 코어가 더 많고 배터리 제약이 덜하기 때문이다. 스케일업과 스케일다운의 예다.
또한 렌더 프로세스 스레딩 아키텍처는 세 가지 다른 최적화 패턴의 적용이라는 점도 흥미롭다.
- 도우미 스레드: 장기간 실행 중인 하위 작업을 추가 스레드로 전송하여 부모 스레드가 동시에 발생하는 다른 요청에 응답하도록 함. 메인 스레드 도우미와 컴포지터 도우미 스레드는 이 기법의 좋은 예다.
- 다중 버퍼링: 렌더링 대기 시간을 숨기기 위해 새 콘텐츠를 렌더링하는 동안 이전에 렌더링된 콘텐츠를 표시. 합성자 스레드는 이 기법을 사용한다.
- 파이프라인 병렬화: 렌더링 파이프라인을 여러 곳에서 동시에 실행 스크롤과 애니메이션은 병렬로 실행될 수 있기 때문에 메인 스레드 렌더링 업데이트가 발생하더라도 스크롤과 애니메이션이 빨라질 수 있는 방법이다.
브라우저 프로세스
- 렌더링 및 컴포지팅 스레드는 브라우저 UI의 입력에 응답하고, 다른 입력 정보를 올바른 렌더링 프로세스로 라우팅하며, 브라우저 UI를 배치하고 도색한다.
- 렌더링 및 컴포지팅 스레드 헬퍼는 이미지 디코딩 작업과 폴백 래스터 또는 디코딩을 실행한다.
브라우저 프로세스 렌더링 및 컴포지팅 스레드는 메인 스레드와 컴포지터 스레드가 하나로 결합되는 것을 제외하고는 렌더 프로세스의 코드 및 기능과 유사하다. 설계에 의한 스레드가 없기 때문에 긴 메인 스레드 작업으로부터 성능을 분리할 필요가 없기 때문에 이 경우에는 스레드가 하나만 필요하다.
Viz 프로세스
- GPU 메인 스레드 래스터는 GPU 텍스처 타일에 목록과 비디오 프레임을 표시하고 합성기 프레임을 화면에 그린다.
- 디스플레이 합성기 스레드는 화면 표시를 위해 각 렌더 프로세스와 브라우저 프로세스의 합성물을 단일 합성기 프레임으로 집계하고 최적화한다.
래스터와 드로잉은 일반적으로 같은 스레드에서 발생하는데, 둘 다 GPU 자원에 의존하고 있고, GPU를 안정적으로 다중 스레드 방식으로 활용하기 어렵기 때문이다(GPU에 대한 보다 쉬운 멀티 스레드 접근은 새로운 Vulkan 표준을 개발하는 하나의 동기이다.). Android WebView에서는 WebView가 네이티브 앱에 내장되는 방식 때문에 그리기 위한 OS 레벨 렌더 스레드가 따로 있다. 다른 플랫폼은 앞으로 이런 실마리를 잡을 것으로 보인다.
디스플레이 합성기는 GPU 메인 스레드의 감속 가능한 원천을 차단하지 않고 항상 반응해야 하기 때문에 다른 스레드에 있다. GPU 메인 스레드가 느려지는 원인 중 하나는 예측하기 어려운 방법으로 느릴 수 있는 벤더별 GPU 드라이버와 같은 비 크로미엄 코드로의 호출이다.
구성요소 구조
각 렌더 프로세스 메인 또는 컴포지터 스레드 내에는 구조화된 방식으로 상호 작용하는 논리적 소프트웨어 구성요소가 있다.
Render process main thread components
- Blink renderer:
- 로컬 프레임 트리 조각은 로컬 프레임의 트리와 프레임 내의 DOM을 나타낸다.
- DOM 및 Canvas APIs 구성 요소는 이러한 API의 모든 구현을 포함한다.
- document 라이프사이클 러너는 커밋 단계까지 렌더링 파이프라인 단계를 실행한다.
- 입력 이벤트 히트 테스트 및 디스패치 컴포넌트는 히트 테스트를 실행하여 이벤트의 대상이 되는 DOM 요소를 파악하고, 입력 이벤트 디스패치 알고리즘과 디폴트 동작을 실행한다.
- 렌더링 이벤트 루프 스케줄러 및 runner는 이벤트 루프에서 실행할 항목과 시기를 결정한다. 장치 디스플레이와 일치하는 시점에서 렌더링이 발생하도록 예약합니다.
로컬 프레임 트리 조각들은 생각하기에 좀 복잡하다. 프레임 트리가 기본 페이지 및 하위 페이지임을 반복적으로 기억하십시오. 프레임은 렌더 프로세스에서 렌더링되는 경우 렌더 프로세스의 로컬이며, 그렇지 않으면 원격이다.
렌더링 공정에 따라 프레임을 색칠하는 것을 상상할 수 있다. 앞의 이미지에서 녹색 원은 모두 하나의 렌더링 공정에서 프레임이며, 빨간색 원은 1초, 파란색 원은 3분의 1이다.
로컬 프레임 트리 파편은 프레임 트리에서 동일한 색상의 연결된 구성 요소다. 이미지에는 사이트 A의 경우 2개, 사이트 B의 경우 1개, 사이트 C의 경우 1개의 로컬 프레임 트리가 있다. 각 로컬 프레임 트리는 자체 블링크 렌더러 구성요소를 가진다. 로컬 프레임 트리의 블링크 렌더러는 다른 로컬 프레임 트리와 동일한 렌더 프로세스에 있을 수도 있고 아닐 수도 있다(앞서 설명한 렌더 프로세스를 선택하는 방법에 따라 결정됨).
Render process compositor thread structure
렌더 프로세스 합성기 구성 요소에는 다음이 포함된다.
- 복합 계층 목록, 표시 목록 및 속성 트리를 유지하는 데이터 핸들러.
- 애니메이션, 스크롤, 합성, 래스터 및 렌더링 파이프라인의 디코딩 단계를 실행하는 라이프사이클 러너. (애니메이션 및 스크롤은 메인 스레드 및 합성기에서 모두 발생할 수 있음을 기억하십시오.)
- 입력 및 히트 테스트 핸들러는 복합 레이어의 분해능에서 입력 처리 및 히트 테스트를 수행하여 합성자 스레드에서 스크롤 제스처를 실행할 수 있는지 여부 및 어떤 렌더링 프로세스 히트 테스트를 대상으로 해야 하는지 판단한다.
결론
보다시피 크롬에서 렌더링하는 것은 꽤 복잡해! 모든 작품을 기억하고 내면화하는 데는 많은 시간이 걸릴 수 있으니, 압도적으로 보여도 걱정하지 마십시오.
가장 중요한 테이크아웃은 개념적으로 간단한 렌더링 파이프라인이 있다는 것인데, 이 파이프라인은 신중한 모듈화와 세부적인 주의를 통해 다수의 자급자족 구성 요소로 분리되었다. 이러한 구성요소는 확장 가능한 성능과 확장 가능성을 극대화하기 위해 병렬 프로세스와 스레드에 걸쳐 분할되었다.
이러한 각 구성요소는 모든 성능을 가능하게 하는 데 중요한 역할을 하며 현대적인 웹 앱이 필요로 하는 특징을 가지고 있다. 곧 우리는 그들 각각과 그들이 하는 중요한 역할들에 대해 심층적인 연구를 발표할 것이다.
그러나 그 전에 이 포스트에 언급된 주요 데이터 구조(렌더링 파이프라인 다이어그램의 측면에 파란색으로 표시된 구조)가 코드 구성요소만큼 렌더링NG에 중요한지 설명하겠다.
읽어줘서 고맙고, 채널을 고정해!
우나 크라베츠의 삽화.
'Chrome' 카테고리의 다른 글
Inside look at modern web browser (part 1) (0) | 2022.11.02 |
---|---|
Key data structures and their roles in RenderingNG(한글)(2) (0) | 2022.04.20 |
Key data structures and their roles in RenderingNG(한글)(1) (0) | 2022.03.09 |