Home 하드웨어 가속
Post
Cancel

하드웨어 가속

하드웨어 가속

하드웨어 가속이란?

브라우저가 페이지를 렌더링하는 과정 중 CSSOM과 DOM 트리를 합쳐서 렌더 트리를 만든 뒤, 렌더 트리를 참조해 화면에 나타낼 부분을 Render Layer를 만드는데 이 과정에서 CPU가 아닌 GPU를 이용해 paint될 레이어들을 Graphics Layer로 분리한다.

CPU가 아닌 하드웨어를 사용하기 때문에 하드웨어 가속 또는 GPU를 사용하기 때문에 GPU 가속이라고도 부른다.

helloworld-201904-sangwoo-ko_3-compositing

가속 대상

렌더 트리에서 다음과 같은 속성을 가지고 있으면 Graphics Layer로 넘어간다.

  • CSS 3D Transform(translate3d, preserve-3d 등)이나 perspective 속성이 적용된 경우
  • <video> 또는 <canvas> 요소
  • CSS3 애니메이션함수나 CSS 필터 함수를 사용하는 경우
  • 자식 요소가 레이어로 구성된 경우
  • z-index 값이 낮은 형제 요소가 레이어로 구성된 경우. 레이어로 구성된 요소의 위에 위치하면 해당 요소도 레이어로 구성된다.

CPU에만 의존해서 렌더링하는 것 보다 비디오나 3D 같이 무거운 부분들은 GPU를 사용해 렌더링하면 성능향상의 이점을 얻을 수 있다.

주의 사항

하드웨어 가속을 이용하면 분명 성능이점을 얻을 수 있지만 페이지의 모든 컨텐츠를 Graphics Layer로 만드는 짓은 하지말자.

구체적으로 다음과 같은 사항들을 유의해서 사용하면된다.

  • 무분별한 하드웨어 가속은 오히려 브라우저를 느리게 한다.
  • 요소에 하드웨어 가속 속성이 부여되면 즉시 대상 영역이 GPU에 업로드되며, 이때 업로드되는 영역이 크면 화면이 깜빡이는 현상이 발생될 수 있다.
  • 요소에 하드웨어 가속 속성이 부여되면 레이어로 분리되며, 레이어는 변경되는 내용이 없는 한 요소를 GPU 메모리에 다시 업로드하지 않는다.
  • 하드웨어 가속 속성을 사용한 요소의 내용이 변경되면 GPU 메모리가 갱신되므로 요소의 내용을 미리 변경한 다음 하드웨어 가속 속성을 부여한다.
  • 성능이 낮은 기기에서 하드웨어 가속을 사용하면 오히려 성능 저하를 가져올 수 있다.

will-change

특정 요소에 의미없는 CSS 3D 속성( transform: translate3d(0, 0, 0) )을 주면 하드웨어 가속을 줄 수 있는데 이러한 방법은 무분별하게 Graphics Layer를 만들고 이 과정의 비용이 크기 때문에 되려 성능 저하를 가져올 수 있다.

따라서 will-change옵션을 사용하면 되는데 이름만 보면 알 수 있듯이 미래에 바뀔 요소를 브라우저에게 미리 알려줘서 최적화를 준비할 수 있게 해주는 옵션이다.

해당 요소에 앞으로 어떤 스타일 변화가 있을 수 있는지 명시해주면 된다.

1
will-change: transform, opacity;

주의 사항

역시 남발하지 말것

1
2
3
4
5
*,
*::before,
*::after {
	will-change: all;
}

모든 요소에 will-change를 페이지를 최적화하겠다는 생각이 기특한 코드이지만 전혀 이점을 가지지 못한다.

브라우저는 이미 가능한 최적화를 시행하고 있으므로 우선 순위가 없는 최적화 코드를 집어넣어버려 오히려 리소스만 사용하는 나쁜 케이스다.

브라우저도 준비가 필요하다

will-change는 브라우저에게 변화가 있는 요소에 힌트를 줘서 최적화의 준비 시간을 준다고 했다.

