| /* | 
 |  * This program is under the GNU GPL. | 
 |  * Use at your own risk. | 
 |  * | 
 |  * written by David Bucciarelli (tech.hmw@plus.it) | 
 |  *            Humanware s.r.l. | 
 |  */ | 
 |  | 
 | #include <stdlib.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <math.h> | 
 | #include <GL/glut.h> | 
 |  | 
 | typedef struct | 
 | { | 
 |    char *name; | 
 |    char *unit; | 
 |    void (*init) (void); | 
 |    int (*run) (int, int); | 
 |    int type; | 
 |    int numsize; | 
 |    int size[10]; | 
 | } | 
 | benchmark; | 
 |  | 
 | static int frontbuffer = 1; | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | static void | 
 | init_test01(void) | 
 | { | 
 |    glMatrixMode(GL_PROJECTION); | 
 |    glLoadIdentity(); | 
 |    gluOrtho2D(-0.5, 639.5, -0.5, 479.5); | 
 |    glMatrixMode(GL_MODELVIEW); | 
 |  | 
 |    glShadeModel(GL_FLAT); | 
 |    glDisable(GL_DEPTH_TEST); | 
 |  | 
 |    glClearColor(0.0, 0.1, 1.0, 0.0); | 
 |    glClear(GL_COLOR_BUFFER_BIT); | 
 |    glColor3f(1.0, 0.0, 0.0); | 
 | } | 
 |  | 
 | static int | 
 | test01(int size, int num) | 
 | { | 
 |    int x, y; | 
 |  | 
 |    glBegin(GL_POINTS); | 
 |    for (y = 0; y < num; y++) | 
 |       for (x = 0; x < 480; x++) | 
 | 	 glVertex2i(x, x); | 
 |    glEnd(); | 
 |  | 
 |    return 480 * num; | 
 | } | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | static void | 
 | init_test02(void) | 
 | { | 
 |    glMatrixMode(GL_PROJECTION); | 
 |    glLoadIdentity(); | 
 |    gluOrtho2D(-0.5, 639.5, -0.5, 479.5); | 
 |    glMatrixMode(GL_MODELVIEW); | 
 |  | 
 |    glShadeModel(GL_SMOOTH); | 
 |    glDisable(GL_DEPTH_TEST); | 
 |  | 
 |    glClearColor(0.0, 0.1, 1.0, 0.0); | 
 |    glClear(GL_COLOR_BUFFER_BIT); | 
 | } | 
 |  | 
 | static int | 
 | test02(int size, int num) | 
 | { | 
 |    int x, y; | 
 |  | 
 |    glBegin(GL_LINES); | 
 |    for (y = 0; y < num; y++) | 
 |       for (x = 0; x < size; x++) { | 
 | 	 glColor3f(0.0, 1.0, y / (float) num); | 
 | 	 glVertex2i(0, size - 1); | 
 | 	 glColor3f(1.0, 0.0, x / (float) size); | 
 | 	 glVertex2i(x, x); | 
 |       } | 
 |    glEnd(); | 
 |  | 
 |    return num * size; | 
 | } | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | static void | 
 | init_test03(void) | 
 | { | 
 |    glMatrixMode(GL_PROJECTION); | 
 |    glLoadIdentity(); | 
 |    glOrtho(-0.5, 639.5, -0.5, 479.5, 1.0, -1000.0 * 480.0); | 
 |    glMatrixMode(GL_MODELVIEW); | 
 |  | 
 |    glShadeModel(GL_SMOOTH); | 
 |    glEnable(GL_DEPTH_TEST); | 
 |  | 
 |    glClearColor(0.0, 0.1, 1.0, 0.0); | 
 |    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
 | } | 
 |  | 
 | static int | 
 | test03(int size, int num) | 
 | { | 
 |    int x, y, z; | 
 |  | 
 |    glBegin(GL_TRIANGLES); | 
 |    for (y = 0; y < num; y++) | 
 |       for (x = 0; x < size; x += 5) { | 
 | 	 z = num * size - (y * size + x); | 
 | 	 glColor3f(0.0, 1.0, 0.0); | 
 | 	 glVertex3i(0, x, z); | 
 |  | 
 | 	 glColor3f(1.0, 0.0, x / (float) size); | 
 | 	 glVertex3i(size - 1 - x, 0, z); | 
 |  | 
 | 	 glColor3f(1.0, x / (float) size, 0.0); | 
 | 	 glVertex3i(x, size - 1 - x, z); | 
 |       } | 
 |    glEnd(); | 
 |  | 
 |    return size * num / 5; | 
 | } | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | static void | 
 | init_test04(void) | 
 | { | 
 |    int x, y; | 
 |    GLubyte tex[128 * 128 * 3]; | 
 |    GLenum gluerr; | 
 |  | 
 |    glMatrixMode(GL_PROJECTION); | 
 |    glLoadIdentity(); | 
 |    glOrtho(-0.5, 639.5, -0.5, 479.5, 1.0, -1000.0 * 480.0); | 
 |  | 
 |    glMatrixMode(GL_MODELVIEW); | 
 |  | 
 |    glShadeModel(GL_SMOOTH); | 
 |    glEnable(GL_DEPTH_TEST); | 
 |  | 
 |    glEnable(GL_BLEND); | 
 |    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | 
 |  | 
 |    for (y = 0; y < 128; y++) | 
 |       for (x = 0; x < 128; x++) { | 
 | 	 tex[(x + y * 128) * 3 + 0] = ((x % (128 / 4)) < (128 / 8)) ? 255 : 0; | 
 | 	 tex[(x + y * 128) * 3 + 1] = ((y % (128 / 4)) < (128 / 8)) ? 255 : 0; | 
 | 	 tex[(x + y * 128) * 3 + 2] = x; | 
 |       } | 
 |  | 
 |    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | 
 |    if ((gluerr = gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 128, 128, GL_RGB, | 
 | 				   GL_UNSIGNED_BYTE, (GLvoid *) (&tex[0])))) { | 
 |       fprintf(stderr, "GLULib%s\n", (char *) gluErrorString(gluerr)); | 
 |       exit(-1); | 
 |    } | 
 |  | 
 |    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_MIN_FILTER, | 
 | 		   GL_LINEAR_MIPMAP_NEAREST); | 
 |    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 
 |  | 
 |    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); | 
 |    glEnable(GL_TEXTURE_2D); | 
 |  | 
 |    glClearColor(0.0, 0.1, 1.0, 0.0); | 
 |    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
 | } | 
 |  | 
 | static int | 
 | test04(int size, int num) | 
 | { | 
 |    int x, y, z; | 
 |  | 
 |    glBegin(GL_TRIANGLES); | 
 |    for (y = 0; y < num; y++) | 
 |       for (x = 0; x < size; x += 5) { | 
 | 	 z = num * size - (y * size + x); | 
 | 	 glTexCoord2f(1.0, 1.0); | 
 | 	 glColor3f(1.0, 0.0, 0.0); | 
 | 	 glVertex3i(0, x, z); | 
 |  | 
 | 	 glTexCoord2f(0.0, 1.0); | 
 | 	 glColor3f(0.0, 1.0, 0.0); | 
 | 	 glVertex3i(size - 1 - x, 0, z); | 
 |  | 
 | 	 glTexCoord2f(1.0, 0.0); | 
 | 	 glColor3f(0.0, 0.0, 1.0); | 
 | 	 glVertex3i(x, size - 1 - x, z); | 
 |       } | 
 |    glEnd(); | 
 |  | 
 |    return num * size / 5; | 
 | } | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | static void | 
 | init_test05(void) | 
 | { | 
 |    int x, y; | 
 |    GLubyte tex[128 * 128 * 3]; | 
 |    GLenum gluerr; | 
 |  | 
 |    glMatrixMode(GL_PROJECTION); | 
 |    glLoadIdentity(); | 
 |    glOrtho(-0.5, 639.5, -0.5, 479.5, -1.0, 1.0); | 
 |  | 
 |    glMatrixMode(GL_MODELVIEW); | 
 |  | 
 |    glShadeModel(GL_SMOOTH); | 
 |    glEnable(GL_DEPTH_TEST); | 
 |  | 
 |    glEnable(GL_BLEND); | 
 |    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | 
 |  | 
 |    for (y = 0; y < 128; y++) | 
 |       for (x = 0; x < 128; x++) { | 
 | 	 tex[(x + y * 128) * 3 + 0] = ((x % (128 / 4)) < (128 / 8)) ? 255 : 0; | 
 | 	 tex[(x + y * 128) * 3 + 1] = ((y % (128 / 4)) < (128 / 8)) ? 255 : 0; | 
 | 	 tex[(x + y * 128) * 3 + 2] = x; | 
 |       } | 
 |  | 
 |    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | 
 |    if ((gluerr = gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 128, 128, GL_RGB, | 
 | 				   GL_UNSIGNED_BYTE, (GLvoid *) (&tex[0])))) { | 
 |       fprintf(stderr, "GLULib%s\n", (char *) gluErrorString(gluerr)); | 
 |       exit(-1); | 
 |    } | 
 |  | 
 |    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_MIN_FILTER, | 
 | 		   GL_LINEAR_MIPMAP_NEAREST); | 
 |    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 
 |  | 
 |    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); | 
 |    glEnable(GL_TEXTURE_2D); | 
 |  | 
 |    glDepthFunc(GL_ALWAYS); | 
 |  | 
 |    glClearColor(0.0, 0.1, 1.0, 0.0); | 
 |    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
 | } | 
 |  | 
 | static int | 
 | test05(int size, int num) | 
 | { | 
 |    int y; | 
 |    float v0[3], v1[3], v2[3], v3[3]; | 
 |    float cv0[3], cv1[3], cv2[3], cv3[3]; | 
 |    float tv0[3], tv1[3], tv2[3], tv3[3]; | 
 |  | 
 |    v0[0] = 320 - size / 2; | 
 |    v0[1] = 240 - size / 2; | 
 |    v0[2] = 0.0; | 
 |    v1[0] = 320 + size / 2; | 
 |    v1[1] = 240 - size / 2; | 
 |    v1[2] = 0.0; | 
 |    v2[0] = 320 - size / 2; | 
 |    v2[1] = 240 + size / 2; | 
 |    v2[2] = 0.0; | 
 |    v3[0] = 320 + size / 2; | 
 |    v3[1] = 240 + size / 2; | 
 |    v3[2] = 0.0; | 
 |    cv0[0] = 1.0; | 
 |    cv0[1] = 0.0; | 
 |    cv0[2] = 0.0; | 
 |    cv1[0] = 1.0; | 
 |    cv1[1] = 1.0; | 
 |    cv1[2] = 0.0; | 
 |    cv2[0] = 1.0; | 
 |    cv2[1] = 0.0; | 
 |    cv2[2] = 1.0; | 
 |    cv3[0] = 1.0; | 
 |    cv3[1] = 1.0; | 
 |    cv3[2] = 1.0; | 
 |    tv0[0] = 0.0; | 
 |    tv0[1] = 0.0; | 
 |    tv0[2] = 0.0; | 
 |    tv1[0] = 1.0; | 
 |    tv1[1] = 0.0; | 
 |    tv1[2] = 0.0; | 
 |    tv2[0] = 0.0; | 
 |    tv2[1] = 1.0; | 
 |    tv2[2] = 0.0; | 
 |    tv3[0] = 1.0; | 
 |    tv3[1] = 1.0; | 
 |    tv3[2] = 0.0; | 
 |  | 
 |    glBegin(GL_TRIANGLE_STRIP); | 
 |    for (y = 0; y < num; y++) { | 
 |       glColor3fv(cv0); | 
 |       glTexCoord2fv(tv0); | 
 |       glVertex3fv(v0); | 
 |  | 
 |       glColor3fv(cv1); | 
 |       glTexCoord2fv(tv1); | 
 |       glVertex3fv(v1); | 
 |  | 
 |       glColor3fv(cv2); | 
 |       glTexCoord2fv(tv2); | 
 |       glVertex3fv(v2); | 
 |  | 
 |       glColor3fv(cv3); | 
 |       glTexCoord2fv(tv3); | 
 |       glVertex3fv(v3); | 
 |    } | 
 |    glEnd(); | 
 |  | 
 |    return 4 * num - 2; | 
 | } | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | static void | 
 | init_test06(void) | 
 | { | 
 |    glMatrixMode(GL_PROJECTION); | 
 |    glLoadIdentity(); | 
 |    gluOrtho2D(-0.5, 639.5, -0.5, 479.5); | 
 |    glMatrixMode(GL_MODELVIEW); | 
 |  | 
 |    glShadeModel(GL_SMOOTH); | 
 |    glEnable(GL_DEPTH_TEST); | 
 |  | 
 |    glClearColor(0.0, 0.1, 1.0, 0.0); | 
 |    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
 | } | 
 |  | 
 | static int | 
 | test06(int size, int num) | 
 | { | 
 |    int y; | 
 |  | 
 |    for (y = 0; y < num; y++) { | 
 |       glClearColor(y / (float) num, 0.1, 1.0, 0.0); | 
 |       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
 |    } | 
 |  | 
 |    return num; | 
 | } | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | #define BMARKS_TIME 5.0 | 
 |  | 
 | #define NUM_BMARKS 6 | 
 |  | 
 | /* 554 ~= sqrt(640*480) */ | 
 |  | 
 | static benchmark bmarks[NUM_BMARKS] = { | 
 |    {"Simple Points", "Pnts", init_test01, test01, 0, 0, | 
 |     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, | 
 |    {"Smooth Lines", "Lins", init_test02, test02, 1, 5, | 
 |     {480, 250, 100, 50, 25, 0, 0, 0, 0, 0}}, | 
 |    {"ZSmooth Triangles", "Tris", init_test03, test03, 1, 5, | 
 |     {480, 250, 100, 50, 25, 0, 0, 0, 0, 0}}, | 
 |    {"ZSmooth Tex Blend Triangles", "Tris", init_test04, test04, 1, 5, | 
 |     {480, 250, 100, 50, 25, 0, 0, 0, 0, 0}}, | 
 |    {"ZSmooth Tex Blend TMesh Triangles", "Tris", init_test05, test05, 2, 8, | 
 |     {400, 250, 100, 50, 25, 10, 5, 2, 0, 0}}, | 
 |    {"Color/Depth Buffer Clears", "Clrs", init_test06, test06, 3, 0, | 
 |     {554, 0, 0, 0, 0, 0, 0, 0, 0, 0}} | 
 | }; | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | static void | 
 | dotest0param(benchmark * bmark) | 
 | { | 
 |    float stime, etime, dtime, tottime, maxtime, mintime; | 
 |    int num, numelem, calibnum, j; | 
 |  | 
 |    glPushAttrib(GL_ALL_ATTRIB_BITS); | 
 |    bmark->init(); | 
 |  | 
 |    stime = glutGet(GLUT_ELAPSED_TIME); | 
 |  | 
 |    dtime = 0.0; | 
 |    calibnum = 0; | 
 |    while (dtime < 2.0) { | 
 |       bmark->run(0, 1); | 
 |       glFinish(); | 
 |       etime = glutGet(GLUT_ELAPSED_TIME); | 
 |       dtime = (etime - stime) / 1000.0; | 
 |       calibnum++; | 
 |    } | 
 |    glPopAttrib(); | 
 |  | 
 |    fprintf(stderr, "Elapsed time for the calibration test (%d): %f\n", | 
 | 	   calibnum, dtime); | 
 |  | 
 |    num = (int) ((BMARKS_TIME / dtime) * calibnum); | 
 |  | 
 |    if (num < 1) | 
 |       num = 1; | 
 |  | 
 |    fprintf(stderr, "Selected number of benchmark iterations: %d\n", num); | 
 |  | 
 |    mintime = HUGE_VAL; | 
 |    maxtime = -HUGE_VAL; | 
 |  | 
 |    for (tottime = 0.0, j = 0; j < 5; j++) { | 
 |       glPushAttrib(GL_ALL_ATTRIB_BITS); | 
 |       bmark->init(); | 
 |  | 
 |       stime = glutGet(GLUT_ELAPSED_TIME); | 
 |       numelem = bmark->run(0, num); | 
 |       glFinish(); | 
 |       etime = glutGet(GLUT_ELAPSED_TIME); | 
 |  | 
 |       glPopAttrib(); | 
 |  | 
 |       dtime = (etime - stime) / 1000.0; | 
 |       tottime += dtime; | 
 |  | 
 |       fprintf(stderr, "Elapsed time for run %d: %f\n", j, dtime); | 
 |  | 
 |       if (dtime < mintime) | 
 | 	 mintime = dtime; | 
 |       if (dtime > maxtime) | 
 | 	 maxtime = dtime; | 
 |    } | 
 |  | 
 |    tottime -= mintime + maxtime; | 
 |  | 
 |    fprintf(stdout, "%s\n%f %s/sec", bmark->name, numelem / (tottime / 3.0), | 
 | 	   bmark->unit); | 
 |  | 
 |    if (bmark->type == 3) | 
 |       fprintf(stdout, ", MPixel Fill/sec: %f\n\n", | 
 | 	      (numelem * bmark->size[0] * (float) bmark->size[0]) / | 
 | 	      (1000000.0 * tottime / 3.0)); | 
 |    else | 
 |       fprintf(stdout, "\n\n"); | 
 | } | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | static void | 
 | dotest1param(benchmark * bmark) | 
 | { | 
 |    float stime, etime, dtime, tottime, maxtime, mintime; | 
 |    int num, numelem, calibnum, j, k; | 
 |  | 
 |    fprintf(stdout, "%s\n", bmark->name); | 
 |  | 
 |    for (j = 0; j < bmark->numsize; j++) { | 
 |       fprintf(stderr, "Current size: %d\n", bmark->size[j]); | 
 |  | 
 |       glPushAttrib(GL_ALL_ATTRIB_BITS); | 
 |       bmark->init(); | 
 |  | 
 |       stime = glutGet(GLUT_ELAPSED_TIME); | 
 |  | 
 |       dtime = 0.0; | 
 |       calibnum = 0; | 
 |       while (dtime < 2.0) { | 
 | 	 bmark->run(bmark->size[j], 1); | 
 | 	 glFinish(); | 
 | 	 etime = glutGet(GLUT_ELAPSED_TIME); | 
 | 	 dtime = (etime - stime) / 1000.0; | 
 | 	 calibnum++; | 
 |       } | 
 |       glPopAttrib(); | 
 |  | 
 |       fprintf(stderr, "Elapsed time for the calibration test (%d): %f\n", | 
 | 	      calibnum, dtime); | 
 |  | 
 |       num = (int) ((BMARKS_TIME / dtime) * calibnum); | 
 |  | 
 |       if (num < 1) | 
 | 	 num = 1; | 
 |  | 
 |       fprintf(stderr, "Selected number of benchmark iterations: %d\n", num); | 
 |  | 
 |       mintime = HUGE_VAL; | 
 |       maxtime = -HUGE_VAL; | 
 |  | 
 |       for (numelem = 1, tottime = 0.0, k = 0; k < 5; k++) { | 
 | 	 glPushAttrib(GL_ALL_ATTRIB_BITS); | 
 | 	 bmark->init(); | 
 |  | 
 | 	 stime = glutGet(GLUT_ELAPSED_TIME); | 
 | 	 numelem = bmark->run(bmark->size[j], num); | 
 | 	 glFinish(); | 
 | 	 etime = glutGet(GLUT_ELAPSED_TIME); | 
 |  | 
 | 	 glPopAttrib(); | 
 |  | 
 | 	 dtime = (etime - stime) / 1000.0; | 
 | 	 tottime += dtime; | 
 |  | 
 | 	 fprintf(stderr, "Elapsed time for run %d: %f\n", k, dtime); | 
 |  | 
 | 	 if (dtime < mintime) | 
 | 	    mintime = dtime; | 
 | 	 if (dtime > maxtime) | 
 | 	    maxtime = dtime; | 
 |       } | 
 |  | 
 |       tottime -= mintime + maxtime; | 
 |  | 
 |       fprintf(stdout, "SIZE=%03d => %f %s/sec", bmark->size[j], | 
 | 	      numelem / (tottime / 3.0), bmark->unit); | 
 |       if (bmark->type == 2) | 
 | 	 fprintf(stdout, ", MPixel Fill/sec: %f\n", | 
 | 		 (numelem * bmark->size[j] * bmark->size[j] / 2) / | 
 | 		 (1000000.0 * tottime / 3.0)); | 
 |       else | 
 | 	 fprintf(stdout, "\n"); | 
 |    } | 
 |  | 
 |    fprintf(stdout, "\n\n"); | 
 | } | 
 |  | 
 | /***************************************************************************/ | 
 |  | 
 | static void | 
 | display(void) | 
 | { | 
 |    int i; | 
 |  | 
 |    if (frontbuffer) | 
 |       glDrawBuffer(GL_FRONT); | 
 |    else | 
 |       glDrawBuffer(GL_BACK); | 
 |  | 
 |    for (i = 0; i < NUM_BMARKS; i++) { | 
 |       fprintf(stderr, "Benchmark: %d\n", i); | 
 |  | 
 |       switch (bmarks[i].type) { | 
 |       case 0: | 
 |       case 3: | 
 | 	 dotest0param(&bmarks[i]); | 
 | 	 break; | 
 |       case 1: | 
 |       case 2: | 
 | 	 dotest1param(&bmarks[i]); | 
 | 	 break; | 
 |       } | 
 |    } | 
 |  | 
 |    exit(0); | 
 | } | 
 |  | 
 | int | 
 | main(int ac, char **av) | 
 | { | 
 |    fprintf(stderr, "GLTest v1.0\nWritten by David Bucciarelli\n"); | 
 |  | 
 |    if (ac == 2) | 
 |       frontbuffer = 0; | 
 |  | 
 |    glutInit(&ac, av); | 
 |    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); | 
 |    glutInitWindowPosition(0, 0); | 
 |    glutInitWindowSize(640, 480); | 
 |    glutCreateWindow("OpenGL/Mesa Performances"); | 
 |    glutDisplayFunc(display); | 
 |    glutMainLoop(); | 
 |  | 
 |    return 0; | 
 | } |