Android 2D 렌더링 파이프라인은 하드웨어 가속을 지원하므로 캔버스에서 실행되는 모든 그리기 작업에서 GPU를 사용합니다. 하드웨어 가속을 사용하려면 필요한 리소스가 늘어나므로 앱에서 더 많은 RAM을 사용합니다.
하드웨어 가속은 기본적으로 사용 설정되어 있습니다. 애플리케이션에서 표준 컴포저블만 사용하면 전역으로 사용 설정해도 그리는 데 불리한 효과가 발생하지 않아야 합니다. 그러나 하드웨어 가속은 모든 2D 그리기 작업에 지원되지 않으므로 사용 설정하면 일부 맞춤 그리기 호출에 영향을 줄 수 있습니다. 일반적으로 문제는 보이지 않는 요소, 예외 또는 잘못 렌더링된 픽셀로 표시됩니다. 이 문제를 해결하기 위해 Android에서는 다양한 수준에서 하드웨어 가속을 사용 또는 중지하는 옵션을 제공합니다. 하드웨어 가속 제어를 참고하세요.
애플리케이션에서 맞춤 그리기를 실행하면 하드웨어 가속을 사용하는 실제 하드웨어 기기에서 애플리케이션을 테스트하여 문제를 찾습니다. 그리기 작업 지원 섹션에서는 하드웨어 가속의 알려진 문제와 이를 해결하는 방법을 설명합니다.
프레임워크 API가 있는 OpenGL도 참고하세요.
하드웨어 가속 제어
다음 수준에서 하드웨어 가속을 제어할 수 있습니다.
- 애플리케이션
- 활동
- 창
- 구성 가능
애플리케이션 수준
Android 매니페스트 파일에서 다음 속성을 <application> 태그에 추가하고 전체 애플리케이션에 하드웨어 가속을 사용 설정합니다.
<application android:hardwareAccelerated="true" ...>
활동 수준
하드웨어 가속이 전역적으로 사용 설정된 상태에서 애플리케이션이 제대로 작동하지 않으면 개별 활동에 맞게 제어할 수도 있습니다. 활동 수준에서 하드웨어 가속을 사용 설정하거나 중지하려면 <activity> 요소의 android:hardwareAccelerated 속성을 사용하면 됩니다. 다음 예에서는 전체 애플리케이션에 하드웨어 가속을 사용하도록 설정하고 하나의 활동에는 하드웨어 가속을 사용하지 않게 설정합니다.
<application android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
창 수준
더 세밀하게 제어해야 하는 경우 다음 코드를 사용하여 특정 창에 하드웨어 가속을 사용 설정할 수 있습니다.
window.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)
컴포저블 수준
Compose에는 하드웨어 가속을 사용 중지하는 컴포저별 스위치가 없습니다.
컴포저블을 자체 레이어로 렌더링하려면 Modifier.graphicsLayer를 사용하세요. 이렇게 하면 컴포저블의 그리기 코드를 다시 실행하지 않고도 변환 속성 (예: alpha, scaleX, scaleY, translationX, translationY, rotationX, rotationY, rotationZ, transformOrigin)을 변경할 수 있습니다. 최상의 성능을 위해 항상 수정자의 람다 형식을 사용하여 이러한 속성을 설정하세요.
레이어 내 맞춤 혼합과 같은 고급 그리기 작업을 위해 오프스크린 버퍼를 명시적으로 강제하려면 CompositingStrategy.Offscreen를 사용하세요.
자세한 내용은 그래픽 수정자를 참고하세요.
소프트웨어 렌더링이 엄격하게 필요한 맞춤 그리기 작업이 있는 경우 AndroidView를 사용하여 기존 뷰를 호스팅하고 해당 뷰에서 setLayerType(View.LAYER_TYPE_SOFTWARE, null)를 호출할 수 있습니다.
그리기 작업 지원
하드웨어 가속을 사용하면 2D 렌더링 파이프라인에서 가장 일반적으로 사용되는 Canvas 그리기 작업 외에도 덜 자주 사용되는 여러 작업을 지원합니다. Android와 함께 제공되는 애플리케이션, 표준 컴포저블, 일반적인 고급 시각 효과(예: 반사 및 타일 질감)를 렌더링하는 데 사용되는 모든 그리기 작업이 지원됩니다.
다음 표에서는 다양한 작업의 API 지원 수준을 설명합니다.
| 처음 지원되는 API 수준 | ||||
| 캔버스 | ||||
| drawBitmapMesh()(색상 배열) | 18 | |||
| drawPicture() | 23 | |||
| drawPosText() | 16 | |||
| drawTextOnPath() | 16 | |||
| drawVertices() | 29 | |||
| setDrawFilter() | 16 | |||
| clipPath() | 18 | |||
| clipRegion() | 18 | |||
| clipRect(Region.Op.XOR) | 18 | |||
| clipRect(Region.Op.Difference) | 18 | |||
| clipRect(Region.Op.ReverseDifference) | 18 | |||
| clipRect() 회전 및 관점 포함 | 18 | |||
| 회화 그리기 | ||||
| setAntiAlias()(텍스트의 경우) | 18 | |||
| setAntiAlias()(선의 경우) | 16 | |||
| setFilterBitmap() | 17 | |||
| setLinearText() | ✗ | |||
| setMaskFilter() | ✗ | |||
| setPathEffect()(선의 경우) | 28 | |||
| setShadowLayer()(텍스트 이외) | 28 | |||
| setStrokeCap()(선의 경우) | 18 | |||
| setStrokeCap()(점의 경우) | 19 | |||
| setSubpixelText() | 28 | |||
| Xfermode | ||||
| PorterDuff.Mode.DARKEN (framebuffer) | 28 | |||
| PorterDuff.Mode.LIGHTEN (framebuffer) | 28 | |||
| PorterDuff.Mode.OVERLAY (framebuffer) | 28 | |||
| 셰이더 | ||||
| ComposeShader 내부의 ComposeShader | 28 | |||
| ComposeShader 내부의 동일한 유형의 셰이더 | 28 | |||
| ComposeShader의 로컬 행렬 | 18 | |||
캔버스 크기 조정
하드웨어 가속이 사용된 2D 렌더링 파이프라인은 먼저 크기가 조정되지 않은 그리기를 지원하도록 빌드되었으며, 일부 그리기 작업에서 크기 값을 높이면 품질이 크게 저하됩니다. 이 작업은 GPU를 통해 변형된 1.0 크기의 텍스처로 구현됩니다. API 수준 28부터 모든 그리기 작업은 문제없이 크기를 조정할 수 있습니다.
다음 표에서는 큰 크기를 올바르게 처리하도록 구현을 변경한 경우를 보여줍니다.
| 크기가 조정될 그리기 작업 | 처음 지원되는 API 수준 |
| drawText() | 18 |
| drawPosText() | 28 |
| drawTextOnPath() | 28 |
| 간단한 도형 | 17 |
| 복잡한 도형 | 28 |
| drawPath() | 28 |
| 그림자 레이어 | 28 |
의존하는 그리기 작업이 하드웨어 가속화되지 않은 경우 영향을 받는 그리기를 오프스크린 소프트웨어 Bitmap (또는 ImageBitmap)에 렌더링하고 결과를 그립니다. UI의 나머지 부분은 하드웨어 가속 경로를 유지합니다.
도움말 및 유용한 정보
하드웨어 가속 2D 그래픽으로 전환하면 성능이 즉시 향상되지만, 여전히 다음 권장사항에 따라 GPU를 효과적으로 사용하도록 애플리케이션을 디자인해야 합니다.
- 레이아웃 복잡성 및 리컴포지션 최소화
- 레이아웃 트리를 얕게 유지하고 리컴포즈되는 양을 제한합니다. 변경사항이 가능한 가장 작은 리전을 다시 그리도록 상태 읽기를 가장 좁은 범위로 연기합니다. 예를 들어 컴포저블의 본문이 아닌
Modifier.graphicsLayer { }내에서 애니메이션 상태를 읽습니다. 자세한 내용은 Jetpack Compose 성능을 참고하세요. - 오버드로 방지
- 너무 많은 레이어를 서로 겹쳐 그리지 마세요. 다른 불투명 요소가 위에 있어 완전히 가려진 UI 요소를 삭제합니다. 여러 레이어를 겹쳐서 그려야 하면 레이어를 하나의 레이어로 병합하는 것이 좋습니다. 경험상, 현재 하드웨어에서 프레임당 화면에 있는 픽셀 수(비트맵 개수로 된 투명 픽셀)의 2.5배 이상으로 그리지 않는 것이 좋습니다.
- 그리기 메서드로 렌더링 객체를 만들지 않음
- 일반적으로 렌더링 메서드를 호출할 때마다 새
Paint또는 새Path를 만드는 실수를 합니다. 그러면 가비지 수집기가 더 자주 실행되며 하드웨어 파이프라인의 캐시와 최적화도 우회합니다. 이를 방지하려면 객체를 재사용하고 변경하세요.- 표준 메서드 사용: 표준
DrawScope메서드 (예:drawRect및drawCircle)는 개발자 할당 없이 내부적으로Paint객체를 재사용합니다. - 재할당 대신 변이: 맞춤 로직을 작성할 때 새
Path를 인스턴스화하는 대신path.rewind를 사용하여 기존Path를 지웁니다. - 상태를 효율적으로 유지: 컴포저블 내에서
remember { Path() }를 사용하여 객체를 한 번 할당합니다. 재사용 가능한 맞춤 수정자 확장 프로그램을 빌드하는 경우DrawModifierNode를 사용하여 새 힙 할당을 발생시키지 않고 객체를 할당하고 재사용하는 맞춤Modifier.Node를 구현합니다.
- 표준 메서드 사용: 표준
- 도형을 너무 자주 수정하지 않음
- 예를 들어 복잡한 도형, 경로, 원은 텍스처 마스크를 사용하여 렌더링합니다. 경로를 만들거나 수정할 때마다 하드웨어 파이프라인에서 리소스를 많이 소비하는 새 마스크를 만듭니다.
- 비트맵을 너무 자주 수정하지 않음
- 비트맵의 콘텐츠를 변경할 때마다 다음 번에 그릴 때 이 콘텐츠가 GPU 텍스처로 다시 업로드됩니다.
- 알파 사용 시 주의
Modifier.alpha또는 Compose 애니메이션 API를 사용하여 컴포저블을 반투명으로 만들면 일반적으로 화면 범위 초과 버퍼에 렌더링되므로 필수 채우기 비율이 두 배가 됩니다. 겹치지 않는 콘텐츠의 화면 밖 버퍼 오버헤드를 방지하려면CompositingStrategy.ModulateAlpha를 설정하세요. 개별 그리기 호출의 경우 레이어를 만들지 않고 그리기 명령어 (예:color = Color.Red.copy(alpha = 0.5f))에 알파를 직접 적용합니다.