다음과 같이 호버 이벤트가 발생했을 때 will-change로 힌트를 주고 바로 애니메이션을 발생시키면 브라우저는 준비할 수가 없다…

1
2
3
4
5
.element:hover {
	will-change: transform;
	transition: transform 2s;
	transform: rotate(30deg) scale(1.5);
}

호버 후 클릭 이벤트로 애니메이션을 발생시킨다면?

그정도면 브라우저에겐 충분한 시간이다.

1
2
3
4
5
6
7
8
9
.element {
	transition: transform 1s ease-out;
}
.element:hover { // 마우스를 올리면 힌트를 주고
	will-change: transform;
}
.element:active { // 마우스를 클릭 할시 애니메이션 발생
	transform: rotateY(180deg);
}

일회용, 재사용 will-change

will-change가 요소에 선언되어있으면 그 자체로 리소스를 잡아먹는다. 따라서 해당 요소가 한 번의 변화만 일으키는 일회성 변화를 가지고 있는 요소라면 변화가 일어난 후 자바스크립트로 will-change`속성을 부여하고/지워줄 것을 추천하고 있다.

처음부터 유저와 잦은 상호작용으로 많은 변화가 일어나는 요소라면 그렇게 할 필요가 없이 css 스타일 시트에서 선언해도 좋다.

합성(Composite)

렌더링 과정을 공부해본 사람은 알겠지만 리렌더링 과정에서 가장 리소스를 적게 먹는 과정은 합성이다.

위에서 살펴본 각각의 레이어들에서 페인트 과정을 거친 뒤, 컴포지터 스레드라고 하는 별도의 스레드에서 사용자에게 보여줄 웹 페이지를 합쳐 보여주는 것을 합성이라고 한다. 메인 스레드의 개입 없이 수행되기 때문에 이 과정은 스타일 계산 혹은 자바스크립트 실행을 기다릴 필요가 없어 부드러운 성능을 보여줄 수 있다.

유저가 브라우저에서 스크롤을 내리게되면 컴포지터 스레드는 이미 페인트가 끝난 레이어들의 위치만 옮겨서 재합성한 뒤 다시 보여주면된다.

이벤트 감지

컴포지터 스레드는 분명 자바스크립트가 실행되는 메인 스레드와 다른 스레드다. 그럼 합성된 페이지 내에서 자바스크립트 이벤트가 발생하는 것을 컴포지터 스레드가 어떻게 감지할 수 있을까?

결론부터 말하면 컴포지터 스레드는 이벤트가 달려있는 요소에 대해서 고속 스크롤 불가 영역(non-fast scrollable region)’으로 표시하고 이벤트가 발생하면 해당 이벤트에 대한 정보를 메인 스레드로 보내준다.

이미지

고속 스크롤 불가 영역 외에서 이벤트가 발생하면 메인 스레드에 정보를 보낼 필요가 없기 때문에 컴포지터 스레드는 합성 과정을 진행한다.

이벤트 위임

하지만 고속 스크롤 불가 영역 내에서 이벤트가 발생하면 메인 스레드에 정보를 보내주어야하기 때문에 컴포지터 스레드는 메인 스레드에서 이벤트 처리가 끝날 때 까지 기다려야하는데 이벤트 위임과 이 상황이 겹치면 다음과 같은 일이 벌어진다.

1
2
3
4
5
document.body.addEventListener('touchstart', event => {  
    if (event.target === area) {
        event.preventDefault();
    }
});

이미지

페이지의 모든 영역이 고속 스크롤 불가 영역이 되어버려 부드러운 합성을 할 수 없는 상태가 되어버리는 것

이를 방지하기 위해 메인 스레드의 응답을 기다리지 말고 새 프레임을 만들어도 된다고 명시해주는 옵션이 passive: true다.

1
2
3
4
5
document.body.addEventListener('touchstart', event => {  
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});

https://d2.naver.com/helloworld/2061385

https://d2.naver.com/helloworld/6204533

https://dev.opera.com/articles/ko/css-will-change-property/

This post is licensed under CC BY 4.0 by the author.