terom@13: #include terom@16: #include terom@16: #include terom@13: #include terom@11: terom@13: #include "common.h" terom@17: #include "render_struct.h" terom@11: #include "render_multi.h" terom@13: #include "render_remote.h" terom@11: #include "remote_node.h" terom@17: #include "remote_pool.h" terom@17: #include "render_slices_struct.h" terom@17: #include "render_slices.h" terom@11: terom@16: /* terom@16: // the states we can go through terom@16: enum render_multi_state { terom@16: STATE_INIT, // the render_multi is in the process of being initialized terom@16: STATE_SENDING, // we're still waiting for some of the requests to be sent terom@16: STATE_RENDER, // we're handling data now! terom@16: STATE_DATA_DONE, // we're finished with all the data terom@16: STATE_PNG_DONE, // PNG data's done terom@16: STATE_FAILED, // we failed terom@16: }; terom@16: terom@17: enum render_multi_node_state { terom@17: STATE_INIT, // the render_multi_node is in the process of being initialized terom@16: STATE_SENDING, // we're waiting for the requests to be sent terom@16: STATE_FILL_ROW, // we're filling the row with data terom@16: STATE_ROW_FULL, // our row is full terom@16: STATE_DATA_DONE, // we're finished with all the data terom@16: STATE_PNG_DONE, // PNG data's done terom@16: STATE_FAILED, // we failed terom@16: terom@16: }; terom@16: */ terom@16: terom@11: struct render_multi { terom@11: // these are used as arguments to render_remote terom@17: struct render_multi_node { terom@17: // the slice info terom@17: struct render_slice_info *info; terom@11: terom@16: // the render_remote_ctx terom@16: struct render_remote *render_remote; terom@11: terom@16: // _render_multi_sent called for this? terom@11: int render_sent; terom@13: terom@13: // how wide our slice is, static terom@13: size_t slice_width; terom@13: terom@13: // how many bytes we have already written into the current row terom@13: size_t col; terom@13: terom@18: // passed in the last segment of data? terom@13: int render_done; terom@17: terom@17: // a pointer to ourself terom@17: struct render_multi *self; terom@16: terom@17: } nodes[RENDER_SLICES_MAX]; terom@11: terom@17: // how many nodes we have terom@17: int node_count; terom@11: terom@16: // is this still alive? terom@16: int alive; terom@16: terom@16: // how many remote renders have been succesfully cb_sent? terom@16: int renders_sent; terom@16: terom@16: // how many of the renders are done? terom@16: int renders_done; terom@16: terom@13: // have we called cb_sent? terom@13: int have_sent; terom@13: terom@17: // the render_slices thing terom@17: struct render_slices slices; terom@13: terom@18: // has render_slices_done returned? terom@18: int slices_done; terom@16: terom@13: // buffer render_png output in this terom@13: struct evbuffer *out_buf; terom@13: terom@11: // our own callbacks that we call terom@11: void (*cb_sent)(void *arg); terom@11: void (*cb_data)(struct evbuffer *buf, void *arg); terom@11: void (*cb_done)(void *arg); terom@11: void (*cb_fail)(void *arg); terom@11: terom@11: void *cb_arg; terom@11: }; terom@11: terom@13: #define FAIL_PARTIAL 0x01 terom@13: #define FAIL_SILENT 0x02 terom@13: terom@13: // prototypes terom@13: static void _render_multi_do_free (struct render_multi *ctx); terom@13: static void _render_multi_do_sent (struct render_multi *ctx); terom@16: static void _render_multi_do_png_data (struct render_multi *ctx); terom@16: static void _render_multi_do_png_done (struct render_multi *ctx); terom@13: static void _render_multi_do_done (struct render_multi *ctx); terom@13: static void _render_multi_do_fail (struct render_multi *ctx, int flags); terom@13: terom@13: /* terom@13: * Actually free the request. Should be de-initialized (either _render_multi_error, or _render_done when this is called terom@13: */ terom@13: static void _render_multi_do_free (struct render_multi *ctx) { terom@16: assert(ctx && ctx->alive == 0); terom@13: terom@17: render_slices_deinit(&ctx->slices); terom@13: terom@13: if (ctx->out_buf) { terom@13: evbuffer_free(ctx->out_buf); terom@13: ctx->out_buf = NULL; terom@13: } terom@13: terom@13: free(ctx); terom@13: } terom@13: terom@13: // the request has been sent terom@13: static void _render_multi_do_sent (struct render_multi *ctx) { terom@13: int i; terom@13: terom@17: // check that all the nodes have indeed been sent terom@17: for (i = 0; i < ctx->node_count; i++) { terom@17: assert(ctx->nodes[i].render_sent); terom@13: } terom@13: terom@13: // assert the callbacks are still valid terom@13: assert(ctx->cb_sent && ctx->cb_fail && ctx->cb_done); terom@13: terom@13: // call cb_sent and then invalidate it terom@13: ctx->cb_sent(ctx->cb_arg); terom@13: ctx->cb_sent = NULL; terom@13: ctx->have_sent = 1; terom@13: terom@13: // we're going to always have the PNG header data buffered at this point, so give that to the user right away terom@13: assert(evbuffer_get_length(ctx->out_buf) > 0); terom@16: _render_multi_do_png_data(ctx); terom@16: } terom@13: terom@16: // possibly call cb_data, and if the renders are all done and the buffer is empty, cb_done terom@16: static void _render_multi_do_png_data (struct render_multi *ctx) { terom@16: // at first we have to wait until we've called cb_sent terom@16: if (!ctx->have_sent) terom@16: return; terom@13: terom@16: // got any PNG data in there? terom@16: if (evbuffer_get_length(ctx->out_buf)) terom@16: ctx->cb_data(ctx->out_buf, ctx->cb_arg); terom@16: terom@16: // was that the last piece of PNG data? terom@18: if (ctx->slices_done && evbuffer_get_length(ctx->out_buf) == 0) { terom@16: // PNG data done! terom@16: _render_multi_do_png_done(ctx); terom@16: } terom@16: } terom@16: terom@16: // the PNG rendering completed succesfully terom@16: static void _render_multi_do_png_done (struct render_multi *ctx) { terom@16: // check that ctx is still valid terom@16: assert(ctx->alive); terom@16: terom@16: // mark as not alive terom@16: ctx->alive = 0; terom@16: terom@16: // call cb_done terom@16: ctx->cb_done(ctx->cb_arg); terom@16: terom@16: // don't free ourself, our user does that (probably did already) terom@13: return; terom@13: } terom@13: terom@16: // the request completed normally, flush the png data and return terom@16: // _render_multi_png_done takes care of calling cb_done, not us! terom@13: static void _render_multi_do_done (struct render_multi *ctx) { terom@17: assert(ctx->alive); terom@16: terom@13: int i; terom@16: terom@13: // check that all the remote_renders are indeed complete terom@17: for (i = 0; i < ctx->node_count; i++) { terom@17: assert(ctx->nodes[i].render_remote == NULL); terom@17: assert(ctx->nodes[i].col == 0); terom@13: } terom@18: terom@18: // finish off the render_slices terom@18: if (render_slices_done(&ctx->slices)) terom@18: ERROR("render_slices_done"); terom@18: terom@18: // mark this as complete, all data is now in the out buffer terom@18: ctx->slices_done = 1; terom@17: terom@16: // if that all the data handled now, we're done terom@16: _render_multi_do_png_data(ctx); terom@13: terom@16: // don't free ourself, our user does that (probably already did, via render_png_done) terom@13: return; terom@13: terom@13: error: terom@18: /* render_slices_done -> render_png_done failed, probably because we didn't have enough data */ terom@13: _render_multi_do_fail(ctx, FAIL_PARTIAL); terom@13: } terom@13: terom@13: // the request completed abnormally. Flags: terom@13: // FAIL_SILENT - don't call cb_fail terom@13: // FAIL_PARTIAL - assume png_info may be NULL terom@13: static void _render_multi_do_fail (struct render_multi *ctx, int flags) { terom@13: int i; terom@13: terom@16: // check that ctx is still valid terom@16: assert(ctx->alive || flags & FAIL_PARTIAL); terom@16: terom@16: // mark as not alive terom@16: ctx->alive = 0; terom@16: terom@13: // cancel any in-progress remote renders terom@17: for (i = 0; i < ctx->node_count; i++) terom@17: if (ctx->nodes[i].render_remote) { terom@17: render_remote_cancel(ctx->nodes[i].render_remote); terom@17: ctx->nodes[i].render_remote = NULL; terom@13: } terom@13: terom@17: // XXX: does render_slices need an abort? terom@13: terom@13: // check that both callbacks are still valid terom@13: assert(ctx->cb_fail && ctx->cb_done); terom@13: terom@13: if (!(flags & FAIL_SILENT)) { terom@13: // call cb_fail and then invalidate it terom@13: ctx->cb_fail(ctx->cb_arg); terom@13: } terom@13: terom@13: ctx->cb_fail = NULL; terom@13: terom@16: // don't free ourself, our user does that terom@13: } terom@13: terom@13: /* terom@13: * One of the remote render commands has succesfully been sent. terom@13: * terom@13: * Once all of these commands have been sent, invoke our cb_sent. terom@13: */ terom@13: static void _render_multi_sent (void *arg) { terom@17: struct render_multi_node *ctx = arg; terom@16: terom@16: // mark these as sent terom@11: ctx->render_sent = 1; terom@16: ctx->self->renders_sent++; terom@11: terom@11: // have all render_sub_ctxs been sent? terom@17: if (ctx->self->renders_sent == ctx->self->node_count) { terom@13: // tell our user terom@13: _render_multi_do_sent(ctx->self); terom@11: } terom@11: } terom@11: terom@13: /* terom@16: * One render node failed, abort the whole thing terom@13: */ terom@16: static void _render_multi_fail (void *arg) { terom@17: struct render_multi_node *ctx = arg; terom@11: terom@16: // free this ctx's remote render terom@16: render_remote_free(ctx->render_remote); terom@16: ctx->render_remote = NULL; terom@16: terom@16: // cancel the rest + cb_fail terom@16: _render_multi_do_fail(ctx->self, 0); terom@16: } terom@13: terom@16: /* terom@16: * Got new data for some remote render terom@16: */ terom@16: static void _render_multi_data_raw (int fd, short event, void *arg) { terom@17: struct render_multi_node *ctx = arg; terom@16: int ret; terom@16: terom@16: assert(ctx->col <= ctx->slice_width); // check it isn't out of range terom@16: terom@16: // if our slice is full, we don't want to receive any more data terom@16: if (ctx->col == ctx->slice_width) terom@13: return; terom@13: terom@16: // read new data into our slice terom@16: ret = read(fd, terom@17: ctx->info->render_buf + ctx->col, // our segment buffer + partial segment offset terom@17: ctx->slice_width - ctx->col // how many bytes left in the segment terom@16: ); terom@16: terom@16: // errors/EOF? terom@16: if (ret == -1) { terom@16: if (errno == EAGAIN) { terom@16: // false alarm terom@16: goto reschedule; terom@16: terom@16: } else terom@16: ERROR("read"); terom@16: terom@16: } else if (ret == 0) { terom@16: // this ctx's remote render is done terom@16: render_remote_done(ctx->render_remote); terom@16: ctx->render_remote = NULL; terom@16: terom@18: // count how many are done terom@18: ctx->self->renders_done++; terom@16: terom@16: // are all of them done? terom@17: if (ctx->self->renders_done == ctx->self->node_count) { terom@16: // finish it off terom@16: _render_multi_do_done(ctx->self); terom@16: terom@16: } // else, just wait for the rest to complete terom@16: terom@16: // do *NOT* reschedule ourself, ctx->render_remote is invalid anyways (as is ctx!) terom@13: return; terom@13: } terom@17: terom@16: // ok, we received some data normally terom@16: ctx->col += ret; terom@13: terom@18: // is this segment full now? terom@13: if (ctx->col == ctx->slice_width) { terom@17: int status; terom@13: terom@18: // pass the segment in to render_slices terom@17: if ((status = render_slices_segment_done(&ctx->self->slices, ctx->info->index)) == -1) terom@17: ERROR("render_slices_segment_done"); terom@18: terom@18: // reset the col marker terom@18: ctx->col = 0; terom@18: terom@17: // row done? terom@17: if (status & SLICE_PROCESS_ROW) { terom@18: // process the row via render_slices terom@17: status = render_slices_process_row(&ctx->self->slices); terom@18: terom@13: } terom@17: terom@18: // do we need to continue? terom@18: if (status & SLICE_CONTINUE) { terom@18: // reschedule the reads in case they were ~SLICE_CONTINUE terom@18: int i; terom@18: terom@18: for (i = 0; i < ctx->self->node_count; i++) { terom@18: // just don't reschedule those that are already EOF... terom@18: if (ctx->self->nodes[i].render_remote) terom@18: render_remote_reschedule(ctx->self->nodes[i].render_remote); terom@18: } terom@18: terom@18: } else { terom@18: // don't read any more data yet, we'll be rescheduled by someone else terom@18: return; terom@18: terom@18: } terom@13: } terom@13: terom@16: // ok, reschedule ourselves terom@16: terom@16: reschedule: terom@16: // reschedule a new call once we get more data terom@16: render_remote_reschedule(ctx->render_remote); terom@16: terom@13: return; terom@13: terom@13: error: terom@13: _render_multi_do_fail(ctx->self, 0); terom@11: } terom@11: terom@13: /* terom@13: * We fed a row of pixels into render_png, and this PNG data came out. terom@13: * terom@13: * We need to pass it back to our caller terom@13: */ terom@13: static int _render_multi_png_data (const unsigned char *data, size_t length, void *arg) { terom@13: struct render_multi *ctx = arg; terom@11: terom@16: // XXX: need a better user-API to avoid these data copies terom@13: if (evbuffer_add(ctx->out_buf, data, length)) terom@13: ERROR("evbuffer_add"); terom@13: terom@16: // handle cb_data/cb_done terom@16: _render_multi_do_png_data(ctx); terom@13: terom@13: // ok terom@13: return 0; terom@13: terom@13: error: terom@13: // don't do any failing here, this will return control to a _render_* function that will handle it terom@13: return -1; terom@11: } terom@11: terom@11: struct render_multi *render_multi ( terom@13: struct render *render, // what to render terom@13: struct remote_pool *pool_info, // what render pool to use terom@11: void (*cb_sent)(void *arg), terom@11: void (*cb_data)(struct evbuffer *buf, void *arg), terom@11: void (*cb_done)(void *arg), terom@11: void (*cb_fail)(void *arg), terom@11: void *cb_arg terom@11: ) { terom@11: struct render_multi *ctx = NULL; terom@11: terom@11: // alloc the render_multi terom@11: ctx = calloc(1, sizeof(struct render_multi)); terom@11: terom@11: if (!ctx) terom@11: ERROR("calloc"); terom@13: terom@11: // store the provided callback functions terom@11: ctx->cb_sent = cb_sent; terom@11: ctx->cb_data = cb_data; terom@11: ctx->cb_done = cb_done; terom@11: ctx->cb_fail = cb_fail; terom@11: ctx->cb_arg = cb_arg; terom@11: terom@13: // store our render_png callbacks, must be before png_info terom@13: if (render_io_custom(render, &_render_multi_png_data, NULL, ctx)) terom@13: ERROR("render_io_custom"); terom@13: terom@13: // evbuffer, must be before png_info terom@13: if (!(ctx->out_buf = evbuffer_new())) terom@13: ERROR("evbuffer_new"); terom@13: terom@17: // then initialize the render_slices terom@17: if (render_slices_init(&ctx->slices, render)) terom@17: ERROR("render_slices_init"); terom@17: terom@17: // how many nodes? terom@17: ctx->node_count = render_slices_get_count(&ctx->slices); terom@11: terom@17: // load them terom@17: int i; terom@17: terom@17: for (i = 0; i < ctx->node_count; i++) { terom@17: // store the info struct terom@17: ctx->nodes[i].info = render_slices_get_slice_info(&ctx->slices, i); terom@17: terom@17: // some simple attributes terom@17: ctx->nodes[i].slice_width = ctx->nodes[i].info->render_info->img_w; terom@17: ctx->nodes[i].self = ctx; terom@17: terom@19: terom@17: // the render_remote terom@19: if (!(ctx->nodes[i].render_remote = render_remote_rawio(ctx->nodes[i].info->render_info, pool_info, terom@17: &_render_multi_sent, &_render_multi_fail, &_render_multi_data_raw, &ctx->nodes[i])) terom@17: ) terom@17: ERROR("render_remote_rawio"); terom@17: } terom@11: terom@16: // we are now alive terom@16: ctx->alive = 1; terom@11: terom@11: // I guess that's a succesfull start now terom@13: return ctx; terom@11: terom@11: error: terom@13: _render_multi_do_fail(ctx, FAIL_SILENT | FAIL_PARTIAL); terom@11: terom@13: return NULL; terom@11: } terom@11: terom@16: terom@16: void render_multi_set_recv (struct render_multi *ctx, size_t recv_threshold, size_t unread_buffer) { terom@16: terom@16: } terom@16: terom@16: int render_multi_flush (struct render_multi *ctx) { terom@16: _render_multi_do_png_data(ctx); terom@16: terom@16: return 0; terom@16: } terom@16: terom@13: void render_multi_cancel (struct render_multi *ctx) { terom@13: _render_multi_do_fail(ctx, FAIL_SILENT); terom@13: } terom@13: terom@16: void render_multi_free (struct render_multi *ctx) { terom@16: _render_multi_do_free(ctx); terom@13: }