출시됨:
Android 11 (API 수준 30) - Thermal API
Android 12 (API 수준 31) - NDK API
(미리보기) Android 15 (DP1) - getThermalHeadroomThresholds()
앱은 기기의 열 상태에 따라 잠재적으로 성능이 제한되며, 기기의 열 상태는 날씨, 최근 사용량, 기기의 열 관련 설계와 같은 특성에 따라 달라질 수 있습니다. 기기는 온도로 인해 성능이 제한되기 전에 제한된 시간 동안만 높은 수준의 성능을 유지할 수 있습니다. 구현의 주요 목표는 열 제한을 초과하지 않고 성능 목표를 달성하는 것입니다. Thermal API를 사용하면 기기별 최적화가 필요하지 않습니다. 또한 성능 문제를 디버그할 때는 기기의 열 상태가 성능을 제한하는지 여부를 파악하는 것이 중요합니다.
게임 엔진에는 일반적으로 엔진이 기기에 부여하는 워크로드를 조정할 수 있는 런타임 성능 매개변수가 있습니다. 예를 들어, 이러한 매개변수는 작업자 스레드 수, 큰 코어와 작은 코어의 작업자 스레드 어피니티, GPU 충실도 옵션, 프레임 버퍼 해상도를 설정할 수 있습니다. Unity Engine에서 게임 개발자는 적응형 성능 플러그인을 사용하여 품질 설정을 변경하여 워크로드를 조정할 수 있습니다. Unreal Engine의 경우 확장성 설정을 사용하여 품질 수준을 동적으로 조정합니다.
기기가 안전하지 않은 열 상태에 도달하면 게임은 이러한 매개변수를 통해 워크로드를 줄여 성능이 제한되는 상황을 피할 수 있습니다. 제한 상황을 피하려면 기기의 열 상태를 모니터링하고 게임 엔진 워크로드를 미리 조정해야 합니다. 기기가 일단 과열되면 열을 방출하기 위해 워크로드가 지속 가능한 성능 수준 아래로 떨어져야 합니다. 열 헤드룸이 더 안전한 수준으로 감소하면 게임에서 품질 설정을 다시 높일 수 있지만 최적의 플레이 시간을 위해 지속 가능한 품질 수준을 찾아야 합니다.
getThermalHeadroom
메서드를 폴링하여 기기의 열 상태를 모니터링할 수 있습니다. 이 메서드는 기기가 과열되지 않고 얼마나 오랫동안 현재 성능 수준을 유지할 수 있는지를 예측합니다. 시간이 워크로드를 실행하는 데 필요한 시간보다 적으면 게임에서 지속 가능한 수준으로 워크로드를 줄여야 합니다. 예를 들어 게임은 더 작은 코어로 전환하거나 프레임 속도를 낮추거나 충실도를 낮출 수 있습니다.
![ADPF Thermal API 사전 통합](https://developer.android.com/static/games/optimize/adpf/images/adpf_thermal_pre-integration.png?authuser=1&hl=ko)
![ADPF Thermal API 통합 후](https://developer.android.com/static/games/optimize/adpf/images/adpf_thermal_post-integration.png?authuser=1&hl=ko)
Thermal Manager 획득
Thermal API를 사용하려면 먼저 Thermal Manager를 가져와야 합니다.
C++
AThermalManager* thermal_manager = AThermal_acquireManager();
자바
PowerManager powerManager = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
더 효과적인 제어를 위해 x초 전에 Thermal Headroom 예측
시스템에 현재 워크로드를 사용하여 x초 후의 온도를 예측하도록 요청할 수 있습니다. 이렇게 하면 열 제한이 발생하지 않도록 워크로드를 줄여 더 세부적으로 제어하고 더 많은 시간을 확보하여 대응할 수 있습니다.
결과는 0.0f (제한 없음, THERMAL_STATUS_NONE
)~1.0f(심한 제한, THERMAL_STATUS_SEVERE
)입니다. 게임에 그래픽 품질 수준이 다른 경우 열 헤드룸 가이드라인을 따르세요.
C++
float thermal_headroom = AThermal_getThermalHeadroom(10);
ALOGI("ThermalHeadroom in 10 sec: %f", thermal_headroom);
자바
float thermalHeadroom = powerManager.getThermalHeadroom(10);
Log.d("ADPF", "ThermalHeadroom in 10 sec: " + thermalHeadroom);
또는 열 상태를 사용하여 명확히 할 수 있습니다.
기기 모델마다 설계가 다를 수 있습니다. 일부 기기는 열을 더 잘 분산할 수 있으므로 제한되기 전에 더 높은 열 헤드룸을 견딜 수 있습니다. 열 헤드룸 범위의 단순화된 그룹을 읽으려면 열 상태를 확인하여 현재 기기의 열 헤드룸 값을 파악하면 됩니다.
C++
AThermalStatus thermal_status = AThermal_getCurrentThermalStatus(thermal_manager);
ALOGI("ThermalStatus is: %d", thermal_status);
자바
int thermalStatus = powerManager.getCurrentThermalStatus();
Log.d("ADPF", "ThermalStatus is: " + thermalStatus);
열 상태 변경 시 알림 받기
thermalStatus
가 특정 수준 (예: THERMAL_STATUS_LIGHT
)에 도달할 때까지 thermalHeadroom
를 폴링하지 않을 수도 있습니다. 이렇게 하려면 콜백을 등록하여 상태가 변경될 때마다 시스템에서 알림을 보내도록 할 수 있습니다.
C++
int result = AThermal_registerThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
// failed, check whether you have previously registered callback that
// hasn’t been unregistered
}
자바
// PowerManager.OnThermalStatusChangedListener is an interface, thus you can
// also define a class that implements the methods
PowerManager.OnThermalStatusChangedListener listener = new
PowerManager.OnThermalStatusChangedListener() {
@Override
public void onThermalStatusChanged(int status) {
Log.d("ADPF", "ThermalStatus changed: " + status);
// check the status and flip the flag to start/stop pooling when
// applicable
}
};
powerManager.addThermalStatusListener(listener);
완료되면 리스너를 삭제해야 합니다.
C++
int result = AThermal_unregisterThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
// failed, check whether the callback has been registered previously
}
자바
powerManager.removeThermalStatusListener(listener);
정리
완료되면 획득한 thermal_manager를 정리해야 합니다. Java를 사용하는 경우 PowerManager 참조가 자동으로 가비지 컬렉션될 수 있습니다. 하지만 JNI를 통해 Java API를 사용하고 참조를 유지한 경우 참조를 정리해야 합니다.
C++
AThermal_releaseManager(thermal_manager);
C++ API (NDK API)와 Java API (JNI를 통해)를 모두 사용하여 네이티브 C++ 게임에서 Thermal API를 구현하는 방법에 관한 전체 가이드는 적응성 Codelab 섹션의 Thermal API 통합 섹션을 참고하세요.
열 헤드룸 가이드라인
getThermalHeadroom
메서드를 폴링하여 기기의 열 상태를 모니터링할 수 있습니다. 이 메서드는 기기가 THERMAL_STATUS_SEVERE
에 도달하기 전에 현재 성능 수준을 얼마나 오래 유지할 수 있는지 예측합니다.
예를 들어 getThermalHeadroom(30)
이 0.8을 반환하면 30초 후에 헤드룸이 0.8에 도달할 것으로 예상됨을 나타냅니다. 여기서 0.8은 심각한 제한(1.0)에서 0.2만큼 떨어져 있습니다. 시간이 워크로드를 실행하는 데 필요한 시간보다 적으면 게임에서 지속 가능한 수준으로 워크로드를 줄여야 합니다. 예를 들어 게임은 프레임 속도를 낮추거나, 충실도를 낮추거나, 네트워크 연결 작업을 줄일 수 있습니다.
열 상태 및 의미
- 기기가 열 제한 상태에 있지 않은 경우:
- 제한이 있지만 성능에 큰 영향은 없는 경우:
- 성능에 영향을 미치는 심각한 제한이 있는 경우:
Thermal API의 기기 제한사항
이전 기기에서 Thermal API를 구현했기 때문에 Thermal API에 몇 가지 알려진 제한사항 또는 추가 요구사항이 있습니다. 제한사항과 이를 해결하는 방법은 다음과 같습니다.
GetThermalHeadroom()
API를 너무 자주 호출하지 마세요. 이렇게 하면 API가NaN
를 반환합니다. 10초마다 한 번 이상 호출하면 안 됩니다.- 여러 스레드에서 호출하지 마세요. 호출 빈도를 보장하기가 더 어렵고 API가
NaN
를 반환할 수 있습니다. GetThermalHeadroom()
의 초기 값이 NaN이면 기기에서 API를 사용할 수 없습니다.GetThermalHeadroom()
가 높은 값 (예: 0.85 이상)을 반환하고GetCurrentThermalStatus()
가 여전히THERMAL_STATUS_NONE
를 반환하면 상태가 업데이트되지 않은 것일 수 있습니다. 휴리스틱을 사용하여 올바른 열 제한 상태를 추정하거나getCurrentThermalStatus()
없이getThermalHeadroom()
를 사용합니다.
휴리스틱 예시:
- Thermal API가 지원되는지 확인합니다.
isAPISupported()
는getThermalHeadroom
의 첫 번째 호출 값이 0 또는 NaN이 아닌지 확인하고 첫 번째 값이 0 또는 NaN인 경우 API 사용을 건너뜁니다. getCurrentThermalStatus()
가THERMAL_STATUS_NONE
이외의 값을 반환하면 기기가 열적으로 제한되고 있는 것입니다.getCurrentThermalStatus()
가 계속THERMAL_STATUS_NONE
를 반환한다고 해서 기기가 열 제한 상태가 아닐 수도 있습니다. 기기에서getCurrentThermalStatus()
가 지원되지 않을 수 있습니다.getThermalHeadroom()
의 반환 값을 확인하여 기기 상태를 확인합니다.getThermalHeadroom()
가 1.0보다 큰 값을 반환하면 상태가 실제로THERMAL_STATUS_SEVERE
이상일 수 있으므로 즉시 워크로드를 줄이고getThermalHeadroom()
가 더 낮은 값을 반환할 때까지 더 낮은 워크로드를 유지합니다.getThermalHeadroom()
가 0.95의 값을 반환하면 실제로 상태가THERMAL_STATUS_MODERATE
이상일 수 있으므로 즉시 워크로드를 줄이고 더 높은 판독 값을 방지하기 위해 주의 깊게 지켜보세요.getThermalHeadroom()
가 0.85의 값을 반환하면 실제로 상태가THERMAL_STATUS_LIGHT
일 수 있으므로 주의하고 가능하면 워크로드를 줄입니다.
의사코드:
bool isAPISupported() {
float first_value_of_thermal_headroom = getThermalHeadroom();
if ( first_value_of_thermal_headroom == 0 ||
first_value_of_thermal_headroom == NaN ) {
// Checked the thermal Headroom API's initial return value
// it is NaN or 0,so, return false (not supported)
return false;
}
return true;
}
if (!isAPISupported()) {
// Checked the thermal Headroom API's initial return value, it is NaN or 0
// Don’t use the API
} else {
// Use thermalStatus API to check if it returns valid values.
if (getCurrentThermalStatus() > THERMAL_STATUS_NONE) {
// The device IS being thermally throttled
} else {
// The device is not being thermally throttled currently. However, it
// could also be an indicator that the ThermalStatus API may not be
// supported in the device.
// Currently this API uses predefined threshold values for thermal status
// mapping. In the future you may be able to query this directly.
float thermal_headroom = getThermalHeadroom();
if ( thermal_headroom > 1.0) {
// The device COULD be severely throttled.
} else if ( thermal_headroom > 0.95) {
// The device COULD be moderately throttled.
} else if ( thermal_headroom > 0.85) {
// The device COULD be experiencing light throttling.
}
}
}
다이어그램: