[comp.benchmarks] Truth or Dare?

thomson@cs.utah.edu (Rich Thomson) (03/28/91)

Several times on comp.graphics the SGI folks have offered up the
famous "1 million polygons/second" quotation from marketing literature
when referring to peak performance on the VGX series of machines.

The polygons in question are 50 pixel triangle strips that are flat
shaded.  Also, they may have rendered them without the Z buffer turned
on (the spec sheet is away from me at the moment).

What I would like to know is: does anybody have a program, no matter
how contorted, that actually achieves this number?  As far as I can
tell the number is derived from theoretical peak performance of the
VGX pipeline, or some portion of it.

This highlights a sore point in graphics benchmarking in general,
which is getting anybody to agree on the same thing to be measured.
Hopefully the PLB and GPC efforts of NCGA will alleviate this problem,
or at least translate it into ``who has the most optimized GPC
program''.

When I asked the local SGI rep for more information about these
numbers, he sent me a data sheet that described the particular
polygons in question (revealing that they were as described above),
but no working code.

So: is this a "theoretical" number, or is it attainable by a program,
no matter how contorted?

						-- Rich
Rich Thomson	thomson@cs.utah.edu  {bellcore,hplabs,uunet}!utah-cs!thomson
    ``Read my MIPs -- no new VAXes!!''  --George Bush after sniffing freon

bruceh@sgi.com (Bruce R. Holloway) (03/29/91)

In article <1991Mar27.174104.26867@hellgate.utah.edu> thomson@cs.utah.edu (Rich Thomson) writes:
>Several times on comp.graphics the SGI folks have offered up the
>famous "1 million polygons/second" quotation from marketing literature
>when referring to peak performance on the VGX series of machines.
>
>The polygons in question are 50 pixel triangle strips that are flat
>shaded.  Also, they may have rendered them without the Z buffer turned
>on (the spec sheet is away from me at the moment).
>...
>So: is this a "theoretical" number, or is it attainable by a program,
>no matter how contorted?

We have a suite of performance measurement programs which allow lots of
primitives with different characteristics to be tried.  I ran a certain
case and got the following output:

* Mesh, DisplayList, Subpixel, RGBmode, Flat, Area = 50.000000, PYM_FILL, MeshesPerSecond = 886681

My machine is only a 5-span, which means I have just one raster memory board.
Apparently the quoted performance numbers are for a 10-span system.  With
slightly smaller triangles I obtained:

* Mesh, DisplayList, Subpixel, RGBmode, Flat, Area = 40.500000, PYM_FILL, MeshesPerSecond = 1022706

The inner loops looked like this:

    makeobj(*name=genobj());

    for(jdx=0; jdx<OBJSZ; jdx++) {
	fp=(float *)rectbuf;
        bgntmesh();
	  v3f(fp); v3f(fp+4);
	  for (fp+=8, i=LoopCount; i>0; --i,fp+=40) {
                 v3f(fp);    v3f(fp+4);
                 v3f(fp+8);  v3f(fp+12);
                 v3f(fp+16); v3f(fp+20);
                 v3f(fp+24); v3f(fp+28);
                 v3f(fp+32); v3f(fp+36);
	  }
        endtmesh();
    }

    closeobj();

    grestartwatch();
    for (i=events/(objects*OBJSZ); i>0; i--)
       callobj(Obj);
    sec = gstopwatch();
    return((int) events/sec);

My cursory inspection of the program revealed zbuffer(TRUE) &
zfunction(ZF_ALWAYS).  I'd send something simple & complete if I had time,
but this is the best I can do right now.

Regards, bruceh

jorge@raphael.dg.com (Jorge Lach) (04/01/91)

