Home | History | Annotate | Download | only in voronoi
      1 // Copyright (c) 2013 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 <unistd.h>
     18 
     19 #include <algorithm>
     20 #include <string>
     21 
     22 #include "common/fps.h"
     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/thread_pool.h"
     29 
     30 using namespace sdk_util;  // For sdk_util::ThreadPool
     31 
     32 // Global properties used to setup Voronoi demo.
     33 namespace {
     34 const int kMinRectSize = 4;
     35 const int kStartRecurseSize = 32;  // must be power-of-two
     36 const float kHugeZ = 1.0e38f;
     37 const float kPI = M_PI;
     38 const float kTwoPI = kPI * 2.0f;
     39 const unsigned int kRandomStartSeed = 0xC0DE533D;
     40 const int kMaxPointCount = 1024;
     41 const int kStartPointCount = 48;
     42 const int kDefaultNumRegions = 256;
     43 
     44 unsigned int g_rand_state = kRandomStartSeed;
     45 
     46 // random number helper
     47 inline unsigned char rand255() {
     48   return static_cast<unsigned char>(rand_r(&g_rand_state) & 255);
     49 }
     50 
     51 // random number helper
     52 inline float frand() {
     53   return (static_cast<float>(rand_r(&g_rand_state)) /
     54       static_cast<float>(RAND_MAX));
     55 }
     56 
     57 // reset random seed
     58 inline void rand_reset(unsigned int seed) {
     59   g_rand_state = seed;
     60 }
     61 
     62 inline uint32_t next_pow2(uint32_t x) {
     63   // Via Hacker's Delight, section 3.2 "Rounding Up/Down to the Next Power of 2"
     64   --x;
     65   x |= (x >> 1);
     66   x |= (x >> 2);
     67   x |= (x >> 4);
     68   x |= (x >> 8);
     69   x |= (x >> 16);
     70   return x + 1;
     71 }
     72 
     73 // BGRA helper function, for constructing a pixel for a BGRA buffer.
     74 inline uint32_t MakeBGRA(uint32_t b, uint32_t g, uint32_t r, uint32_t a) {
     75   return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b));
     76 }
     77 }  // namespace
     78 
     79 // Vec2, simple 2D vector
     80 struct Vec2 {
     81   float x, y;
     82   Vec2() {}
     83   Vec2(float px, float py) {
     84     x = px;
     85     y = py;
     86   }
     87   void Set(float px, float py) {
     88     x = px;
     89     y = py;
     90   }
     91 };
     92 
     93 // The main object that runs Voronoi simulation.
     94 class Voronoi {
     95  public:
     96   Voronoi();
     97   virtual ~Voronoi();
     98   // Runs a tick of the simulations, update 2D output.
     99   void Update();
    100   // Handle event from user, or message from JS.
    101   void HandleEvent(PSEvent* ps_event);
    102 
    103  private:
    104   // Methods prefixed with 'w' are run on worker threads.
    105   uint32_t* wGetAddr(int x, int y);
    106   int wCell(float x, float y);
    107   inline void wFillSpan(uint32_t *pixels, uint32_t color, int width);
    108   void wRenderTile(int x, int y, int w, int h);
    109   void wProcessTile(int x, int y, int w, int h);
    110   void wSubdivide(int x, int y, int w, int h);
    111   void wMakeRect(int region, int *x, int *y, int *w, int *h);
    112   bool wTestRect(int *m, int x, int y, int w, int h);
    113   void wFillRect(int x, int y, int w, int h, uint32_t color);
    114   void wRenderRect(int x0, int y0, int x1, int y1);
    115   void wRenderRegion(int region);
    116   static void wRenderRegionEntry(int region, void *thiz);
    117 
    118   // These methods are only called by the main thread.
    119   void Reset();
    120   void UpdateSim();
    121   void RenderDot(float x, float y, uint32_t color1, uint32_t color2);
    122   void SuperimposePositions();
    123   void Render();
    124   void Draw();
    125   // Helper to post small update messages to JS.
    126   void PostUpdateMessage(const char* message_name, double value);
    127 
    128   PSContext2D_t* ps_context_;
    129   Vec2 positions_[kMaxPointCount];
    130   Vec2 screen_positions_[kMaxPointCount];
    131   Vec2 velocities_[kMaxPointCount];
    132   uint32_t colors_[kMaxPointCount];
    133   float ang_;
    134   const int num_regions_;
    135   int num_threads_;
    136   int point_count_;
    137   bool draw_points_;
    138   bool draw_interiors_;
    139   ThreadPool* workers_;
    140   FpsState fps_state_;
    141 };
    142 
    143 
    144 void Voronoi::Reset() {
    145   rand_reset(kRandomStartSeed);
    146   ang_ = 0.0f;
    147   for (int i = 0; i < kMaxPointCount; i++) {
    148     // random initial start position
    149     const float x = frand();
    150     const float y = frand();
    151     positions_[i].Set(x, y);
    152     // random directional velocity ( -1..1, -1..1 )
    153     const float speed = 0.0005f;
    154     const float u = (frand() * 2.0f - 1.0f) * speed;
    155     const float v = (frand() * 2.0f - 1.0f) * speed;
    156     velocities_[i].Set(u, v);
    157     // 'unique' color (well... unique enough for our purposes)
    158     colors_[i] = MakeBGRA(rand255(), rand255(), rand255(), 255);
    159   }
    160 }
    161 
    162 Voronoi::Voronoi() : num_regions_(kDefaultNumRegions), num_threads_(0),
    163     point_count_(kStartPointCount), draw_points_(true), draw_interiors_(true) {
    164   Reset();
    165   // By default, render from the dispatch thread.
    166   workers_ = new ThreadPool(num_threads_);
    167   PSEventSetFilter(PSE_ALL);
    168   ps_context_ = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL);
    169   FpsInit(&fps_state_);
    170 }
    171 
    172 Voronoi::~Voronoi() {
    173   delete workers_;
    174   PSContext2DFree(ps_context_);
    175 }
    176 
    177 inline uint32_t* Voronoi::wGetAddr(int x, int y) {
    178   return ps_context_->data + x + y * ps_context_->stride / sizeof(uint32_t);
    179 }
    180 
    181 // This is the core of the Voronoi calculation.  At a given point on the
    182 // screen, iterate through all voronoi positions and render them as 3D cones.
    183 // We're looking for the voronoi cell that generates the closest z value.
    184 // (not really cones - since it is all relative, we avoid doing the
    185 // expensive sqrt and test against z*z instead)
    186 // If multithreading, this function is only called by the worker threads.
    187 int Voronoi::wCell(float x, float y) {
    188   int closest_cell = 0;
    189   float zz = kHugeZ;
    190   Vec2* pos = screen_positions_;
    191   for (int i = 0; i < point_count_; ++i) {
    192     // measured 5.18 cycles per iteration on a core2
    193     float dx = x - pos[i].x;
    194     float dy = y - pos[i].y;
    195     float dd = (dx * dx + dy * dy);
    196     if (dd < zz) {
    197       zz = dd;
    198       closest_cell = i;
    199     }
    200   }
    201   return closest_cell;
    202 }
    203 
    204 // Given a region r, derive a non-overlapping rectangle for a thread to
    205 // work on.
    206 // If multithreading, this function is only called by the worker threads.
    207 void Voronoi::wMakeRect(int r, int* x, int* y, int* w, int* h) {
    208   const int parts = 16;
    209   assert(parts * parts == num_regions_);
    210   // Round up to the nearest power of two so we can divide by parts cleanly. We
    211   // could round up to the nearest 16 pixels, but it runs much faster when
    212   // subdividing power-of-two squares.
    213   //
    214   // Many of these squares are outside of the canvas, but they will be
    215   // trivially culled by wRenderRect.
    216   *w = static_cast<int>(next_pow2(ps_context_->width)) / parts;
    217   *h = static_cast<int>(next_pow2(ps_context_->height)) / parts;
    218   *x = *w * (r % parts);
    219   *y = *h * ((r / parts) % parts);
    220 }
    221 
    222 // Test 4 corners of a rectangle to see if they all belong to the same
    223 // voronoi cell.  Each test is expensive so bail asap.  Returns true
    224 // if all 4 corners match.
    225 // If multithreading, this function is only called by the worker threads.
    226 bool Voronoi::wTestRect(int* m, int x, int y, int w, int h) {
    227   // each test is expensive, so exit ASAP
    228   const int m0 = wCell(x, y);
    229   const int m1 = wCell(x + w - 1, y);
    230   if (m0 != m1) return false;
    231   const int m2 = wCell(x, y + h - 1);
    232   if (m0 != m2) return false;
    233   const int m3 = wCell(x + w - 1, y + h - 1);
    234   if (m0 != m3) return false;
    235   // all 4 corners belong to the same cell
    236   *m = m0;
    237   return true;
    238 }
    239 
    240 // Quickly fill a span of pixels with a solid color.
    241 // If multithreading, this function is only called by the worker threads.
    242 inline void Voronoi::wFillSpan(uint32_t* pixels, uint32_t color, int width) {
    243   if (!draw_interiors_) {
    244     const uint32_t gray = MakeBGRA(128, 128, 128, 255);
    245     color = gray;
    246   }
    247 
    248   for (int i = 0; i < width; ++i)
    249     *pixels++ = color;
    250 }
    251 
    252 // Quickly fill a rectangle with a solid color.
    253 // If multithreading, this function is only called by the worker threads.
    254 void Voronoi::wFillRect(int x, int y, int w, int h, uint32_t color) {
    255   const uint32_t stride_in_pixels = ps_context_->stride / sizeof(uint32_t);
    256   uint32_t* pixels = wGetAddr(x, y);
    257   for (int j = 0; j < h; j++) {
    258     wFillSpan(pixels, color, w);
    259     pixels += stride_in_pixels;
    260   }
    261 }
    262 
    263 // When recursive subdivision reaches a certain minimum without finding a
    264 // rectangle that has four matching corners belonging to the same voronoi
    265 // cell, this function will break the retangular 'tile' into smaller scanlines
    266 //  and look for opportunities to quick fill at the scanline level.  If the
    267 // scanline can't be quick filled, it will slow down even further and compute
    268 // voronoi membership per pixel.
    269 void Voronoi::wRenderTile(int x, int y, int w, int h) {
    270   // rip through a tile
    271   const uint32_t stride_in_pixels = ps_context_->stride / sizeof(uint32_t);
    272   uint32_t* pixels = wGetAddr(x, y);
    273   for (int j = 0; j < h; j++) {
    274     // get start and end cell values
    275     int ms = wCell(x + 0, y + j);
    276     int me = wCell(x + w - 1, y + j);
    277     // if the end points are the same, quick fill the span
    278     if (ms == me) {
    279       wFillSpan(pixels, colors_[ms], w);
    280     } else {
    281       // else compute each pixel in the span... this is the slow part!
    282       uint32_t* p = pixels;
    283       *p++ = colors_[ms];
    284       for (int i = 1; i < (w - 1); i++) {
    285         int m = wCell(x + i, y + j);
    286         *p++ = colors_[m];
    287       }
    288       *p++ = colors_[me];
    289     }
    290     pixels += stride_in_pixels;
    291   }
    292 }
    293 
    294 // Take a rectangular region and do one of -
    295 //   If all four corners below to the same voronoi cell, stop recursion and
    296 // quick fill the rectangle.
    297 //   If the minimum rectangle size has been reached, break out of recursion
    298 // and process the rectangle.  This small rectangle is called a tile.
    299 //   Otherwise, keep recursively subdividing the rectangle into 4 equally
    300 // sized smaller rectangles.
    301 // If multithreading, this function is only called by the worker threads.
    302 void Voronoi::wSubdivide(int x, int y, int w, int h) {
    303   int m;
    304   // if all 4 corners are equal, quick fill interior
    305   if (wTestRect(&m, x, y, w, h)) {
    306     wFillRect(x, y, w, h, colors_[m]);
    307   } else {
    308     // did we reach the minimum rectangle size?
    309     if ((w <= kMinRectSize) || (h <= kMinRectSize)) {
    310       wRenderTile(x, y, w, h);
    311     } else {
    312       // else recurse into smaller rectangles
    313       const int half_w = w / 2;
    314       const int half_h = h / 2;
    315       wSubdivide(x, y, half_w, half_h);
    316       wSubdivide(x + half_w, y, w - half_w, half_h);
    317       wSubdivide(x, y + half_h, half_w, h - half_h);
    318       wSubdivide(x + half_w, y + half_h, w - half_w, h - half_h);
    319     }
    320   }
    321 }
    322 
    323 // This function cuts up the rectangle into squares (preferably power-of-two).
    324 // If multithreading, this function is only called by the worker threads.
    325 void Voronoi::wRenderRect(int x, int y, int w, int h) {
    326   for (int iy = y; iy < (y + h); iy += kStartRecurseSize) {
    327     for (int ix = x; ix < (x + w); ix += kStartRecurseSize) {
    328       int iw = kStartRecurseSize;
    329       int ih = kStartRecurseSize;
    330       // Clamp width + height.
    331       if (ix + iw > ps_context_->width)
    332         iw = ps_context_->width - ix;
    333       if (iy + ih > ps_context_->height)
    334         ih = ps_context_->height - iy;
    335       if (iw <= 0 || ih <= 0)
    336         continue;
    337 
    338       wSubdivide(ix, iy, iw, ih);
    339     }
    340   }
    341 }
    342 
    343 // If multithreading, this function is only called by the worker threads.
    344 void Voronoi::wRenderRegion(int region) {
    345   // convert region # into x0, y0, x1, y1 rectangle
    346   int x, y, w, h;
    347   wMakeRect(region, &x, &y, &w, &h);
    348   // render this rectangle
    349   wRenderRect(x, y, w, h);
    350 }
    351 
    352 // Entry point for worker thread.  Can't pass a member function around, so we
    353 // have to do this little round-about.
    354 void Voronoi::wRenderRegionEntry(int region, void* thiz) {
    355   static_cast<Voronoi*>(thiz)->wRenderRegion(region);
    356 }
    357 
    358 // Function Voronoi::UpdateSim()
    359 // Run a simple sim to move the voronoi positions.  This update loop
    360 // is run once per frame.  Called from the main thread only, and only
    361 // when the worker threads are idle.
    362 void Voronoi::UpdateSim() {
    363   ang_ += 0.002f;
    364   if (ang_ > kTwoPI) {
    365     ang_ = ang_ - kTwoPI;
    366   }
    367   float z = cosf(ang_) * 3.0f;
    368   // push the points around on the screen for animation
    369   for (int j = 0; j < kMaxPointCount; j++) {
    370     positions_[j].x += (velocities_[j].x) * z;
    371     positions_[j].y += (velocities_[j].y) * z;
    372     screen_positions_[j].x = positions_[j].x * ps_context_->width;
    373     screen_positions_[j].y = positions_[j].y * ps_context_->height;
    374   }
    375 }
    376 
    377 // Renders a small diamond shaped dot at x, y clipped against the window
    378 void Voronoi::RenderDot(float x, float y, uint32_t color1, uint32_t color2) {
    379   const int ix = static_cast<int>(x);
    380   const int iy = static_cast<int>(y);
    381   const uint32_t stride_in_pixels = ps_context_->stride / sizeof(uint32_t);
    382   // clip it against window
    383   if (ix < 1) return;
    384   if (ix >= (ps_context_->width - 1)) return;
    385   if (iy < 1) return;
    386   if (iy >= (ps_context_->height - 1)) return;
    387   uint32_t* pixel = wGetAddr(ix, iy);
    388   // render dot as a small diamond
    389   *pixel = color1;
    390   *(pixel - 1) = color2;
    391   *(pixel + 1) = color2;
    392   *(pixel - stride_in_pixels) = color2;
    393   *(pixel + stride_in_pixels) = color2;
    394 }
    395 
    396 // Superimposes dots on the positions.
    397 void Voronoi::SuperimposePositions() {
    398   const uint32_t white = MakeBGRA(255, 255, 255, 255);
    399   const uint32_t gray = MakeBGRA(192, 192, 192, 255);
    400   for (int i = 0; i < point_count_; i++) {
    401     RenderDot(
    402         screen_positions_[i].x, screen_positions_[i].y, white, gray);
    403   }
    404 }
    405 
    406 // Renders the Voronoi diagram, dispatching the work to multiple threads.
    407 void Voronoi::Render() {
    408   workers_->Dispatch(num_regions_, wRenderRegionEntry, this);
    409   if (draw_points_)
    410     SuperimposePositions();
    411 }
    412 
    413 // Handle input events from the user and messages from JS.
    414 void Voronoi::HandleEvent(PSEvent* ps_event) {
    415   // Give the 2D context a chance to process the event.
    416   if (0 != PSContext2DHandleEvent(ps_context_, ps_event))
    417     return;
    418   if (ps_event->type == PSE_INSTANCE_HANDLEINPUT) {
    419     // Convert Pepper Simple event to a PPAPI C++ event
    420     pp::InputEvent event(ps_event->as_resource);
    421     switch (event.GetType()) {
    422       case PP_INPUTEVENT_TYPE_TOUCHSTART:
    423       case PP_INPUTEVENT_TYPE_TOUCHMOVE: {
    424         pp::TouchInputEvent touches = pp::TouchInputEvent(event);
    425         uint32_t count = touches.GetTouchCount(PP_TOUCHLIST_TYPE_TOUCHES);
    426         // Touch points 0..n directly set position of points 0..n in
    427         // Voronoi diagram.
    428         for (uint32_t i = 0; i < count; i++) {
    429           pp::TouchPoint touch =
    430               touches.GetTouchByIndex(PP_TOUCHLIST_TYPE_TOUCHES, i);
    431           pp::FloatPoint point = touch.position();
    432           positions_[i].Set(point.x() / ps_context_->width,
    433                             point.y() / ps_context_->height);
    434         }
    435         break;
    436       }
    437       default:
    438         break;
    439     }
    440   } else if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) {
    441     // Convert Pepper Simple message to PPAPI C++ var
    442     pp::Var var(ps_event->as_var);
    443     if (var.is_dictionary()) {
    444       pp::VarDictionary dictionary(var);
    445       std::string message = dictionary.Get("message").AsString();
    446       if (message == "draw_points")
    447         draw_points_ = dictionary.Get("value").AsBool();
    448       else if (message == "draw_interiors")
    449         draw_interiors_ = dictionary.Get("value").AsBool();
    450       else if (message == "set_points") {
    451         int num_points = dictionary.Get("value").AsInt();
    452         point_count_ = std::min(kMaxPointCount, std::max(0, num_points));
    453       } else if (message == "set_threads") {
    454         int thread_count = dictionary.Get("value").AsInt();
    455         delete workers_;
    456         workers_ = new ThreadPool(thread_count);
    457       }
    458     }
    459   }
    460 }
    461 
    462 // PostUpdateMessage() helper function for sendimg small messages to JS.
    463 void Voronoi::PostUpdateMessage(const char* message_name, double value) {
    464   pp::VarDictionary message;
    465   message.Set("message", message_name);
    466   message.Set("value", value);
    467   PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
    468 }
    469 
    470 void Voronoi::Update() {
    471   PSContext2DGetBuffer(ps_context_);
    472   if (NULL == ps_context_->data)
    473     return;
    474 
    475   UpdateSim();
    476   Render();
    477   PSContext2DSwapBuffer(ps_context_);
    478 
    479   double fps;
    480   if (FpsStep(&fps_state_, &fps))
    481     PostUpdateMessage("fps", fps);
    482 }
    483 
    484 // Starting point for the module.  We do not use main since it would
    485 // collide with main in libppapi_cpp.
    486 int example_main(int argc, char* argv[]) {
    487   Voronoi voronoi;
    488   while (true) {
    489     PSEvent* ps_event;
    490     // Consume all available events.
    491     while ((ps_event = PSEventTryAcquire()) != NULL) {
    492       voronoi.HandleEvent(ps_event);
    493       PSEventRelease(ps_event);
    494     }
    495     // Do simulation, render and present.
    496     voronoi.Update();
    497   }
    498 
    499   return 0;
    500 }
    501 
    502 // Register the function to call once the Instance Object is initialized.
    503 // see: pappi_simple/ps_main.h
    504 PPAPI_SIMPLE_REGISTER_MAIN(example_main);
    505