하드웨어 가속

Android 3.0(API 수준 11)부터 Android 2D 렌더링 파이프라인에서 하드웨어 가속을 지원하므로, View의 캔버스에서 실행되는 모든 그리기 작업에서 GPU를 사용합니다. 하드웨어 가속을 사용하려면 필요한 리소스가 늘어나므로 앱에서 더 많은 RAM을 사용합니다.

하드웨어 가속은 대상 API 수준이 14 이상이면 기본적으로 사용되지만 명시적으로 사용 설정할 수도 있습니다. 애플리케이션에서 표준 보기와 Drawable만 사용하면 전역으로 사용하도록 설정해도 그리는 데 불리한 효과가 발생하지 않아야 합니다. 그러나 하드웨어 가속은 모든 2D 그리기 작업에 지원되지 않으므로 사용 설정하면 일부 맞춤 보기 또는 그리기 호출에 영향을 줄 수 있습니다. 일반적인 문제로는 보이지 않는 요소, 예외 또는 잘못 렌더링된 픽셀 등이 있습니다. 이 문제를 해결하기 위해 Android에서는 다양한 수준에서 하드웨어 가속을 사용 또는 중지하는 옵션을 제공합니다. 하드웨어 가속 제어를 참고하세요.

애플리케이션에서 맞춤 그리기를 실행하면 하드웨어 가속을 사용하는 실제 하드웨어 기기에서 애플리케이션을 테스트하여 문제를 찾습니다. 그리기 작업 지원 섹션에서는 하드웨어 가속의 알려진 문제와 이를 해결하는 방법을 설명합니다.

Framework API가 있는 OpenGLRenderscript도 참고하세요.

하드웨어 가속 제어

다음 수준에서 하드웨어 가속을 제어할 수 있습니다.

  • 애플리케이션
  • 활동
  • 보기

애플리케이션 수준

Android 매니페스트 파일에서 다음 속성을 <application> 태그에 추가하고 전체 애플리케이션에 하드웨어 가속을 사용 설정합니다.

<application android:hardwareAccelerated="true" ...>

활동 수준

하드웨어 가속이 전역적으로 사용 설정된 상태에서 애플리케이션이 제대로 작동하지 않으면 개별 활동에 맞게 제어할 수도 있습니다. 활동 수준에서 하드웨어 가속을 사용 또는 중지하려면 <activity> 요소의 android:hardwareAccelerated 속성을 사용할 수 있습니다. 다음 예에서는 전체 애플리케이션에 하드웨어 가속을 사용하도록 설정하고 하나의 활동에는 하드웨어 가속을 사용하지 않게 설정합니다.

<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

창 수준

더 세밀하게 제어해야 하는 경우 다음 코드를 사용하여 특정 창에 하드웨어 가속을 사용 설정할 수 있습니다.

Kotlin

window.setFlags(
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)

Java

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

참고: 현재 창 수준에서 하드웨어 가속을 중지할 수 없습니다.

보기 수준

다음 코드를 사용하여 런타임 시 개별 보기의 하드웨어 가속을 중지할 수 있습니다.

Kotlin

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)

Java

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

참고: 보기 수준에서 하드웨어 가속을 사용 설정할 수 없습니다. 보기 레이어에는 하드웨어 가속을 중지하는 것 이외의 다른 기능이 있습니다. 레이어 사용에 관한 자세한 내용은 보기 레이어를 참고하세요.

보기에 하드웨어 가속이 사용되었는지 확인

특히 맞춤 보기와 같은 경우 애플리케이션에서 현재 하드웨어 가속이 사용 설정되었는지를 아는 것이 유용할 수 있습니다. 애플리케이션에서 많은 맞춤 그리기를 실행하고 새 렌더링 파이프라인에서 일부 작업을 제대로 지원하지 않는 경우 특히 유용합니다.

애플리케이션에서 하드웨어 가속이 사용되는지 확인하는 방법에는 다음 두 가지가 있습니다.

그리기 코드에서 이를 확인해야 하는 경우 가능하면 View.isHardwareAccelerated() 대신 Canvas.isHardwareAccelerated()를 사용합니다. 보기를 하드웨어 가속 창에 연결하면 하드웨어 가속이 사용되지 않은 캔버스를 사용하여 여전히 그릴 수 있습니다. 예를 들어 캐싱을 위해 보기를 비트맵으로 그리는 경우입니다.

Android 그리기 모델

하드웨어 가속을 사용하면 Android 프레임워크에서 표시 목록을 사용하여 애플리케이션을 화면에 맞게 렌더링하는 새로운 그리기 모델을 사용합니다. 표시 목록과 애플리케이션에 미치는 영향을 완전히 이해하려면 Android에서 하드웨어 가속 없이 보기를 그리는 방법을 이해하는 것이 좋습니다. 다음 섹션에서는 소프트웨어 기반 및 하드웨어 가속 그리기 모델을 설명합니다.

