(none)

Lance M. Optican - LMO lmo at lsr-vax.UUCP
Tue Nov 27 03:26:32 AEST 1990


Subject:  Parallel Programming

To all:

	I am learning to use the parallel
library on my 4D/140.  As an exercise, I took
Paul Haeberli's amusing chaos program "smoke"
and modified it to run in parallel and display
in 3-D (and stereo, if you have it!).  There is
also some code to give timing info for testing
efficiency of parallelism.  I have also included
my animation version of SGI's stereo perspective
call.

	I hope some of you find this of interest.
The m_fork(), etc. calls on the Iris work
wonderfully, and are easy to use.  I highly
recommend the minimal effort it takes to go
through the Iris manual on parallel programming.
Another useful reference is:
  "Guide to Parallel Programming on Sequent
   Computer Systems", 2nd edition, A. Osterhaug, Ed.,
   Prentice-Hall, 1989.

	To run, just cut this note apart, and
type "make".  Then "smoke" with no parameters
to see the help message.

	Thanks to Paul Haeberli for posting his
original program.

			Good Luck!

----------------------------------------------+--------------------------
   Lance M. Optican			      | uunet.uu.net!lsr-vax!lmo 
   Laboratory of Sensorimotor Research	      | lsr-vax!lmo at uunet.uu.net
   National Eye Institute, NIH, Bethesda, MD  |	(301) 496-3549    
----------------------------------------------+--------------------------

---------------------------------------------
Makefile
---------------------------------------------
#
#

smoke: smoke.o stereo.o
	cc -O -o smoke smoke.o stereo.o -lmpc -lgl_s -lm

smoke.o: smoke.c
	cc -align16 -c -O smoke.c

stereo.o: stereo.c
	cc -align16 -c -O stereo.c

---------------------------------------------
smoke.c
---------------------------------------------
/*
 *	smoke -
 *		Render a 3D Chaotic attractor on your IRIS.
 *		create an image of a 3-D chaotic attractor.
 *	From a paper by Clifford Pickover called "A Note on
 *	Rendering 3-D Strange Attractors" from Computers
 *	and Graphics Vol 12, No 2. pp. 263-267, 1988.
 *
 *			Paul Haeberli - 1990
 * paul haeberli
 * paul at sgi.com
 * 415-962-3665
 *
 * 7nov90	LM Optican	Modified to run in parallel, 3-D
 *				and stereo!  Use 3-producer and
 *				1-consumer model of parallelism.
 *
 */
#include <stdio.h>
#include <math.h>
#include <task.h>
#include <gl/gl.h>
#include <device.h>
#include "stereo.h"

/* timing info ... */
long	start=0;		/* second of start			*/
long	stop =0;		/* second of stop			*/
/* timing info ... */
int num_proc = 3;

/*
 * make global for sharing
 */
float xxmin, xxmax;
float yymin, yymax;
float zzmin, zzmax;
float xinc, yinc, zinc;
float neighborhood;
int pstop = 0;
int iter;
int istereo = 0;
float a, b, c, d, e;
float *xi, *yi, *zi;
short val;

#define Y_WIDTH 4.0    /* width of y-field in user units */
Angle fovy = 700;
Coord nearclip = 0.25, farclip = 12.0;			/* clipping planes */
Coord cx_dist = 0, cy_dist = 0, cz_dist = 4;	/* camera distances */
float eye_sep = 0.15;
Object O_view = 0, O_look = 0, O_clr = 0;
Angle xangle = 0, yangle = 0, zangle = 0;	/* rotation angles */

typedef struct list {
	Coord x, y, z;
	unsigned char r, g, b;
} LIST;
#define SIZE	512
#define LSIZE	2500
LIST buf[LSIZE] = { 0 };	/* x,y,z & r, g, b values */
int lfree = LSIZE;

#define STEP 250

