Renderscript

RenderScript는 계산이 많은 작업을 Android에서 높은 성능으로 실행하기 위한 프레임워크입니다. 이는 데이터 병행 계산에 사용되도록 되어 있지만 연속적인 워크로드도 이점을 누릴 수 있습니다. RenderScript 런타임은 기기에서 제공되는 여러 프로세서(예: 멀티 코어 CPU 및 GPU)에 걸쳐 작업을 병렬화하여 개발자가 작업 예약이 아닌 알고리즘 표현에 집중할 수 있게 해줍니다. RenderScript는 이미지 처리, 컴퓨팅 사진, 컴퓨터 비전을 수행하는 애플리케이션에 특히 유용합니다.

Android 8.0 이상을 실행하는 기기는 다음과 같은 RenderScript 프레임워크 및 공급업체 HAL을 사용합니다.

그림 1. 내부 lib에 링크되는 공급업체 코드

Android 7.x 이하의 RenderScript에서 비롯되는 차이점은 다음과 같습니다.

  • 프로세스에 RenderScript 내부 lib의 인스턴스 2개가 포함됩니다. 하나는 CPU 대체 경로에 사용되고 /system/lib에서 바로 발생하며 나머지 하나는 GPU 경로 에 사용되고 /system/lib/vndk-sp에서 발생합니다.
  • /system/lib의 RS 내부 lib는 플랫폼의 일부로 빌드되며 system.img가 업그레이드될 때 업데이트됩니다. 하지만 /system/lib/vndk-sp의 lib는 공급업체에 대해 빌드되며 system.img가 업그레이드될 때 업데이트되지 않습니다(보안 수정에 대한 업데이트는 가능하지만 ABI가 동일하게 유지됨).
  • 공급업체 코드(RS HAL, RS 드라이버 및 bcc plugin)는 /system/lib/vndk-sp에 위치한 RenderScript 내부 lib에 대해 링크됩니다. /system/lib의 lib에 대해서는 링크할 수 없습니다. 이는 해당 디렉터리의 lib가 플랫폼에 대해 빌드되었고, 따라서 공급업체 코드와 호환되지 않을 수도 있기 때문입니다(기호가 삭제될 수 있음). 이렇게 할 경우 프레임워크 전용 OTA가 불가능합니다.

설계

다음 섹션에서는 Android 8.0 이상의 RenderScript 설계에 대해 자세히 설명합니다.

공급업체에 제공되는 RenderScript lib

이 섹션에는 공급업체 코드에 제공되고 링크 가능한 RenderScript lib(동일 프로세스 HAL 또는 VNDK-SP의 경우 공급업체 NDK로 알려짐)가 나열되어 있습니다. 또한 RenderScript와는 관련이 없지만 마찬가지로 공급업체 코드에 제공되는 추가 라이브러리에 대한 세부정보도 포함하고 있습니다.

다음 라이브러리 목록은 Android 출시 간에 다를 수 있지만 특정 Android출시의 경우에는 변경되지 않습니다. 가용한 라이브러리의 최신 목록은 /system/etc/ld.config.txt를 참조하세요.

RenderScript Lib 비 RenderScript Lib
  • android.hardware.graphics.renderscript@1.0.so
  • libRS_internal.so
  • libRSCpuRef.so
  • libblas.so
  • libbcinfo.so
  • libcompiler_rt.so
  • libRSDriver.so
  • libc.so
  • libm.so
  • libdl.so
  • libstdc++.so
  • liblog.so
  • libnativewindow.so
  • libsync.so
  • libvndksupport.so
  • libbase.so
  • libc++.so
  • libcutils.so
  • libutils.so
  • libhardware.so
  • libhidlbase.so
  • libhidltransport.so
  • libhwbinder.so
  • liblzma.so
  • libz.so
  • libEGL.so
  • libGLESv1_CM.so
  • libGLESv2.so

링커 네임스페이스 구성