In article <1991Mar29.005630.22949@odin.corp.sgi.com>, bruceh@sgi.com (Bruce R. Holloway) writes:
|> In article <1991Mar27.174104.26867@hellgate.utah.edu> thomson@cs.utah.edu (Rich Thomson) writes:
|> >Several times on comp.graphics the SGI folks have offered up the
|> >famous "1 million polygons/second" quotation from marketing literature
|> >when referring to peak performance on the VGX series of machines.
|> >
|> >The polygons in question are 50 pixel triangle strips that are flat
|> >shaded.  Also, they may have rendered them without the Z buffer turned
|> >on (the spec sheet is away from me at the moment).
|> >...
|> >So: is this a "theoretical" number, or is it attainable by a program,
|> >no matter how contorted?
|> 
|> We have a suite of performance measurement programs which allow lots of
|> primitives with different characteristics to be tried.  I ran a certain
|> case and got the following output:
|> 
|> * Mesh, DisplayList, Subpixel, RGBmode, Flat, Area = 50.000000, PYM_FILL, MeshesPerSecond = 886681
|> ...
|> * Mesh, DisplayList, Subpixel, RGBmode, Flat, Area = 40.500000, PYM_FILL, MeshesPerSecond = 1022706
|> 


And what would the performance be in shading modes other than flat?
Also, what kind of light modelling is being applied to the polygons
in question?



------------------

jorge@dg.dg.com		Technical Systems Division
Jorge Lach		Data General Corp., Westboro, Massachusetts

	"I speak only for myself, not for my company; in fact, my
	 company does not speak, and it is not really mine..."

kurt@cashew.asd.sgi.com (Kurt Akeley) (04/01/91)

In article <1991Mar27.174104.26867@hellgate.utah.edu> thomson@cs.utah.edu (Rich Thomson) writes:
|>Several times on comp.graphics the SGI folks have offered up the
|>famous "1 million polygons/second" quotation from marketing literature
|>when referring to peak performance on the VGX series of machines.
|>
|>The polygons in question are 50 pixel triangle strips that are flat
|>shaded.  Also, they may have rendered them without the Z buffer turned
|>on (the spec sheet is away from me at the moment).
|>...
|>So: is this a "theoretical" number, or is it attainable by a program,
|>no matter how contorted?

We take our graphics performance claims very seriously here at Silicon
Graphics.  They represent performances that are achievable with carefully
tuned programs that use ONLY commands that are available in the
Graphics Library.  We distinguish between two different classes of
performance: primitive performance and fill performance.  Primitive
performance is the rate that points, lines, polygons, and characters
can be drawn per second, assuming no limitation from pixel manipulation,
measured with different modes activated (such as lighting).  Fill
performance is the rate that pixels are filled, assuming an infinite supply
of graphics primitives, again with different modes active (zbuffer,
blending, etc).  Primitive performances are always specified conservatively,
we always claim a lower number than can actually be achieved.  Fill
performances are usually specified as a "not-to-be-exceeded" maximum
that is approached asymptotically as larger and larger primitives are
drawn.  Due to slight overheads of memory and display refresh,
achievable fill rates are sometimes as much as 10 percent lower
than the claimed rates.

I have included the source code to the program that I use to verify
the performance of triangle meshes.  I ran this program on my 5-span
VGX with the following results:

    size=8, offset=4, zbuffer(1), events=500000, lighting=1
    running on cashew, GL4DVGX-4.0, Fri Mar 29 15:22:58 1991
    Triangle mesh performance (lighted):
       1 triangles per mesh: 189393 triangles per second
       2 triangles per mesh: 304878 triangles per second
       3 triangles per mesh: 299400 triangles per second
       4 triangles per mesh: 387596 triangles per second
       5 triangles per mesh: 471698 triangles per second
      10 triangles per mesh: 574712 triangles per second
      20 triangles per mesh: 641025 triangles per second
      30 triangles per mesh: 675648 triangles per second
      62 triangles per mesh: 714240 triangles per second
    Display listed triangle mesh (lighted):
      62 triangles per mesh: 769181 triangles per second
    Display listed triangle mesh (colored):
      62 triangles per mesh: 1020342 triangles per second
    Quadrilateral strip performance (lighted):
      31 quads per mesh: 342465 quads per second
    Independent triangle performance (lighted):
       1 triangles per mesh: 192307 triangles per second
    Triangle mesh performance (flat shaded):
      10 triangles per mesh: 943396 triangles per second
      20 triangles per mesh: 925925 triangles per second
      30 triangles per mesh: 1063787 triangles per second
      62 triangles per mesh: 1086886 triangles per second
    Quadrilateral strip performance (flat shaded):
      31 quads per mesh: 420167 quads per second
    Independent triangle performance (flat shaded):
       1 triangles per mesh: 280898 triangles per second