main(argc, argv)
	int argc;
	char **argv;
{
	int i;

	parse_com(argc, argv);	/* parse the command line */


	xxmax = 2.0;
	xxmin = -2.0;
	yymax = 2.0;
	yymin = -2.0;
	zzmax = 2.0;
	zzmin = -2.0;
	xinc = SIZE/(xxmax-xxmin);
	yinc = SIZE/(yymax-yymin);
	zinc = SIZE/(zzmax-zzmin);
	a = 2.24;
	b = 0.43;
	c = -0.65;
	d = -2.43;
	e = 1.00;

	set_graph();


	/*
	 * allocate processors
	 */
	i = cpus_online() - 1;
	if (i < 1) i = 1;
	if (num_proc > i) num_proc = i;
	m_set_procs(num_proc+1);	/* +1 for graphics consumer */

	if (m_get_numprocs() != (num_proc + 1)) {
		fprintf(stderr, "too few processors !\n");
		exit(1);
	}

	iter = 3 * 1000 * LSIZE / num_proc;

	xi = (float *) malloc(num_proc * sizeof(*xi));
	yi = (float *) malloc(num_proc * sizeof(*yi));
	zi = (float *) malloc(num_proc * sizeof(*zi));
	for (i = 0; i < num_proc; i++) {	/* start in different places */
		xi[i] = yi[i] = zi[i] = i * xxmax / 1e2;
	}
	xangle = yangle = zangle = 0;

	start = time ( (long *) 0 );	/* set up for current second		*/
	do_pix();
	stop = time ( (long *) 0 );	/* set up for current second		*/

	m_kill_procs(); 		/* get rid of parallel procs */

	ringbell();
	printf("smoke done!!!\n");
	printf("Elapsed Time with %d %s = %d sec\n", num_proc, (num_proc == 1 ? "cpu" : "cpus"),(stop - start));

	while(1) {
		pstop = 0;
		do_draw();
		if(qread(&val) == REDRAW) drawit();
	}
}

set_graph()
{
	char namebuf[100];

	sprintf(namebuf, "smoke: %d CPUs\n", num_proc);
	if (istereo) {
		neighborhood = 2 * (xxmax - xxmin) / (WX_MAX - WX_MIN);
		prefposition(WX_MIN, WX_MAX, WY_MIN, WY_MAX);
	}
	else {
		neighborhood = 2 * (xxmax - xxmin) / SIZE;
		prefposition(10, 10+SIZE, 10, 10+SIZE);
	}
	neighborhood *= neighborhood;

	foreground();	/* required for parallel */
	winopen(namebuf);

	RGBmode();
	doublebuffer();
	gconfig();
	cpack(0x000000);
	clear();
	swapbuffers();
	clear();

	makeobj(O_view);
		perspective(fovy, 1.0, nearclip,  farclip);
	closeobj();
	makeobj(O_look);
		lookat(cx_dist, cy_dist, cz_dist, 0.0, 0.0, 0.0, 0);
	closeobj();

	if (istereo) {
		stereop_set(fovy, 1.0, nearclip, farclip,
			cz_dist, eye_sep / 2, O_look);
	}
	else {
		viewport(10, 10+SIZE, 10, 10+SIZE);
		callobj(O_view);
	}
}


/* PARSE_COM
 * parse command line
 */
parse_com(argc, argv)
	register int argc;
	register char **argv;
{
	register char * str;

	/* are there any parameters? */
	if (argc < 2) {
		fprintf(stderr, "Usage:  smoke [flags]\n");
		fprintf(stderr, "Flags:\n");
		fprintf(stderr, "\t-P n: Use n processors\n");
		fprintf(stderr, "\t-s:	stereo\n");
		return;
	}

	/* process parameters */
	for (argv++; *argv > 0; argv++) {
		str = *argv;

		/*
		 * do flags
		 */
		if (*str == '-') {
			while(*++str) switch(*str) {
			case 'P':	/* processor number */
				sscanf(*(++argv), "%d", &num_proc);
				break;
			case 's':	/* stereo */
				istereo = 1;
				break;
			default:
				fprintf(stdout, "BAD FLAG:  %c\n",*str);
				exit(2);
				break;
			} /* end while switch */
		}
		else {
		}
	}
}

do_draw()
{
	int i;

	for (i = 0; i < 1000; i++) {
		xangle += 2;
		yangle += 5;
		zangle += 9;
		if (xangle >= 3600) xangle -= 3600;
		if (yangle >= 3600) yangle -= 3600;
		if (zangle >= 3600) zangle -= 3600;
		draw_eyes();
		if (pstop == num_proc) return(0);
	}
	return(1);
}

