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.
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>
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"
30 using namespace sdk_util
; // For sdk_util::ThreadPool
32 // Global properties used to setup Voronoi demo.
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 int kFramesToBenchmark
= 100;
40 const unsigned int kRandomStartSeed
= 0xC0DE533D;
41 const int kMaxPointCount
= 1024;
42 const int kStartPointCount
= 48;
43 const int kDefaultNumRegions
= 256;
45 unsigned int g_rand_state
= kRandomStartSeed
;
47 // random number helper
48 inline unsigned char rand255() {
49 return static_cast<unsigned char>(rand_r(&g_rand_state
) & 255);
52 // random number helper
53 inline float frand() {
54 return (static_cast<float>(rand_r(&g_rand_state
)) /
55 static_cast<float>(RAND_MAX
));
59 inline void rand_reset(unsigned int seed
) {
64 // returns true if input is power of two.
65 // only used in assertions.
66 inline bool is_pow2(int x
) {
67 return (x
& (x
- 1)) == 0;
71 inline double getseconds() {
72 const double usec_to_sec
= 0.000001;
74 if (0 == gettimeofday(&tv
, NULL
))
75 return tv
.tv_sec
+ tv
.tv_usec
* usec_to_sec
;
79 // BGRA helper function, for constructing a pixel for a BGRA buffer.
80 inline uint32_t MakeBGRA(uint32_t b
, uint32_t g
, uint32_t r
, uint32_t a
) {
81 return (((a
) << 24) | ((r
) << 16) | ((g
) << 8) | (b
));
85 // Vec2, simple 2D vector
89 Vec2(float px
, float py
) {
93 void Set(float px
, float py
) {
99 // The main object that runs Voronoi simulation.
104 // Runs a tick of the simulations, update 2D output.
106 // Handle event from user, or message from JS.
107 void HandleEvent(PSEvent
* ps_event
);
110 // Methods prefixed with 'w' are run on worker threads.
111 uint32_t* wGetAddr(int x
, int y
);
112 int wCell(float x
, float y
);
113 inline void wFillSpan(uint32_t *pixels
, uint32_t color
, int width
);
114 void wRenderTile(int x
, int y
, int w
, int h
);
115 void wProcessTile(int x
, int y
, int w
, int h
);
116 void wSubdivide(int x
, int y
, int w
, int h
);
117 void wMakeRect(int region
, int *x
, int *y
, int *w
, int *h
);
118 bool wTestRect(int *m
, int x
, int y
, int w
, int h
);
119 void wFillRect(int x
, int y
, int w
, int h
, uint32_t color
);
120 void wRenderRect(int x0
, int y0
, int x1
, int y1
);
121 void wRenderRegion(int region
);
122 static void wRenderRegionEntry(int region
, void *thiz
);
124 // These methods are only called by the main thread.
127 void RenderDot(float x
, float y
, uint32_t color1
, uint32_t color2
);
128 void SuperimposePositions();
131 void StartBenchmark();
133 // Helper to post small update messages to JS.
134 void PostUpdateMessage(const char* message_name
, double value
);
136 PSContext2D_t
* ps_context_
;
137 Vec2 positions_
[kMaxPointCount
];
138 Vec2 screen_positions_
[kMaxPointCount
];
139 Vec2 velocities_
[kMaxPointCount
];
140 uint32_t colors_
[kMaxPointCount
];
142 const int num_regions_
;
146 bool draw_interiors_
;
147 ThreadPool
* workers_
;
148 int benchmark_frame_counter_
;
150 double benchmark_start_time_
;
151 double benchmark_end_time_
;
155 void Voronoi::Reset() {
156 rand_reset(kRandomStartSeed
);
158 for (int i
= 0; i
< kMaxPointCount
; i
++) {
159 // random initial start position
160 const float x
= frand();
161 const float y
= frand();
162 positions_
[i
].Set(x
, y
);
163 // random directional velocity ( -1..1, -1..1 )
164 const float speed
= 0.0005f
;
165 const float u
= (frand() * 2.0f
- 1.0f
) * speed
;
166 const float v
= (frand() * 2.0f
- 1.0f
) * speed
;
167 velocities_
[i
].Set(u
, v
);
168 // 'unique' color (well... unique enough for our purposes)
169 colors_
[i
] = MakeBGRA(rand255(), rand255(), rand255(), 255);
173 Voronoi::Voronoi() : num_regions_(kDefaultNumRegions
), num_threads_(0),
174 point_count_(kStartPointCount
), draw_points_(true), draw_interiors_(true),
175 benchmark_frame_counter_(0), benchmarking_(false) {
177 // By default, render from the dispatch thread.
178 workers_
= new ThreadPool(num_threads_
);
179 PSEventSetFilter(PSE_ALL
);
180 ps_context_
= PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL
);
183 Voronoi::~Voronoi() {
185 PSContext2DFree(ps_context_
);
188 inline uint32_t* Voronoi::wGetAddr(int x
, int y
) {
189 return ps_context_
->data
+ x
+ y
* ps_context_
->stride
/ sizeof(uint32_t);
192 // This is the core of the Voronoi calculation. At a given point on the
193 // screen, iterate through all voronoi positions and render them as 3D cones.
194 // We're looking for the voronoi cell that generates the closest z value.
195 // (not really cones - since it is all relative, we avoid doing the
196 // expensive sqrt and test against z*z instead)
197 // If multithreading, this function is only called by the worker threads.
198 int Voronoi::wCell(float x
, float y
) {
199 int closest_cell
= 0;
201 Vec2
* pos
= screen_positions_
;
202 for (int i
= 0; i
< point_count_
; ++i
) {
203 // measured 5.18 cycles per iteration on a core2
204 float dx
= x
- pos
[i
].x
;
205 float dy
= y
- pos
[i
].y
;
206 float dd
= (dx
* dx
+ dy
* dy
);
215 // Given a region r, derive a non-overlapping rectangle for a thread to
217 // If multithreading, this function is only called by the worker threads.
218 void Voronoi::wMakeRect(int r
, int* x
, int* y
, int* w
, int* h
) {
219 const int parts
= 16;
220 assert(parts
* parts
== num_regions_
);
221 *w
= ps_context_
->width
/ parts
;
222 *h
= ps_context_
->height
/ parts
;
223 *x
= *w
* (r
% parts
);
224 *y
= *h
* ((r
/ parts
) % parts
);
227 // Test 4 corners of a rectangle to see if they all belong to the same
228 // voronoi cell. Each test is expensive so bail asap. Returns true
229 // if all 4 corners match.
230 // If multithreading, this function is only called by the worker threads.
231 bool Voronoi::wTestRect(int* m
, int x
, int y
, int w
, int h
) {
232 // each test is expensive, so exit ASAP
233 const int m0
= wCell(x
, y
);
234 const int m1
= wCell(x
+ w
- 1, y
);
235 if (m0
!= m1
) return false;
236 const int m2
= wCell(x
, y
+ h
- 1);
237 if (m0
!= m2
) return false;
238 const int m3
= wCell(x
+ w
- 1, y
+ h
- 1);
239 if (m0
!= m3
) return false;
240 // all 4 corners belong to the same cell
245 // Quickly fill a span of pixels with a solid color. Assumes
246 // span width is divisible by 4.
247 // If multithreading, this function is only called by the worker threads.
248 inline void Voronoi::wFillSpan(uint32_t* pixels
, uint32_t color
, int width
) {
249 if (!draw_interiors_
) {
250 const uint32_t gray
= MakeBGRA(128, 128, 128, 255);
253 for (int i
= 0; i
< width
; i
+= 4) {
261 // Quickly fill a rectangle with a solid color. Assumes
262 // the width w parameter is evenly divisible by 4.
263 // If multithreading, this function is only called by the worker threads.
264 void Voronoi::wFillRect(int x
, int y
, int w
, int h
, uint32_t color
) {
265 const uint32_t stride_in_pixels
= ps_context_
->stride
/ sizeof(uint32_t);
266 uint32_t* pixels
= wGetAddr(x
, y
);
267 for (int j
= 0; j
< h
; j
++) {
268 wFillSpan(pixels
, color
, w
);
269 pixels
+= stride_in_pixels
;
273 // When recursive subdivision reaches a certain minimum without finding a
274 // rectangle that has four matching corners belonging to the same voronoi
275 // cell, this function will break the retangular 'tile' into smaller scanlines
276 // and look for opportunities to quick fill at the scanline level. If the
277 // scanline can't be quick filled, it will slow down even further and compute
278 // voronoi membership per pixel.
279 void Voronoi::wRenderTile(int x
, int y
, int w
, int h
) {
280 // rip through a tile
281 const uint32_t stride_in_pixels
= ps_context_
->stride
/ sizeof(uint32_t);
282 uint32_t* pixels
= wGetAddr(x
, y
);
283 for (int j
= 0; j
< h
; j
++) {
284 // get start and end cell values
285 int ms
= wCell(x
+ 0, y
+ j
);
286 int me
= wCell(x
+ w
- 1, y
+ j
);
287 // if the end points are the same, quick fill the span
289 wFillSpan(pixels
, colors_
[ms
], w
);
291 // else compute each pixel in the span... this is the slow part!
292 uint32_t* p
= pixels
;
294 for (int i
= 1; i
< (w
- 1); i
++) {
295 int m
= wCell(x
+ i
, y
+ j
);
300 pixels
+= stride_in_pixels
;
304 // Take a rectangular region and do one of -
305 // If all four corners below to the same voronoi cell, stop recursion and
306 // quick fill the rectangle.
307 // If the minimum rectangle size has been reached, break out of recursion
308 // and process the rectangle. This small rectangle is called a tile.
309 // Otherwise, keep recursively subdividing the rectangle into 4 equally
310 // sized smaller rectangles.
311 // Note: at the moment, these will always be squares, not rectangles.
312 // If multithreading, this function is only called by the worker threads.
313 void Voronoi::wSubdivide(int x
, int y
, int w
, int h
) {
315 // if all 4 corners are equal, quick fill interior
316 if (wTestRect(&m
, x
, y
, w
, h
)) {
317 wFillRect(x
, y
, w
, h
, colors_
[m
]);
319 // did we reach the minimum rectangle size?
320 if ((w
<= kMinRectSize
) || (h
<= kMinRectSize
)) {
321 wRenderTile(x
, y
, w
, h
);
323 // else recurse into smaller rectangles
324 const int half_w
= w
/ 2;
325 const int half_h
= h
/ 2;
326 wSubdivide(x
, y
, half_w
, half_h
);
327 wSubdivide(x
+ half_w
, y
, half_w
, half_h
);
328 wSubdivide(x
, y
+ half_h
, half_w
, half_h
);
329 wSubdivide(x
+ half_w
, y
+ half_h
, half_w
, half_h
);
334 // This function cuts up the rectangle into power of 2 sized squares. It
335 // assumes the input rectangle w & h are evenly divisible by
336 // kStartRecurseSize.
337 // If multithreading, this function is only called by the worker threads.
338 void Voronoi::wRenderRect(int x
, int y
, int w
, int h
) {
339 for (int iy
= y
; iy
< (y
+ h
); iy
+= kStartRecurseSize
) {
340 for (int ix
= x
; ix
< (x
+ w
); ix
+= kStartRecurseSize
) {
341 wSubdivide(ix
, iy
, kStartRecurseSize
, kStartRecurseSize
);
346 // If multithreading, this function is only called by the worker threads.
347 void Voronoi::wRenderRegion(int region
) {
348 // convert region # into x0, y0, x1, y1 rectangle
350 wMakeRect(region
, &x
, &y
, &w
, &h
);
351 // render this rectangle
352 wRenderRect(x
, y
, w
, h
);
355 // Entry point for worker thread. Can't pass a member function around, so we
356 // have to do this little round-about.
357 void Voronoi::wRenderRegionEntry(int region
, void* thiz
) {
358 static_cast<Voronoi
*>(thiz
)->wRenderRegion(region
);
361 // Function Voronoi::UpdateSim()
362 // Run a simple sim to move the voronoi positions. This update loop
363 // is run once per frame. Called from the main thread only, and only
364 // when the worker threads are idle.
365 void Voronoi::UpdateSim() {
368 ang_
= ang_
- kTwoPI
;
370 float z
= cosf(ang_
) * 3.0f
;
371 // push the points around on the screen for animation
372 for (int j
= 0; j
< kMaxPointCount
; j
++) {
373 positions_
[j
].x
+= (velocities_
[j
].x
) * z
;
374 positions_
[j
].y
+= (velocities_
[j
].y
) * z
;
375 screen_positions_
[j
].x
= positions_
[j
].x
* ps_context_
->width
;
376 screen_positions_
[j
].y
= positions_
[j
].y
* ps_context_
->height
;
380 // Renders a small diamond shaped dot at x, y clipped against the window
381 void Voronoi::RenderDot(float x
, float y
, uint32_t color1
, uint32_t color2
) {
382 const int ix
= static_cast<int>(x
);
383 const int iy
= static_cast<int>(y
);
384 const uint32_t stride_in_pixels
= ps_context_
->stride
/ sizeof(uint32_t);
385 // clip it against window
387 if (ix
>= (ps_context_
->width
- 1)) return;
389 if (iy
>= (ps_context_
->height
- 1)) return;
390 uint32_t* pixel
= wGetAddr(ix
, iy
);
391 // render dot as a small diamond
393 *(pixel
- 1) = color2
;
394 *(pixel
+ 1) = color2
;
395 *(pixel
- stride_in_pixels
) = color2
;
396 *(pixel
+ stride_in_pixels
) = color2
;
399 // Superimposes dots on the positions.
400 void Voronoi::SuperimposePositions() {
401 const uint32_t white
= MakeBGRA(255, 255, 255, 255);
402 const uint32_t gray
= MakeBGRA(192, 192, 192, 255);
403 for (int i
= 0; i
< point_count_
; i
++) {
405 screen_positions_
[i
].x
, screen_positions_
[i
].y
, white
, gray
);
409 // Renders the Voronoi diagram, dispatching the work to multiple threads.
410 void Voronoi::Render() {
411 workers_
->Dispatch(num_regions_
, wRenderRegionEntry
, this);
413 SuperimposePositions();
416 void Voronoi::StartBenchmark() {
418 printf("Benchmark started...\n");
419 benchmark_frame_counter_
= kFramesToBenchmark
;
420 benchmarking_
= true;
421 benchmark_start_time_
= getseconds();
424 void Voronoi::EndBenchmark() {
425 benchmark_end_time_
= getseconds();
426 printf("Benchmark ended... time: %2.5f\n",
427 benchmark_end_time_
- benchmark_start_time_
);
428 benchmarking_
= false;
429 benchmark_frame_counter_
= 0;
430 double result
= benchmark_end_time_
- benchmark_start_time_
;
431 PostUpdateMessage("benchmark_result", result
);
434 // Handle input events from the user and messages from JS.
435 void Voronoi::HandleEvent(PSEvent
* ps_event
) {
436 // Give the 2D context a chance to process the event.
437 if (0 != PSContext2DHandleEvent(ps_context_
, ps_event
))
439 if (ps_event
->type
== PSE_INSTANCE_HANDLEINPUT
) {
440 // Convert Pepper Simple event to a PPAPI C++ event
441 pp::InputEvent
event(ps_event
->as_resource
);
442 switch (event
.GetType()) {
443 case PP_INPUTEVENT_TYPE_KEYDOWN
: {
444 pp::KeyboardInputEvent key
= pp::KeyboardInputEvent(event
);
445 uint32_t key_code
= key
.GetKeyCode();
446 if (key_code
== 84) // 't' key
451 case PP_INPUTEVENT_TYPE_TOUCHSTART
:
452 case PP_INPUTEVENT_TYPE_TOUCHMOVE
: {
453 pp::TouchInputEvent touches
= pp::TouchInputEvent(event
);
454 uint32_t count
= touches
.GetTouchCount(PP_TOUCHLIST_TYPE_TOUCHES
);
455 // Touch points 0..n directly set position of points 0..n in
457 for (uint32_t i
= 0; i
< count
; i
++) {
458 pp::TouchPoint touch
=
459 touches
.GetTouchByIndex(PP_TOUCHLIST_TYPE_TOUCHES
, i
);
460 pp::FloatPoint point
= touch
.position();
461 positions_
[i
].Set(point
.x() / ps_context_
->width
,
462 point
.y() / ps_context_
->height
);
469 } else if (ps_event
->type
== PSE_INSTANCE_HANDLEMESSAGE
) {
470 // Convert Pepper Simple message to PPAPI C++ var
471 pp::Var
var(ps_event
->as_var
);
472 if (var
.is_dictionary()) {
473 pp::VarDictionary
dictionary(var
);
474 std::string message
= dictionary
.Get("message").AsString();
475 if (message
== "run_benchmark" && !benchmarking_
)
477 else if (message
== "draw_points")
478 draw_points_
= dictionary
.Get("value").AsBool();
479 else if (message
== "draw_interiors")
480 draw_interiors_
= dictionary
.Get("value").AsBool();
481 else if (message
== "set_points") {
482 int num_points
= dictionary
.Get("value").AsInt();
483 point_count_
= std::min(kMaxPointCount
, std::max(0, num_points
));
484 } else if (message
== "set_threads") {
485 int thread_count
= dictionary
.Get("value").AsInt();
487 workers_
= new ThreadPool(thread_count
);
493 // PostUpdateMessage() helper function for sendimg small messages to JS.
494 void Voronoi::PostUpdateMessage(const char* message_name
, double value
) {
495 pp::VarDictionary message
;
496 message
.Set("message", message_name
);
497 message
.Set("value", value
);
498 PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message
.pp_var());
501 void Voronoi::Update() {
502 PSContext2DGetBuffer(ps_context_
);
503 if (NULL
== ps_context_
->data
)
505 assert(is_pow2(ps_context_
->width
));
506 assert(is_pow2(ps_context_
->height
));
508 // When benchmarking is running, don't update display via
509 // PSContext2DSwapBuffer() - vsync is enabled by default, and will throttle
510 // the benchmark results.
514 if (!benchmarking_
) break;
515 --benchmark_frame_counter_
;
516 } while (benchmark_frame_counter_
> 0);
520 PSContext2DSwapBuffer(ps_context_
);
523 // Starting point for the module. We do not use main since it would
524 // collide with main in libppapi_cpp.
525 int example_main(int argc
, char* argv
[]) {
529 // Consume all available events.
530 while ((ps_event
= PSEventTryAcquire()) != NULL
) {
531 voronoi
.HandleEvent(ps_event
);
532 PSEventRelease(ps_event
);
534 // Do simulation, render and present.
541 // Register the function to call once the Instance Object is initialized.
542 // see: pappi_simple/ps_main.h
543 PPAPI_SIMPLE_REGISTER_MAIN(example_main
);