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.
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"
21 #include "ppapi_simple/ps_event.h"
22 #include "ppapi_simple/ps_main.h"
25 PPB_Fullscreen* g_pFullscreen;
26 PPB_Graphics2D* g_pGraphics2D;
27 PPB_ImageData* g_pImageData;
28 PPB_Instance* g_pInstance;
30 PPB_InputEvent* g_pInputEvent;
31 PPB_KeyboardInputEvent* g_pKeyboardInput;
32 PPB_MouseInputEvent* g_pMouseInput;
33 PPB_TouchInputEvent* g_pTouchInput;
45 const unsigned int kInitialRandSeed = 0xC0DE533D;
46 const int kCellAlignment = 0x10;
48 #define INLINE inline __attribute__((always_inline))
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))
54 /* 128 bit vector types */
55 typedef uint8_t u8x16_t __attribute__ ((vector_size (16)));
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};
65 * Convert a count value into a live (green) or dead color value.
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),
89 * These represent the new health value of a cell based on its neighboring
90 * values. The health is binary: either alive or dead.
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
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) {
101 size_t size = stride * height;
104 free(g_Context.cell_in);
105 free(g_Context.cell_out);
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;
116 memset(g_Context.cell_out, 0, size);
117 for (index = 0; index < size; index++) {
118 g_Context.cell_in[index] = rand() & 1;
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;
128 g_pGraphics2D->Create(PSGetInstanceId(), &g_Context.size, PP_TRUE);
130 g_pInstance->BindGraphics(PSGetInstanceId(), g_Context.ctx);
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;
138 if (!g_Context.cell_in) return;
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;
148 void ProcessTouchEvent(PSEvent* event) {
149 uint32_t count = g_pTouchInput->GetTouchCount(event->as_resource,
150 PP_TOUCHLIST_TYPE_TOUCHES);
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);
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: {
176 g_pView->GetRect(event->as_resource, &rect);
177 UpdateContext(rect.size.width, rect.size.height);
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);
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);
198 case PP_INPUTEVENT_TYPE_TOUCHSTART:
199 case PP_INPUTEVENT_TYPE_TOUCHMOVE:
200 ProcessTouchEvent(event);
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);
213 /* case PSE_INSTANCE_HANDLEINPUT */
224 int32_t width = g_Context.size.width;
225 int32_t height = g_Context.size.height;
226 int32_t stride = g_Context.cell_stride;
228 if (g_Context.cell_in == NULL || g_Context.cell_out == NULL)
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;
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;
243 struct PP_Size* psize = &g_Context.size;
244 PP_ImageDataFormat format = PP_IMAGEDATAFORMAT_BGRA_PREMUL;
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.
251 g_pImageData->Create(PSGetInstanceId(), format, psize, PP_FALSE);
252 uint8_t* pixels = g_pImageData->Map(image);
254 struct PP_ImageDataDesc desc;
258 /* If we somehow have not allocated these pointers yet, skip this frame. */
259 if (!g_Context.cell_in || !g_Context.cell_out) return;
261 /* Get the pixel stride. */
262 g_pImageData->Describe(image, &desc);
264 /* Stir up the edges to prevent the simulation from reaching steady state. */
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.
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};
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];
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) {
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.
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);
316 /* Sum the jittered sources to construct neighbor count. */
317 u8x16_t count = src0j0 + src0j1 + src0j2 +
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;
329 * At this point, alive[x] will be one of two values:
330 * 0x00 for a dead cell
331 * 0xFF for an alive cell.
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.
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);
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;
358 /* Convert alive mask to 1 or 0 and store in destination cell array. */
359 *(u8x16_t*)dst = alive & kOne;
361 /* Increment pointers. */
368 /* Shift source over by 16 cells and read the next 16 cells. */
370 src01 = *(u8x16_t*)&src0[16];
372 src11 = *(u8x16_t*)&src1[16];
374 src21 = *(u8x16_t*)&src2[16];
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.
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];
399 cell_temp = g_Context.cell_in;
400 g_Context.cell_in = g_Context.cell_out;
401 g_Context.cell_out = cell_temp;
403 /* Unmap the range, we no longer need it. */
404 g_pImageData->Unmap(image);
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());
410 /* Release the image data, we no longer need it. */
411 g_pCore->ReleaseResource(image);
415 * Starting point for the module. We do not use main since it would
416 * collide with main in libppapi_cpp.
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);
428 (PPB_InputEvent*) PSGetInterface(PPB_INPUT_EVENT_INTERFACE);
429 g_pKeyboardInput = (PPB_KeyboardInputEvent*)
430 PSGetInterface(PPB_KEYBOARD_INPUT_EVENT_INTERFACE);
432 (PPB_MouseInputEvent*) PSGetInterface(PPB_MOUSE_INPUT_EVENT_INTERFACE);
434 (PPB_TouchInputEvent*) PSGetInterface(PPB_TOUCH_INPUT_EVENT_INTERFACE);
436 PSEventSetFilter(PSE_ALL);
438 /* Process all waiting events without blocking */
440 while ((event = PSEventTryAcquire()) != NULL) {
442 PSEventRelease(event);
445 /* Render a frame, blocking until complete. */
446 if (g_Context.bound) {
454 * Register the function to call once the Instance Object is initialized.
455 * see: pappi_simple/ps_main.h
457 PPAPI_SIMPLE_REGISTER_MAIN(example_main);