draw_eyes()
{
	int eye;

	if (istereo) {
	  for(eye = 0; eye < 2; eye++) {
		pushmatrix();
		stereop_do(eye);
		rotate(xangle, 'x');
		rotate(yangle, 'y');
		rotate(zangle, 'z');
		draw_all();
		popmatrix();
	  }
	}
	else {
		pushmatrix();
		perspective(fovy, 1.0, nearclip,  farclip);
		lookat(cx_dist, cy_dist, cz_dist, 0.0, 0.0, 0.0, 0);
		rotate(xangle, 'x');
		rotate(yangle, 'y');
		rotate(zangle, 'z');
		draw_all();
		popmatrix();
	}
	swapbuffers();
}

drawit()
{
	reshapeviewport();

	draw_eyes();
}

draw_all()
{
	int i, j;
	register LIST *p;

	cpack(0x000000);
	clear();
	for (i = 0, p = buf; i < LSIZE; i++, p++) {
		RGBcolor((short) p->r, (short) p->g, (short) p->b);
		pnt(p->x, p->y, p->z);
	}
}


LIST *
get_index(x, y, z)
	float x, y, z;
{
	LIST *p;
	static int indx = 0;

	p = &buf[indx];
	p->x = x;
	p->y = y;
	p->z = z;
	p->r = p->g = p->b = 150;
	if (++indx == LSIZE) indx = 0;	
	return(p);
}

LIST *
near(x, y, z)
	float x, y, z;
{
	register LIST *p, *m;
	register int i, im;
	float d, dm, dx, dy, dz;
	
	dm = 3 * 10 * 10;
	m = NULL;
	if (lfree) {
		for (i = im = 0, p = buf; i < LSIZE; i++, p++) {
			if (p->r || p->g || p->b) {
				dx = p->x - x;
				dx *= dx;
				if (dx > neighborhood) continue;

				dy = p->y - y;
				dy *= dy;
				if (dy > neighborhood) continue;

				dz = p->z - z;
				dz *= dz;
				if (dz > neighborhood) continue;

				d = dx + dy + dz;
				if (d < dm) {
					dm = d;
					m = p;
					im = i;
				}
			}
			else break;	/* have hit a new list element */
		}
		if (i == LSIZE) return(NULL);
		if (m == NULL) {
			--lfree;
			p->x = x;
			p->y = y;
			p->z = z;
			return(p);
		}
	}
	else {	/* use only existing points */
		for (i = im = 0, p = buf; i < LSIZE; i++, p++) {
			dx = p->x - x;
			dx *= dx;
			dy = p->y - y;
			dy *= dy;
			dz = p->z - z;
			dz *= dz;
			d = dx + dy + dz;
			if (d < dm) {
				dm = d;
				m = p;
				im = i;
			}
		}
	}
	return(m);
}

para_pix()
{
	register int i, j, me;
	static int first[3] = {1, 1, 1};
	static float x[3], y[3], z[3];
	float xx, yy, zz;
	register LIST *p;
	
	me = m_get_myid();

	if (me == num_proc) {
		while(do_draw());	/* start the consumer */
		return;
	}

	for(; (i = m_next()) < iter; ) {
		if (first[me] == 1) {
			first[me] = 0;
			x[me] = xi[me];
			y[me] = yi[me];
			z[me] = zi[me];
		}
		xx = sin(a*y[me])-z[me]*cos(b*x[me]);
		yy = z[me]*sin(c*x[me])-cos(d*y[me]);
		zz = e*sin(x[me]);
		x[me] = xx;
		y[me] = yy;
		z[me] = zz;

		if (xx < xxmin || xxmax < xx ||
		    yy < yymin || yymax < yy ||
		    zz < zzmin || zzmax < zz) continue;

		m_lock();
			p = get_index(xx, yy, zz);
		m_unlock();

		switch(me) {
		case 0:
			p->r = 255;
			break;
		case 1:
			p->g = 255;
			break;
		case 2:
			p->b = 255;
			break;
		}
	}	
	pstop++;
}

do_pix()
{
	/*
	 * PARALLEL THREADS
	 */

	m_fork(para_pix);

	/*
	 * SINGLE THREAD
	 */
}
---------------------------------------------
stereo.h
---------------------------------------------
/*
 * STEREO.H
 *	Header for carrying out stereo perspective
 * transformations in SGI GL immediate mode
 */

#ifndef STEREO_H
#define STEREO_H

/*
 * window sizes
 */
#define WX_MIN	0
#define WX_MAX	1279
#define WY_MIN	0
#define WY_MAX	1023

