#include <GL/glu.h>
#include <GL/glx.h>
+#undef DEBUG /* Defining this causes check_gl_error() to be called inside
+ time-critical sections, which could slow things down (since
+ it might result in a round-trip, and stall of the pipeline.)
+ */
+
extern void clear_gl_error (void);
extern void check_gl_error (const char *type);
static int fps_text_x = 10;
static int fps_text_y = 10;
static int fps_ascent, fps_descent;
-static int fps_sample_frames = 10;
static GLuint font_dlist;
static Bool fps_clear_p = False;
+static char fps_string[1024];
static void
fps_init (ModeInfo *mi)
static void
fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
{
- int i;
- /* save the current state */
- /* note: could be expensive! */
+ const char *L2 = strchr (string, '\n');
if (y < 0)
- y = mi->xgwa.height + y;
+ {
+ y = mi->xgwa.height + y;
+ if (L2)
+ y -= (fps_ascent + fps_descent);
+ }
+# ifdef DEBUG
clear_gl_error ();
- glPushAttrib(GL_ALL_ATTRIB_BITS);
+# endif
+
+ /* Sadly, this causes a stall of the graphics pipeline (as would the
+ equivalent calls to glGet*.) But there's no way around this, short
+ of having each caller set up the specific display matrix we need
+ here, which would kind of defeat the purpose of centralizing this
+ code in one file.
+ */
+ glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */
+ GL_ENABLE_BIT | /* for various glDisable calls */
+ GL_CURRENT_BIT | /* for glColor3f() */
+ GL_LIST_BIT); /* for glListBase() */
{
+# ifdef DEBUG
check_gl_error ("glPushAttrib");
+# endif
/* disable lighting and texturing when drawing bitmaps!
- (glPopAttrib() restores these, I believe.)
+ (glPopAttrib() restores these.)
*/
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_LIGHTING);
- glDisable(GL_BLEND);
- glDisable(GL_DEPTH_TEST);
+ glDisable (GL_TEXTURE_2D);
+ glDisable (GL_LIGHTING);
+ glDisable (GL_BLEND);
+ glDisable (GL_DEPTH_TEST);
+ glDisable (GL_CULL_FACE);
/* glPopAttrib() does not restore matrix changes, so we must
push/pop the matrix stacks to be non-intrusive there.
glMatrixMode(GL_PROJECTION);
glPushMatrix();
{
+# ifdef DEBUG
check_gl_error ("glPushMatrix");
+# endif
glLoadIdentity();
/* Each matrix mode has its own stack, so we need to push/pop
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
{
+# ifdef DEBUG
check_gl_error ("glPushMatrix");
+# endif
glLoadIdentity();
gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
+# ifdef DEBUG
check_gl_error ("gluOrtho2D");
+# endif
/* clear the background */
if (fps_clear_p)
{
+ int lines = L2 ? 2 : 1;
glColor3f (0, 0, 0);
- glRecti (x, y - fps_descent,
+ glRecti (x / 2, y - fps_descent,
mi->xgwa.width - x,
- y + fps_ascent + fps_descent);
+ y + lines * (fps_ascent + fps_descent));
}
/* draw the text */
glColor3f (1, 1, 1);
glRasterPos2f (x, y);
- for (i = 0; i < strlen(string); i++)
- glCallList (font_dlist + (int)string[i]);
+ glListBase (font_dlist);
+ if (L2)
+ {
+ L2++;
+ glCallLists (strlen(L2), GL_UNSIGNED_BYTE, L2);
+ glRasterPos2f (x, y + (fps_ascent + fps_descent));
+ glCallLists (L2 - string - 1, GL_UNSIGNED_BYTE, string);
+ }
+ else
+ {
+ glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
+ }
+
+# ifdef DEBUG
check_gl_error ("fps_print_string");
+# endif
}
glPopMatrix();
}
}
/* clean up after our state changes */
glPopAttrib();
+# ifdef DEBUG
check_gl_error ("glPopAttrib");
+# endif
}
-void
-do_fps (ModeInfo *mi)
+GLfloat
+fps_1 (ModeInfo *mi)
{
- /* every N frames, get the time and use it to get the frames per second */
- static int frame_counter = -1;
- static double oldtime = 0; /* time in usecs, as a double */
- static double newtime = 0;
-
- static char msg [1024] = { 0, };
-
- if (frame_counter == -1)
+ static Bool initted_p = False;
+ static int last_ifps = -1;
+ static GLfloat last_fps = -1;
+ static int frame_count = 0;
+ static struct timeval prev = { 0, 0 };
+ static struct timeval now = { 0, 0 };
+
+ if (!initted_p)
{
+ initted_p = True;
fps_init (mi);
- frame_counter = fps_sample_frames;
+ strcpy (fps_string, "FPS: (accumulating...)");
}
- if (frame_counter++ == fps_sample_frames)
+ /* Every N frames (where N is approximately one second's worth of frames)
+ check the wall clock. We do this because checking the wall clock is
+ a slow operation.
+ */
+ if (frame_count++ >= last_ifps)
{
- double fps;
- struct timeval now;
# ifdef GETTIMEOFDAY_TWO_ARGS
struct timezone tzp;
gettimeofday(&now, &tzp);
gettimeofday(&now);
# endif
- oldtime = newtime;
- newtime = now.tv_sec + ((double) now.tv_usec * 0.000001);
+ if (prev.tv_sec == 0)
+ prev = now;
+ }
+
+ /* If we've probed the wall-clock time, regenerate the string.
+ */
+ if (now.tv_sec != prev.tv_sec)
+ {
+ double uprev = prev.tv_sec + ((double) prev.tv_usec * 0.000001);
+ double unow = now.tv_sec + ((double) now.tv_usec * 0.000001);
+ double fps = frame_count / (unow - uprev);
+
+ prev = now;
+ frame_count = 0;
+ last_ifps = fps;
+ last_fps = fps;
- fps = fps_sample_frames / (newtime - oldtime);
+ sprintf (fps_string, "FPS: %.02f", fps);
- if (fps < 0.0001)
+ if (mi->pause != 0)
{
- strcpy(msg, "FPS: (accumulating...)");
+ char buf[40];
+ sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
+ while(*buf && buf[strlen(buf)-1] == '0')
+ buf[strlen(buf)-1] = 0;
+ if (buf[strlen(buf)-1] == '.')
+ buf[strlen(buf)-1] = 0;
+ sprintf(fps_string + strlen(fps_string),
+ " (including %s sec/frame delay)",
+ buf);
}
- else
+
+ if (mi->polygon_count > 0)
{
- sprintf(msg, "FPS: %.02f", fps);
-
- if (mi->pause != 0)
- {
- char buf[40];
- sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
- while(*buf && buf[strlen(buf)-1] == '0')
- buf[strlen(buf)-1] = 0;
- if (buf[strlen(buf)-1] == '.')
- buf[strlen(buf)-1] = 0;
- sprintf(msg + strlen(msg), " (including %s sec/frame delay)",
- buf);
- }
- }
+ unsigned long p = mi->polygon_count;
+ const char *s = "";
+# if 0
+ if (p >= (1024 * 1024)) p >>= 20, s = "M";
+ else if (p >= 2048) p >>= 10, s = "K";
+# endif
- frame_counter = 0;
+ strcat (fps_string, "\nPolys: ");
+ if (p >= 1000000)
+ sprintf (fps_string + strlen(fps_string), "%lu,%03lu,%03lu%s",
+ (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
+ else if (p >= 1000)
+ sprintf (fps_string + strlen(fps_string), "%lu,%03lu%s",
+ (p / 1000), (p % 1000), s);
+ else
+ sprintf (fps_string + strlen(fps_string), "%lu%s", p, s);
+ }
}
- fps_print_string (mi, fps_text_x, fps_text_y, msg);
+ return last_fps;
+}
+
+void
+fps_2 (ModeInfo *mi)
+{
+ fps_print_string (mi, fps_text_x, fps_text_y, fps_string);
+}
+
+
+void
+do_fps (ModeInfo *mi)
+{
+ fps_1 (mi); /* Lazily compute current FPS value, about once a second. */
+ fps_2 (mi); /* Print the string every frame (else nothing shows up.) */
}