Skip to content

브라우저의 GPU 가속 이해하기

Published:Edit

인트로: 브라우저가 왜 GPU를 사용할까?

오늘날의 웹은 이미지, 애니메이션, 동영상, 인터랙션으로 가득 차 있다
이런 리치 콘텐츠를 렌더링하는 데 모든 계산을 CPU에만 맡기면 성능 병목이 생길 수 있다.

그래서 브라우저는 일부 그래픽 처리를 GPU(그래픽카드) 로 넘겨 하드웨어 가속을 사용한다.
하지만 모든 상황에서 GPU가 작동하지는 않는다.

GPU가 쓰이는 것 상황이 언제이며, 어떤 영향이 있는지 좀 더 자세히 알고 싶어서, 이 글을 쓰면서 정리해본다.


브라우저 렌더링 파이프라인

HTML → DOM Tree → CSSOM Tree → Render Tree → Layout → Paint → Composite

위 순서는 브라우저가 콘텐츠를 렌더링하는 대략적인 파이프라인이다.

하드웨어(GPU) 가속은 주로 마지막에 있는 Composite 단계에서 일어난다.

레이어(Layer) 분리

GPU가 화면을 그릴 때는 레이어(Layer) 를 조합하여 그리게 된다. 레이어는 포토샵에서 있는 레이어와 마찬가지로 GPU에서 여러 이미지를 겹쳐서 처리하는 방식이다. GPU는 여러 레이어를 병렬적으로 처리하고, 필요할 때만 다시 그리거나 조합할 수 있다.

브라우저가 모든 요소를 하나의 레이어로만 처리한다면, 아주 작은 변경에도 전체 페이지를 다시 렌더링해야 하지만 레이어를 적절히 분리하면 변경이 일어난 부분만 다시 그리면 되기 때문에 성능 향상에 도움이 된다.

그래서 GPU 가속을 활용할 가능성이 있는 부분은 보통 새로운 레이어로 분리하여 처리하게 된다.

개발자 도구에서 레이어 확인하기

참고: 크롬 개발자 도구에서 실제 레이어가 나뉘진 방식을 시각적으로 확인할 수 있다.(개발자 도구 우측 상단 점 3개 → “More tools” → “Layers” 를 선택)

크롬 개발자 도구에서 레이어 확인하기


GPU 가속이 발생하는 상황

브라우저가 GPU 가속을 사용하는 상황은 크게 다음과 같다.

  1. 특정 CSS 속성 사용 (transform, opacity, will-change 등)
  2. 비디오 재생 및 캔버스 요소 처리
  3. WebGL 및 3D 그래픽 렌더링
  4. CSS 애니메이션 및 트랜지션
  5. 복잡한 레이어 합성(Compositing) 작업

각 상황에 대해 자세히 살펴보겠습니다.

1. 특정 CSS 속성 사용

GPU 가속을 유도하거나 자동으로 발생시키는 CSS 속성이 있다.
GPU는 transform, opacity 같은 픽셀 조작을 빠르게 처리하기 위해 별도 레이어로 분리하여 렌더링하고, 필요한 레이어에 대해서 GPU가속을 하게 된다. 이 속성들은 주로 레이아웃, 페인트 단계를 건너뛰고 합성 단계만 수정한다는 특징이 있다.

또한 position: fixed, sticky와 같은 속성이 적용된 요소나, 복잡한 스크롤 영역의 콘텐츠는 브라우저가 독립적으로 합성할 필요가 있다고 판단할 경우 GPU 레이어로 분리되기도 한다.

대표적인 속성들

그 외에도 position: fixed/sticky, overflow와 함께 사용되는 스크롤 요소, perspective, animation, box-shadow, text-shadow 등의 CSS 속성들이 GPU 가속을 유발할 수 있다. 자세한 내용은 CSS Triggers에서 확인 가능하다.

will-change와 애니매이션 최적화

will-change를 사용하면, 애니매이션이 발생하기 전에 gpu layer를 생성하도록 할 수 있다. 애니매이션이 곧 발생할 것이니 준비하라는 뜻이다.

.element {
  will-change: transform, opacity;
}

위와같이 transform과 opacity에서 변화가 일어날 가능성이 있다는 사실을 미리 알린다.

will-change를 사용하지 않으면, 변화가 감지된 후에야 GPU 레이어를 생성 시도하기 때문에 최초 프레임에서 끊기는 현상이 있을 수도 있다.

다만, 너무 많이 쓰면 메모리 누수로 인해 GPU 메모리 과부하 의 원인이 될 수 있어서 꼭 필요한 상황에만 쓰는게 좋다.

GPU 가속이 되지 않는 속성들

이 속성들은 애니메이션을 적용해도 GPU가 아닌 CPU에서 처리되며, 브라우저는 layout → paint → composite의 전 과정을 반복하게 된다.

속성설명
top, left, right, bottom위치 이동을 위해 layout 계산 필요
width, height크기 변화 reflow 발생
margin, padding주변 여백 변경 layout 재계산
background-color, color, border-color색상 변화 paint 단계 필요
border, box-sizing테두리 변화 layout & paint 영향
font-size, line-height텍스트 레이아웃 변화 layout 영향

