The open source OpenXR runtime

d/android: Reduce CPU usage

1. "ALooper_pollAll" sets the timeout to "max_wait_milliseconds".
2. Destroy the event queue when the thread exits.
3. Handle sensor sleep situation.

Part-of: <https://gitlab.freedesktop.org/monado/monado/-/merge_requests/2260>

authored by

Meng Jiao and committed by
Korcan Hussein
fe8ecf0a d98fda64

+83 -57
+83 -52
src/xrt/drivers/android/android_sensors.c
··· 23 23 24 24 #include <xrt/xrt_config_android.h> 25 25 26 + // Workaround to avoid the inclusion of "android_native_app_glue.h. 27 + #ifndef LOOPER_ID_USER 28 + #define LOOPER_ID_USER 3 29 + #endif 30 + 26 31 // 60 events per second (in us). 27 32 #define POLL_RATE_USEC (1000L / 60) * 1000 28 33 ··· 37 42 38 43 // Callback for the Android sensor event queue 39 44 static int 40 - android_sensor_callback(int fd, int events, void *data) 45 + android_sensor_callback(ASensorEvent *event, struct android_device *d) 41 46 { 42 - struct android_device *d = (struct android_device *)data; 43 - 44 - if (d->accelerometer == NULL || d->gyroscope == NULL) 45 - return 1; 46 - 47 - ASensorEvent event; 48 47 struct xrt_vec3 gyro; 49 48 struct xrt_vec3 accel; 50 - while (ASensorEventQueue_getEvents(d->event_queue, &event, 1) > 0) { 51 49 52 - switch (event.type) { 53 - case ASENSOR_TYPE_ACCELEROMETER: { 54 - accel.x = event.acceleration.y; 55 - accel.y = -event.acceleration.x; 56 - accel.z = event.acceleration.z; 50 + switch (event->type) { 51 + case ASENSOR_TYPE_ACCELEROMETER: { 52 + accel.x = event->acceleration.y; 53 + accel.y = -event->acceleration.x; 54 + accel.z = event->acceleration.z; 57 55 58 - ANDROID_TRACE(d, "accel %" PRId64 " %.2f %.2f %.2f", event.timestamp, accel.x, accel.y, 59 - accel.z); 60 - break; 61 - } 62 - case ASENSOR_TYPE_GYROSCOPE: { 63 - gyro.x = -event.data[1]; 64 - gyro.y = event.data[0]; 65 - gyro.z = event.data[2]; 56 + ANDROID_TRACE(d, "accel %" PRId64 " %.2f %.2f %.2f", event->timestamp, accel.x, accel.y, accel.z); 57 + break; 58 + } 59 + case ASENSOR_TYPE_GYROSCOPE: { 60 + gyro.x = -event->data[1]; 61 + gyro.y = event->data[0]; 62 + gyro.z = event->data[2]; 66 63 67 - ANDROID_TRACE(d, "gyro %" PRId64 " %.2f %.2f %.2f", event.timestamp, gyro.x, gyro.y, gyro.z); 64 + ANDROID_TRACE(d, "gyro %" PRId64 " %.2f %.2f %.2f", event->timestamp, gyro.x, gyro.y, gyro.z); 68 65 69 - // TODO: Make filter handle accelerometer 70 - struct xrt_vec3 null_accel; 66 + // TODO: Make filter handle accelerometer 67 + struct xrt_vec3 null_accel; 71 68 72 - // Lock last and the fusion. 73 - os_mutex_lock(&d->lock); 69 + // Lock last and the fusion. 70 + os_mutex_lock(&d->lock); 74 71 75 - m_imu_3dof_update(&d->fusion, event.timestamp, &null_accel, &gyro); 72 + m_imu_3dof_update(&d->fusion, event->timestamp, &null_accel, &gyro); 76 73 77 - // Now done. 78 - os_mutex_unlock(&d->lock); 79 - } 80 - default: ANDROID_TRACE(d, "Unhandled event type %d", event.type); 81 - } 74 + // Now done. 75 + os_mutex_unlock(&d->lock); 76 + } 77 + default: ANDROID_TRACE(d, "Unhandled event type %d", event->type); 82 78 } 83 79 84 80 return 1; ··· 97 93 { 98 94 struct android_device *d = (struct android_device *)ptr; 99 95 const int32_t poll_rate_usec = android_get_sensor_poll_rate(d); 100 - 96 + // Maximum waiting time for sensor events. 97 + static const int max_wait_milliseconds = 100; 98 + ASensorManager *sensor_manager = NULL; 99 + const ASensor *accelerometer = NULL; 100 + const ASensor *gyroscope = NULL; 101 + ASensorEventQueue *event_queue = NULL; 101 102 #if __ANDROID_API__ >= 26 102 - d->sensor_manager = ASensorManager_getInstanceForPackage(XRT_ANDROID_PACKAGE); 103 + sensor_manager = ASensorManager_getInstanceForPackage(XRT_ANDROID_PACKAGE); 103 104 #else 104 - d->sensor_manager = ASensorManager_getInstance(); 105 + sensor_manager = ASensorManager_getInstance(); 105 106 #endif 106 107 107 - d->accelerometer = ASensorManager_getDefaultSensor(d->sensor_manager, ASENSOR_TYPE_ACCELEROMETER); 108 - d->gyroscope = ASensorManager_getDefaultSensor(d->sensor_manager, ASENSOR_TYPE_GYROSCOPE); 108 + accelerometer = ASensorManager_getDefaultSensor(sensor_manager, ASENSOR_TYPE_ACCELEROMETER); 109 + gyroscope = ASensorManager_getDefaultSensor(sensor_manager, ASENSOR_TYPE_GYROSCOPE); 109 110 110 - ALooper *looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); 111 + ALooper *event_looper = ALooper_forThread(); 112 + if (event_looper == NULL) { 113 + event_looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); 114 + ANDROID_INFO(d, 115 + "Created new event event_looper for " 116 + "sensor capture thread."); 117 + } 111 118 112 - d->event_queue = ASensorManager_createEventQueue(d->sensor_manager, looper, ALOOPER_POLL_CALLBACK, 113 - android_sensor_callback, (void *)d); 119 + event_queue = ASensorManager_createEventQueue(sensor_manager, event_looper, LOOPER_ID_USER, NULL, (void *)d); 120 + 114 121 115 122 /* 116 123 * Start sensors in case this was not done already. ··· 121 128 * the screen refresh rate, which could be smaller than the sensor's 122 129 * minimum delay value. Make sure to set it to a valid value. 123 130 */ 124 - if (d->accelerometer != NULL) { 125 - int32_t accelerometer_min_delay = ASensor_getMinDelay(d->accelerometer); 131 + if (accelerometer != NULL) { 132 + int32_t accelerometer_min_delay = ASensor_getMinDelay(accelerometer); 126 133 int32_t accelerometer_poll_rate_usec = MAX(poll_rate_usec, accelerometer_min_delay); 127 134 128 - ASensorEventQueue_enableSensor(d->event_queue, d->accelerometer); 129 - ASensorEventQueue_setEventRate(d->event_queue, d->accelerometer, accelerometer_poll_rate_usec); 135 + ASensorEventQueue_enableSensor(event_queue, accelerometer); 136 + ASensorEventQueue_setEventRate(event_queue, accelerometer, accelerometer_poll_rate_usec); 130 137 } 131 - if (d->gyroscope != NULL) { 132 - int32_t gyroscope_min_delay = ASensor_getMinDelay(d->gyroscope); 138 + if (gyroscope != NULL) { 139 + int32_t gyroscope_min_delay = ASensor_getMinDelay(gyroscope); 133 140 int32_t gyroscope_poll_rate_usec = MAX(poll_rate_usec, gyroscope_min_delay); 134 141 135 - ASensorEventQueue_enableSensor(d->event_queue, d->gyroscope); 136 - ASensorEventQueue_setEventRate(d->event_queue, d->gyroscope, gyroscope_poll_rate_usec); 142 + ASensorEventQueue_enableSensor(event_queue, gyroscope); 143 + ASensorEventQueue_setEventRate(event_queue, gyroscope, gyroscope_poll_rate_usec); 137 144 } 138 145 139 - int ret = 0; 140 - while (d->oth.running && ret != ALOOPER_POLL_ERROR) { 141 - ret = ALooper_pollAll(0, NULL, NULL, NULL); 146 + while (d->oth.running) { 147 + int num_events = 0; 148 + const int looper_id = ALooper_pollAll(max_wait_milliseconds, NULL, &num_events, NULL); 149 + // The device may have enabled a power-saving policy, causing the sensor to sleep and return 150 + // ALOOPER_POLL_ERROR. However, we want to continue reading data when it wakes up. 151 + if (looper_id != LOOPER_ID_USER) { 152 + ANDROID_ERROR(d, "ALooper_pollAll failed with looper_id: %d", looper_id); 153 + continue; 154 + } 155 + if (num_events <= 0) { 156 + ANDROID_ERROR(d, "ALooper_pollAll returned zero events"); 157 + continue; 158 + } 159 + // read event 160 + ASensorEvent event; 161 + while (ASensorEventQueue_getEvents(event_queue, &event, 1) > 0) { 162 + android_sensor_callback(&event, d); 163 + } 142 164 } 143 - 165 + // Disable sensors. 166 + if (accelerometer != NULL) { 167 + ASensorEventQueue_disableSensor(event_queue, accelerometer); 168 + } 169 + if (gyroscope != NULL) { 170 + ASensorEventQueue_disableSensor(event_queue, gyroscope); 171 + } 172 + // Destroy the event queue. 173 + ASensorManager_destroyEventQueue(sensor_manager, event_queue); 174 + ANDROID_INFO(d, "android_run_thread exit"); 144 175 return NULL; 145 176 } 146 177
-5
src/xrt/drivers/android/android_sensors.h
··· 33 33 { 34 34 struct xrt_device base; 35 35 struct os_thread_helper oth; 36 - 37 - ASensorManager *sensor_manager; 38 - const ASensor *accelerometer; 39 - const ASensor *gyroscope; 40 - ASensorEventQueue *event_queue; 41 36 struct u_cardboard_distortion cardboard; 42 37 43 38