/** * Random rendering, to check for crashes, hangs, etc. * * Brian Paul * 21 June 2007 */ #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <GL/glew.h> #include <GL/glut.h> static int Win; static GLboolean Anim = GL_TRUE; static int Width = 200, Height = 200; static int DB = 0; static int MinVertexCount = 0, MaxVertexCount = 1000; static int Count = 0; struct vertex { int type; float v[4]; }; static int BufferSize = 10000; static struct vertex *Vbuffer = NULL; static int Vcount, Vprim; enum { BEGIN, END, VERTEX2, VERTEX3, VERTEX4, COLOR3, COLOR4, TEX2, TEX3, TEX4, SECCOLOR3, NORMAL3 }; /** * This can be called from within gdb after a crash: * (gdb) call ReportState() */ static void ReportState(void) { static const struct { GLenum token; char *str; GLenum type; } state [] = { { GL_ALPHA_TEST, "GL_ALPHA_TEST", GL_INT }, { GL_BLEND, "GL_BLEND", GL_INT }, { GL_CLIP_PLANE0, "GL_CLIP_PLANE0", GL_INT }, { GL_DEPTH_TEST, "GL_DEPTH_TEST", GL_INT }, { GL_LIGHTING, "GL_LIGHTING", GL_INT }, { GL_LINE_WIDTH, "GL_LINE_WIDTH", GL_FLOAT }, { GL_POINT_SIZE, "GL_POINT_SIZE", GL_FLOAT }, { GL_SHADE_MODEL, "GL_SHADE_MODEL", GL_INT }, { GL_SCISSOR_TEST, "GL_SCISSOR_TEST", GL_INT }, { 0, NULL, 0 } }; GLint i; for (i = 0; state[i].token; i++) { if (state[i].type == GL_INT) { GLint v; glGetIntegerv(state[i].token, &v); printf("%s = %d\n", state[i].str, v); } else { GLfloat v; glGetFloatv(state[i].token, &v); printf("%s = %f\n", state[i].str, v); } } } static void PrintVertex(const char *f, const struct vertex *v, int sz) { int i; printf("%s(", f); for (i = 0; i < sz; i++) { printf("%g%s", v->v[i], (i == sz-1) ? "" : ", "); } printf(");\n"); } /** * This can be called from within gdb after a crash: * (gdb) call ReportState() */ static void LastPrim(void) { int i; for (i = 0; i < Vcount; i++) { switch (Vbuffer[i].type) { case BEGIN: printf("glBegin(%d);\n", (int) Vbuffer[i].v[0]); break; case END: printf("glEnd();\n"); break; case VERTEX2: PrintVertex("glVertex2f", Vbuffer + i, 2); break; case VERTEX3: PrintVertex("glVertex3f", Vbuffer + i, 3); break; case VERTEX4: PrintVertex("glVertex4f", Vbuffer + i, 4); break; case COLOR3: PrintVertex("glColor3f", Vbuffer + i, 3); break; case COLOR4: PrintVertex("glColor4f", Vbuffer + i, 4); break; case TEX2: PrintVertex("glTexCoord2f", Vbuffer + i, 2); break; case TEX3: PrintVertex("glTexCoord3f", Vbuffer + i, 3); break; case TEX4: PrintVertex("glTexCoord4f", Vbuffer + i, 4); break; case SECCOLOR3: PrintVertex("glSecondaryColor3f", Vbuffer + i, 3); break; case NORMAL3: PrintVertex("glNormal3f", Vbuffer + i, 3); break; default: abort(); } } } static int RandomInt(int max) { if (max == 0) return 0; return rand() % max; } static float RandomFloat(float min, float max) { int k = rand() % 10000; float x = min + (max - min) * k / 10000.0; return x; } /* * Return true if random number in [0,1] is <= percentile. */ static GLboolean RandomChoice(float percentile) { return RandomFloat(0.0, 1.0) <= percentile; } static void RandomStateChange(void) { int k = RandomInt(19); switch (k) { case 0: glEnable(GL_BLEND); break; case 1: glDisable(GL_BLEND); break; case 2: glEnable(GL_ALPHA_TEST); break; case 3: glEnable(GL_ALPHA_TEST); break; case 4: glEnable(GL_DEPTH_TEST); break; case 5: glEnable(GL_DEPTH_TEST); break; case 6: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); break; case 7: glPointSize(10.0); break; case 8: glPointSize(1.0); break; case 9: glLineWidth(10.0); break; case 10: glLineWidth(1.0); break; case 11: glEnable(GL_LIGHTING); break; case 12: glDisable(GL_LIGHTING); break; case 13: glEnable(GL_SCISSOR_TEST); break; case 14: glDisable(GL_SCISSOR_TEST); break; case 15: glEnable(GL_CLIP_PLANE0); break; case 16: glDisable(GL_CLIP_PLANE0); break; case 17: glShadeModel(GL_FLAT); break; case 18: glShadeModel(GL_SMOOTH); break; } } static void RandomPrimitive(void) { int i; int len = MinVertexCount + RandomInt(MaxVertexCount - MinVertexCount); Vprim = RandomInt(10); glBegin(Vprim); Vbuffer[Vcount].type = BEGIN; Vbuffer[Vcount].v[0] = Vprim; Vcount++; for (i = 0; i < len; i++) { int k = RandomInt(9); Vbuffer[Vcount].v[0] = RandomFloat(-3, 3); Vbuffer[Vcount].v[1] = RandomFloat(-3, 3); Vbuffer[Vcount].v[2] = RandomFloat(-3, 3); Vbuffer[Vcount].v[3] = RandomFloat(-3, 3); switch (k) { case 0: glVertex2fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = VERTEX2; break; case 1: glVertex3fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = VERTEX3; break; case 2: glVertex4fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = VERTEX4; break; case 3: glColor3fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = COLOR3; break; case 4: glColor4fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = COLOR4; break; case 5: glTexCoord2fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = TEX2; break; case 6: glTexCoord3fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = TEX3; break; case 7: glTexCoord4fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = TEX4; break; case 8: glSecondaryColor3fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = SECCOLOR3; break; case 9: glNormal3fv(Vbuffer[Vcount].v); Vbuffer[Vcount].type = NORMAL3; break; default: abort(); } Vcount++; if (Vcount >= BufferSize - 2) { /* reset */ Vcount = 0; } } Vbuffer[Vcount++].type = END; glEnd(); } static void RandomDraw(void) { int i; GLboolean dlist = RandomChoice(0.1); if (dlist) glNewList(1, GL_COMPILE); for (i = 0; i < 3; i++) { RandomStateChange(); } RandomPrimitive(); if (dlist) { glEndList(); glCallList(1); } } static void Idle(void) { glutPostRedisplay(); } static void Draw(void) { #if 1 RandomDraw(); Count++; #else /* cut & paste temp code here */ #endif assert(glGetError() == 0); if (DB) glutSwapBuffers(); else glFinish(); } static void Reshape(int width, int height) { Width = width; Height = height; glViewport(0, 0, width, height); glScissor(20, 20, Width-40, Height-40); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -15.0); } static void Key(unsigned char key, int x, int y) { (void) x; (void) y; switch (key) { case 27: glutDestroyWindow(Win); exit(0); break; } glutPostRedisplay(); } static void Init(void) { static const GLdouble plane[4] = {1, 1, 0, 0}; glDrawBuffer(GL_FRONT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_LIGHT0); glClipPlane(GL_CLIP_PLANE0, plane); Vbuffer = (struct vertex *) malloc(BufferSize * sizeof(struct vertex)); /* silence warnings */ (void) ReportState; (void) LastPrim; } static void ParseArgs(int argc, char *argv[]) { int i; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-s") == 0) { int j = atoi(argv[i + 1]); printf("Random seed value: %d\n", j); srand(j); i++; } else if (strcmp(argv[i], "-a") == 0) { i++; MinVertexCount = atoi(argv[i]); } else if (strcmp(argv[i], "-b") == 0) { i++; MaxVertexCount = atoi(argv[i]); } } } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitWindowPosition(0, 0); glutInitWindowSize(Width, Height); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); Win = glutCreateWindow(argv[0]); glewInit(); ParseArgs(argc, argv); glutReshapeFunc(Reshape); glutKeyboardFunc(Key); glutDisplayFunc(Draw); if (Anim) glutIdleFunc(Idle); Init(); glutMainLoop(); return 0; }