(주의) 브라우저 호환성

자세한 호환성 정보는 Can I Use에서 확인.


2. 비디오(

<video> 태그로 재생되는 비디오는 대부분 GPU 디코딩을 사용한다.

<canvas>의 경우 WebGL을 사용하는 <canvas>는 GPU 가속이 기본으로 활성화되고, WebGL이 사용되지 않는 일반 <canvas>는 성능, 해상도, 변경 빈도 등에 따라 자동 GPU 승격 여부가 판단된다.

3. WebGL 및 3D 그래픽 렌더링

WebGL은 브라우저에서 OpenGL ES 기반의 3D 그래픽을 처리할 수 있게 해주는 API이다. GPU가 정점 셰이더/프래그먼트 셰이더 등을 통해 직접 연산한다.

예) Three.js, Babylon.js, PlayCanvas 같은 3D 라이브러리 기반 앱

WebGL 코드 예시

// WebGL 컨텍스트 생성
const canvas = document.createElement("canvas");
const gl = canvas.getContext("webgl");

// GPU 가속을 활용한 3D 렌더링 예시
if (gl) {
  // 버텍스 셰이더와 프래그먼트 셰이더 컴파일
  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

  // 셰이더 프로그램 생성 및 GPU에 전달
  const program = gl.createProgram();
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);

  // GPU 메모리에 버퍼 생성
  const positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

  // GPU에서 실행될 셰이더 프로그램 활성화
  gl.useProgram(program);
}

위 코드에서 canvas.getContext('webgl')는 WebGL 컨텍스트를 생성하는 부분이다. (실제로 GPU 가속을 활용하기 위해서는 브라우저, 그래픽 카드, 드라이버가 모두 WebGL을 지원해야 한다.)

WebGL을 통한 GPU 병렬 처리는 다음과 같은 과정으로 이루어진다.

  1. GPU 메모리에 셰이더 프로그램을 로드
  2. 버텍스 데이터를 GPU 버퍼에 저장
  3. GPU에서 직접 셰이더 프로그램을 실행하여 렌더링

예시 코드를 보면 이러한 과정이 잘 드러난다. 먼저 WebGL 컨텍스트를 생성하고, 버텍스와 프래그먼트 셰이더를 컴파일한 후, 셰이더 프로그램을 생성하여 GPU에 전달한다. 그리고 GPU 메모리에 버퍼를 생성하고 셰이더 프로그램을 활성화하는 과정을 거친다.

이런 방식으로 CPU 대신 GPU의 병렬 처리 능력을 활용하면 복잡한 그래픽 작업을 훨씬 효율적으로 처리할 수 있다.

💡 참고: WebGL이 지원되지 않는 환경을 대비해 getContext('webgl')이 실패하면 getContext('2d')로 폴백하는 방어 코드를 작성하는 것이 좋다.

WebGPU (차세대 GPU API)


GPU 가속의 오해와 주의점

translateZ(0)을 사용하면 요소가 GPU 레이어로 분리될 가능성이 높아진다. 이 덕분에 transform, opacity 기반의 애니메이션이 더 부드럽게 작동하게 된다. 그래서 초기에 성능 최적화 목적이나 그래픽 깨짐 방지를 위해 많이 사용하게 된다.

비슷하게 will-change도 브라우저에 해당 속성이 곧 변경될 것이라는 힌트를 주어, 미리 최적화 준비를 하게 만든다. 둘 다 GPU 가속을 유도하는 데 효과적이지만, 실제로 GPU 레이어로 분리되는지는 브라우저가 내부적으로 판단하기 때문에 무조건 적용된다고 보장할 수는 없다.

또한 너무 많은 요소에 translateZ(0)이나 will-change를 남용하면 GPU 메모리를 불필요하게 소비하게 되고, 성능이 저하될 수도 있다. 따라서 실제로 애니메이션이 발생하는 핵심 요소에만 제한적으로 적용하는 것이 좋다.


DevTools로 GPU 가속 확인하는 방법

1. Chrome → Rendering → Layer Borders 표시

2. about:gpu

3. Performance 패널

4. 기타 도구


마무리

GPU 가속을 고려하지 않고 개발해도 대부분의 경우 큰 문제가 발생하지 않아서 깊이 있게 알아보지 않았다. 그러나 이번 기회를 통해 브라우저가 자동으로 처리하는 데에도 한계가 있다는 점을 자세히 알 수 있었다. 특히, translateZ(0)will-change 속성이 GPU 가속을 유도하지만, 반드시 보장하지 않는다는 점도 처음 알게 되었다. GPU 가속의 원리를 이해하고, 각 속성들이 어떤 구조와 상황에서 GPU가 개입되는지 알고 개발을 할 때 좀더 고수준의 최적화를 할 수 있을 것으로 기대가 된다.


참고 문서


Previous Post
나의 SEO 항해기
Next Post
클로저(Closure) 이해하기