├── AndroidPerformance ├── AndroidPerformance.uplugin ├── Config │ └── FilterPlugin.ini ├── Resources │ └── Icon128.png └── Source │ └── AndroidPerformance │ ├── AndroidPerformance.Build.cs │ ├── Private │ ├── ADPFManager.cpp │ ├── ADPFManager.h │ ├── AndroidPerformanceLog.h │ └── AndroidPerformanceModule.cpp │ └── Public │ └── AndroidPerformanceModule.h ├── LICENSE ├── README.md └── contributing.md /AndroidPerformance/AndroidPerformance.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Android Performance", 6 | "Description": "Plugin adjusts game graphic quality based on device temperature.", 7 | "Category": "Other", 8 | "CreatedBy": "Google", 9 | "CreatedByURL": "https://developer.android.com/games/optimize/adpf", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": true, 17 | "Modules": [ 18 | { 19 | "Name": "AndroidPerformance", 20 | "Type": "Runtime", 21 | "LoadingPhase": "Default", 22 | "WhitelistPlatforms": [ 23 | "Android" 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /AndroidPerformance/Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | -------------------------------------------------------------------------------- /AndroidPerformance/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android/adpf-unreal-plugin/d9bf0554b7989b4e159a9194b1b81ee37f94a96b/AndroidPerformance/Resources/Icon128.png -------------------------------------------------------------------------------- /AndroidPerformance/Source/AndroidPerformance/AndroidPerformance.Build.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using UnrealBuildTool; 18 | using System.IO; 19 | 20 | public class AndroidPerformance : ModuleRules 21 | { 22 | public AndroidPerformance(ReadOnlyTargetRules Target) : base(Target) 23 | { 24 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 25 | 26 | PrivateDependencyModuleNames.AddRange( 27 | new string[] 28 | { 29 | "Engine", 30 | "RenderCore", 31 | 32 | // ... add private dependencies that you statically link with here ... 33 | } 34 | ); 35 | 36 | PrecompileForTargets = PrecompileTargetsType.Any; 37 | 38 | string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath); 39 | 40 | PrivateDependencyModuleNames.AddRange(new string[] { "Launch" }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /AndroidPerformance/Source/AndroidPerformance/Private/ADPFManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ADPFManager.h" 18 | #include "AndroidPerformanceLog.h" 19 | #include "RenderCore.h" 20 | 21 | #if PLATFORM_ANDROID 22 | #include "Android/AndroidApplication.h" 23 | #endif 24 | 25 | #include 26 | #include 27 | 28 | static TAutoConsoleVariable CVarAndroidPerformanceEnabled( 29 | TEXT("r.AndroidPerformanceEnabled"), 30 | 1, 31 | TEXT("Enable/disable the Android Performance plugin in the Monitor() method.\n") 32 | TEXT("The plugin uses the Android adaptability API to adjust the game settings based on the thermal status of the device and will adjust the CPU as needed.\n") 33 | TEXT(" 0: off (disabled)\n") 34 | TEXT(" 1: on (enabled)"), 35 | ECVF_RenderThreadSafe); 36 | 37 | static TAutoConsoleVariable CVarAndroidPerformanceHintEnabled( 38 | TEXT("r.AndroidPerformanceHintEnabled"), 39 | 1, 40 | TEXT("Enable/disable the performance hint manager in the Monitor() method.\n") 41 | TEXT("Enable this setting for optimal thread boosting on supported Android devices.\n") 42 | TEXT(" 0: off (disabled)\n") 43 | TEXT(" 1: on (enabled)"), 44 | ECVF_RenderThreadSafe); 45 | 46 | static TAutoConsoleVariable CVarAndroidPerformanceChangeQualites( 47 | TEXT("r.AndroidPerformanceChangeQualities"), 48 | 1, 49 | TEXT("Choose how the thermal status adjusts the game's fidelity level.\n") 50 | TEXT(" 0: The system does not adjust any settings\n") 51 | TEXT(" 1: Settings are adjusted according to the thermal headroom\n"), 52 | ECVF_RenderThreadSafe); 53 | 54 | float Clock() { 55 | static struct timespec _base; 56 | static bool first_call = true; 57 | 58 | if (first_call) { 59 | clock_gettime(CLOCK_MONOTONIC, &_base); 60 | first_call = false; 61 | } 62 | 63 | struct timespec t; 64 | clock_gettime(CLOCK_MONOTONIC, &t); 65 | float sec_diff = (float)(t.tv_sec - _base.tv_sec); 66 | float msec_diff = (float)((t.tv_nsec - _base.tv_nsec) / 1000000); 67 | return sec_diff + 0.001f * msec_diff; 68 | } 69 | 70 | ADPFManager::ADPFManager() 71 | : thermal_manager_(nullptr), 72 | initialized_performance_hint_manager(false), 73 | support_performance_hint_manager(true), 74 | thermal_headroom_(0.f), 75 | obj_perfhint_game_session_(nullptr), 76 | obj_perfhint_render_session_(nullptr), 77 | obj_perfhint_rhi_session_(nullptr), 78 | current_quality_level(max_quality_count - 1), 79 | target_quality_level(max_quality_count - 1), 80 | prev_max_fps(-1.0f), 81 | prev_max_fps_nano(0), 82 | fps_total(0.0f), 83 | fps_count(0){ 84 | last_clock_ = Clock(); 85 | 86 | // Load current quality level, and set this quality level is maximum. 87 | Scalability::FQualityLevels current_level = Scalability::GetQualityLevels(); 88 | for(int32_t i = 0; i < max_quality_count; ++i) { 89 | quality_levels[i].SetFromSingleQualityLevel(i); 90 | 91 | if(quality_levels[i].ResolutionQuality > current_level.ResolutionQuality) { 92 | quality_levels[i].ResolutionQuality = current_level.ResolutionQuality; 93 | } 94 | if(quality_levels[i].ViewDistanceQuality > current_level.ViewDistanceQuality) { 95 | quality_levels[i].ViewDistanceQuality = current_level.ViewDistanceQuality; 96 | } 97 | if(quality_levels[i].AntiAliasingQuality > current_level.AntiAliasingQuality) { 98 | quality_levels[i].AntiAliasingQuality = current_level.AntiAliasingQuality; 99 | } 100 | if(quality_levels[i].ShadowQuality > current_level.ShadowQuality) { 101 | quality_levels[i].ShadowQuality = current_level.ShadowQuality; 102 | } 103 | if(quality_levels[i].PostProcessQuality > current_level.PostProcessQuality) { 104 | quality_levels[i].PostProcessQuality = current_level.PostProcessQuality; 105 | } 106 | if(quality_levels[i].TextureQuality > current_level.TextureQuality) { 107 | quality_levels[i].TextureQuality = current_level.TextureQuality; 108 | } 109 | if(quality_levels[i].EffectsQuality > current_level.EffectsQuality) { 110 | quality_levels[i].EffectsQuality = current_level.EffectsQuality; 111 | } 112 | if(quality_levels[i].FoliageQuality > current_level.FoliageQuality) { 113 | quality_levels[i].FoliageQuality = current_level.FoliageQuality; 114 | } 115 | if(quality_levels[i].ShadingQuality > current_level.ShadingQuality) { 116 | quality_levels[i].ShadingQuality = current_level.ShadingQuality; 117 | } 118 | } 119 | } 120 | 121 | ADPFManager::~ADPFManager() { 122 | destroy(); 123 | } 124 | 125 | bool ADPFManager::initialize() { 126 | #if __ANDROID_API__ < 31 127 | #error "Android API level is less than 31, the thermal API is not supported. Recommend to increase 'NDK API Level' to 'android-33' or 'use-jni' branch in github." 128 | #endif 129 | #if __ANDROID_API__ < 33 130 | #error "Android API level is less than 33, the performance hint APIs are not supported. Recommend to increase 'NDK API Level' to 'android-33' or 'use-jni' branch in github." 131 | #endif 132 | 133 | #if defined(PLATFORM_ANDROID) && __ANDROID_API__ >= 31 134 | // Initialize PowerManager reference. 135 | if(android_get_device_api_level() < 31) { 136 | // The device might not support thermal APIs, it will not initialized. 137 | UE_LOG(LogAndroidPerformance, Log, TEXT("Device API level is less than 31, the ADPF plugin will not work in this device.")); 138 | return false; 139 | } 140 | else if(InitializePowerManager() == false) { 141 | // The device might not support thermal APIs, it will not initialized. 142 | UE_LOG(LogAndroidPerformance, Log, TEXT("Initialize PowerManager failed, the ADPF plugin will not work in this device.")); 143 | return false; 144 | } 145 | 146 | // Initialize PerformanceHintManager reference. 147 | auto manager = GetThermalManager(); 148 | if (manager != nullptr) { 149 | UE_LOG(LogAndroidPerformance, Log, TEXT("ADPFManager is initialized, the ADPF plugin will work in this device.")); 150 | return true; 151 | } 152 | #endif 153 | 154 | // The device might not support thermal APIs, it will not initialized. 155 | UE_LOG(LogAndroidPerformance, Log, TEXT("ADPFManager is not initialized, the ADPF plugin will not work in this device.")); 156 | return false; 157 | } 158 | 159 | void ADPFManager::destroy() { 160 | #if defined(PLATFORM_ANDROID) && __ANDROID_API__ >= 33 161 | // Destroy the performance hint sessions and release the thermal manager. 162 | UE_LOG(LogAndroidPerformance, Log, TEXT("Destroying ADPFManager.")); 163 | DestroyPerformanceHintManager(); 164 | if (thermal_manager_ != nullptr) { 165 | AThermal_releaseManager(thermal_manager_); 166 | } 167 | #endif 168 | } 169 | 170 | // Invoke the method periodically (once a frame) to monitor 171 | // the device's thermal throttling status. 172 | void ADPFManager::Monitor() { 173 | #if defined(PLATFORM_ANDROID) && __ANDROID_API__ >= 31 174 | if (CVarAndroidPerformanceEnabled.GetValueOnAnyThread() == 0) { 175 | // check performance hint session is created, and delete it. 176 | if(initialized_performance_hint_manager) { 177 | initialized_performance_hint_manager = false; 178 | DestroyPerformanceHintManager(); 179 | UE_LOG(LogAndroidPerformance, Log, TEXT("Performance Hint Manager Destroyed because of CVar disabled.")); 180 | } 181 | 182 | return; 183 | } 184 | 185 | // for debug 186 | extern ENGINE_API float GAverageFPS; 187 | fps_total += GAverageFPS; 188 | fps_count++; 189 | 190 | // Change graphic quality by theraml. 191 | float current_clock = Clock(); 192 | if (current_clock - last_clock_ >= kThermalHeadroomUpdateThreshold) { 193 | // Update thermal headroom. 194 | UpdateThermalStatusHeadRoom(); 195 | last_clock_ = current_clock; 196 | 197 | // for debug 198 | UE_LOG(LogAndroidPerformance, Log, TEXT("Headroom %.3f FPS %.2f temp %.2f"), thermal_headroom_, 199 | fps_total / (float)fps_count, FAndroidMisc::GetDeviceTemperatureLevel()); 200 | fps_total = 0.0f; 201 | fps_count = 0; 202 | 203 | const int32 quality_mode = CVarAndroidPerformanceChangeQualites.GetValueOnAnyThread(); 204 | if (quality_mode != 0) { 205 | if (quality_mode == 1) { 206 | saveQualityLevel(thermal_headroom_); 207 | } 208 | 209 | // TODO Change the quality and FPS settings to match the game's status. 210 | uint32_t new_target = target_quality_level; 211 | if(current_quality_level != new_target) { 212 | if(new_target > max_quality_count) { 213 | new_target = max_quality_count - 1; 214 | } 215 | current_quality_level = new_target; 216 | 217 | // Change Unreal scalability quality. 218 | // https://docs.unrealengine.com/4.27/en-US/TestingAndOptimization/PerformanceAndProfiling/Scalability/ScalabilityReference/ 219 | UE_LOG(LogAndroidPerformance, Log, TEXT("Change quality level to %d"), new_target); 220 | SetQualityLevels(quality_levels[new_target], true); 221 | } 222 | } 223 | } 224 | 225 | // Hint manager logic based on current FPS and actual thread time. 226 | if ((CVarAndroidPerformanceHintEnabled.GetValueOnAnyThread()) != 0 && support_performance_hint_manager) { 227 | // Initialization, attempt only once if not already initialized. 228 | if (initialized_performance_hint_manager == false) { 229 | if(InitializePerformanceHintManager()) { 230 | UE_LOG(LogAndroidPerformance, Log, TEXT("Performance Hint Manager Initialized.")); 231 | initialized_performance_hint_manager = true; 232 | 233 | // Force target duration calculation on the first update after initialization 234 | prev_max_fps = -1.0f; 235 | } 236 | else { 237 | // Initialization failed, disable support permanently for this run. 238 | support_performance_hint_manager = false; 239 | } 240 | } 241 | 242 | // Update Logic, proceed only if successfully initialized. 243 | if(initialized_performance_hint_manager) { 244 | // Check max fps is changed, and caluate nanosec duration 245 | const float current_max_fps = GEngine->GetMaxFPS(); 246 | bool update_target_duration = false; 247 | long long target_duration_nano = prev_max_fps_nano; // Default to previous value 248 | if(prev_max_fps != current_max_fps) { 249 | prev_max_fps = current_max_fps; 250 | update_target_duration = true; 251 | 252 | // Use default if max FPS is 0 (unlimited) or negative, otherwise calculate based on FPS. 253 | target_duration_nano = (prev_max_fps <= 0.0f) ? 16666666 : fpsToNanosec(prev_max_fps); 254 | prev_max_fps_nano = target_duration_nano; // Store the newly calculated value for next frame 255 | UE_LOG(LogAndroidPerformance, Verbose, TEXT("Max FPS changed to %.2f, Target Duration set to %lld ns"), prev_max_fps, prev_max_fps_nano); 256 | } 257 | 258 | // Update the performance hint sessions with the actual thread time and target duration. 259 | if(GGameThreadTime > 0) { 260 | UpdatePerfHintSession(static_cast(GGameThreadTime * 1000), target_duration_nano, update_target_duration, 261 | obj_perfhint_game_session_); 262 | } 263 | else { 264 | prev_max_fps = -1.0f; 265 | } 266 | UpdatePerfHintSession(static_cast(GRenderThreadTime * 1000), target_duration_nano, update_target_duration, 267 | obj_perfhint_render_session_); 268 | UpdatePerfHintSession(static_cast(GRHIThreadTime * 1000), target_duration_nano, update_target_duration, 269 | obj_perfhint_rhi_session_); 270 | } 271 | } 272 | else { 273 | // Cleanup Logic, ff feature was initialized but is now disabled/unsupported 274 | if(initialized_performance_hint_manager) { 275 | initialized_performance_hint_manager = false; 276 | prev_max_fps = -1.0f; 277 | DestroyPerformanceHintManager(); 278 | UE_LOG(LogAndroidPerformance, Log, TEXT("Performance Hint Manager Destroyed because of CVar disabled.")); 279 | } 280 | } 281 | #endif 282 | } 283 | 284 | // Initialize JNI calls for the powermanager. 285 | bool ADPFManager::InitializePowerManager() { 286 | #if defined(PLATFORM_ANDROID) && __ANDROID_API__ >= 31 287 | if (android_get_device_api_level() >= 31) { 288 | // Use NDK API to retrieve thermal headroom. 289 | thermal_manager_ = AThermal_acquireManager(); 290 | } else { 291 | return false; 292 | } 293 | #endif 294 | 295 | if(FMath::IsNaN(UpdateThermalStatusHeadRoom())) { 296 | // If thermal headroom is NaN, this device is not support thermal API. 297 | return false; 298 | } 299 | 300 | return true; 301 | } 302 | 303 | // Retrieve current thermal headroom using JNI call. 304 | float ADPFManager::UpdateThermalStatusHeadRoom() { 305 | #if defined(PLATFORM_ANDROID) && __ANDROID_API__ >= 31 306 | // Get the current thermal headroom. 307 | if(thermal_manager_) { 308 | thermal_headroom_ = AThermal_getThermalHeadroom( 309 | thermal_manager_, kThermalHeadroomForecastSeconds); 310 | return thermal_headroom_; 311 | } 312 | #endif 313 | return thermal_headroom_; 314 | } 315 | 316 | // Initialize JNI calls for the PowerHintManager. 317 | bool ADPFManager::InitializePerformanceHintManager() { 318 | #if defined(PLATFORM_ANDROID) && __ANDROID_API__ >= 33 319 | if (android_get_device_api_level() < 31) { 320 | // The device might not support performance hint APIs, it will not initialized. 321 | UE_LOG(LogAndroidPerformance, Log, TEXT("The device API level is less than 31, the ADPF performance hint APIs will not work in this device.")); 322 | return false; 323 | } 324 | 325 | APerformanceHintManager* obj_perfhint_manager = APerformanceHint_getManager(); 326 | if(obj_perfhint_manager == nullptr) { 327 | // The device might not support performance hint APIs, it will not initialized. 328 | UE_LOG(LogAndroidPerformance, Log, TEXT("Get performance hint manager failed, the ADPF performance hint APIs will not work in this device.")); 329 | return false; 330 | } 331 | 332 | // Create performance hint sessions for game, render, and RHI threads. 333 | PRAGMA_DISABLE_DEPRECATION_WARNINGS 334 | const int32_t kGameThreadId = GGameThreadId; 335 | const int32_t kRenderThreadId = GRenderThreadId; 336 | const int32_t kRHIThreadId = GRHIThreadId; 337 | obj_perfhint_game_session_ = APerformanceHint_createSession(obj_perfhint_manager, 338 | &kGameThreadId, 1, 166666666); 339 | obj_perfhint_render_session_ = APerformanceHint_createSession(obj_perfhint_manager, 340 | &kRenderThreadId, 1, 166666666); 341 | obj_perfhint_rhi_session_ = APerformanceHint_createSession(obj_perfhint_manager, 342 | &kRHIThreadId, 1, 166666666); 343 | PRAGMA_ENABLE_DEPRECATION_WARNINGS 344 | #endif 345 | 346 | // Check if the performance hint sessions are created successfully. 347 | // If not, the device might not support performance hint APIs. 348 | if(obj_perfhint_game_session_ == nullptr || obj_perfhint_render_session_ == nullptr || 349 | obj_perfhint_rhi_session_ == nullptr) { 350 | // The API is not supported well. 351 | UE_LOG(LogAndroidPerformance, Log, TEXT("Creating performance hint session failed, the ADPF performance hint APIs will not work in this device.")); 352 | DestroyPerformanceHintManager(); 353 | return false; 354 | } 355 | return true; 356 | } 357 | 358 | void ADPFManager::DestroyPerformanceHintManager() { 359 | #if defined(PLATFORM_ANDROID) && __ANDROID_API__ >= 33 360 | prev_max_fps = -1.0f; 361 | 362 | UE_LOG(LogAndroidPerformance, Log, TEXT("Destroying performance hint sessions.")); 363 | if(obj_perfhint_game_session_) { 364 | APerformanceHint_closeSession(obj_perfhint_game_session_); 365 | obj_perfhint_game_session_ = nullptr; 366 | } 367 | if(obj_perfhint_render_session_) { 368 | APerformanceHint_closeSession(obj_perfhint_render_session_); 369 | obj_perfhint_render_session_ = nullptr; 370 | } 371 | if(obj_perfhint_rhi_session_) { 372 | APerformanceHint_closeSession(obj_perfhint_rhi_session_); 373 | obj_perfhint_rhi_session_ = nullptr; 374 | } 375 | #endif 376 | } 377 | 378 | // Indicates the start and end of the performance intensive task. 379 | // The methods call performance hint API to tell the performance 380 | // hint to the system. 381 | void ADPFManager::UpdatePerfHintSession(jlong duration_ns, jlong target_duration_ns, bool update_target_duration, 382 | APerformanceHintSession* obj_perfhint_session_) { 383 | #if defined(PLATFORM_ANDROID) && __ANDROID_API__ >= 33 384 | if (obj_perfhint_session_) { 385 | // UE_LOG(LogAndroidPerformance, Log, TEXT("Update performance hint session duration %lld ns, update target duration %s and %lld ns"), 386 | // duration_ns, update_target_duration ? TEXT("true") : TEXT("false"), target_duration_ns); 387 | APerformanceHint_reportActualWorkDuration(obj_perfhint_session_, duration_ns); 388 | if(update_target_duration) { 389 | APerformanceHint_updateTargetWorkDuration(obj_perfhint_session_, target_duration_ns); 390 | } 391 | } 392 | #endif 393 | } 394 | 395 | jlong ADPFManager::fpsToNanosec(const float maxFPS) { 396 | return static_cast(1000000000.0f / maxFPS); 397 | } 398 | 399 | void ADPFManager::saveQualityLevel(const float head_room) { 400 | if(head_room < 0.75f) { 401 | // 0.0 < x < 0.75 402 | target_quality_level = 3; 403 | } 404 | else if(head_room < 0.85f) { 405 | // 0.75 < x < 0.85 406 | target_quality_level = 2; 407 | } 408 | else if(head_room < 0.95f) { 409 | // 0.85 < x < 0.95 410 | target_quality_level = 1; 411 | } 412 | else { 413 | // 0.95 < x 414 | target_quality_level = 0; 415 | } 416 | } 417 | -------------------------------------------------------------------------------- /AndroidPerformance/Source/AndroidPerformance/Private/ADPFManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef ADPF_MANAGER_H_ 18 | #define ADPF_MANAGER_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "Scalability.h" 27 | 28 | /* 29 | * ADPFManager class anages the ADPF APIs. 30 | */ 31 | class ADPFManager { 32 | public: 33 | // Singleton function. 34 | static ADPFManager& getInstance() { 35 | static ADPFManager instance; 36 | return instance; 37 | } 38 | // Dtor. 39 | ~ADPFManager(); 40 | // Delete copy constructor since the class is used as a singleton. 41 | ADPFManager(ADPFManager const&) = delete; 42 | void operator=(ADPFManager const&) = delete; 43 | 44 | bool initialize(); 45 | void destroy(); 46 | 47 | // Invoke the method periodically (once a frame) to monitor 48 | // the device's thermal throttling status. 49 | void Monitor(); 50 | 51 | // Method to retrieve thermal manager. The API is used to register/unregister 52 | // callbacks from C API. 53 | AThermalManager* GetThermalManager() { return thermal_manager_; } 54 | 55 | private: 56 | inline jlong fpsToNanosec(const float maxFPS); 57 | void saveQualityLevel(const float head_room); 58 | 59 | // Update thermal headroom every 15 seconds. 60 | static constexpr int32_t kThermalHeadroomUpdateThreshold = 15; 61 | 62 | // Get current thermal headroom. 63 | static constexpr int32_t kThermalHeadroomForecastSeconds = 0; 64 | 65 | // Ctor. It's private since the class is designed as a singleton. 66 | ADPFManager(); 67 | 68 | // Functions to initialize ADPF API's calls. 69 | bool InitializePowerManager(); 70 | float UpdateThermalStatusHeadRoom(); 71 | bool InitializePerformanceHintManager(); 72 | void DestroyPerformanceHintManager(); 73 | 74 | // Get current thermal status and headroom. 75 | float GetThermalHeadroom() { return thermal_headroom_; } 76 | 77 | // Indicates the start and end of the performance intensive task. 78 | // The methods call performance hint API to tell the performance 79 | // hint to the system. 80 | void UpdatePerfHintSession(jlong duration_ns, jlong target_duration_ns, bool update_target_duration, 81 | APerformanceHintSession* obj_perfhint_session_); 82 | 83 | AThermalManager* thermal_manager_; 84 | bool initialized_performance_hint_manager; 85 | bool support_performance_hint_manager; 86 | float thermal_headroom_; 87 | float last_clock_; 88 | 89 | APerformanceHintSession* obj_perfhint_game_session_; 90 | APerformanceHintSession* obj_perfhint_render_session_; 91 | APerformanceHintSession* obj_perfhint_rhi_session_; 92 | 93 | static const int32_t max_quality_count = 4; 94 | Scalability::FQualityLevels quality_levels[max_quality_count]; 95 | int32_t current_quality_level; 96 | int32_t target_quality_level; 97 | 98 | float prev_max_fps; 99 | jlong prev_max_fps_nano; 100 | 101 | // for debug 102 | float fps_total; 103 | int fps_count; 104 | }; 105 | 106 | #endif // ADPF_MANAGER_H_ 107 | -------------------------------------------------------------------------------- /AndroidPerformance/Source/AndroidPerformance/Private/AndroidPerformanceLog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "CoreMinimal.h" 20 | 21 | DECLARE_LOG_CATEGORY_EXTERN(LogAndroidPerformance, Log, All); 22 | -------------------------------------------------------------------------------- /AndroidPerformance/Source/AndroidPerformance/Private/AndroidPerformanceModule.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "AndroidPerformanceModule.h" 18 | 19 | #define LOGTEXT_NAMESPACE "AndroidPerformance" 20 | 21 | #include "AndroidPerformanceLog.h" 22 | #include "ADPFManager.h" 23 | 24 | IMPLEMENT_MODULE(FAndroidPerformanceModule, AndroidPerformance) 25 | 26 | DEFINE_LOG_CATEGORY(LogAndroidPerformance); 27 | 28 | void FAndroidPerformanceModule::StartupModule() 29 | { 30 | #if PLATFORM_ANDROID 31 | UE_LOG(LogAndroidPerformance, Log, TEXT("Android Performance Module Started")); 32 | 33 | bool isInitialized = ADPFManager::getInstance().initialize(); 34 | 35 | // registration tick 36 | if(isInitialized) 37 | { 38 | FWorldDelegates::OnWorldTickStart.AddRaw(this, &FAndroidPerformanceModule::Tick); 39 | } 40 | else 41 | { 42 | UE_LOG(LogAndroidPerformance, Log, TEXT("Android Performance is not initialized because of not supporint device")); 43 | } 44 | #endif 45 | } 46 | 47 | void FAndroidPerformanceModule::ShutdownModule() 48 | { 49 | #if PLATFORM_ANDROID 50 | UE_LOG(LogAndroidPerformance, Log, TEXT("Android Performance Module Shutdown")); 51 | 52 | // unregistration tick 53 | FWorldDelegates::OnWorldTickStart.RemoveAll(this); 54 | 55 | ADPFManager::getInstance().destroy(); 56 | #endif 57 | } 58 | 59 | void FAndroidPerformanceModule::Tick(UWorld* world, ELevelTick tick_type, float delta_time) 60 | { 61 | #if PLATFORM_ANDROID 62 | ADPFManager::getInstance().Monitor(); 63 | #endif 64 | } 65 | 66 | #undef LOGTEXT_NAMESPACE 67 | -------------------------------------------------------------------------------- /AndroidPerformance/Source/AndroidPerformance/Public/AndroidPerformanceModule.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "Modules/ModuleManager.h" 20 | 21 | class FAndroidPerformanceModule final : public IModuleInterface 22 | { 23 | public: 24 | // IModuleInterface implementation 25 | void StartupModule() override; 26 | void ShutdownModule() override; 27 | 28 | void Tick(UWorld* world, ELevelTick tick_type, float delta_time); 29 | }; 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Dynamic Performance Framework (ADPF) Unreal plugin 2 | 3 | This repository enables you to use the [ADPF](https://developer.android.com/games/optimize/adpf) plugin in Unreal Engine. 4 | 5 | The plugin has two main features: thermal state and CPU performance hints. The plugin monitors the thermal state of a device and proactively adjusts performance before the level of performance becomes unsustainable. CPU performance hints let Android choose the right CPU clocks and core types instead of Android choosing based on previous workloads. 6 | 7 | ## Setup 8 | 1. Copy the AndroidPerformance folder to `GameProjectFolder/Plugins`. 9 | 2. Open the game project. 10 | 3. Enter **Edit → Plugins**. 11 | 4. Find **Android Performance** and check **Enabled**. The Unreal Engine suggests restarting the project. 12 | 5. Restart the game project, and trigger a build. 13 | 14 | If the game use 'NDK API Level' to 'android-33' or higher, use the main branch. Otherwise use 'use-jni' branch. 15 | 16 | ## Graphics quality levels 17 | Graphics quality is set by the Unreal Scalability `SetQualityLevels()` function, which changes view distance, anti-aliasing, shadow, post-processing, texture and effects, foliage, and shading quality levels. 18 | 19 | For more details about these graphics qualities, see the [Unreal Scalability reference](https://docs.unrealengine.com/4.27/en-US/TestingAndOptimization/PerformanceAndProfiling/Scalability/ScalabilityReference/). The Unreal plugin changes graphics quality level from 0 (lowest) to 3 (highest) based on the thermal state. Customize the graphics quality levels 0-3 based on the needs of your game environment. 20 | 21 | The game **must customize ADPF graphic quality logic** to use in-game graphic quality logic. 22 | 23 | ## License 24 | 25 | Copyright 2024 The Android Open Source Project 26 | 27 | Licensed under the Apache License, Version 2.0 (the "License"); 28 | you may not use this file except in compliance with the License. 29 | You may obtain a copy of the License at 30 | 31 | https://www.apache.org/licenses/LICENSE-2.0 32 | 33 | Unless required by applicable law or agreed to in writing, software 34 | distributed under the License is distributed on an "AS IS" BASIS, 35 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 36 | See the License for the specific language governing permissions and 37 | limitations under the License. 38 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We would love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our Community Guidelines 22 | 23 | This project follows [Google's Open Source Community 24 | Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code Reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests) 32 | for this purpose. 33 | --------------------------------------------------------------------------------