Note that performances of well over 1 million triangles per second are
achieved for long meshes of single- and multi-colored triangles, with
the zbuffer enabled.  When lighting and smooth shading are enabled, the
performance drops to roughly 3/4 of a million triangles per second.
(Here's where the marketing error crept in - the correct claim is
1 million connected triangles per second, multi-colored, flat shaded,
zbuffered, projected, subpixel positioned.  It's hard to keep all these
modes straight!)  Note also that I had to limit the triangles to about
30 pixels each, as my machine is a 5-span VGX.  A 10-span VGX has double
the fill rate, and is able to achieve these performances with 50-pixel
triangles.

I will be happy to supply other performance testing routines by email
on request.

-- kurt

----------------------------- cut here ----------------------------------

/**************************************************************************
Copyright 1991 by Silicon Graphics Incorporated, Mountain View, California.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the name of Silicon Graphics not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

**************************************************************************/

/*
 *	Kurt Akeley
 *	March 1991
 *
 *	Test performance of triangle meshes (strips)
 */

#include <stdio.h>
#include <gl.h>
#include <device.h>
#include <sys/types.h>
#include <sys/times.h>
#include <sys/param.h>

#define OUTFILE stdout
#define MAXVERTEX 102
#define VERTSIZE 8

#define LIGHTVERT(i) n3f(fp+(VERTSIZE*(i))); v3f(fp+(VERTSIZE*(i))+4)
#define COLORVERT(i) c3f(fp+(VERTSIZE*(i))); v3f(fp+(VERTSIZE*(i))+4)
#define FLATVERT(i) v3f(fp+(VERTSIZE*(i))+4)

float *meshbuf;
int dolighting = TRUE;

long events;

main(argc,argv) char *argv[]; {
    register i,j,k;
    int size, offset;
    int doz;

    /* allocate a quad-aligned buffer */
    meshbuf = (float*)malloc(sizeof(float)*(MAXVERTEX*VERTSIZE + 3));
    meshbuf = (float*)(((long)(meshbuf+3)) & 0xfffffff0);

    /* evaluate command line arguments */
    size = 10;
    offset = 5;
    doz = TRUE;
    events = 100000;
    dolighting = TRUE;
    if (argc > 1)
	size = atoi(argv[1]);
    if (argc > 2)
	offset = atoi(argv[2]);
    if (argc > 3)
	doz = atoi(argv[3]);
    if (argc > 4)
	events = atoi(argv[4]);
    if (argc > 5)
	dolighting = atoi(argv[5]);
    fprintf(OUTFILE,"size=%d, offset=%d, zbuffer(%d), events=%d, lighting=%d\n",
	size, offset, doz, events, dolighting);
    fflush(OUTFILE);
    
    /* initialize graphics */
    prefposition(10,32*size+30,10,size+30);
    foreground();
    winopen("meshspeed");
    RGBmode();
    overlay(2);
    gconfig();
    zbuffer(doz);
    zfunction(ZF_ALWAYS);
    subpixel(TRUE);
    qdevice(ESCKEY);
    shademodel(GOURAUD);
    timestamp();

    /* initialize lighting */
    initlight();

    /* clear the screen */
    cpack(0);
    clear();
    drawmode(OVERDRAW);
    color(0);
    clear();
    drawmode(NORMALDRAW);

    /* initialize data arrays */
    for (i=0; i<MAXVERTEX; i+=1) {
	meshbuf[VERTSIZE*i+0] = (i&1) ? 0.0 : 1.0;
	meshbuf[VERTSIZE*i+1] = 0.0;
	meshbuf[VERTSIZE*i+2] = (i&1) ? 1.0 : 0.0;
	meshbuf[VERTSIZE*i+3] = 0;
	meshbuf[VERTSIZE*i+4] = 10.0 + (float)(size*(i>>1)) +
				       (float)(offset*(i&1));
	meshbuf[VERTSIZE*i+5] = 10.0 + (float)(size*(i&1));
	meshbuf[VERTSIZE*i+6] = 0.0;
	meshbuf[VERTSIZE*i+7] = 0;
    }

    /* run the tests */
    fprintf(OUTFILE,"Triangle mesh performance (lighted):\n");
    meshlight1();
    meshlight2();
    meshlight3();
    meshlight4();
    meshlight5();
    meshlight10();
    meshlight20();
    meshlight30();
    meshlight62();
    fprintf(OUTFILE,"Display listed triangle mesh (lighted):\n");
    dlmeshlight62();
    fprintf(OUTFILE,"Display listed triangle mesh (colored):\n");
    dlmeshcolor62();
    fprintf(OUTFILE,"Quadrilateral strip performance (lighted):\n");
    quadlight31();
    fprintf(OUTFILE,"Independent triangle performance (lighted):\n");
    indlight();
    fprintf(OUTFILE,"Triangle mesh performance (flat shaded):\n");
    meshflat10();
    meshflat20();
    meshflat30();
    meshflat62();
    fprintf(OUTFILE,"Quadrilateral strip performance (flat shaded):\n");
    quadflat31();
    fprintf(OUTFILE,"Independent triangle performance (flat shaded):\n");
    indflat();
}