소프트웨어 기반 그리기 모델

소프트웨어 그리기 모델에서 보기는 다음 두 단계로 그립니다.

  1. 계층 구조 무효화
  2. 계층 구조 그리기

애플리케이션에서 UI의 일부를 업데이트해야 할 때마다 콘텐츠가 변경된 모든 보기에서 invalidate()(또는 변형 중 하나)를 호출합니다. 새로 그려야 하는 화면의 영역(더티 영역)을 계산하기 위해 보기 계층 구조의 맨 위까지 무효화 메시지가 입력됩니다. 그러면 Android 시스템이 계층 구조에서 더티 영역과 교차하는 보기를 그립니다. 이 그리기 모델에는 다음과 같은 두 가지 단점이 있습니다.

  • 먼저, 이 모델에서는 모든 그리기 단계에서 많은 코드를 실행해야 합니다. 예를 들어, 애플리케이션이 버튼에서 invalidate()를 호출하고 그 버튼이 다른 보기에 있으면, 보기가 변경되지 않았어도 Android 시스템에서 보기를 다시 그립니다.
  • 두 번째 문제는 그리기 모델에서 애플리케이션의 버그를 숨길 수 있다는 점입니다. Android 시스템에서는 보기가 더티 영역과 교차할 때 보기를 다시 그리므로, 콘텐츠를 변경한 보기에서 invalidate()를 호출하지 않아도 해당 보기를 다시 그릴 수 있습니다. 이 경우 올바르게 동작하도록 다른 보기를 무효합니다. 이 동작은 애플리케이션을 수정할 때마다 변경될 수 있습니다. 따라서 보기의 그리기 코드에 영향을 주는 데이터나 상태를 수정할 때마다 항상 맞춤 보기에서 invalidate()를 호출해야 합니다.

참고: TextView의 텍스트 또는 배경색 등의 속성이 변경될 때 Android 보기에서 자동으로 invalidate()를 호출합니다.

하드웨어 가속 그리기 모델

Android 시스템에서는 여전히 invalidate()draw()를 사용하여 화면을 업데이트하고 보기를 렌더링하지만, 실제 그리기는 다르게 처리합니다. Android 시스템에서는 즉시 그리기 명령을 실행하지 않고, 보기 계층 구조의 그리기 코드 출력을 포함하는 표시 목록에 기록합니다. 또한 Android 시스템에서 invalidate() 호출을 통해 더티로 표시된 보기의 표시 목록을 기록하고 업데이트만 하면 되도록 최적화 작업이 이루어집니다. 무효화되지 않은 보기는 단순히 이전에 기록된 표시 목록을 다시 실행하여 다시 그릴 수 있습니다. 새 그리기 모델에는 다음 세 단계가 포함됩니다.

  1. 계층 구조 무효화
  2. 표시 목록 기록 및 업데이트
  3. 표시 목록 그리기

이 모델을 사용하면 더티 영역을 교차하는 보기를 사용하지 않고 draw() 메서드를 실행할 수 있습니다. Android 시스템에서 보기의 표시 목록을 기록하게 하려면 invalidate()를 호출해야 합니다. 그러지 않으면 보기가 변경된 후에도 동일하게 표시됩니다.

알파 또는 회전과 같은 특정 속성을 설정하면 타겟팅된 보기를 무효화할 필요가 없으므로(자동으로 실행됨) 표시 목록을 사용하면 애니메이션 성능도 향상됩니다. 이 최적화는 표시 목록이 있는 보기(및 애플리케이션에 하드웨어 가속이 사용된 모든 보기)에도 적용됩니다. 예를 들어 Button 위에 ListView를 포함하는 LinearLayout이 있습니다. LinearLayout의 표시 목록은 다음과 같습니다.

  • DrawDisplayList(ListView)
  • DrawDisplayList(Button)

이제 ListView의 불투명도를 변경하려 한다고 가정합니다. ListView에서 setAlpha(0.5f)를 호출하면 이제 표시 목록에 다음이 포함됩니다.

  • SaveLayerAlpha(0.5)
  • DrawDisplayList(ListView)
  • 복원
  • DrawDisplayList(Button)

ListView의 복잡한 그리기 코드가 실행되지 않았습니다. 대신 시스템에서 훨씬 더 단순한 LinearLayout의 표시 목록만 업데이트했습니다. 하드웨어 가속을 사용하지 않는 애플리케이션에서는 목록과 상위 요소의 그리기 코드가 다시 실행됩니다.

그리기 작업 지원

하드웨어 가속을 사용하면 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

