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, I believe.)
106 glDisable(GL_TEXTURE_2D);
107 glDisable(GL_LIGHTING);
109 glDisable(GL_DEPTH_TEST);
111 /* glPopAttrib() does not restore matrix changes, so we must
112 push/pop the matrix stacks to be non-intrusive there.
114 glMatrixMode(GL_PROJECTION);
118 check_gl_error ("glPushMatrix");
122 /* Each matrix mode has its own stack, so we need to push/pop
124 glMatrixMode(GL_MODELVIEW);
128 check_gl_error ("glPushMatrix");
132 gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
134 check_gl_error ("gluOrtho2D");
137 /* clear the background */
140 int lines = L2 ? 2 : 1;
142 glRecti (x / 2, y - fps_descent,
144 y + lines * (fps_ascent + fps_descent));
149 glRasterPos2f (x, y);
150 glListBase (font_dlist);
155 glCallLists (strlen(L2), GL_UNSIGNED_BYTE, L2);
156 glRasterPos2f (x, y + (fps_ascent + fps_descent));
157 glCallLists (L2 - string - 1, GL_UNSIGNED_BYTE, string);
161 glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
165 check_gl_error ("fps_print_string");
170 glMatrixMode(GL_PROJECTION);
174 /* clean up after our state changes */
177 check_gl_error ("glPopAttrib");
185 static Bool initted_p = False;
186 static int last_ifps = -1;
187 static GLfloat last_fps = -1;
188 static int frame_count = 0;
189 static struct timeval prev = { 0, 0 };
190 static struct timeval now = { 0, 0 };
196 strcpy (fps_string, "FPS: (accumulating...)");
199 /* Every N frames (where N is approximately one second's worth of frames)
200 check the wall clock. We do this because checking the wall clock is
203 if (frame_count++ >= last_ifps)
205 # ifdef GETTIMEOFDAY_TWO_ARGS
207 gettimeofday(&now, &tzp);
212 if (prev.tv_sec == 0)
216 /* If we've probed the wall-clock time, regenerate the string.
218 if (now.tv_sec != prev.tv_sec)
220 double uprev = prev.tv_sec + ((double) prev.tv_usec * 0.000001);
221 double unow = now.tv_sec + ((double) now.tv_usec * 0.000001);
222 double fps = frame_count / (unow - uprev);
229 sprintf (fps_string, "FPS: %.02f", fps);
234 sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
235 while(*buf && buf[strlen(buf)-1] == '0')
236 buf[strlen(buf)-1] = 0;
237 if (buf[strlen(buf)-1] == '.')
238 buf[strlen(buf)-1] = 0;
239 sprintf(fps_string + strlen(fps_string),
240 " (including %s sec/frame delay)",
244 if (mi->polygon_count > 0)
246 unsigned long p = mi->polygon_count;
249 if (p >= (1024 * 1024)) p >>= 20, s = "M";
250 else if (p >= 2048) p >>= 10, s = "K";
253 strcat (fps_string, "\nPolys: ");
255 sprintf (fps_string + strlen(fps_string), "%lu,%03lu,%03lu%s",
256 (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
258 sprintf (fps_string + strlen(fps_string), "%lu,%03lu%s",
259 (p / 1000), (p % 1000), s);
261 sprintf (fps_string + strlen(fps_string), "%lu%s", p, s);
271 fps_print_string (mi, fps_text_x, fps_text_y, fps_string);
276 do_fps (ModeInfo *mi)
278 fps_1 (mi); /* Lazily compute current FPS value, about once a second. */
279 fps_2 (mi); /* Print the string every frame (else nothing shows up.) */