meshlight1() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	endtmesh();
    }
    stopclock(events,1,"triangles");
    light(FALSE);
}

meshlight2() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/2; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	LIGHTVERT(3);
	endtmesh();
    }
    stopclock(events/2,2,"triangles");
    light(FALSE);
}

meshlight3() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/3; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	LIGHTVERT(3);
	LIGHTVERT(4);
	endtmesh();
    }
    stopclock(events/3,3,"triangles");
    light(FALSE);
}

meshlight4() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/4; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	LIGHTVERT(3);
	LIGHTVERT(4);
	LIGHTVERT(5);
	endtmesh();
    }
    stopclock(events/4,4,"triangles");
    light(FALSE);
}

meshlight5() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/5; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	LIGHTVERT(3);
	LIGHTVERT(4);
	LIGHTVERT(5);
	LIGHTVERT(6);
	endtmesh();
    }
    stopclock(events/5,5,"triangles");
    light(FALSE);
}

meshlight10() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/10; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	LIGHTVERT(3);
	LIGHTVERT(4);
	LIGHTVERT(5);
	LIGHTVERT(6);
	LIGHTVERT(7);
	LIGHTVERT(8);
	LIGHTVERT(9);
	LIGHTVERT(10);
	LIGHTVERT(11);
	endtmesh();
    }
    stopclock(events/10,10,"triangles");
    light(FALSE);
}

meshlight20() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/20; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	LIGHTVERT(3);
	LIGHTVERT(4);
	LIGHTVERT(5);
	LIGHTVERT(6);
	LIGHTVERT(7);
	LIGHTVERT(8);
	LIGHTVERT(9);
	LIGHTVERT(10);
	LIGHTVERT(11);
	LIGHTVERT(12);
	LIGHTVERT(13);
	LIGHTVERT(14);
	LIGHTVERT(15);
	LIGHTVERT(16);
	LIGHTVERT(17);
	LIGHTVERT(18);
	LIGHTVERT(19);
	LIGHTVERT(20);
	LIGHTVERT(21);
	endtmesh();
    }
    stopclock(events/20,20,"triangles");
    light(FALSE);
}

meshlight30() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/30; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	LIGHTVERT(3);
	LIGHTVERT(4);
	LIGHTVERT(5);
	LIGHTVERT(6);
	LIGHTVERT(7);
	LIGHTVERT(8);
	LIGHTVERT(9);
	LIGHTVERT(10);
	LIGHTVERT(11);
	LIGHTVERT(12);
	LIGHTVERT(13);
	LIGHTVERT(14);
	LIGHTVERT(15);
	LIGHTVERT(16);
	LIGHTVERT(17);
	LIGHTVERT(18);
	LIGHTVERT(19);
	LIGHTVERT(20);
	LIGHTVERT(21);
	LIGHTVERT(22);
	LIGHTVERT(23);
	LIGHTVERT(24);
	LIGHTVERT(25);
	LIGHTVERT(26);
	LIGHTVERT(27);
	LIGHTVERT(28);
	LIGHTVERT(29);
	LIGHTVERT(30);
	LIGHTVERT(31);
	endtmesh();
    }
    stopclock(events/30,30,"triangles");
    light(FALSE);
}

