void process_buffer(vx_gtk_buffer_manager_t * man, vx_code_input_stream_t * cins) { int world_id = cins->read_uint32(cins); const char * name = cins->read_str(cins); int draw_order = cins->read_uint32(cins); // find a matching layer: int update = 0; pthread_mutex_lock(&man->mutex); { layer_info_t * linfo = NULL; int layer_id = 0; zhash_iterator_t itr; zhash_iterator_init(man->layers, &itr); while(zhash_iterator_next(&itr, &layer_id, &linfo)) { if (linfo->world_id == world_id) { break; } } if (linfo == NULL) { // XXX Assert? pthread_mutex_unlock(&man->mutex); return; } // Now ensure there's a buffer_info_t in this layer. buffer_info_t * buffer = NULL; int success = zhash_get(linfo->buffers, &name, &buffer); // buffer doesn't exist yet? if (!success) { buffer = calloc(1, sizeof(buffer_info_t)); buffer->layer_id = linfo->layer_id; buffer->name = strdup(name); buffer->enabled = 1; buffer->draw_order = draw_order; buffer->man = man; zhash_put(linfo->buffers, &buffer->name, &buffer, NULL, NULL); update = 1; } // draw order changed? if (success && draw_order != buffer->draw_order) { assert(buffer != NULL); buffer->draw_order = draw_order; update = 1; } } pthread_mutex_unlock(&man->mutex); if (update == 1) queue_update_view(man); }
// XXX make static render_info_t * render_info_copy (render_info_t * rinfo) { assert(rinfo != NULL); render_info_t * rinfo_copy = render_info_create(); // copy a list of ints (ordered) for (int i = 0; i < zarray_size(rinfo->layers); i++) { layer_info_t * layer_info = NULL; zarray_get(rinfo->layers, i, &layer_info); zarray_add(rinfo_copy->layers, &layer_info); } // copy all the camera positions { zhash_iterator_t vit; uint32_t layer_id; vx_camera_pos_t *pos; zhash_iterator_init (rinfo->camera_positions, &vit); while (zhash_iterator_next (&vit, &layer_id, &pos)) { vx_camera_pos_t *pos_copy = malloc (sizeof(*pos_copy)); memcpy(pos_copy, pos, sizeof(vx_camera_pos_t)); zhash_put (rinfo_copy->camera_positions, &layer_id, &pos_copy, NULL, NULL); } } // copy each individual layer viewports { zhash_iterator_t itr; uint32_t layer_id; int *viewport; zhash_iterator_init (rinfo->layer_positions, &itr); while (zhash_iterator_next (&itr, &layer_id, &viewport)) { int *viewport_copy = malloc (4*sizeof(int)); memcpy(viewport_copy, viewport, 4*sizeof(int)); zhash_put (rinfo_copy->layer_positions, &layer_id, &viewport_copy, NULL, NULL); } } memcpy(rinfo_copy->viewport, rinfo->viewport, 4*sizeof(int)); return rinfo_copy; }
// Add all elements in B to A static void addAll(zhash_t * A, zhash_t * B) { zhash_iterator_t itr; zhash_iterator_init(B, &itr); uint64_t id = -1; void * value; while(zhash_iterator_next(&itr, &id, &value)) { if (!zhash_contains(A, &id)) zhash_put(A, &id, &value, NULL, NULL); } }
// Remove all elements from A which appear in B static void removeAll(zhash_t * A, zhash_t * B) { zhash_iterator_t itr; zhash_iterator_init(A, &itr); uint64_t id = -1; void * value; while(zhash_iterator_next(&itr, &id, &value)) { if (zhash_contains(B, &id)) zhash_iterator_remove(&itr); } }
static void print_manager(vx_resc_manager_t * mgr) { printf(" #mgr has %d worlds:\n", zhash_size(mgr->allLiveSets)); zhash_iterator_t world_itr; zhash_iterator_init(mgr->allLiveSets, &world_itr); uint32_t worldId = 0; zhash_t * buffer_map = NULL; while (zhash_iterator_next(&world_itr, &worldId, &buffer_map)) { zhash_iterator_t buffer_itr; zhash_iterator_init(buffer_map, &buffer_itr); printf(" >world %d contains %d buffers:\n", worldId, zhash_size(buffer_map)); char * buffer_name = NULL; zhash_t * res_map = NULL; while(zhash_iterator_next(&buffer_itr, &buffer_name, &res_map)) { zhash_iterator_t res_itr; zhash_iterator_init(res_map, &res_itr); printf(" >>buffer %s (%d): ", buffer_name, zhash_size(res_map)); uint64_t vrid = 0; vx_resc_t * vr = NULL; while (zhash_iterator_next(&res_itr, &vrid, &vr)) { printf("%"PRIu64" ", vrid); } printf("\n"); } } printf(" #aggregate (%d): ", zhash_size(mgr->remoteResc)); zhash_iterator_t res_itr; zhash_iterator_init(mgr->remoteResc, &res_itr); uint64_t vrid = 0; vx_resc_t * vr = NULL; while (zhash_iterator_next(&res_itr, &vrid, &vr)) { printf("%"PRIu64" ", vrid); } printf("\n"); }
static vx_code_output_stream_t * make_buffer_resource_codes(vx_buffer_t * buffer, zhash_t * resources) { vx_code_output_stream_t * couts = vx_code_output_stream_create(256); couts->write_uint32(couts, OP_BUFFER_RESOURCES); // tell the display which resources are currently in use couts->write_uint32(couts, buffer->world->worldID); couts->write_str(couts, buffer->name); couts->write_uint32(couts, zhash_size(resources)); zhash_iterator_t itr; zhash_iterator_init(resources, &itr); uint64_t guid = -1; vx_resc_t *resc = NULL; while (zhash_iterator_next(&itr, &guid, &resc)) couts->write_uint64(couts, guid); return couts; }
int eecs467_set_camera (vx_application_t *app, const float eye[3], const float lookat[3], const float up[3]) { int set = 0; eecs467_default_implementation_t *impl = app->impl; pthread_mutex_lock (&impl->mutex); { zhash_iterator_t zit; zhash_iterator_init (impl->layers, &zit); vx_layer_t *layer = NULL; while (zhash_iterator_next (&zit, NULL, &layer)) { vx_layer_camera_lookat (layer, eye, lookat, up, 1); set++; } } pthread_mutex_unlock (&impl->mutex); return set; }
// Pass in a codes describing which resources are no longer in use. Decrement user counts, // and return a list of all resources whos counts have reached zero, which therefore // should be deleted from the display using a OP_DEALLOC_RESOURCES opcode void vx_resc_manager_buffer_resources(vx_resc_manager_t * mgr, const uint8_t * data, int datalen) { if (0) print_manager(mgr); vx_code_input_stream_t * cins = vx_code_input_stream_create(data, datalen); int code = cins->read_uint32(cins); assert(code == OP_BUFFER_RESOURCES); int worldID = cins->read_uint32(cins); char * name = strdup(cins->read_str(cins)); //freed when cur_resources is eventually removed from the buffer map int count = cins->read_uint32(cins); zhash_t * cur_resources = zhash_create(sizeof(uint64_t), sizeof(vx_resc_t*), zhash_uint64_hash, zhash_uint64_equals); vx_resc_t * vr = NULL; for (int i = 0; i < count; i++) { uint64_t id = cins->read_uint64(cins); zhash_put(cur_resources, &id, &vr, NULL, NULL); } assert(cins->pos == cins->len); // we've emptied the stream vx_code_input_stream_destroy(cins); // 1 Update our records zhash_t * worldBuffers = NULL; zhash_get(mgr->allLiveSets, &worldID, &worldBuffers); if (worldBuffers == NULL) { worldBuffers = zhash_create(sizeof(char*), sizeof(zhash_t*), zhash_str_hash, zhash_str_equals); zhash_put(mgr->allLiveSets, &worldID, &worldBuffers, NULL, NULL); } zhash_t * old_resources = NULL; char * old_name = NULL; zhash_put(worldBuffers, &name, &cur_resources, &old_name, &old_resources); free(old_name); // 2 Figure out which resources have become unused: if(old_resources != NULL) { removeAll(old_resources, cur_resources); zarray_t * dealloc = zarray_create(sizeof(uint64_t)); // now 'old_resources' contains only the resources that are no longer referenced // iterate through each one, and see if there is a buffer somewhere that references it zhash_iterator_t prev_itr; zhash_iterator_init(old_resources, &prev_itr); uint64_t id = -1; vx_resc_t * vr = NULL; while(zhash_iterator_next(&prev_itr, &id, &vr)) { // Check all worlds zhash_iterator_t world_itr;// gives us all worlds zhash_iterator_init(mgr->allLiveSets, &world_itr); uint32_t wIDl = -1; zhash_t * buffer_map = NULL; while(zhash_iterator_next(&world_itr, &wIDl, &buffer_map)) { zhash_iterator_t buffer_itr; // gives us all buffers zhash_iterator_init(buffer_map, &buffer_itr); char * bName = NULL; zhash_t * resc_map = NULL; while(zhash_iterator_next(&buffer_itr, &bName, &resc_map)) { if (zhash_contains(resc_map, &id)) { goto continue_outer_loop; } } } // If none of the worlds have this resource, we need to flag removal zarray_add(dealloc, &id); continue_outer_loop: ; } // 3 Issue dealloc commands if (zarray_size(dealloc) > 0) { vx_code_output_stream_t * couts = vx_code_output_stream_create(512); couts->write_uint32(couts, OP_DEALLOC_RESOURCES); couts->write_uint32(couts, zarray_size(dealloc)); for (int i = 0; i < zarray_size(dealloc); i++) { uint64_t id = 0; zarray_get(dealloc, i, &id); couts->write_uint64(couts, id); } mgr->disp->send_codes(mgr->disp, couts->data, couts->pos); vx_code_output_stream_destroy(couts); // Also remove the resources we deallocated from remoteResc for (int i = 0; i < zarray_size(dealloc); i++) { uint64_t id = 0; zarray_get(dealloc, i, &id); assert(zhash_contains(mgr->remoteResc, &id)); zhash_remove(mgr->remoteResc, &id, NULL, NULL); } } zarray_destroy(dealloc); zhash_destroy(old_resources); } if (0) { print_manager(mgr); printf("\n\n"); } }
// this loop tries to run at X fps, and issue render commands static void * render_loop(void * foo) { state_t * state = foo; if (verbose)printf("Starting render thread!\n"); uint64_t render_count = 0; uint64_t last_mtime = vx_mtime(); double avgDT = 1.0f/state->target_frame_rate; uint64_t avg_loop_us = 3000; // initial render time guess while (state->rendering) { int64_t sleeptime = (1000000 / state->target_frame_rate) - (int64_t) avg_loop_us; if (sleeptime > 0) usleep(sleeptime); // XXX fix to include render time // Diagnostic tracking uint64_t mtime_start = vx_mtime(); // XXX avgDT = avgDT*.9 + .1 * (mtime_start - last_mtime)/1000; last_mtime = mtime_start; render_count++; if (verbose) { if (render_count % 100 == 0) printf("Average render DT = %.3f FPS = %.3f avgloopus %"PRIu64" sleeptime = %"PRIi64"\n", avgDT, 1.0/avgDT, avg_loop_us, sleeptime); } // prep the render data render_buffer_t rbuf; rbuf.state = state; rbuf.width = gtku_image_pane_get_width(state->imagePane); rbuf.height = gtku_image_pane_get_height(state->imagePane); if (rbuf.width == 0 && rbuf.height == 0) continue; // if the viewport is 0,0 // smartly reuse, or reallocate the output pixel buffer when resizing occurs GdkPixbuf * pixbuf = state->pixbufs[state->cur_pb_idx]; if (pixbuf == NULL || gdk_pixbuf_get_width(pixbuf) != rbuf.width || gdk_pixbuf_get_height(pixbuf) != rbuf.height) { if (pixbuf != NULL) { g_object_unref(pixbuf); free(state->pixdatas[state->cur_pb_idx]); } state->pixdatas[state->cur_pb_idx] = malloc(rbuf.width*rbuf.height*3); // can't stack allocate, can be too big (retina) pixbuf = gdk_pixbuf_new_from_data(state->pixdatas[state->cur_pb_idx], GDK_COLORSPACE_RGB, FALSE, 8, rbuf.width, rbuf.height, rbuf.width*3, NULL, NULL); // no destructor fn for pix data, handle manually state->pixbufs[state->cur_pb_idx] = pixbuf; } // second half of init: rbuf.out_buf = gdk_pixbuf_get_pixels(pixbuf); rbuf.format = GL_RGB; rbuf.rendered = 0; // 1 compute all the viewports render_info_t * rinfo = render_info_create(); rinfo->viewport[0] = rinfo->viewport[1] = 0; rinfo->viewport[2] = rbuf.width; rinfo->viewport[3] = rbuf.height; { zhash_iterator_t itr; uint32_t layer_id = 0; layer_info_t * linfo = NULL; zhash_iterator_init(state->layer_info_map, &itr); while(zhash_iterator_next(&itr, &layer_id, &linfo)){ zarray_add(rinfo->layers, &linfo); } zarray_sort(rinfo->layers, zvx_layer_info_compare); } zarray_t * fp = zarray_create(sizeof(matd_t*)); matd_t *mm = matd_create(4,4); zarray_add(fp, &mm); matd_t *pm = matd_create(4,4); zarray_add(fp, &pm); pthread_mutex_lock(&state->mutex); for (int i = 0; i < zarray_size(rinfo->layers); i++) { layer_info_t *linfo = NULL; zarray_get(rinfo->layers, i, &linfo); int * viewport = vx_viewport_mgr_get_pos(linfo->vp_mgr, rinfo->viewport, mtime_start); vx_camera_pos_t *pos = default_cam_mgr_get_cam_pos(linfo->cam_mgr, viewport, mtime_start); // store viewport, pos zhash_put(rinfo->layer_positions, &linfo->layer_id, &viewport, NULL, NULL); zhash_put(rinfo->camera_positions, &linfo->layer_id, &pos, NULL, NULL); // feed the actual camera/projection matrix to the gl side vx_camera_pos_model_matrix(pos,mm->data); vx_camera_pos_projection_matrix(pos, pm->data); matd_t * pmmm = matd_multiply(pm,mm); zarray_add(fp, &pmmm); float pm16[16]; vx_util_copy_floats(pmmm->data, pm16, 16); float eye3[16]; vx_util_copy_floats(pos->eye, eye3, 3); vx_gl_renderer_set_layer_render_details(state->glrend, linfo->layer_id, viewport, pm16, eye3); } // 2 Render the data task_thread_schedule_blocking(gl_thread, render_task, &rbuf); render_info_t * old = state->last_render_info; state->last_render_info = rinfo; pthread_mutex_unlock(&state->mutex); // 3 if a render occurred, then swap gtk buffers if (rbuf.rendered) { // point to the correct buffer for the next render: state->cur_pb_idx = (state->cur_pb_idx +1 ) % 2; // flip y coordinate in place: vx_util_flipy(rbuf.width*3, rbuf.height, rbuf.out_buf); // swap the image's backing buffer g_object_ref(pixbuf); // XXX Since gtku always unrefs with each of these calls, increment accordingly gtku_image_pane_set_buffer(state->imagePane, pixbuf); } // 3.1 If a movie is in progress, also need to serialize the frame pthread_mutex_lock(&state->movie_mutex); if (state->movie_file != NULL) { int last_idx = (state->cur_pb_idx + 1) % 2; GdkPixbuf * pb = state->pixbufs[last_idx]; movie_frame_t * movie_img = calloc(1, sizeof(movie_frame_t)); movie_img->mtime = mtime_start; movie_img->width = gdk_pixbuf_get_width(pb); movie_img->height = gdk_pixbuf_get_height(pb); movie_img->stride = 3*movie_img->width; movie_img->buf = malloc(movie_img->stride*movie_img->height); memcpy(movie_img->buf, state->pixdatas[last_idx], movie_img->stride*movie_img->height); // Alloc in this thread, dealloc in movie thread zarray_add(state->movie_pending, & movie_img); pthread_cond_signal(&state->movie_cond); } pthread_mutex_unlock(&state->movie_mutex); // cleanup if (old) render_info_destroy(old); zarray_vmap(fp, matd_destroy); zarray_destroy(fp); uint64_t mtime_end = vx_mtime(); avg_loop_us = (uint64_t)(.5*avg_loop_us + .5 * 1000 * (mtime_end - mtime_start)); } if (verbose) printf("Render thread exiting\n"); pthread_exit(NULL); }
void * camera_loop(void * data) { state_t * state = data; sleep(2); // wait for 2 seconds before starting the animation matd_t * zaxis = matd_create(3,1); zaxis->data[2] = 1; vx_buffer_add_back(vx_world_get_buffer(state->world, "cam-circle"), vxo_chain(vxo_mat_scale(CAM_RADIUS), vxo_circle(vxo_lines_style(vx_green, 3)))); vx_buffer_swap(vx_world_get_buffer(state->world, "cam-circle")); int64_t start_mtime = vx_util_mtime(); // tell each layer to follow pthread_mutex_lock(&state->mutex); { zhash_iterator_t itr; zhash_iterator_init(state->layers, &itr); vx_display_t * key; vx_layer_t * vl; while(zhash_iterator_next(&itr, &key, &vl)) { if (1) { float eye3[] = {CAM_RADIUS,-CAM_RADIUS,45.0f}; float lookat3[] = {CAM_RADIUS,0,0.0f}; float up3[] = {0,1,0}; vx_layer_camera_lookat(vl, eye3, lookat3, up3, 0); } } } pthread_mutex_unlock(&state->mutex); while (state->running) { // 5 seconds revolutions double rad = ( (vx_util_mtime() - start_mtime) % 5000) * 2* M_PI / 5e3; // compute the current position and orientation of the "robot" matd_t * orientation = matd_angle_axis_to_quat(rad, zaxis); matd_t * pos = matd_create(3,1); pos->data[0] = cos(rad) * CAM_RADIUS; pos->data[1] = sin(rad) * CAM_RADIUS; // tell each layer to follow pthread_mutex_lock(&state->mutex); { zhash_iterator_t itr; zhash_iterator_init(state->layers, &itr); vx_display_t * key; vx_layer_t * vl; while(zhash_iterator_next(&itr, &key, &vl)) { vx_layer_camera_follow(vl, pos->data, orientation->data, 1); } } pthread_mutex_unlock(&state->mutex); vx_buffer_add_back(vx_world_get_buffer(state->world, "robot-proxy"), vxo_chain(vxo_mat_quat_pos(orientation->data, pos->data), vxo_box(vxo_lines_style(vx_purple, 3)))); vx_buffer_swap(vx_world_get_buffer(state->world, "robot-proxy")); matd_destroy(orientation); matd_destroy(pos); usleep(100000); } matd_destroy(zaxis); return NULL; }
static void * run_process(void * data) { vx_world_t * world = data; while (world->process_running) { vx_world_listener_t * listener = NULL; char * buffer_name = NULL; // 1) Wait until there's data pthread_mutex_lock(&world->queue_mutex); while (zarray_size(world->buffer_queue) == 0 && zarray_size(world->listener_queue) == 0 && world->process_running) { pthread_cond_wait(&world->queue_cond, &world->queue_mutex); } if (!world->process_running) { // XXX cleaning out the queue? pthread_mutex_unlock(&world->queue_mutex); break; } // Processing new listeners takes priority if ( zarray_size(world->listener_queue) > 0) { zarray_get(world->listener_queue, 0, &listener); zarray_remove_index(world->listener_queue, 0, 0); } else { assert(zarray_size(world->buffer_queue) > 0); zarray_get(world->buffer_queue, 0, &buffer_name); zarray_remove_index(world->buffer_queue, 0, 0); } pthread_mutex_unlock(&world->queue_mutex); // Operation A: New listener if (listener != NULL) { // re-transmit each buffer that has already been serialized pthread_mutex_lock(&world->buffer_mutex); zhash_iterator_t itr; zhash_iterator_init(world->buffer_map, &itr); char * name = NULL; vx_buffer_t * buffer = NULL; while(zhash_iterator_next(&itr, &name, &buffer)) { if (buffer->front_codes->pos != 0) { vx_code_output_stream_t * bresc_codes = make_buffer_resource_codes(buffer, buffer->front_resc); // Doing a swap in 3 steps (instead of 4) like this // is only safe if the listener is brand new, which we are // guaranteed is the case in this chunk of code listener->send_codes(listener, bresc_codes->data, bresc_codes->pos); listener->send_resources(listener, buffer->front_resc); listener->send_codes(listener, buffer->front_codes->data, buffer->front_codes->pos); vx_code_output_stream_destroy(bresc_codes); } } pthread_mutex_unlock(&world->buffer_mutex); } // Operation B: buffer swap if (buffer_name != NULL) { vx_buffer_t * buffer = NULL; pthread_mutex_lock(&world->buffer_mutex); zhash_get(world->buffer_map, &buffer_name, &buffer); pthread_mutex_unlock(&world->buffer_mutex); delayed_swap(buffer); } } pthread_exit(NULL); }