static int isst_init(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { struct isst_s *isst; Togl *togl; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "pathName"); return TCL_ERROR; } if (Togl_GetToglFromObj(interp, objv[1], &togl) != TCL_OK) { return TCL_ERROR; } BU_ALLOC(isst, struct isst_s); isst->ui = 0; isst->uic = 0; BU_ALLOC(isst->tie, struct tie_s); TIENET_BUFFER_INIT(isst->buffer_image); render_camera_init(&isst->camera, bu_avail_cpus()); isst->camera.type = RENDER_CAMERA_PERSPECTIVE; isst->camera.fov = 25; Togl_SetClientData(togl, (ClientData) isst); return TCL_OK; }
void render_camera_init(render_camera_t *camera, int threads) { camera->type = RENDER_CAMERA_PERSPECTIVE; camera->view_num = 1; camera->view_list = (render_camera_view_t *) bu_malloc (sizeof(render_camera_view_t), "render_camera_init"); camera->dof = 0; camera->tilt = 0; /* The camera will use a thread for every cpu the machine has. */ camera->thread_num = threads ? threads : (uint8_t)bu_avail_cpus(); bu_semaphore_init(TIE_SEM_LAST); /* Initialize camera to rendering surface normals */ render_normal_init(&camera->render, NULL); camera->rm = RENDER_METHOD_PHONG; if (shaders == NULL) { #define REGISTER(x) render_shader_register((const char *)#x, render_##x##_init); REGISTER(component); REGISTER(cut); REGISTER(depth); REGISTER(flat); REGISTER(flos); REGISTER(grid); REGISTER(normal); REGISTER(path); REGISTER(phong); REGISTER(spall); REGISTER(surfel); #undef REGISTER } }
int main(int argc, char **argv) { int n; if (argc < 2) { fprintf(stderr, "%s", srv_usage); return 1; } while (argv[1][0] == '-') { if (BU_STR_EQUAL(argv[1], "-d")) { debug++; } else if (BU_STR_EQUAL(argv[1], "-x")) { sscanf(argv[2], "%x", (unsigned int *)&RTG.debug); argc--; argv++; } else if (BU_STR_EQUAL(argv[1], "-X")) { sscanf(argv[2], "%x", (unsigned int *)&rdebug); argc--; argv++; } else { fprintf(stderr, "%s", srv_usage); return 3; } argc--; argv++; } if (argc != 3 && argc != 4) { fprintf(stderr, "%s", srv_usage); return 2; } control_host = argv[1]; tcp_port = argv[2]; /* Note that the LIBPKG error logger can not be * "bu_log", as that can cause bu_log to be entered recursively. * Given the special version of bu_log in use here, * that will result in a deadlock in bu_semaphore_acquire(res_syscall)! * libpkg will default to stderr via pkg_errlog(), which is fine. */ pcsrv = pkg_open(control_host, tcp_port, "tcp", "", "", pkgswitch, NULL); if (pcsrv == PKC_ERROR) { fprintf(stderr, "rtsrv: unable to contact %s, port %s\n", control_host, tcp_port); return 1; } if (argc == 4) { /* Slip one command to dispatcher */ (void)pkg_send(MSG_CMD, argv[3], strlen(argv[3])+1, pcsrv); /* Prevent chasing the package with an immediate TCP close */ sleep(1); pkg_close(pcsrv); return 0; } #ifdef SO_SNDBUF /* increase the default send buffer size to 32k since we're * sending pixels more than likely. */ { int val = 32767; n = setsockopt(pcsrv->pkc_fd, SOL_SOCKET, SO_SNDBUF, (const void *)&val, sizeof(val)); if (n < 0) perror("setsockopt: SO_SNDBUF"); } #endif if (!debug) { /* A fresh process */ if (fork()) return 0; /* Go into our own process group */ n = bu_process_id(); #ifdef HAVE_SETPGID if (setpgid(n, n) < 0) perror("setpgid"); #else /* SysV uses setpgrp with no args and it can't fail, * obsoleted by setpgid. */ setpgrp(); #endif /* * Unless controller process has specifically said * that this is an interactive session, e.g., for a demo, * drop to the lowest sensible priority. */ if (!interactive) { bu_nice_set(19); /* lowest priority */ } /* Close off the world */ fclose(stdin); fclose(stdout); fclose(stderr); (void)close(0); (void)close(1); (void)close(2); /* For stdio & perror safety, reopen 0, 1, 2 */ (void)open("/dev/null", 0); /* to fd 0 */ n = dup(0); /* to fd 1 */ if (n == -1) perror("dup"); n = dup(0); /* to fd 2 */ if (n == -1) perror("dup"); #if defined(HAVE_SYS_IOCTL_H) && defined(TIOCNOTTY) n = open("/dev/tty", 2); if (n >= 0) { (void)ioctl(n, TIOCNOTTY, 0); (void)close(n); } #endif } /* Send our version string */ if (pkg_send(MSG_VERSION, PROTOCOL_VERSION, strlen(PROTOCOL_VERSION)+1, pcsrv) < 0) { fprintf(stderr, "pkg_send MSG_VERSION error\n"); return 1; } if (debug) fprintf(stderr, "PROTOCOL_VERSION='%s'\n", PROTOCOL_VERSION); /* * Now that the fork() has been done, it is safe to initialize * the parallel processing support. */ avail_cpus = bu_avail_cpus(); /* Need to set rtg_parallel non_zero here for RES_INIT to work */ npsw = avail_cpus; if (npsw > 1) { RTG.rtg_parallel = 1; } else RTG.rtg_parallel = 0; bu_semaphore_init(RT_SEM_LAST); bu_log("using %d of %d cpus\n", npsw, avail_cpus); /* * Initialize the non-parallel memory resource. * The parallel guys are initialized after the rt_dirbuild(). */ rt_init_resource(&rt_uniresource, MAX_PSW, NULL); bn_rand_init(rt_uniresource.re_randptr, MAX_PSW); BU_LIST_INIT(&WorkHead); for (;;) { struct pkg_queue *lp; fd_set ifds; struct timeval tv; /* First, process any packages in library buffers */ if (pkg_process(pcsrv) < 0) { bu_log("pkg_get error\n"); break; } /* Second, see if any input to read */ FD_ZERO(&ifds); FD_SET(pcsrv->pkc_fd, &ifds); tv.tv_sec = BU_LIST_NON_EMPTY(&WorkHead) ? 0L : 9999L; tv.tv_usec = 0L; if (select(pcsrv->pkc_fd+1, &ifds, (fd_set *)0, (fd_set *)0, &tv) != 0) { n = pkg_suckin(pcsrv); if (n < 0) { bu_log("pkg_suckin error\n"); break; } else if (n == 0) { /* EOF detected */ break; } else { /* All is well */ } } /* Third, process any new packages in library buffers */ if (pkg_process(pcsrv) < 0) { bu_log("pkg_get error\n"); break; } /* Finally, more work may have just arrived, check our list */ if (BU_LIST_NON_EMPTY(&WorkHead)) { lp = BU_LIST_FIRST(pkg_queue, &WorkHead); BU_LIST_DEQUEUE(&lp->l); switch (lp->type) { case MSG_MATRIX: ph_matrix((struct pkg_conn *)0, lp->buf); break; case MSG_LINES: ph_lines((struct pkg_conn *)0, lp->buf); break; case MSG_OPTIONS: ph_options((struct pkg_conn *)0, lp->buf); break; case MSG_GETTREES: ph_gettrees((struct pkg_conn *)0, lp->buf); break; default: bu_log("bad list element, type=%d\n", lp->type); return 33; } BU_PUT(lp, struct pkg_queue); } } return 0; /* bu_exit(0, NULL) */ }
int main(int argc, char **argv) { int i; bu_setlinebuf(stderr); bu_log("\n\nThis program is deprecated and will not be supported in future releases\n"); bu_log("\tPlease use \"rtedge\" instead\n"); bu_log("\tPlease notify \"[email protected]\" if you need enhancements to \"rtedge\"\n"); bu_log("\nPress \"Enter\" to continue\n\n"); (void)getchar(); npsw = bu_avail_cpus(); if (npsw > MAX_PSW) npsw = MAX_PSW; if (npsw > 1) RTG.rtg_parallel = 1; else RTG.rtg_parallel = 0; bu_semaphore_init(RT_SEM_LAST); init_Lgts(); if (! pars_Argv(argc, argv)) { prnt_Usage(); return 1; } for (i = 0; i < NSIG; i++) switch (i) { case SIGINT : if ((norml_sig = signal(i, SIG_IGN)) == SIG_IGN) { if (! tty) abort_sig = SIG_IGN; else { /* MEX windows on IRIS (other than the console) ignore SIGINT. */ prnt_Scroll("WARNING: Signal 1 was being ignored!"); goto tty_sig; } } else { tty_sig: norml_sig = intr_sig; abort_sig = abort_RT; (void) signal(i, norml_sig); } break; case SIGCHLD : break; /* Leave SIGCHLD alone. */ case SIGPIPE : (void) signal(i, SIG_IGN); break; case SIGQUIT : break; case SIGTSTP : (void) signal(i, stop_sig); break; } /* Main loop. */ user_Interaction(); /*NOTREACHED*/ return 99; /* Stupid UTX compiler considers this as reachable. */ }
/* 0 = no difference within tolerance, 1 = difference >= tolerance */ int analyze_raydiff(/* TODO - decide what to return. Probably some sort of left, common, right segment sets. See what rtcheck does... */ struct db_i *dbip, const char *obj1, const char *obj2, struct bn_tol *tol) { int ret; int count = 0; struct rt_i *rtip; int ncpus = bu_avail_cpus(); point_t min, mid, max; struct rt_pattern_data *xdata, *ydata, *zdata; fastf_t *rays; struct raydiff_container *state; if (!dbip || !obj1 || !obj2 || !tol) return 0; rtip = rt_new_rti(dbip); if (rt_gettree(rtip, obj1) < 0) return -1; if (rt_gettree(rtip, obj2) < 0) return -1; rt_prep_parallel(rtip, 1); /* Now we've got the bounding box - set up the grids */ VMOVE(min, rtip->mdl_min); VMOVE(max, rtip->mdl_max); VSET(mid, (max[0] - min[0])/2, (max[1] - min[1])/2, (max[2] - min[2])/2); BU_GET(xdata, struct rt_pattern_data); VSET(xdata->center_pt, min[0] - 0.1 * min[0], mid[1], mid[2]); VSET(xdata->center_dir, 1, 0, 0); xdata->vn = 2; xdata->pn = 2; xdata->n_vec = (vect_t *)bu_calloc(xdata->vn + 1, sizeof(vect_t), "vects array"); xdata->n_p = (fastf_t *)bu_calloc(xdata->pn + 1, sizeof(fastf_t), "params array"); xdata->n_p[0] = 50; /* TODO - get tolerances from caller */ xdata->n_p[1] = 50; VSET(xdata->n_vec[0], 0, max[1], 0); VSET(xdata->n_vec[1], 0, 0, max[2]); ret = rt_pattern(xdata, RT_PATTERN_RECT_ORTHOGRID); bu_free(xdata->n_vec, "x vec inputs"); bu_free(xdata->n_p, "x p inputs"); if (ret < 0) return -1; BU_GET(ydata, struct rt_pattern_data); VSET(ydata->center_pt, mid[0], min[1] - 0.1 * min[1], mid[2]); VSET(ydata->center_dir, 0, 1, 0); ydata->vn = 2; ydata->pn = 2; ydata->n_vec = (vect_t *)bu_calloc(ydata->vn + 1, sizeof(vect_t), "vects array"); ydata->n_p = (fastf_t *)bu_calloc(ydata->pn + 1, sizeof(fastf_t), "params array"); ydata->n_p[0] = 50; /* TODO - get tolerances from caller */ ydata->n_p[1] = 50; VSET(ydata->n_vec[0], max[0], 0, 0); VSET(ydata->n_vec[1], 0, 0, max[2]); ret = rt_pattern(ydata, RT_PATTERN_RECT_ORTHOGRID); bu_free(ydata->n_vec, "y vec inputs"); bu_free(ydata->n_p, "y p inputs"); if (ret < 0) return -1; BU_GET(zdata, struct rt_pattern_data); VSET(zdata->center_pt, mid[0], mid[1], min[2] - 0.1 * min[2]); VSET(zdata->center_dir, 0, 0, 1); zdata->vn = 2; zdata->pn = 2; zdata->n_vec = (vect_t *)bu_calloc(zdata->vn + 1, sizeof(vect_t), "vects array"); zdata->n_p = (fastf_t *)bu_calloc(zdata->pn + 1, sizeof(fastf_t), "params array"); zdata->n_p[0] = 50; /* TODO - get tolerances from caller */ zdata->n_p[1] = 50; VSET(zdata->n_vec[0], max[0], 0, 0); VSET(zdata->n_vec[1], 0, max[1], 0); ret = rt_pattern(zdata, RT_PATTERN_RECT_ORTHOGRID); bu_free(zdata->n_vec, "x vec inputs"); bu_free(zdata->n_p, "x p inputs"); if (ret < 0) return -1; /* Consolidate the grids into a single ray array */ { size_t i, j; rays = (fastf_t *)bu_calloc((xdata->ray_cnt + ydata->ray_cnt + zdata->ray_cnt + 1) * 6, sizeof(fastf_t), "rays"); count = 0; for (i = 0; i < xdata->ray_cnt; i++) { for (j = 0; j < 6; j++) { rays[6*count+j] = xdata->rays[6*i + j]; } count++; } for (i = 0; i < ydata->ray_cnt; i++) { for (j = 0; j < 6; j++) { rays[6*count+j] = ydata->rays[6*i + j]; } count++; } for (i = 0; i < zdata->ray_cnt; i++) { for (j = 0; j < 6; j++) { rays[6*count+j] = zdata->rays[6*i+j]; } count++; } } bu_free(xdata->rays, "x rays"); bu_free(ydata->rays, "y rays"); bu_free(zdata->rays, "z rays"); BU_PUT(xdata, struct rt_pattern_data); BU_PUT(ydata, struct rt_pattern_data); BU_PUT(zdata, struct rt_pattern_data); bu_log("ray cnt: %d\n", count); { int i, j; ncpus = 2; state = (struct raydiff_container *)bu_calloc(ncpus+1, sizeof(struct raydiff_container), "resources"); for (i = 0; i < ncpus+1; i++) { state[i].rtip = rtip; state[i].tol = 0.5; state[i].ncpus = ncpus; state[i].left_name = bu_strdup(obj1); state[i].right_name = bu_strdup(obj2); BU_GET(state[i].resp, struct resource); rt_init_resource(state[i].resp, i, state->rtip); BU_GET(state[i].left, struct bu_ptbl); bu_ptbl_init(state[i].left, 64, "left solid hits"); BU_GET(state[i].both, struct bu_ptbl); bu_ptbl_init(state[i].both, 64, "hits on both solids"); BU_GET(state[i].right, struct bu_ptbl); bu_ptbl_init(state[i].right, 64, "right solid hits"); state[i].rays_cnt = count; state[i].rays = rays; } bu_parallel(raydiff_gen_worker, ncpus, (void *)state); /* Collect and print all of the results */ for (i = 0; i < ncpus+1; i++) { for (j = 0; j < (int)BU_PTBL_LEN(state[i].left); j++) { struct diff_seg *dseg = (struct diff_seg *)BU_PTBL_GET(state[i].left, j); bu_log("Result: LEFT diff vol (%s): %g %g %g -> %g %g %g\n", obj1, V3ARGS(dseg->in_pt), V3ARGS(dseg->out_pt)); } for (j = 0; j < (int)BU_PTBL_LEN(state[i].both); j++) { struct diff_seg *dseg = (struct diff_seg *)BU_PTBL_GET(state[i].both, j); bu_log("Result: BOTH): %g %g %g -> %g %g %g\n", V3ARGS(dseg->in_pt), V3ARGS(dseg->out_pt)); } for (j = 0; j < (int)BU_PTBL_LEN(state[i].right); j++) { struct diff_seg *dseg = (struct diff_seg *)BU_PTBL_GET(state[i].right, j); bu_log("Result: RIGHT diff vol (%s): %g %g %g -> %g %g %g\n", obj2, V3ARGS(dseg->in_pt), V3ARGS(dseg->out_pt)); } } /* Free results */ for (i = 0; i < ncpus+1; i++) { for (j = 0; j < (int)BU_PTBL_LEN(state[i].left); j++) { struct diff_seg *dseg = (struct diff_seg *)BU_PTBL_GET(state[i].left, j); BU_PUT(dseg, struct diff_seg); } bu_ptbl_free(state[i].left); BU_PUT(state[i].left, struct diff_seg); for (j = 0; j < (int)BU_PTBL_LEN(state[i].both); j++) { struct diff_seg *dseg = (struct diff_seg *)BU_PTBL_GET(state[i].both, j); BU_PUT(dseg, struct diff_seg); } bu_ptbl_free(state[i].both); BU_PUT(state[i].both, struct diff_seg); for (j = 0; j < (int)BU_PTBL_LEN(state[i].right); j++) { struct diff_seg *dseg = (struct diff_seg *)BU_PTBL_GET(state[i].right, j); BU_PUT(dseg, struct diff_seg); } bu_ptbl_free(state[i].right); BU_PUT(state[i].right, struct diff_seg); bu_free((void *)state[i].left_name, "left name"); bu_free((void *)state[i].right_name, "right name"); BU_PUT(state[i].resp, struct resource); } bu_free(state, "free state containers"); } return 0; }
int parallel_set_affinity(int cpu) { #if defined(HAVE_PTHREAD_H) && defined(CPU_ZERO) /* Linux and BSD pthread affinity */ #ifdef HAVE_CPU_SET_T cpu_set_t set_of_cpus; /* bsd */ #else cpuset_t set_of_cpus; /* linux */ #endif int ret; int ncpus = bu_avail_cpus(); CPU_ZERO(&set_of_cpus); /* Set affinity to a single CPU core */ CPU_SET(cpu % ncpus, &set_of_cpus); ret = pthread_setaffinity_np(pthread_self(), sizeof(set_of_cpus), &set_of_cpus); return ret; #elif defined(HAVE_MACH_THREAD_POLICY_H) /* Mac OS X mach thread affinity hinting. Mach implements a CPU * affinity policy by default so this just sets up an additional * hint on how threads can be grouped/ungrouped. Here we set all * threads up into their own group so threads will get their own * cpu and hopefully be kept in place by Mach from there. */ thread_extended_policy_data_t epolicy; thread_affinity_policy_data_t apolicy; thread_t curr_thread = mach_thread_self(); kern_return_t ret; /* discourage interrupting this thread */ epolicy.timeshare = FALSE; ret = thread_policy_set(curr_thread, THREAD_EXTENDED_POLICY, (thread_policy_t) &epolicy, THREAD_EXTENDED_POLICY_COUNT); if (ret != KERN_SUCCESS) return -1; /* put each thread into a separate group */ apolicy.affinity_tag = cpu % bu_avail_cpus(); ret = thread_policy_set(curr_thread, THREAD_EXTENDED_POLICY, (thread_policy_t) &apolicy, THREAD_EXTENDED_POLICY_COUNT); if (ret != KERN_SUCCESS) return -1; return 0; #elif defined(HAVE_WINDOWS_H) BOOL ret = SetThreadAffinityMask(GetCurrentThread(), 1ul << cpu % bu_avail_cpus()); if (ret == 0) return -1; return 0; #else /* don't know how to set thread affinity on this platform */ cpu = 0; /* do something with cpu to avoid an unused parameter warning */ return cpu; #endif }
/* * M A I N */ int main(int argc, char **argv) { struct rt_i *rtip = NULL; char *title_file = NULL, *title_obj = NULL; /* name of file and first object */ char idbuf[RT_BUFSIZE] = {0}; /* First ID record info */ void application_init(); struct bu_vls times; int i; #if defined(_WIN32) && !defined(__CYGWIN__) setmode(fileno(stdin), O_BINARY); setmode(fileno(stdout), O_BINARY); setmode(fileno(stderr), O_BINARY); #else bu_setlinebuf( stdout ); bu_setlinebuf( stderr ); #endif #ifdef HAVE_SBRK beginptr = (char *) sbrk(0); #endif azimuth = 35.0; /* GIFT defaults */ elevation = 25.0; AmbientIntensity=0.4; background[0] = background[1] = 0.0; background[2] = 1.0/255.0; /* slightly non-black */ /* Before option processing, get default number of processors */ npsw = bu_avail_cpus(); /* Use all that are present */ if ( npsw > MAX_PSW ) npsw = MAX_PSW; /* Before option processing, do application-specific initialization */ RT_APPLICATION_INIT( &ap ); application_init(); /* Process command line options */ if ( !get_args( argc, argv ) ) { (void)fputs(usage, stderr); return 1; } /* Identify the versions of the libraries we are using. */ if (rt_verbosity & VERBOSE_LIBVERSIONS) { (void)fprintf(stderr, "%s%s%s%s\n", brlcad_ident(title), rt_version(), bn_version(), bu_version() ); } #if defined(DEBUG) (void)fprintf(stderr, "Compile-time debug symbols are available\n"); #endif #if defined(NO_BOMBING_MACROS) || defined(NO_MAGIC_CHECKING) || defined(NO_BADRAY_CECHKING) || defined(NO_DEBUG_CHECKING) (void)fprintf(stderr, "WARNING: Run-time debugging is disabled and may enhance performance\n"); #endif /* Identify what host we're running on */ if (rt_verbosity & VERBOSE_LIBVERSIONS) { char hostname[512] = {0}; #ifndef _WIN32 if ( gethostname( hostname, sizeof(hostname) ) >= 0 && hostname[0] != '\0' ) (void)fprintf(stderr, "Running on %s\n", hostname); #else sprintf(hostname, "Microsoft Windows"); (void)fprintf(stderr, "Running on %s\n", hostname); #endif } if ( bu_optind >= argc ) { fprintf(stderr, "%s: MGED database not specified\n", argv[0]); (void)fputs(usage, stderr); return 1; } if (rpt_overlap) ap.a_logoverlap = ((void (*)())0); else ap.a_logoverlap = rt_silent_logoverlap; /* If user gave no sizing info at all, use 512 as default */ if ( width <= 0 && cell_width <= 0 ) width = 512; if ( height <= 0 && cell_height <= 0 ) height = 512; /* If user didn't provide an aspect ratio, use the image * dimensions ratio as a default. */ if (aspect <= 0.0) { aspect = (fastf_t)width / (fastf_t)height; } if ( sub_grid_mode ) { /* check that we have a legal subgrid */ if ( sub_xmax >= width || sub_ymax >= height ) { fprintf( stderr, "rt: illegal values for subgrid %d,%d,%d,%d\n", sub_xmin, sub_ymin, sub_xmax, sub_ymax ); fprintf( stderr, "\tFor a %d X %d image, the subgrid must be within 0, 0,%d,%d\n", width, height, width-1, height-1 ); return 1; } } if ( incr_mode ) { int x = height; if ( x < width ) x = width; incr_nlevel = 1; while ( (1<<incr_nlevel) < x ) incr_nlevel++; height = width = 1<<incr_nlevel; if (rt_verbosity & VERBOSE_INCREMENTAL) fprintf(stderr, "incremental resolution, nlevels = %d, width=%d\n", incr_nlevel, width); } /* * Handle parallel initialization, if applicable. */ #ifndef PARALLEL npsw = 1; /* force serial */ #endif if ( npsw < 0 ) { /* Negative number means "all but" npsw */ npsw = bu_avail_cpus() + npsw; } /* allow debug builds to go higher than the max */ if (!(bu_debug & BU_DEBUG_PARALLEL)) { if ( npsw > MAX_PSW ) { npsw = MAX_PSW; } } if (npsw > 1) { rt_g.rtg_parallel = 1; if (rt_verbosity & VERBOSE_MULTICPU) fprintf(stderr, "Planning to run with %d processors\n", npsw ); } else { rt_g.rtg_parallel = 0; } /* Initialize parallel processor support */ bu_semaphore_init( RT_SEM_LAST ); /* * Do not use bu_log() or bu_malloc() before this point! */ if ( bu_debug ) { bu_printb( "libbu bu_debug", bu_debug, BU_DEBUG_FORMAT ); bu_log("\n"); } if ( RT_G_DEBUG ) { bu_printb( "librt rt_g.debug", rt_g.debug, DEBUG_FORMAT ); bu_log("\n"); } if ( rdebug ) { bu_printb( "rt rdebug", rdebug, RDEBUG_FORMAT ); bu_log("\n"); } /* We need this to run rt_dirbuild */ rt_init_resource( &rt_uniresource, MAX_PSW, NULL ); bn_rand_init( rt_uniresource.re_randptr, 0 ); title_file = argv[bu_optind]; title_obj = argv[bu_optind+1]; nobjs = argc - bu_optind - 1; objtab = &(argv[bu_optind+1]); if ( nobjs <= 0 ) { bu_log("%s: no objects specified -- raytrace aborted\n", argv[0]); return 1; } /* Echo back the command line arugments as given, in 3 Tcl commands */ if (rt_verbosity & VERBOSE_MODELTITLE) { struct bu_vls str; bu_vls_init(&str); bu_vls_from_argv( &str, bu_optind, (const char **)argv ); bu_vls_strcat( &str, "\nopendb " ); bu_vls_strcat( &str, title_file ); bu_vls_strcat( &str, ";\ntree " ); bu_vls_from_argv( &str, nobjs <= 16 ? nobjs : 16, (const char **)argv+bu_optind+1 ); if ( nobjs > 16 ) bu_vls_strcat( &str, " ..."); else bu_vls_putc( &str, ';' ); bu_log("%s\n", bu_vls_addr(&str) ); bu_vls_free(&str); } /* Build directory of GED database */ bu_vls_init( × ); rt_prep_timer(); if ( (rtip=rt_dirbuild(title_file, idbuf, sizeof(idbuf))) == RTI_NULL ) { bu_log("rt: rt_dirbuild(%s) failure\n", title_file); return 2; } ap.a_rt_i = rtip; (void)rt_get_timer( ×, NULL ); if (rt_verbosity & VERBOSE_MODELTITLE) bu_log("db title: %s\n", idbuf); if (rt_verbosity & VERBOSE_STATS) bu_log("DIRBUILD: %s\n", bu_vls_addr(×) ); bu_vls_free( × ); memory_summary(); /* Copy values from command line options into rtip */ rtip->rti_space_partition = space_partition; rtip->rti_nugrid_dimlimit = nugrid_dimlimit; rtip->rti_nu_gfactor = nu_gfactor; rtip->useair = use_air; rtip->rti_save_overlaps = save_overlaps; if ( rt_dist_tol > 0 ) { rtip->rti_tol.dist = rt_dist_tol; rtip->rti_tol.dist_sq = rt_dist_tol * rt_dist_tol; } if ( rt_perp_tol > 0 ) { rtip->rti_tol.perp = rt_perp_tol; rtip->rti_tol.para = 1 - rt_perp_tol; } if (rt_verbosity & VERBOSE_TOLERANCE) rt_pr_tol( &rtip->rti_tol ); /* before view_init */ if ( outputfile && strcmp( outputfile, "-") == 0 ) outputfile = (char *)0; /* * Initialize application. * Note that width & height may not have been set yet, * since they may change from frame to frame. */ if ( view_init( &ap, title_file, title_obj, outputfile!=(char *)0, framebuffer!=(char *)0 ) != 0 ) { /* Framebuffer is desired */ register int xx, yy; int zoom; /* Ask for a fb big enough to hold the image, at least 512. */ /* This is so MGED-invoked "postage stamps" get zoomed up big enough to see */ xx = yy = 512; if ( width > xx || height > yy ) { xx = width; yy = height; } bu_semaphore_acquire( BU_SEM_SYSCALL ); fbp = fb_open( framebuffer, xx, yy ); bu_semaphore_release( BU_SEM_SYSCALL ); if ( fbp == FBIO_NULL ) { fprintf(stderr, "rt: can't open frame buffer\n"); return 12; } bu_semaphore_acquire( BU_SEM_SYSCALL ); /* If fb came out smaller than requested, do less work */ if ( fb_getwidth(fbp) < width ) width = fb_getwidth(fbp); if ( fb_getheight(fbp) < height ) height = fb_getheight(fbp); /* If the fb is lots bigger (>= 2X), zoom up & center */ if ( width > 0 && height > 0 ) { zoom = fb_getwidth(fbp)/width; if ( fb_getheight(fbp)/height < zoom ) zoom = fb_getheight(fbp)/height; } else { zoom = 1; } (void)fb_view( fbp, width/2, height/2, zoom, zoom ); bu_semaphore_release( BU_SEM_SYSCALL ); } if ( (outputfile == (char *)0) && (fbp == FBIO_NULL) ) { /* If not going to framebuffer, or to a file, then use stdout */ if ( outfp == NULL ) outfp = stdout; /* output_is_binary is changed by view_init, as appropriate */ if ( output_is_binary && isatty(fileno(outfp)) ) { fprintf(stderr, "rt: attempting to send binary output to terminal, aborting\n"); return 14; } } /* * Initialize all the per-CPU memory resources. * The number of processors can change at runtime, init them all. */ for ( i=0; i < MAX_PSW; i++ ) { rt_init_resource( &resource[i], i, rtip ); bn_rand_init( resource[i].re_randptr, i ); } memory_summary(); #ifdef SIGUSR1 (void)signal( SIGUSR1, siginfo_handler ); #endif #ifdef SIGINFO (void)signal( SIGINFO, siginfo_handler ); #endif if ( !matflag ) { int frame_retval; def_tree( rtip ); /* Load the default trees */ do_ae( azimuth, elevation ); frame_retval = do_frame( curframe ); if (frame_retval != 0) { /* Release the framebuffer, if any */ if ( fbp != FBIO_NULL ) { fb_close(fbp); } return 1; } } else if ( !isatty(fileno(stdin)) && old_way( stdin ) ) { ; /* All is done */ } else { register char *buf; register int ret; /* * New way - command driven. * Process sequence of input commands. * All the work happens in the functions * called by rt_do_cmd(). */ while ( (buf = rt_read_cmd( stdin )) != (char *)0 ) { if ( R_DEBUG&RDEBUG_PARSE ) fprintf(stderr, "cmd: %s\n", buf ); ret = rt_do_cmd( rtip, buf, rt_cmdtab ); bu_free( buf, "rt_read_cmd command buffer" ); if ( ret < 0 ) break; } if ( curframe < desiredframe ) { fprintf(stderr, "rt: Desired frame %d not reached, last was %d\n", desiredframe, curframe); } } /* Release the framebuffer, if any */ if (fbp != FBIO_NULL) { fb_close(fbp); } return(0); }
/* * G E T _ A R G S */ int get_args( int argc, register char **argv ) { register int c; register int i; bu_optind = 1; /* restart */ #define GETOPT_STR \ ".:,:@:a:b:c:d:e:f:g:h:ij:k:l:n:o:p:q:rs:tu:v:w:x:A:BC:D:E:F:G:H:IJ:K:MN:O:P:Q:RST:U:V:WX:!:+:" while ( (c=bu_getopt( argc, argv, GETOPT_STR )) != EOF ) { switch ( c ) { case 'q': i = atoi(bu_optarg); if (i <= 0) { bu_exit(EXIT_FAILURE, "-q %d is < 0\n", i); } if ( i > BN_RANDHALFTABSIZE) { bu_exit(EXIT_FAILURE, "-q %d is > maximum (%d)\n", i, BN_RANDHALFTABSIZE); } bn_randhalftabsize = i; break; case 'h': i = sscanf(bu_optarg, "%lg,%lg,%lg,%lg", &airdensity, &haze[X], &haze[Y], &haze[Z]); break; case 't': transpose_grid = 1; break; case 'j': { register char *cp = bu_optarg; sub_xmin = atoi(cp); while ( (*cp >= '0' && *cp <= '9') ) cp++; while ( *cp && (*cp < '0' || *cp > '9') ) cp++; sub_ymin = atoi(cp); while ( (*cp >= '0' && *cp <= '9') ) cp++; while ( *cp && (*cp < '0' || *cp > '9') ) cp++; sub_xmax = atoi(cp); while ( (*cp >= '0' && *cp <= '9') ) cp++; while ( *cp && (*cp < '0' || *cp > '9') ) cp++; sub_ymax = atoi(cp); bu_log("Sub-rectangle: (%d,%d) (%d,%d)\n", sub_xmin, sub_ymin, sub_xmax, sub_ymax ); if ( sub_xmin >= 0 && sub_xmin < sub_xmax && sub_ymin >= 0 && sub_ymin < sub_ymax ) { sub_grid_mode = 1; } else { sub_grid_mode = 0; bu_log("WARNING: bad sub-rectangle, ignored\n"); } } break; case 'k': /* define cutting plane */ { fastf_t f; do_kut_plane = 1; i = sscanf(bu_optarg, "%lg,%lg,%lg,%lg", &kut_plane[X], &kut_plane[Y], &kut_plane[Z], &kut_plane[H]); if( i != 4 ) { bu_exit( EXIT_FAILURE, "ERROR: bad cutting plane\n" ); } /* verify that normal has unit length */ f = MAGNITUDE( kut_plane ); if( f <= SMALL ) { bu_exit( EXIT_FAILURE, "Bad normal for cutting plane, length=%g\n", f ); } f = 1.0 /f; VSCALE( kut_plane, kut_plane, f ); kut_plane[3] *= f; break; } case '.': nu_gfactor = (double)atof( bu_optarg ); break; case ',': space_partition = atoi(bu_optarg); break; case '@': nugrid_dimlimit = atoi(bu_optarg); break; case 'c': (void)rt_do_cmd( (struct rt_i *)0, bu_optarg, rt_cmdtab ); break; case 'C': { char buf[128] = {0}; int r, g, b; register char *cp = bu_optarg; r = atoi(cp); while ( (*cp >= '0' && *cp <= '9') ) cp++; while ( *cp && (*cp < '0' || *cp > '9') ) cp++; g = atoi(cp); while ( (*cp >= '0' && *cp <= '9') ) cp++; while ( *cp && (*cp < '0' || *cp > '9') ) cp++; b = atoi(cp); if ( r < 0 || r > 255 ) r = 255; if ( g < 0 || g > 255 ) g = 255; if ( b < 0 || b > 255 ) b = 255; #if defined(_WIN32) if (r == 0) background[0] = 0.0; else background[0] = r / 255.0; if (g == 0) background[1] = 0.0; else background[1] = g / 255.0; if (b == 0) background[2] = 0.0; else background[2] = b / 255.0; #else sprintf(buf, "set background=%f/%f/%f", r/255., g/255., b/255. ); (void)rt_do_cmd( (struct rt_i *)0, buf, rt_cmdtab ); #endif } break; case 'T': { double f; char *cp; f = 0; if ( sscanf( bu_optarg, "%lf", &f ) == 1 ) { if ( f > 0 ) rt_dist_tol = f; } f = 0; if ( (cp = strchr(bu_optarg, '/')) || (cp = strchr(bu_optarg, ',')) ) { if ( sscanf( cp+1, "%lf", &f ) == 1 ) { if ( f > 0 && f < 1 ) rt_perp_tol = f; } } bu_log("Using tolerance %lg", f); break; } case 'U': use_air = atoi( bu_optarg ); break; case 'I': interactive = 1; break; case 'i': incr_mode = 1; break; case 'S': stereo = 1; break; case 'J': sscanf( bu_optarg, "%x", &jitter ); break; case 'H': hypersample = atoi( bu_optarg ); if ( hypersample > 0 ) jitter = 1; break; case 'F': framebuffer = bu_optarg; break; case 'D': desiredframe = atoi( bu_optarg ); break; case 'K': finalframe = atoi( bu_optarg ); break; case 'N': sscanf( bu_optarg, "%x", (unsigned int *)&rt_g.NMG_debug); bu_log("NMG_debug=0x%x\n", rt_g.NMG_debug); break; case 'M': matflag = 1; break; case 'A': AmbientIntensity = atof( bu_optarg ); break; case 'x': sscanf( bu_optarg, "%x", (unsigned int *)&rt_g.debug ); break; case 'X': sscanf( bu_optarg, "%x", (unsigned int *)&rdebug ); break; case '!': sscanf( bu_optarg, "%x", (unsigned int *)&bu_debug ); break; case 's': /* Square size */ i = atoi( bu_optarg ); if ( i < 2 || i > MAX_WIDTH ) fprintf(stderr, "squaresize=%d out of range\n", i); else width = height = i; break; case 'n': i = atoi( bu_optarg ); if ( i < 2 || i > MAX_WIDTH ) fprintf(stderr, "height=%d out of range\n", i); else height = i; break; case 'W': (void)rt_do_cmd( (struct rt_i *)0, "set background=1.0/1.0/1.0", rt_cmdtab ); default_background = 0; break; case 'w': i = atoi( bu_optarg ); if ( i < 2 || i > MAX_WIDTH ) fprintf(stderr, "width=%d out of range\n", i); else width = i; break; case 'g': cell_width = atof( bu_optarg ); cell_newsize = 1; break; case 'G': cell_height = atof( bu_optarg ); cell_newsize = 1; break; case 'a': /* Set azimuth */ azimuth = atof( bu_optarg ); matflag = 0; break; case 'e': /* Set elevation */ elevation = atof( bu_optarg ); matflag = 0; break; case 'l': { char *item; /* Select lighting model # */ lightmodel= 1; /* Initialize with Full Lighting Model */ item= strtok(bu_optarg, ","); lightmodel= atoi(item); if (lightmodel == 7) { /* Process the photon mapping arguments */ item= strtok(NULL, ","); pmargs[0]= item ? atoi(item) : 16384; /* Number of Global Photons */ item= strtok(NULL, ","); pmargs[1]= item ? atof(item) : 50; /* Percent of Global Photons that should be used for Caustic Photons */ item= strtok(NULL, ","); pmargs[2]= item ? atoi(item) : 10; /* Number of Irradiance Sample Rays, Total Rays is this number squared */ item= strtok(NULL, ","); pmargs[3]= item ? atof(item) : 60.0; /* Angular Tolerance */ item= strtok(NULL, ","); pmargs[4]= item ? atoi(item) : 0; /* Random Seed */ item= strtok(NULL, ","); pmargs[5]= item ? atoi(item) : 0; /* Importance Mapping */ item= strtok(NULL, ","); pmargs[6]= item ? atoi(item) : 0; /* Irradiance Hypersampling */ item= strtok(NULL, ","); pmargs[7]= item ? atoi(item) : 0; /* Visualize Irradiance */ item= strtok(NULL, ","); pmargs[8]= item ? atof(item) : 1.0; /* Scale Lumens */ item= strtok(NULL,","); if (item) { bu_strlcpy(pmfile, item, sizeof(pmfile)); } else { pmfile[0]= 0; } } } break; case 'O': /* Output pixel file name, double precision format */ outputfile = bu_optarg; doubles_out = 1; break; case 'o': /* Output pixel file name, unsigned char format */ outputfile = bu_optarg; doubles_out = 0; break; case 'p': rt_perspective = atof( bu_optarg ); if ( rt_perspective < 0 || rt_perspective > 179 ) { fprintf(stderr, "persp=%g out of range\n", rt_perspective); rt_perspective = 0; } break; case 'u': units = bu_units_conversion(bu_optarg); if (units <= 0.0) { units = 1.0; bu_log("WARNING: bad units, using default (%s)\n", bu_units_string(units)); } break; case 'v': /* Set level of "non-debug" debugging output */ sscanf( bu_optarg, "%x", (unsigned int *)&rt_verbosity ); bu_printb( "Verbosity:", rt_verbosity, VERBOSE_FORMAT); bu_log("\n"); break; case 'E': eye_backoff = atof( bu_optarg ); break; case 'P': { /* Number of parallel workers */ int avail_cpus; avail_cpus = bu_avail_cpus(); npsw = atoi( bu_optarg ); if (npsw > avail_cpus ) { fprintf( stderr, "Requesting %d cpus, only %d available.", npsw, avail_cpus ); if ((bu_debug & BU_DEBUG_PARALLEL) || (RT_G_DEBUG & DEBUG_PARALLEL)) { fprintf(stderr, "\nAllowing surplus cpus due to debug flag.\n"); } else { fprintf( stderr, " Will use %d.\n", avail_cpus ); npsw = avail_cpus; } } if ( npsw == 0 || npsw < -MAX_PSW || npsw > MAX_PSW ) { fprintf(stderr, "Numer of requested cpus (%d) is out of range 1..%d", npsw, MAX_PSW); if ((bu_debug & BU_DEBUG_PARALLEL) || (RT_G_DEBUG & DEBUG_PARALLEL)) { fprintf(stderr, ", but allowing due to debug flag\n"); } else { fprintf(stderr, ", using -P1\n"); npsw = 1; } } } break; case 'Q': Query_one_pixel = ! Query_one_pixel; sscanf(bu_optarg, "%d,%d\n", &query_x, &query_y); break; case 'B': /* Remove all intentional random effects * (dither, etc) for benchmarking purposes. */ benchmark = 1; bn_mathtab_constant(); break; case 'b': /* Specify a single pixel to be done */ /* Actually processed in do_frame() */ string_pix_start = bu_optarg; npsw = 1; /* Cancel running in parallel */ break; case 'f': /* set expected playback rate in frames-per-second. * This actually gets stored as the delta-t per frame. */ if ( (frame_delta_t=atof( bu_optarg )) == 0.0) { fprintf(stderr, "Invalid frames/sec (%s) == 0.0\n", bu_optarg); frame_delta_t = 30.0; } frame_delta_t = 1.0 / frame_delta_t; break; case 'V': { /* View aspect */ fastf_t xx, yy; register char *cp = bu_optarg; xx = atof(cp); while ( (*cp >= '0' && *cp <= '9') || *cp == '.' ) cp++; while ( *cp && (*cp < '0' || *cp > '9') ) cp++; yy = atof(cp); if ( yy == 0 ) aspect = xx; else aspect = xx/yy; if ( aspect <= 0.0 ) { fprintf(stderr, "Bogus aspect %g, using 1.0\n", aspect); aspect = 1.0; } } break; case 'r': /* report overlapping region names */ rpt_overlap = 1; break; case 'R': /* DON'T report overlapping region names */ rpt_overlap = 0; break; case 'd': rpt_dist = atoi( bu_optarg ); break; case '+': { register char *cp = bu_optarg; switch (*cp) { case 't': output_is_binary = 0; break; default: fprintf(stderr, "ERROR: unknown option %c\n", *cp); return(0); /* BAD */ } } break; case EOF: fprintf(stderr, "ERROR: unknown option %c\n", c); return(0); /* BAD */ default: /* '?' */ fprintf(stderr, "ERROR: bad option specified\n"); return(0); /* BAD */ } } /* sanity checks for sane values */ if ( aspect <= 0.0 ) { aspect = 1.0; } /* Compat */ if (RT_G_DEBUG || R_DEBUG || rt_g.NMG_debug ) bu_debug |= BU_DEBUG_COREDUMP; if (RT_G_DEBUG & DEBUG_MEM_FULL) bu_debug |= BU_DEBUG_MEM_CHECK; if (RT_G_DEBUG & DEBUG_MEM) bu_debug |= BU_DEBUG_MEM_LOG; if (RT_G_DEBUG & DEBUG_PARALLEL) bu_debug |= BU_DEBUG_PARALLEL; if (RT_G_DEBUG & DEBUG_MATH) bu_debug |= BU_DEBUG_MATH; if (R_DEBUG & RDEBUG_RTMEM_END) bu_debug |= BU_DEBUG_MEM_CHECK; return(1); /* OK */ }
void bu_parallel(void (*func)(int, void *), size_t ncpu, void *arg) { #ifndef PARALLEL if (!func) return; /* nothing to do */ bu_log("bu_parallel(%zu., %p): Not compiled for PARALLEL machine, running single-threaded\n", ncpu, arg); /* do the work anyways */ (*func)(0, arg); #else struct thread_data *thread_context; rt_thread_t thread_tbl[MAX_PSW]; size_t avail_cpus = 1; size_t x; size_t i; /* number of threads created/ended */ size_t nthreadc; size_t nthreade; char *libbu_affinity = NULL; /* OFF by default as modern schedulers are smarter than this. */ int affinity = 0; /* ncpu == 0 means throttle our thread creation as slots become available */ int throttle = 0; struct parallel_info *parent; rt_thread_t thread; if (!func) return; /* nothing to do */ if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(%zu, %p)\n", ncpu, arg); if (ncpu > MAX_PSW) { bu_log("WARNING: bu_parallel() ncpu(%zd) > MAX_PSW(%d), adjusting ncpu\n", ncpu, MAX_PSW); ncpu = MAX_PSW; } libbu_affinity = getenv("LIBBU_AFFINITY"); if (libbu_affinity) affinity = (int)strtol(libbu_affinity, NULL, 0x10); if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) { if (affinity) bu_log("CPU affinity enabled. (LIBBU_AFFINITY=%d)\n", affinity); else bu_log("CPU affinity disabled.\n"); } /* if we're in debug mode, allow additional cpus */ if (!(bu_debug & BU_DEBUG_PARALLEL)) { /* otherwise, limit ourselves to what is actually available */ avail_cpus = bu_avail_cpus(); if (ncpu > avail_cpus) { bu_log("%zd cpus requested, but only %d available\n", ncpu, avail_cpus); ncpu = avail_cpus; } } parent = parallel_mapping(PARALLEL_GET, bu_parallel_id(), ncpu); if (ncpu < 1) { /* want to maximize threading potential, but have to throttle * thread creation. what is our parallelization limit? */ throttle = 1; /* any "zero" limit scopes propagate upward */ while (parent->lim == 0 && parent->id > 0) { parent = parallel_mapping(PARALLEL_GET, parent->parent, ncpu); } /* if the top-most parent is unspecified, use all available cpus */ if (parent->lim == 0) { ncpu = bu_avail_cpus(); } else { ncpu = parent->lim; } /* starting a "zero" bu_parallel means we get one worker * thread back (for this thread) */ bu_semaphore_acquire(BU_SEM_THREAD); if (parent->started > 0) parent->started--; bu_semaphore_release(BU_SEM_THREAD); } else if (ncpu == 1) { /* single cpu case bypasses nearly everything, just invoke */ (*func)(0, arg); parallel_mapping(PARALLEL_PUT, bu_parallel_id(), 0); return; } thread_context = (struct thread_data *)bu_calloc(ncpu, sizeof(*thread_context), "struct thread_data *thread_context"); /* Fill in the data of thread_context structures of all threads */ for (x = 0; x < ncpu; x++) { struct parallel_info *next = parallel_mapping(PARALLEL_GET, -1, ncpu); thread_context[x].user_func = func; thread_context[x].user_arg = arg; thread_context[x].cpu_id = next->id; thread_context[x].affinity = affinity; thread_context[x].parent = parent; } /* * multithreading support for SunOS 5.X / Solaris 2.x */ # if defined(SUNOS) && SUNOS >= 52 nthreadc = 0; /* Give the thread system a hint... */ { static size_t concurrency = 0; /* Max concurrency we have set */ if (ncpu > concurrency) { if (thr_setconcurrency((int)ncpu)) { bu_log("ERROR parallel.c/bu_parallel(): thr_setconcurrency(%zd) failed\n", ncpu); /* Not much to do, lump it */ } else { concurrency = ncpu; } } } /* Create the threads */ for (x = 0; x < ncpu; x++) { parallel_wait_for_slot(throttle, parent, ncpu); if (thr_create(0, 0, (void *(*)(void *))parallel_interface_arg, &thread_context[x], 0, &thread)) { bu_log("ERROR: bu_parallel: thr_create(0x0, 0x0, 0x%x, 0x0, 0, 0x%x) failed for processor thread # %d\n", parallel_interface_arg, &thread, x); /* Not much to do, lump it */ } else { if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): created thread: (thread: 0x%x) (loop:%d) (nthreadc:%zu)\n", thread, x, nthreadc); thread_tbl[nthreadc] = thread; nthreadc++; } } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) for (i = 0; i < nthreadc; i++) bu_log("bu_parallel(): thread_tbl[%d] = 0x%x\n", i, thread_tbl[i]); /* * Wait for completion of all threads. We don't wait for threads * in order. We wait for any old thread but we keep track of how * many have returned and whether it is one that we started */ nthreade = 0; for (x = 0; x < nthreadc; x++) { if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): waiting for thread to complete:\t(loop:%d) (nthreadc:%zu) (nthreade:%zu)\n", x, nthreadc, nthreade); if (thr_join((rt_thread_t)0, &thread, NULL)) { /* badness happened */ perror("thr_join"); bu_log("thr_join() failed"); } /* Check to see if this is one the threads we created */ for (i = 0; i < nthreadc; i++) { if (thread_tbl[i] == thread) { thread_tbl[i] = (rt_thread_t)-1; nthreade++; break; } } if ((thread_tbl[i] != (rt_thread_t)-1) && i < nthreadc) { bu_log("bu_parallel(): unknown thread %d completed.\n", thread); } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): thread completed: (thread: %d)\t(loop:%d) (nthreadc:%zu) (nthreade:%zu)\n", thread, x, nthreadc, nthreade); } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): %zu threads created. %zud threads exited.\n", nthreadc, nthreade); # endif /* SUNOS */ # if defined(HAVE_PTHREAD_H) /* Create the posix threads. * * Start at 1 so we can treat the parent as thread 0. */ nthreadc = 0; for (x = 0; x < ncpu; x++) { pthread_attr_t attrs; pthread_attr_init(&attrs); pthread_attr_setstacksize(&attrs, 10*1024*1024); parallel_wait_for_slot(throttle, parent, ncpu); if (pthread_create(&thread, &attrs, (void *(*)(void *))parallel_interface_arg, &thread_context[x])) { bu_log("ERROR: bu_parallel: pthread_create(0x0, 0x0, 0x%lx, 0x0, 0, %p) failed for processor thread # %zu\n", (unsigned long int)parallel_interface_arg, (void *)&thread, x); } else { if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) { bu_log("bu_parallel(): created thread: (thread: %p) (loop: %zu) (nthreadc: %zu)\n", (void*)thread, x, nthreadc); } thread_tbl[nthreadc] = thread; nthreadc++; } /* done with the attributes after create */ pthread_attr_destroy(&attrs); } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) { for (i = 0; i < nthreadc; i++) { bu_log("bu_parallel(): thread_tbl[%d] = %p\n", i, (void *)thread_tbl[i]); } # ifdef SIGINFO /* may be BSD-only (calls _thread_dump_info()) */ raise(SIGINFO); # endif } /* * Wait for completion of all threads. * Wait for them in order. */ nthreade = 0; for (x = 0; x < nthreadc; x++) { int ret; if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): waiting for thread %p to complete:\t(loop:%d) (nthreadc:%zu) (nthreade:%zu)\n", (void *)thread_tbl[x], x, nthreadc, nthreade); if ((ret = pthread_join(thread_tbl[x], NULL)) != 0) { /* badness happened */ bu_log("pthread_join(thread_tbl[%d]=%p) ret=%d\n", x, (void *)thread_tbl[x], ret); } nthreade++; thread = thread_tbl[x]; thread_tbl[x] = (rt_thread_t)-1; if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): thread completed: (thread: %p)\t(loop:%zu) (nthreadc:%zu) (nthreade:%zu)\n", (void *)thread, x, nthreadc, nthreade); } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): %zu threads created. %zu threads exited.\n", nthreadc, nthreade); # endif /* end if posix threads */ # ifdef WIN32 /* Create the Win32 threads */ nthreadc = 0; for (i = 0; i < ncpu; i++) { parallel_wait_for_slot(throttle, parent, ncpu); thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)parallel_interface_arg_stub, &thread_context[i], 0, NULL); thread_tbl[i] = thread; nthreadc++; /* Ensure that all successfully created threads are in sequential order.*/ if (thread_tbl[i] == NULL) { bu_log("bu_parallel(): Error in CreateThread, Win32 error code %d.\n", GetLastError()); --nthreadc; } } { /* Wait for other threads in the array */ DWORD returnCode; returnCode = WaitForMultipleObjects((DWORD)nthreadc, thread_tbl, TRUE, INFINITE); if (returnCode == WAIT_FAILED) { bu_log("bu_parallel(): Error in WaitForMultipleObjects, Win32 error code %d.\n", GetLastError()); } } nthreade = 0; for (x = 0; x < nthreadc; x++) { int ret; if ((ret = CloseHandle(thread_tbl[x]) == 0)) { /* Thread didn't close properly if return value is zero; don't retry and potentially loop forever. */ bu_log("bu_parallel(): Error closing thread %zu of %zu, Win32 error code %d.\n", x, nthreadc, GetLastError()); } nthreade++; thread_tbl[x] = (rt_thread_t)-1; } # endif /* end if Win32 threads */ parallel_mapping(PARALLEL_PUT, bu_parallel_id(), 0); if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(%zd) complete\n", ncpu); bu_free(thread_context, "struct thread_data *thread_context"); #endif /* PARALLEL */ return; }
HIDDEN int _rt_generate_points(int **faces, int *num_faces, point_t **points, int *num_pnts, struct bu_ptbl *hit_pnts, struct db_i *dbip, const char *obj, fastf_t delta) { int i, dir1, j; point_t min, max; int ncpus = bu_avail_cpus(); struct rt_parallel_container *state; struct bu_vls vlsstr; bu_vls_init(&vlsstr); if (!hit_pnts || !dbip || !obj) return -1; BU_GET(state, struct rt_parallel_container); state->rtip = rt_new_rti(dbip); state->delta = delta; if (rt_gettree(state->rtip, obj) < 0) return -1; rt_prep_parallel(state->rtip, 1); state->resp = (struct resource *)bu_calloc(ncpus+1, sizeof(struct resource), "resources"); for (i = 0; i < ncpus+1; i++) { rt_init_resource(&(state->resp[i]), i, state->rtip); } state->npts = (struct rt_point_container *)bu_calloc(ncpus+1, sizeof(struct rt_point_container), "point container arrays"); int n[3]; VMOVE(min, state->rtip->mdl_min); VMOVE(max, state->rtip->mdl_max); for (i = 0; i < 3; i++) { n[i] = (int)((max[i] - min[i])/state->delta) + 2; if(n[i] < 12) n[i] = 12; } int total = 0; for (i = 0; i < 3; i++) total += n[i]*n[(i+1)%3]; if (total > 1e6) total = 1e6; for (i = 0; i < ncpus+1; i++) { state->npts[i].pts = (struct npoints *)bu_calloc(total, sizeof(struct npoints), "npoints arrays"); state->npts[i].pnt_cnt = 0; state->npts[i].capacity = total; } for (dir1 = 0; dir1 < 3; dir1++) { state->ray_dir = dir1; state->ncpus = ncpus; state->delta = delta; bu_parallel(_rt_gen_worker, ncpus, (void *)state); } int out_cnt = 0; for (i = 0; i < ncpus+1; i++) { bu_log("%d, pnt_cnt: %d\n", i, state->npts[i].pnt_cnt); for (j = 0; j < state->npts[i].pnt_cnt; j++) { struct npoints *npt = &(state->npts[i].pts[j]); if (npt->in.is_set) out_cnt++; if (npt->out.is_set) out_cnt++; } } struct rt_vert **rt_verts = (struct rt_vert **)bu_calloc(out_cnt, sizeof(struct rt_vert *), "output array"); int curr_ind = 0; for (i = 0; i < ncpus+1; i++) { for (j = 0; j < state->npts[i].pnt_cnt; j++) { struct npoints *npt = &(state->npts[i].pts[j]); if (npt->in.is_set) { rt_verts[curr_ind] = &(npt->in); curr_ind++; } if (npt->out.is_set) { rt_verts[curr_ind] = &(npt->out); curr_ind++; } } } struct spr_options opts = SPR_OPTIONS_DEFAULT_INIT; (void)spr_surface_build(faces, num_faces, (double **)points, num_pnts, (const struct cvertex **)rt_verts, out_cnt, &opts); return 0; }
void bu_parallel(void (*func)(int, void *), int ncpu, void *arg) { /* avoid using the 'register' keyword in here "just in case" */ #ifndef PARALLEL bu_log("bu_parallel(%d., %p): Not compiled for PARALLEL machine, running single-threaded\n", ncpu, arg); /* do the work anyways */ (*func)(0, arg); #else struct thread_data *user_thread_data_bu; int avail_cpus = 1; int x; char *libbu_affinity = NULL; /* OFF by default until linux issue is debugged */ int affinity = 0; /* * multithreading support for SunOS 5.X / Solaris 2.x */ # if defined(SUNOS) && SUNOS >= 52 static int concurrency = 0; /* Max concurrency we have set */ # endif # if (defined(SUNOS) && SUNOS >= 52) || defined(HAVE_PTHREAD_H) int nthreadc; int nthreade; rt_thread_t thread; rt_thread_t thread_tbl[MAX_PSW]; int i; # endif /* SUNOS */ # ifdef WIN32 int nthreadc = ncpu; HANDLE hThreadArray[MAX_PSW] = {0}; int i; DWORD returnCode; # endif /* WIN32 */ if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(%d, %p)\n", ncpu, arg); if (UNLIKELY(pid_of_initiating_thread)) bu_bomb("bu_parallel() called from within parallel section\n"); pid_of_initiating_thread = bu_process_id(); if (ncpu > MAX_PSW) { bu_log("WARNING: bu_parallel() ncpu(%d) > MAX_PSW(%d), adjusting ncpu\n", ncpu, MAX_PSW); ncpu = MAX_PSW; } parallel_nthreads_started = 0; parallel_nthreads_finished = 0; libbu_affinity = getenv("LIBBU_AFFINITY"); if (libbu_affinity) affinity = (int)strtol(libbu_affinity, NULL, 0x10); if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) { if (affinity) bu_log("CPU affinity enabled. (LIBBU_AFFINITY=%d)\n", affinity); else bu_log("CPU affinity disabled.\n", affinity); } user_thread_data_bu = (struct thread_data *)bu_calloc(ncpu, sizeof(*user_thread_data_bu), "struct thread_data *user_thread_data_bu"); /* Fill in the data of user_thread_data_bu structures of all threads */ for (x = 0; x < ncpu; x++) { user_thread_data_bu[x].user_func = func; user_thread_data_bu[x].user_arg = arg; user_thread_data_bu[x].cpu_id = x; user_thread_data_bu[x].counted = 0; user_thread_data_bu[x].affinity = affinity; } /* if we're in debug mode, allow additional cpus */ if (!(bu_debug & BU_DEBUG_PARALLEL)) { avail_cpus = bu_avail_cpus(); if (ncpu > avail_cpus) { bu_log("%d cpus requested, but only %d available\n", ncpu, avail_cpus); ncpu = avail_cpus; } } /* * multithreading support for SunOS 5.X / Solaris 2.x */ # if defined(SUNOS) && SUNOS >= 52 thread = 0; nthreadc = 0; /* Give the thread system a hint... */ if (ncpu > concurrency) { if (thr_setconcurrency(ncpu)) { bu_log("ERROR parallel.c/bu_parallel(): thr_setconcurrency(%d) failed\n", ncpu); /* Not much to do, lump it */ } else { concurrency = ncpu; } } /* Create the threads */ for (x = 0; x < ncpu; x++) { if (thr_create(0, 0, (void *(*)(void *))parallel_interface_arg, &user_thread_data_bu[x], 0, &thread)) { bu_log("ERROR: bu_parallel: thr_create(0x0, 0x0, 0x%x, 0x0, 0, 0x%x) failed for processor thread # %d\n", parallel_interface_arg, &thread, x); /* Not much to do, lump it */ } else { if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): created thread: (thread: 0x%x) (loop:%d) (nthreadc:%d)\n", thread, x, nthreadc); thread_tbl[nthreadc] = thread; nthreadc++; } } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) for (i = 0; i < nthreadc; i++) bu_log("bu_parallel(): thread_tbl[%d] = 0x%x\n", i, thread_tbl[i]); /* * Wait for completion of all threads. We don't wait for threads * in order. We wait for any old thread but we keep track of how * many have returned and whether it is one that we started */ thread = 0; nthreade = 0; for (x = 0; x < nthreadc; x++) { if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): waiting for thread to complete:\t(loop:%d) (nthreadc:%d) (nthreade:%d)\n", x, nthreadc, nthreade); if (thr_join((rt_thread_t)0, &thread, NULL)) { /* badness happened */ perror("thr_join"); bu_log("thr_join() failed"); } /* Check to see if this is one the threads we created */ for (i = 0; i < nthreadc; i++) { if (thread_tbl[i] == thread) { thread_tbl[i] = (rt_thread_t)-1; nthreade++; break; } } if ((thread_tbl[i] != (rt_thread_t)-1) && i < nthreadc) { bu_log("bu_parallel(): unknown thread %d completed.\n", thread); } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): thread completed: (thread: %d)\t(loop:%d) (nthreadc:%d) (nthreade:%d)\n", thread, x, nthreadc, nthreade); } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): %d threads created. %d threads exited.\n", nthreadc, nthreade); # endif /* SUNOS */ # if defined(HAVE_PTHREAD_H) thread = 0; nthreadc = 0; /* Create the posix threads. * * Start at 1 so we can treat the parent as thread 0. */ for (x = 0; x < ncpu; x++) { pthread_attr_t attrs; pthread_attr_init(&attrs); pthread_attr_setstacksize(&attrs, 10*1024*1024); if (pthread_create(&thread, &attrs, (void *(*)(void *))parallel_interface_arg, &user_thread_data_bu[x])) { bu_log("ERROR: bu_parallel: pthread_create(0x0, 0x0, 0x%lx, 0x0, 0, %p) failed for processor thread # %d\n", (unsigned long int)parallel_interface_arg, (void *)&thread, x); /* Not much to do, lump it */ } else { if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) { bu_log("bu_parallel(): created thread: (thread: %p) (loop: %d) (nthreadc: %d)\n", (void*)thread, x, nthreadc); } thread_tbl[nthreadc] = thread; nthreadc++; } /* done with the attributes after create */ pthread_attr_destroy(&attrs); } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) { for (i = 0; i < nthreadc; i++) { bu_log("bu_parallel(): thread_tbl[%d] = %p\n", i, (void *)thread_tbl[i]); } # ifdef SIGINFO /* may be BSD-only (calls _thread_dump_info()) */ raise(SIGINFO); # endif } /* * Wait for completion of all threads. * Wait for them in order. */ thread = 0; nthreade = 0; for (x = 0; x < nthreadc; x++) { int ret; if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): waiting for thread %p to complete:\t(loop:%d) (nthreadc:%d) (nthreade:%d)\n", (void *)thread_tbl[x], x, nthreadc, nthreade); if ((ret = pthread_join(thread_tbl[x], NULL)) != 0) { /* badness happened */ bu_log("pthread_join(thread_tbl[%d]=%p) ret=%d\n", x, (void *)thread_tbl[x], ret); } nthreade++; thread_tbl[x] = (rt_thread_t)-1; if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): thread completed: (thread: %p)\t(loop:%d) (nthreadc:%d) (nthreade:%d)\n", (void *)thread, x, nthreadc, nthreade); } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(): %d threads created. %d threads exited.\n", nthreadc, nthreade); # endif /* end if posix threads */ # ifdef WIN32 /* Create the Win32 threads */ for (i = 0; i < nthreadc; i++) { hThreadArray[i] = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)parallel_interface_arg_stub, &user_thread_data_bu[i], 0, NULL); /* Ensure that all successfully created threads are in sequential order.*/ if (hThreadArray[i] == NULL) { bu_log("bu_parallel(): Error in CreateThread, Win32 error code %d.\n", GetLastError()); --nthreadc; } } /* Wait for other threads in the array */ returnCode = WaitForMultipleObjects(nthreadc, hThreadArray, TRUE, INFINITE); if (returnCode == WAIT_FAILED) { bu_log("bu_parallel(): Error in WaitForMultipleObjects, Win32 error code %d.\n", GetLastError()); } for (x = 0; x < nthreadc; x++) { int ret; if ((ret = CloseHandle(hThreadArray[x]) == 0)) { /* Thread didn't close properly if return value is zero; don't retry and potentially loop forever. */ bu_log("bu_parallel(): Error closing thread %d of %d, Win32 error code %d.\n", x, nthreadc, GetLastError()); } } # endif /* end if Win32 threads */ /* * Ensure that all the threads are REALLY finished. On some * systems, if threads core dump, the rest of the gang keeps * going, so this can actually happen (sigh). */ if (UNLIKELY(parallel_nthreads_finished != parallel_nthreads_started)) { bu_log("*** ERROR bu_parallel(%d): %d workers did not finish!\n\n", ncpu, ncpu - parallel_nthreads_finished); } if (UNLIKELY(parallel_nthreads_started != ncpu)) { bu_log("bu_parallel() NOTICE: only %d workers started, expected %d\n", parallel_nthreads_started, ncpu); } if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) bu_log("bu_parallel(%d) complete, now serial\n", ncpu); # if defined(unix) || defined(__unix) /* Cray is known to wander among various pids, perhaps others. * * At this point, all multi-tasking activity should have ceased, * and we should be just a single UNIX process with our original * PID and open file table (kernel struct u). If not, then any * output may be written into the wrong file. */ x = bu_process_id(); if (UNLIKELY(pid_of_initiating_thread != x)) { bu_log("WARNING: bu_parallel(): PID of initiating thread changed from %d to %d, open file table may be botched!\n", pid_of_initiating_thread, x); } # endif pid_of_initiating_thread = 0; /* No threads any more */ bu_free(user_thread_data_bu, "struct thread_data *user_thread_data_bu"); #endif /* PARALLEL */ return; }