meshlight62() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/62; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	LIGHTVERT(3);
	LIGHTVERT(4);
	LIGHTVERT(5);
	LIGHTVERT(6);
	LIGHTVERT(7);
	LIGHTVERT(8);
	LIGHTVERT(9);
	LIGHTVERT(10);
	LIGHTVERT(11);
	LIGHTVERT(12);
	LIGHTVERT(13);
	LIGHTVERT(14);
	LIGHTVERT(15);
	LIGHTVERT(16);
	LIGHTVERT(17);
	LIGHTVERT(18);
	LIGHTVERT(19);
	LIGHTVERT(20);
	LIGHTVERT(21);
	LIGHTVERT(22);
	LIGHTVERT(23);
	LIGHTVERT(24);
	LIGHTVERT(25);
	LIGHTVERT(26);
	LIGHTVERT(27);
	LIGHTVERT(28);
	LIGHTVERT(29);
	LIGHTVERT(30);
	LIGHTVERT(31);
	LIGHTVERT(32);
	LIGHTVERT(33);
	LIGHTVERT(34);
	LIGHTVERT(35);
	LIGHTVERT(36);
	LIGHTVERT(37);
	LIGHTVERT(38);
	LIGHTVERT(39);
	LIGHTVERT(40);
	LIGHTVERT(41);
	LIGHTVERT(42);
	LIGHTVERT(43);
	LIGHTVERT(44);
	LIGHTVERT(45);
	LIGHTVERT(46);
	LIGHTVERT(47);
	LIGHTVERT(48);
	LIGHTVERT(49);
	LIGHTVERT(50);
	LIGHTVERT(51);
	LIGHTVERT(52);
	LIGHTVERT(53);
	LIGHTVERT(54);
	LIGHTVERT(55);
	LIGHTVERT(56);
	LIGHTVERT(57);
	LIGHTVERT(58);
	LIGHTVERT(59);
	LIGHTVERT(60);
	LIGHTVERT(61);
	LIGHTVERT(62);
	LIGHTVERT(63);
	endtmesh();
    }
    stopclock(events/62,62,"triangles");
    light(FALSE);
}

#define MESHPERDL 10

dlmeshlight62() {
    register i,j;
    register float *fp;
    makeobj(1);
    for (i=0; i<MESHPERDL; i++) {
	fp = meshbuf;
	bgntmesh();
	for (j=0; j<64; j++) {
	    LIGHTVERT(j);
	}
	endtmesh();
    }
    closeobj();
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/(62*MESHPERDL); i>0; i--)
	callobj(1);
    stopclock(events/62,62,"triangles");
    light(FALSE);
}

dlmeshcolor62() {
    register i,j;
    register float *fp;
    makeobj(1);
    for (i=0; i<MESHPERDL; i++) {
	fp = meshbuf;
	bgntmesh();
	for (j=0; j<64; j++) {
	    COLORVERT(j);
	}
	endtmesh();
    }
    closeobj();
    czclear(0,0);
    shademodel(FLAT);
    startclock();
    for (i=events/(62*MESHPERDL); i>0; i--)
	callobj(1);
    stopclock(events/62,62,"triangles");
    shademodel(GOURAUD);
}

