Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!sdd.hp.com!ucsd!ucbvax!lsr-vax.UUCP!lmo From: lmo@lsr-vax.UUCP ("Lance M. Optican - LMO") Newsgroups: comp.sys.sgi Subject: (none) Message-ID: <9011261626.AA24376@> Date: 26 Nov 90 16:26:32 GMT Sender: daemon@ucbvax.BERKELEY.EDU Organization: The Internet Lines: 644 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@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@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 #include #include #include #include #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 #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); }