참고: '단순' 도형은 PathEffect가 없는 Paint에서 실행하고 기본이 아닌 조인(setStrokeJoin() / setStrokeMiter() 사용)을 포함하지 않는 drawRect(), drawCircle(), drawOval(), drawRoundRect()drawArc()(useCenter=false) 명령입니다. 이러한 그리기 명령의 기타 인스턴스는 위의 차트에서 '복합'에 속합니다.

애플리케이션이 이러한 누락된 기능 또는 제한사항의 영향을 받으면 setLayerType(View.LAYER_TYPE_SOFTWARE, null)을 호출하여 애플리케이션의 영향을 받은 부분만 하드웨어 가속을 사용 중지할 수 있습니다. 그러면 다른 위치에서는 계속 하드웨어 가속을 활용할 수 있습니다. 애플리케이션의 다양한 수준에서 하드웨어 가속을 사용 및 중지하는 방법에 관한 자세한 내용은 하드웨어 가속 제어를 참고하세요.

보기 레이어

모든 Android 버전에서 보기에는 보기의 그리기 캐시를 사용하거나 Canvas.saveLayer()를 사용하여 화면 범위 초과 버퍼로 렌더링하는 기능이 있습니다. 화면 범위 초과 버퍼나 레이어는 여러 용도로 사용됩니다. 복잡한 보기를 애니메이션으로 보여줄 때 성능을 향상시키거나 구성 효과를 적용하기 위해 사용할 수 있습니다. 예를 들어 Canvas.saveLayer()를 사용하여 임시로 보기를 레이어로 렌더링하는 페이드 효과를 구현한 다음 불투명도 요소를 사용하여 다시 화면에 구성할 수 있습니다.

Android 3.0(API 수준 11)부터 View.setLayerType() 메서드와 함께 레이어를 사용하는 방법 및 시기를 제어하는 기능이 강화됩니다. 이 API에서는 두 개의 매개변수, 즉 사용하려는 레이어 유형과 레이어 구성 방법을 설명하는 선택적 Paint 객체가 있습니다. Paint 매개변수를 사용하여 색상 필터, 특수 블렌딩 모드 또는 불투명도를 레이어에 적용할 수 있습니다. 보기에서는 다음 세 가지 레이어 유형 중 하나를 사용할 수 있습니다.

  • LAYER_TYPE_NONE: 보기는 정상적으로 렌더링되고 화면 범위 초과 버퍼에서 지원하지 않습니다. 이것이 기본 동작입니다.
  • LAYER_TYPE_HARDWARE: 애플리케이션에서 하드웨어 가속을 사용하면 하드웨어에서 하드웨어 텍스처로 보기가 렌더링됩니다. 애플리케이션에서 하드웨어 가속을 사용하지 않으면 이 레이어 유형은 LAYER_TYPE_SOFTWARE와 동일하게 작동합니다.
  • LAYER_TYPE_SOFTWARE: 보기는 소프트웨어에서 비트맵으로 렌더링됩니다.

사용하는 레이어 유형은 목표에 따라 달라집니다.

  • 성능: 하드웨어 레이어 유형을 사용하여 보기를 하드웨어 텍스처로 렌더링합니다. 보기를 레이어로 렌더링하면 보기에서 invalidate()를 호출할 때까지 그리기 코드를 실행하지 않아도 됩니다. 그런 다음 알파 애니메이션과 같은 일부 애니메이션을 레이어에 직접 적용할 수 있습니다. 이 방법은 GPU의 작업에 매우 효율적입니다.
  • 시각 효과: 하드웨어 또는 소프트웨어 레이어 유형과 Paint를 사용하여 보기에 특수 시각 처리를 적용할 수 있습니다. 예를 들어 ColorMatrixColorFilter를 사용하여 흑백으로 보기를 그릴 수 있습니다.
  • 호환성: 소프트웨어 레이어 유형을 사용하여 소프트웨어에서 보기를 강제로 렌더링합니다. 하드웨어 가속이 사용된 보기(예 : 전체 애플리케이션에 하드웨어 가속이 사용되는 경우)에서 렌더링 문제가 발생하는 경우 이 방법을 사용하면 하드웨어 렌더링 파이프라인의 제한사항을 쉽게 해결할 수 있습니다.

보기 레이어 및 애니메이션

하드웨어 레이어에서는 애플리케이션에 하드웨어 가속이 사용되면 더 빠르고 원활하게 애니메이션을 제공할 수 있습니다. 많은 양의 그리기 작업을 생성하는 복잡한 보기를 애니메이션화할 때 항상 초당 60개의 프레임으로 애니메이션을 실행하는 것은 불가능합니다. 하드웨어 레이어를 사용하여 보기를 하드웨어 텍스처로 렌더링하면 이러한 현상을 줄일 수 있습니다. 그런 다음 하드웨어 텍스처를 사용하여 보기를 애니메이션화하면 보기를 애니메이션으로 보여줄 때 보기를 계속 다시 그릴 필요가 없습니다. 보기의 속성을 변경하여 invalidate()를 호출하지 않거나 invalidate()를 수동으로 호출하면 보기를 다시 그리지 않습니다. 애플리케이션에서 애니메이션을 실행하고 원하는 대로 원활하게 결과를 얻지 못하면 애니메이션화된 보기에서 하드웨어 레이어를 사용으로 설정하는 것이 좋습니다.

