| OLD | NEW |
| (Empty) |
| 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); | |
| OLD | NEW |