* massive structural rewrite. Split off code into several new modules (render, render_png, render_local) and updated new modules to use them.
* the beginnings of render_multi, really not done yet
committer: Tero Marttila <terom@fixme.fi>
--- a/Makefile Fri Jun 06 16:05:26 2008 +0300
+++ b/Makefile Fri Jun 06 18:35:46 2008 +0300
@@ -11,13 +11,15 @@
remote_pool.o: remote_pool.c remote_pool.h
render.o: render.c render.h
render_remote.o: render_remote.c render_remote.h
+render_png.o: render_png.c render_png.h
+render_local.o: render_local.c render_local.h
render_file.o: render_file.c
render_node.o: render_node.c
web_main.o: web_main.c
-render_file: render_file.o common.o render.o mandelbrot.o
-render_node: render_node.o common.o render.o mandelbrot.o
+render_file: render_file.o common.o render.o render_png.o render_local.o mandelbrot.o
+render_node: render_node.o common.o render.o render_png.o render_local.o mandelbrot.o
web_main: web_main.o common.o http.o render.o remote_node.o remote_pool.o render_remote.o
clean :
--- a/common.c Fri Jun 06 16:05:26 2008 +0300
+++ b/common.c Fri Jun 06 18:35:46 2008 +0300
@@ -54,6 +54,30 @@
exit(EXIT_FAILURE);
}
+void err_func (const char *func, const char *fmt, ...) {
+ va_list va;
+
+ fprintf(stderr, "%s: ", func);
+
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+
+ fprintf(stderr, "\n");
+}
+
+void perr_func (const char *func, const char *fmt, ...) {
+ va_list va;
+
+ fprintf(stderr, "%s: ", func);
+
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+
+ fprintf(stderr, ": %s\n", strerror(errno));
+}
+
int parse_hostport (char *hostport, char **host, char **port) {
char *c;
--- a/common.h Fri Jun 06 16:05:26 2008 +0300
+++ b/common.h Fri Jun 06 18:35:46 2008 +0300
@@ -18,6 +18,18 @@
// fprintf + newline + exit
void err_exit (const char *fmt, ...);
+// fprintf (__func__ + msg, ...)
+void err_func (const char *func, const char *fmt, ...);
+
+// fprintf (__func__ + msg + strerror, ...)
+void perr_func (const char *func, const char *fmt, ...);
+
+// error(func + colon + msg, ...) + goto error
+#define ERROR(msg) do { err_func(__func__, "%s", msg); goto error; } while (0)
+#define ERROR_FMT(fmt, ...) do { err_func(__func__, fmt, __VA_ARGS__); goto error; } while (0)
+#define PERROR(msg) do { perr_func(__func__, "%s", msg); goto error; } while (0)
+#define PERROR_FMT(fmt, ...) do { perr_func(__func__, fmt, __VA_ARGS__); goto error; } while (0)
+
/*
* Parse a host:port string.
*
--- a/mandelbrot.c Fri Jun 06 16:05:26 2008 +0300
+++ b/mandelbrot.c Fri Jun 06 18:35:46 2008 +0300
@@ -1,9 +1,7 @@
#include <stdlib.h>
#include <time.h>
-#include <png.h>
-
-#include "render.h"
+#include "render_internal.h"
#include "mandelbrot.h"
#include "common.h"
@@ -11,97 +9,17 @@
#define absdelta(a, b) (a>b ? a-b : b-a)
-void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
- struct render_ctx *ctx = (struct render_ctx *) png_get_io_ptr(png_ptr);
-
- if (ctx->io_write_fn)
- switch (ctx->io_write_fn(data, length, ctx->io_cb_arg)) {
- case RENDER_CB_ERR :
- ctx->io_error = 1;
- break;
-
- case RENDER_CB_OK :
- // great!
- break;
- }
-}
-
-void user_flush_data(png_structp png_ptr) {
- struct render_ctx *ctx = (struct render_ctx *) png_get_io_ptr(png_ptr);
-
- if (ctx->io_flush_fn)
- switch (ctx->io_flush_fn(ctx->io_cb_arg)) {
- case RENDER_CB_ERR :
- ctx->io_error = 1;
- break;
-
- case RENDER_CB_OK :
- // great!
- break;
- }
-}
-
-int mandelbrot_render (render_t *ctx) {
- // libpng handles
- png_structp png_ptr = NULL;
- png_infop info_ptr = NULL;
-
+int render_mandelbrot (struct render *ctx) {
// render algorithm vars
u_int32_t img_x, img_y;
double x0, y0, x, y, _x, _y, w_scale, h_scale;
u_int8_t iter;
u_int8_t *row;
- // clear out any potential error in ctx
- ctx->io_error = 0;
-
// calcluate the scale factors
w_scale = ctx->img_w/absdelta(ctx->x1, ctx->x2);
h_scale = ctx->img_h/absdelta(ctx->y1, ctx->y2);
- // malloc the memory used to render each row
- row = (u_int8_t *) malloc(ctx->img_w);
-
- if (!row)
- goto error;
-
- // PNG or not?
- if (ctx->mode == RENDER_PNG) {
- // libpng initialization
- png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
-
- if (!png_ptr)
- goto error;
-
- info_ptr = png_create_info_struct(png_ptr);
-
- if (!info_ptr)
- goto error;
-
- // libpng error handling
- if (setjmp(png_jmpbuf(png_ptr))) {
- goto error;
- }
-
- if (ctx->io_stream) {
- // use normal libpng I/O
- png_init_io(png_ptr, ctx->io_stream);
- } else {
- // setup our custom I/O callbacks
- png_set_write_fn(png_ptr, ctx, &user_write_data, &user_flush_data);
- }
-
- // some PNG metadata
- png_set_IHDR(png_ptr, info_ptr, ctx->img_w, ctx->img_h, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-
- // write out the PNG header
- png_write_info(png_ptr, info_ptr);
-
- // possible error return
- if (ctx->io_error)
- goto error;
- }
-
// start rendering!
for (img_y=0; img_y < ctx->img_h; img_y++) {
// render the current row
@@ -122,56 +40,24 @@
iter--;
}
- row[img_x] = iter;
+ (*ctx->local_rowbuf_addr)[img_x] = iter;
}
-
- if (ctx->mode == RENDER_PNG) {
- // write the raw pixels to libpng
- png_write_row(png_ptr, row);
-
- // check for user errors return
- if (ctx->io_error)
- goto error;
- } else {
- // pass on the pixels to the io callback
- if (ctx->io_write_fn(row, ctx->img_w, ctx->io_cb_arg) == RENDER_CB_ERR) {
- ctx->io_error = 1;
- goto error;
- }
- }
+
+ // row cb
+ if (ctx->local_row_fn(ctx->cb_arg, (*ctx->local_rowbuf_addr)))
+ ERROR("local_row_fn");
}
- if (ctx->mode == RENDER_PNG) {
- // finished writing
- png_write_end(png_ptr, info_ptr);
- }
-
- // clean up
- png_destroy_write_struct(&png_ptr, &info_ptr);
- free(row); row = NULL;
-
- // check for user errors return
- if (ctx->io_error)
- goto error;
-
// return succesfully
- return MANDELBROT_OK;
+ return 0;
error:
- if (png_ptr || info_ptr)
- png_destroy_write_struct(&png_ptr, &info_ptr);
-
- if (row)
- free(row);
-
- row = NULL;
-
- return MANDELBROT_ERR;
+ return -1;
}
-int mandelbrot_render_timed (render_t *ctx, double *duration) {
+int render_mandelbrot_timed (struct render *ctx, double *duration) {
clock_t t1 = clock();
- int ret = mandelbrot_render(ctx);
+ int ret = render_mandelbrot(ctx);
clock_t t2 = clock();
*duration = ((double)(t2 - t1))/CLOCKS_PER_SEC;
--- a/mandelbrot.h Fri Jun 06 16:05:26 2008 +0300
+++ b/mandelbrot.h Fri Jun 06 18:35:46 2008 +0300
@@ -13,6 +13,6 @@
/*
* Render the image specified by the given render context
*/
-int mandelbrot_render (render_t *ctx);
-int mandelbrot_render_timed (render_t *ctx, double *duration);
+int render_mandelbrot (struct render *ctx);
+int render_mandelbrot_timed (struct render *ctx, double *duration);
--- a/render.c Fri Jun 06 16:05:26 2008 +0300
+++ b/render.c Fri Jun 06 18:35:46 2008 +0300
@@ -1,66 +1,91 @@
+#include <stdlib.h>
#include <string.h>
-#include <arpa/inet.h>
+#include "render_internal.h"
#include "render.h"
-int render_init (render_t *ctx, int mode) {
+int render_init (struct render *ctx, int mode) {
memset(ctx, 0, sizeof(*ctx));
return render_set_mode(ctx, mode);
}
-int render_set_mode (render_t *ctx, int mode) {
+struct render *render_alloc () {
+ struct render *ctx;
+
+ if (!(ctx = calloc(1, sizeof(struct render))))
+ return NULL;
+
+ return ctx;
+}
+
+int render_set_mode (struct render *ctx, int mode) {
if (mode != RENDER_RAW && mode != RENDER_PNG)
- return RENDER_ERR;
+ return -1;
ctx->mode = mode;
- return RENDER_OK;
+ return 0;
}
-int render_set_size (render_t *ctx, u_int32_t img_w, u_int32_t img_h) {
+int render_set_size (struct render *ctx, u_int32_t img_w, u_int32_t img_h) {
if (img_w == 0 || img_h == 0)
- return RENDER_ERR;
+ return -1;
ctx->img_w = img_w;
ctx->img_h = img_h;
- return RENDER_OK;
+ return 0;
}
-int render_region_full (render_t *ctx) {
+int render_get_size (struct render *ctx, u_int32_t *img_w, u_int32_t *img_h) {
+ *img_w = ctx->img_w;
+ *img_h = ctx->img_h;
+
+ return 0;
+}
+
+int render_region_full (struct render *ctx) {
return render_region_raw(ctx, REGION_X1, REGION_Y1, REGION_X2, REGION_Y2);
}
-int render_region_raw (render_t *ctx, double x1, double y1, double x2, double y2) {
+int render_region_raw (struct render *ctx, double x1, double y1, double x2, double y2) {
if ((x1 >= x2) || (y1 >= y2))
- return RENDER_ERR;
+ return -1;
ctx->x1 = x1;
ctx->y1 = y1;
ctx->x2 = x2;
ctx->y2 = y2;
- return RENDER_OK;
+ return 0;
}
-int render_io_custom (render_t *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg) {
+int render_io_custom (struct render *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg) {
if (!write_fn)
- return RENDER_ERR;
+ return -1;
ctx->io_write_fn = write_fn;
ctx->io_flush_fn = flush_fn;
- ctx->io_cb_arg = arg;
+ ctx->cb_arg = arg;
- return RENDER_OK;
+ return 0;
}
-int render_io_stream (render_t *ctx, FILE *fh) {
+int render_io_stream (struct render *ctx, FILE *fh) {
if (!fh)
- return RENDER_ERR;
+ return -1;
ctx->io_stream = fh;
- return RENDER_OK;
+ return 0;
}
+int render_local_mem (struct render *ctx, unsigned char **rowbuf_addr, render_ctx_row_cb row_fn, void *arg) {
+ ctx->local_rowbuf_addr = rowbuf_addr;
+ ctx->local_row_fn = row_fn;
+ ctx->cb_arg = arg;
+
+ return 0;
+}
+
--- a/render.h Fri Jun 06 16:05:26 2008 +0300
+++ b/render.h Fri Jun 06 18:35:46 2008 +0300
@@ -8,16 +8,18 @@
* This module provides various ways to render mandelbrot images in various formats
*/
-// custom I/O callbacks function signatures
+/*
+ * Custom callbacks
+ *
+ * These should return non-zero on error, whereupon the rendering will be aborted, or zero on success
+ */
typedef int (*render_ctx_write_cb)(const unsigned char *data, size_t length, void *arg);
typedef int (*render_ctx_flush_cb)(void *arg);
+typedef int (*render_ctx_row_cb)(void *arg, unsigned char *rowbuf);
+typedef int (*render_ctx_done_cb)(void *arg);
-// callback return codes
-#define RENDER_CB_OK 0
-#define RENDER_CB_ERR 1
-
-#define RENDER_OK 0
-#define RENDER_ERR 1
+// include render_internal.h first if you need it
+struct render;
// output types
enum {
@@ -25,51 +27,21 @@
RENDER_PNG, // a png image
};
-typedef struct render_ctx {
-/* render mode */
- int mode;
-
-/* image size */
- u_int32_t img_w;
- u_int32_t img_h;
-
-/* mandelbrot region */
- double x1;
- double y1;
- double x2;
- double y2;
-
-/* I/O parameters */
- // if this is non-NULL, use libpng's normal IO on this stream
- FILE *io_stream;
-
- // called to handle the output data
- render_ctx_write_cb io_write_fn;
-
- // called when the output data should be flushed - can be safely ignored if not needed
- render_ctx_flush_cb io_flush_fn;
-
- // the callback argument
- void *io_cb_arg;
-
- // error status
- int io_error;
-} render_t;
-
/*
- * Clear out the render context and set the mode
+ * Alloc a new render context
*/
-int render_init (render_t *ctx, int mode);
+struct render *render_alloc ();
/*
* What kind of image to render, PNG or RAW?
*/
-int render_set_mode (render_t *ctx, int mode);
+int render_set_mode (struct render *ctx, int mode);
/*
* What size of image to render
*/
-int render_set_size (render_t *ctx, u_int32_t img_w, u_int32_t img_h);
+int render_set_size (struct render *ctx, u_int32_t img_w, u_int32_t img_h);
+int render_get_size (struct render *ctx, u_int32_t *img_w, u_int32_t *img_h);
/*
* Select what region to render
@@ -81,13 +53,25 @@
#define REGION_X2 1.0
#define REGION_Y2 1.5
-int render_region_full (render_t *ctx);
-int render_region_raw (render_t *ctx, double x1, double y1, double x2, double y2);
+int render_region_full (struct render *ctx);
+int render_region_raw (struct render *ctx, double x1, double y1, double x2, double y2);
+
+/*
+ * Render raw pixel data directly to local memory.
+ *
+ * rowbuf_addr should point to a memory address that the mandelbrot will be
+ * rendered into. This memory address should have room for <img_w> bytes.
+ *
+ * row_fn will be called after each row has been written, passing in arg and
+ * *rowbuf_addr as arguments.
+ *
+ */
+int render_local_mem (struct render *ctx, unsigned char **rowbuf_addr, render_ctx_row_cb row_fn, void *arg);
/*
* How to handle the I/O
*/
-int render_io_custom (render_t *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg);
-int render_io_stream (render_t *ctx, FILE *fh);
+int render_io_custom (struct render *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg);
+int render_io_stream (struct render *ctx, FILE *fh);
#endif /* RENDER_H */
--- a/render_file.c Fri Jun 06 16:05:26 2008 +0300
+++ b/render_file.c Fri Jun 06 18:35:46 2008 +0300
@@ -6,27 +6,110 @@
#include <sys/socket.h>
#include <arpa/inet.h>
+#include "common.h"
#include "render.h"
-#include "mandelbrot.h"
-#include "common.h"
+#include "render_local.h"
static int verbose;
-void render_local (int img_w, int img_h, FILE *output) {
- render_t ctx;
-
- render_init(&ctx, RENDER_PNG);
- render_set_size(&ctx, img_w, img_h);
- render_region_full(&ctx);
- render_io_stream(&ctx, output);
-
+int render_file_local (struct render *ctx, FILE *fh) {
double duration;
- if (mandelbrot_render_timed(&ctx, &duration) != MANDELBROT_OK)
- err_exit("mandelbrot_render_region failed");
+ if (render_io_stream(ctx, fh))
+ ERROR("render_io_stream");
+
+ if (render_local(ctx, &duration))
+ ERROR("render_local");
+
+ if (verbose) {
+ u_int32_t img_w, img_h;
+
+ if (render_get_size(ctx, &img_w, &img_h))
+ ERROR("render_get_size");
+
+ fprintf(stdout, "rendered %dx%d mandelbrot in %f seconds\n", img_w, img_h, duration);
+ }
- if (verbose)
- fprintf(stdout, "rendered %dx%d mandelbrot in %f seconds\n", img_w, img_h, duration);
+ return 0;
+
+error:
+ return -1;
+}
+
+int main (int argc, char **argv) {
+ int error = 1;
+
+ struct render *ctx = NULL;
+
+ // parse arguments
+ int opt;
+ FILE *output = NULL;
+ int img_w = 256, img_h = 256;
+
+ while ((opt = getopt(argc, argv, "w:h:o:v")) != -1) {
+ switch(opt) {
+ case 'w' :
+ img_w = atoi(optarg);
+ break;
+
+ case 'h' :
+ img_h = atoi(optarg);
+ break;
+
+ case 'o' :
+ if (output)
+ err_exit("Only use -o once");
+
+ output = fopen(optarg, "w");
+
+ if (!output)
+ die(optarg);
+
+ break;
+
+ case 'v' :
+ verbose = 1;
+ break;
+
+
+ default :
+ err_exit("Usage: %s [-w img_w] [-h img_h] [-o output_file] [-v]", argv[0]);
+ }
+ }
+
+ if (!output)
+ output = stdout;
+
+ // setup the struct render
+ if (!(ctx = render_alloc()))
+ ERROR("render_alloc");
+
+ if (render_set_mode(ctx, RENDER_PNG))
+ ERROR("render_set_mode");
+
+ if (render_set_size(ctx, img_w, img_h))
+ ERROR("render_set_size");
+
+ if (render_region_full(ctx))
+ ERROR("render_region_full");
+
+ // do the render!
+ if (verbose)
+ fprintf(stderr, "Render [%dx%d] mandelbrot locally...\n", img_w, img_h);
+
+ if (render_file_local(ctx, output))
+ ERROR("render_local");
+
+ // success
+ error = 0;
+
+error:
+ if (output)
+ fclose(output);
+
+ free(ctx);
+
+ return error;
}
#if 0
@@ -105,74 +188,4 @@
}
#endif
-int main (int argc, char **argv) {
- int opt;
-
- FILE *output = NULL /*, *remote = NULL */ ;
- int img_w = 256, img_h = 256;
-
-
- while ((opt = getopt(argc, argv, "w:h:o:vr:")) != -1) {
- switch(opt) {
- case 'w' :
- img_w = atoi(optarg);
- break;
-
- case 'h' :
- img_h = atoi(optarg);
- break;
-
- case 'o' :
- if (output)
- err_exit("Only use -o once");
-
- output = fopen(optarg, "w");
-
- if (!output)
- die(optarg);
-
- break;
-
- case 'v' :
- verbose = 1;
- break;
-#if 0
- case 'r' :
- if (remote)
- err_exit("Only use -r once");
-
- remote = open_remote(optarg);
-
- break;
-#endif
-
- default :
- err_exit("Usage: %s [-w img_w] [-h img_h] [-o output_file] [-v] [-r host[:port]]", argv[0]);
- }
- }
-
- if (!output)
- output = stdout;
-
-#if 0
- if (remote) {
- if (verbose)
- fprintf(stderr, "Render [%dx%d] mandelbrot remotely\n", img_w, img_h);
-
- render_remote(img_w, img_h, output, remote);
-
- fclose(remote);
- } else
-#endif
- {
- if (verbose)
- fprintf(stderr, "Render [%dx%d] mandelbrot locally\n", img_w, img_h);
-
- render_local(img_w, img_h, output);
- }
-
- fclose(output);
-
- return 0;
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_internal.h Fri Jun 06 18:35:46 2008 +0300
@@ -0,0 +1,43 @@
+#ifdef RENDER_H
+#error render_internal.h must be included before render.h
+#endif
+
+#ifndef RENDER_INTERNAL_H
+#define RENDER_INTERNAL_H
+
+#include "render.h"
+
+struct render {
+/* render mode */
+ int mode;
+
+/* image size */
+ u_int32_t img_w;
+ u_int32_t img_h;
+
+/* mandelbrot region */
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+
+/* local rendering */
+ unsigned char **local_rowbuf_addr;
+ render_ctx_row_cb local_row_fn;
+
+/* local I/O parameters */
+ // if this is non-NULL, the image data is simply fwrite():en to this stream
+ FILE *io_stream;
+
+ // called to handle the output data
+ render_ctx_write_cb io_write_fn;
+
+ // called when the output data should be flushed - can be safely ignored if not needed
+ render_ctx_flush_cb io_flush_fn;
+
+ // the callback argument for all of the above *_fn
+ void *cb_arg;
+};
+
+#endif /* RENDER_INTERNAL_H */
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_local.c Fri Jun 06 18:35:46 2008 +0300
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include "common.h"
+#include "render_internal.h"
+#include "render_local.h"
+#include "render_png.h"
+#include "mandelbrot.h"
+
+int render_local (struct render *render, double *duration) {
+ assert(render->mode == RENDER_PNG);
+
+ unsigned char *rowbuf = NULL;
+ struct render_png *png_ctx = NULL;
+
+ *duration = -1;
+
+ // alloc the memory buffer
+ if (!(rowbuf = malloc(render->img_w)))
+ ERROR("malloc");
+
+ // the render_png stuff
+ if (!(png_ctx = render_png_init(render)))
+ ERROR("render_png_init");
+
+ // set render_* to use it
+ if (render_local_mem(render, &rowbuf, (int(*)(void *arg, unsigned char *)) &render_png_row, png_ctx))
+ ERROR("render_local_mem");
+
+ // then we can actually render
+ if (duration ? render_mandelbrot_timed(render, duration) : render_mandelbrot(render))
+ ERROR("render_mandelbrot");
+
+ // success
+ return 0;
+
+error:
+ free(rowbuf);
+ free(png_ctx);
+
+ return -1;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_local.h Fri Jun 06 18:35:46 2008 +0300
@@ -0,0 +1,11 @@
+#ifndef RENDER_LOCAL_H
+#define RENDER_LOCAL_H
+
+#include "render.h"
+
+/*
+ * Renders the given struct render locally in one operation.
+ */
+int render_local (struct render *render, double *duration);
+
+#endif /* RENDER_LOCAL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_multi.c Fri Jun 06 18:35:46 2008 +0300
@@ -0,0 +1,143 @@
+
+
+#include "render_multi.h"
+#include "remote_node.h"
+
+
+struct render_multi {
+ // these are used as arguments to render_remote
+ struct render_sub_ctx {
+ // our offset in the list
+ int index;
+
+ // the remote_render_ctx
+ struct remote_render_ctx *remote_render;
+
+ // a pointer to ourself
+ struct render_multi *self;
+
+ // _render_sent called for this?
+ int render_sent;
+ } remote_renders[RENDER_MULTI_NODES_MAX];
+
+ // how many remote_renders we have
+ int remote_render_count;
+
+ // our own callbacks that we call
+ void (*cb_sent)(void *arg);
+ void (*cb_data)(struct evbuffer *buf, void *arg);
+ void (*cb_done)(void *arg);
+ void (*cb_fail)(void *arg);
+
+ void *cb_arg;
+
+};
+
+// our render_remote callbacks
+void _render_sent (void *arg) {
+ struct render_sub_ctx *ctx, *ctx2 = arg;
+
+ ctx->render_sent = 1;
+
+ // have all render_sub_ctxs been sent?
+ int i;
+ for (i = 0; i < ctx->self->remote_render_count; i++) {
+ if (!ctx->self->remote_renders[i].render_sent)
+ break;
+ }
+
+ if (i == ctx->self->remote_render_count) {
+ // call cb_sent
+ ctx->self->cb_sent(ctx->self->cb_arg);
+ }
+}
+
+void _render_data (struct evbuffer *buf, void *arg) {
+
+}
+
+void _render_done (void *arg) {
+
+}
+
+void _render_fail (void *arg) {
+
+}
+
+#define ROUND_DOWN(dividend, divisor) ((dividend) / (divisor))
+#define ROUND_UP(dividend, divisor) (((dividend) / (divisor)) + ((dividend) % (divisor)))
+
+#define HALF(a, b) (( a + b) / 2)
+
+struct render_multi *render_multi (
+ render_t *render_ctx, // what to render
+ struct remote_pool *render_pool, // what render pool to use
+ void (*cb_sent)(void *arg),
+ void (*cb_data)(struct evbuffer *buf, void *arg),
+ void (*cb_done)(void *arg),
+ void (*cb_fail)(void *arg),
+ void *cb_arg
+) {
+ struct render_multi *ctx = NULL;
+ render_t r_left, r_right;
+
+ // for now, just split it in half into two render_ts
+ assert(RENDER_MULTI_NODES_MAX >= 2);
+
+ if (
+ render_init(&r_left, RENDER_RAW)
+ || render_init(&r_right, RENDER_RAW)
+ || render_set_size(&r_left, render_ctx->img_w / 2, render_ctx->img_h)
+ || render_set_size(&r_right, render_ctx->img_w / 2 + render_ctx->img_w % 2, render_ctx->img_h)
+ || render_set_region_raw(&r_left, render_ctx->x1, render_ctx->y1, HALF(render_ctx->x1, render_ctx->x2), render_ctx->y2)
+ || render_set_region_raw(&r_right, HALF(render_ctx->x1, render_ctx->x2), render_ctx->y1, render_ctx->x2, render_ctx->y2)
+ )
+ ERROR("render_{init,set_size,set_region_raw}");
+
+ // alloc the render_multi
+ ctx = calloc(1, sizeof(struct render_multi));
+
+ if (!ctx)
+ ERROR("calloc");
+
+ // store the provided callback functions
+ ctx->cb_sent = cb_sent;
+ ctx->cb_data = cb_data;
+ ctx->cb_done = cb_done;
+ ctx->cb_fail = cb_fail;
+ ctx->cb_arg = cb_arg;
+
+ // pull two nodes from the pool
+ struct remote_node *node_left, *node_right;
+
+ if (
+ !(node_left = remote_pool_get(pool_info))
+ || !(node_right = remote_pool_get(pool_info))
+ )
+ ERROR("remote_pool_get");
+
+ // init the remote_render
+ ctx->remote_renders[0].index = 0;
+ ctx->remote_renders[0].self = &ctx
+ ctx->remote_renders[1].index = 1;
+ ctx->remote_renders[1].self = &ctx
+ ctx->remote_render_count = 2;
+
+ // the two render_remote calls
+ if (
+ !(ctx->remote_renders[0].remote_render = render_remote(&r_left, node_left,
+ &_render_sent, &_render_data, &_render_done, &_render_fail, &ctx->remote_renders[0]))
+ || !(ctx->remote_renders[1].remote_render = render_remote(&r_right, node_right,
+ &_render_sent, &_render_data, &_render_done, &_render_fail, &ctx->remote_renders[1]))
+ )
+ ERROR("render_remote");
+
+ // I guess that's a succesfull start now
+ return 0;
+
+error:
+ free(ctx);
+
+ return -1;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_multi.h Fri Jun 06 18:35:46 2008 +0300
@@ -0,0 +1,30 @@
+#ifndef RENDER_MULTI_H
+#define RENDER_MULTI_H
+
+#include "remote_pool.h"
+
+/*
+ * Execute a given render_t operate across multiple remote render nodes, and aggregate the results into a single PNG stream locally
+ */
+
+struct render_multi;
+
+// how many nodes can be involved in a render operation at most
+#define RENDER_MULTI_NODES_MAX 2
+
+/*
+ * Execute the given render operation on multiple nodes from the given remote pool, streaming back the result in PNG format via the given callbacks
+ *
+ * The behaviour of the callbacks is mostly the same as for render_remote.h
+ */
+struct render_multi *render_multi (
+ render_t *render_ctx, // what to render
+ struct remote_pool *render_pool, // what render pool to use
+ void (*cb_sent)(void *arg),
+ void (*cb_data)(struct evbuffer *buf, void *arg),
+ void (*cb_done)(void *arg),
+ void (*cb_fail)(void *arg),
+ void *cb_arg
+);
+
+#endif /* RENDER_MULTI_H */
--- a/render_node.c Fri Jun 06 16:05:26 2008 +0300
+++ b/render_node.c Fri Jun 06 18:35:46 2008 +0300
@@ -7,10 +7,11 @@
#include <string.h>
#include <unistd.h>
+#include "common.h"
#include "render.h"
#include "render_remote.h"
+#include "render_local.h"
#include "mandelbrot.h"
-#include "common.h"
void sigpipe_handler (int signal) {
/* ignore */
@@ -63,48 +64,57 @@
}
void handle_client (int sock) {
- // open it as a FILE*
- FILE *fh = fdopen(sock, "r+");
-
- // read the parameters
+ double duration;
+ struct render *ctx;
+ FILE *fh;
u_int8_t mode;
u_int32_t img_w, img_h;
double x1, y1, x2, y2;
+
+ // open it as a FILE*
+ if (!(fh = fdopen(sock, "r+")))
+ ERROR("fdopen");
+
+ // read the parameters
if (
- !read_byte(fh, &mode) ||
- !read_int(fh, &img_w) ||
- !read_int(fh, &img_h) ||
- !read_double(fh, &x1) ||
- !read_double(fh, &y1) ||
- !read_double(fh, &x2) ||
- !read_double(fh, &y2)
- ) {
- error("ERR: invalid arguments");
- fclose(fh);
- return;
- }
+ !read_byte(fh, &mode)
+ || !read_int(fh, &img_w)
+ || !read_int(fh, &img_h)
+ || !read_double(fh, &x1)
+ || !read_double(fh, &y1)
+ || !read_double(fh, &x2)
+ || !read_double(fh, &y2)
+ )
+ ERROR("read_{byte,int,double}");
- printf("RENDER: [%ux%u] (%f, %f) -> (%f, %f): ...", img_w, img_h, x1, y1, x2, y2);
+ printf("RENDER: [%ux%u] (%f, %f) -> (%f, %f): ... ", img_w, img_h, x1, y1, x2, y2);
fflush(stdout);
- double duration;
-
// set up the render_ctx
- struct render_ctx ctx;
- render_init(&ctx, RENDER_PNG);
- render_set_size(&ctx, img_w, img_h);
- render_region_raw(&ctx, x1, y1, x2, y2);
- render_io_stream(&ctx, fh);
-
- sigpipe_ignore();
+ if (!(ctx = render_alloc()))
+ ERROR("render_alloc");
+
+ if (render_set_mode(ctx, mode))
+ ERROR("render_set_mode");
+
+ if (render_set_size(ctx, img_w, img_h))
+ ERROR("render_set_size");
+
+ if (render_region_raw(ctx, x1, y1, x2, y2))
+ ERROR("render_region_raw");
+
+ if (render_io_stream(ctx, fh))
+ ERROR("render_io_stream");
+
// render!
- if (mandelbrot_render_timed(&ctx, &duration))
- printf("error\n"); // XXX: notify our client?
- else
- printf("time=%fs\n", duration);
-
+ if (render_local(ctx, &duration))
+ ERROR("render_local");
+
+ printf("time=%fs\n", duration);
+
+error:
// close the FILE* and socket
fclose(fh);
@@ -120,12 +130,13 @@
// parse arguments
int opt;
const char *port_name = NULL;
+ unsigned short port;
while ((opt = getopt(argc, argv, "l:")) != -1) {
switch (opt) {
case 'l':
if (port_name)
- err_exit("only specify -l once");
+ ERROR("only specify -l once");
port_name = optarg;
break;
@@ -134,43 +145,48 @@
err_exit("Usage: %s [-l port]", argv[0]);
}
}
-
+
+ // post-process arguments
if (!port_name)
port_name = RENDER_PORT_NAME;
- unsigned short port = atoi(port_name);
-
- if (!port)
- err_exit("invalid port: %s", port_name);
+ if (!(port = atoi(port_name)))
+ ERROR_FMT("invalid port: %s", port_name);
// create the socket
if ((ssock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
- perr_exit("socket");
+ PERROR("socket");
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(ssock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == -1)
- perr_exit("bind");
+ PERROR("bind");
if (listen(ssock, 1) == -1)
- perr_exit("listen");
+ PERROR("listen");
+ // ignore sigpipe
+ sigpipe_ignore();
+
+ // main accept loop
printf("RUN: %s:%hu\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
- // main accept loop
while (1) {
addr_len = sizeof(struct sockaddr_in);
// accept a new client
if ((sock = accept(ssock, (struct sockaddr *) &addr, &addr_len)) == -1)
- perr_exit("accept");
+ PERROR("accept");
printf("ACCEPT: %s:%hu\n", inet_ntoa(addr.sin_addr), addr.sin_port);
// handle their resquest
handle_client(sock);
}
+
+error:
+ return 1;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_png.c Fri Jun 06 18:35:46 2008 +0300
@@ -0,0 +1,136 @@
+#include <stdlib.h>
+
+#include <png.h>
+
+#include "common.h"
+#include "render_internal.h"
+#include "render_png.h"
+
+struct render_png {
+ // libpng handles
+ png_structp png_ptr;
+ png_infop info_ptr;
+
+ // our render op, containing the I/O callbacks
+ struct render *render;
+};
+
+void _render_png_write(png_structp png_ptr, png_bytep data, png_size_t length) {
+ struct render_png *ctx = png_get_io_ptr(png_ptr);
+
+ if (ctx->render->io_write_fn)
+ if (ctx->render->io_write_fn(data, length, ctx->render->cb_arg)) {
+ // error, doesn't return
+ png_error(png_ptr, "_render_png_write: io_write_fn");
+ }
+}
+
+void _render_png_flush(png_structp png_ptr) {
+ struct render_png *ctx = png_get_io_ptr(png_ptr);
+
+ if (ctx->render->io_flush_fn)
+ if (ctx->render->io_flush_fn(ctx->render->cb_arg)) {
+ // error, doesn't return
+ png_error(png_ptr, "_render_png_flush: io_flush_fn");
+ }
+}
+
+void _render_png_free (struct render_png *ctx) {
+ if (ctx)
+ png_destroy_write_struct(&ctx->png_ptr, &ctx->info_ptr);
+
+ free(ctx);
+}
+
+
+struct render_png *render_png_init (struct render *render) {
+
+ struct render_png *ctx = NULL;
+
+ // calloc the render_png
+ if (!(ctx = calloc(1, sizeof(struct render_png))))
+ ERROR("calloc");
+
+ // store the struct render
+ ctx->render = render;
+
+ // libpng initialization
+ if (!(ctx->png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)))
+ ERROR("png_create_write_struct");
+
+ if (!(ctx->info_ptr = png_create_info_struct(ctx->png_ptr)))
+ ERROR("png_create_info_struct");
+
+ // libpng error handling
+ if (setjmp(png_jmpbuf(ctx->png_ptr)))
+ ERROR("libpng");
+
+ if (render->io_stream) {
+ // use normal libpng I/O
+ // XXX: who fcloses this?
+ png_init_io(ctx->png_ptr, render->io_stream);
+ } else {
+ // setup our custom I/O callbacks
+ png_set_write_fn(ctx->png_ptr, ctx, &_render_png_write, &_render_png_flush);
+ }
+
+ // some PNG metadata
+ png_set_IHDR(ctx->png_ptr, ctx->info_ptr, render->img_w, render->img_h, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ // write out the PNG header
+ png_write_info(ctx->png_ptr, ctx->info_ptr);
+
+ // success
+ return ctx;
+
+error:
+ _render_png_free(ctx);
+ return NULL;
+}
+
+int render_png_row (struct render_png *ctx, unsigned char *rowbuf) {
+ // libpng error handling
+ if (setjmp(png_jmpbuf(ctx->png_ptr)))
+ ERROR("libpng");
+
+ // write it in
+ png_write_row(ctx->png_ptr, rowbuf);
+
+ // success
+ return 0;
+
+error:
+ _render_png_free(ctx);
+ return -1;
+}
+
+int render_png_done (struct render_png *ctx) {
+ // libpng error handling
+ if (setjmp(png_jmpbuf(ctx->png_ptr)))
+ ERROR("libpng");
+
+ // write end
+ png_write_end(ctx->png_ptr, ctx->info_ptr);
+
+ // success
+ return 0;
+
+error:
+ _render_png_free(ctx);
+ return -1;
+}
+
+int render_png_abort (struct render_png *ctx) {
+ // libpng error handling
+ if (setjmp(png_jmpbuf(ctx->png_ptr)))
+ ERROR("libpng");
+
+ _render_png_free(ctx);
+
+ // success
+ return 0;
+
+error:
+ return -1;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_png.h Fri Jun 06 18:35:46 2008 +0300
@@ -0,0 +1,37 @@
+#ifndef RENDER_PNG_H
+#define RENDER_PNG_H
+
+#include "render.h"
+
+/*
+ * For rendering PNG images
+ */
+struct render_png;
+
+/*
+ * Build and return a render_png that can be used to render a PNG of the given struct render.
+ *
+ * This inspects the size and io properties of the struct render.
+ *
+ * returns NULL on failure.
+ */
+struct render_png *render_png_init (struct render *render);
+
+/*
+ * Feed a full row of raw pixel data into the PNG image.
+ *
+ * buf must be struct render.img_w bytes wide.
+ */
+int render_png_row (struct render_png *ctx, unsigned char *rowbuf);
+
+/*
+ * Mark the render as complete and free the render_png, flushing out any remaining data.
+ */
+int render_png_done (struct render_png *ctx);
+
+/*
+ * Abort the render, free the render_png, and discard any remaining data.
+ */
+int render_png_abort (struct render_png *ctx);
+
+#endif /* RENDER_PNG_H */
--- a/render_remote.c Fri Jun 06 16:05:26 2008 +0300
+++ b/render_remote.c Fri Jun 06 18:35:46 2008 +0300
@@ -7,6 +7,7 @@
#include <event2/event.h>
#include <event2/bufferevent.h>
+#include "render_internal.h" // for render_cmd_build
#include "render_remote.h"
#include "common.h"
@@ -14,8 +15,6 @@
struct event *ev_conn;
struct bufferevent *data_bev;
- struct remote_node *remote_node;
-
#pragma pack(push)
#pragma pack(1)
@@ -41,33 +40,27 @@
void *cb_arg;
};
-void _remote_render_ctx_free (struct remote_render_ctx *ctx) {
- // close the socket (ctx->ev_conn remains valid even after we're done with it...)
- close(event_get_fd(ctx->ev_conn));
-
- // free the connect event
- event_free(ctx->ev_conn);
-
+void _remote_render_ctx_free (struct remote_render_ctx **ctx) {
// free the data_bev
- if (ctx->data_bev) {
- bufferevent_free(ctx->data_bev);
- ctx->data_bev = NULL;
+ if ((*ctx)->data_bev) {
+ bufferevent_free((*ctx)->data_bev);
+ (*ctx)->data_bev = NULL;
}
- // update remote_node load
- ctx->remote_node->current_load--;
+ // and the event
+ event_free((*ctx)->ev_conn);
// free the context structure
- free(ctx);
+ free(*ctx);
- ctx = NULL;
+ *ctx = NULL;
}
#define RENDER_FAILED(ctx, desc) \
do { \
perror(desc); \
ctx->cb_fail(ctx->cb_arg); \
- _remote_render_ctx_free(ctx); \
+ _remote_render_ctx_free(&ctx); \
return; \
} while (0)
@@ -125,7 +118,7 @@
}
// free resources
- _remote_render_ctx_free(ctx);
+ _remote_render_ctx_free(&ctx);
}
void _remote_connected (int fd, short event, void *arg) {
@@ -144,19 +137,19 @@
RENDER_FAILED(ctx, "render_remote: bufferevent_enable");
}
-void render_cmd_build (render_t *rctx, struct remote_render_ctx *rrctx) {
+void render_cmd_build (struct render *render, struct remote_render_ctx *ctx) {
// just copy over the render params to the render_cmd
- rrctx->render_cmd.mode = rctx->mode;
- rrctx->render_cmd.img_w = htonl(rctx->img_w);
- rrctx->render_cmd.img_h = htonl(rctx->img_h);
- rrctx->render_cmd.x1 = rctx->x1;
- rrctx->render_cmd.y1 = rctx->y1;
- rrctx->render_cmd.x2 = rctx->x2;
- rrctx->render_cmd.y2 = rctx->y2;
+ ctx->render_cmd.mode = render->mode;
+ ctx->render_cmd.img_w = htonl(render->img_w);
+ ctx->render_cmd.img_h = htonl(render->img_h);
+ ctx->render_cmd.x1 = render->x1;
+ ctx->render_cmd.y1 = render->y1;
+ ctx->render_cmd.x2 = render->x2;
+ ctx->render_cmd.y2 = render->y2;
}
struct remote_render_ctx *render_remote (
- render_t *render_ctx,
+ struct render *render,
struct remote_node *remote_node,
void (*cb_sent)(void *arg),
void (*cb_data)(struct evbuffer *buf, void *arg),
@@ -164,15 +157,14 @@
void (*cb_fail)(void *arg),
void *cb_arg
) {
- printf("render_remote: remote render load: %d/%d\n", remote_node->current_load, remote_node->parallel_renders);
+ struct remote_render_ctx *ctx;
+ int sock;
+
+ printf("remote_node render load: %d/%d\n", remote_node->current_load, remote_node->parallel_renders);
// alloc the remote render ctx
- struct remote_render_ctx *ctx = malloc(sizeof(struct remote_render_ctx));
-
- if (!ctx) {
- error("render_remote: malloc");
- return NULL;
- }
+ if (!(ctx = calloc(1, sizeof(struct remote_render_ctx))))
+ ERROR("calloc");
// store the provided callback functions
ctx->cb_sent = cb_sent;
@@ -180,50 +172,30 @@
ctx->cb_done = cb_done;
ctx->cb_fail = cb_fail;
ctx->cb_arg = cb_arg;
-
- // keep a reference to remote_node so we can decr it's load
- ctx->remote_node = remote_node;
// copy the relevant stuff from the render_ctx
- render_cmd_build(render_ctx, ctx);
+ render_cmd_build(render, ctx);
// create the socket
- int sock = socket(remote_node->addr.ss_family, SOCK_STREAM, 0);
-
- if (sock < 0) {
- perror("render_remote: socket");
- goto error;
- }
+ if ((sock = socket(remote_node->addr.ss_family, SOCK_STREAM, 0)) < 0)
+ PERROR("socket");
// mark it as nonblocking
- if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
- perror("render_remote: fcntl");
- goto error;
- }
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
+ PERROR("fcntl");
// initiate the connect
int err = connect(sock, (struct sockaddr *) &remote_node->addr, sizeof(remote_node->addr));
- if (err != -1 || errno != EINPROGRESS) {
- perror("render_remote: connect");
- goto error;
- }
+ if (err != -1 || errno != EINPROGRESS)
+ PERROR("connect");
// do the libevent dance
- ctx->ev_conn = event_new(NULL, sock, EV_WRITE, &_remote_connected, ctx);
-
- if (!ctx->ev_conn) {
- error("render_remote: event_new");
- goto error;
- }
+ if (!(ctx->ev_conn = event_new(NULL, sock, EV_WRITE, &_remote_connected, ctx)))
+ ERROR("event_new");
- if (event_add(ctx->ev_conn, NULL)) {
- error("render_remote: event_add");
- goto error;
- }
-
- // update remote_node load
- remote_node->current_load++;
+ if (event_add(ctx->ev_conn, NULL))
+ ERROR("event_add");
// success
return ctx;
@@ -259,9 +231,13 @@
// if it's still just connecting, cancel that
if (event_pending(ctx->ev_conn, EV_WRITE, NULL)) {
event_del(ctx->ev_conn);
+
}
+ // close the socket (ctx->ev_conn remains valid even after we're done with it...)
+ close(event_get_fd(ctx->ev_conn));
+
// this takes care of the rest
- _remote_render_ctx_free (ctx);
+ _remote_render_ctx_free (&ctx);
}
--- a/render_remote.h Fri Jun 06 16:05:26 2008 +0300
+++ b/render_remote.h Fri Jun 06 18:35:46 2008 +0300
@@ -25,9 +25,11 @@
* cb_data is called whenever new data has been received. See also, render_remote_set_chunk_size
* cb_done is called after our last call to cb_data (note: see render_remote_shake)
* cb_fail is called when an error is encountered. This can (and will) happen at any time!
+ *
+ * Returns NULL on error, or a handle that can be used for cancel/etc on success
*/
struct remote_render_ctx *render_remote (
- render_t *render_ctx, // what to render
+ struct render *render, // what to render
struct remote_node *remote_node, // what render node to use
void (*cb_sent)(void *arg),
void (*cb_data)(struct evbuffer *buf, void *arg),