Rob Jenkins (robj++at++quid)
Mon, 18 Nov 1996 09:25:47 -0800
--------------------------------------------------------------------
/*
* tenmillion.c
*
* OpenGL performance test for triangles in a display list.
* lacroute++at++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 <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <time.h>
#include <ctype.h>
#include <string.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include <math.h>
#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};
int RGBattributes[] = {
GLX_RGBA,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
None
};
int RGBZattributes[] = {
GLX_RGBA,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_DEPTH_SIZE, 16,
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");
/* 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);
/* 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);
}
void
main(int argc, char **argv)
{
parse_args(argc, argv);
open_window();
benchmark();
}
--
________________________________________________________________
Rob Jenkins mailto:robj++at++csd.sgi.com
Silicon Graphics, Mtn View, California, USA
=======================================================================
List Archives, FAQ, FTP: http://www.sgi.com/Technology/Performer/
Submissions: info-performer++at++sgi.com
Admin. requests: info-performer-request++at++sgi.com
This archive was generated by hypermail 2.0b2 on Mon Aug 10 1998 - 17:53:58 PDT