/* * tenmillion.c * * OpenGL performance test for triangles in a display list. * lacroute@asd.sgi.com, March 1996 * * To compile: * cc -o tenmillion -O2 tenmillion.c -lGLU -lGL -lX11 -lm * * To run: * tenmillion [options] * tenmillion help */ /* * Notes on using this program with InfiniteReality * ------------------------------------------------ * * On a 4RM InfiniteReality this program draws over * 10-million 50-pixel triangles/sec. Just type: * tenmillion * On machines with only one or two RMs the performance * is lower because the machine is fill-limited. You * can still achieve over 10 million triangles/sec if * you use a smaller triangle size. For a 2RM system * use a 12 pixel triangle: * tenmillion area=12 * For a 1RM system use a 3 pixel triangle: * tenmillion area=3 * Note that when you double the number of RMs the * size of the triangle you can use more than doubles. * (The super-linear speedup is due to edge effects: * larger triangles have a higher area-to-circumference * ratio.) * * Here are some examples for a 1RM system. * * Unlit, untextured, zbuffered triangles: (11.3 million triangles/sec) * tenmillion area=3 zbuffer * * Lit, untextured, zbuffered triangles: (8.0 million triangles/sec) * tenmillion area=3 light smooth zbuffer * * Unlit, textured, zbuffered triangles: (7.8 million triangles/sec) * tenmillion area=3 texture zbuffer * * Lit, textured, zbuffered triangles: (6.1 million triangles/sec) * tenmillion area=3 light smooth texture zbuffer * * Fill rate for large, untextured, non-zbuffered triangles: (225 Mpixels/sec) * tenmillion area=10000 * * Fill rate for large, untextured, zbuffered triangles: (223 Mpixels/sec) * tenmillion area=10000 zbuffer * * Fill rate for large, textured, non-zbuffered triangles: (196 Mpixels/sec) * tenmillion area=10000 texture * * Fill rate for large, textured, zbuffered triangles: (194 Mpixels/sec) * tenmillion area=10000 zbuffer texture */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* Additions for Linux/Mesa - Dave Pape */ #include #define GL_RGBA4_EXT GL_RGBA4 #define gettimeofday(a) gettimeofday(a,NULL) /* end additions */ #define TEXTURE_SIZE 64 /* texture dimension (texels/side) */ #define TEXTURE_LOD 0.5 /* texture level-of-detail */ #define MARGIN 10 /* margin around mesh (pixels) */ #define MAX_WIN_SIZE 850 /* maximum window width/height */ #define DEFAULT_STRIPLEN 90 /* preferred strip length */ #define DEFAULT_STRIPS 1 /* preferred number of strips */ #define TEST_DURATION 2.0 /* test duration in seconds */ #define DLIST_NAME 1 /* display list name */ Display *display; /* connection to X server */ XVisualInfo *vi; /* window visual */ Window window; /* window for drawing */ GLXContext context; /* graphics context */ unsigned win_w, win_h; /* window size */ int striplen; /* triangles/strip */ int strips; /* number of strips */ float xsize, ysize, xoffset; /* triangle base, height, and apex offset */ int texture = 0; /* if true, enable texturing */ int light = 0; /* if true, enable lighting */ int smooth = 0; /* if true, use smooth shading */ int zbuffer = 0; /* if true, enable zbuffer */ int area = 50; /* triangle area in pixels */ #define NUM_ANGLES 8 float angles[NUM_ANGLES] = {0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5}; /* Changed all the minimum sizes to 1, rather than 8, for Linux - Dave Pape */ int RGBattributes[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; int RGBZattributes[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 1, None }; /* * help * * Print a usage message. */ void help(void) { printf("Usage: tenmillion [options]\n\n"); printf("Options:\n" " texture enable texture mapping (mipmapped)\n" " light enable lighting (one infinite light)\n" " smooth enable smooth shading (Gouraud)\n" " zbuffer enable zbuffer\n" " area=N set triangle area to N pixels\n"); exit(0); } /* * parse_args * * Parse the command line arguments. */ void parse_args(int argc, char **argv) { float w, h; while (--argc) { ++argv; if (!strcmp(*argv, "texture")) { texture = 1; } else if (!strcmp(*argv, "light")) { light = 1; } else if (!strcmp(*argv, "smooth")) { smooth = 1; } else if (!strcmp(*argv, "zbuffer")) { zbuffer = 1; } else if (!strncmp(*argv, "area=", 5)) { area = strtol(*argv+5, NULL, 0); } else { fprintf(stderr, "unrecognized argument %s\n", *argv); help(); } } /* compute triangle dimensions from triangle size */ xsize = sqrt(2 * area); ysize = 2 * area / xsize; xoffset = xsize/2; /* compute strip length and number of strips */ if (xsize*(DEFAULT_STRIPLEN+1)/2 + xoffset + 2*MARGIN > MAX_WIN_SIZE) { striplen = 2*(MAX_WIN_SIZE - xoffset - 2*MARGIN) / xsize; if (striplen < 2) striplen = 2; } else { striplen = DEFAULT_STRIPLEN; } strips = 10/ysize; if (strips < 1) strips = 1; if (ysize*strips + 2*MARGIN > MAX_WIN_SIZE) { strips = (MAX_WIN_SIZE - 2*MARGIN) / ysize; if (strips < 1) strips = 1; } /* compute window size */ w = xsize*((striplen+1)/2) + 2*MARGIN; if ((striplen & 1) == 0) w += xoffset; h = ysize*strips + 2*MARGIN; win_w = 2*(int)ceil(sqrt(w*w/4 + h*h/4)); if (win_w < 300) win_w = 300; if (win_w > MAX_WIN_SIZE) { fprintf(stderr, "Triangle area is too large to fit.\n"); exit(1); } win_h = win_w; } /* * wait_for_map_notify * * Callback for XIfEvent. */ int wait_for_map_notify(Display *display, XEvent *event, char *arg) { return(event->type == MapNotify && event->xmap.window == (Window)arg); } /* * open_window * * Create an X window. */ void open_window(void) { XSetWindowAttributes swa; XSizeHints hints; XEvent event; XVisualInfo template; int c, x, y; display = XOpenDisplay(0); if (display == NULL) { fprintf(stderr, "Can't connect to display \"%s\"\n", getenv("DISPLAY")); exit(1); } vi = glXChooseVisual(display, DefaultScreen(display), zbuffer ? RGBZattributes : RGBattributes); if (vi == NULL) { fprintf(stderr, "can't find appropriate visual\n"); exit(1); } swa.border_pixel = 0; swa.colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone); swa.event_mask = ExposureMask | StructureNotifyMask | KeyPressMask; x = (DisplayWidth(display, vi->screen) - win_w) / 2; y = (DisplayHeight(display, vi->screen) - win_h) / 2; window = XCreateWindow(display, RootWindow(display, vi->screen), x, y, win_w, win_h, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); if (window == 0) { fprintf(stderr, "could not create a window\n"); exit(1); } XStoreName(display, window, "InfiniteReality Speed Demo"); hints.x = x; hints.y = y; hints.width = win_w; hints.height = win_h; hints.flags = USPosition | PSize; XSetNormalHints(display, window, &hints); XMapWindow(display, window); XIfEvent(display, &event, wait_for_map_notify, (char *)window); context = glXCreateContext(display, vi, 0, GL_TRUE); glXMakeCurrent(display, window, context); glViewport(0, 0, win_w, win_h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, win_w, 0, win_h, -1, 1); glMatrixMode(GL_MODELVIEW); } /* * compile_test * * Compile a test into a display list. */ void compile_test(void) { float *vdata; /* array containing vertex data */ float *vptr; /* pointer to data for current vertex */ int r; /* current row number */ int v; /* current vertex number within row */ int a; /* angle number */ float x, y, tx, ty, c, s; /* compute mesh data */ vdata = memalign(16, NUM_ANGLES*(striplen+2)*strips*2*sizeof(float)); if (vdata == NULL) { fprintf(stderr, "could not allocate memory for mesh data\n"); exit(1); } vptr = vdata; tx = -(xsize * ((striplen+1)/2) + ((striplen & 1) ? 0 : xoffset))/2; ty = -ysize*strips/2; for (a = 0; a < NUM_ANGLES; a++) { c = cos(angles[a]*M_PI/180); s = sin(angles[a]*M_PI/180); for (r = 0; r < strips; r++) { for (v = 0; v < striplen+2; v++) { if (v & 1) { x = (v/2)*xsize + xoffset + tx; y = (r+1)*ysize + ty; } else { x = (v/2)*xsize + tx; y = r*ysize + ty; } vptr[0] = win_w/2 + c*x - s*y; vptr[1] = win_h/2 + s*x + c*y; vptr += 2; } } } /* create display list */ vptr = vdata; glNewList(DLIST_NAME, GL_COMPILE); for (a = 0; a < NUM_ANGLES; a++) { for (r = 0; r < strips; r++) { glBegin(GL_TRIANGLE_STRIP); for (v = 0; v < striplen+2; v++) { if (light) { glNormal3s((v&1) ? 0 : 32767, 0, (v&1) ? 32767 : 0); } if (texture) { /* use vertex coordinates for texture coordinates */ glTexCoord2s((short)vptr[0], (short)vptr[1]); } glVertex3f(vptr[0], vptr[1], 0); vptr += 2; } glEnd(); } } glEndList(); free(vdata); } /* * make_texture * * Create a checkerboard texture. */ GLubyte * make_texture(void) { int i, j, c; GLubyte *texdata, *tptr; if ((texdata = malloc(TEXTURE_SIZE*TEXTURE_SIZE*3)) == NULL) { fprintf(stderr, "not enough memory for texture data\n"); exit(1); } tptr = texdata; for (j = 0; j < TEXTURE_SIZE; j++) { for (i = 0; i < TEXTURE_SIZE; i++) { c = (((i&8) == 0) ^ ((j&8) == 0)) * 255; tptr[0] = (GLubyte)c; tptr[1] = (GLubyte)c; tptr[2] = (GLubyte)c; tptr += 3; } } return(texdata); } /* * init_test * * Initialize graphics state for a test. This code is not timed. */ void init_test(void) { int l; static float light_color[4] = {0, 1, 0, 1}; static float light_pos[4] = {1, 1, -1, 0}; float texture_scale; GLubyte *texdata; glDrawBuffer(GL_FRONT); glClearColor(0.5,0.5,0.5,0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(1,1,1); glFrontFace(GL_CW); if (smooth) { glShadeModel(GL_SMOOTH); } else { glShadeModel(GL_FLAT); } if (zbuffer) { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); } else { glDisable(GL_DEPTH_TEST); } if (light) { glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_POSITION, light_pos); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color); glLightfv(GL_LIGHT0, GL_SPECULAR, light_color); } else { glDisable(GL_LIGHTING); } if (texture) { texdata = make_texture(); if (light) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); else glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA4_EXT, TEXTURE_SIZE, TEXTURE_SIZE, GL_RGB, GL_UNSIGNED_BYTE, texdata); glMatrixMode(GL_TEXTURE); glLoadIdentity(); texture_scale = pow(2, TEXTURE_LOD) / (double)TEXTURE_SIZE; glScalef(texture_scale, texture_scale, 1); glMatrixMode(GL_MODELVIEW); glEnable(GL_TEXTURE_2D); } else { glDisable(GL_TEXTURE_2D); } } /* * get_clock * * Get current time (expressed in seconds). */ double get_clock(void) { struct timeval t; gettimeofday(&t); return((double)t.tv_sec + (double)t.tv_usec*1E-6); } /* * benchmark * * Run the benchmark. */ void benchmark(void) { time_t t; char machine[MAXHOSTNAMELEN+1]; long reps, i; double start, current; float frame_rate; GLenum error; /* print information about the test */ t = time(0); gethostname(machine, MAXHOSTNAMELEN); machine[MAXHOSTNAMELEN] = '\0'; printf("running on %s (%s) %s", machine, glGetString(GL_RENDERER), ctime(&t)); printf("visual: 0x%x\n", vi->visualid); printf("%d pixel triangles, %d triangles/strip, %d strips\n", area, striplen, strips); printf("vertex data:"); if (light) printf(" n3s"); if (texture) printf(" t2s"); printf(" v3f\n"); printf("%s shading, zbuffer %s, light %s, texture %s\n", smooth ? "smooth" : "flat", zbuffer ? "on" : "off", light ? "on" : "off", texture ? "on" : "off"); printf("window size: %d x %d\n",win_w,win_h); /* initialize and run the test once to make sure display list is in the cache */ compile_test(); init_test(); glCallList(DLIST_NAME); /* calibration loop */ glFinish(); reps = 1; current = 0; start = 0; while ((current - start) < TEST_DURATION/4) { reps = reps * 2; start = get_clock(); while ((current = get_clock()) == start) /* wait for next tick */ ; start = current; for (i = reps; i > 0; --i) glCallList(DLIST_NAME); glFinish(); current = get_clock(); } reps = reps * (TEST_DURATION / (current - start)); if (reps < 1) reps = 1; /* timing loop */ start = get_clock(); while ((current = get_clock()) == start) /* wait for next tick */ ; start = current; for (i = reps; i > 0; --i) glCallList(DLIST_NAME); glFinish(); current = get_clock(); frame_rate = reps / (current - start); while ((error = glGetError()) != GL_NO_ERROR) fprintf(stderr, "GL Error: %s\n", (char *)gluErrorString(error)); printf("test time: %.3f secs.\n", current - start); printf("time per frame: %.3f msecs.\n", ((current - start)*1000)/reps); /* print results */ printf("geometry rate: %.0f triangles/sec\n", NUM_ANGLES*strips*striplen*frame_rate); printf("fill rate: %.3f Mpixels/sec\n", area*NUM_ANGLES*strips*striplen*frame_rate/1e6); } /**** main returns an int, not void! - Dave Pape ***/ int main(int argc, char **argv) { parse_args(argc, argv); open_window(); benchmark(); }