16ee1b0827fb7b852eaf8ab6a60c1f0ef1a7cba4
[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 int fps_sample_frames = 10;
36 static GLuint font_dlist;
37 static Bool fps_clear_p = False;
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   /* save the current state */
76   /* note: could be expensive! */
77
78   if (y < 0)
79     y = mi->xgwa.height + y;
80
81   clear_gl_error ();
82
83   /* Sadly, this causes a stall of the graphics pipeline (as would the
84      equivalent calls to glGet*.)  But there's no way around this, short
85      of having each caller set up the specific display matrix we need
86      here, which would kind of defeat the purpose of centralizing this
87      code in one file.
88    */
89   glPushAttrib(GL_TRANSFORM_BIT |  /* for matrix contents */
90                GL_ENABLE_BIT |     /* for various glDisable calls */
91                GL_CURRENT_BIT |    /* for glColor3f() */
92                GL_LIST_BIT);       /* for glListBase() */
93   {
94 # ifdef DEBUG
95     check_gl_error ("glPushAttrib");
96 # endif
97
98     /* disable lighting and texturing when drawing bitmaps!
99        (glPopAttrib() restores these, I believe.)
100      */
101     glDisable(GL_TEXTURE_2D);
102     glDisable(GL_LIGHTING);
103     glDisable(GL_BLEND);
104     glDisable(GL_DEPTH_TEST);
105
106     /* glPopAttrib() does not restore matrix changes, so we must
107        push/pop the matrix stacks to be non-intrusive there.
108      */
109     glMatrixMode(GL_PROJECTION);
110     glPushMatrix();
111     {
112 # ifdef DEBUG
113       check_gl_error ("glPushMatrix");
114 # endif
115       glLoadIdentity();
116
117       /* Each matrix mode has its own stack, so we need to push/pop
118          them separately. */
119       glMatrixMode(GL_MODELVIEW);
120       glPushMatrix();
121       {
122 # ifdef DEBUG
123         check_gl_error ("glPushMatrix");
124 # endif
125         glLoadIdentity();
126
127         gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
128 # ifdef DEBUG
129         check_gl_error ("gluOrtho2D");
130 # endif
131
132         /* clear the background */
133         if (fps_clear_p)
134           {
135             glColor3f (0, 0, 0);
136             glRecti (x, y - fps_descent,
137                      mi->xgwa.width - x,
138                      y + fps_ascent + fps_descent);
139           }
140
141         /* draw the text */
142         glColor3f (1, 1, 1);
143         glRasterPos2f (x, y);
144         glListBase (font_dlist);
145         glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
146
147 # ifdef DEBUG
148         check_gl_error ("fps_print_string");
149 # endif
150       }
151       glPopMatrix();
152     }
153     glMatrixMode(GL_PROJECTION);
154     glPopMatrix();
155
156   }
157   /* clean up after our state changes */
158   glPopAttrib();
159 # ifdef DEBUG
160   check_gl_error ("glPopAttrib");
161 # endif
162 }
163
164
165 void
166 do_fps (ModeInfo *mi)
167 {
168   /* every N frames, get the time and use it to get the frames per second */
169   static int frame_counter = -1;
170   static double oldtime = 0; /* time in usecs, as a double */
171   static double newtime = 0;
172
173   static char msg [1024] = { 0, };
174
175   if (frame_counter == -1)
176     {
177       fps_init (mi);
178       frame_counter = fps_sample_frames;
179     }
180
181   if (frame_counter++ == fps_sample_frames)
182     {
183       double fps;
184       struct timeval now;
185 # ifdef GETTIMEOFDAY_TWO_ARGS
186       struct timezone tzp;
187       gettimeofday(&now, &tzp);
188 # else
189       gettimeofday(&now);
190 # endif
191
192       oldtime = newtime;
193       newtime = now.tv_sec + ((double) now.tv_usec * 0.000001);
194
195       fps = fps_sample_frames / (newtime - oldtime);
196
197       if (fps < 0.0001)
198         {
199           strcpy(msg, "FPS: (accumulating...)");
200         }
201       else
202         {
203           sprintf(msg, "FPS: %.02f", fps);
204
205           if (mi->pause != 0)
206             {
207               char buf[40];
208               sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
209               while(*buf && buf[strlen(buf)-1] == '0')
210                 buf[strlen(buf)-1] = 0;
211               if (buf[strlen(buf)-1] == '.')
212                 buf[strlen(buf)-1] = 0;
213               sprintf(msg + strlen(msg), " (including %s sec/frame delay)",
214                       buf);
215             }
216         }
217
218       frame_counter = 0;
219     }
220
221   fps_print_string (mi, fps_text_x, fps_text_y, msg);
222 }