quadlight31() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/31; i>0; i--) {
	fp = meshbuf;
	bgnqstrip();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	LIGHTVERT(3);
	LIGHTVERT(4);
	LIGHTVERT(5);
	LIGHTVERT(6);
	LIGHTVERT(7);
	LIGHTVERT(8);
	LIGHTVERT(9);
	LIGHTVERT(10);
	LIGHTVERT(11);
	LIGHTVERT(12);
	LIGHTVERT(13);
	LIGHTVERT(14);
	LIGHTVERT(15);
	LIGHTVERT(16);
	LIGHTVERT(17);
	LIGHTVERT(18);
	LIGHTVERT(19);
	LIGHTVERT(20);
	LIGHTVERT(21);
	LIGHTVERT(22);
	LIGHTVERT(23);
	LIGHTVERT(24);
	LIGHTVERT(25);
	LIGHTVERT(26);
	LIGHTVERT(27);
	LIGHTVERT(28);
	LIGHTVERT(29);
	LIGHTVERT(30);
	LIGHTVERT(31);
	LIGHTVERT(32);
	LIGHTVERT(33);
	LIGHTVERT(34);
	LIGHTVERT(35);
	LIGHTVERT(36);
	LIGHTVERT(37);
	LIGHTVERT(38);
	LIGHTVERT(39);
	LIGHTVERT(40);
	LIGHTVERT(41);
	LIGHTVERT(42);
	LIGHTVERT(43);
	LIGHTVERT(44);
	LIGHTVERT(45);
	LIGHTVERT(46);
	LIGHTVERT(47);
	LIGHTVERT(48);
	LIGHTVERT(49);
	LIGHTVERT(50);
	LIGHTVERT(51);
	LIGHTVERT(52);
	LIGHTVERT(53);
	LIGHTVERT(54);
	LIGHTVERT(55);
	LIGHTVERT(56);
	LIGHTVERT(57);
	LIGHTVERT(58);
	LIGHTVERT(59);
	LIGHTVERT(60);
	LIGHTVERT(61);
	LIGHTVERT(62);
	LIGHTVERT(63);
	endqstrip();
    }
    stopclock(events/31,31,"quads");
    light(FALSE);
}

indlight() {
    register i;
    register float *fp;
    czclear(0,0);
    light(TRUE);
    startclock();
    for (i=events/4; i>0; i--) {
	fp = meshbuf;
	bgnpolygon();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	endpolygon();
	bgnpolygon();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	endpolygon();
	bgnpolygon();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	endpolygon();
	bgnpolygon();
	LIGHTVERT(0);
	LIGHTVERT(1);
	LIGHTVERT(2);
	endpolygon();
    }
    stopclock(events,1,"triangles");
    light(FALSE);
}

meshflat10() {
    register i;
    register float *fp;
    czclear(0,0);
    cpack(0xffffffff);
    shademodel(FLAT);
    startclock();
    for (i=events/10; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	FLATVERT(0);
	FLATVERT(1);
	FLATVERT(2);
	FLATVERT(3);
	FLATVERT(4);
	FLATVERT(5);
	FLATVERT(6);
	FLATVERT(7);
	FLATVERT(8);
	FLATVERT(9);
	FLATVERT(10);
	FLATVERT(11);
	endtmesh();
    }
    stopclock(events/10,10,"triangles");
    shademodel(GOURAUD);
}

meshflat20() {
    register i;
    register float *fp;
    czclear(0,0);
    cpack(0xffffffff);
    shademodel(FLAT);
    startclock();
    for (i=events/20; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	FLATVERT(0);
	FLATVERT(1);
	FLATVERT(2);
	FLATVERT(3);
	FLATVERT(4);
	FLATVERT(5);
	FLATVERT(6);
	FLATVERT(7);
	FLATVERT(8);
	FLATVERT(9);
	FLATVERT(10);
	FLATVERT(11);
	FLATVERT(12);
	FLATVERT(13);
	FLATVERT(14);
	FLATVERT(15);
	FLATVERT(16);
	FLATVERT(17);
	FLATVERT(18);
	FLATVERT(19);
	FLATVERT(20);
	FLATVERT(21);
	endtmesh();
    }
    stopclock(events/20,20,"triangles");
    shademodel(GOURAUD);
}

meshflat30() {
    register i;
    register float *fp;
    czclear(0,0);
    cpack(0xffffffff);
    shademodel(FLAT);
    startclock();
    for (i=events/30; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	FLATVERT(0);
	FLATVERT(1);
	FLATVERT(2);
	FLATVERT(3);
	FLATVERT(4);
	FLATVERT(5);
	FLATVERT(6);
	FLATVERT(7);
	FLATVERT(8);
	FLATVERT(9);
	FLATVERT(10);
	FLATVERT(11);
	FLATVERT(12);
	FLATVERT(13);
	FLATVERT(14);
	FLATVERT(15);
	FLATVERT(16);
	FLATVERT(17);
	FLATVERT(18);
	FLATVERT(19);
	FLATVERT(20);
	FLATVERT(21);
	FLATVERT(22);
	FLATVERT(23);
	FLATVERT(24);
	FLATVERT(25);
	FLATVERT(26);
	FLATVERT(27);
	FLATVERT(28);
	FLATVERT(29);
	FLATVERT(30);
	FLATVERT(31);
	endtmesh();
    }
    stopclock(events/30,30,"triangles");
    shademodel(GOURAUD);
}

