http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[xscreensaver] / hacks / glx / fps.c
1 /* tube, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
2  * Utility function to draw a frames-per-second display.
3  *
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 
10  * implied warranty.
11  */
12
13 #include "config.h"
14 #include <stdlib.h>
15 #include <stdio.h>
16
17 #include "screenhack.h"
18 #include "xlockmoreI.h"
19
20 #include <GL/gl.h>
21 #include <GL/glu.h>
22 #include <GL/glx.h>
23
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.)
27                */
28
29 extern void clear_gl_error (void);
30 extern void check_gl_error (const char *type);
31
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];
38
39 static void
40 fps_init (ModeInfo *mi)
41 {
42   const char *font = get_string_resource ("fpsFont", "Font");
43   XFontStruct *f;
44   Font id;
45   int first, last;
46
47   fps_clear_p = get_boolean_resource ("fpsSolid", "FPSSolid");
48
49   if (!font) font = "-*-courier-bold-r-normal-*-180-*";
50   f = XLoadQueryFont(mi->dpy, font);
51   if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
52
53   id = f->fid;
54   first = f->min_char_or_byte2;
55   last = f->max_char_or_byte2;
56   
57   clear_gl_error ();
58   font_dlist = glGenLists ((GLuint) last+1);
59   check_gl_error ("glGenLists");
60
61   fps_ascent = f->ascent;
62   fps_descent = f->descent;
63
64   if (get_boolean_resource ("fpsTop", "FPSTop"))
65     fps_text_y = - (f->ascent + 10);
66
67   glXUseXFont(id, first, last-first+1, font_dlist + first);
68   check_gl_error ("glXUseXFont");
69 }
70
71
72 static void
73 fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
74 {
75   const char *L2 = strchr (string, '\n');
76
77   if (y < 0)
78     {
79       y = mi->xgwa.height + y;
80       if (L2)
81         y -= (fps_ascent + fps_descent);
82     }
83
84 # ifdef DEBUG
85   clear_gl_error ();
86 # endif
87
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
92      code in one file.
93    */
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() */
98   {
99 # ifdef DEBUG
100     check_gl_error ("glPushAttrib");
101 # endif
102
103     /* disable lighting and texturing when drawing bitmaps!
104        (glPopAttrib() restores these.)
105      */
106     glDisable (GL_TEXTURE_2D);
107     glDisable (GL_LIGHTING);
108     glDisable (GL_BLEND);
109     glDisable (GL_DEPTH_TEST);
110     glDisable (GL_CULL_FACE);
111
112     /* glPopAttrib() does not restore matrix changes, so we must
113        push/pop the matrix stacks to be non-intrusive there.
114      */
115     glMatrixMode(GL_PROJECTION);
116     glPushMatrix();
117     {
118 # ifdef DEBUG
119       check_gl_error ("glPushMatrix");
120 # endif
121       glLoadIdentity();
122
123       /* Each matrix mode has its own stack, so we need to push/pop
124          them separately. */
125       glMatrixMode(GL_MODELVIEW);
126       glPushMatrix();
127       {
128 # ifdef DEBUG
129         check_gl_error ("glPushMatrix");
130 # endif
131         glLoadIdentity();
132
133         gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
134 # ifdef DEBUG
135         check_gl_error ("gluOrtho2D");
136 # endif
137
138         /* clear the background */
139         if (fps_clear_p)
140           {
141             int lines = L2 ? 2 : 1;
142             glColor3f (0, 0, 0);
143             glRecti (x / 2, y - fps_descent,
144                      mi->xgwa.width - x,
145                      y + lines * (fps_ascent + fps_descent));
146           }
147
148         /* draw the text */
149         glColor3f (1, 1, 1);
150         glRasterPos2f (x, y);
151         glListBase (font_dlist);
152
153         if (L2)
154           {
155             L2++;
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);
159           }
160         else
161           {
162             glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
163           }
164
165 # ifdef DEBUG
166         check_gl_error ("fps_print_string");
167 # endif
168       }
169       glPopMatrix();
170     }
171     glMatrixMode(GL_PROJECTION);
172     glPopMatrix();
173
174   }
175   /* clean up after our state changes */
176   glPopAttrib();
177 # ifdef DEBUG
178   check_gl_error ("glPopAttrib");
179 # endif
180 }
181
182
183 GLfloat
184 fps_1 (ModeInfo *mi)
185 {
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 };
192
193   if (!initted_p)
194     {
195       initted_p = True;
196       fps_init (mi);
197       strcpy (fps_string, "FPS: (accumulating...)");
198     }
199
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
202      a slow operation.
203    */
204   if (frame_count++ >= last_ifps)
205     {
206 # ifdef GETTIMEOFDAY_TWO_ARGS
207       struct timezone tzp;
208       gettimeofday(&now, &tzp);
209 # else
210       gettimeofday(&now);
211 # endif
212
213       if (prev.tv_sec == 0)
214         prev = now;
215     }
216
217   /* If we've probed the wall-clock time, regenerate the string.
218    */
219   if (now.tv_sec != prev.tv_sec)
220     {
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);
224
225       prev = now;
226       frame_count = 0;
227       last_ifps = fps;
228       last_fps  = fps;
229
230       sprintf (fps_string, "FPS: %.02f", fps);
231
232       if (mi->pause != 0)
233         {
234           char buf[40];
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)",
242                   buf);
243         }
244
245       if (mi->polygon_count > 0)
246         {
247           unsigned long p = mi->polygon_count;
248           const char *s = "";
249 # if 0
250           if      (p >= (1024 * 1024)) p >>= 20, s = "M";
251           else if (p >= 2048)          p >>= 10, s = "K";
252 # endif
253
254           strcat (fps_string, "\nPolys: ");
255           if (p >= 1000000)
256             sprintf (fps_string + strlen(fps_string), "%lu,%03lu,%03lu%s",
257                      (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
258           else if (p >= 1000)
259             sprintf (fps_string + strlen(fps_string), "%lu,%03lu%s",
260                      (p / 1000), (p % 1000), s);
261           else
262             sprintf (fps_string + strlen(fps_string), "%lu%s", p, s);
263         }
264     }
265
266   return last_fps;
267 }
268
269 void
270 fps_2 (ModeInfo *mi)
271 {
272   fps_print_string (mi, fps_text_x, fps_text_y, fps_string);
273 }
274
275
276 void
277 do_fps (ModeInfo *mi)
278 {
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.) */
281 }