コード例 #1
0
ファイル: isst_tcltk.c プロジェクト: cogitokat/brlcad
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;
}
コード例 #2
0
ファイル: camera.c プロジェクト: kanzure/brlcad
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
    }
}
コード例 #3
0
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) */
}
コード例 #4
0
ファイル: lgt.c プロジェクト: kanzure/brlcad
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. */
}
コード例 #5
0
/* 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;
}
コード例 #6
0
ファイル: affinity.c プロジェクト: kanzure/brlcad
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
}
コード例 #7
0
/*
 *			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( &times );
    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( &times, 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(&times) );
    bu_vls_free( &times );
    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);
}
コード例 #8
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 */
}
コード例 #9
0
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;
}
コード例 #10
0
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;
}
コード例 #11
0
ファイル: parallel.c プロジェクト: kanzure/brlcad
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;
}