diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2013-06-30 21:44:01 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2013-06-30 23:45:34 +0100 |
commit | 7e89dc41c2a866025c219a109e6ffa0d1e622ad9 (patch) | |
tree | 4e57742b78d723affdea353813caef4b974e914a | |
parent | 482330fb4a7ce5e4541a3741e0f014d4937f81fa (diff) |
Add a new demo similar to Bubbles!!!
In the spirit of
http://ie.microsoft.com/testdrive/Performance/Bubbles/Default.html
this demo animates a number of sprites bouncing off the walls and each
other. When a pair of bubbles collide, part of the background is
revealed.
The original Microsoft demo computes the blur on the CPU (presumably to
highlight a weakness in chrome and firefox), this demo instead performs
all pixel manipulation on the GPU.
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | bubble-demo.c | 377 | ||||
-rw-r--r-- | bubble.png | bin | 0 -> 2726 bytes | |||
-rw-r--r-- | bubblebg.png | bin | 0 -> 1417623 bytes |
4 files changed, 380 insertions, 1 deletions
@@ -56,7 +56,7 @@ else DEFINES+=-DHAVE_SKIA=0 endif -all: spiral-demo tiger-demo fish-demo flowers-demo gears-demo gradient-demo chart-demo waterfall-demo dragon-demo pythagoras-demo wave-demo sierpinski-demo maze-demo +all: spiral-demo tiger-demo fish-demo flowers-demo gears-demo gradient-demo chart-demo waterfall-demo dragon-demo pythagoras-demo wave-demo sierpinski-demo maze-demo bubble-demo ifeq ($(shell pkg-config --exists poppler-glib && echo 1), 1) all: poppler-demo @@ -86,6 +86,8 @@ fish-demo: fish-demo.c $(SOURCES) demo.h Makefile $(CC) $(DEFINES) $(CFLAGS) -o $@ fish-demo.c $(SOURCES) $(LIBS) fish2-demo: fish2-demo.c $(SOURCES) demo.h Makefile $(CC) $(DEFINES) $(CFLAGS) -o $@ fish2-demo.c $(SOURCES) $(LIBS) +bubble-demo: bubble-demo.c $(SOURCES) demo.h Makefile + $(CC) $(DEFINES) $(CFLAGS) -o $@ bubble-demo.c $(SOURCES) $(LIBS) flowers-demo: flowers-demo.c $(SOURCES) demo.h Makefile $(CC) $(DEFINES) $(CFLAGS) -o $@ flowers-demo.c $(SOURCES) $(LIBS) gears-demo: gears-demo.c $(SOURCES) demo.h Makefile diff --git a/bubble-demo.c b/bubble-demo.c new file mode 100644 index 0000000..6eaac59 --- /dev/null +++ b/bubble-demo.c @@ -0,0 +1,377 @@ +#include <cairo.h> +#include <sys/time.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <unistd.h> +#include <string.h> +#include <math.h> + +#include "demo.h" + +struct surface { + cairo_surface_t *surface; + cairo_pattern_t *pattern; + int width, height; + double sf_x, sf_y; +}; + +struct bubble { + double x, y; + double dx, dy; + double scale, radius; + struct surface *fg; +}; + +static int create_background(struct device *device, struct surface *s) +{ + cairo_surface_t *image; + cairo_matrix_t m; + cairo_t *cr; + + image = surface_create_from_png(device->scanout, "bubblebg.png"); + s->height = cairo_image_surface_get_height(image); + s->width = cairo_image_surface_get_width(image); + + s->surface = cairo_surface_create_similar(device->scanout, + CAIRO_CONTENT_COLOR, + s->width, s->height); + cr = cairo_create(s->surface); + cairo_surface_destroy(s->surface); + cairo_set_source_surface(cr, image, 0, 0); + cairo_surface_destroy(image); + cairo_paint(cr); + + s->surface = cairo_surface_reference(cairo_get_target(cr)); + cairo_destroy (cr); + + s->sf_x = s->width / (double)device->width; + s->sf_y = s->height / (double)device->height; + cairo_matrix_init_scale(&m, s->sf_x, s->sf_y); + s->pattern = cairo_pattern_create_for_surface(s->surface); + cairo_pattern_set_matrix(s->pattern, &m); + + return cairo_pattern_status(s->pattern) == CAIRO_STATUS_SUCCESS; +} + +static int create_gauss(struct device *device, struct surface *s, double radius) +{ + cairo_surface_t *image, *surface; + cairo_t *cr; + int size, i, j, stride; + uint8_t *pixel; + + size = 4*ceil(radius); + size |= 1; + + image = cairo_image_surface_create(CAIRO_FORMAT_A8, size, size); + if (cairo_surface_status(image)) + return 0; + + pixel = (uint8_t *)cairo_image_surface_get_data(image); + stride = cairo_image_surface_get_stride(image) / sizeof(uint8_t); + cairo_surface_flush(image); + for (j = 0; j < size; j++) { + double dy = j - size / 2.; + for (i = 0; i < size; i++) { + double dx = i - size / 2.; + pixel[j*stride + i] = 96*exp(-(dx*dx + dy*dy)/(radius*radius)); + } + } + cairo_surface_mark_dirty(image); + + surface = cairo_surface_create_similar(device->scanout, + CAIRO_CONTENT_ALPHA, + size, size); + cr = cairo_create(surface); + cairo_surface_destroy(surface); + + cairo_set_source_surface(cr, image, 0, 0); + cairo_surface_destroy(image); + cairo_paint(cr); + s->surface = cairo_surface_reference(cairo_get_target(cr)); + cairo_destroy (cr); + + s->pattern = cairo_pattern_create_for_surface(s->surface); + s->height = s->width = size; + s->sf_x = s->sf_y = 1.; + + return cairo_pattern_status(s->pattern) == CAIRO_STATUS_SUCCESS; +} + +static int create_mask(struct device *device, struct surface *s) +{ + s->width = device->width; + s->height = device->height; + s->surface = cairo_surface_create_similar(device->scanout, + CAIRO_CONTENT_ALPHA, + s->width, s->height); + s->pattern = cairo_pattern_create_for_surface(s->surface); + s->sf_x = s->sf_y = 1.; + + return cairo_pattern_status(s->pattern) == CAIRO_STATUS_SUCCESS; +} + +static int create_foreground(struct device *device, struct surface *s) +{ + cairo_surface_t *image; + cairo_t *cr; + + image = surface_create_from_png(device->scanout, "bubble.png"); + s->height = cairo_image_surface_get_height(image); + s->width = cairo_image_surface_get_width(image); + + s->surface = cairo_surface_create_similar(device->scanout, + CAIRO_CONTENT_COLOR_ALPHA, + s->width, s->height); + cr = cairo_create(s->surface); + cairo_set_source_surface(cr, image, 0, 0); + cairo_surface_destroy(image); + cairo_paint(cr); + cairo_destroy(cr); + + s->pattern = NULL; + s->sf_x = s->sf_y = 1.; + return 1; +} + +static int bubble_overlap(struct bubble *a, struct bubble *b) +{ + double dx = a->x - b->x; + double dy = a->y - b->y; + double r = a->radius + b->radius; + return dx * dx + dy * dy < r * r; +} + +static void bubble_init(struct device *device, + struct bubble *bubbles, int n, + struct surface *fg, double scale) +{ + struct bubble *b = &bubbles[n]; + int retry = 100; + int m; + + do { + b->dx = (random() % 5 + 1) / 3.; + if (random() % 2) + b->dx = -b->dx; + b->dy = (random() % 5 + 1) / 3.; + if (random() % 2) + b->dy = -b->dy; + + b->fg = fg; + b->scale = 100. * scale / (random() % 50 + 50); + b->radius = hypot(b->fg->width/2, b->fg->height/2) * b->scale / M_SQRT2; + b->dx /= b->scale / scale; + b->dy /= b->scale / scale; + + do { + b->x = random() % device->width; + b->y = random() % device->height; + } while (b->x < fg->width * b->scale/2 || + b->x > device->width - fg->width * b->scale/2 || + b->y < fg->height * b->scale/2 || + b->y > device->height - fg->height * b->scale/2); + + for (m = 0; m < n; m++) { + struct bubble *a = &bubbles[m]; + if (bubble_overlap(a,b)) + break; + } + } while (retry-- && m != n); +} + +static void bubble_draw(cairo_t *cr, struct bubble *f) +{ + double scale = f->scale; + cairo_save(cr); + cairo_scale(cr, scale, scale); + cairo_set_source_surface(cr, f->fg->surface, + (f->x-f->fg->width/2.)/scale, + (f->y-f->fg->height/2.)/scale); + cairo_paint(cr); + cairo_restore(cr); +} + +static void bubble_move(struct device *device, struct bubble *f) +{ + f->x += f->dx; + f->y += f->dy; + + if (f->x < f->fg->width*f->scale/2 || + f->x > device->width-f->fg->width*f->scale/2) { + f->dx = -f->dx; + f->x += f->dx; + } + + if (f->y < f->fg->height*f->scale/2 || + f->y > device->height-f->fg->height*f->scale/2) { + f->dy = -f->dy; + f->y += f->dy; + } +} + +static void bubble_collide(struct bubble *a, struct bubble *b, + cairo_t *cr, struct surface *o) +{ + double dx, dy, dvx, dvy, d2, r; + + r = a->radius + b->radius; + + dx = a->x - b->x; + dy = a->y - b->y; + d2 = dx * dx + dy * dy; + if (d2 > r*r) + return; + + r = (a->scale + b->scale) / 2.; + cairo_save(cr); + cairo_scale(cr, r, r); + cairo_set_source_surface(cr, o->surface, + (a->x + b->x - o->width)/(2 * r), + (a->y + b->y - o->height)/(2 * r)); + cairo_paint(cr); + cairo_restore(cr); + + dvx = a->dx - b->dx; + dvy = a->dy - b->dy; + + d2 = (dvx * dx + dvy * dy) / d2; + + a->dx -= dx * d2; + b->dx += dx * d2; + + a->dy -= dy * d2; + b->dy += dy * d2; +} + +static int done; +static void signal_handler(int sig) +{ + done = sig; +} + +int main(int argc, char **argv) +{ + struct device *device; + struct timeval start, last_tty, last_fps, now; + + struct surface fg, gauss, mask, bg; + struct bubble *bubble; + int num_bubbles = 100, n, m; + + double delta; + int frame = 0; + int benchmark; + const char *version; + enum clip clip; + + device = device_open(argc, argv); + version = device_show_version(argc, argv); + clip = device_get_clip(argc, argv); + benchmark = device_get_benchmark(argc, argv); + if (benchmark == 0) + benchmark = 20; + + signal(SIGHUP, signal_handler); + + for (n = 1; n < argc; n++) { + if (strncmp(argv[n], "--num-bubbles=", 14) == 0) + num_bubbles = atoi(argv[n]+14); + } + + if (!create_foreground(device, &fg)) + return 1; + + if (!create_background(device, &bg)) + return 1; + + if (!create_mask(device, &mask)) + return 1; + + if (!create_gauss(device, &gauss, hypot(fg.width, fg.height) / 5)) + return 1; + + delta = (device->width * device->height) / (num_bubbles * fg.width * fg.height * 25.); + + bubble = (struct bubble *)malloc(sizeof(*bubble)*num_bubbles); + for (n = 0; n < num_bubbles; n++) + bubble_init(device, bubble, n, &fg, delta); + + gettimeofday(&start, 0); now = last_tty = last_fps = start; + do { + struct framebuffer *fb = device->get_framebuffer (device); + cairo_t *cr; + + cr = cairo_create(fb->surface); + + device_apply_clip(device, cr, clip); + + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source(cr, bg.pattern); + cairo_mask(cr, mask.pattern); + + for (n = 0; n < num_bubbles; n++) + bubble_draw(cr, &bubble[n]); + + gettimeofday(&now, NULL); + if (benchmark < 0 && last_fps.tv_sec) { + cairo_reset_clip (cr); + fps_draw(cr, device->name, version, &last_fps, &now); + } + last_fps = now; + + cairo_destroy(cr); + + fb->show(fb); + fb->destroy(fb); + + for (n = 0; n < num_bubbles; n++) + bubble_move(device, &bubble[n]); + + cr = cairo_create(mask.surface); + cairo_set_operator(cr, CAIRO_OPERATOR_ADD); + for (n = 0; n < num_bubbles; n++) + for (m = n + 1; m < num_bubbles; m++) + bubble_collide(&bubble[n], &bubble[m], + cr, &gauss); + cairo_destroy(cr); + + frame++; + if (benchmark > 0) { + delta = now.tv_sec - start.tv_sec; + delta += (now.tv_usec - start.tv_usec)*1e-6; + if (delta > benchmark) { + printf("bubble: %.2f fps\n", frame / delta); + break; + } + } + } while (!done); + + if (benchmark < 0) { + struct framebuffer *fb = device->get_framebuffer (device); + cairo_t *cr = cairo_create(fb->surface); + + device_apply_clip(device, cr, clip); + + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source(cr, bg.pattern); + cairo_mask(cr, mask.pattern); + + for (n = 0; n < num_bubbles; n++) + bubble_draw(cr, &bubble[n]); + cairo_destroy(cr); + + fps_finish(fb, device->name, version); + fb->show (fb); + fb->destroy (fb); + pause(); + } + + return 0; +} diff --git a/bubble.png b/bubble.png Binary files differnew file mode 100644 index 0000000..2c6cf39 --- /dev/null +++ b/bubble.png diff --git a/bubblebg.png b/bubblebg.png Binary files differnew file mode 100644 index 0000000..475f19c --- /dev/null +++ b/bubblebg.png |