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
53 typedef 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
76 typedef 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__)
101 static Fixed *angle_sin_tab;
102 #else
103 static Fixed angle_sin_tab[ANGLE_2PI+1];
104 #endif // !__GDK__
105
init_angles(void)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
angle_sin(Angle a)115 static __inline__ Fixed angle_sin( Angle a )
116 {
117 return angle_sin_tab[(uint32_t)a & (ANGLE_2PI-1)];
118 }
119
angle_cos(Angle a)120 static __inline__ Fixed angle_cos( Angle a )
121 {
122 return angle_sin(a + ANGLE_PI2);
123 }
124
fixed_sin(Fixed f)125 static __inline__ Fixed fixed_sin( Fixed f )
126 {
127 return angle_sin(ANGLE_FROM_FIXED(f));
128 }
129
fixed_cos(Fixed f)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
make565(int red,int green,int blue)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
init_palette(void)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
palette_from_fixed(uint16_t * palette,Fixed x)177 static __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
init_tables(void)187 static void init_tables(void)
188 {
189 init_palette();
190 init_angles();
191 }
192
193
fill_plasma(uint32_t width,uint32_t height,uint32_t stride,double t,uint16_t * palette,void * pixels,Fixed * _angle_sin_tab)194 extern "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 */
now_ms(void)282 static 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 */
290 typedef struct {
291 double renderTime;
292 double frameTime;
293 } FrameStats;
294
295 #define MAX_FRAME_STATS 200
296 #define MAX_PERIOD_MS 1500
297
298 typedef 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
308 static void
stats_init(Stats * s)309 stats_init( Stats* s )
310 {
311 s->lastTime = now_ms();
312 s->firstTime = 0.;
313 s->firstFrame = 0;
314 s->numFrames = 0;
315 }
316
317 static void
stats_startFrame(Stats * s)318 stats_startFrame( Stats* s )
319 {
320 s->frameTime = now_ms();
321 }
322
323 static void
stats_endFrame(Stats * s)324 stats_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
384 typedef void (*pPlasmaType)(uint32_t, uint32_t, uint32_t, double, uint16_t*, void*, Fixed*);
385
386 #if !defined(__GDK__) && !defined(__NOGDK__)
lookupSymbol(void * pContext,const char * name)387 static void* lookupSymbol(void* pContext, const char* name)
388 {
389 return (void*) dlsym(RTLD_DEFAULT, name);
390 }
391 #endif // !__GDK__ && !__NOGDK__
392
Java_com_example_plasma_llvm_PlasmaView_gdk(JNIEnv * env,jobject obj)393 extern "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
Java_com_example_plasma_llvm_PlasmaView_nativeRenderPlasma(JNIEnv * env,jobject obj,jobject bitmap,jlong time_ms,jbyteArray scriptRef,jint length,jboolean use_llvm)402 extern "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