summaryrefslogtreecommitdiffstats
path: root/native_client_sdk
diff options
context:
space:
mode:
authornfullagar@chromium.org <nfullagar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-16 03:20:17 +0000
committernfullagar@chromium.org <nfullagar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-16 03:20:17 +0000
commitadd12abcdc863375db4650019b97d41ef34ad8b0 (patch)
treee8e0bed07fc1ad82977242b1fb55f2b573a07b7c /native_client_sdk
parent26b547c7a1c9d5c32a5a948fc0a38c467e02463a (diff)
downloadchromium_src-add12abcdc863375db4650019b97d41ef34ad8b0.zip
chromium_src-add12abcdc863375db4650019b97d41ef34ad8b0.tar.gz
chromium_src-add12abcdc863375db4650019b97d41ef34ad8b0.tar.bz2
Initial SIMD demos life and earth for PNaCl.
The Life SIMD demo exercises 16 element fixed point vectors to operate on 16 cells at a time. The Earth SIMD demo uses a mix of 4 element single precision floating point and a small amount of fixed point SIMD. At the moment, both demos do not exhibit much speedup from using SIMD, but this should improve as the quality of PNaCl's code generation gets better. Also, fix an issue where dragging a slider wasn't continuously updating in the regular voronoi & earth demos. R=binji@chromium.org BUG=http://code.google.com/p/nativeclient/issues/detail?id=2205 TEST=demo Review URL: https://codereview.chromium.org/289023002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@270916 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rwxr-xr-xnative_client_sdk/src/build_tools/build_projects.py2
-rw-r--r--native_client_sdk/src/build_tools/sdk_files.list2
-rw-r--r--native_client_sdk/src/examples/demo/earth/earth.cc2
-rw-r--r--native_client_sdk/src/examples/demo/earth/example.js4
-rw-r--r--native_client_sdk/src/examples/demo/earth_simd/earth.cc862
-rw-r--r--native_client_sdk/src/examples/demo/earth_simd/earth.jpgbin0 -> 194518 bytes
-rw-r--r--native_client_sdk/src/examples/demo/earth_simd/earthnight.jpgbin0 -> 351714 bytes
-rw-r--r--native_client_sdk/src/examples/demo/earth_simd/example.dsc22
-rw-r--r--native_client_sdk/src/examples/demo/earth_simd/example.js88
-rw-r--r--native_client_sdk/src/examples/demo/earth_simd/index.html69
-rw-r--r--native_client_sdk/src/examples/demo/life/life.c57
-rw-r--r--native_client_sdk/src/examples/demo/life_simd/example.dsc18
-rw-r--r--native_client_sdk/src/examples/demo/life_simd/index.html21
-rw-r--r--native_client_sdk/src/examples/demo/life_simd/life.c457
-rw-r--r--native_client_sdk/src/examples/demo/voronoi/example.js2
15 files changed, 1572 insertions, 34 deletions
diff --git a/native_client_sdk/src/build_tools/build_projects.py b/native_client_sdk/src/build_tools/build_projects.py
index fc8ad59..4e65708 100755
--- a/native_client_sdk/src/build_tools/build_projects.py
+++ b/native_client_sdk/src/build_tools/build_projects.py
@@ -338,7 +338,7 @@ def main(argv):
else:
configs = ['Debug', 'Release']
for config in configs:
- BuildProjects(pepperdir, project_tree, config=config)
+ BuildProjects(pepperdir, project_tree, config=config, deps=False)
return 0
diff --git a/native_client_sdk/src/build_tools/sdk_files.list b/native_client_sdk/src/build_tools/sdk_files.list
index b96a836..46dffbc 100644
--- a/native_client_sdk/src/build_tools/sdk_files.list
+++ b/native_client_sdk/src/build_tools/sdk_files.list
@@ -23,8 +23,10 @@ examples/button_close.png
examples/button_close_hover.png
examples/demo/drive/*
examples/demo/earth/*
+examples/demo/earth_simd/*
examples/demo/flock/*
examples/demo/life/*
+examples/demo/life_simd/*
[win]examples/demo/make.bat
examples/demo/Makefile
examples/demo/nacl_io/*
diff --git a/native_client_sdk/src/examples/demo/earth/earth.cc b/native_client_sdk/src/examples/demo/earth/earth.cc
index 420448b..1f75007 100644
--- a/native_client_sdk/src/examples/demo/earth/earth.cc
+++ b/native_client_sdk/src/examples/demo/earth/earth.cc
@@ -452,7 +452,7 @@ void Planet::wRenderPixelSpan(int x0, int x1, int y) {
float dp = planet_equator_x_ * nx +
planet_equator_y_ * ny +
planet_equator_z_ * nz;
- float w = dp / sin(ang);
+ float w = dp / sinf(ang);
if (w > 1.0f) w = 1.0f;
if (w < -1.0f) w = -1.0f;
float th = acos_.TableLerp(w) * kOneOver2PI;
diff --git a/native_client_sdk/src/examples/demo/earth/example.js b/native_client_sdk/src/examples/demo/earth/example.js
index 34f6c11..6f27c6e 100644
--- a/native_client_sdk/src/examples/demo/earth/example.js
+++ b/native_client_sdk/src/examples/demo/earth/example.js
@@ -25,13 +25,13 @@ function attachListeners() {
document.getElementById('radio'+i).addEventListener('click',
postThreadFunc(threads[i]));
}
- document.getElementById('zoomRange').addEventListener('change',
+ document.getElementById('zoomRange').addEventListener('input',
function() {
var value = parseFloat(document.getElementById('zoomRange').value);
common.naclModule.postMessage({'message' : 'set_zoom',
'value' : value});
});
- document.getElementById('lightRange').addEventListener('change',
+ document.getElementById('lightRange').addEventListener('input',
function() {
var value = parseFloat(document.getElementById('lightRange').value);
common.naclModule.postMessage({'message' : 'set_light',
diff --git a/native_client_sdk/src/examples/demo/earth_simd/earth.cc b/native_client_sdk/src/examples/demo/earth_simd/earth.cc
new file mode 100644
index 0000000..2cf29e3
--- /dev/null
+++ b/native_client_sdk/src/examples/demo/earth_simd/earth.cc
@@ -0,0 +1,862 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <assert.h>
+#include <math.h>
+#include <ppapi/c/ppb_input_event.h>
+#include <ppapi/cpp/input_event.h>
+#include <ppapi/cpp/var.h>
+#include <ppapi/cpp/var_array.h>
+#include <ppapi/cpp/var_array_buffer.h>
+#include <ppapi/cpp/var_dictionary.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+
+#include "ppapi_simple/ps.h"
+#include "ppapi_simple/ps_context_2d.h"
+#include "ppapi_simple/ps_event.h"
+#include "ppapi_simple/ps_interface.h"
+#include "ppapi_simple/ps_main.h"
+#include "sdk_util/macros.h"
+#include "sdk_util/thread_pool.h"
+
+using namespace sdk_util; // For sdk_util::ThreadPool
+
+#define INLINE inline __attribute__((always_inline))
+
+// 128 bit SIMD vector types
+typedef uint8_t u8x16_t __attribute__ ((vector_size (16)));
+typedef int32_t i32x4_t __attribute__ ((vector_size (16)));
+typedef uint32_t u32x4_t __attribute__ ((vector_size (16)));
+typedef float f32x4_t __attribute__ ((vector_size (16)));
+
+// Global properties used to setup Earth demo.
+namespace {
+const float kPI = M_PI;
+const float kTwoPI = kPI * 2.0f;
+const float kOneOverPI = 1.0f / kPI;
+const float kOneOver2PI = 1.0f / kTwoPI;
+const int kArcCosineTableSize = 4096;
+const int kFramesToBenchmark = 100;
+const float kZoomMin = 1.0f;
+const float kZoomMax = 50.0f;
+const float kWheelSpeed = 2.0f;
+const float kLightMin = 0.0f;
+const float kLightMax = 2.0f;
+
+// Timer helper for benchmarking. Returns seconds elapsed since program start,
+// as a double.
+timeval start_tv;
+int start_tv_retv = gettimeofday(&start_tv, NULL);
+
+inline double getseconds() {
+ const double usec_to_sec = 0.000001;
+ timeval tv;
+ if ((0 == start_tv_retv) && (0 == gettimeofday(&tv, NULL)))
+ return (tv.tv_sec - start_tv.tv_sec) + tv.tv_usec * usec_to_sec;
+ return 0.0;
+}
+
+// SIMD Vector helper functions.
+//
+// Note that a compare between two vectors will return a signed integer vector
+// with the same number of elements, where each element will be all bits set
+// for true (-1), and all bits clear for false (0) This integer vector can be
+// useful as a mask.
+//
+// Also note that c-style casts do not mutate the bits of a vector - only the
+// type. Boolean operators can't operate on float vectors, but it is possible
+// to cast them temporarily to integer vector, perform the mask, and cast
+// them back to float.
+//
+// To convert a float vector to an integer vector using trunction, or to
+// convert an integer vector to a float vector, use __builtin_convertvector().
+
+INLINE f32x4_t min(f32x4_t a, f32x4_t b) {
+ i32x4_t m = a < b;
+ return (f32x4_t)(((i32x4_t)a & m) | ((i32x4_t)b & ~m));
+}
+
+INLINE f32x4_t max(f32x4_t a, f32x4_t b) {
+ i32x4_t m = a > b;
+ return (f32x4_t)(((i32x4_t)a & m) | ((i32x4_t)b & ~m));
+}
+
+INLINE float dot3(f32x4_t a, f32x4_t b) {
+ f32x4_t c = a * b;
+ return c[0] + c[1] + c[2];
+}
+
+INLINE f32x4_t broadcast(float x) {
+ f32x4_t r = {x, x, x, x};
+ return r;
+}
+
+// SIMD RGBA helper functions, used for extracting color from RGBA source image.
+INLINE f32x4_t ExtractRGBA(uint32_t c) {
+ const f32x4_t kOneOver255 = broadcast(1.0f / 255.0f);
+ const i32x4_t kZero = {0, 0, 0, 0};
+ i32x4_t v = {c, c, c, c};
+ // zero extend packed color into 32x4 integer vector
+ v = (i32x4_t)__builtin_shufflevector((u8x16_t)v, (u8x16_t)kZero,
+ 0, 16, 16, 16, 1, 16, 16, 16, 2, 16, 16, 16, 3, 16, 16, 16);
+ // convert color values to float, range 0..1
+ f32x4_t f = __builtin_convertvector(v, f32x4_t) * kOneOver255;
+ return f;
+}
+
+// SIMD BGRA helper function, for constructing a pixel for a BGRA buffer.
+INLINE uint32_t PackBGRA(f32x4_t f) {
+ const f32x4_t kZero = broadcast(0.0f);
+ const f32x4_t kHalf = broadcast(0.5f);
+ const f32x4_t k255 = broadcast(255.0f);
+ f = max(f, kZero);
+ // Add 0.5 to perform rounding instead of truncation.
+ f = f * k255 + kHalf;
+ f = min(f, k255);
+ i32x4_t i = __builtin_convertvector(f, i32x4_t);
+ u32x4_t p = (u32x4_t)__builtin_shufflevector((u8x16_t)i, (u8x16_t)i,
+ 8, 4, 0, 12, 8, 4, 0, 12, 8, 4, 0, 12, 8, 4, 0, 12);
+ return p[0];
+}
+
+// BGRA helper function, for constructing a pixel for a BGRA buffer.
+INLINE uint32_t MakeBGRA(uint32_t b, uint32_t g, uint32_t r, uint32_t a) {
+ return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b));
+}
+
+// simple container for earth texture
+struct Texture {
+ int width, height;
+ uint32_t* pixels;
+ Texture(int w, int h) : width(w), height(h) {
+ pixels = new uint32_t[w * h];
+ memset(pixels, 0, sizeof(uint32_t) * w * h);
+ }
+ explicit Texture(int w, int h, uint32_t* p) : width(w), height(h) {
+ pixels = new uint32_t[w * h];
+ memcpy(pixels, p, sizeof(uint32_t) * w * h);
+ }
+ ~Texture() { delete[] pixels; }
+
+ DISALLOW_COPY_AND_ASSIGN(Texture);
+};
+
+
+
+struct ArcCosine {
+ // slightly larger table so we can interpolate beyond table size
+ float table[kArcCosineTableSize + 2];
+ float TableLerp(float x);
+ ArcCosine();
+};
+
+ArcCosine::ArcCosine() {
+ // build a slightly larger table to allow for numeric imprecision
+ for (int i = 0; i < (kArcCosineTableSize + 2); ++i) {
+ float f = static_cast<float>(i) / kArcCosineTableSize;
+ f = f * 2.0f - 1.0f;
+ table[i] = acos(f);
+ }
+}
+
+// looks up acos(f) using a table and lerping between entries
+// (it is expected that input f is between -1 and 1)
+INLINE float ArcCosine::TableLerp(float f) {
+ float x = (f + 1.0f) * 0.5f;
+ x = x * kArcCosineTableSize;
+ int ix = static_cast<int>(x);
+ float fx = static_cast<float>(ix);
+ float dx = x - fx;
+ float af = table[ix];
+ float af2 = table[ix + 1];
+ return af + (af2 - af) * dx;
+}
+
+// Helper functions for quick but approximate sqrt.
+union Convert {
+ float f;
+ int i;
+ Convert(int x) { i = x; }
+ Convert(float x) { f = x; }
+ int AsInt() { return i; }
+ float AsFloat() { return f; }
+};
+
+INLINE const int AsInteger(const float f) {
+ Convert u(f);
+ return u.AsInt();
+}
+
+INLINE const float AsFloat(const int i) {
+ Convert u(i);
+ return u.AsFloat();
+}
+
+const long int kOneAsInteger = AsInteger(1.0f);
+
+INLINE float inline_quick_sqrt(float x) {
+ int i;
+ i = (AsInteger(x) >> 1) + (kOneAsInteger >> 1);
+ return AsFloat(i);
+}
+
+INLINE float inline_sqrt(float x) {
+ float y;
+ y = inline_quick_sqrt(x);
+ y = (y * y + x) / (2.0f * y);
+ y = (y * y + x) / (2.0f * y);
+ return y;
+}
+
+} // namespace
+
+
+// The main object that runs the Earth demo.
+class Planet {
+ public:
+ Planet();
+ virtual ~Planet();
+ // Runs a tick of the simulations, update 2D output.
+ void Update();
+ // Handle event from user, or message from JS.
+ void HandleEvent(PSEvent* ps_event);
+
+ private:
+ // Methods prefixed with 'w' are run on worker threads.
+ uint32_t* wGetAddr(int x, int y);
+ void wRenderPixelSpan(int x0, int x1, int y);
+ void wMakeRect(int r, int *x, int *y, int *w, int *h);
+ void wRenderRect(int x0, int y0, int x1, int y1);
+ void wRenderRegion(int region);
+ static void wRenderRegionEntry(int region, void *thiz);
+
+ // These methods are only called by the main thread.
+ void CacheCalcs();
+ void SetPlanetXYZR(float x, float y, float z, float r);
+ void SetPlanetPole(float x, float y, float z);
+ void SetPlanetEquator(float x, float y, float z);
+ void SetPlanetSpin(float x, float y);
+ void SetEyeXYZ(float x, float y, float z);
+ void SetLightXYZ(float x, float y, float z);
+ void SetAmbientRGB(float r, float g, float b);
+ void SetDiffuseRGB(float r, float g, float b);
+ void SetZoom(float zoom);
+ void SetLight(float zoom);
+ void SetTexture(const std::string& name, int width, int height,
+ uint32_t* pixels);
+ void SpinPlanet(pp::Point new_point, pp::Point last_point);
+
+ void Reset();
+ void RequestTextures();
+ void UpdateSim();
+ void Render();
+ void Draw();
+ void StartBenchmark();
+ void EndBenchmark();
+ // Post a small key-value message to update JS.
+ void PostUpdateMessage(const char* message_name, double value);
+
+ // User Interface settings. These settings are controlled via html
+ // controls or via user input.
+ float ui_light_;
+ float ui_zoom_;
+ float ui_spin_x_;
+ float ui_spin_y_;
+ pp::Point ui_last_point_;
+
+ // Various settings for position & orientation of planet. Do not change
+ // these variables, instead use SetPlanet*() functions.
+ float planet_radius_;
+ float planet_spin_x_;
+ float planet_spin_y_;
+ float planet_x_, planet_y_, planet_z_;
+ float planet_pole_x_, planet_pole_y_, planet_pole_z_;
+ float planet_equator_x_, planet_equator_y_, planet_equator_z_;
+
+ // Observer's eye. Do not change these variables, instead use SetEyeXYZ().
+ float eye_x_, eye_y_, eye_z_;
+
+ // Light position, ambient and diffuse settings. Do not change these
+ // variables, instead use SetLightXYZ(), SetAmbientRGB() and SetDiffuseRGB().
+ float light_x_, light_y_, light_z_;
+ float diffuse_r_, diffuse_g_, diffuse_b_;
+ float ambient_r_, ambient_g_, ambient_b_;
+
+ // Cached calculations. Do not change these variables - they are updated by
+ // CacheCalcs() function.
+ float planet_xyz_;
+ float planet_pole_x_equator_x_;
+ float planet_pole_x_equator_y_;
+ float planet_pole_x_equator_z_;
+ float planet_radius2_;
+ float planet_one_over_radius_;
+ float eye_xyz_;
+
+ // Source texture (earth map).
+ Texture* base_tex_;
+ Texture* night_tex_;
+ int width_for_tex_;
+ int height_for_tex_;
+
+ // Quick ArcCos helper.
+ ArcCosine acos_;
+
+ // Misc.
+ PSContext2D_t* ps_context_;
+ int num_threads_;
+ ThreadPool* workers_;
+ bool benchmarking_;
+ int benchmark_frame_counter_;
+ double benchmark_start_time_;
+ double benchmark_end_time_;
+};
+
+
+void Planet::RequestTextures() {
+ // Request a set of images from JS. After images are loaded by JS, a
+ // message from JS -> NaCl will arrive containing the pixel data. See
+ // HandleMessage() method in this file.
+ pp::VarDictionary message;
+ message.Set("message", "request_textures");
+ pp::VarArray names;
+ names.Set(0, "earth.jpg");
+ names.Set(1, "earthnight.jpg");
+ message.Set("names", names);
+ PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
+}
+
+void Planet::Reset() {
+ // Reset has to first fill in all variables with valid floats, so
+ // CacheCalcs() doesn't potentially propagate NaNs when calling Set*()
+ // functions further below.
+ planet_radius_ = 1.0f;
+ planet_spin_x_ = 0.0f;
+ planet_spin_y_ = 0.0f;
+ planet_x_ = 0.0f;
+ planet_y_ = 0.0f;
+ planet_z_ = 0.0f;
+ planet_pole_x_ = 0.0f;
+ planet_pole_y_ = 0.0f;
+ planet_pole_z_ = 0.0f;
+ planet_equator_x_ = 0.0f;
+ planet_equator_y_ = 0.0f;
+ planet_equator_z_ = 0.0f;
+ eye_x_ = 0.0f;
+ eye_y_ = 0.0f;
+ eye_z_ = 0.0f;
+ light_x_ = 0.0f;
+ light_y_ = 0.0f;
+ light_z_ = 0.0f;
+ diffuse_r_ = 0.0f;
+ diffuse_g_ = 0.0f;
+ diffuse_b_ = 0.0f;
+ ambient_r_ = 0.0f;
+ ambient_g_ = 0.0f;
+ ambient_b_ = 0.0f;
+ planet_xyz_ = 0.0f;
+ planet_pole_x_equator_x_ = 0.0f;
+ planet_pole_x_equator_y_ = 0.0f;
+ planet_pole_x_equator_z_ = 0.0f;
+ planet_radius2_ = 0.0f;
+ planet_one_over_radius_ = 0.0f;
+ eye_xyz_ = 0.0f;
+ ui_zoom_ = 14.0f;
+ ui_light_ = 1.0f;
+ ui_spin_x_ = 0.01f;
+ ui_spin_y_ = 0.0f;
+ ui_last_point_ = pp::Point(0, 0);
+
+ // Set up reasonable default values.
+ SetPlanetXYZR(0.0f, 0.0f, 48.0f, 4.0f);
+ SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
+ SetLightXYZ(-60.0f, -30.0f, 0.0f);
+ SetAmbientRGB(0.05f, 0.05f, 0.05f);
+ SetDiffuseRGB(0.8f, 0.8f, 0.8f);
+ SetPlanetPole(0.0f, 1.0f, 0.0f);
+ SetPlanetEquator(1.0f, 0.0f, 0.0f);
+ SetPlanetSpin(kPI / 2.0f, kPI / 2.0f);
+ SetZoom(ui_zoom_);
+ SetLight(ui_light_);
+
+ // Send UI values to JS to reset html sliders.
+ PostUpdateMessage("set_zoom", ui_zoom_);
+ PostUpdateMessage("set_light", ui_light_);
+}
+
+
+Planet::Planet() : base_tex_(NULL), night_tex_(NULL), num_threads_(0),
+ benchmarking_(false), benchmark_frame_counter_(0) {
+
+ Reset();
+ RequestTextures();
+ // By default, render from the dispatch thread.
+ workers_ = new ThreadPool(num_threads_);
+ PSEventSetFilter(PSE_ALL);
+ ps_context_ = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL);
+}
+
+Planet::~Planet() {
+ delete workers_;
+ PSContext2DFree(ps_context_);
+}
+
+// Given a region r, derive a rectangle.
+// This rectangle shouldn't overlap with work being done by other workers.
+// If multithreading, this function is only called by the worker threads.
+void Planet::wMakeRect(int r, int *x, int *y, int *w, int *h) {
+ *x = 0;
+ *w = ps_context_->width;
+ *y = r;
+ *h = 1;
+}
+
+
+inline uint32_t* Planet::wGetAddr(int x, int y) {
+ return ps_context_->data + x + y * ps_context_->stride / sizeof(uint32_t);
+}
+
+// This is the inner loop of the ray tracer. Given a pixel span (x0, x1) on
+// scanline y, shoot rays into the scene and render what they hit. Use
+// scanline coherence to do a few optimizations.
+// This version uses portable SIMD 4 element single precision floating point
+// vectors to perform many of the calculations, and builds only on PNaCl.
+void Planet::wRenderPixelSpan(int x0, int x1, int y) {
+ if (!base_tex_ || !night_tex_)
+ return;
+ const uint32_t kColorBlack = MakeBGRA(0, 0, 0, 0xFF);
+ const uint32_t kSolidAlpha = MakeBGRA(0, 0, 0, 0xFF);
+ const f32x4_t kOne = {1.0f, 1.0f, 1.0f, 1.0f};
+ const f32x4_t diffuse = {diffuse_r_, diffuse_g_, diffuse_b_, 0.0f};
+ const f32x4_t ambient = {ambient_r_, ambient_g_, ambient_b_, 0.0f};
+ const f32x4_t light_pos = {light_x_, light_y_, light_z_, 1.0f};
+ const f32x4_t planet_pos = {planet_x_, planet_y_, planet_z_, 1.0f};
+ const f32x4_t planet_one_over_radius = broadcast(planet_one_over_radius_);
+ const f32x4_t planet_equator = {
+ planet_equator_x_, planet_equator_y_, planet_equator_z_, 0.0f};
+ const f32x4_t planet_pole = {
+ planet_pole_x_, planet_pole_y_, planet_pole_z_, 1.0f};
+ const f32x4_t planet_pole_x_equator = {
+ planet_pole_x_equator_x_, planet_pole_x_equator_y_,
+ planet_pole_x_equator_z_, 0.0f};
+
+ float width = ps_context_->width;
+ float height = ps_context_->height;
+ float min_dim = width < height ? width : height;
+ float offset_x = width < height ? 0 : (width - min_dim) * 0.5f;
+ float offset_y = width < height ? (height - min_dim) * 0.5f : 0;
+ float y0 = eye_y_;
+ float z0 = eye_z_;
+ float y1 = (static_cast<float>(y - offset_y) / min_dim) * 2.0f - 1.0f;
+ float z1 = 0.0f;
+ float dy = (y1 - y0);
+ float dz = (z1 - z0);
+ float dy_dy_dz_dz = dy * dy + dz * dz;
+ float two_dy_y0_y_two_dz_z0_z = 2.0f * dy * (y0 - planet_y_) +
+ 2.0f * dz * (z0 - planet_z_);
+ float planet_xyz_eye_xyz = planet_xyz_ + eye_xyz_;
+ float y_y0_z_z0 = planet_y_ * y0 + planet_z_ * z0;
+ float oowidth = 1.0f / min_dim;
+ uint32_t* pixels = this->wGetAddr(x0, y);
+ for (int x = x0; x <= x1; ++x) {
+ // scan normalized screen -1..1
+ float x1 = (static_cast<float>(x - offset_x) * oowidth) * 2.0f - 1.0f;
+ // eye
+ float x0 = eye_x_;
+ // delta from screen to eye
+ float dx = (x1 - x0);
+ // build a, b, c
+ float a = dx * dx + dy_dy_dz_dz;
+ float b = 2.0f * dx * (x0 - planet_x_) + two_dy_y0_y_two_dz_z0_z;
+ float c = planet_xyz_eye_xyz +
+ -2.0f * (planet_x_ * x0 + y_y0_z_z0) - (planet_radius2_);
+ // calculate discriminant
+ float disc = b * b - 4.0f * a * c;
+
+ // Did ray hit the sphere?
+ if (disc < 0.0f) {
+ *pixels = kColorBlack;
+ ++pixels;
+ continue;
+ }
+
+ f32x4_t delta = {dx, dy, dz, 1.0f};
+ f32x4_t base = {x0, y0, z0, 1.0f};
+
+ // Calc parametric t value.
+ float t = (-b - inline_sqrt(disc)) / (2.0f * a);
+
+ f32x4_t pos = base + broadcast(t) * delta;
+ f32x4_t normal = (pos - planet_pos) * planet_one_over_radius;
+
+ // Misc raytrace calculations.
+ f32x4_t L = light_pos - pos;
+ float Lq = 1.0f / inline_quick_sqrt(dot3(L, L));
+ L = L * broadcast(Lq);
+ float d = dot3(L, normal);
+ f32x4_t p = diffuse * broadcast(d) + ambient;
+ float ds = -dot3(normal, planet_pole);
+ float ang = acos_.TableLerp(ds);
+ float v = ang * kOneOverPI;
+ float dp = dot3(planet_equator, normal);
+ float w = dp / sinf(ang);
+ if (w > 1.0f) w = 1.0f;
+ if (w < -1.0f) w = -1.0f;
+ float th = acos_.TableLerp(w) * kOneOver2PI;
+ float dps = dot3(planet_pole_x_equator, normal);
+ float u;
+ if (dps < 0.0f)
+ u = th;
+ else
+ u = 1.0f - th;
+
+ // Look up daylight texel.
+ int tx = static_cast<int>(u * base_tex_->width);
+ int ty = static_cast<int>(v * base_tex_->height);
+ int offset = tx + ty * base_tex_->width;
+ uint32_t base_texel = base_tex_->pixels[offset];
+ f32x4_t dc = ExtractRGBA(base_texel);
+
+ // Look up night texel.
+ int nix = static_cast<int>(u * night_tex_->width);
+ int niy = static_cast<int>(v * night_tex_->height);
+ int noffset = nix + niy * night_tex_->width;
+ uint32_t night_texel = night_tex_->pixels[noffset];
+ f32x4_t nc = ExtractRGBA(night_texel);
+
+ // Blend between daylight (dc) and nighttime (nc) color.
+ f32x4_t pc = min(p, kOne);
+ f32x4_t fc = dc * p + nc * (kOne - pc);
+ uint32_t color = PackBGRA(fc);
+
+ *pixels = color | kSolidAlpha;
+ ++pixels;
+ }
+}
+
+// Renders a rectangular area of the screen, scan line at a time
+void Planet::wRenderRect(int x, int y, int w, int h) {
+ for (int j = y; j < (y + h); ++j) {
+ this->wRenderPixelSpan(x, x + w - 1, j);
+ }
+}
+
+// If multithreading, this function is only called by the worker threads.
+void Planet::wRenderRegion(int region) {
+ // convert region # into x0, y0, x1, y1 rectangle
+ int x, y, w, h;
+ wMakeRect(region, &x, &y, &w, &h);
+ // render this rectangle
+ wRenderRect(x, y, w, h);
+}
+
+// Entry point for worker thread. Can't pass a member function around, so we
+// have to do this little round-about.
+void Planet::wRenderRegionEntry(int region, void* thiz) {
+ static_cast<Planet*>(thiz)->wRenderRegion(region);
+}
+
+// Renders the planet, dispatching the work to multiple threads.
+void Planet::Render() {
+ workers_->Dispatch(ps_context_->height, wRenderRegionEntry, this);
+}
+
+// Pre-calculations to make inner loops faster.
+void Planet::CacheCalcs() {
+ planet_xyz_ = planet_x_ * planet_x_ +
+ planet_y_ * planet_y_ +
+ planet_z_ * planet_z_;
+ planet_radius2_ = planet_radius_ * planet_radius_;
+ planet_one_over_radius_ = 1.0f / planet_radius_;
+ eye_xyz_ = eye_x_ * eye_x_ + eye_y_ * eye_y_ + eye_z_ * eye_z_;
+ // spin vector from center->equator
+ planet_equator_x_ = cos(planet_spin_x_);
+ planet_equator_y_ = 0.0f;
+ planet_equator_z_ = sin(planet_spin_x_);
+
+ // cache cross product of pole & equator
+ planet_pole_x_equator_x_ = planet_pole_y_ * planet_equator_z_ -
+ planet_pole_z_ * planet_equator_y_;
+ planet_pole_x_equator_y_ = planet_pole_z_ * planet_equator_x_ -
+ planet_pole_x_ * planet_equator_z_;
+ planet_pole_x_equator_z_ = planet_pole_x_ * planet_equator_y_ -
+ planet_pole_y_ * planet_equator_x_;
+}
+
+void Planet::SetPlanetXYZR(float x, float y, float z, float r) {
+ planet_x_ = x;
+ planet_y_ = y;
+ planet_z_ = z;
+ planet_radius_ = r;
+ CacheCalcs();
+}
+
+void Planet::SetEyeXYZ(float x, float y, float z) {
+ eye_x_ = x;
+ eye_y_ = y;
+ eye_z_ = z;
+ CacheCalcs();
+}
+
+void Planet::SetLightXYZ(float x, float y, float z) {
+ light_x_ = x;
+ light_y_ = y;
+ light_z_ = z;
+ CacheCalcs();
+}
+
+void Planet::SetAmbientRGB(float r, float g, float b) {
+ ambient_r_ = r;
+ ambient_g_ = g;
+ ambient_b_ = b;
+ CacheCalcs();
+}
+
+void Planet::SetDiffuseRGB(float r, float g, float b) {
+ diffuse_r_ = r;
+ diffuse_g_ = g;
+ diffuse_b_ = b;
+ CacheCalcs();
+}
+
+void Planet::SetPlanetPole(float x, float y, float z) {
+ planet_pole_x_ = x;
+ planet_pole_y_ = y;
+ planet_pole_z_ = z;
+ CacheCalcs();
+}
+
+void Planet::SetPlanetEquator(float x, float y, float z) {
+ // This is really over-ridden by spin at the momenent.
+ planet_equator_x_ = x;
+ planet_equator_y_ = y;
+ planet_equator_z_ = z;
+ CacheCalcs();
+}
+
+void Planet::SetPlanetSpin(float x, float y) {
+ planet_spin_x_ = x;
+ planet_spin_y_ = y;
+ CacheCalcs();
+}
+
+// Run a simple sim to spin the planet. Update loop is run once per frame.
+// Called from the main thread only and only when the worker threads are idle.
+void Planet::UpdateSim() {
+ float x = planet_spin_x_ + ui_spin_x_;
+ float y = planet_spin_y_ + ui_spin_y_;
+ // keep in nice range
+ if (x > (kPI * 2.0f))
+ x = x - kPI * 2.0f;
+ else if (x < (-kPI * 2.0f))
+ x = x + kPI * 2.0f;
+ if (y > (kPI * 2.0f))
+ y = y - kPI * 2.0f;
+ else if (y < (-kPI * 2.0f))
+ y = y + kPI * 2.0f;
+ SetPlanetSpin(x, y);
+}
+
+void Planet::StartBenchmark() {
+ // For more consistent benchmark numbers, reset to default state.
+ Reset();
+ printf("Benchmark started...\n");
+ benchmark_frame_counter_ = kFramesToBenchmark;
+ benchmarking_ = true;
+ benchmark_start_time_ = getseconds();
+}
+
+void Planet::EndBenchmark() {
+ benchmark_end_time_ = getseconds();
+ printf("Benchmark ended... time: %2.5f\n",
+ benchmark_end_time_ - benchmark_start_time_);
+ benchmarking_ = false;
+ benchmark_frame_counter_ = 0;
+ double total_time = benchmark_end_time_ - benchmark_start_time_;
+ // Send benchmark result to JS.
+ PostUpdateMessage("benchmark_result", total_time);
+}
+
+void Planet::SetZoom(float zoom) {
+ ui_zoom_ = std::min(kZoomMax, std::max(kZoomMin, zoom));
+ SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
+}
+
+void Planet::SetLight(float light) {
+ ui_light_ = std::min(kLightMax, std::max(kLightMin, light));
+ SetDiffuseRGB(0.8f * ui_light_, 0.8f * ui_light_, 0.8f * ui_light_);
+ SetAmbientRGB(0.4f * ui_light_, 0.4f * ui_light_, 0.4f * ui_light_);
+}
+
+void Planet::SetTexture(const std::string& name, int width, int height,
+ uint32_t* pixels) {
+ if (pixels) {
+ if (name == "earth.jpg") {
+ delete base_tex_;
+ base_tex_ = new Texture(width, height, pixels);
+ } else if (name == "earthnight.jpg") {
+ delete night_tex_;
+ night_tex_ = new Texture(width, height, pixels);
+ }
+ }
+}
+
+void Planet::SpinPlanet(pp::Point new_point, pp::Point last_point) {
+ float delta_x = static_cast<float>(new_point.x() - last_point.x());
+ float delta_y = static_cast<float>(new_point.y() - last_point.y());
+ float spin_x = std::min(10.0f, std::max(-10.0f, delta_x * 0.5f));
+ float spin_y = std::min(10.0f, std::max(-10.0f, delta_y * 0.5f));
+ ui_spin_x_ = spin_x / 100.0f;
+ ui_spin_y_ = spin_y / 100.0f;
+ ui_last_point_ = new_point;
+}
+
+// Handle input events from the user and messages from JS.
+void Planet::HandleEvent(PSEvent* ps_event) {
+ // Give the 2D context a chance to process the event.
+ if (0 != PSContext2DHandleEvent(ps_context_, ps_event))
+ return;
+ if (ps_event->type == PSE_INSTANCE_HANDLEINPUT) {
+ // Convert Pepper Simple event to a PPAPI C++ event
+ pp::InputEvent event(ps_event->as_resource);
+ switch (event.GetType()) {
+ case PP_INPUTEVENT_TYPE_KEYDOWN: {
+ pp::KeyboardInputEvent key(event);
+ uint32_t key_code = key.GetKeyCode();
+ if (key_code == 84) // 't' key
+ if (!benchmarking_)
+ StartBenchmark();
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN:
+ case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
+ pp::MouseInputEvent mouse = pp::MouseInputEvent(event);
+ if (mouse.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
+ if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN)
+ SpinPlanet(mouse.GetPosition(), mouse.GetPosition());
+ else
+ SpinPlanet(mouse.GetPosition(), ui_last_point_);
+ }
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_WHEEL: {
+ pp::WheelInputEvent wheel = pp::WheelInputEvent(event);
+ PP_FloatPoint ticks = wheel.GetTicks();
+ SetZoom(ui_zoom_ + (ticks.x + ticks.y) * kWheelSpeed);
+ // Update html slider by sending update message to JS.
+ PostUpdateMessage("set_zoom", ui_zoom_);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_TOUCHSTART:
+ case PP_INPUTEVENT_TYPE_TOUCHMOVE: {
+ pp::TouchInputEvent touches = pp::TouchInputEvent(event);
+ uint32_t count = touches.GetTouchCount(PP_TOUCHLIST_TYPE_TOUCHES);
+ if (count > 0) {
+ // Use first touch point to spin planet.
+ pp::TouchPoint touch =
+ touches.GetTouchByIndex(PP_TOUCHLIST_TYPE_TOUCHES, 0);
+ pp::Point screen_point(touch.position().x(),
+ touch.position().y());
+ if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART)
+ SpinPlanet(screen_point, screen_point);
+ else
+ SpinPlanet(screen_point, ui_last_point_);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ } else if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) {
+ // Convert Pepper Simple message to PPAPI C++ vars
+ pp::Var var(ps_event->as_var);
+ if (var.is_dictionary()) {
+ pp::VarDictionary dictionary(var);
+ std::string message = dictionary.Get("message").AsString();
+ if (message == "run benchmark" && !benchmarking_) {
+ StartBenchmark();
+ } else if (message == "set_light") {
+ SetLight(static_cast<float>(dictionary.Get("value").AsDouble()));
+ } else if (message == "set_zoom") {
+ SetZoom(static_cast<float>(dictionary.Get("value").AsDouble()));
+ } else if (message == "set_threads") {
+ int threads = dictionary.Get("value").AsInt();
+ delete workers_;
+ workers_ = new ThreadPool(threads);
+ } else if (message == "texture") {
+ std::string name = dictionary.Get("name").AsString();
+ int width = dictionary.Get("width").AsInt();
+ int height = dictionary.Get("height").AsInt();
+ pp::VarArrayBuffer array_buffer(dictionary.Get("data"));
+ if (!name.empty() && !array_buffer.is_null()) {
+ if (width > 0 && height > 0) {
+ uint32_t* pixels = static_cast<uint32_t*>(array_buffer.Map());
+ SetTexture(name, width, height, pixels);
+ array_buffer.Unmap();
+ }
+ }
+ }
+ } else {
+ printf("Handle message unknown type: %s\n", var.DebugString().c_str());
+ }
+ }
+}
+
+// PostUpdateMessage() helper function for sending small messages to JS.
+void Planet::PostUpdateMessage(const char* message_name, double value) {
+ pp::VarDictionary message;
+ message.Set("message", message_name);
+ message.Set("value", value);
+ PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
+}
+
+void Planet::Update() {
+ // When benchmarking is running, don't update display via
+ // PSContext2DSwapBuffer() - vsync is enabled by default, and will throttle
+ // the benchmark results.
+ PSContext2DGetBuffer(ps_context_);
+ if (NULL == ps_context_->data)
+ return;
+
+ do {
+ UpdateSim();
+ Render();
+ if (!benchmarking_) break;
+ --benchmark_frame_counter_;
+ } while (benchmark_frame_counter_ > 0);
+ if (benchmarking_)
+ EndBenchmark();
+
+ PSContext2DSwapBuffer(ps_context_);
+}
+
+
+// Starting point for the module. We do not use main since it would
+// collide with main in libppapi_cpp.
+int example_main(int argc, char* argv[]) {
+ Planet earth;
+ while (true) {
+ PSEvent* ps_event;
+ // Consume all available events
+ while ((ps_event = PSEventTryAcquire()) != NULL) {
+ earth.HandleEvent(ps_event);
+ PSEventRelease(ps_event);
+ }
+ // Do simulation, render and present.
+ earth.Update();
+ }
+
+ return 0;
+}
+
+// Register the function to call once the Instance Object is initialized.
+// see: pappi_simple/ps_main.h
+PPAPI_SIMPLE_REGISTER_MAIN(example_main);
diff --git a/native_client_sdk/src/examples/demo/earth_simd/earth.jpg b/native_client_sdk/src/examples/demo/earth_simd/earth.jpg
new file mode 100644
index 0000000..fbfb6bd
--- /dev/null
+++ b/native_client_sdk/src/examples/demo/earth_simd/earth.jpg
Binary files differ
diff --git a/native_client_sdk/src/examples/demo/earth_simd/earthnight.jpg b/native_client_sdk/src/examples/demo/earth_simd/earthnight.jpg
new file mode 100644
index 0000000..fa721ae
--- /dev/null
+++ b/native_client_sdk/src/examples/demo/earth_simd/earthnight.jpg
Binary files differ
diff --git a/native_client_sdk/src/examples/demo/earth_simd/example.dsc b/native_client_sdk/src/examples/demo/earth_simd/example.dsc
new file mode 100644
index 0000000..b6ab87a
--- /dev/null
+++ b/native_client_sdk/src/examples/demo/earth_simd/example.dsc
@@ -0,0 +1,22 @@
+{
+ 'TOOLS': ['pnacl'],
+ 'TARGETS': [
+ {
+ 'NAME' : 'earth_simd',
+ 'TYPE' : 'main',
+ 'SOURCES' : [
+ 'earth.cc'
+ ],
+ 'LIBS': ['ppapi_simple', 'nacl_io', 'sdk_util', 'ppapi_cpp', 'ppapi', 'pthread']
+ }
+ ],
+ 'DATA': [
+ 'earth.jpg',
+ 'earthnight.jpg',
+ 'example.js',
+ ],
+ 'DEST': 'examples/demo',
+ 'NAME': 'earth_simd',
+ 'TITLE': 'Multi-Threaded SIMD Earth Demo for PNaCl',
+ 'GROUP': 'Demo'
+}
diff --git a/native_client_sdk/src/examples/demo/earth_simd/example.js b/native_client_sdk/src/examples/demo/earth_simd/example.js
new file mode 100644
index 0000000..a4ea35e
--- /dev/null
+++ b/native_client_sdk/src/examples/demo/earth_simd/example.js
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function moduleDidLoad() {
+}
+
+function postThreadFunc(numThreads) {
+ return function () {
+ common.naclModule.postMessage({'message' : 'set_threads',
+ 'value' : numThreads});
+ }
+}
+
+// Add event listeners after the NaCl module has loaded. These listeners will
+// forward messages to the NaCl module via postMessage()
+function attachListeners() {
+ document.getElementById('benchmark').addEventListener('click',
+ function() {
+ common.naclModule.postMessage({'message' : 'run benchmark'});
+ common.updateStatus('BENCHMARKING... (please wait)');
+ });
+ var threads = [0, 1, 2, 4, 6, 8, 12, 16, 24, 32];
+ for (var i = 0; i < threads.length; i++) {
+ document.getElementById('radio'+i).addEventListener('click',
+ postThreadFunc(threads[i]));
+ }
+ document.getElementById('zoomRange').addEventListener('input',
+ function() {
+ var value = parseFloat(document.getElementById('zoomRange').value);
+ common.naclModule.postMessage({'message' : 'set_zoom',
+ 'value' : value});
+ });
+ document.getElementById('lightRange').addEventListener('input',
+ function() {
+ var value = parseFloat(document.getElementById('lightRange').value);
+ common.naclModule.postMessage({'message' : 'set_light',
+ 'value' : value});
+ });
+}
+
+// Load a texture and send pixel data down to NaCl module.
+function loadTexture(name) {
+ // Load image from jpg, decompress into canvas.
+ var img = new Image();
+ img.onload = function() {
+ var graph = document.createElement('canvas');
+ graph.width = img.width;
+ graph.height = img.height;
+ var context = graph.getContext('2d');
+ context.drawImage(img, 0, 0);
+ var imageData = context.getImageData(0, 0, img.width, img.height);
+ // Send NaCl module the raw image data obtained from canvas.
+ common.naclModule.postMessage({'message' : 'texture',
+ 'name' : name,
+ 'width' : img.width,
+ 'height' : img.height,
+ 'data' : imageData.data.buffer});
+ }
+ img.src = name;
+}
+
+// Handle a message coming from the NaCl module.
+function handleMessage(message_event) {
+ if (message_event.data['message'] == 'benchmark_result') {
+ // benchmark result
+ var result = message_event.data['value'];
+ console.log('Benchmark result:' + result);
+ result = (Math.round(result * 1000) / 1000).toFixed(3);
+ document.getElementById('result').textContent =
+ 'Result: ' + result + ' seconds';
+ common.updateStatus('SUCCESS');
+ } else if (message_event.data['message'] == 'set_zoom') {
+ // zoom slider
+ var zoom = message_event.data['value'];
+ document.getElementById('zoomRange').value = zoom;
+ } else if (message_event.data['message'] == 'set_light') {
+ // light slider
+ var light = message_event.data['value'];
+ document.getElementById('lightRange').value = light;
+ } else if (message_event.data['message'] == 'request_textures') {
+ // NaCl module is requesting a set of textures.
+ var names = message_event.data['names'];
+ for (var i = 0; i < names.length; i++)
+ loadTexture(names[i]);
+ }
+}
+
diff --git a/native_client_sdk/src/examples/demo/earth_simd/index.html b/native_client_sdk/src/examples/demo/earth_simd/index.html
new file mode 100644
index 0000000..042da94
--- /dev/null
+++ b/native_client_sdk/src/examples/demo/earth_simd/index.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2014 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<head>
+ <meta http-equiv="Pragma" content="no-cache">
+ <meta http-equiv="Expires" content="-1">
+ <meta charset="UTF-8">
+ <title>{{title}}</title>
+ <script type="text/javascript" src="common.js"></script>
+ <script type="text/javascript" src="example.js"></script>
+</head>
+<body {{attrs}} data-width="640" data-height="640">
+ <h1>{{title}}</h1>
+ <h2>Status: <code id="statusField">NO-STATUS</code></h2>
+ <div>
+ This PNaCl demo uses portable SIMD and renders a rotating globe using the
+ Graphics2D interface.
+ Image Credit:
+ NASA Goddard Space Flight Center Image by Reto Stöckli (land surface,
+ shallow water, clouds). Enhancements by Robert Simmon (ocean color,
+ compositing, 3D globes, animation).
+ Data and technical support: MODIS Land Group; MODIS Science Data,
+ Support Team; MODIS Atmosphere Group; MODIS Ocean Group Additional data:
+ USGS EROS Data Center (topography); USGS Terrestrial Remote Sensing
+ Flagstaff Field Center (Antarctica); Defense Meteorological
+ Satellite Program (city lights).
+ <br>
+ Zoom:
+ <input type="range" id="zoomRange"
+ min="1.0" max="50.0" step="0.5" value="14.0" />
+ Light:
+ <input type="range" id="lightRange"
+ min="0.2" max="2.0" step=".01" value="1.0" />
+ <br>
+ Number of threads (0 is main thread):
+ <input type="radio" name="threadCount" id="radio0" value="0"
+ checked="checked">
+ <label for="radio0">0</label>
+ <input type="radio" name="threadCount" id="radio1" value="1">
+ <label for="radio1">1</label>
+ <input type="radio" name="threadCount" id="radio2" value="2">
+ <label for="radio2">2</label>
+ <input type="radio" name="threadCount" id="radio3" value="4">
+ <label for="radio3">4</label>
+ <input type="radio" name="threadCount" id="radio4" value="6">
+ <label for="radio4">6</label>
+ <input type="radio" name="threadCount" id="radio5" value="8">
+ <label for="radio5">8</label>
+ <input type="radio" name="threadCount" id="radio6" value="12">
+ <label for="radio6">12</label>
+ <input type="radio" name="threadCount" id="radio7" value="16">
+ <label for="radio7">16</label>
+ <input type="radio" name="threadCount" id="radio8" value="24">
+ <label for="radio8">24</label>
+ <input type="radio" name="threadCount" id="radio9" value="32">
+ <label for="radio9">32</label>
+ <br>
+ <input type="submit" id="benchmark" value="Run Benchmark">
+ <label id="result" name="result"> </label>
+ </div>
+ <!-- The NaCl plugin will be embedded inside the element with id "listener".
+ See common.js.-->
+ <div id="listener"></div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/demo/life/life.c b/native_client_sdk/src/examples/demo/life/life.c
index d9d9def..6bd28b5 100644
--- a/native_client_sdk/src/examples/demo/life/life.c
+++ b/native_client_sdk/src/examples/demo/life/life.c
@@ -49,30 +49,27 @@ const unsigned int kInitialRandSeed = 0xC0DE533D;
/*
- * Given a count of cells in a 3x3 grid where cells are worth 1 except for
- * the center which is worth 9, this is a color representation of how
- * "alive" that cell is making for a more interesting representation than
- * a binary alive or dead.
+ * Convert a count value into a live (green) or dead color value.
*/
const uint32_t kNeighborColors[] = {
- MakeBGRA(0x00, 0x00, 0x00, 0xff),
- MakeBGRA(0x00, 0x40, 0x00, 0xff),
- MakeBGRA(0x00, 0x60, 0x00, 0xff),
- MakeBGRA(0x00, 0x80, 0x00, 0xff),
- MakeBGRA(0x00, 0xA0, 0x00, 0xff),
- MakeBGRA(0x00, 0xC0, 0x00, 0xff),
- MakeBGRA(0x00, 0xE0, 0x00, 0xff),
- MakeBGRA(0x00, 0x00, 0x00, 0xff),
- MakeBGRA(0x00, 0x40, 0x00, 0xff),
- MakeBGRA(0x00, 0x60, 0x00, 0xff),
- MakeBGRA(0x00, 0x80, 0x00, 0xff),
- MakeBGRA(0x00, 0xA0, 0x00, 0xff),
- MakeBGRA(0x00, 0xC0, 0x00, 0xff),
- MakeBGRA(0x00, 0xE0, 0x00, 0xff),
- MakeBGRA(0x00, 0xFF, 0x00, 0xff),
- MakeBGRA(0x00, 0xFF, 0x00, 0xff),
- MakeBGRA(0x00, 0xFF, 0x00, 0xff),
- MakeBGRA(0x00, 0xFF, 0x00, 0xff),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
+ MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
+ MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
};
/*
@@ -80,8 +77,8 @@ const uint32_t kNeighborColors[] = {
* values. The health is binary: either alive or dead.
*/
const uint8_t kIsAlive[] = {
- 0, 0, 0, 1, 0, 0, 0, 0, 0, /* Values if the center cell is dead. */
- 0, 0, 1, 1, 0, 0, 0, 0, 0 /* Values if the center cell is alive. */
+ 0, 0, 0, 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
};
void UpdateContext(uint32_t width, uint32_t height) {
@@ -252,12 +249,14 @@ void Render() {
uint32_t *pixel_line = (uint32_t*) (pixels + y * desc.stride);
for (x = 1; x < (desc.size.width - 1); ++x) {
- /* Build sum, weight center by 9x. */
- count = src0[-1] + src0[0] + src0[1] +
- src1[-1] + src1[0] * 9 + src1[1] +
- src2[-1] + src2[0] + src2[1];
+ /* Jitter and sum neighbors. */
+ count = src0[-1] + src0[0] + src0[1] +
+ src1[-1] + + src1[1] +
+ src2[-1] + src2[0] + src2[1];
+ /* Include center cell. */
+ count = count + count + src1[0];
+ /* Use table lookup indexed by count to determine pixel & alive state. */
color = kNeighborColors[count];
-
*pixel_line++ = color;
*dst++ = kIsAlive[count];
++src0;
diff --git a/native_client_sdk/src/examples/demo/life_simd/example.dsc b/native_client_sdk/src/examples/demo/life_simd/example.dsc
new file mode 100644
index 0000000..a27736e
--- /dev/null
+++ b/native_client_sdk/src/examples/demo/life_simd/example.dsc
@@ -0,0 +1,18 @@
+{
+ 'TOOLS': ['pnacl'],
+ 'TARGETS': [
+ {
+ 'NAME' : 'life_simd',
+ 'TYPE' : 'main',
+ 'SOURCES' : [
+ 'life.c',
+ ],
+ 'DEPS': ['ppapi_simple', 'nacl_io'],
+ 'LIBS': ['ppapi_simple', 'nacl_io', 'ppapi_cpp', 'ppapi', 'pthread']
+ }
+ ],
+ 'DEST': 'examples/demo',
+ 'NAME': 'life_simd',
+ 'TITLE': "Conway's Life (SIMD version)",
+ 'GROUP': 'Demo'
+}
diff --git a/native_client_sdk/src/examples/demo/life_simd/index.html b/native_client_sdk/src/examples/demo/life_simd/index.html
new file mode 100644
index 0000000..5d354fd
--- /dev/null
+++ b/native_client_sdk/src/examples/demo/life_simd/index.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2014 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<head>
+ <meta http-equiv="Pragma" content="no-cache">
+ <meta http-equiv="Expires" content="-1">
+ <title>{{title}}</title>
+ <script type="text/javascript" src="common.js"></script>
+</head>
+<body data-width="640" data-height="640" {{attrs}}>
+ <h1>{{title}}</h1>
+ <h2>Status: <code id="statusField">NO-STATUS</code></h2>
+ <!-- The NaCl plugin will be embedded inside the element with id "listener".
+ See common.js.-->
+ <div id="listener"></div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/demo/life_simd/life.c b/native_client_sdk/src/examples/demo/life_simd/life.c
new file mode 100644
index 0000000..e928a4a
--- /dev/null
+++ b/native_client_sdk/src/examples/demo/life_simd/life.c
@@ -0,0 +1,457 @@
+/* Copyright 2014 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ppapi/c/pp_resource.h"
+#include "ppapi/c/ppb_core.h"
+#include "ppapi/c/ppb_fullscreen.h"
+#include "ppapi/c/ppb_graphics_2d.h"
+#include "ppapi/c/ppb_image_data.h"
+#include "ppapi/c/ppb_input_event.h"
+#include "ppapi/c/ppb_instance.h"
+#include "ppapi/c/ppb_view.h"
+
+#include "ppapi_simple/ps_event.h"
+#include "ppapi_simple/ps_main.h"
+
+PPB_Core* g_pCore;
+PPB_Fullscreen* g_pFullscreen;
+PPB_Graphics2D* g_pGraphics2D;
+PPB_ImageData* g_pImageData;
+PPB_Instance* g_pInstance;
+PPB_View* g_pView;
+PPB_InputEvent* g_pInputEvent;
+PPB_KeyboardInputEvent* g_pKeyboardInput;
+PPB_MouseInputEvent* g_pMouseInput;
+PPB_TouchInputEvent* g_pTouchInput;
+
+struct {
+ PP_Resource ctx;
+ struct PP_Size size;
+ int bound;
+ uint8_t* cell_in;
+ uint8_t* cell_out;
+ int32_t cell_stride;
+} g_Context;
+
+
+const unsigned int kInitialRandSeed = 0xC0DE533D;
+const int kCellAlignment = 0x10;
+
+#define INLINE inline __attribute__((always_inline))
+
+/* BGRA helper macro, for constructing a pixel for a BGRA buffer. */
+#define MakeBGRA(b, g, r, a) \
+ (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+
+/* 128 bit vector types */
+typedef uint8_t u8x16_t __attribute__ ((vector_size (16)));
+
+/* Helper function to broadcast x across 16 element vector. */
+INLINE u8x16_t broadcast(uint8_t x) {
+ u8x16_t r = {x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x};
+ return r;
+}
+
+
+/*
+ * Convert a count value into a live (green) or dead color value.
+ */
+const uint32_t kNeighborColors[] = {
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
+ MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
+ MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+ MakeBGRA(0x00, 0x00, 0x00, 0xFF),
+};
+
+/*
+ * These represent the new health value of a cell based on its neighboring
+ * values. The health is binary: either alive or dead.
+ */
+const uint8_t kIsAlive[] = {
+ 0, 0, 0, 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void UpdateContext(uint32_t width, uint32_t height) {
+ int stride = (width + kCellAlignment - 1) & ~kCellAlignment;
+ if (width != g_Context.size.width || height != g_Context.size.height) {
+
+ size_t size = stride * height;
+ size_t index;
+
+ free(g_Context.cell_in);
+ free(g_Context.cell_out);
+
+ /* Create a new context */
+ void* in_buffer = NULL;
+ void* out_buffer = NULL;
+ /* alloc buffers aligned on 16 bytes */
+ posix_memalign(&in_buffer, kCellAlignment, size);
+ posix_memalign(&out_buffer, kCellAlignment, size);
+ g_Context.cell_in = (uint8_t*) in_buffer;
+ g_Context.cell_out = (uint8_t*) out_buffer;
+
+ memset(g_Context.cell_out, 0, size);
+ for (index = 0; index < size; index++) {
+ g_Context.cell_in[index] = rand() & 1;
+ }
+ }
+
+ /* Recreate the graphics context on a view change */
+ g_pCore->ReleaseResource(g_Context.ctx);
+ g_Context.size.width = width;
+ g_Context.size.height = height;
+ g_Context.cell_stride = stride;
+ g_Context.ctx =
+ g_pGraphics2D->Create(PSGetInstanceId(), &g_Context.size, PP_TRUE);
+ g_Context.bound =
+ g_pInstance->BindGraphics(PSGetInstanceId(), g_Context.ctx);
+}
+
+void DrawCell(int32_t x, int32_t y) {
+ int32_t width = g_Context.size.width;
+ int32_t height = g_Context.size.height;
+ int32_t stride = g_Context.cell_stride;
+
+ if (!g_Context.cell_in) return;
+
+ if (x > 0 && x < width - 1 && y > 0 && y < height - 1) {
+ g_Context.cell_in[x - 1 + y * stride] = 1;
+ g_Context.cell_in[x + 1 + y * stride] = 1;
+ g_Context.cell_in[x + (y - 1) * stride] = 1;
+ g_Context.cell_in[x + (y + 1) * stride] = 1;
+ }
+}
+
+void ProcessTouchEvent(PSEvent* event) {
+ uint32_t count = g_pTouchInput->GetTouchCount(event->as_resource,
+ PP_TOUCHLIST_TYPE_TOUCHES);
+ uint32_t i, j;
+ for (i = 0; i < count; i++) {
+ struct PP_TouchPoint touch = g_pTouchInput->GetTouchByIndex(
+ event->as_resource, PP_TOUCHLIST_TYPE_TOUCHES, i);
+ int radius = (int)touch.radius.x;
+ int x = (int)touch.position.x;
+ int y = (int)touch.position.y;
+ /* num = 1/100th the area of touch point */
+ int num = (int)(M_PI * radius * radius / 100.0f);
+ for (j = 0; j < num; j++) {
+ int dx = rand() % (radius * 2) - radius;
+ int dy = rand() % (radius * 2) - radius;
+ /* only plot random cells within the touch area */
+ if (dx * dx + dy * dy <= radius * radius)
+ DrawCell(x + dx, y + dy);
+ }
+ }
+}
+
+void ProcessEvent(PSEvent* event) {
+ switch(event->type) {
+ /* If the view updates, build a new Graphics 2D Context */
+ case PSE_INSTANCE_DIDCHANGEVIEW: {
+ struct PP_Rect rect;
+
+ g_pView->GetRect(event->as_resource, &rect);
+ UpdateContext(rect.size.width, rect.size.height);
+ break;
+ }
+
+ case PSE_INSTANCE_HANDLEINPUT: {
+ PP_InputEvent_Type type = g_pInputEvent->GetType(event->as_resource);
+ PP_InputEvent_Modifier modifiers =
+ g_pInputEvent->GetModifiers(event->as_resource);
+
+ switch(type) {
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN:
+ case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
+ struct PP_Point location =
+ g_pMouseInput->GetPosition(event->as_resource);
+ /* If the button is down, draw */
+ if (modifiers & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
+ DrawCell(location.x, location.y);
+ }
+ break;
+ }
+
+ case PP_INPUTEVENT_TYPE_TOUCHSTART:
+ case PP_INPUTEVENT_TYPE_TOUCHMOVE:
+ ProcessTouchEvent(event);
+ break;
+
+ case PP_INPUTEVENT_TYPE_KEYDOWN: {
+ PP_Bool fullscreen = g_pFullscreen->IsFullscreen(PSGetInstanceId());
+ g_pFullscreen->SetFullscreen(PSGetInstanceId(),
+ fullscreen ? PP_FALSE : PP_TRUE);
+ break;
+ }
+
+ default:
+ break;
+ }
+ /* case PSE_INSTANCE_HANDLEINPUT */
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+
+void Stir() {
+ int32_t width = g_Context.size.width;
+ int32_t height = g_Context.size.height;
+ int32_t stride = g_Context.cell_stride;
+ int32_t i;
+ if (g_Context.cell_in == NULL || g_Context.cell_out == NULL)
+ return;
+
+ for (i = 0; i < width; ++i) {
+ g_Context.cell_in[i] = rand() & 1;
+ g_Context.cell_in[i + (height - 1) * stride] = rand() & 1;
+ }
+ for (i = 0; i < height; ++i) {
+ g_Context.cell_in[i * stride] = rand() & 1;
+ g_Context.cell_in[i * stride + (width - 1)] = rand() & 1;
+ }
+}
+
+
+void Render() {
+ struct PP_Size* psize = &g_Context.size;
+ PP_ImageDataFormat format = PP_IMAGEDATAFORMAT_BGRA_PREMUL;
+
+ /*
+ * Create a buffer to draw into. Since we are waiting until the next flush
+ * chrome has an opportunity to cache this buffer see ppb_graphics_2d.h.
+ */
+ PP_Resource image =
+ g_pImageData->Create(PSGetInstanceId(), format, psize, PP_FALSE);
+ uint8_t* pixels = g_pImageData->Map(image);
+
+ struct PP_ImageDataDesc desc;
+ uint8_t* cell_temp;
+ uint32_t x, y;
+
+ /* If we somehow have not allocated these pointers yet, skip this frame. */
+ if (!g_Context.cell_in || !g_Context.cell_out) return;
+
+ /* Get the pixel stride. */
+ g_pImageData->Describe(image, &desc);
+
+ /* Stir up the edges to prevent the simulation from reaching steady state. */
+ Stir();
+
+ /*
+ * Do neighbor summation; apply rules, output pixel color. Note that a 1 cell
+ * wide perimeter is excluded from the simulation update; only cells from
+ * x = 1 to x < width - 1 and y = 1 to y < height - 1 are updated.
+ */
+
+ for (y = 1; y < g_Context.size.height - 1; ++y) {
+ uint8_t *src0 = (g_Context.cell_in + (y - 1) * g_Context.cell_stride);
+ uint8_t *src1 = src0 + g_Context.cell_stride;
+ uint8_t *src2 = src1 + g_Context.cell_stride;
+ uint8_t *dst = (g_Context.cell_out + y * g_Context.cell_stride) + 1;
+ uint32_t *pixel_line = (uint32_t*) (pixels + y * desc.stride);
+ const u8x16_t kOne = broadcast(1);
+ const u8x16_t kFour = broadcast(4);
+ const u8x16_t kEight = broadcast(8);
+ const u8x16_t kZero255 = {0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ /* Prime the src */
+ u8x16_t src00 = *(u8x16_t*)&src0[0];
+ u8x16_t src01 = *(u8x16_t*)&src0[16];
+ u8x16_t src10 = *(u8x16_t*)&src1[0];
+ u8x16_t src11 = *(u8x16_t*)&src1[16];
+ u8x16_t src20 = *(u8x16_t*)&src2[0];
+ u8x16_t src21 = *(u8x16_t*)&src2[16];
+
+ /* This inner loop is SIMD - each loop iteration will process 16 cells. */
+ for (x = 1; (x + 15) < (g_Context.size.width - 1); x += 16) {
+
+ /*
+ * Construct jittered source temps, using __builtin_shufflevector(..) to
+ * extract a shifted 16 element vector from the 32 element concatenation
+ * of two source vectors.
+ */
+ u8x16_t src0j0 = src00;
+ u8x16_t src0j1 = __builtin_shufflevector(src00, src01,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
+ u8x16_t src0j2 = __builtin_shufflevector(src00, src01,
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
+ u8x16_t src1j0 = src10;
+ u8x16_t src1j1 = __builtin_shufflevector(src10, src11,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
+ u8x16_t src1j2 = __builtin_shufflevector(src10, src11,
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
+ u8x16_t src2j0 = src20;
+ u8x16_t src2j1 = __builtin_shufflevector(src20, src21,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
+ u8x16_t src2j2 = __builtin_shufflevector(src20, src21,
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
+
+ /* Sum the jittered sources to construct neighbor count. */
+ u8x16_t count = src0j0 + src0j1 + src0j2 +
+ src1j0 + + src1j2 +
+ src2j0 + src2j1 + src2j2;
+ /* Add the center cell. */
+ count = count + count + src1j1;
+ /* If count > 4 and < 8, center cell will be alive in the next frame. */
+ u8x16_t alive1 = count > kFour;
+ u8x16_t alive2 = count < kEight;
+ /* Intersect the two comparisons from above. */
+ u8x16_t alive = alive1 & alive2;
+
+ /*
+ * At this point, alive[x] will be one of two values:
+ * 0x00 for a dead cell
+ * 0xFF for an alive cell.
+ *
+ * Next, convert alive cells to green pixel color.
+ * Use __builtin_shufflevector(..) to construct output pixels from
+ * concantination of alive vector and kZero255 const vector.
+ * Indices 0..15 select the 16 cells from alive vector.
+ * Index 16 is zero constant from kZero255 constant vector.
+ * Index 17 is 255 constant from kZero255 constant vector.
+ * Output pixel color values are in BGRABGRABGRABGRA order.
+ * Since each pixel needs 4 bytes of color information, 16 cells will
+ * need to expand to 4 seperate 16 byte pixel splats.
+ */
+ u8x16_t pixel0_3 = __builtin_shufflevector(alive, kZero255,
+ 16, 0, 16, 17, 16, 1, 16, 17, 16, 2, 16, 17, 16, 3, 16, 17);
+ u8x16_t pixel4_7 = __builtin_shufflevector(alive, kZero255,
+ 16, 4, 16, 17, 16, 5, 16, 17, 16, 6, 16, 17, 16, 7, 16, 17);
+ u8x16_t pixel8_11 = __builtin_shufflevector(alive, kZero255,
+ 16, 8, 16, 17, 16, 9, 16, 17, 16, 10, 16, 17, 16, 11, 16, 17);
+ u8x16_t pixel12_15 = __builtin_shufflevector(alive, kZero255,
+ 16, 12, 16, 17, 16, 13, 16, 17, 16, 14, 16, 17, 16, 15, 16, 17);
+
+ /* Write 16 pixels to output pixel buffer. */
+ *(u8x16_t*)(pixel_line + 0) = pixel0_3;
+ *(u8x16_t*)(pixel_line + 4) = pixel4_7;
+ *(u8x16_t*)(pixel_line + 8) = pixel8_11;
+ *(u8x16_t*)(pixel_line + 12) = pixel12_15;
+
+ /* Convert alive mask to 1 or 0 and store in destination cell array. */
+ *(u8x16_t*)dst = alive & kOne;
+
+ /* Increment pointers. */
+ pixel_line += 16;
+ dst += 16;
+ src0 += 16;
+ src1 += 16;
+ src2 += 16;
+
+ /* Shift source over by 16 cells and read the next 16 cells. */
+ src00 = src01;
+ src01 = *(u8x16_t*)&src0[16];
+ src10 = src11;
+ src11 = *(u8x16_t*)&src1[16];
+ src20 = src21;
+ src21 = *(u8x16_t*)&src2[16];
+ }
+
+ /*
+ * The SIMD loop above does 16 cells at a time. The loop below is the
+ * regular version which processes one cell at a time. It is used to
+ * finish the remainder of the scanline not handled by the SIMD loop.
+ */
+ for (; x < (g_Context.size.width - 1); ++x) {
+ /* Sum the jittered sources to construct neighbor count. */
+ int count = src0[0] + src0[1] + src0[2] +
+ src1[0] + + src1[2] +
+ src2[0] + src2[1] + src2[2];
+ /* Add the center cell. */
+ count = count + count + src1[1];
+ /* Use table lookup indexed by count to determine pixel & alive state. */
+ uint32_t color = kNeighborColors[count];
+ *pixel_line++ = color;
+ *dst++ = kIsAlive[count];
+ ++src0;
+ ++src1;
+ ++src2;
+ }
+ }
+
+ cell_temp = g_Context.cell_in;
+ g_Context.cell_in = g_Context.cell_out;
+ g_Context.cell_out = cell_temp;
+
+ /* Unmap the range, we no longer need it. */
+ g_pImageData->Unmap(image);
+
+ /* Replace the contexts, and block until it's on the screen. */
+ g_pGraphics2D->ReplaceContents(g_Context.ctx, image);
+ g_pGraphics2D->Flush(g_Context.ctx, PP_BlockUntilComplete());
+
+ /* Release the image data, we no longer need it. */
+ g_pCore->ReleaseResource(image);
+}
+
+/*
+ * Starting point for the module. We do not use main since it would
+ * collide with main in libppapi_cpp.
+ */
+int example_main(int argc, char *argv[]) {
+ fprintf(stdout,"Started main.\n");
+ g_pCore = (PPB_Core*)PSGetInterface(PPB_CORE_INTERFACE);
+ g_pFullscreen = (PPB_Fullscreen*)PSGetInterface(PPB_FULLSCREEN_INTERFACE);
+ g_pGraphics2D = (PPB_Graphics2D*)PSGetInterface(PPB_GRAPHICS_2D_INTERFACE);
+ g_pInstance = (PPB_Instance*)PSGetInterface(PPB_INSTANCE_INTERFACE);
+ g_pImageData = (PPB_ImageData*)PSGetInterface(PPB_IMAGEDATA_INTERFACE);
+ g_pView = (PPB_View*)PSGetInterface(PPB_VIEW_INTERFACE);
+
+ g_pInputEvent =
+ (PPB_InputEvent*) PSGetInterface(PPB_INPUT_EVENT_INTERFACE);
+ g_pKeyboardInput = (PPB_KeyboardInputEvent*)
+ PSGetInterface(PPB_KEYBOARD_INPUT_EVENT_INTERFACE);
+ g_pMouseInput =
+ (PPB_MouseInputEvent*) PSGetInterface(PPB_MOUSE_INPUT_EVENT_INTERFACE);
+ g_pTouchInput =
+ (PPB_TouchInputEvent*) PSGetInterface(PPB_TOUCH_INPUT_EVENT_INTERFACE);
+
+ PSEventSetFilter(PSE_ALL);
+ while (1) {
+ /* Process all waiting events without blocking */
+ PSEvent* event;
+ while ((event = PSEventTryAcquire()) != NULL) {
+ ProcessEvent(event);
+ PSEventRelease(event);
+ }
+
+ /* Render a frame, blocking until complete. */
+ if (g_Context.bound) {
+ Render();
+ }
+ }
+ return 0;
+}
+
+/*
+ * Register the function to call once the Instance Object is initialized.
+ * see: pappi_simple/ps_main.h
+ */
+PPAPI_SIMPLE_REGISTER_MAIN(example_main);
diff --git a/native_client_sdk/src/examples/demo/voronoi/example.js b/native_client_sdk/src/examples/demo/voronoi/example.js
index 1807831..53993ca 100644
--- a/native_client_sdk/src/examples/demo/voronoi/example.js
+++ b/native_client_sdk/src/examples/demo/voronoi/example.js
@@ -37,7 +37,7 @@ function attachListeners() {
document.getElementById('radio' + i).addEventListener('click',
postThreadFunc(threads[i]));
}
- document.getElementById('pointRange').addEventListener('change',
+ document.getElementById('pointRange').addEventListener('input',
function() {
var value = parseFloat(document.getElementById('pointRange').value);
common.naclModule.postMessage({'message' : 'set_points',