Home | History | Annotate | Download | only in earth_simd
      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 #include <assert.h>
      6 #include <math.h>
      7 #include <ppapi/c/ppb_input_event.h>
      8 #include <ppapi/cpp/input_event.h>
      9 #include <ppapi/cpp/var.h>
     10 #include <ppapi/cpp/var_array.h>
     11 #include <ppapi/cpp/var_array_buffer.h>
     12 #include <ppapi/cpp/var_dictionary.h>
     13 #include <pthread.h>
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #include <sys/time.h>
     18 #include <unistd.h>
     19 
     20 #include <algorithm>
     21 #include <string>
     22 
     23 #include "ppapi_simple/ps.h"
     24 #include "ppapi_simple/ps_context_2d.h"
     25 #include "ppapi_simple/ps_event.h"
     26 #include "ppapi_simple/ps_interface.h"
     27 #include "ppapi_simple/ps_main.h"
     28 #include "sdk_util/macros.h"
     29 #include "sdk_util/thread_pool.h"
     30 
     31 using namespace sdk_util;  // For sdk_util::ThreadPool
     32 
     33 #define INLINE inline __attribute__((always_inline))
     34 
     35 // 128 bit SIMD vector types
     36 typedef uint8_t u8x16_t __attribute__ ((vector_size (16)));
     37 typedef int32_t i32x4_t __attribute__ ((vector_size (16)));
     38 typedef uint32_t u32x4_t __attribute__ ((vector_size (16)));
     39 typedef float f32x4_t __attribute__ ((vector_size (16)));
     40 
     41 // Global properties used to setup Earth demo.
     42 namespace {
     43 const float kPI = M_PI;
     44 const float kTwoPI = kPI * 2.0f;
     45 const float kOneOverPI = 1.0f / kPI;
     46 const float kOneOver2PI = 1.0f / kTwoPI;
     47 const int kArcCosineTableSize = 4096;
     48 const int kFramesToBenchmark = 100;
     49 const float kZoomMin = 1.0f;
     50 const float kZoomMax = 50.0f;
     51 const float kWheelSpeed = 2.0f;
     52 const float kLightMin = 0.0f;
     53 const float kLightMax = 2.0f;
     54 
     55 // Timer helper for benchmarking.  Returns seconds elapsed since program start,
     56 // as a double.
     57 timeval start_tv;
     58 int start_tv_retv = gettimeofday(&start_tv, NULL);
     59 
     60 inline double getseconds() {
     61   const double usec_to_sec = 0.000001;
     62   timeval tv;
     63   if ((0 == start_tv_retv) && (0 == gettimeofday(&tv, NULL)))
     64     return (tv.tv_sec - start_tv.tv_sec) + tv.tv_usec * usec_to_sec;
     65   return 0.0;
     66 }
     67 
     68 // SIMD Vector helper functions.
     69 //
     70 // Note that a compare between two vectors will return a signed integer vector
     71 // with the same number of elements, where each element will be all bits set
     72 // for true (-1), and all bits clear for false (0) This integer vector can be
     73 // useful as a mask.
     74 //
     75 // Also note that c-style casts do not mutate the bits of a vector - only the
     76 // type.  Boolean operators can't operate on float vectors, but it is possible
     77 // to cast them temporarily to integer vector, perform the mask, and cast
     78 // them back to float.
     79 //
     80 // To convert a float vector to an integer vector using trunction, or to
     81 // convert an integer vector to a float vector, use __builtin_convertvector().
     82 
     83 INLINE f32x4_t min(f32x4_t a, f32x4_t b) {
     84   i32x4_t m = a < b;
     85   return (f32x4_t)(((i32x4_t)a & m) | ((i32x4_t)b & ~m));
     86 }
     87 
     88 INLINE f32x4_t max(f32x4_t a, f32x4_t b) {
     89   i32x4_t m = a > b;
     90   return (f32x4_t)(((i32x4_t)a & m) | ((i32x4_t)b & ~m));
     91 }
     92 
     93 INLINE float dot3(f32x4_t a, f32x4_t b) {
     94   f32x4_t c = a * b;
     95   return c[0] + c[1] + c[2];
     96 }
     97 
     98 INLINE f32x4_t broadcast(float x) {
     99   f32x4_t r = {x, x, x, x};
    100   return r;
    101 }
    102 
    103 // SIMD RGBA helper functions, used for extracting color from RGBA source image.
    104 INLINE f32x4_t ExtractRGBA(uint32_t c) {
    105   const f32x4_t kOneOver255 = broadcast(1.0f / 255.0f);
    106   const i32x4_t kZero = {0, 0, 0, 0};
    107   i32x4_t v = {c, c, c, c};
    108   // zero extend packed color into 32x4 integer vector
    109   v = (i32x4_t)__builtin_shufflevector((u8x16_t)v, (u8x16_t)kZero,
    110       0, 16, 16, 16, 1, 16, 16, 16, 2, 16, 16, 16, 3, 16, 16, 16);
    111   // convert color values to float, range 0..1
    112   f32x4_t f = __builtin_convertvector(v, f32x4_t) * kOneOver255;
    113   return f;
    114 }
    115 
    116 // SIMD BGRA helper function, for constructing a pixel for a BGRA buffer.
    117 INLINE uint32_t PackBGRA(f32x4_t f) {
    118   const f32x4_t kZero = broadcast(0.0f);
    119   const f32x4_t kHalf = broadcast(0.5f);
    120   const f32x4_t k255 = broadcast(255.0f);
    121   f = max(f, kZero);
    122   // Add 0.5 to perform rounding instead of truncation.
    123   f = f * k255 + kHalf;
    124   f = min(f, k255);
    125   i32x4_t i = __builtin_convertvector(f, i32x4_t);
    126   u32x4_t p = (u32x4_t)__builtin_shufflevector((u8x16_t)i, (u8x16_t)i,
    127       8, 4, 0, 12, 8, 4, 0, 12, 8, 4, 0, 12, 8, 4, 0, 12);
    128   return p[0];
    129 }
    130 
    131 // BGRA helper function, for constructing a pixel for a BGRA buffer.
    132 INLINE uint32_t MakeBGRA(uint32_t b, uint32_t g, uint32_t r, uint32_t a) {
    133   return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b));
    134 }
    135 
    136 // simple container for earth texture
    137 struct Texture {
    138   int width, height;
    139   uint32_t* pixels;
    140   Texture(int w, int h) : width(w), height(h) {
    141     pixels = new uint32_t[w * h];
    142     memset(pixels, 0, sizeof(uint32_t) * w * h);
    143   }
    144   explicit Texture(int w, int h, uint32_t* p) : width(w), height(h) {
    145     pixels = new uint32_t[w * h];
    146     memcpy(pixels, p, sizeof(uint32_t) * w * h);
    147   }
    148   ~Texture() { delete[] pixels; }
    149 
    150   DISALLOW_COPY_AND_ASSIGN(Texture);
    151 };
    152 
    153 
    154 
    155 struct ArcCosine {
    156   // slightly larger table so we can interpolate beyond table size
    157   float table[kArcCosineTableSize + 2];
    158   float TableLerp(float x);
    159   ArcCosine();
    160 };
    161 
    162 ArcCosine::ArcCosine() {
    163   // build a slightly larger table to allow for numeric imprecision
    164   for (int i = 0; i < (kArcCosineTableSize + 2); ++i) {
    165     float f = static_cast<float>(i) / kArcCosineTableSize;
    166     f = f * 2.0f - 1.0f;
    167     table[i] = acos(f);
    168   }
    169 }
    170 
    171 // looks up acos(f) using a table and lerping between entries
    172 // (it is expected that input f is between -1 and 1)
    173 INLINE float ArcCosine::TableLerp(float f) {
    174   float x = (f + 1.0f) * 0.5f;
    175   x = x * kArcCosineTableSize;
    176   int ix = static_cast<int>(x);
    177   float fx = static_cast<float>(ix);
    178   float dx = x - fx;
    179   float af = table[ix];
    180   float af2 = table[ix + 1];
    181   return af + (af2 - af) * dx;
    182 }
    183 
    184 // Helper functions for quick but approximate sqrt.
    185 union Convert {
    186   float f;
    187   int i;
    188   Convert(int x) { i = x; }
    189   Convert(float x) { f = x; }
    190   int AsInt() { return i; }
    191   float AsFloat() { return f; }
    192 };
    193 
    194 INLINE const int AsInteger(const float f) {
    195   Convert u(f);
    196   return u.AsInt();
    197 }
    198 
    199 INLINE const float AsFloat(const int i) {
    200   Convert u(i);
    201   return u.AsFloat();
    202 }
    203 
    204 const long int kOneAsInteger = AsInteger(1.0f);
    205 
    206 INLINE float inline_quick_sqrt(float x) {
    207   int i;
    208   i = (AsInteger(x) >> 1) + (kOneAsInteger >> 1);
    209   return AsFloat(i);
    210 }
    211 
    212 INLINE float inline_sqrt(float x) {
    213   float y;
    214   y = inline_quick_sqrt(x);
    215   y = (y * y + x) / (2.0f * y);
    216   y = (y * y + x) / (2.0f * y);
    217   return y;
    218 }
    219 
    220 }  // namespace
    221 
    222 
    223 // The main object that runs the Earth demo.
    224 class Planet {
    225  public:
    226   Planet();
    227   virtual ~Planet();
    228   // Runs a tick of the simulations, update 2D output.
    229   void Update();
    230   // Handle event from user, or message from JS.
    231   void HandleEvent(PSEvent* ps_event);
    232 
    233  private:
    234   // Methods prefixed with 'w' are run on worker threads.
    235   uint32_t* wGetAddr(int x, int y);
    236   void wRenderPixelSpan(int x0, int x1, int y);
    237   void wMakeRect(int r, int *x, int *y, int *w, int *h);
    238   void wRenderRect(int x0, int y0, int x1, int y1);
    239   void wRenderRegion(int region);
    240   static void wRenderRegionEntry(int region, void *thiz);
    241 
    242   // These methods are only called by the main thread.
    243   void CacheCalcs();
    244   void SetPlanetXYZR(float x, float y, float z, float r);
    245   void SetPlanetPole(float x, float y, float z);
    246   void SetPlanetEquator(float x, float y, float z);
    247   void SetPlanetSpin(float x, float y);
    248   void SetEyeXYZ(float x, float y, float z);
    249   void SetLightXYZ(float x, float y, float z);
    250   void SetAmbientRGB(float r, float g, float b);
    251   void SetDiffuseRGB(float r, float g, float b);
    252   void SetZoom(float zoom);
    253   void SetLight(float zoom);
    254   void SetTexture(const std::string& name, int width, int height,
    255       uint32_t* pixels);
    256   void SpinPlanet(pp::Point new_point, pp::Point last_point);
    257 
    258   void Reset();
    259   void RequestTextures();
    260   void UpdateSim();
    261   void Render();
    262   void Draw();
    263   void StartBenchmark();
    264   void EndBenchmark();
    265   // Post a small key-value message to update JS.
    266   void PostUpdateMessage(const char* message_name, double value);
    267 
    268   // User Interface settings.  These settings are controlled via html
    269   // controls or via user input.
    270   float ui_light_;
    271   float ui_zoom_;
    272   float ui_spin_x_;
    273   float ui_spin_y_;
    274   pp::Point ui_last_point_;
    275 
    276   // Various settings for position & orientation of planet.  Do not change
    277   // these variables, instead use SetPlanet*() functions.
    278   float planet_radius_;
    279   float planet_spin_x_;
    280   float planet_spin_y_;
    281   float planet_x_, planet_y_, planet_z_;
    282   float planet_pole_x_, planet_pole_y_, planet_pole_z_;
    283   float planet_equator_x_, planet_equator_y_, planet_equator_z_;
    284 
    285   // Observer's eye.  Do not change these variables, instead use SetEyeXYZ().
    286   float eye_x_, eye_y_, eye_z_;
    287 
    288   // Light position, ambient and diffuse settings.  Do not change these
    289   // variables, instead use SetLightXYZ(), SetAmbientRGB() and SetDiffuseRGB().
    290   float light_x_, light_y_, light_z_;
    291   float diffuse_r_, diffuse_g_, diffuse_b_;
    292   float ambient_r_, ambient_g_, ambient_b_;
    293 
    294   // Cached calculations.  Do not change these variables - they are updated by
    295   // CacheCalcs() function.
    296   float planet_xyz_;
    297   float planet_pole_x_equator_x_;
    298   float planet_pole_x_equator_y_;
    299   float planet_pole_x_equator_z_;
    300   float planet_radius2_;
    301   float planet_one_over_radius_;
    302   float eye_xyz_;
    303 
    304   // Source texture (earth map).
    305   Texture* base_tex_;
    306   Texture* night_tex_;
    307   int width_for_tex_;
    308   int height_for_tex_;
    309 
    310   // Quick ArcCos helper.
    311   ArcCosine acos_;
    312 
    313   // Misc.
    314   PSContext2D_t* ps_context_;
    315   int num_threads_;
    316   ThreadPool* workers_;
    317   bool benchmarking_;
    318   int benchmark_frame_counter_;
    319   double benchmark_start_time_;
    320   double benchmark_end_time_;
    321 };
    322 
    323 
    324 void Planet::RequestTextures() {
    325   // Request a set of images from JS.  After images are loaded by JS, a
    326   // message from JS -> NaCl will arrive containing the pixel data.  See
    327   // HandleMessage() method in this file.
    328   pp::VarDictionary message;
    329   message.Set("message", "request_textures");
    330   pp::VarArray names;
    331   names.Set(0, "earth.jpg");
    332   names.Set(1, "earthnight.jpg");
    333   message.Set("names", names);
    334   PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
    335 }
    336 
    337 void Planet::Reset() {
    338   // Reset has to first fill in all variables with valid floats, so
    339   // CacheCalcs() doesn't potentially propagate NaNs when calling Set*()
    340   // functions further below.
    341   planet_radius_ = 1.0f;
    342   planet_spin_x_ = 0.0f;
    343   planet_spin_y_ = 0.0f;
    344   planet_x_ = 0.0f;
    345   planet_y_ = 0.0f;
    346   planet_z_ = 0.0f;
    347   planet_pole_x_ = 0.0f;
    348   planet_pole_y_ = 0.0f;
    349   planet_pole_z_ = 0.0f;
    350   planet_equator_x_ = 0.0f;
    351   planet_equator_y_ = 0.0f;
    352   planet_equator_z_ = 0.0f;
    353   eye_x_ = 0.0f;
    354   eye_y_ = 0.0f;
    355   eye_z_ = 0.0f;
    356   light_x_ = 0.0f;
    357   light_y_ = 0.0f;
    358   light_z_ = 0.0f;
    359   diffuse_r_ = 0.0f;
    360   diffuse_g_ = 0.0f;
    361   diffuse_b_ = 0.0f;
    362   ambient_r_ = 0.0f;
    363   ambient_g_ = 0.0f;
    364   ambient_b_ = 0.0f;
    365   planet_xyz_ = 0.0f;
    366   planet_pole_x_equator_x_ = 0.0f;
    367   planet_pole_x_equator_y_ = 0.0f;
    368   planet_pole_x_equator_z_ = 0.0f;
    369   planet_radius2_ = 0.0f;
    370   planet_one_over_radius_ = 0.0f;
    371   eye_xyz_ = 0.0f;
    372   ui_zoom_ = 14.0f;
    373   ui_light_ = 1.0f;
    374   ui_spin_x_ = 0.01f;
    375   ui_spin_y_ = 0.0f;
    376   ui_last_point_ = pp::Point(0, 0);
    377 
    378   // Set up reasonable default values.
    379   SetPlanetXYZR(0.0f, 0.0f, 48.0f, 4.0f);
    380   SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
    381   SetLightXYZ(-60.0f, -30.0f, 0.0f);
    382   SetAmbientRGB(0.05f, 0.05f, 0.05f);
    383   SetDiffuseRGB(0.8f, 0.8f, 0.8f);
    384   SetPlanetPole(0.0f, 1.0f, 0.0f);
    385   SetPlanetEquator(1.0f, 0.0f, 0.0f);
    386   SetPlanetSpin(kPI / 2.0f, kPI / 2.0f);
    387   SetZoom(ui_zoom_);
    388   SetLight(ui_light_);
    389 
    390   // Send UI values to JS to reset html sliders.
    391   PostUpdateMessage("set_zoom", ui_zoom_);
    392   PostUpdateMessage("set_light", ui_light_);
    393 }
    394 
    395 
    396 Planet::Planet() : base_tex_(NULL), night_tex_(NULL), num_threads_(0),
    397     benchmarking_(false), benchmark_frame_counter_(0) {
    398 
    399   Reset();
    400   RequestTextures();
    401   // By default, render from the dispatch thread.
    402   workers_ = new ThreadPool(num_threads_);
    403   PSEventSetFilter(PSE_ALL);
    404   ps_context_ = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL);
    405 }
    406 
    407 Planet::~Planet() {
    408   delete workers_;
    409   PSContext2DFree(ps_context_);
    410 }
    411 
    412 // Given a region r, derive a rectangle.
    413 // This rectangle shouldn't overlap with work being done by other workers.
    414 // If multithreading, this function is only called by the worker threads.
    415 void Planet::wMakeRect(int r, int *x, int *y, int *w, int *h) {
    416   *x = 0;
    417   *w = ps_context_->width;
    418   *y = r;
    419   *h = 1;
    420 }
    421 
    422 
    423 inline uint32_t* Planet::wGetAddr(int x, int y) {
    424   return ps_context_->data + x + y * ps_context_->stride / sizeof(uint32_t);
    425 }
    426 
    427 // This is the inner loop of the ray tracer.  Given a pixel span (x0, x1) on
    428 // scanline y, shoot rays into the scene and render what they hit.  Use
    429 // scanline coherence to do a few optimizations.
    430 // This version uses portable SIMD 4 element single precision floating point
    431 // vectors to perform many of the calculations, and builds only on PNaCl.
    432 void Planet::wRenderPixelSpan(int x0, int x1, int y) {
    433   if (!base_tex_ || !night_tex_)
    434     return;
    435   const uint32_t kColorBlack = MakeBGRA(0, 0, 0, 0xFF);
    436   const uint32_t kSolidAlpha = MakeBGRA(0, 0, 0, 0xFF);
    437   const f32x4_t kOne = {1.0f, 1.0f, 1.0f, 1.0f};
    438   const f32x4_t diffuse = {diffuse_r_, diffuse_g_, diffuse_b_, 0.0f};
    439   const f32x4_t ambient = {ambient_r_, ambient_g_, ambient_b_, 0.0f};
    440   const f32x4_t light_pos = {light_x_, light_y_, light_z_, 1.0f};
    441   const f32x4_t planet_pos = {planet_x_, planet_y_, planet_z_, 1.0f};
    442   const f32x4_t planet_one_over_radius = broadcast(planet_one_over_radius_);
    443   const f32x4_t planet_equator = {
    444       planet_equator_x_, planet_equator_y_, planet_equator_z_, 0.0f};
    445   const f32x4_t planet_pole = {
    446       planet_pole_x_, planet_pole_y_, planet_pole_z_, 1.0f};
    447   const f32x4_t planet_pole_x_equator = {
    448       planet_pole_x_equator_x_, planet_pole_x_equator_y_,
    449       planet_pole_x_equator_z_, 0.0f};
    450 
    451   float width = ps_context_->width;
    452   float height = ps_context_->height;
    453   float min_dim = width < height ? width : height;
    454   float offset_x = width < height ? 0 : (width - min_dim) * 0.5f;
    455   float offset_y = width < height ? (height - min_dim) * 0.5f : 0;
    456   float y0 = eye_y_;
    457   float z0 = eye_z_;
    458   float y1 = (static_cast<float>(y - offset_y) / min_dim) * 2.0f - 1.0f;
    459   float z1 = 0.0f;
    460   float dy = (y1 - y0);
    461   float dz = (z1 - z0);
    462   float dy_dy_dz_dz = dy * dy + dz * dz;
    463   float two_dy_y0_y_two_dz_z0_z = 2.0f * dy * (y0 - planet_y_) +
    464                                   2.0f * dz * (z0 - planet_z_);
    465   float planet_xyz_eye_xyz = planet_xyz_ + eye_xyz_;
    466   float y_y0_z_z0 = planet_y_ * y0 + planet_z_ * z0;
    467   float oowidth = 1.0f / min_dim;
    468   uint32_t* pixels = this->wGetAddr(x0, y);
    469   for (int x = x0; x <= x1; ++x) {
    470     // scan normalized screen -1..1
    471     float x1 = (static_cast<float>(x - offset_x) * oowidth) * 2.0f - 1.0f;
    472     // eye
    473     float x0 = eye_x_;
    474     // delta from screen to eye
    475     float dx = (x1 - x0);
    476     // build a, b, c
    477     float a = dx * dx + dy_dy_dz_dz;
    478     float b = 2.0f * dx * (x0 - planet_x_) + two_dy_y0_y_two_dz_z0_z;
    479     float c = planet_xyz_eye_xyz +
    480               -2.0f * (planet_x_ * x0 + y_y0_z_z0) - (planet_radius2_);
    481     // calculate discriminant
    482     float disc = b * b - 4.0f * a * c;
    483 
    484     // Did ray hit the sphere?
    485     if (disc < 0.0f) {
    486       *pixels = kColorBlack;
    487       ++pixels;
    488       continue;
    489     }
    490 
    491     f32x4_t delta = {dx, dy, dz, 1.0f};
    492     f32x4_t base = {x0, y0, z0, 1.0f};
    493 
    494     // Calc parametric t value.
    495     float t = (-b - inline_sqrt(disc)) / (2.0f * a);
    496 
    497     f32x4_t pos = base + broadcast(t) * delta;
    498     f32x4_t normal = (pos - planet_pos) * planet_one_over_radius;
    499 
    500     // Misc raytrace calculations.
    501     f32x4_t L = light_pos - pos;
    502     float Lq = 1.0f / inline_quick_sqrt(dot3(L, L));
    503     L = L * broadcast(Lq);
    504     float d = dot3(L, normal);
    505     f32x4_t p = diffuse * broadcast(d) + ambient;
    506     float ds = -dot3(normal, planet_pole);
    507     float ang = acos_.TableLerp(ds);
    508     float v = ang * kOneOverPI;
    509     float dp = dot3(planet_equator, normal);
    510     float w = dp / sinf(ang);
    511     if (w > 1.0f) w = 1.0f;
    512     if (w < -1.0f) w = -1.0f;
    513     float th = acos_.TableLerp(w) * kOneOver2PI;
    514     float dps = dot3(planet_pole_x_equator, normal);
    515     float u;
    516     if (dps < 0.0f)
    517       u = th;
    518     else
    519       u = 1.0f - th;
    520 
    521     // Look up daylight texel.
    522     int tx = static_cast<int>(u * base_tex_->width);
    523     int ty = static_cast<int>(v * base_tex_->height);
    524     int offset = tx + ty * base_tex_->width;
    525     uint32_t base_texel = base_tex_->pixels[offset];
    526     f32x4_t dc = ExtractRGBA(base_texel);
    527 
    528     // Look up night texel.
    529     int nix = static_cast<int>(u * night_tex_->width);
    530     int niy = static_cast<int>(v * night_tex_->height);
    531     int noffset = nix + niy * night_tex_->width;
    532     uint32_t night_texel = night_tex_->pixels[noffset];
    533     f32x4_t nc = ExtractRGBA(night_texel);
    534 
    535     // Blend between daylight (dc) and nighttime (nc) color.
    536     f32x4_t pc = min(p, kOne);
    537     f32x4_t fc = dc * p + nc * (kOne - pc);
    538     uint32_t color = PackBGRA(fc);
    539 
    540     *pixels = color | kSolidAlpha;
    541     ++pixels;
    542   }
    543 }
    544 
    545 // Renders a rectangular area of the screen, scan line at a time
    546 void Planet::wRenderRect(int x, int y, int w, int h) {
    547   for (int j = y; j < (y + h); ++j) {
    548     this->wRenderPixelSpan(x, x + w - 1, j);
    549   }
    550 }
    551 
    552 // If multithreading, this function is only called by the worker threads.
    553 void Planet::wRenderRegion(int region) {
    554   // convert region # into x0, y0, x1, y1 rectangle
    555   int x, y, w, h;
    556   wMakeRect(region, &x, &y, &w, &h);
    557   // render this rectangle
    558   wRenderRect(x, y, w, h);
    559 }
    560 
    561 // Entry point for worker thread.  Can't pass a member function around, so we
    562 // have to do this little round-about.
    563 void Planet::wRenderRegionEntry(int region, void* thiz) {
    564   static_cast<Planet*>(thiz)->wRenderRegion(region);
    565 }
    566 
    567 // Renders the planet, dispatching the work to multiple threads.
    568 void Planet::Render() {
    569   workers_->Dispatch(ps_context_->height, wRenderRegionEntry, this);
    570 }
    571 
    572 // Pre-calculations to make inner loops faster.
    573 void Planet::CacheCalcs() {
    574   planet_xyz_ = planet_x_ * planet_x_ +
    575                 planet_y_ * planet_y_ +
    576                 planet_z_ * planet_z_;
    577   planet_radius2_ = planet_radius_ * planet_radius_;
    578   planet_one_over_radius_ = 1.0f / planet_radius_;
    579   eye_xyz_ = eye_x_ * eye_x_ + eye_y_ * eye_y_ + eye_z_ * eye_z_;
    580   // spin vector from center->equator
    581   planet_equator_x_ = cos(planet_spin_x_);
    582   planet_equator_y_ = 0.0f;
    583   planet_equator_z_ = sin(planet_spin_x_);
    584 
    585   // cache cross product of pole & equator
    586   planet_pole_x_equator_x_ = planet_pole_y_ * planet_equator_z_ -
    587                              planet_pole_z_ * planet_equator_y_;
    588   planet_pole_x_equator_y_ = planet_pole_z_ * planet_equator_x_ -
    589                              planet_pole_x_ * planet_equator_z_;
    590   planet_pole_x_equator_z_ = planet_pole_x_ * planet_equator_y_ -
    591                              planet_pole_y_ * planet_equator_x_;
    592 }
    593 
    594 void Planet::SetPlanetXYZR(float x, float y, float z, float r) {
    595   planet_x_ = x;
    596   planet_y_ = y;
    597   planet_z_ = z;
    598   planet_radius_ = r;
    599   CacheCalcs();
    600 }
    601 
    602 void Planet::SetEyeXYZ(float x, float y, float z) {
    603   eye_x_ = x;
    604   eye_y_ = y;
    605   eye_z_ = z;
    606   CacheCalcs();
    607 }
    608 
    609 void Planet::SetLightXYZ(float x, float y, float z) {
    610   light_x_ = x;
    611   light_y_ = y;
    612   light_z_ = z;
    613   CacheCalcs();
    614 }
    615 
    616 void Planet::SetAmbientRGB(float r, float g, float b) {
    617   ambient_r_ = r;
    618   ambient_g_ = g;
    619   ambient_b_ = b;
    620   CacheCalcs();
    621 }
    622 
    623 void Planet::SetDiffuseRGB(float r, float g, float b) {
    624   diffuse_r_ = r;
    625   diffuse_g_ = g;
    626   diffuse_b_ = b;
    627   CacheCalcs();
    628 }
    629 
    630 void Planet::SetPlanetPole(float x, float y, float z) {
    631   planet_pole_x_ = x;
    632   planet_pole_y_ = y;
    633   planet_pole_z_ = z;
    634   CacheCalcs();
    635 }
    636 
    637 void Planet::SetPlanetEquator(float x, float y, float z) {
    638   // This is really over-ridden by spin at the momenent.
    639   planet_equator_x_ = x;
    640   planet_equator_y_ = y;
    641   planet_equator_z_ = z;
    642   CacheCalcs();
    643 }
    644 
    645 void Planet::SetPlanetSpin(float x, float y) {
    646   planet_spin_x_ = x;
    647   planet_spin_y_ = y;
    648   CacheCalcs();
    649 }
    650 
    651 // Run a simple sim to spin the planet.  Update loop is run once per frame.
    652 // Called from the main thread only and only when the worker threads are idle.
    653 void Planet::UpdateSim() {
    654   float x = planet_spin_x_ + ui_spin_x_;
    655   float y = planet_spin_y_ + ui_spin_y_;
    656   // keep in nice range
    657   if (x > (kPI * 2.0f))
    658     x = x - kPI * 2.0f;
    659   else if (x < (-kPI * 2.0f))
    660     x = x + kPI * 2.0f;
    661   if (y > (kPI * 2.0f))
    662     y = y - kPI * 2.0f;
    663   else if (y < (-kPI * 2.0f))
    664     y = y + kPI * 2.0f;
    665   SetPlanetSpin(x, y);
    666 }
    667 
    668 void Planet::StartBenchmark() {
    669   // For more consistent benchmark numbers, reset to default state.
    670   Reset();
    671   printf("Benchmark started...\n");
    672   benchmark_frame_counter_ = kFramesToBenchmark;
    673   benchmarking_ = true;
    674   benchmark_start_time_ = getseconds();
    675 }
    676 
    677 void Planet::EndBenchmark() {
    678   benchmark_end_time_ = getseconds();
    679   printf("Benchmark ended... time: %2.5f\n",
    680       benchmark_end_time_ - benchmark_start_time_);
    681   benchmarking_ = false;
    682   benchmark_frame_counter_ = 0;
    683   double total_time = benchmark_end_time_ - benchmark_start_time_;
    684   // Send benchmark result to JS.
    685   PostUpdateMessage("benchmark_result", total_time);
    686 }
    687 
    688 void Planet::SetZoom(float zoom) {
    689   ui_zoom_ = std::min(kZoomMax, std::max(kZoomMin, zoom));
    690   SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
    691 }
    692 
    693 void Planet::SetLight(float light) {
    694   ui_light_ = std::min(kLightMax, std::max(kLightMin, light));
    695   SetDiffuseRGB(0.8f * ui_light_, 0.8f * ui_light_, 0.8f * ui_light_);
    696   SetAmbientRGB(0.4f * ui_light_, 0.4f * ui_light_, 0.4f * ui_light_);
    697 }
    698 
    699 void Planet::SetTexture(const std::string& name, int width, int height,
    700                         uint32_t* pixels) {
    701   if (pixels) {
    702     if (name == "earth.jpg") {
    703       delete base_tex_;
    704       base_tex_ = new Texture(width, height, pixels);
    705     } else if (name == "earthnight.jpg") {
    706       delete night_tex_;
    707       night_tex_ = new Texture(width, height, pixels);
    708     }
    709   }
    710 }
    711 
    712 void Planet::SpinPlanet(pp::Point new_point, pp::Point last_point) {
    713   float delta_x = static_cast<float>(new_point.x() - last_point.x());
    714   float delta_y = static_cast<float>(new_point.y() - last_point.y());
    715   float spin_x = std::min(10.0f, std::max(-10.0f, delta_x * 0.5f));
    716   float spin_y = std::min(10.0f, std::max(-10.0f, delta_y * 0.5f));
    717   ui_spin_x_ = spin_x / 100.0f;
    718   ui_spin_y_ = spin_y / 100.0f;
    719   ui_last_point_ = new_point;
    720 }
    721 
    722 // Handle input events from the user and messages from JS.
    723 void Planet::HandleEvent(PSEvent* ps_event) {
    724   // Give the 2D context a chance to process the event.
    725   if (0 != PSContext2DHandleEvent(ps_context_, ps_event))
    726     return;
    727   if (ps_event->type == PSE_INSTANCE_HANDLEINPUT) {
    728     // Convert Pepper Simple event to a PPAPI C++ event
    729     pp::InputEvent event(ps_event->as_resource);
    730     switch (event.GetType()) {
    731       case PP_INPUTEVENT_TYPE_KEYDOWN: {
    732         pp::KeyboardInputEvent key(event);
    733         uint32_t key_code = key.GetKeyCode();
    734         if (key_code == 84)  // 't' key
    735           if (!benchmarking_)
    736             StartBenchmark();
    737         break;
    738       }
    739       case PP_INPUTEVENT_TYPE_MOUSEDOWN:
    740       case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
    741         pp::MouseInputEvent mouse = pp::MouseInputEvent(event);
    742         if (mouse.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
    743           if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN)
    744             SpinPlanet(mouse.GetPosition(), mouse.GetPosition());
    745           else
    746             SpinPlanet(mouse.GetPosition(), ui_last_point_);
    747         }
    748         break;
    749       }
    750       case PP_INPUTEVENT_TYPE_WHEEL: {
    751         pp::WheelInputEvent wheel = pp::WheelInputEvent(event);
    752         PP_FloatPoint ticks = wheel.GetTicks();
    753         SetZoom(ui_zoom_ + (ticks.x + ticks.y) * kWheelSpeed);
    754         // Update html slider by sending update message to JS.
    755         PostUpdateMessage("set_zoom", ui_zoom_);
    756         break;
    757       }
    758       case PP_INPUTEVENT_TYPE_TOUCHSTART:
    759       case PP_INPUTEVENT_TYPE_TOUCHMOVE: {
    760         pp::TouchInputEvent touches = pp::TouchInputEvent(event);
    761         uint32_t count = touches.GetTouchCount(PP_TOUCHLIST_TYPE_TOUCHES);
    762         if (count > 0) {
    763           // Use first touch point to spin planet.
    764           pp::TouchPoint touch =
    765               touches.GetTouchByIndex(PP_TOUCHLIST_TYPE_TOUCHES, 0);
    766           pp::Point screen_point(touch.position().x(),
    767                                  touch.position().y());
    768           if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART)
    769             SpinPlanet(screen_point, screen_point);
    770           else
    771             SpinPlanet(screen_point, ui_last_point_);
    772         }
    773         break;
    774       }
    775       default:
    776         break;
    777     }
    778   } else if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) {
    779     // Convert Pepper Simple message to PPAPI C++ vars
    780     pp::Var var(ps_event->as_var);
    781     if (var.is_dictionary()) {
    782       pp::VarDictionary dictionary(var);
    783       std::string message = dictionary.Get("message").AsString();
    784       if (message == "run benchmark" && !benchmarking_) {
    785         StartBenchmark();
    786       } else if (message == "set_light") {
    787         SetLight(static_cast<float>(dictionary.Get("value").AsDouble()));
    788       } else if (message == "set_zoom") {
    789         SetZoom(static_cast<float>(dictionary.Get("value").AsDouble()));
    790       } else if (message == "set_threads") {
    791         int threads = dictionary.Get("value").AsInt();
    792         delete workers_;
    793         workers_ = new ThreadPool(threads);
    794       } else if (message == "texture") {
    795         std::string name = dictionary.Get("name").AsString();
    796         int width = dictionary.Get("width").AsInt();
    797         int height = dictionary.Get("height").AsInt();
    798         pp::VarArrayBuffer array_buffer(dictionary.Get("data"));
    799         if (!name.empty() && !array_buffer.is_null()) {
    800           if (width > 0 && height > 0) {
    801             uint32_t* pixels = static_cast<uint32_t*>(array_buffer.Map());
    802             SetTexture(name, width, height, pixels);
    803             array_buffer.Unmap();
    804           }
    805         }
    806       }
    807     } else {
    808       printf("Handle message unknown type: %s\n", var.DebugString().c_str());
    809     }
    810   }
    811 }
    812 
    813 // PostUpdateMessage() helper function for sending small messages to JS.
    814 void Planet::PostUpdateMessage(const char* message_name, double value) {
    815   pp::VarDictionary message;
    816   message.Set("message", message_name);
    817   message.Set("value", value);
    818   PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
    819 }
    820 
    821 void Planet::Update() {
    822   // When benchmarking is running, don't update display via
    823   // PSContext2DSwapBuffer() - vsync is enabled by default, and will throttle
    824   // the benchmark results.
    825   PSContext2DGetBuffer(ps_context_);
    826   if (NULL == ps_context_->data)
    827     return;
    828 
    829   do {
    830     UpdateSim();
    831     Render();
    832     if (!benchmarking_) break;
    833     --benchmark_frame_counter_;
    834   } while (benchmark_frame_counter_ > 0);
    835   if (benchmarking_)
    836     EndBenchmark();
    837 
    838   PSContext2DSwapBuffer(ps_context_);
    839 }
    840 
    841 
    842 // Starting point for the module.  We do not use main since it would
    843 // collide with main in libppapi_cpp.
    844 int example_main(int argc, char* argv[]) {
    845   Planet earth;
    846   while (true) {
    847     PSEvent* ps_event;
    848     // Consume all available events
    849     while ((ps_event = PSEventTryAcquire()) != NULL) {
    850       earth.HandleEvent(ps_event);
    851       PSEventRelease(ps_event);
    852     }
    853     // Do simulation, render and present.
    854     earth.Update();
    855   }
    856 
    857   return 0;
    858 }
    859 
    860 // Register the function to call once the Instance Object is initialized.
    861 // see: pappi_simple/ps_main.h
    862 PPAPI_SIMPLE_REGISTER_MAIN(example_main);
    863