ncval-annotate: Go faster: use --start-address with objdump
[nativeclient.git] / tests / mandel_nav / mandel_nav.c
blob3731c389024426cedc3e75ca76e77a1f502b1753
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 simple demo for nacl client, computed mandelbrot set
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <math.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #include <sys/mman.h>
44 #include <sys/stat.h>
45 #include <pthread.h>
46 #include <nacl/nacl_srpc.h>
47 #include <nacl/nacl_av.h>
49 #define FPEPSILON 1.0e-10
50 #define FPEQUAL(x, y) (abs(x - y) < FPEPSILON)
52 #define IND(arr, width, i, j) *(arr + (i) * (width) + (j))
54 #define BASEWIDTH 500
55 #define BASEHEIGHT 500
57 pthread_mutex_t work_mu = PTHREAD_MUTEX_INITIALIZER;
58 pthread_cond_t work_cv = PTHREAD_COND_INITIALIZER;
60 enum work_id { WORK_SETUP, WORK_SHUTDOWN, WORK_SET_REGION, WORK_DISPLAY,
61 WORK_SHIFT_COLOR, };
63 struct work_item {
64 enum work_id kind;
65 pthread_mutex_t *mu;
66 pthread_cond_t *cv;
67 int *done;
68 union {
69 double setup_canvas_width;
70 struct {
71 double new_x_left, new_y_top, new_x_right, new_y_bottom;
72 } set_region;
73 int shift_color;
74 } u;
77 struct work_item *work = NULL;
79 __thread struct work_item tls_item;
80 __thread pthread_mutex_t tls_mu = PTHREAD_MUTEX_INITIALIZER;
81 __thread pthread_cond_t tls_cv = PTHREAD_COND_INITIALIZER;
82 __thread int tls_done;
84 void work_put(struct work_item *item) {
85 item->mu = &tls_mu;
86 item->cv = &tls_cv;
87 item->done = &tls_done;
88 tls_done = 0;
90 pthread_mutex_lock(&work_mu);
91 while (NULL != work) {
92 pthread_cond_wait(&work_cv, &work_mu);
94 work = item;
95 pthread_cond_broadcast(&work_cv);
96 pthread_mutex_unlock(&work_mu);
98 pthread_mutex_lock(item->mu);
99 while (0 == *item->done) {
100 pthread_cond_wait(item->cv, item->mu);
102 pthread_mutex_unlock(item->mu);
105 struct work_item *work_get(void) {
106 struct work_item *cur;
108 pthread_mutex_lock(&work_mu);
109 while (NULL == (cur = work)) {
110 pthread_cond_wait(&work_cv, &work_mu);
112 pthread_mutex_unlock(&work_mu);
114 return cur;
117 void work_mark_done(struct work_item *item) {
118 pthread_mutex_lock(item->mu);
119 *item->done = 1;
120 pthread_cond_broadcast(item->cv);
121 pthread_mutex_unlock(item->mu);
123 pthread_mutex_lock(&work_mu);
124 work = NULL;
125 pthread_cond_broadcast(&work_cv);
126 pthread_mutex_unlock(&work_mu);
130 * Local types.
132 typedef struct Rgb_ {
133 unsigned char r;
134 unsigned char g;
135 unsigned char b;
136 } Rgb;
138 typedef struct Pixel_ {
139 unsigned char b;
140 unsigned char g;
141 unsigned char r;
142 unsigned char a;
143 } Pixel;
146 * Module static variables.
150 * Display memory related variables.
152 static char display_mem[BASEWIDTH * BASEHEIGHT * sizeof(Pixel)];
153 static char pixmap[BASEWIDTH * BASEHEIGHT];
154 static char mask[BASEWIDTH * BASEHEIGHT];
157 * Mandelbrot set range values.
158 * The first four variables are set to "funny" values to force a complete
159 * computation of the client-requested region on the first invocation.
161 static double x_left = -MAXFLOAT;
162 static double y_top = MAXFLOAT;
163 static double x_right = MAXFLOAT;
164 static double y_bottom = -MAXFLOAT;
165 static double xstep;
166 static double ystep;
167 static double new_x_left;
168 static double new_y_top;
169 static double new_x_right;
170 static double new_y_bottom;
174 * The pixel map is a canvas_width * canvas_width array of palette indices.
176 static double canvas_width;
177 static unsigned int palette_select = 0;
178 static unsigned int color_interpolation_step = 0;
181 * Palette indices select one of 256 different colors from a palette that
182 * may be changing (psychedelic mode).
184 #define kPsychInterpSteps 4
185 static Pixel palette[256][256];
188 * Interpolate ARGB values from the starting colors.
190 static void InterpolateColors() {
191 static const Rgb base_colors[] = {
193 * Starting color values are inspired by those used by the Xaos
194 * visualizer (sadly not the same as their palette). We picked
195 * some nearby colors using a color wheel site.
197 { 0, 0, 0}, /* black */
198 { 30, 144, 255}, /* blue */
199 { 32, 13, 33}, /* dim violet */
200 {222, 85, 34}, /* red-orange */
201 { 17, 10, 6}, /* dim violet */
202 {135, 46, 71}, /* dim orange */
203 { 53, 60, 28}, /* dim greenish-yellow */
204 {216, 204, 94}, /* yellow */
205 { 34, 58, 46}, /* dim light green */
206 {220, 146, 139}, /* reddish / salmon */
207 { 24, 11, 53}, /* dark blue */
208 {133, 115, 209}, /* blue-purple */
209 { 64, 50, 37}, /* dim orange */
210 { 26, 161, 33}, /* green */
211 { 28, 21, 61}, /* dim blue */
212 { 29, 114, 53}, /* green */
213 { 39, 5, 42}, /* dim violet */
214 {163, 198, 247}, /* sky blue */
215 { 10, 51, 14}, /* dim green */
216 {211, 132, 10}, /* orange */
217 { 17, 25, 42}, /* dim blue */
218 {166, 138, 68}, /* yellow-orange */
219 { 25, 46, 10}, /* dim mint green */
220 {190, 252, 177}, /* bright mint green */
221 { 12, 6, 62}, /* dim blue */
222 { 46, 67, 32}, /* dim yellow-gren */
223 { 23, 51, 53}, /* dim sky blue */
224 {146, 78, 186}, /* violet */
225 { 11, 6, 15}, /* dim violet */
226 {227, 87, 216}, /* pink-purple */
227 { 47, 49, 28}, /* dim yellow */
228 { 19, 28, 55} /* dim blue */
231 const int colors = sizeof(base_colors) / sizeof(Rgb);
232 const int palette_entries = 256;
233 const int entries_per_color = palette_entries / colors;
234 const double scale = (double) colors / (double) palette_entries;
235 int i;
236 int j;
237 int k;
240 * First, build the colors for normal display.
242 for (i = 0; i< colors; ++i) {
243 int r = base_colors[i].r;
244 int g = base_colors[i].g;
245 int b = base_colors[i].b;
246 double rs = (double) (base_colors[(i + 1) % colors].r - r) * scale;
247 double gs = (double) (base_colors[(i + 1) % colors].g - g) * scale;
248 double bs = (double) (base_colors[(i + 1) % colors].b - b) * scale;
249 for (j = 0; j < entries_per_color; ++j) {
250 int index = i * entries_per_color + j;
251 palette[0][index].r = base_colors[i].r + (double) j * rs;
252 palette[0][index].g = base_colors[i].g + (double) j * gs;
253 palette[0][index].b = base_colors[i].b + (double) j * bs;
254 palette[0][index].a = 0;
258 * Then build the rotated palettes.
260 for (i = 1; i < palette_entries; ++i) {
261 for (j = 0; j < palette_entries; ++j) {
262 palette[i][j] = palette[0][(j + i) % palette_entries];
268 * The routine to set up the display arrays to be used by all the calls.
271 int SetupGlobals(NaClSrpcChannel *channel,
272 NaClSrpcArg **in_args,
273 NaClSrpcArg **out_args) {
274 struct work_item p;
275 p.kind = WORK_SETUP;
276 p.u.setup_canvas_width = in_args[0]->u.dval;
277 work_put(&p);
278 return NACL_SRPC_RESULT_OK;
281 void do_SetupGlobals(struct work_item *p) {
282 canvas_width = p->u.setup_canvas_width;
283 /* Initialize the mask so that all pixels recompute every time */
284 memset(mask, 0, sizeof(mask));
285 InterpolateColors();
288 NACL_SRPC_METHOD("setup:d:", SetupGlobals);
290 int ShutdownSharedMemory(NaClSrpcChannel *channel,
291 NaClSrpcArg **in_args,
292 NaClSrpcArg **out_args) {
293 struct work_item p;
294 p.kind = WORK_SHUTDOWN;
295 work_put(&p);
296 return NACL_SRPC_RESULT_OK;
299 void do_ShutdownSharedMemory(struct work_item *p) {
300 /* Free the pixmap. */
301 free(pixmap);
302 /* Return success. */
305 NACL_SRPC_METHOD("shutdown::", ShutdownSharedMemory);
308 * Compute ARGB value for one pixel.
310 static inline unsigned int mandel(double cx, double cy) {
311 double re = cx;
312 double im = cy;
313 unsigned int count = 0;
314 const double threshold = 1.0e8;
316 while (count < 255 && re * re + im * im < threshold) {
317 double new_re = re * re - im * im + cx;
318 double new_im = 2 * re * im + cy;
319 re = new_re;
320 im = new_im;
321 ++count;
324 return count;
328 * Compute an entire pixmap of palette values.
330 static void Compute(int xlo, int xhi, double xstart,
331 int ylo, int yhi, double ystart,
332 int width, char* map) {
333 double x;
334 double y;
335 int i;
336 int j;
338 /* Compute the mandelbrot set, leaving color values in pixmap. */
339 y = ystart;
340 for (i = ylo; i < yhi; ++i) {
341 x = xstart;
342 for (j = xlo; j < xhi; ++j) {
343 IND(map, width, i, j) = mandel(x, y);
344 x += xstep;
346 y += ystep;
350 static void ComputeMasked(int xlo, int xhi, double xstart,
351 int ylo, int yhi, double ystart,
352 int width, char* map) {
353 double x;
354 double y;
355 int i;
356 int j;
358 /* Compute the mandelbrot set, leaving color values in pixmap. */
359 y = ystart;
360 for (i = ylo; i < yhi; ++i) {
361 x = xstart;
362 for (j = xlo; j < xhi; ++j) {
363 if (!IND(mask, width, i, j)) {
364 IND(map, width, i, j) = mandel(x, y);
366 x += xstep;
368 y += ystep;
372 static void BuildMask(int width, char* map, char* mask) {
373 int i;
374 int j;
375 unsigned int total;
377 if (x_left == -MAXFLOAT &&
378 y_top == MAXFLOAT &&
379 x_right == MAXFLOAT &&
380 y_bottom == -MAXFLOAT) {
381 return;
383 /* Compute a nine-point stencil around the current point */
384 for (i = 1; i < width - 1; ++i) {
385 for (j = 1; j < width - 1; ++j) {
386 total = (IND(map, width, i - 1, j - 1) + IND(map, width, i - 1, j) +
387 IND(map, width, i - 1, j + 1) +
388 IND(map, width, i, j - 1) + IND(map, width, i, j + 1) +
389 IND(map, width, i + 1, j - 1) + IND(map, width, i + 1, j) +
390 IND(map, width, i + 1, j + 1)) >> 3;
391 if (total == IND(map, width, i, j)) {
392 IND(mask, width, i, j) = 1;
393 } else {
394 IND(mask, width, i, j) = 0;
401 * Move a selected region up or down, reusing already computed values.
403 static void MoveUD(int offset) {
404 int width = (int) canvas_width;
406 if (offset > 0) {
407 char* p = pixmap;
408 char* q = pixmap + offset * width;
409 size_t size = (width - offset) * width;
411 /* Move the invariant picture down by offset pixels. */
412 memmove(q, p, size);
413 /* Recompute the newly revealed top portion */
414 Compute(0, width, x_left,
415 0, offset, new_y_top,
416 (int) canvas_width, pixmap);
417 } else {
418 char* p = pixmap - offset * width;
419 char* q = pixmap;
420 size_t size = (width + offset) * width;
422 /* Move the invariant picture up by offset pixels. */
423 memmove(q, p, size);
424 /* Recompute the newly revealed bottom portion */
425 Compute(0, width, x_left,
426 width + offset, width, y_bottom,
427 (int) canvas_width, pixmap);
432 * Move a selected region left or right, reusing already computed values.
434 static void MoveLR(int offset) {
435 int i;
436 int j;
437 int width = canvas_width;
439 if (offset > 0) {
440 /* Move the invariant picture left by offset pixels. */
441 for (i = 0; i < width; ++i) {
442 for (j = 0; j < width - offset; ++j) {
443 pixmap[i * width + j] = pixmap[i * width + j + offset];
446 /* Recompute the newly revealed right portion */
447 Compute(width - offset, width, x_right,
448 0, width, y_top,
449 (int) canvas_width, pixmap);
450 } else {
451 /* Move the invariant picture right by offset pixels. */
452 for (i = 0; i < width; ++i) {
453 for (j = width - 1; j >= -offset; --j) {
454 pixmap[i * width + j] = pixmap[i * width + j + offset];
457 /* Recompute the newly revealed left portion */
458 Compute(0, -offset, new_x_left,
459 0, width, y_top,
460 (int) canvas_width, pixmap);
465 * Select the region of the X-Y plane to be viewed and compute.
467 int SetRegion(NaClSrpcChannel *channel,
468 NaClSrpcArg **in_args,
469 NaClSrpcArg **out_args) {
470 struct work_item p;
471 p.kind = WORK_SET_REGION;
472 p.u.set_region.new_x_left = in_args[0]->u.dval;
473 p.u.set_region.new_y_top = in_args[1]->u.dval;
474 p.u.set_region.new_x_right = in_args[2]->u.dval;
475 p.u.set_region.new_y_bottom = in_args[3]->u.dval;
476 work_put(&p);
477 return NACL_SRPC_RESULT_OK;
480 void do_SetRegion(struct work_item *p) {
481 new_x_left = p->u.set_region.new_x_left;
482 new_y_top = p->u.set_region.new_y_top;
483 new_x_right = p->u.set_region.new_x_right;
484 new_y_bottom = p->u.set_region.new_y_bottom;
486 double new_xstep = (new_x_right - new_x_left) / canvas_width;
487 double new_ystep = (new_y_bottom - new_y_top) / canvas_width;
489 if (new_x_left == x_left && new_x_right == x_right) {
490 if (new_y_top == y_top && new_y_bottom == y_bottom) {
491 /* No change */
492 } else if (new_ystep == ystep) {
493 /* Vertical pan */
494 /* Note that ystep is negative */
495 MoveUD(-(new_y_top - y_top) / ystep);
496 y_top = new_y_top;
497 y_bottom = new_y_bottom;
498 } else {
499 /* Vertical zoom (and possibly pan) */
500 y_top = new_y_top;
501 y_bottom = new_y_bottom;
502 ystep = new_ystep;
503 Compute(0, canvas_width, x_left,
504 0, canvas_width, y_top,
505 (int) canvas_width, pixmap);
507 } else if (new_y_top == y_top && new_y_bottom == y_bottom) {
508 if (new_x_left == x_left && new_x_right == x_right) {
509 /* No change */
510 } else if (new_xstep == xstep) {
511 /* Horizontal pan */
512 MoveLR((new_x_right - x_right) / xstep);
513 x_left = new_x_left;
514 x_right = new_x_right;
515 } else {
516 /* Horizontal zoom (and possibly pan) */
517 x_left = new_x_left;
518 x_right = new_x_right;
519 xstep = new_xstep;
520 Compute(0, canvas_width, x_left,
521 0, canvas_width, y_top,
522 (int) canvas_width, pixmap);
524 } else if (FPEQUAL((x_right - x_left) / (y_top - y_bottom),
525 (new_x_right - new_x_left) / (new_y_top - new_y_bottom)) &&
526 ((x_right - x_left) / (y_top - y_bottom) >=
527 (new_x_right - new_x_left) / (new_y_top - new_y_bottom))) {
528 /* Uniform zoom. */
529 BuildMask(canvas_width, pixmap, mask);
530 x_left = new_x_left;
531 y_top = new_y_top;
532 x_right = new_x_right;
533 y_bottom = new_y_bottom;
534 xstep = new_xstep;
535 ystep = new_ystep;
536 Compute(0, canvas_width, x_left,
537 0, canvas_width, y_top,
538 (int) canvas_width, pixmap);
539 } else {
540 /* General move. */
541 x_left = new_x_left;
542 y_top = new_y_top;
543 x_right = new_x_right;
544 y_bottom = new_y_bottom;
545 xstep = new_xstep;
546 ystep = new_ystep;
547 Compute(0, canvas_width, x_left,
548 0, canvas_width, y_top,
549 (int) canvas_width, pixmap);
553 NACL_SRPC_METHOD("set_region:dddd:", SetRegion);
556 * Display the pixmap, using the color palette with a possible shift.
558 int MandelDisplay(NaClSrpcChannel *channel,
559 NaClSrpcArg** in_args,
560 NaClSrpcArg** out_args) {
561 struct work_item p;
562 p.kind = WORK_DISPLAY;
563 work_put(&p);
564 return NACL_SRPC_RESULT_OK;
567 void do_MandelDisplay(struct work_item *wp) {
568 Pixel* p = (Pixel*) display_mem;
569 char* q = pixmap;
570 int i;
571 int j;
573 // Render the color values according to the palette.
574 for (i = 0; i < canvas_width; ++i) {
575 for (j = 0; j < canvas_width; ++j) {
576 *p = palette[palette_select][*q];
577 ++p;
578 ++q;
581 nacl_video_update(display_mem);
584 NACL_SRPC_METHOD("display::", MandelDisplay);
587 * To implement "psychedelic mode" we select one of 256 palettes
588 * (actually rotations of the same palette) by using palette_select.
589 * ShiftColors can set it to the default value (0) by passing zero
590 * or simply bump it by one when anything else is passed.
592 int ShiftColors(NaClSrpcChannel *channel,
593 NaClSrpcArg** in_args,
594 NaClSrpcArg** out_args) {
595 struct work_item p;
596 p.kind = WORK_SHIFT_COLOR; p.u.shift_color = in_args[0]->u.ival;
597 work_put(&p);
598 return NACL_SRPC_RESULT_OK;
601 void do_ShiftColors(struct work_item *p) {
602 int shift_color = p->u.shift_color;
604 if (0 == shift_color) {
605 /* Reset to the default color palette */
606 palette_select = 0;
607 } else {
608 palette_select = (palette_select + 1) % 256;
612 NACL_SRPC_METHOD("shift_colors:i:", ShiftColors);
617 * The main program.
620 extern void srpc_init();
622 int main(int argc, char* argv[]) {
623 int retval;
624 struct work_item *item;
625 /* Initialize the graphics subsystem. */
626 srpc_init();
627 retval = nacl_multimedia_init(NACL_SUBSYSTEM_VIDEO | NACL_SUBSYSTEM_EMBED);
628 if (0 != retval) {
629 return 1;
631 retval = nacl_video_init(NACL_VIDEO_FORMAT_RGBA, BASEWIDTH, BASEHEIGHT);
632 if (0 != retval) {
633 return 1;
635 /* Process requests from the UI. */
636 while (NULL != (item = work_get())) {
637 switch (item->kind) {
638 #define D(k,m) case k: m(item); break
639 D(WORK_SETUP, do_SetupGlobals);
640 D(WORK_SHUTDOWN, do_ShutdownSharedMemory);
641 D(WORK_SET_REGION, do_SetRegion);
642 D(WORK_DISPLAY, do_MandelDisplay);
643 D(WORK_SHIFT_COLOR, do_ShiftColors);
645 work_mark_done(item);
647 /* Shutdown and return. */
648 nacl_video_shutdown();
649 nacl_multimedia_shutdown();
650 return 0;