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
30using namespace sdk_util;  // For sdk_util::ThreadPool
31
32// Global properties used to setup Voronoi demo.
33namespace {
34const int kMinRectSize = 4;
35const int kStartRecurseSize = 32;  // must be power-of-two
36const float kHugeZ = 1.0e38f;
37const float kPI = M_PI;
38const float kTwoPI = kPI * 2.0f;
39const unsigned int kRandomStartSeed = 0xC0DE533D;
40const int kMaxPointCount = 1024;
41const int kStartPointCount = 48;
42const int kDefaultNumRegions = 256;
43
44unsigned int g_rand_state = kRandomStartSeed;
45
46// random number helper
47inline unsigned char rand255() {
48  return static_cast<unsigned char>(rand_r(&g_rand_state) & 255);
49}
50
51// random number helper
52inline float frand() {
53  return (static_cast<float>(rand_r(&g_rand_state)) /
54      static_cast<float>(RAND_MAX));
55}
56
57// reset random seed
58inline void rand_reset(unsigned int seed) {
59  g_rand_state = seed;
60}
61
62inline 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.
74inline 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
80struct 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.
94class 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
144void 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
162Voronoi::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
172Voronoi::~Voronoi() {
173  delete workers_;
174  PSContext2DFree(ps_context_);
175}
176
177inline 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.
187int 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.
207void 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.
226bool 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.
242inline 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.
254void 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.
269void 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.
302void 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.
325void 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.
344void 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.
354void 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.
362void 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
378void 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.
397void 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.
407void 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.
414void 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.
463void 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
470void 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.
486int 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
504PPAPI_SIMPLE_REGISTER_MAIN(example_main);
505