http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.06.tar.gz
[xscreensaver] / hacks / glx / fps.c
1 /* fps, Copyright (c) 2001-2007 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 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif /* HAVE_CONFIG_H */
16
17 #include "xlockmoreI.h"
18
19 #ifdef HAVE_COCOA
20 # undef usleep /* conflicts with 10.5 headers... */
21 # include <OpenGL/gl.h>
22 # include <OpenGL/glu.h>
23 # include <AGL/agl.h>
24 #else /* !HAVE_COCOA -- real Xlib */
25 # include <GL/gl.h>
26 # include <GL/glu.h>
27 # include <GL/glx.h>
28 #endif /* !HAVE_COCOA */
29
30 #undef DEBUG  /* Defining this causes check_gl_error() to be called inside
31                  time-critical sections, which could slow things down (since
32                  it might result in a round-trip, and stall of the pipeline.)
33                */
34
35 extern void clear_gl_error (void);
36 extern void check_gl_error (const char *type);
37
38 extern GLfloat fps_1 (ModeInfo *mi);
39 extern void    fps_2 (ModeInfo *mi);
40 extern void    do_fps (ModeInfo *mi);
41 extern void    fps_free (ModeInfo *mi);
42
43 struct fps_state {
44   int x, y;
45   int ascent, descent;
46   GLuint font_dlist;
47   Bool clear_p;
48   char string[1024];
49
50   int last_ifps;
51   GLfloat last_fps;
52   int frame_count;
53   struct timeval prev, now;
54 };
55
56
57 static void
58 fps_init (ModeInfo *mi)
59 {
60   struct fps_state *st = mi->fps_state;
61
62   if (st) free (st);
63   st = (struct fps_state *) calloc (1, sizeof(*st));
64   mi->fps_state = st;
65
66   st->clear_p = get_boolean_resource (mi->dpy, "fpsSolid", "FPSSolid");
67
68 # ifndef HAVE_COCOA /* Xlib version */
69   {
70     const char *font;
71     XFontStruct *f;
72     Font id;
73     int first, last;
74
75     font = get_string_resource (mi->dpy, "fpsFont", "Font");
76
77     if (!font) font = "-*-courier-bold-r-normal-*-180-*";
78     f = XLoadQueryFont (mi->dpy, font);
79     if (!f) f = XLoadQueryFont (mi->dpy, "fixed");
80
81     id = f->fid;
82     first = f->min_char_or_byte2;
83     last = f->max_char_or_byte2;
84   
85     clear_gl_error ();
86     st->font_dlist = glGenLists ((GLuint) last+1);
87     check_gl_error ("glGenLists");
88
89     st->ascent = f->ascent;
90     st->descent = f->descent;
91
92     glXUseXFont (id, first, last-first+1, st->font_dlist + first);
93     check_gl_error ("glXUseXFont");
94   }
95
96 # else  /* HAVE_COCOA */
97
98   {
99     AGLContext ctx = aglGetCurrentContext();
100     GLint id    = 0;   /* 0 = system font; 1 = application font */
101     Style face  = 1;   /* 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc. */
102     GLint size  = 24;
103     GLint first = 32;
104     GLint last  = 255;
105
106     st->ascent  = size * 0.9;
107     st->descent = size - st->ascent;
108
109     clear_gl_error ();
110     st->font_dlist = glGenLists ((GLuint) last+1);
111     check_gl_error ("glGenLists");
112
113     if (! aglUseFont (ctx, id, face, size, 
114                       first, last-first+1, st->font_dlist + first)) {
115       check_gl_error ("aglUseFont");
116       abort();
117     }
118   }
119
120 # endif  /* HAVE_COCOA */
121
122   st->x = 10;
123   st->y = 10;
124   if (get_boolean_resource (mi->dpy, "fpsTop", "FPSTop"))
125     st->y = - (st->ascent + 10);
126 }
127
128 void
129 fps_free (ModeInfo *mi)
130 {
131   if (mi->fps_state)
132     free (mi->fps_state);
133   mi->fps_state = 0;
134 }
135
136 static void
137 fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
138 {
139   struct fps_state *st = mi->fps_state;
140   const char *L2 = strchr (string, '\n');
141
142   if (y < 0)
143     {
144       y = mi->xgwa.height + y;
145       if (L2)
146         y -= (st->ascent + st->descent);
147     }
148
149 # ifdef DEBUG
150   clear_gl_error ();
151 # endif
152
153   /* Sadly, this causes a stall of the graphics pipeline (as would the
154      equivalent calls to glGet*.)  But there's no way around this, short
155      of having each caller set up the specific display matrix we need
156      here, which would kind of defeat the purpose of centralizing this
157      code in one file.
158    */
159   glPushAttrib (GL_TRANSFORM_BIT |  /* for matrix contents */
160                 GL_ENABLE_BIT |     /* for various glDisable calls */
161                 GL_CURRENT_BIT |    /* for glColor3f() */
162                 GL_LIST_BIT);       /* for glListBase() */
163   {
164 # ifdef DEBUG
165     check_gl_error ("glPushAttrib");
166 # endif
167
168     /* disable lighting and texturing when drawing bitmaps!
169        (glPopAttrib() restores these.)
170      */
171     glDisable (GL_TEXTURE_2D);
172     glDisable (GL_LIGHTING);
173     glDisable (GL_BLEND);
174     glDisable (GL_DEPTH_TEST);
175     glDisable (GL_CULL_FACE);
176
177     /* glPopAttrib() does not restore matrix changes, so we must
178        push/pop the matrix stacks to be non-intrusive there.
179      */
180     glMatrixMode(GL_PROJECTION);
181     glPushMatrix();
182     {
183 # ifdef DEBUG
184       check_gl_error ("glPushMatrix");
185 # endif
186       glLoadIdentity();
187
188       /* Each matrix mode has its own stack, so we need to push/pop
189          them separately. */
190       glMatrixMode(GL_MODELVIEW);
191       glPushMatrix();
192       {
193 # ifdef DEBUG
194         check_gl_error ("glPushMatrix");
195 # endif
196         glLoadIdentity();
197
198         gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
199 # ifdef DEBUG
200         check_gl_error ("gluOrtho2D");
201 # endif
202
203         /* clear the background */
204         if (st->clear_p)
205           {
206             int lines = L2 ? 2 : 1;
207             glColor3f (0, 0, 0);
208             glRecti (x / 2, y - st->descent,
209                      mi->xgwa.width - x,
210                      y + lines * (st->ascent + st->descent));
211           }
212
213         /* draw the text */
214         glColor3f (1, 1, 1);
215         glRasterPos2f (x, y);
216         glListBase (st->font_dlist);
217
218         if (L2)
219           {
220             L2++;
221             glCallLists (strlen(L2), GL_UNSIGNED_BYTE, L2);
222             glRasterPos2f (x, y + (st->ascent + st->descent));
223             glCallLists (L2 - string - 1, GL_UNSIGNED_BYTE, string);
224           }
225         else
226           {
227             glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
228           }
229
230 # ifdef DEBUG
231         check_gl_error ("fps_print_string");
232 # endif
233       }
234       glPopMatrix();
235     }
236     glMatrixMode(GL_PROJECTION);
237     glPopMatrix();
238
239   }
240   /* clean up after our state changes */
241   glPopAttrib();
242 # ifdef DEBUG
243   check_gl_error ("glPopAttrib");
244 # endif
245 }
246
247
248 GLfloat
249 fps_1 (ModeInfo *mi)
250 {
251   struct fps_state *st = mi->fps_state;
252   if (!st)
253     {
254       fps_init (mi);
255       st = mi->fps_state;
256       strcpy (st->string, "FPS: (accumulating...)");
257     }
258
259   /* Every N frames (where N is approximately one second's worth of frames)
260      check the wall clock.  We do this because checking the wall clock is
261      a slow operation.
262    */
263   if (st->frame_count++ >= st->last_ifps)
264     {
265 # ifdef GETTIMEOFDAY_TWO_ARGS
266       struct timezone tzp;
267       gettimeofday(&st->now, &tzp);
268 # else
269       gettimeofday(&st->now);
270 # endif
271
272       if (st->prev.tv_sec == 0)
273         st->prev = st->now;
274     }
275
276   /* If we've probed the wall-clock time, regenerate the string.
277    */
278   if (st->now.tv_sec != st->prev.tv_sec)
279     {
280       double uprev = st->prev.tv_sec + ((double) st->prev.tv_usec * 0.000001);
281       double unow  =  st->now.tv_sec + ((double)  st->now.tv_usec * 0.000001);
282       double fps   = st->frame_count / (unow - uprev);
283
284       st->prev = st->now;
285       st->frame_count = 0;
286       st->last_ifps = fps;
287       st->last_fps  = fps;
288
289       sprintf (st->string, "FPS: %.02f", fps);
290
291 #ifndef HAVE_COCOA
292       /* since there's no "-delay 0" in the Cocoa version,
293          don't bother mentioning the inter-frame delay. */
294       if (mi->pause != 0)
295         {
296           char buf[40];
297           sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
298           while(*buf && buf[strlen(buf)-1] == '0')
299             buf[strlen(buf)-1] = 0;
300           if (buf[strlen(buf)-1] == '.')
301             buf[strlen(buf)-1] = 0;
302           sprintf(st->string + strlen(st->string),
303                   " (including %s sec/frame delay)",
304                   buf);
305         }
306 #endif /* HAVE_COCOA */
307
308       if (mi->polygon_count > 0)
309         {
310           unsigned long p = mi->polygon_count;
311           const char *s = "";
312 # if 0
313           if      (p >= (1024 * 1024)) p >>= 20, s = "M";
314           else if (p >= 2048)          p >>= 10, s = "K";
315 # endif
316
317           strcat (st->string, "\nPolys: ");
318           if (p >= 1000000)
319             sprintf (st->string + strlen(st->string), "%lu,%03lu,%03lu%s",
320                      (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
321           else if (p >= 1000)
322             sprintf (st->string + strlen(st->string), "%lu,%03lu%s",
323                      (p / 1000), (p % 1000), s);
324           else
325             sprintf (st->string + strlen(st->string), "%lu%s", p, s);
326         }
327     }
328
329   return st->last_fps;
330 }
331
332 void
333 fps_2 (ModeInfo *mi)
334 {
335   struct fps_state *st = mi->fps_state;
336   fps_print_string (mi, st->x, st->y, st->string);
337 }
338
339
340 void
341 do_fps (ModeInfo *mi)
342 {
343   fps_1 (mi);   /* Lazily compute current FPS value, about once a second. */
344   fps_2 (mi);   /* Print the string every frame (else nothing shows up.) */
345 }