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