1 /* 2 * Copyright (C) 2010 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 * http://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 18 #include <android_native_app_glue.h> 19 20 #include <errno.h> 21 #include <jni.h> 22 #include <sys/time.h> 23 #include <time.h> 24 #include <android/log.h> 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <math.h> 29 30 #define LOG_TAG "libplasma" 31 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 32 #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) 33 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) 34 35 /* Set to 1 to enable debug log traces. */ 36 #define DEBUG 0 37 38 /* Set to 1 to optimize memory stores when generating plasma. */ 39 #define OPTIMIZE_WRITES 1 40 41 /* Return current time in milliseconds */ 42 static double now_ms(void) 43 { 44 struct timeval tv; 45 gettimeofday(&tv, NULL); 46 return tv.tv_sec*1000. + tv.tv_usec/1000.; 47 } 48 49 /* We're going to perform computations for every pixel of the target 50 * bitmap. floating-point operations are very slow on ARMv5, and not 51 * too bad on ARMv7 with the exception of trigonometric functions. 52 * 53 * For better performance on all platforms, we're going to use fixed-point 54 * arithmetic and all kinds of tricks 55 */ 56 57 typedef int32_t Fixed; 58 59 #define FIXED_BITS 16 60 #define FIXED_ONE (1 << FIXED_BITS) 61 #define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1) 62 63 #define FIXED_FROM_INT(x) ((x) << FIXED_BITS) 64 #define FIXED_TO_INT(x) ((x) >> FIXED_BITS) 65 66 #define FIXED_FROM_FLOAT(x) ((Fixed)((x)*FIXED_ONE)) 67 #define FIXED_TO_FLOAT(x) ((x)/(1.*FIXED_ONE)) 68 69 #define FIXED_MUL(x,y) (((int64_t)(x) * (y)) >> FIXED_BITS) 70 #define FIXED_DIV(x,y) (((int64_t)(x) * FIXED_ONE) / (y)) 71 72 #define FIXED_DIV2(x) ((x) >> 1) 73 #define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1) 74 75 #define FIXED_FRAC(x) ((x) & ((1 << FIXED_BITS)-1)) 76 #define FIXED_TRUNC(x) ((x) & ~((1 << FIXED_BITS)-1)) 77 78 #define FIXED_FROM_INT_FLOAT(x,f) (Fixed)((x)*(FIXED_ONE*(f))) 79 80 typedef int32_t Angle; 81 82 #define ANGLE_BITS 9 83 84 #if ANGLE_BITS < 8 85 # error ANGLE_BITS must be at least 8 86 #endif 87 88 #define ANGLE_2PI (1 << ANGLE_BITS) 89 #define ANGLE_PI (1 << (ANGLE_BITS-1)) 90 #define ANGLE_PI2 (1 << (ANGLE_BITS-2)) 91 #define ANGLE_PI4 (1 << (ANGLE_BITS-3)) 92 93 #define ANGLE_FROM_FLOAT(x) (Angle)((x)*ANGLE_PI/M_PI) 94 #define ANGLE_TO_FLOAT(x) ((x)*M_PI/ANGLE_PI) 95 96 #if ANGLE_BITS <= FIXED_BITS 97 # define ANGLE_FROM_FIXED(x) (Angle)((x) >> (FIXED_BITS - ANGLE_BITS)) 98 # define ANGLE_TO_FIXED(x) (Fixed)((x) << (FIXED_BITS - ANGLE_BITS)) 99 #else 100 # define ANGLE_FROM_FIXED(x) (Angle)((x) << (ANGLE_BITS - FIXED_BITS)) 101 # define ANGLE_TO_FIXED(x) (Fixed)((x) >> (ANGLE_BITS - FIXED_BITS)) 102 #endif 103 104 static Fixed angle_sin_tab[ANGLE_2PI+1]; 105 106 static void init_angles(void) 107 { 108 int nn; 109 for (nn = 0; nn < ANGLE_2PI+1; nn++) { 110 double radians = nn*M_PI/ANGLE_PI; 111 angle_sin_tab[nn] = FIXED_FROM_FLOAT(sin(radians)); 112 } 113 } 114 115 static __inline__ Fixed angle_sin( Angle a ) 116 { 117 return angle_sin_tab[(uint32_t)a & (ANGLE_2PI-1)]; 118 } 119 120 static __inline__ Fixed angle_cos( Angle a ) 121 { 122 return angle_sin(a + ANGLE_PI2); 123 } 124 125 static __inline__ Fixed fixed_sin( Fixed f ) 126 { 127 return angle_sin(ANGLE_FROM_FIXED(f)); 128 } 129 130 static __inline__ Fixed fixed_cos( Fixed f ) 131 { 132 return angle_cos(ANGLE_FROM_FIXED(f)); 133 } 134 135 /* Color palette used for rendering the plasma */ 136 #define PALETTE_BITS 8 137 #define PALETTE_SIZE (1 << PALETTE_BITS) 138 139 #if PALETTE_BITS > FIXED_BITS 140 # error PALETTE_BITS must be smaller than FIXED_BITS 141 #endif 142 143 static uint16_t palette[PALETTE_SIZE]; 144 145 static uint16_t make565(int red, int green, int blue) 146 { 147 return (uint16_t)( ((red << 8) & 0xf800) | 148 ((green << 2) & 0x03e0) | 149 ((blue >> 3) & 0x001f) ); 150 } 151 152 static void init_palette(void) 153 { 154 int nn, mm = 0; 155 /* fun with colors */ 156 for (nn = 0; nn < PALETTE_SIZE/4; nn++) { 157 int jj = (nn-mm)*4*255/PALETTE_SIZE; 158 palette[nn] = make565(255, jj, 255-jj); 159 } 160 161 for ( mm = nn; nn < PALETTE_SIZE/2; nn++ ) { 162 int jj = (nn-mm)*4*255/PALETTE_SIZE; 163 palette[nn] = make565(255-jj, 255, jj); 164 } 165 166 for ( mm = nn; nn < PALETTE_SIZE*3/4; nn++ ) { 167 int jj = (nn-mm)*4*255/PALETTE_SIZE; 168 palette[nn] = make565(0, 255-jj, 255); 169 } 170 171 for ( mm = nn; nn < PALETTE_SIZE; nn++ ) { 172 int jj = (nn-mm)*4*255/PALETTE_SIZE; 173 palette[nn] = make565(jj, 0, 255); 174 } 175 } 176 177 static __inline__ uint16_t palette_from_fixed( Fixed x ) 178 { 179 if (x < 0) x = -x; 180 if (x >= FIXED_ONE) x = FIXED_ONE-1; 181 int idx = FIXED_FRAC(x) >> (FIXED_BITS - PALETTE_BITS); 182 return palette[idx & (PALETTE_SIZE-1)]; 183 } 184 185 /* Angles expressed as fixed point radians */ 186 187 static void init_tables(void) 188 { 189 init_palette(); 190 init_angles(); 191 } 192 193 static void fill_plasma(ANativeWindow_Buffer* buffer, double t) 194 { 195 Fixed ft = FIXED_FROM_FLOAT(t/1000.); 196 Fixed yt1 = FIXED_FROM_FLOAT(t/1230.); 197 Fixed yt2 = yt1; 198 Fixed xt10 = FIXED_FROM_FLOAT(t/3000.); 199 Fixed xt20 = xt10; 200 201 #define YT1_INCR FIXED_FROM_FLOAT(1/100.) 202 #define YT2_INCR FIXED_FROM_FLOAT(1/163.) 203 204 void* pixels = buffer->bits; 205 //LOGI("width=%d height=%d stride=%d format=%d", buffer->width, buffer->height, 206 // buffer->stride, buffer->format); 207 208 int yy; 209 for (yy = 0; yy < buffer->height; yy++) { 210 uint16_t* line = (uint16_t*)pixels; 211 Fixed base = fixed_sin(yt1) + fixed_sin(yt2); 212 Fixed xt1 = xt10; 213 Fixed xt2 = xt20; 214 215 yt1 += YT1_INCR; 216 yt2 += YT2_INCR; 217 218 #define XT1_INCR FIXED_FROM_FLOAT(1/173.) 219 #define XT2_INCR FIXED_FROM_FLOAT(1/242.) 220 221 #if OPTIMIZE_WRITES 222 /* optimize memory writes by generating one aligned 32-bit store 223 * for every pair of pixels. 224 */ 225 uint16_t* line_end = line + buffer->width; 226 227 if (line < line_end) { 228 if (((uint32_t)line & 3) != 0) { 229 Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2); 230 231 xt1 += XT1_INCR; 232 xt2 += XT2_INCR; 233 234 line[0] = palette_from_fixed(ii >> 2); 235 line++; 236 } 237 238 while (line + 2 <= line_end) { 239 Fixed i1 = base + fixed_sin(xt1) + fixed_sin(xt2); 240 xt1 += XT1_INCR; 241 xt2 += XT2_INCR; 242 243 Fixed i2 = base + fixed_sin(xt1) + fixed_sin(xt2); 244 xt1 += XT1_INCR; 245 xt2 += XT2_INCR; 246 247 uint32_t pixel = ((uint32_t)palette_from_fixed(i1 >> 2) << 16) | 248 (uint32_t)palette_from_fixed(i2 >> 2); 249 250 ((uint32_t*)line)[0] = pixel; 251 line += 2; 252 } 253 254 if (line < line_end) { 255 Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2); 256 line[0] = palette_from_fixed(ii >> 2); 257 line++; 258 } 259 } 260 #else /* !OPTIMIZE_WRITES */ 261 int xx; 262 for (xx = 0; xx < buffer->width; xx++) { 263 264 Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2); 265 266 xt1 += XT1_INCR; 267 xt2 += XT2_INCR; 268 269 line[xx] = palette_from_fixed(ii / 4); 270 } 271 #endif /* !OPTIMIZE_WRITES */ 272 273 // go to next line 274 pixels = (uint16_t*)pixels + buffer->stride; 275 } 276 } 277 278 /* simple stats management */ 279 typedef struct { 280 double renderTime; 281 double frameTime; 282 } FrameStats; 283 284 #define MAX_FRAME_STATS 200 285 #define MAX_PERIOD_MS 1500 286 287 typedef struct { 288 double firstTime; 289 double lastTime; 290 double frameTime; 291 292 int firstFrame; 293 int numFrames; 294 FrameStats frames[ MAX_FRAME_STATS ]; 295 } Stats; 296 297 static void 298 stats_init( Stats* s ) 299 { 300 s->lastTime = now_ms(); 301 s->firstTime = 0.; 302 s->firstFrame = 0; 303 s->numFrames = 0; 304 } 305 306 static void 307 stats_startFrame( Stats* s ) 308 { 309 s->frameTime = now_ms(); 310 } 311 312 static void 313 stats_endFrame( Stats* s ) 314 { 315 double now = now_ms(); 316 double renderTime = now - s->frameTime; 317 double frameTime = now - s->lastTime; 318 int nn; 319 320 if (now - s->firstTime >= MAX_PERIOD_MS) { 321 if (s->numFrames > 0) { 322 double minRender, maxRender, avgRender; 323 double minFrame, maxFrame, avgFrame; 324 int count; 325 326 nn = s->firstFrame; 327 minRender = maxRender = avgRender = s->frames[nn].renderTime; 328 minFrame = maxFrame = avgFrame = s->frames[nn].frameTime; 329 for (count = s->numFrames; count > 0; count-- ) { 330 nn += 1; 331 if (nn >= MAX_FRAME_STATS) 332 nn -= MAX_FRAME_STATS; 333 double render = s->frames[nn].renderTime; 334 if (render < minRender) minRender = render; 335 if (render > maxRender) maxRender = render; 336 double frame = s->frames[nn].frameTime; 337 if (frame < minFrame) minFrame = frame; 338 if (frame > maxFrame) maxFrame = frame; 339 avgRender += render; 340 avgFrame += frame; 341 } 342 avgRender /= s->numFrames; 343 avgFrame /= s->numFrames; 344 345 LOGI("frame/s (avg,min,max) = (%.1f,%.1f,%.1f) " 346 "render time ms (avg,min,max) = (%.1f,%.1f,%.1f)\n", 347 1000./avgFrame, 1000./maxFrame, 1000./minFrame, 348 avgRender, minRender, maxRender); 349 } 350 s->numFrames = 0; 351 s->firstFrame = 0; 352 s->firstTime = now; 353 } 354 355 nn = s->firstFrame + s->numFrames; 356 if (nn >= MAX_FRAME_STATS) 357 nn -= MAX_FRAME_STATS; 358 359 s->frames[nn].renderTime = renderTime; 360 s->frames[nn].frameTime = frameTime; 361 362 if (s->numFrames < MAX_FRAME_STATS) { 363 s->numFrames += 1; 364 } else { 365 s->firstFrame += 1; 366 if (s->firstFrame >= MAX_FRAME_STATS) 367 s->firstFrame -= MAX_FRAME_STATS; 368 } 369 370 s->lastTime = now; 371 } 372 373 // ---------------------------------------------------------------------- 374 375 struct engine { 376 struct android_app* app; 377 378 Stats stats; 379 380 int animating; 381 }; 382 383 static void engine_draw_frame(struct engine* engine) { 384 if (engine->app->window == NULL) { 385 // No window. 386 return; 387 } 388 389 ANativeWindow_Buffer buffer; 390 if (ANativeWindow_lock(engine->app->window, &buffer, NULL) < 0) { 391 LOGW("Unable to lock window buffer"); 392 return; 393 } 394 395 stats_startFrame(&engine->stats); 396 397 struct timespec t; 398 t.tv_sec = t.tv_nsec = 0; 399 clock_gettime(CLOCK_MONOTONIC, &t); 400 int64_t time_ms = (((int64_t)t.tv_sec)*1000000000LL + t.tv_nsec)/1000000; 401 402 /* Now fill the values with a nice little plasma */ 403 fill_plasma(&buffer, time_ms); 404 405 ANativeWindow_unlockAndPost(engine->app->window); 406 407 stats_endFrame(&engine->stats); 408 } 409 410 static int engine_term_display(struct engine* engine) { 411 engine->animating = 0; 412 } 413 414 static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { 415 struct engine* engine = (struct engine*)app->userData; 416 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { 417 engine->animating = 1; 418 return 1; 419 } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { 420 LOGI("Key event: action=%d keyCode=%d metaState=0x%x", 421 AKeyEvent_getAction(event), 422 AKeyEvent_getKeyCode(event), 423 AKeyEvent_getMetaState(event)); 424 } 425 426 return 0; 427 } 428 429 static void engine_handle_cmd(struct android_app* app, int32_t cmd) { 430 struct engine* engine = (struct engine*)app->userData; 431 switch (cmd) { 432 case APP_CMD_INIT_WINDOW: 433 if (engine->app->window != NULL) { 434 engine_draw_frame(engine); 435 } 436 break; 437 case APP_CMD_TERM_WINDOW: 438 engine_term_display(engine); 439 break; 440 case APP_CMD_LOST_FOCUS: 441 engine->animating = 0; 442 engine_draw_frame(engine); 443 break; 444 } 445 } 446 447 void android_main(struct android_app* state) { 448 static int init; 449 450 struct engine engine; 451 452 // Make sure glue isn't stripped. 453 app_dummy(); 454 455 memset(&engine, 0, sizeof(engine)); 456 state->userData = &engine; 457 state->onAppCmd = engine_handle_cmd; 458 state->onInputEvent = engine_handle_input; 459 engine.app = state; 460 461 if (!init) { 462 init_tables(); 463 init = 1; 464 } 465 466 stats_init(&engine.stats); 467 468 // loop waiting for stuff to do. 469 470 while (1) { 471 // Read all pending events. 472 int ident; 473 int events; 474 struct android_poll_source* source; 475 476 // If not animating, we will block forever waiting for events. 477 // If animating, we loop until all events are read, then continue 478 // to draw the next frame of animation. 479 while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, 480 (void**)&source)) >= 0) { 481 482 // Process this event. 483 if (source != NULL) { 484 source->process(state, source); 485 } 486 487 // Check if we are exiting. 488 if (state->destroyRequested != 0) { 489 LOGI("Engine thread destroy requested!"); 490 engine_term_display(&engine); 491 return; 492 } 493 } 494 495 if (engine.animating) { 496 engine_draw_frame(&engine); 497 } 498 } 499 } 500