meshflat62() {
    register i;
    register float *fp;
    czclear(0,0);
    cpack(0xffffffff);
    shademodel(FLAT);
    startclock();
    for (i=events/62; i>0; i--) {
	fp = meshbuf;
	bgntmesh();
	FLATVERT(0);
	FLATVERT(1);
	FLATVERT(2);
	FLATVERT(3);
	FLATVERT(4);
	FLATVERT(5);
	FLATVERT(6);
	FLATVERT(7);
	FLATVERT(8);
	FLATVERT(9);
	FLATVERT(10);
	FLATVERT(11);
	FLATVERT(12);
	FLATVERT(13);
	FLATVERT(14);
	FLATVERT(15);
	FLATVERT(16);
	FLATVERT(17);
	FLATVERT(18);
	FLATVERT(19);
	FLATVERT(20);
	FLATVERT(21);
	FLATVERT(22);
	FLATVERT(23);
	FLATVERT(24);
	FLATVERT(25);
	FLATVERT(26);
	FLATVERT(27);
	FLATVERT(28);
	FLATVERT(29);
	FLATVERT(30);
	FLATVERT(31);
	FLATVERT(32);
	FLATVERT(33);
	FLATVERT(34);
	FLATVERT(35);
	FLATVERT(36);
	FLATVERT(37);
	FLATVERT(38);
	FLATVERT(39);
	FLATVERT(40);
	FLATVERT(41);
	FLATVERT(42);
	FLATVERT(43);
	FLATVERT(44);
	FLATVERT(45);
	FLATVERT(46);
	FLATVERT(47);
	FLATVERT(48);
	FLATVERT(49);
	FLATVERT(50);
	FLATVERT(51);
	FLATVERT(52);
	FLATVERT(53);
	FLATVERT(54);
	FLATVERT(55);
	FLATVERT(56);
	FLATVERT(57);
	FLATVERT(58);
	FLATVERT(59);
	FLATVERT(60);
	FLATVERT(61);
	FLATVERT(62);
	FLATVERT(63);
	endtmesh();
    }
    stopclock(events/62,62,"triangles");
    shademodel(GOURAUD);
}

quadflat31() {
    register i;
    register float *fp;
    czclear(0,0);
    cpack(0xffffffff);
    shademodel(FLAT);
    startclock();
    for (i=events/31; i>0; i--) {
	fp = meshbuf;
	bgnqstrip();
	FLATVERT(0);
	FLATVERT(1);
	FLATVERT(2);
	FLATVERT(3);
	FLATVERT(4);
	FLATVERT(5);
	FLATVERT(6);
	FLATVERT(7);
	FLATVERT(8);
	FLATVERT(9);
	FLATVERT(10);
	FLATVERT(11);
	FLATVERT(12);
	FLATVERT(13);
	FLATVERT(14);
	FLATVERT(15);
	FLATVERT(16);
	FLATVERT(17);
	FLATVERT(18);
	FLATVERT(19);
	FLATVERT(20);
	FLATVERT(21);
	FLATVERT(22);
	FLATVERT(23);
	FLATVERT(24);
	FLATVERT(25);
	FLATVERT(26);
	FLATVERT(27);
	FLATVERT(28);
	FLATVERT(29);
	FLATVERT(30);
	FLATVERT(31);
	FLATVERT(32);
	FLATVERT(33);
	FLATVERT(34);
	FLATVERT(35);
	FLATVERT(36);
	FLATVERT(37);
	FLATVERT(38);
	FLATVERT(39);
	FLATVERT(40);
	FLATVERT(41);
	FLATVERT(42);
	FLATVERT(43);
	FLATVERT(44);
	FLATVERT(45);
	FLATVERT(46);
	FLATVERT(47);
	FLATVERT(48);
	FLATVERT(49);
	FLATVERT(50);
	FLATVERT(51);
	FLATVERT(52);
	FLATVERT(53);
	FLATVERT(54);
	FLATVERT(55);
	FLATVERT(56);
	FLATVERT(57);
	FLATVERT(58);
	FLATVERT(59);
	FLATVERT(60);
	FLATVERT(61);
	FLATVERT(62);
	FLATVERT(63);
	endqstrip();
    }
    stopclock(events/31,31,"quads");
    shademodel(GOURAUD);
}

