Hardware Acceleration
Safari의 GPU 레이어 관리와 렌더링 문제
개요
Safari는 성능 최적화를 위해 GPU 레이어를 관리하는 독자적인 전략을 사용합니다.
이는 메모리와 배터리를 절약하기 위한 선택이지만, Hardware Acceleration이 제대로 적용되지않는 등 다양한 렌더링 문제를 일으킵니다.
GPU 레이어 관리의 기본 개념
CPU vs GPU 렌더링
CPU 렌더링 (Software Compositing)
┌──────────────────────────┐
│ 메인 스레드에서 순차 처리 │
│ - 느린 처리 속도 │
│ - 낮은 메모리 사용 │
│ - 배터리 효율적 │
└──────────────────────────┘
GPU 렌더링 (Hardware Acceleration)
┌──────────────────────────┐
│ GPU에서 병렬 처리 │
│ - 빠른 처리 속도 │
│ - 높은 메모리 사용 │
│ - 배터리 소모 증가 │
└──────────────────────────┘
레이어(Compositing Layer)란?
브라우저는 성능 최적화를 위해 특정 요소를 별도의 레이어로 분리하여 GPU에서 처리합니다.
화면 구성:
┌─────────────────────────────────┐
│ Layer 1 (CPU) │
│ ├─ header │
│ └─ content │
├─────────────────────────────────┤
│ Layer 2 (GPU) - animated-box │
│ - 독립적으로 변환/애니메이션 │
│ - 다른 요소 영향 없음 │
└─────────────────────────────────┘
레이어 승격(Promotion) 기준
Chrome/Firefox (적극적 승격):
/* 자동으로 GPU 레이어로 승격되는 속성들 */
.element {
transform: translateX(100px); /* ✅ GPU */
opacity: 0.5; /* ✅ GPU */
filter: blur(5px); /* ✅ GPU */
will-change: transform; /* ✅ GPU */
}
Safari (보수적 승격):
/* Safari는 더 엄격한 기준 적용 */
.element {
transform: translateX(100px); /* ✅ GPU */
opacity: 0.5; /* ⚠️ 경우에 따라 CPU */
filter: blur(5px); /* ⚠️ 경우에 따라 CPU */
}
/* 명시적으로 요청해야 GPU 레이어 생성 */
.element {
opacity: 0.5;
transform: translateZ(0); /* ✅ 강제 GPU */
will-change: opacity; /* ✅ 강제 GPU */
}
Safari의 레이어 관리 철학
핵심 원칙: 성능 vs 메모리의 균형
Safari의 설계 목표:
┌─────────────────────────────────┐
│ 모바일 환경 최적화 │
│ - 제한된 RAM (iPhone: ~6GB) │
│ - 배터리 수명 │
│ - 발열 관리 │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ 보수적 레이어 관리 전략 │
│ 1. 불필요한 레이어 생성 자제 │
│ 2. GPU 사용을 신중하게 결정 │
│ 3. 메모리 절약 우선 │
└─────────────────────────────────┘
Hardware Acceleration 미적용 문제
증상
Chrome/Firefox: 부드러운 fade 애니메이션 ✅
Safari: 버벅이거나 애니메이션이 제대로 작동하지 않음 ❌
원인 분석
Safari의 레이어 판단 프로세스:
1. CSS 파싱
.modal-closed { opacity: 0; transition: opacity 0.3s; }
2. 레이어 승격 판단
├─ 3D transform 있음? → 없음
├─ will-change 있음? → 없음
├─ opacity만 있음? → ⚠️ GPU 레이어로 안 만듦
└─ 결정: CPU에서 처리
3. Transition 실행
CPU 렌더링:
├─ opacity: 0 → 0.1 → 0.2 → ... → 1.0
├─ 각 프레임마다 전체 페이지 repaint
└─ 느리고 버벅이는 애니메이션
성능 영향
// 성능 측정 예시
const performanceComparison = {
Chrome: {
레이어: 'GPU',
FPS: 60,
CPU사용률: '10%',
렌더링방식: '레이어 합성만',
},
Safari_문제상황: {
레이어: 'CPU',
FPS: 30 - 45,
CPU사용률: '40%',
렌더링방식: '전체 페이지 repaint',
},
};
원리:
translateZ(0)의 효과:
1. Safari가 인식: "3D transform이 있네!"
2. GPU 레이어 생성 결정
3. 이후 opacity transition도 GPU에서 처리
4. 부드러운 애니메이션 ✅
주의: translateZ(0)는 실제로 아무것도 이동하지 않음
단지 GPU 레이어를 만들기 위한 트릭
방법 2: will-change - 변경 예고
.modal-closed {
opacity: 0;
transition: opacity 0.3s ease;
/* 브라우저에게 변경 예고 */
will-change: opacity;
}
/* 또는 */
.modal {
will-change: opacity, transform;
}
원리:
will-change의 효과:
1. 브라우저에게 "이 속성이 변경될 예정"이라고 알림
2. 브라우저는 미리 GPU 레이어를 생성
3. 실제 변경 시 빠른 처리
주의사항:
- 모든 요소에 남발하지 말 것 (메모리 낭비)
- 애니메이션 완료 후 제거하는 것이 좋음
방법 3: backface-visibility - 렌더링 최적화
.modal-closed {
opacity: 0;
transition: opacity 0.3s ease;
/* Safari 렌더링 최적화 */
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}