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