VNDK-SP에 없는 lib를 공급업체 코드에 사용할 수 없도록 막는 링크 제한이 런타임 시에 링커 네임스페이스를 사용하여 적용됩니다. 자세한 내용은 VNDK 설계 프레젠테이션을 참조하세요.

Android 8.0 이상을 실행하는 기기에서는 RenderScript를 제외한 모든 동일 프로세스 HAL(SP-HAL)이 링커 네임스페이스 sphal 안으로 로드됩니다. RenderScript는 RenderScript 전용 네임스페이스인 rs로 로드됩니다. 이 위치는 RenderScript lib의 느슨한 적용을 가능하게 해줍니다. RS 구현은 컴파일된 비트코드를 로드해야 합니다. 따라서 /data/*/*.sors 네임스페이스의 경로에 추가됩니다(다른 SP-HAL은 데이터 파티션에서 lib를 로드할 수 없음).

또한 rs 네임스페이스는 다른 네임스페이스에 의해 제공된 것보다 더 많은 lib를 허용합니다. libmediandk.solibft2.sors 네임스페이스에 노출되며, 이는 libRS_internal.so에 이러한 라이브러리에 대한 내부 종속 항목이 있기 때문입니다.

그림 2. 링커의 네임스페이스 구성

드라이버 로드

CPU 대체 경로

RS 컨텍스트 생성 시의 RS_CONTEXT_LOW_LATENCY 비트 존재 여부에 따라 CPU 또는 GPU 경로가 선택됩니다. CPU 경로가 선택되면 RS lib의 플랫폼 버전이 제공되는 기본 링커 네임스페이스에서 libRS_internal.so(RS 프레임워크의 기본 구현)가 직접적으로 dlopen됩니다.

공급업체의 RS HAL 구현은 CPU 대체 경로를 취할 때 아예 사용되지 않으며 RsContext 객체는 null mVendorDriverName으로 생성됩니다. libRSDriver.so는 기본적으로 dlopen되며 드라이버 lib는 default 네임스페이스에서 로드됩니다. 이는 호출자(libRS_internal.so)도 default 네임스페이스에서 로드되기 때문입니다.

그림 4. CPU 대체 경로

GPU 경로

GPU 경로의 경우 libRS_internal.so가 다르게 로드됩니다. 우선 libRS.soandroid.hardware.renderscript@1.0.so(및 기본 libhidltransport.so)를 사용하여 android.hardware.renderscript@1.0-impl.so(RS HAL의 공급업체 구현)를 sphal이라는 다른 링커 네임스페이스에 로드합니다. RS HAL은 rs라는 또 다른 링커 네임스페이스에 libRS_internal.sodlopen합니다.

공급업체는 빌드 시간 플래그 OVERRIDE_RS_DRIVER를 설정하여 자체 RS 드라이버를 제공할 수 있습니다. 이 플래그는 RS HAL 구현(hardware/interfaces/renderscript/1.0/default/Context.cpp)에 삽입할 수 있습니다. 그러면 이 드라이버 이름이 GPU 경로의 RS 컨텍스트에 대해 dlopen됩니다.

RsContext 객체 생성은 RS HAL 구현에 위임됩니다. HAL은 rsContextCreateVendor() 함수, 그리고 인수로 사용할 드라이버 이름을 사용하여 RS 프레임워크를 콜백합니다. 그러면 RS 프레임워크는 RsContext가 초기화되었을 때 지정된 드라이버를 로드합니다. 이 경우 드라이버 라이브러리가 rs 네임스페이스에 로드됩니다. 이는 RsContext 객체가 rs 네임스페이스 안에 생성되었고 /vendor/lib가 네임스페이스의 검색 경로에 위치하기 때문입니다.

그림 5. GPU 대체 경로

default 네임스페이스에서 sphal 네임스페이스로 전환하는 경우 libhidltransport.soandroid_load_sphal_library() 함수를 사용하여 동적 링커가 sphal 네임스페이스에서 -impl.so 라이브러리를 로드하도록 명시적으로 명령합니다.

sphal 네임스페이스에서 rs 네임스페이스로 전환할 때는 로드가 /system/etc/ld.config.txt에서 다음 행에 의해 간접적으로 수행됩니다.

namespace.sphal.link.rs.shared_libs = libRS_internal.so

이 행은 lib를 sphal 네임스페이스에서 찾거나 로드할 수 없을 때 동적 링커가 libRS_internal.sors 네임스페이스에서 로드하도록 지정합니다(sphal 네임스페이스는 libRS_internal.so가 있는 /system/lib/vndk-sp를 검색하지 않으므로 항상 동일함). 이 구성에서는 dlopen()을 단순히 libRS_internal.so에 호출하기만 해도 네임스페이스 전환 작업을 충분히 완료할 수 있습니다.

bcc 플러그인 로드

bcc pluginbcc 컴파일러에 로드되는 공급업체 제공 라이브러리입니다. bcc/system/bin 디렉터리의 시스템 프로세스이므로 bcc plugin 라이브러리를 SP-HAL로 간주할 수 있습니다(즉, 바인더화하지 않고도 시스템 프로세스에 바로 로드할 수 있는 공급업체 HAL). SP-HAL로서의 bcc-plugin 라이브러리가 지니는 특성은 다음과 같습니다.

  • libLLVM.so와 같은 프레임워크 전용 라이브러리에 대해 링크할 수 없습니다.
  • 공급업체에 제공되는 VNDK-SP 라이브러리에 대해서만 링크할 수 있습니다.

이러한 제한사항은 android_sphal_load_library() 함수를 사용하여 bcc pluginsphal 네임스페이스에 로드하는 방식으로 적용됩니다. Android의 이전 버전에서는 플러그인 이름이 -load 옵션을 사용하여 지정되었으며, lib는 libLLVM.so에 의한 단순한 dlopen()을 사용하여 로드되었습니다. Android 8.0 이상에서는 -plugin 옵션에서 지정되며, lib가 bcc 자체에 의해 직접적으로 로드됩니다. 이 옵션을 사용하면 오픈소스 LLVM 프로젝트에 대한 비 Android 경로가 지원됩니다.

그림 6. bcc 플러그인 로드, Android 7.x 이하


그림 7. bcc 플러그인 로드, Android 8.0 이상

ld.mc 관련 경로 검색

ld.mc를 실행하면 일부 RS 런타임 lib에 링커에 대한 입력이 주어집니다. 앱의 RS 비트코드는 런타임 lib에 대해 링크되며, 변환된 비트코드가 앱 프로세스에 로드되면 런타임 lib가 변환된 비트코드에서 다시 동적으로 링크됩니다.

런타임 lib에 포함되는 항목은 다음과 같습니다.

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • RS 드라이버(libRSDriver.so 또는 OVERRIDE_RS_DRIVER)

컴파일된 비트코드를 앱 프로세스에 로드할 때에는 ld.mc에 사용된 동일한 라이브러리를 제공해야 합니다. 그렇지 않으면 컴파일된 비트코드는 링크되었을 때 사용할 수 있었던 기호를 찾을 수 없을지도 모릅니다.

이를 위해 RS 프레임워크는 ld.mc를 실행할 때 RS 프레임워크 자체가 /system/lib 또는 /system/lib/vndk-sp에서 로드되는지 여부에 따라 런타임 lib의 다른 검색 경로를 사용합니다. 이는 RS 프레임워크 lib의 임의 기호 주소를 읽고 dladdr()을 사용하여 주소에 매핑된 파일 경로를 가져오는 방식으로 파악할 수 있습니다.

SELinux 정책

Android 8.0 이상의 SELinux 정책 변화의 결과로, 이제 vendor 파티션의 추가 파일에 라벨을 지정할 때 neverallows를 통해 적용되는 특정 규칙을 따라야 합니다.

  • vendor_filevendor 파티션에서 모든 파일의 기본 라벨이어야 합니다. 플랫폼 정책에서는 이 라벨이 패스스루 HAL 구현에 액세스하도록 요구합니다.
  • 공급업체 SEPolicy를 통해 vendor 파티션에 추가된 새로운 모든 exec_types에는 vendor_file_type 속성이 있어야 합니다. 이 속성은 neverallows를 통해 실행됩니다.
  • 향후 플랫폼/프레임워크 업데이트와의 충돌을 방지하려면 vendor 파티션에서 exec_types 외의 다른 라벨을 파일에 지정하지 않아야 합니다.
  • AOSP에서 식별한 동일한 프로세스 HAL의 모든 라이브러리 종속 항목에는 same_process_hal_file로 라벨을 지정해야 합니다.

SELinux 정책에 관한 자세한 내용은 Android의 보안이 강화된 Linux를 참고하세요.

비트코드의 ABI 호환성

추가된 새 API가 없어서 HAL 버전 범프가 없는 경우 RS 프레임워크는 계속해서 기존 GPU(HAL 1.0) 드라이버를 사용합니다.

비트코드에 영향을 미치지 않는 소규모 HAL 변경사항(HAL 1.1)의 경우, 프레임워크는 새로 추가된 API에 대해 CPU로 대체되어야 하며, 다른 위치에서 계속해서 GPU(HAL 1.0) 드라이버를 사용해야 합니다.

비트코드 컴파일/링크에 영향을 미치는 대규모 HAL 변경사항(HAL 2.0)의 경우 RS 프레임워크는 공급업체에서 제공한 GPU 드라이버를 로드하는 대신 가속화를 위한 CPU 또는 Vulkan 경로를 사용해야 합니다.

RenderScript 비트코드 소비는 아래와 같이 3단계에 걸쳐 발생합니다.

단계 세부정보
컴파일
  • bcc의 입력 비트코드(.bc)가 LLVM 3.2 비트코드 형식이어야 하며 bcc가 기존(레거시) 앱과 하위 호환되어야 합니다.
  • 하지만 .bc의 메타데이터는 변경될 수 있습니다(할당 setter, getter, 수학 함수 등의 새로운 런타임 함수가 있을 수 있음). 런타임 함수의 일부는 libclcore.bc에 상주하며, 일부는 LibRSDriver 또는 공급업체 상응 대상에 상주합니다.
  • 새 런타임 함수 또는 메타데이터 변경사항 분류에는 비트코드 API 수준 증분이 필요합니다. 공급업체 드라이버는 이를 소비할 수 없으므로 HAL 버전도 증분되어야 합니다.
  • 공급업체는 자체 컴파일러를 보유할 수도 있지만 bcc와 관련된 결과/요구사항 역시 이러한 컴파일러에 적용됩니다.
링크
  • 컴파일된 .o는 공급업체 드라이버(libRSDriver_foo.solibcompiler_rt.so 등)와 링크됩니다. CPU 경로는 libRSDriver.so와 링크됩니다.
  • .o에 libRSDriver_foo의 새 런타임 API가 필요한 경우 이를 지원할 수 있도록 공급업체 드라이버를 업데이트해야 합니다.
  • 특정 공급업체는 자체 링커를 보유할 수도 있지만 ld.mc의 인수 또한 링커에 적용됩니다.
로드
  • libRSCpuRef는 공유된 객체를 로드합니다. 이 인터페이스가 변경된 경우 HAL 버전 범프가 필요합니다.
  • 공급업체는 libRSCpuRef에 의존하여 공유된 객체를 로드하거나 고유한 객체를 구현합니다.

HAL 외에 런타임 API 및 노출된 기호도 함께 상호작용합니다. 두 인터페이스 모두 Android 7.0(API 24) 이후로 변경된 부분이 없으며, Android 8.0 이상에서도 당분간은 변경 계획이 없습니다. 하지만 인터페이스가 변경되는 경우 HAL 버전도 증분됩니다.

공급업체 구현

Android 8.0 이상에서 GPU 드라이버가 올바르게 작동하기 위해서는 GPU 드라이버에 대한 어느 정도의 변경이 필요합니다.

드라이버 모듈

  • 드라이버 모듈은 목록에 없는 어떠한 시스템 라이브러리에도 종속되면 안 됩니다.
  • 드라이버는 자체 android.hardware.renderscript@1.0-impl_{NAME}을 제공하거나 기본 구현 android.hardware.renderscript@1.0-impl을 종속 항목으로 선언해야 합니다.
  • CPU 구현 libRSDriver.so는 비 VNDK-SP 종속 항목을 삭제하는 방법의 좋은 예입니다.

비트코드 컴파일러

공급업체 드라이버의 RenderScript 비트코드는 두 가지 방식으로 컴파일할 수 있습니다.

  1. /vendor/bin/에서 공급업체별 RenderScript 컴파일러를 호출합니다(GPU 컴파일에 적합). 다른 드라이버 모듈과 마찬가지로, 공급업체 컴파일러 바이너리는 공급업체에 제공되는 RenderScript lib 목록에 없는 어떠한 시스템 라이브러리에도 종속될 수 없습니다.
  2. 시스템 bcc, 즉 공급업체에서 제공한 bcc plugin이 포함된 /system/bin/bcc를 호출합니다. 이 플러그인은 공급업체에 제공되는 RenderScript lib 목록에 없는 어떠한 시스템 라이브러리에도 종속될 수 없습니다.

공급업체 bcc plugin이 CPU 컴파일에 개입해야 하고 libLLVM.so에 대한 종속 항목을 쉽게 삭제할 수 없는 경우 공급업체는 bcc(libLLVM.solibbcc.so를 비롯한 모든 비 LL-NDK 종속 항목)를 /vendor 파티션에 복사해야 합니다.

또한 공급업체는 다음과 같이 변경 작업을 수행해야 합니다.

그림 8. 공급업체 드라이버 변경
  1. libclcore.bc/vendor 파티션으로 복사합니다. 이렇게 하면 libclcore.bc, libLLVM.so, libbcc.so가 동기화되도록 보장합니다.
  2. RS HAL 구현에서 RsdCpuScriptImpl::BCC_EXE_PATH를 설정하여 bcc 실행 파일 경로를 변경합니다.

SELinux 정책

SELinux 정책은 드라이버와 컴파일러 실행 파일 둘 다에 영향을 미칩니다. 모든 드라이버 모듈은 기기의 file_contexts에서 same_process_hal_file로 라벨이 지정되어야 합니다. 예:

/vendor/lib(64)?/libRSDriver_EXAMPLE\.so     u:object_r:same_process_hal_file:s0

컴파일러 실행 파일은 bcc의 공급업체 사본(/vendor/bin/bcc)처럼 앱 프로세스에 의해 호출될 수 있어야 합니다. 예를 들면 다음과 같습니다.

device/vendor_foo/device_bar/sepolicy/file_contexts:
/vendor/bin/bcc                    u:object_r:same_process_hal_file:s0

레거시 기기

레거시 기기는 다음과 같은 조건을 충족합니다.

  1. PRODUCT_SHIPPING_API_LEVEL은 26보다 낮습니다.
  2. PRODUCT_FULL_TREBLE_OVERRIDE는 정의되어 있지 않습니다.

레거시 기기의 경우 Android 8.0 이상으로 업그레이드할 때 제한사항이 적용되지 않습니다. 즉, 드라이버가 계속해서 /system/lib[64]의 라이브러리에 링크됩니다. 하지만 아키텍처 변경사항이 OVERRIDE_RS_DRIVER와 관련이 있으므로 android.hardware.renderscript@1.0-impl/vendor 파티션에 설치해야 합니다. 실패하면 RenderScript 런타임이 CPU 경로로 강제 대체됩니다.

Renderscript 지원 중단 동기에 관한 자세한 내용은 Android 개발자 블로그: Android GPU 컴퓨팅 전망을 참고하세요. 이번 지원 중단에 관한 리소스 정보는 다음과 같습니다.