mesa/progs/tests/debugger.c

734 lines
21 KiB
C

/*
* Test the GL_MESA_program_debug extension
*/
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/glut.h>
/*
* 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;
}