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