1 /* tube, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
2 * Utility function to draw a frames-per-second display.
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #include "screenhack.h"
18 #include "xlockmoreI.h"
24 #undef DEBUG /* Defining this causes check_gl_error() to be called inside
25 time-critical sections, which could slow things down (since
26 it might result in a round-trip, and stall of the pipeline.)
29 extern void clear_gl_error (void);
30 extern void check_gl_error (const char *type);
32 static int fps_text_x = 10;
33 static int fps_text_y = 10;
34 static int fps_ascent, fps_descent;
35 static GLuint font_dlist;
36 static Bool fps_clear_p = False;
37 static char fps_string[1024];
40 fps_init (ModeInfo *mi)
42 const char *font = get_string_resource ("fpsFont", "Font");
47 fps_clear_p = get_boolean_resource ("fpsSolid", "FPSSolid");
49 if (!font) font = "-*-courier-bold-r-normal-*-180-*";
50 f = XLoadQueryFont(mi->dpy, font);
51 if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
54 first = f->min_char_or_byte2;
55 last = f->max_char_or_byte2;
58 font_dlist = glGenLists ((GLuint) last+1);
59 check_gl_error ("glGenLists");
61 fps_ascent = f->ascent;
62 fps_descent = f->descent;
64 if (get_boolean_resource ("fpsTop", "FPSTop"))
65 fps_text_y = - (f->ascent + 10);
67 glXUseXFont(id, first, last-first+1, font_dlist + first);
68 check_gl_error ("glXUseXFont");
73 fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
75 const char *L2 = strchr (string, '\n');
79 y = mi->xgwa.height + y;
81 y -= (fps_ascent + fps_descent);
88 /* Sadly, this causes a stall of the graphics pipeline (as would the
89 equivalent calls to glGet*.) But there's no way around this, short
90 of having each caller set up the specific display matrix we need
91 here, which would kind of defeat the purpose of centralizing this
94 glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */
95 GL_ENABLE_BIT | /* for various glDisable calls */
96 GL_CURRENT_BIT | /* for glColor3f() */
97 GL_LIST_BIT); /* for glListBase() */
100 check_gl_error ("glPushAttrib");
103 /* disable lighting and texturing when drawing bitmaps!
104 (glPopAttrib() restores these.)
106 glDisable (GL_TEXTURE_2D);
107 glDisable (GL_LIGHTING);
108 glDisable (GL_BLEND);
109 glDisable (GL_DEPTH_TEST);
110 glDisable (GL_CULL_FACE);
112 /* glPopAttrib() does not restore matrix changes, so we must
113 push/pop the matrix stacks to be non-intrusive there.
115 glMatrixMode(GL_PROJECTION);
119 check_gl_error ("glPushMatrix");
123 /* Each matrix mode has its own stack, so we need to push/pop
125 glMatrixMode(GL_MODELVIEW);
129 check_gl_error ("glPushMatrix");
133 gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
135 check_gl_error ("gluOrtho2D");
138 /* clear the background */
141 int lines = L2 ? 2 : 1;
143 glRecti (x / 2, y - fps_descent,
145 y + lines * (fps_ascent + fps_descent));
150 glRasterPos2f (x, y);
151 glListBase (font_dlist);
156 glCallLists (strlen(L2), GL_UNSIGNED_BYTE, L2);
157 glRasterPos2f (x, y + (fps_ascent + fps_descent));
158 glCallLists (L2 - string - 1, GL_UNSIGNED_BYTE, string);
162 glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
166 check_gl_error ("fps_print_string");
171 glMatrixMode(GL_PROJECTION);
175 /* clean up after our state changes */
178 check_gl_error ("glPopAttrib");
186 static Bool initted_p = False;
187 static int last_ifps = -1;
188 static GLfloat last_fps = -1;
189 static int frame_count = 0;
190 static struct timeval prev = { 0, 0 };
191 static struct timeval now = { 0, 0 };
197 strcpy (fps_string, "FPS: (accumulating...)");
200 /* Every N frames (where N is approximately one second's worth of frames)
201 check the wall clock. We do this because checking the wall clock is
204 if (frame_count++ >= last_ifps)
206 # ifdef GETTIMEOFDAY_TWO_ARGS
208 gettimeofday(&now, &tzp);
213 if (prev.tv_sec == 0)
217 /* If we've probed the wall-clock time, regenerate the string.
219 if (now.tv_sec != prev.tv_sec)
221 double uprev = prev.tv_sec + ((double) prev.tv_usec * 0.000001);
222 double unow = now.tv_sec + ((double) now.tv_usec * 0.000001);
223 double fps = frame_count / (unow - uprev);
230 sprintf (fps_string, "FPS: %.02f", fps);
235 sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
236 while(*buf && buf[strlen(buf)-1] == '0')
237 buf[strlen(buf)-1] = 0;
238 if (buf[strlen(buf)-1] == '.')
239 buf[strlen(buf)-1] = 0;
240 sprintf(fps_string + strlen(fps_string),
241 " (including %s sec/frame delay)",
245 if (mi->polygon_count > 0)
247 unsigned long p = mi->polygon_count;
250 if (p >= (1024 * 1024)) p >>= 20, s = "M";
251 else if (p >= 2048) p >>= 10, s = "K";
254 strcat (fps_string, "\nPolys: ");
256 sprintf (fps_string + strlen(fps_string), "%lu,%03lu,%03lu%s",
257 (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
259 sprintf (fps_string + strlen(fps_string), "%lu,%03lu%s",
260 (p / 1000), (p % 1000), s);
262 sprintf (fps_string + strlen(fps_string), "%lu%s", p, s);
272 fps_print_string (mi, fps_text_x, fps_text_y, fps_string);
277 do_fps (ModeInfo *mi)
279 fps_1 (mi); /* Lazily compute current FPS value, about once a second. */
280 fps_2 (mi); /* Print the string every frame (else nothing shows up.) */