Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / examples / demo / life_simd / life.c
1 /* Copyright 2014 The Chromium Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5
6 #include <assert.h>
7 #include <math.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "ppapi/c/pp_resource.h"
13 #include "ppapi/c/ppb_core.h"
14 #include "ppapi/c/ppb_fullscreen.h"
15 #include "ppapi/c/ppb_graphics_2d.h"
16 #include "ppapi/c/ppb_image_data.h"
17 #include "ppapi/c/ppb_input_event.h"
18 #include "ppapi/c/ppb_instance.h"
19 #include "ppapi/c/ppb_view.h"
20
21 #include "ppapi_simple/ps_event.h"
22 #include "ppapi_simple/ps_main.h"
23
24 PPB_Core* g_pCore;
25 PPB_Fullscreen* g_pFullscreen;
26 PPB_Graphics2D* g_pGraphics2D;
27 PPB_ImageData* g_pImageData;
28 PPB_Instance* g_pInstance;
29 PPB_View* g_pView;
30 PPB_InputEvent* g_pInputEvent;
31 PPB_KeyboardInputEvent* g_pKeyboardInput;
32 PPB_MouseInputEvent* g_pMouseInput;
33 PPB_TouchInputEvent* g_pTouchInput;
34
35 struct {
36   PP_Resource ctx;
37   struct PP_Size size;
38   int bound;
39   uint8_t* cell_in;
40   uint8_t* cell_out;
41   int32_t cell_stride;
42 } g_Context;
43
44
45 const unsigned int kInitialRandSeed = 0xC0DE533D;
46 const int kCellAlignment = 0x10;
47
48 #define INLINE inline __attribute__((always_inline))
49
50 /* BGRA helper macro, for constructing a pixel for a BGRA buffer. */
51 #define MakeBGRA(b, g, r, a)  \
52   (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
53
54 /* 128 bit vector types */
55 typedef uint8_t u8x16_t __attribute__ ((vector_size (16)));
56
57 /* Helper function to broadcast x across 16 element vector. */
58 INLINE u8x16_t broadcast(uint8_t x) {
59   u8x16_t r = {x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x};
60   return r;
61 }
62
63
64 /*
65  * Convert a count value into a live (green) or dead color value.
66  */
67 const uint32_t kNeighborColors[] = {
68     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
69     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
70     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
71     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
72     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
73     MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
74     MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
75     MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
76     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
77     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
78     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
79     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
80     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
81     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
82     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
83     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
84     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
85     MakeBGRA(0x00, 0x00, 0x00, 0xFF),
86 };
87
88 /*
89  * These represent the new health value of a cell based on its neighboring
90  * values.  The health is binary: either alive or dead.
91  */
92 const uint8_t kIsAlive[] = {
93       0, 0, 0, 0, 0, 1, 1, 1, 0,
94       0, 0, 0, 0, 0, 0, 0, 0, 0
95 };
96
97 void UpdateContext(uint32_t width, uint32_t height) {
98   int stride = (width + kCellAlignment - 1) & ~kCellAlignment;
99   if (width != g_Context.size.width || height != g_Context.size.height) {
100
101     size_t size = stride * height;
102     size_t index;
103
104     free(g_Context.cell_in);
105     free(g_Context.cell_out);
106
107     /* Create a new context */
108     void* in_buffer = NULL;
109     void* out_buffer = NULL;
110     /* alloc buffers aligned on 16 bytes */
111     posix_memalign(&in_buffer, kCellAlignment, size);
112     posix_memalign(&out_buffer, kCellAlignment, size);
113     g_Context.cell_in = (uint8_t*) in_buffer;
114     g_Context.cell_out = (uint8_t*) out_buffer;
115
116     memset(g_Context.cell_out, 0, size);
117     for (index = 0; index < size; index++) {
118       g_Context.cell_in[index] = rand() & 1;
119     }
120   }
121
122   /* Recreate the graphics context on a view change */
123   g_pCore->ReleaseResource(g_Context.ctx);
124   g_Context.size.width = width;
125   g_Context.size.height = height;
126   g_Context.cell_stride = stride;
127   g_Context.ctx =
128       g_pGraphics2D->Create(PSGetInstanceId(), &g_Context.size, PP_TRUE);
129   g_Context.bound =
130       g_pInstance->BindGraphics(PSGetInstanceId(), g_Context.ctx);
131 }
132
133 void DrawCell(int32_t x, int32_t y) {
134   int32_t width = g_Context.size.width;
135   int32_t height = g_Context.size.height;
136   int32_t stride = g_Context.cell_stride;
137
138   if (!g_Context.cell_in) return;
139
140   if (x > 0 && x < width - 1 && y > 0 && y < height - 1) {
141     g_Context.cell_in[x - 1 + y * stride] = 1;
142     g_Context.cell_in[x + 1 + y * stride] = 1;
143     g_Context.cell_in[x + (y - 1) * stride] = 1;
144     g_Context.cell_in[x + (y + 1) * stride] = 1;
145   }
146 }
147
148 void ProcessTouchEvent(PSEvent* event) {
149   uint32_t count = g_pTouchInput->GetTouchCount(event->as_resource,
150       PP_TOUCHLIST_TYPE_TOUCHES);
151   uint32_t i, j;
152   for (i = 0; i < count; i++) {
153     struct PP_TouchPoint touch = g_pTouchInput->GetTouchByIndex(
154         event->as_resource, PP_TOUCHLIST_TYPE_TOUCHES, i);
155     int radius = (int)touch.radius.x;
156     int x = (int)touch.position.x;
157     int y = (int)touch.position.y;
158     /* num = 1/100th the area of touch point */
159     int num = (int)(M_PI * radius * radius / 100.0f);
160     for (j = 0; j < num; j++) {
161       int dx = rand() % (radius * 2) - radius;
162       int dy = rand() % (radius * 2) - radius;
163       /* only plot random cells within the touch area */
164       if (dx * dx + dy * dy <= radius * radius)
165         DrawCell(x + dx, y + dy);
166     }
167   }
168 }
169
170 void ProcessEvent(PSEvent* event) {
171   switch(event->type) {
172     /* If the view updates, build a new Graphics 2D Context */
173     case PSE_INSTANCE_DIDCHANGEVIEW: {
174       struct PP_Rect rect;
175
176       g_pView->GetRect(event->as_resource, &rect);
177       UpdateContext(rect.size.width, rect.size.height);
178       break;
179     }
180
181     case PSE_INSTANCE_HANDLEINPUT: {
182       PP_InputEvent_Type type = g_pInputEvent->GetType(event->as_resource);
183       PP_InputEvent_Modifier modifiers =
184           g_pInputEvent->GetModifiers(event->as_resource);
185
186       switch(type) {
187         case PP_INPUTEVENT_TYPE_MOUSEDOWN:
188         case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
189           struct PP_Point location =
190               g_pMouseInput->GetPosition(event->as_resource);
191           /* If the button is down, draw */
192           if (modifiers & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
193             DrawCell(location.x, location.y);
194           }
195           break;
196         }
197
198         case PP_INPUTEVENT_TYPE_TOUCHSTART:
199         case PP_INPUTEVENT_TYPE_TOUCHMOVE:
200           ProcessTouchEvent(event);
201           break;
202
203         case PP_INPUTEVENT_TYPE_KEYDOWN: {
204           PP_Bool fullscreen = g_pFullscreen->IsFullscreen(PSGetInstanceId());
205           g_pFullscreen->SetFullscreen(PSGetInstanceId(),
206                                        fullscreen ? PP_FALSE : PP_TRUE);
207           break;
208         }
209
210         default:
211           break;
212       }
213       /* case PSE_INSTANCE_HANDLEINPUT */
214       break;
215     }
216
217     default:
218       break;
219   }
220 }
221
222
223 void Stir() {
224   int32_t width = g_Context.size.width;
225   int32_t height = g_Context.size.height;
226   int32_t stride = g_Context.cell_stride;
227   int32_t i;
228   if (g_Context.cell_in == NULL || g_Context.cell_out == NULL)
229     return;
230
231   for (i = 0; i < width; ++i) {
232     g_Context.cell_in[i] = rand() & 1;
233     g_Context.cell_in[i + (height - 1) * stride] = rand() & 1;
234   }
235   for (i = 0; i < height; ++i) {
236     g_Context.cell_in[i * stride] = rand() & 1;
237     g_Context.cell_in[i * stride + (width - 1)] = rand() & 1;
238   }
239 }
240
241
242 void Render() {
243   struct PP_Size* psize = &g_Context.size;
244   PP_ImageDataFormat format = PP_IMAGEDATAFORMAT_BGRA_PREMUL;
245
246   /*
247    * Create a buffer to draw into.  Since we are waiting until the next flush
248    * chrome has an opportunity to cache this buffer see ppb_graphics_2d.h.
249    */
250   PP_Resource image =
251       g_pImageData->Create(PSGetInstanceId(), format, psize, PP_FALSE);
252   uint8_t* pixels = g_pImageData->Map(image);
253
254   struct PP_ImageDataDesc desc;
255   uint8_t* cell_temp;
256   uint32_t x, y;
257
258   /* If we somehow have not allocated these pointers yet, skip this frame. */
259   if (!g_Context.cell_in || !g_Context.cell_out) return;
260
261   /* Get the pixel stride. */
262   g_pImageData->Describe(image, &desc);
263
264   /* Stir up the edges to prevent the simulation from reaching steady state. */
265   Stir();
266
267   /*
268    * Do neighbor summation; apply rules, output pixel color. Note that a 1 cell
269    * wide perimeter is excluded from the simulation update; only cells from
270    * x = 1 to x < width - 1 and y = 1 to y < height - 1 are updated.
271    */
272
273   for (y = 1; y < g_Context.size.height - 1; ++y) {
274     uint8_t *src0 = (g_Context.cell_in + (y - 1) * g_Context.cell_stride);
275     uint8_t *src1 = src0 + g_Context.cell_stride;
276     uint8_t *src2 = src1 + g_Context.cell_stride;
277     uint8_t *dst = (g_Context.cell_out + y * g_Context.cell_stride) + 1;
278     uint32_t *pixel_line =  (uint32_t*) (pixels + y * desc.stride);
279     const u8x16_t kOne = broadcast(1);
280     const u8x16_t kFour = broadcast(4);
281     const u8x16_t kEight = broadcast(8);
282     const u8x16_t kZero255 = {0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
283
284     /* Prime the src */
285     u8x16_t src00 = *(u8x16_t*)&src0[0];
286     u8x16_t src01 = *(u8x16_t*)&src0[16];
287     u8x16_t src10 = *(u8x16_t*)&src1[0];
288     u8x16_t src11 = *(u8x16_t*)&src1[16];
289     u8x16_t src20 = *(u8x16_t*)&src2[0];
290     u8x16_t src21 = *(u8x16_t*)&src2[16];
291
292     /* This inner loop is SIMD - each loop iteration will process 16 cells. */
293     for (x = 1; (x + 15) < (g_Context.size.width - 1); x += 16) {
294
295       /*
296        * Construct jittered source temps, using __builtin_shufflevector(..) to
297        * extract a shifted 16 element vector from the 32 element concatenation
298        * of two source vectors.
299        */
300       u8x16_t src0j0 = src00;
301       u8x16_t src0j1 = __builtin_shufflevector(src00, src01,
302           1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
303       u8x16_t src0j2 = __builtin_shufflevector(src00, src01,
304           2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
305       u8x16_t src1j0 = src10;
306       u8x16_t src1j1 = __builtin_shufflevector(src10, src11,
307           1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
308       u8x16_t src1j2 = __builtin_shufflevector(src10, src11,
309           2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
310       u8x16_t src2j0 = src20;
311       u8x16_t src2j1 = __builtin_shufflevector(src20, src21,
312           1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
313       u8x16_t src2j2 = __builtin_shufflevector(src20, src21,
314           2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
315
316       /* Sum the jittered sources to construct neighbor count. */
317       u8x16_t count = src0j0 + src0j1 +  src0j2 +
318                       src1j0 +        +  src1j2 +
319                       src2j0 + src2j1 +  src2j2;
320       /* Add the center cell. */
321       count = count + count + src1j1;
322       /* If count > 4 and < 8, center cell will be alive in the next frame. */
323       u8x16_t alive1 = count > kFour;
324       u8x16_t alive2 = count < kEight;
325       /* Intersect the two comparisons from above. */
326       u8x16_t alive = alive1 & alive2;
327
328       /*
329        * At this point, alive[x] will be one of two values:
330        *   0x00 for a dead cell
331        *   0xFF for an alive cell.
332        *
333        * Next, convert alive cells to green pixel color.
334        * Use __builtin_shufflevector(..) to construct output pixels from
335        * concantination of alive vector and kZero255 const vector.
336        *   Indices 0..15 select the 16 cells from alive vector.
337        *   Index 16 is zero constant from kZero255 constant vector.
338        *   Index 17 is 255 constant from kZero255 constant vector.
339        *   Output pixel color values are in BGRABGRABGRABGRA order.
340        * Since each pixel needs 4 bytes of color information, 16 cells will
341        * need to expand to 4 seperate 16 byte pixel splats.
342        */
343       u8x16_t pixel0_3 = __builtin_shufflevector(alive, kZero255,
344         16, 0, 16, 17, 16, 1, 16, 17, 16, 2, 16, 17, 16, 3, 16, 17);
345       u8x16_t pixel4_7 = __builtin_shufflevector(alive, kZero255,
346         16, 4, 16, 17, 16, 5, 16, 17, 16, 6, 16, 17, 16, 7, 16, 17);
347       u8x16_t pixel8_11 = __builtin_shufflevector(alive, kZero255,
348         16, 8, 16, 17, 16, 9, 16, 17, 16, 10, 16, 17, 16, 11, 16, 17);
349       u8x16_t pixel12_15 = __builtin_shufflevector(alive, kZero255,
350         16, 12, 16, 17, 16, 13, 16, 17, 16, 14, 16, 17, 16, 15, 16, 17);
351
352       /* Write 16 pixels to output pixel buffer. */
353       *(u8x16_t*)(pixel_line + 0) = pixel0_3;
354       *(u8x16_t*)(pixel_line + 4) = pixel4_7;
355       *(u8x16_t*)(pixel_line + 8) = pixel8_11;
356       *(u8x16_t*)(pixel_line + 12) = pixel12_15;
357
358       /* Convert alive mask to 1 or 0 and store in destination cell array. */
359       *(u8x16_t*)dst = alive & kOne;
360
361       /* Increment pointers. */
362       pixel_line += 16;
363       dst += 16;
364       src0 += 16;
365       src1 += 16;
366       src2 += 16;
367
368       /* Shift source over by 16 cells and read the next 16 cells. */
369       src00 = src01;
370       src01 = *(u8x16_t*)&src0[16];
371       src10 = src11;
372       src11 = *(u8x16_t*)&src1[16];
373       src20 = src21;
374       src21 = *(u8x16_t*)&src2[16];
375     }
376
377     /*
378      * The SIMD loop above does 16 cells at a time.  The loop below is the
379      * regular version which processes one cell at a time.  It is used to
380      * finish the remainder of the scanline not handled by the SIMD loop.
381      */
382     for (; x < (g_Context.size.width - 1); ++x) {
383       /* Sum the jittered sources to construct neighbor count. */
384       int count = src0[0] + src0[1] + src0[2] +
385                   src1[0] +         + src1[2] +
386                   src2[0] + src2[1] + src2[2];
387       /* Add the center cell. */
388       count = count + count + src1[1];
389       /* Use table lookup indexed by count to determine pixel & alive state. */
390       uint32_t color = kNeighborColors[count];
391       *pixel_line++ = color;
392       *dst++ = kIsAlive[count];
393       ++src0;
394       ++src1;
395       ++src2;
396     }
397   }
398
399   cell_temp = g_Context.cell_in;
400   g_Context.cell_in = g_Context.cell_out;
401   g_Context.cell_out = cell_temp;
402
403   /* Unmap the range, we no longer need it. */
404   g_pImageData->Unmap(image);
405
406   /* Replace the contexts, and block until it's on the screen. */
407   g_pGraphics2D->ReplaceContents(g_Context.ctx, image);
408   g_pGraphics2D->Flush(g_Context.ctx, PP_BlockUntilComplete());
409
410   /* Release the image data, we no longer need it. */
411   g_pCore->ReleaseResource(image);
412 }
413
414 /*
415  * Starting point for the module.  We do not use main since it would
416  * collide with main in libppapi_cpp.
417  */
418 int example_main(int argc, char *argv[]) {
419   fprintf(stdout,"Started main.\n");
420   g_pCore = (PPB_Core*)PSGetInterface(PPB_CORE_INTERFACE);
421   g_pFullscreen = (PPB_Fullscreen*)PSGetInterface(PPB_FULLSCREEN_INTERFACE);
422   g_pGraphics2D = (PPB_Graphics2D*)PSGetInterface(PPB_GRAPHICS_2D_INTERFACE);
423   g_pInstance = (PPB_Instance*)PSGetInterface(PPB_INSTANCE_INTERFACE);
424   g_pImageData = (PPB_ImageData*)PSGetInterface(PPB_IMAGEDATA_INTERFACE);
425   g_pView = (PPB_View*)PSGetInterface(PPB_VIEW_INTERFACE);
426
427   g_pInputEvent =
428       (PPB_InputEvent*) PSGetInterface(PPB_INPUT_EVENT_INTERFACE);
429   g_pKeyboardInput = (PPB_KeyboardInputEvent*)
430       PSGetInterface(PPB_KEYBOARD_INPUT_EVENT_INTERFACE);
431   g_pMouseInput =
432       (PPB_MouseInputEvent*) PSGetInterface(PPB_MOUSE_INPUT_EVENT_INTERFACE);
433   g_pTouchInput =
434       (PPB_TouchInputEvent*) PSGetInterface(PPB_TOUCH_INPUT_EVENT_INTERFACE);
435
436   PSEventSetFilter(PSE_ALL);
437   while (1) {
438     /* Process all waiting events without blocking */
439     PSEvent* event;
440     while ((event = PSEventTryAcquire()) != NULL) {
441       ProcessEvent(event);
442       PSEventRelease(event);
443     }
444
445     /* Render a frame, blocking until complete. */
446     if (g_Context.bound) {
447       Render();
448     }
449   }
450   return 0;
451 }
452
453 /*
454  * Register the function to call once the Instance Object is initialized.
455  * see: pappi_simple/ps_main.h
456  */
457 PPAPI_SIMPLE_REGISTER_MAIN(example_main);