indflat() {
    register i;
    register float *fp;
    czclear(0,0);
    cpack(0xffffffff);
    shademodel(FLAT);
    startclock();
    for (i=events/4; i>0; i--) {
	fp = meshbuf;
	bgnpolygon();
	FLATVERT(0);
	FLATVERT(1);
	FLATVERT(2);
	endpolygon();
	bgnpolygon();
	FLATVERT(0);
	FLATVERT(1);
	FLATVERT(2);
	endpolygon();
	bgnpolygon();
	FLATVERT(0);
	FLATVERT(1);
	FLATVERT(2);
	endpolygon();
	bgnpolygon();
	FLATVERT(0);
	FLATVERT(1);
	FLATVERT(2);
	endpolygon();
    }
    stopclock(events,1,"triangles");
    shademodel(GOURAUD);
}

struct tms tbuf;
long gtime;

startclock() {
    sleep(1);
    finish();
    gtime = times(&tbuf);
}

stopclock(meshes,tripermesh,s)
long meshes,tripermesh;
char *s;
{
    float period;
    float timepermesh;
    float rate, crate;
    int callspermesh;
    finish();
    callspermesh = 2*tripermesh + 6;
    period = (float)(times(&tbuf) - gtime) / 100.0;
    timepermesh = period / (float)meshes;
    rate = (float)tripermesh / timepermesh;
    fprintf(OUTFILE,"  %2d %s per mesh: %6d %s per second\n",
	tripermesh, s, (int)rate, s);
    fflush(OUTFILE);
    interrupt();
}

float brass[] = {
    AMBIENT, 0.35, 0.25,  0.1,
    DIFFUSE, 0.65, 0.5, 0.35,
    SPECULAR, 0.0, 0.0, 0.0,
    SHININESS, 5.0,
    LMNULL
};

float whitelight[] = {
    AMBIENT, 0.0, 0.0, 0.0, 
    LCOLOR, 1.0, 1.0, 1.0, 
    POSITION, 0.0, 0.0, 1.0, 0.0,
    LMNULL
};
		    
float infinite[] = {
    AMBIENT, 0.3,  0.3, 0.3, 
    LOCALVIEWER, 0.0, 
    LMNULL
};

float idmat[] = {
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    0.0, 0.0, 0.0, 1.0
};


initlight() {
    /* provide a simple lighting model just for timing purposes */
    long xsize,ysize;
    getsize(&xsize,&ysize);
    mmode(MPROJECTION);
    ortho(-0.5,(float)xsize-0.5,-0.5,(float)ysize-0.5,0.0,10000.0);
    mmode(MVIEWING); 
    loadmatrix(idmat);
    
    lmdef(DEFMATERIAL, 1, 0, brass);
    lmdef(DEFLIGHT,    1, 0, whitelight);
    lmdef(DEFLMODEL,   1, 0, infinite);
    lmbind(LIGHT1, 1);
    lmbind(LMODEL, 1);
}

light(b) {
    /* turn lighting on and off */
    if (dolighting)
	lmbind(MATERIAL, b ? 1 : 0);
    else
	cpack(0xffffffff);
}

interrupt() {
    /* check queue for escape key and exit if found */
    short dev,val;
    while (qtest()) {
	if (qread(&val) == ESCKEY) {
	    exit(0);
	}
    }
}

timestamp() {
    /* print host name and time/date */
    char s[100];
    char gv[100];
    time_t t;

    gethostname(s,100);
    gversion(gv);
    t = time(0);
    fprintf(OUTFILE,"running on %s, %s, %s",s,gv,ctime(&t));
}