#define WX_SIZE (WX_MAX - WX_MIN + 1)
#define WY_SIZE (WY_MAX - WY_MIN + 1)


/*
 * RV_ are the y-coords of the right-view's viewport
 * LV_ are the y-coords of the left-view's viewport
 */
#define STEREO_VBLANK	41
#define RV_BOTTOM	WY_MIN
#define RV_TOP		(RV_BOTTOM + (WY_SIZE - STEREO_VBLANK)/2)
#define LV_BOTTOM	(RV_TOP + STEREO_VBLANK)
#define LV_TOP		WY_MAX

#define YMAXSTEREO	RV_TOP
#define YOFFSET		LV_BOTTOM

extern void stereopersp(Angle, float, float, float, float, float);
extern void sterop_set(Angle, float, float, float, float, float, Object);
extern void stereop_do(int);
#endif STEREO_H

---------------------------------------------
stereo.c
---------------------------------------------
#include <gl/gl.h>
#include "stereo.h"
#include "math.h"

extern void translate(float, float, float);
extern void window(float, float, float,float, float, float);

static float st_left[2], st_right[2], st_bottom, st_top, st_near,
	st_far, st_eye[2];
Object O_view_left = 0;
Object O_view_right = 0;

/*
 * calculate transformations and do them.  This is
 * the original function from "Iris Universe", but is
 * very slow.  This can be replaced by calls to
 * sterop_set() and stereop_do() for speeding up loops.
 */
void
stereopersp(fovy, aspect, near, far, conv, eye_sep)
	Angle fovy;
	float aspect, near, far, conv, eye_sep;
{
	float left, right, top, bottom;
	float gltan;

	gltan = tan(fovy/2.0/10.0*M_PI/180.0);

	top = gltan * near;
	bottom = -top;

	gltan = tan(fovy*aspect/2.0/10.0*M_PI/180.0);

	left = -gltan*near - eye_sep*near/conv;
	right = gltan*near - eye_sep*near/conv;

	window(left, right, bottom, top, near, far);
	translate(-eye_sep, 0.0, 0.0);
}

/*
 * function to set up transformations as objects
 * to be called in a fast display loop.  This is
 * like the perspective call for non-3D.  Note that
 * "conv" is the distance to the point at which the
 * eyes converge.
 */
void
stereop_set(fovy, aspect, near, far, conv, eye_sep, O_look)
	Angle fovy;
	float aspect, near, far, conv, eye_sep;
	Object O_look;	/* must be set to object for lookat() */
{
	float gltan;

	gltan = tan(fovy/2.0/10.0*M_PI/180.0);

	st_top = gltan * near;
	st_bottom = -st_top;

	gltan = tan(fovy*aspect/2.0/10.0*M_PI/180.0);

	/*
	 * right eye
	 */
	st_left[1] = near * (-gltan - eye_sep/conv);
	st_right[1] = near * (gltan - eye_sep/conv);


	/*
	 * left eye
	 */
	st_left[0] = near * (-gltan + eye_sep/conv);
	st_right[0] = near * (gltan + eye_sep/conv);

	st_eye[0] = -eye_sep;
	st_eye[1] = eye_sep;
	st_near = near;
	st_far = far;

	/*
	 * set up display objects for speed
	 */
	if (O_view_left == 0) O_view_left = genobj();
	makeobj(O_view_left);
		viewport(WX_MIN, WX_MAX, LV_BOTTOM, LV_TOP);
		window(st_left[0], st_right[0], st_bottom, st_top,
			st_near, st_far);
		translate(-st_eye[0], 0.0, 0.0);
		callobj(O_look);
	closeobj();

	if (O_view_right == 0) O_view_right = genobj();
	makeobj(O_view_right);
		viewport(WX_MIN, WX_MAX, RV_BOTTOM, RV_TOP);
		window(st_left[1], st_right[1], st_bottom, st_top,
			st_near, st_far);
		translate(-st_eye[1], 0.0, 0.0);
		callobj(O_look);
	closeobj();
}

/*
 * sets up stereo window for:
 * rt_eye = 0 left eye
 *	  = 1 right eye
 */
void
stereop_do(rt_eye)
	int rt_eye;
{
	if (rt_eye) callobj(O_view_right);
	else	    callobj(O_view_left);
}



More information about the Comp.sys.sgi mailing list