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 #include <jni.h> 18 #include <time.h> 19 #include <android/log.h> 20 #include <android/bitmap.h> 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <math.h> 25 26 #include "bcc/bcc.h" 27 28 #define LOG_TAG "libplasma" 29 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 30 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) 31 32 /* Set to 1 to enable debug log traces. */ 33 #define DEBUG 0 34 35 /* Set to 1 to optimize memory stores when generating plasma. */ 36 #define OPTIMIZE_WRITES 1 37 38 /* Return current time in milliseconds */ 39 static double now_ms(void) 40 { 41 struct timeval tv; 42 gettimeofday(&tv, NULL); 43 return tv.tv_sec*1000. + tv.tv_usec/1000.; 44 } 45 46 /* We're going to perform computations for every pixel of the target 47 * bitmap. floating-point operations are very slow on ARMv5, and not 48 * too bad on ARMv7 with the exception of trigonometric functions. 49 * 50 * For better performance on all platforms, we're going to use fixed-point 51 * arithmetic and all kinds of tricks 52 */ 53 54 typedef int32_t Fixed; 55 56 #define FIXED_BITS 16 57 #define FIXED_ONE (1 << FIXED_BITS) 58 #define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1) 59 60 #define FIXED_FROM_INT(x) ((x) << FIXED_BITS) 61 #define FIXED_TO_INT(x) ((x) >> FIXED_BITS) 62 63 #define FIXED_FROM_FLOAT(x) ((Fixed)((x)*FIXED_ONE)) 64 #define FIXED_TO_FLOAT(x) ((x)/(1.*FIXED_ONE)) 65 66 #define FIXED_MUL(x,y) (((int64_t)(x) * (y)) >> FIXED_BITS) 67 #define FIXED_DIV(x,y) (((int64_t)(x) * FIXED_ONE) / (y)) 68 69 #define FIXED_DIV2(x) ((x) >> 1) 70 #define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1) 71 72 #define FIXED_FRAC(x) ((x) & ((1 << FIXED_BITS)-1)) 73 #define FIXED_TRUNC(x) ((x) & ~((1 << FIXED_BITS)-1)) 74 75 #define FIXED_FROM_INT_FLOAT(x,f) (Fixed)((x)*(FIXED_ONE*(f))) 76 77 typedef int32_t Angle; 78 79 #define ANGLE_BITS 9 80 81 #if ANGLE_BITS < 8 82 # error ANGLE_BITS must be at least 8 83 #endif 84 85 #define ANGLE_2PI (1 << ANGLE_BITS) 86 #define ANGLE_PI (1 << (ANGLE_BITS-1)) 87 #define ANGLE_PI2 (1 << (ANGLE_BITS-2)) 88 #define ANGLE_PI4 (1 << (ANGLE_BITS-3)) 89 90 #define ANGLE_FROM_FLOAT(x) (Angle)((x)*ANGLE_PI/M_PI) 91 #define ANGLE_TO_FLOAT(x) ((x)*M_PI/ANGLE_PI) 92 93 #if ANGLE_BITS <= FIXED_BITS 94 # define ANGLE_FROM_FIXED(x) (Angle)((x) >> (FIXED_BITS - ANGLE_BITS)) 95 # define ANGLE_TO_FIXED(x) (Fixed)((x) << (FIXED_BITS - ANGLE_BITS)) 96 #else 97 # define ANGLE_FROM_FIXED(x) (Angle)((x) << (ANGLE_BITS - FIXED_BITS)) 98 # define ANGLE_TO_FIXED(x) (Fixed)((x) >> (ANGLE_BITS - FIXED_BITS)) 99 #endif 100 101 static Fixed angle_sin_tab[ANGLE_2PI+1]; 102 103 static void init_angles(void) 104 { 105 int nn; 106 for (nn = 0; nn < ANGLE_2PI+1; nn++) { 107 double radians = nn*M_PI/ANGLE_PI; 108 angle_sin_tab[nn] = FIXED_FROM_FLOAT(sin(radians)); 109 } 110 } 111 112 static __inline__ Fixed angle_sin( Angle a ) 113 { 114 return angle_sin_tab[(uint32_t)a & (ANGLE_2PI-1)]; 115 } 116 117 static __inline__ Fixed angle_cos( Angle a ) 118 { 119 return angle_sin(a + ANGLE_PI2); 120 } 121 122 static __inline__ Fixed fixed_sin( Fixed f ) 123 { 124 return angle_sin(ANGLE_FROM_FIXED(f)); 125 } 126 127 static __inline__ Fixed fixed_cos( Fixed f ) 128 { 129 return angle_cos(ANGLE_FROM_FIXED(f)); 130 } 131 132 /* Color palette used for rendering the plasma */ 133 #define PALETTE_BITS 8 134 #define PALETTE_SIZE (1 << PALETTE_BITS) 135 136 #if PALETTE_BITS > FIXED_BITS 137 # error PALETTE_BITS must be smaller than FIXED_BITS 138 #endif 139 140 static uint16_t palette[PALETTE_SIZE]; 141 142 static uint16_t make565(int red, int green, int blue) 143 { 144 return (uint16_t)( ((red << 8) & 0xf800) | 145 ((green << 2) & 0x03e0) | 146 ((blue >> 3) & 0x001f) ); 147 } 148 149 static void init_palette(void) 150 { 151 int nn, mm = 0; 152 /* fun with colors */ 153 for (nn = 0; nn < PALETTE_SIZE/4; nn++) { 154 int jj = (nn-mm)*4*255/PALETTE_SIZE; 155 palette[nn] = make565(255, jj, 255-jj); 156 } 157 158 for ( mm = nn; nn < PALETTE_SIZE/2; nn++ ) { 159 int jj = (nn-mm)*4*255/PALETTE_SIZE; 160 palette[nn] = make565(255-jj, 255, jj); 161 } 162 163 for ( mm = nn; nn < PALETTE_SIZE*3/4; nn++ ) { 164 int jj = (nn-mm)*4*255/PALETTE_SIZE; 165 palette[nn] = make565(0, 255-jj, 255); 166 } 167 168 for ( mm = nn; nn < PALETTE_SIZE; nn++ ) { 169 int jj = (nn-mm)*4*255/PALETTE_SIZE; 170 palette[nn] = make565(jj, 0, 255); 171 } 172 } 173 174 static __inline__ uint16_t palette_from_fixed( Fixed x ) 175 { 176 if (x < 0) x = -x; 177 if (x >= FIXED_ONE) x = FIXED_ONE-1; 178 int idx = FIXED_FRAC(x) >> (FIXED_BITS - PALETTE_BITS); 179 return palette[idx & (PALETTE_SIZE-1)]; 180 } 181 182 /* Angles expressed as fixed point radians */ 183 184 static void init_tables(void) 185 { 186 init_palette(); 187 init_angles(); 188 } 189 190 static void fill_plasma( AndroidBitmapInfo* info, void* pixels, double t ) 191 { 192 Fixed ft = FIXED_FROM_FLOAT(t/1000.); 193 Fixed yt1 = FIXED_FROM_FLOAT(t/1230.); 194 Fixed yt2 = yt1; 195 Fixed xt10 = FIXED_FROM_FLOAT(t/3000.); 196 Fixed xt20 = xt10; 197 198 #define YT1_INCR FIXED_FROM_FLOAT(1/100.) 199 #define YT2_INCR FIXED_FROM_FLOAT(1/163.) 200 201 int yy; 202 for (yy = 0; yy < info->height; yy++) { 203 uint16_t* line = (uint16_t*)pixels; 204 Fixed base = fixed_sin(yt1) + fixed_sin(yt2); 205 Fixed xt1 = xt10; 206 Fixed xt2 = xt20; 207 208 yt1 += YT1_INCR; 209 yt2 += YT2_INCR; 210 211 #define XT1_INCR FIXED_FROM_FLOAT(1/173.) 212 #define XT2_INCR FIXED_FROM_FLOAT(1/242.) 213 214 #if OPTIMIZE_WRITES 215 /* optimize memory writes by generating one aligned 32-bit store 216 * for every pair of pixels. 217 */ 218 uint16_t* line_end = line + info->width; 219 220 if (line < line_end) { 221 if (((uint32_t)line & 3) != 0) { 222 Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2); 223 224 xt1 += XT1_INCR; 225 xt2 += XT2_INCR; 226 227 line[0] = palette_from_fixed(ii >> 2); 228 line++; 229 } 230 231 while (line + 2 <= line_end) { 232 Fixed i1 = base + fixed_sin(xt1) + fixed_sin(xt2); 233 xt1 += XT1_INCR; 234 xt2 += XT2_INCR; 235 236 Fixed i2 = base + fixed_sin(xt1) + fixed_sin(xt2); 237 xt1 += XT1_INCR; 238 xt2 += XT2_INCR; 239 240 uint32_t pixel = ((uint32_t)palette_from_fixed(i1 >> 2) << 16) | 241 (uint32_t)palette_from_fixed(i2 >> 2); 242 243 ((uint32_t*)line)[0] = pixel; 244 line += 2; 245 } 246 247 if (line < line_end) { 248 Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2); 249 line[0] = palette_from_fixed(ii >> 2); 250 line++; 251 } 252 } 253 #else /* !OPTIMIZE_WRITES */ 254 int xx; 255 for (xx = 0; xx < info->width; xx++) { 256 257 Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2); 258 259 xt1 += XT1_INCR; 260 xt2 += XT2_INCR; 261 262 line[xx] = palette_from_fixed(ii / 4); 263 } 264 #endif /* !OPTIMIZE_WRITES */ 265 266 // go to next line 267 pixels = (char*)pixels + info->stride; 268 } 269 } 270 271 /* simple stats management */ 272 typedef struct { 273 double renderTime; 274 double frameTime; 275 } FrameStats; 276 277 #define MAX_FRAME_STATS 200 278 #define MAX_PERIOD_MS 1500 279 280 typedef struct { 281 double firstTime; 282 double lastTime; 283 double frameTime; 284 285 int firstFrame; 286 int numFrames; 287 FrameStats frames[ MAX_FRAME_STATS ]; 288 } Stats; 289 290 static void 291 stats_init( Stats* s ) 292 { 293 s->lastTime = now_ms(); 294 s->firstTime = 0.; 295 s->firstFrame = 0; 296 s->numFrames = 0; 297 } 298 299 static void 300 stats_startFrame( Stats* s ) 301 { 302 s->frameTime = now_ms(); 303 } 304 305 static void 306 stats_endFrame( Stats* s ) 307 { 308 double now = now_ms(); 309 double renderTime = now - s->frameTime; 310 double frameTime = now - s->lastTime; 311 int nn; 312 313 if (now - s->firstTime >= MAX_PERIOD_MS) { 314 if (s->numFrames > 0) { 315 double minRender, maxRender, avgRender; 316 double minFrame, maxFrame, avgFrame; 317 int count; 318 319 nn = s->firstFrame; 320 minRender = maxRender = avgRender = s->frames[nn].renderTime; 321 minFrame = maxFrame = avgFrame = s->frames[nn].frameTime; 322 for (count = s->numFrames; count > 0; count-- ) { 323 nn += 1; 324 if (nn >= MAX_FRAME_STATS) 325 nn -= MAX_FRAME_STATS; 326 double render = s->frames[nn].renderTime; 327 if (render < minRender) minRender = render; 328 if (render > maxRender) maxRender = render; 329 double frame = s->frames[nn].frameTime; 330 if (frame < minFrame) minFrame = frame; 331 if (frame > maxFrame) maxFrame = frame; 332 avgRender += render; 333 avgFrame += frame; 334 } 335 avgRender /= s->numFrames; 336 avgFrame /= s->numFrames; 337 338 LOGI("frame/s (avg,min,max) = (%.1f,%.1f,%.1f) " 339 "render time ms (avg,min,max) = (%.1f,%.1f,%.1f)\n", 340 1000./avgFrame, 1000./maxFrame, 1000./minFrame, 341 avgRender, minRender, maxRender); 342 } 343 s->numFrames = 0; 344 s->firstFrame = 0; 345 s->firstTime = now; 346 } 347 348 nn = s->firstFrame + s->numFrames; 349 if (nn >= MAX_FRAME_STATS) 350 nn -= MAX_FRAME_STATS; 351 352 s->frames[nn].renderTime = renderTime; 353 s->frames[nn].frameTime = frameTime; 354 355 if (s->numFrames < MAX_FRAME_STATS) { 356 s->numFrames += 1; 357 } else { 358 s->firstFrame += 1; 359 if (s->firstFrame >= MAX_FRAME_STATS) 360 s->firstFrame -= MAX_FRAME_STATS; 361 } 362 363 s->lastTime = now; 364 } 365 366 typedef void (*pPlasmaType)(uint32_t, uint32_t, uint32_t, double, uint16_t*, void*, void*); 367 368 extern "C" JNIEXPORT jint JNICALL Java_com_example_plasma_llvm_PlasmaView_nativeRenderPlasma 369 (JNIEnv * env, jobject obj, 370 jobject bitmap, jlong time_ms, jbyteArray scriptRef, jint length, jboolean use_llvm) 371 { 372 AndroidBitmapInfo info; 373 void* pixels; 374 int ret; 375 static Stats stats; 376 static int init; 377 static double time_sum = 0; 378 static int count = 0; 379 static bool last_mode = false; 380 static pPlasmaType native_function = NULL; 381 static BCCScriptRef script_ref; 382 383 if (last_mode != use_llvm) 384 count = 0, time_sum = 0; 385 last_mode = use_llvm; 386 387 if (!init) { 388 init_tables(); 389 stats_init(&stats); 390 init = 1; 391 } 392 393 if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { 394 LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); 395 return -1; 396 } 397 398 if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) { 399 LOGE("Bitmap format is not RGB_565 !"); 400 return -1; 401 } 402 403 if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { 404 LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); 405 } 406 407 408 if (use_llvm) { 409 double start_jit = now_ms(); 410 411 if (native_function == NULL) { 412 script_ref = bccCreateScript(); 413 414 jbyte* script_ptr = (jbyte *)env->GetPrimitiveArrayCritical(scriptRef, (jboolean *)0); 415 416 LOGI("BCC Script Len: %d", length); 417 if(bccReadBC(script_ref, "libplasma_portable.bc", (const char*)script_ptr, length, 0)) { 418 LOGE("Error! Cannot bccReadBc"); 419 return -1; 420 } 421 if (script_ptr) { 422 env->ReleasePrimitiveArrayCritical(scriptRef, script_ptr, 0); 423 } 424 425 if (bccLinkFile(script_ref, "/system/lib/libclcore.bc", 0)) { 426 LOGE("Error! Cannot bccLinkBC"); 427 return -1; 428 } 429 430 if (bccPrepareExecutable(script_ref, "/data/data/com.example.plasma.llvm/", "plasmaLLVM", 0)) { 431 LOGE("Error! Cannot bccPrepareExecutable"); 432 return -1; 433 } 434 native_function = (pPlasmaType)bccGetFuncAddr(script_ref, "root"); 435 if (native_function == NULL) { 436 LOGE("Error! Cannot find fill_plasma()"); 437 return -1; 438 } 439 } 440 441 double start_run = now_ms(); 442 native_function(info.width, info.height, info.stride, time_ms, palette, pixels, angle_sin_tab); 443 double diff = now_ms()-start_run; 444 LOGI("LLVM Time JIT: %.2lf , Run: %.2lf, Avg: %.2lf", start_run-start_jit, diff, time_sum / count); 445 time_sum += diff + start_run - start_jit; 446 } else { 447 double start_run = now_ms(); 448 fill_plasma(&info, pixels, time_ms ); 449 double diff = now_ms()-start_run; 450 LOGI("GCC Time Run: %.2lf, Avg: %.2lf", diff, time_sum / count); 451 time_sum += diff; 452 } 453 count++; 454 455 AndroidBitmap_unlockPixels(env, bitmap); 456 457 return count * 1000.0 / time_sum; 458 } 459