18
18
// BEGIN_INCLUDE(all)
19
19
#include < EGL/egl.h>
20
20
#include < GLES/gl.h>
21
+ #include < android/choreographer.h>
21
22
#include < android/log.h>
22
23
#include < android/sensor.h>
23
24
#include < android/set_abort_message.h>
@@ -104,18 +105,80 @@ struct Engine {
104
105
sensorManager, app->looper , ALOOPER_POLL_CALLBACK, callback, this );
105
106
}
106
107
107
- bool animating () const { return animating_; }
108
+ // / Resumes ticking the application.
109
+ void Resume () {
110
+ // Checked to make sure we don't double schedule Choreographer.
111
+ if (!running_) {
112
+ running_ = true ;
113
+ ScheduleNextTick ();
114
+ }
115
+ }
116
+
117
+ // / Pauses ticking the application.
118
+ // /
119
+ // / When paused, sensor and input events will still be processed, but the
120
+ // / update and render parts of the loop will not run.
121
+ void Pause () { running_ = false ; }
122
+
123
+ private:
124
+ bool running_;
108
125
109
- void StartRendering () {
110
- animating_ = true ;
126
+ void ScheduleNextTick () {
127
+ AChoreographer_postFrameCallback ( AChoreographer_getInstance (), Tick, this ) ;
111
128
}
112
129
113
- void StopRendering () {
114
- animating_ = false ;
130
+ // / Entry point for Choreographer.
131
+ // /
132
+ // / The first argument (the frame time) is not used as it is not needed for
133
+ // / this sample. If you copy from this sample and make use of that argument,
134
+ // / note that there's an API bug: that time is a signed 32-bit nanosecond
135
+ // / counter on 32-bit systems, so it will roll over every ~2 seconds. If your
136
+ // / minSdkVersion is 29 or higher, use AChoreographer_postFrameCallback64
137
+ // / instead, which is 64-bits for all architectures. Otherwise, bitwise-and
138
+ // / the value with the upper bits from CLOCK_MONOTONIC.
139
+ // /
140
+ // / \param data The Engine being ticked.
141
+ static void Tick (long , void * data) {
142
+ CHECK_NOT_NULL (data);
143
+ auto engine = reinterpret_cast <Engine*>(data);
144
+ engine->DoTick ();
115
145
}
116
146
117
- private:
118
- bool animating_;
147
+ void DoTick () {
148
+ if (!running_) {
149
+ return ;
150
+ }
151
+
152
+ // Input and sensor feedback is handled via their own callbacks.
153
+ // Choreographer ensures that those callbacks run before this callback does.
154
+
155
+ // Choreographer does not continuously schedule the callback. We have to re-
156
+ // register the callback each time we're ticked.
157
+ ScheduleNextTick ();
158
+ Update ();
159
+ DrawFrame ();
160
+ }
161
+
162
+ void Update () {
163
+ state.angle += .01f ;
164
+ if (state.angle > 1 ) {
165
+ state.angle = 0 ;
166
+ }
167
+ }
168
+
169
+ void DrawFrame () {
170
+ if (display == nullptr ) {
171
+ // No display.
172
+ return ;
173
+ }
174
+
175
+ // Just fill the screen with a color.
176
+ glClearColor (((float )state.x ) / width, state.angle ,
177
+ ((float )state.y ) / height, 1 );
178
+ glClear (GL_COLOR_BUFFER_BIT);
179
+
180
+ eglSwapBuffers (display, surface);
181
+ }
119
182
};
120
183
121
184
/* *
@@ -218,23 +281,6 @@ static int engine_init_display(Engine* engine) {
218
281
return 0 ;
219
282
}
220
283
221
- /* *
222
- * Just the current frame in the display.
223
- */
224
- static void engine_draw_frame (Engine* engine) {
225
- if (engine->display == nullptr ) {
226
- // No display.
227
- return ;
228
- }
229
-
230
- // Just fill the screen with a color.
231
- glClearColor (((float )engine->state .x ) / engine->width , engine->state .angle ,
232
- ((float )engine->state .y ) / engine->height , 1 );
233
- glClear (GL_COLOR_BUFFER_BIT);
234
-
235
- eglSwapBuffers (engine->display , engine->surface );
236
- }
237
-
238
284
/* *
239
285
* Tear down the EGL context currently associated with the display.
240
286
*/
@@ -250,7 +296,7 @@ static void engine_term_display(Engine* engine) {
250
296
}
251
297
eglTerminate (engine->display );
252
298
}
253
- engine->StopRendering ();
299
+ engine->Pause ();
254
300
engine->display = EGL_NO_DISPLAY;
255
301
engine->context = EGL_NO_CONTEXT;
256
302
engine->surface = EGL_NO_SURFACE;
@@ -302,7 +348,7 @@ static void engine_handle_cmd(android_app* app, int32_t cmd) {
302
348
engine->accelerometerSensor ,
303
349
(1000L / 60 ) * 1000 );
304
350
}
305
- engine->StartRendering ();
351
+ engine->Resume ();
306
352
break ;
307
353
case APP_CMD_LOST_FOCUS:
308
354
// When our app loses focus, we stop monitoring the accelerometer.
@@ -311,7 +357,7 @@ static void engine_handle_cmd(android_app* app, int32_t cmd) {
311
357
ASensorEventQueue_disableSensor (engine->sensorEventQueue ,
312
358
engine->accelerometerSensor );
313
359
}
314
- engine->StopRendering ();
360
+ engine->Pause ();
315
361
break ;
316
362
default :
317
363
break ;
@@ -358,18 +404,14 @@ void android_main(android_app* state) {
358
404
engine.state = *(SavedState*)state->savedState ;
359
405
}
360
406
361
- // loop waiting for stuff to do.
362
-
363
407
while (true ) {
364
408
// Read all pending events.
365
409
int events;
366
410
android_poll_source* source;
367
411
368
- // If not animating_, we will block forever waiting for events.
369
- // If animating_, we loop until all events are read, then continue
370
- // to draw the next frame of animation.
371
- while ((ALooper_pollAll (engine.animating () ? 0 : -1 , nullptr , &events,
372
- (void **)&source)) >= 0 ) {
412
+ // Our input, sensor, and update/render logic is all driven by callbacks, so
413
+ // we don't need to use the non-blocking poll.
414
+ while ((ALooper_pollAll (-1 , nullptr , &events, (void **)&source)) >= 0 ) {
373
415
// Process this event.
374
416
if (source != nullptr ) {
375
417
source->process (state, source);
@@ -381,18 +423,6 @@ void android_main(android_app* state) {
381
423
return ;
382
424
}
383
425
}
384
-
385
- if (engine.animating ()) {
386
- // Done with events; draw next animation frame.
387
- engine.state .angle += .01f ;
388
- if (engine.state .angle > 1 ) {
389
- engine.state .angle = 0 ;
390
- }
391
-
392
- // Drawing is throttled to the screen update rate, so there
393
- // is no need to do timing here.
394
- engine_draw_frame (&engine);
395
- }
396
426
}
397
427
}
398
428
// END_INCLUDE(all)
0 commit comments