하드웨어 레이어에서 보기를 지원하는 경우 일부 속성은 화면에서 레이어가 구성되는 방식에 따라 처리됩니다. 이 속성을 설정하면 보기를 무효화하고 다시 그릴 필요가 없으므로 효율적입니다. 다음 속성 목록은 레이어 구성 방식에 영향을 미칩니다. 다음 속성 중 하나에 setter를 호출하면 최적의 무효화가 발생하고 타겟 보기를 다시 그리지 않습니다.

  • alpha: 레이어의 불투명도 변경
  • x, y, translationX, translationY: 레이어의 위치 변경
  • scaleX, scaleY: 레이어의 크기 변경
  • rotation, rotationX, rotationY: 3D 공간에서 레이어의 방향 변경
  • pivotX, pivotY: 레이어의 변환 원본 변경

이러한 속성은 ObjectAnimator로 보기를 애니메이션화할 때 사용되는 이름입니다. 이러한 속성에 액세스하려면 적절한 setter 또는 getter를 호출합니다. 예를 들어 알파 속성을 수정하려면 setAlpha()를 호출합니다. 다음 코드 스니펫에서는 Y축을 중심으로 3D로 보기를 회전하는 가장 효율적인 방법을 보여줍니다.

Kotlin

view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
ObjectAnimator.ofFloat(view, "rotationY", 180f).start()

Java

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator.ofFloat(view, "rotationY", 180).start();

하드웨어 레이어에서는 동영상 메모리를 사용하므로 애니메이션 지속 기간 동안에만 사용하고 애니메이션이 끝난 후에는 중지하는 것이 좋습니다. 애니메이션 리스너를 사용하여 다음을 완료할 수 있습니다.

Kotlin

view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
ObjectAnimator.ofFloat(view, "rotationY", 180f).apply {
    addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            view.setLayerType(View.LAYER_TYPE_NONE, null)
        }
    })
    start()
}

Java

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});
animator.start();

속성 애니메이션에 관한 자세한 내용은 속성 애니메이션을 참고하세요.

도움말 및 유용한 정보

하드웨어 가속 2D 그래픽으로 전환하면 성능이 즉시 향상되지만, 여전히 다음 권장사항에 따라 GPU를 효과적으로 사용하도록 애플리케이션을 디자인해야 합니다.

애플리케이션의 보기 수 줄이기
시스템에서 그려야 하는 보기 수가 많을수록 속도가 느려집니다. 이 점은 소프트웨어 렌더링 파이프라인에도 적용됩니다. 보기를 줄이는 것이 UI를 최적화하는 가장 쉬운 방법 중 하나입니다.
오버드로 방지
너무 많은 레이어를 서로 겹쳐 그리지 마세요. 다른 불투명 보기가 위에 있어 완전히 가려진 보기는 제거합니다. 여러 레이어를 겹쳐서 그려야 하면 레이어를 하나의 레이어로 병합하는 것이 좋습니다. 경험상, 현재 하드웨어에서 프레임당 화면에 있는 픽셀 수(비트맵 개수로 된 투명 픽셀)의 2.5배 이상으로 그리지 않는 것이 좋습니다.
그리기 메서드로 렌더링 객체를 만들지 않음
일반적으로 렌더링 메서드를 호출할 때마다 새 Paint 또는 새 Path를 만드는 실수를 합니다. 그러면 가비지 수집기가 더 자주 실행되며 하드웨어 파이프라인의 캐시와 최적화도 우회합니다.
도형을 너무 자주 수정하지 않음
예를 들어 복잡한 도형, 경로 및 원은 텍스처 마스크를 사용하여 렌더링합니다. 경로를 만들거나 수정할 때마다 하드웨어 파이프라인에서 리소스를 많이 소비하는 새 마스크를 만듭니다.
비트맵을 너무 자주 수정하지 않음
비트맵의 콘텐츠를 변경할 때마다 다음 번에 그릴 때 이 콘텐츠가 GPU 텍스처로 다시 업로드됩니다.
알파 사용 시 주의
setAlpha(), AlphaAnimation 또는 ObjectAnimator를 사용하여 보기를 반투명으로 만들면 화면 범위 초과 버퍼에 렌더링되므로, 필수 채우기 비율이 두 배가 됩니다. 매우 큰 보기에 알파를 적용할 때 보기의 레이어 유형을 LAYER_TYPE_HARDWARE로 설정하는 것이 좋습니다.