/* * Test the GL_MESA_program_debug extension */ #include #include #include #include #include #include #define GL_GLEXT_PROTOTYPES #include /* * Print the string with line numbers */ static void list_program(const GLubyte *string, GLsizei len) { const char *c = (const char *) string; int i, line = 1, printNumber = 1; for (i = 0; i < len; i++) { if (printNumber) { printf("%3d ", line); printNumber = 0; } if (*c == '\n') { line++; printNumber = 1; } putchar(*c); c++; } putchar('\n'); } /* * Return the line number and column number that corresponds to the * given program position. Also return a null-terminated copy of that * line of the program string. */ static const GLubyte * find_line_column(const GLubyte *string, const GLubyte *pos, GLint *line, GLint *col) { const GLubyte *lineStart = string; const GLubyte *p = string; GLubyte *s; int len; *line = 1; while (p != pos) { if (*p == (GLubyte) '\n') { (*line)++; lineStart = p + 1; } p++; } *col = (pos - lineStart) + 1; /* return copy of this line */ while (*p != 0 && *p != '\n') p++; len = p - lineStart; s = (GLubyte *) malloc(len + 1); memcpy(s, lineStart, len); s[len] = 0; return s; } #define ARB_VERTEX_PROGRAM 1 #define ARB_FRAGMENT_PROGRAM 2 #define NV_VERTEX_PROGRAM 3 #define NV_FRAGMENT_PROGRAM 4 struct breakpoint { enum {PIXEL, LINE} type; int x, y; int line; GLboolean enabled; }; #define MAX_BREAKPOINTS 100 static struct breakpoint Breakpoints[MAX_BREAKPOINTS]; static int NumBreakpoints = 0; /* * Interactive debugger */ static void Debugger2(GLenum target, GLvoid *data) { static GLuint skipCount = 0; const GLubyte *ln; GLint pos, line, column; GLint id; int progType; GLint len; GLubyte *program; GLboolean stop; int i; /* Sigh, GL_VERTEX_PROGRAM_ARB == GL_VERTEX_PROGRAM_NV so it's a bit * hard to distinguish between them. */ if (target == GL_FRAGMENT_PROGRAM_ARB) progType = ARB_FRAGMENT_PROGRAM; else if (target == GL_FRAGMENT_PROGRAM_NV) progType = NV_FRAGMENT_PROGRAM; else progType = NV_VERTEX_PROGRAM; /* Until we hit zero, continue rendering */ if (skipCount > 0) { skipCount--; return; } /* Get id of the program and current position */ switch (progType) { case ARB_FRAGMENT_PROGRAM: glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &id); glGetIntegerv(GL_FRAGMENT_PROGRAM_POSITION_MESA, &pos); break; case NV_FRAGMENT_PROGRAM: glGetIntegerv(GL_FRAGMENT_PROGRAM_BINDING_NV, &id); glGetIntegerv(GL_FRAGMENT_PROGRAM_POSITION_MESA, &pos); break; case ARB_VERTEX_PROGRAM: glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &id); glGetIntegerv(GL_VERTEX_PROGRAM_POSITION_MESA, &pos); break; case NV_VERTEX_PROGRAM: glGetIntegerv(GL_VERTEX_PROGRAM_BINDING_NV, &id); glGetIntegerv(GL_VERTEX_PROGRAM_POSITION_MESA, &pos); break; default: abort(); } /* get program string */ if (progType == ARB_VERTEX_PROGRAM || progType == ARB_FRAGMENT_PROGRAM) glGetProgramivARB(target, GL_PROGRAM_LENGTH_ARB, &len); else glGetProgramivNV(id, GL_PROGRAM_LENGTH_NV, &len); program = malloc(len + 1); if (progType == ARB_VERTEX_PROGRAM || progType == ARB_FRAGMENT_PROGRAM) glGetProgramStringARB(target, GL_PROGRAM_STRING_ARB, program); else glGetProgramStringNV(id, GL_PROGRAM_STRING_NV, program); /* Get current line number, column, line string */ ln = find_line_column(program, program + pos, &line, &column); /* test breakpoints */ if (NumBreakpoints > 0) stop = GL_FALSE; else stop = GL_TRUE; for (i = 0; i < NumBreakpoints; i++) { if (Breakpoints[i].enabled) { switch (Breakpoints[i].type) { case PIXEL: if (progType == ARB_FRAGMENT_PROGRAM) { } else if (progType == NV_FRAGMENT_PROGRAM) { GLfloat pos[4]; int px, py; glGetProgramRegisterfvMESA(GL_FRAGMENT_PROGRAM_NV, 6, (GLubyte *) "f[WPOS]", pos); px = (int) pos[0]; py = (int) pos[1]; printf("%d, %d\n", px, py); if (px == Breakpoints[i].x && py == Breakpoints[i].y) { printf("Break at pixel (%d, %d)\n", px, py); stop = GL_TRUE; } } break; case LINE: if (line == Breakpoints[i].line) { /* hit a breakpoint! */ printf("Break at line %d\n", line); stop = GL_TRUE; } break; } } } if (!stop) { free(program); return; } printf("%d: %s\n", line, ln); /* get commands from stdin */ while (1) { char command[1000], *cmd; /* print prompt and get command */ printf("(%s %d) ", (target == GL_VERTEX_PROGRAM_ARB ? "vert" : "frag"), line); fgets(command, 999, stdin); /* skip leading whitespace */ for (cmd = command; cmd[0] == ' '; cmd++) ; if (!cmd[0]) /* nothing (repeat the previous cmd?) */ continue; switch (cmd[0]) { case 's': /* skip N instructions */ i = atoi(cmd + 2); skipCount = i; printf("Skipping %d instructions\n", i); return; case 'n': /* next */ return; case 'c': return; case 'd': /* dump machine state */ if (progType == NV_FRAGMENT_PROGRAM) { static const char *inRegs[] = { "f[WPOS]", "f[COL0]", "f[COL1]", "f[FOGC]", "f[TEX0]", "f[TEX1]", "f[TEX2]", "f[TEX3]", NULL }; static const char *outRegs[] = { "o[COLR]", "o[COLH]", "o[DEPR]", NULL }; GLfloat v[4]; int i; printf("Fragment input attributes:\n"); for (i = 0; inRegs[i]; i++) { glGetProgramRegisterfvMESA(GL_FRAGMENT_PROGRAM_NV, strlen(inRegs[i]), (const GLubyte *) inRegs[i], v); printf(" %s: %g, %g, %g, %g\n", inRegs[i], v[0], v[1], v[2], v[3]); } printf("Fragment output attributes:\n"); for (i = 0; outRegs[i]; i++) { glGetProgramRegisterfvMESA(GL_FRAGMENT_PROGRAM_NV, strlen(outRegs[i]), (const GLubyte *) outRegs[i], v); printf(" %s: %g, %g, %g, %g\n", outRegs[i], v[0], v[1], v[2], v[3]); } printf("Temporaries:\n"); for (i = 0; i < 4; i++) { char temp[100]; GLfloat v[4]; sprintf(temp, "R%d", i); glGetProgramRegisterfvMESA(GL_FRAGMENT_PROGRAM_NV, strlen(temp), (const GLubyte *) temp, v); printf(" %s: %g, %g, %g, %g\n", temp, v[0],v[1],v[2],v[3]); } } else if (progType == NV_VERTEX_PROGRAM) { GLfloat v[4]; int i; static const char *inRegs[] = { "v[OPOS]", "v[WGHT]", "v[NRML]", "v[COL0]", "v[COL1]", "v[FOGC]", "v[6]", "v[7]", "v[TEX0]", "v[TEX1]", "v[TEX2]", "v[TEX3]", "v[TEX4]", "v[TEX5]", "v[TEX6]", "v[TEX7]", NULL }; static const char *outRegs[] = { "o[HPOS]", "o[COL0]", "o[COL1]", "o[BFC0]", "o[BFC1]", "o[FOGC]", "o[PSIZ]", "o[TEX0]", "o[TEX1]", "o[TEX2]", "o[TEX3]", "o[TEX4]", "o[TEX5]", "o[TEX6]", "o[TEX7]", NULL }; printf("Vertex input attributes:\n"); for (i = 0; inRegs[i]; i++) { glGetProgramRegisterfvMESA(GL_VERTEX_PROGRAM_NV, strlen(inRegs[i]), (const GLubyte *) inRegs[i], v); printf(" %s: %g, %g, %g, %g\n", inRegs[i], v[0], v[1], v[2], v[3]); } printf("Vertex output attributes:\n"); for (i = 0; outRegs[i]; i++) { glGetProgramRegisterfvMESA(GL_VERTEX_PROGRAM_NV, strlen(outRegs[i]), (const GLubyte *) outRegs[i], v); printf(" %s: %g, %g, %g, %g\n", outRegs[i], v[0], v[1], v[2], v[3]); } printf("Temporaries:\n"); for (i = 0; i < 4; i++) { char temp[100]; GLfloat v[4]; sprintf(temp, "R%d", i); glGetProgramRegisterfvMESA(GL_VERTEX_PROGRAM_NV, strlen(temp), (const GLubyte *) temp, v); printf(" %s: %g, %g, %g, %g\n", temp, v[0],v[1],v[2],v[3]); } } break; case 'l': /* list */ list_program(program, len); break; case 'p': /* print */ { GLfloat v[4]; char *c; cmd++; while (*cmd == ' ') cmd++; c = cmd; while (*c) { if (*c == '\n' || *c == '\r') *c = 0; else c++; } glGetProgramRegisterfvMESA(target, strlen(cmd), (const GLubyte *) cmd, v); if (glGetError() == GL_NO_ERROR) printf("%s = %g, %g, %g, %g\n", cmd, v[0], v[1], v[2], v[3]); else printf("Invalid expression\n"); } break; case 'b': if (cmd[1] == ' ' && isdigit(cmd[2])) { char *comma = strchr(cmd, ','); if (comma) { /* break at pixel */ int x = atoi(cmd + 2); int y = atoi(comma + 1); if (NumBreakpoints < MAX_BREAKPOINTS) { Breakpoints[NumBreakpoints].type = PIXEL; Breakpoints[NumBreakpoints].x = x; Breakpoints[NumBreakpoints].y = y; Breakpoints[NumBreakpoints].enabled = GL_TRUE; NumBreakpoints++; printf("Breakpoint %d: break at pixel (%d, %d)\n", NumBreakpoints, x, y); } } else { /* break at line */ int l = atoi(cmd + 2); if (l && NumBreakpoints < MAX_BREAKPOINTS) { Breakpoints[NumBreakpoints].type = LINE; Breakpoints[NumBreakpoints].line = l; Breakpoints[NumBreakpoints].enabled = GL_TRUE; NumBreakpoints++; printf("Breakpoint %d: break at line %d\n", NumBreakpoints, l); } } } else { /* list breakpoints */ printf("Breakpoints:\n"); for (i = 0; i < NumBreakpoints; i++) { switch (Breakpoints[i].type) { case LINE: printf(" %d: break at line %d\n", i + 1, Breakpoints[i].line); break; case PIXEL: printf(" %d: break at pixel (%d, %d)\n", i + 1, Breakpoints[i].x, Breakpoints[i].y); break; } } } break; case 'h': /* help */ printf("Debugger commands:\n"); printf(" b list breakpoints\n"); printf(" b N break at line N\n"); printf(" b x,y break at pixel x,y\n"); printf(" c continue execution\n"); printf(" d display register values\n"); printf(" h help\n"); printf(" l list program\n"); printf(" n next instruction\n"); printf(" p V print value V\n"); printf(" s N skip N instructions\n"); break; default: printf("Unknown command: %c\n", cmd[0]); } } } /* * Print current line, some registers, and continue. */ static void Debugger(GLenum target, GLvoid *data) { GLint pos; const GLubyte *ln; GLint line, column; GLfloat v[4]; assert(target == GL_FRAGMENT_PROGRAM_NV); glGetIntegerv(GL_FRAGMENT_PROGRAM_POSITION_MESA, &pos); ln = find_line_column((const GLubyte *) data, (const GLubyte *) data + pos, &line, &column); printf("%d:%d: %s\n", line, column, (char *) ln); glGetProgramRegisterfvMESA(GL_FRAGMENT_PROGRAM_NV, 2, (const GLubyte *) "R0", v); printf(" R0 = %g, %g, %g, %g\n", v[0], v[1], v[2], v[3]); glGetProgramRegisterfvMESA(GL_FRAGMENT_PROGRAM_NV, 7, (const GLubyte *) "f[WPOS]", v); printf(" o[WPOS] = %g, %g, %g, %g\n", v[0], v[1], v[2], v[3]); glGetProgramRegisterfvMESA(GL_FRAGMENT_PROGRAM_NV, 7, (const GLubyte *) "o[COLR]", v); printf(" o[COLR] = %g, %g, %g, %g\n", v[0], v[1], v[2], v[3]); free((void *) ln); } /**********************************************************************/ static GLfloat Diffuse[4] = { 0.5, 0.5, 1.0, 1.0 }; static GLfloat Specular[4] = { 0.8, 0.8, 0.8, 1.0 }; static GLfloat LightPos[4] = { 0.0, 10.0, 20.0, 1.0 }; static GLfloat Delta = 1.0; static GLuint FragProg; static GLuint VertProg; static GLboolean Anim = GL_TRUE; static GLboolean Wire = GL_FALSE; static GLboolean PixelLight = GL_TRUE; static GLfloat Xrot = 0, Yrot = 0; #define NAMED_PARAMETER4FV(prog, name, v) \ glProgramNamedParameter4fvNV(prog, strlen(name), (const GLubyte *) name, v) static void Display( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); if (PixelLight) { NAMED_PARAMETER4FV(FragProg, "LightPos", LightPos); glEnable(GL_FRAGMENT_PROGRAM_NV); glEnable(GL_VERTEX_PROGRAM_NV); glDisable(GL_LIGHTING); } else { glLightfv(GL_LIGHT0, GL_POSITION, LightPos); glDisable(GL_FRAGMENT_PROGRAM_NV); glDisable(GL_VERTEX_PROGRAM_NV); glEnable(GL_LIGHTING); } glPushMatrix(); glRotatef(Xrot, 1, 0, 0); glRotatef(Yrot, 0, 1, 0); #if 1 glutSolidSphere(2.0, 10, 5); #else { GLUquadricObj *q = gluNewQuadric(); gluQuadricNormals(q, GL_SMOOTH); gluQuadricTexture(q, GL_TRUE); glRotatef(90, 1, 0, 0); glTranslatef(0, 0, -1); gluCylinder(q, 1.0, 1.0, 2.0, 24, 1); gluDeleteQuadric(q); } #endif glPopMatrix(); glutSwapBuffers(); } static void Idle(void) { LightPos[0] += Delta; if (LightPos[0] > 25.0) Delta = -1.0; else if (LightPos[0] <- 25.0) Delta = 1.0; glutPostRedisplay(); } static void Reshape( int width, int height ) { glViewport( 0, 0, width, height ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 ); /*glOrtho( -2.0, 2.0, -2.0, 2.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 ' ': Anim = !Anim; if (Anim) glutIdleFunc(Idle); else glutIdleFunc(NULL); break; case 'x': LightPos[0] -= 1.0; break; case 'X': LightPos[0] += 1.0; break; case 'w': Wire = !Wire; if (Wire) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; case 'p': PixelLight = !PixelLight; if (PixelLight) { printf("Per-pixel lighting\n"); } else { printf("Conventional lighting\n"); } break; case 27: exit(0); break; } glutPostRedisplay(); } static void SpecialKey( int key, int x, int y ) { const GLfloat step = 3.0; (void) x; (void) y; switch (key) { case GLUT_KEY_UP: Xrot -= step; break; case GLUT_KEY_DOWN: Xrot += step; break; case GLUT_KEY_LEFT: Yrot -= step; break; case GLUT_KEY_RIGHT: Yrot += step; break; } glutPostRedisplay(); } static void Init( int argc, char *argv[] ) { static const char *fragProgramText = "!!FP1.0\n" "DECLARE Diffuse; \n" "DECLARE Specular; \n" "DECLARE LightPos; \n" "# Compute normalized LightPos, put it in R0\n" "DP3 R0.x, LightPos, LightPos;\n" "RSQ R0.y, R0.x;\n" "MUL R0, LightPos, R0.y;\n" "# Compute normalized normal, put it in R1\n" "DP3 R1, f[TEX0], f[TEX0]; \n" "RSQ R1.y, R1.x;\n" "MUL R1, f[TEX0], R1.y;\n" "# Compute dot product of light direction and normal vector\n" "DP3 R2, R0, R1;\n" "MUL R3, Diffuse, R2; # diffuse attenuation\n" "POW R4, R2.x, {20.0}.x; # specular exponent\n" "MUL R5, Specular, R4; # specular attenuation\n" "ADD o[COLR], R3, R5; # add diffuse and specular colors\n" "END \n" ; static const char *vertProgramText = "!!VP1.0\n" "# typical modelview/projection transform\n" "DP4 o[HPOS].x, c[0], v[OPOS] ;\n" "DP4 o[HPOS].y, c[1], v[OPOS] ;\n" "DP4 o[HPOS].z, c[2], v[OPOS] ;\n" "DP4 o[HPOS].w, c[3], v[OPOS] ;\n" "# transform normal by inv transpose of modelview, put in tex0\n" "DP4 o[TEX0].x, c[4], v[NRML] ;\n" "DP4 o[TEX0].y, c[5], v[NRML] ;\n" "DP4 o[TEX0].z, c[6], v[NRML] ;\n" "DP4 o[TEX0].w, c[7], v[NRML] ;\n" "END\n"; ; if (!glutExtensionSupported("GL_NV_vertex_program")) { printf("Sorry, this demo requires GL_NV_vertex_program\n"); exit(1); } if (!glutExtensionSupported("GL_NV_fragment_program")) { printf("Sorry, this demo requires GL_NV_fragment_program\n"); exit(1); } glGenProgramsNV(1, &FragProg); assert(FragProg > 0); glGenProgramsNV(1, &VertProg); assert(VertProg > 0); /* * Fragment program */ glLoadProgramNV(GL_FRAGMENT_PROGRAM_NV, FragProg, strlen(fragProgramText), (const GLubyte *) fragProgramText); assert(glIsProgramNV(FragProg)); glBindProgramNV(GL_FRAGMENT_PROGRAM_NV, FragProg); NAMED_PARAMETER4FV(FragProg, "Diffuse", Diffuse); NAMED_PARAMETER4FV(FragProg, "Specular", Specular); /* * Vertex program */ glLoadProgramNV(GL_VERTEX_PROGRAM_NV, VertProg, strlen(vertProgramText), (const GLubyte *) vertProgramText); assert(glIsProgramNV(VertProg)); glBindProgramNV(GL_VERTEX_PROGRAM_NV, VertProg); glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV); glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 4, GL_MODELVIEW, GL_INVERSE_TRANSPOSE_NV); /* * Misc init */ glClearColor(0.3, 0.3, 0.3, 0.0); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Diffuse); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Specular); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0); printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); printf("Press p to toggle between per-pixel and per-vertex lighting\n"); #ifdef GL_MESA_program_debug if (argc > 1 && strcmp(argv[1], "fragment") == 0) { printf(">> Debugging fragment program\n"); glProgramCallbackMESA(GL_FRAGMENT_PROGRAM_ARB, Debugger2, (GLvoid *) fragProgramText); glEnable(GL_FRAGMENT_PROGRAM_CALLBACK_MESA); } else { printf(">> Debugging vertex program\n"); glProgramCallbackMESA(GL_VERTEX_PROGRAM_ARB, Debugger2, (GLvoid *) fragProgramText); glEnable(GL_VERTEX_PROGRAM_CALLBACK_MESA); } #endif } int main( int argc, char *argv[] ) { glutInit( &argc, argv ); glutInitWindowPosition( 0, 0 ); glutInitWindowSize( 200, 200 ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); glutCreateWindow(argv[0]); glutReshapeFunc( Reshape ); glutKeyboardFunc( Key ); glutSpecialFunc( SpecialKey ); glutDisplayFunc( Display ); if (Anim) glutIdleFunc(Idle); Init(argc, argv); glutMainLoop(); return 0; }