http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.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, I believe.)
105      */
106     glDisable(GL_TEXTURE_2D);
107     glDisable(GL_LIGHTING);
108     glDisable(GL_BLEND);
109     glDisable(GL_DEPTH_TEST);
110
111     /* glPopAttrib() does not restore matrix changes, so we must
112        push/pop the matrix stacks to be non-intrusive there.
113      */
114     glMatrixMode(GL_PROJECTION);
115     glPushMatrix();
116     {
117 # ifdef DEBUG
118       check_gl_error ("glPushMatrix");
119 # endif
120       glLoadIdentity();
121
122       /* Each matrix mode has its own stack, so we need to push/pop
123          them separately. */
124       glMatrixMode(GL_MODELVIEW);
125       glPushMatrix();
126       {
127 # ifdef DEBUG
128         check_gl_error ("glPushMatrix");
129 # endif
130         glLoadIdentity();
131
132         gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
133 # ifdef DEBUG
134         check_gl_error ("gluOrtho2D");
135 # endif
136
137         /* clear the background */
138         if (fps_clear_p)
139           {
140             int lines = L2 ? 2 : 1;
141             glColor3f (0, 0, 0);
142             glRecti (x / 2, y - fps_descent,
143                      mi->xgwa.width - x,
144                      y + lines * (fps_ascent + fps_descent));
145           }
146
147         /* draw the text */
148         glColor3f (1, 1, 1);
149         glRasterPos2f (x, y);
150         glListBase (font_dlist);
151
152         if (L2)
153           {
154             L2++;
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);
158           }
159         else
160           {
161             glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
162           }
163
164 # ifdef DEBUG
165         check_gl_error ("fps_print_string");
166 # endif
167       }
168       glPopMatrix();
169     }
170     glMatrixMode(GL_PROJECTION);
171     glPopMatrix();
172
173   }
174   /* clean up after our state changes */
175   glPopAttrib();
176 # ifdef DEBUG
177   check_gl_error ("glPopAttrib");
178 # endif
179 }
180
181
182 GLfloat
183 fps_1 (ModeInfo *mi)
184 {
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 };
191
192   if (!initted_p)
193     {
194       initted_p = True;
195       fps_init (mi);
196       strcpy (fps_string, "FPS: (accumulating...)");
197     }
198
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
201      a slow operation.
202    */
203   if (frame_count++ >= last_ifps)
204     {
205 # ifdef GETTIMEOFDAY_TWO_ARGS
206       struct timezone tzp;
207       gettimeofday(&now, &tzp);
208 # else
209       gettimeofday(&now);
210 # endif
211
212       if (prev.tv_sec == 0)
213         prev = now;
214     }
215
216   /* If we've probed the wall-clock time, regenerate the string.
217    */
218   if (now.tv_sec != prev.tv_sec)
219     {
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);
223
224       prev = now;
225       frame_count = 0;
226       last_ifps = fps;
227       last_fps  = fps;
228
229       sprintf (fps_string, "FPS: %.02f", fps);
230
231       if (mi->pause != 0)
232         {
233           char buf[40];
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)",
241                   buf);
242         }
243
244       if (mi->polygon_count > 0)
245         {
246           unsigned long p = mi->polygon_count;
247           const char *s = "";
248 # if 0
249           if      (p >= (1024 * 1024)) p >>= 20, s = "M";
250           else if (p >= 2048)          p >>= 10, s = "K";
251 # endif
252
253           strcat (fps_string, "\nPolys: ");
254           if (p >= 1000000)
255             sprintf (fps_string + strlen(fps_string), "%lu,%03lu,%03lu%s",
256                      (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
257           else if (p >= 1000)
258             sprintf (fps_string + strlen(fps_string), "%lu,%03lu%s",
259                      (p / 1000), (p % 1000), s);
260           else
261             sprintf (fps_string + strlen(fps_string), "%lu%s", p, s);
262         }
263     }
264
265   return last_fps;
266 }
267
268 void
269 fps_2 (ModeInfo *mi)
270 {
271   fps_print_string (mi, fps_text_x, fps_text_y, fps_string);
272 }
273
274
275 void
276 do_fps (ModeInfo *mi)
277 {
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.) */
280 }