]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_screen.c
Major update, been neglecting CVS for some time...
[xonotic/darkplaces.git] / gl_screen.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 // screen.c -- master for refresh, status bar, console, chat, notify, etc
22
23 #include "quakedef.h"
24
25 /*
26
27 background clear
28 rendering
29 turtle/net/ram icons
30 sbar
31 centerprint / slow centerprint
32 notify lines
33 intermission / finale overlay
34 loading plaque
35 console
36 menu
37
38 required background clears
39 required update regions
40
41
42 syncronous draw mode or async
43 One off screen buffer, with updates either copied or xblited
44 Need to double buffer?
45
46
47 async draw will require the refresh area to be cleared, because it will be
48 xblited, but sync draw can just ignore it.
49
50 sync
51 draw
52
53 CenterPrint ()
54 SlowPrint ()
55 Screen_Update ();
56 Con_Printf ();
57
58 net 
59 turn off messages option
60
61 the refresh is always rendered, unless the console is full screen
62
63
64 console is:
65         notify lines
66         half
67         full
68         
69
70 */
71
72
73 int             glx, gly, glwidth, glheight;
74
75 float   scr_con_current;
76 float   scr_conlines;           // lines of console to display
77
78 float   oldscreensize, oldfov;
79 cvar_t  scr_viewsize = {"viewsize","100", true};
80 cvar_t  scr_fov = {"fov","90"}; // 10 - 170
81 cvar_t  scr_conspeed = {"scr_conspeed","300"};
82 cvar_t  scr_centertime = {"scr_centertime","2"};
83 cvar_t  scr_showram = {"showram","1"};
84 cvar_t  scr_showturtle = {"showturtle","0"};
85 cvar_t  scr_showpause = {"showpause","1"};
86 cvar_t  scr_printspeed = {"scr_printspeed","8"};
87 cvar_t  showfps = {"showfps", "0", true};
88 cvar_t  r_render = {"r_render", "1"};
89 cvar_t  r_brightness = {"r_brightness", "1", true}; // LordHavoc: a method of operating system independent color correction
90 cvar_t  r_contrast = {"r_contrast", "1", true}; // LordHavoc: a method of operating system independent color correction
91
92 qboolean        scr_initialized;                // ready to draw
93
94 qpic_t          *scr_ram;
95 qpic_t          *scr_net;
96 qpic_t          *scr_turtle;
97
98 int                     clearconsole;
99 int                     clearnotify;
100
101 qboolean        scr_disabled_for_loading;
102 //qboolean      scr_drawloading;
103 //float         scr_disabled_time;
104
105 void SCR_ScreenShot_f (void);
106
107 /*
108 ===============================================================================
109
110 CENTER PRINTING
111
112 ===============================================================================
113 */
114
115 char            scr_centerstring[1024];
116 float           scr_centertime_start;   // for slow victory printing
117 float           scr_centertime_off;
118 int                     scr_center_lines;
119 int                     scr_erase_lines;
120 int                     scr_erase_center;
121
122 /*
123 ==============
124 SCR_CenterPrint
125
126 Called for important messages that should stay in the center of the screen
127 for a few moments
128 ==============
129 */
130 void SCR_CenterPrint (char *str)
131 {
132         strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
133         scr_centertime_off = scr_centertime.value;
134         scr_centertime_start = cl.time;
135
136 // count the number of lines for centering
137         scr_center_lines = 1;
138         while (*str)
139         {
140                 if (*str == '\n')
141                         scr_center_lines++;
142                 str++;
143         }
144 }
145
146
147 void SCR_DrawCenterString (void)
148 {
149         char    *start;
150         int             l;
151         int             x, y;
152         int             remaining;
153
154 // the finale prints the characters one at a time
155         if (cl.intermission)
156                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
157         else
158                 remaining = 9999;
159
160         scr_erase_center = 0;
161         start = scr_centerstring;
162
163         if (scr_center_lines <= 4)
164                 y = vid.height*0.35;
165         else
166                 y = 48;
167
168         do      
169         {
170         // scan the width of the line
171                 for (l=0 ; l<40 ; l++)
172                         if (start[l] == '\n' || !start[l])
173                                 break;
174                 x = (vid.width - l*8)/2;
175                 // LordHavoc: speedup
176                 if (l > 0)
177                 {
178                         if (remaining < l)
179                                 l = remaining;
180                         Draw_String(x, y, start, l);
181                         remaining -= l;
182                         if (remaining <= 0)
183                                 return;
184                 }
185                 /*
186                 for (j=0 ; j<l ; j++, x+=8)
187                 {
188                         Draw_Character (x, y, start[j]);        
189                         if (!remaining--)
190                                 return;
191                 }
192                 */
193                         
194                 y += 8;
195
196                 while (*start && *start != '\n')
197                         start++;
198
199                 if (!*start)
200                         break;
201                 start++;                // skip the \n
202         } while (1);
203 }
204
205 void SCR_CheckDrawCenterString (void)
206 {
207         if (scr_center_lines > scr_erase_lines)
208                 scr_erase_lines = scr_center_lines;
209
210         scr_centertime_off -= host_frametime;
211         
212         if (scr_centertime_off <= 0 && !cl.intermission)
213                 return;
214         if (key_dest != key_game)
215                 return;
216
217         SCR_DrawCenterString ();
218 }
219
220 //=============================================================================
221
222 /*
223 ====================
224 CalcFov
225 ====================
226 */
227 float CalcFov (float fov_x, float width, float height)
228 {
229         float   a;
230         float   x;
231
232         if (fov_x < 1 || fov_x > 179)
233                 Sys_Error ("Bad fov: %f", fov_x);
234
235         x = width/tan(fov_x/360*M_PI);
236
237         a = atan (height/x);
238
239         a = a*360/M_PI;
240
241         return a;
242 }
243
244 /*
245 =================
246 SCR_CalcRefdef
247
248 Must be called whenever vid changes
249 Internal use only
250 =================
251 */
252 static void SCR_CalcRefdef (void)
253 {
254         float           size;
255         int             h;
256         qboolean                full = false;
257
258
259         vid.recalc_refdef = 0;
260
261 //========================================
262         
263 // bound viewsize
264         if (scr_viewsize.value < 30)
265                 Cvar_Set ("viewsize","30");
266         if (scr_viewsize.value > 120)
267                 Cvar_Set ("viewsize","120");
268
269 // bound field of view
270         if (scr_fov.value < 10)
271                 Cvar_Set ("fov","10");
272         if (scr_fov.value > 170)
273                 Cvar_Set ("fov","170");
274
275 // intermission is always full screen   
276         if (cl.intermission)
277         {
278                 full = true;
279                 size = 1;
280                 sb_lines = 0;
281         }
282         else
283         {
284                 if (scr_viewsize.value >= 120)
285                         sb_lines = 0;           // no status bar at all
286                 else if (scr_viewsize.value >= 110)
287                         sb_lines = 24;          // no inventory
288                 else
289                         sb_lines = 24+16+8;
290
291                 if (scr_viewsize.value >= 100.0)
292                 {
293                         full = true;
294                         size = 1.0f;
295                 }
296                 else
297                         size = scr_viewsize.value * (1.0f / 100.0f);
298         }
299
300         // LordHavoc: always fullscreen rendering
301         h = vid.height/* - sb_lines*/;
302
303         r_refdef.vrect.width = vid.width * size;
304         if (r_refdef.vrect.width < 96)
305         {
306                 size = 96.0 / r_refdef.vrect.width;
307                 r_refdef.vrect.width = 96;      // min for icons
308         }
309
310         r_refdef.vrect.height = vid.height * size;
311         //if (r_refdef.vrect.height > vid.height - sb_lines)
312         //      r_refdef.vrect.height = vid.height - sb_lines;
313         if (r_refdef.vrect.height > (int) vid.height)
314                         r_refdef.vrect.height = vid.height;
315         r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2;
316         if (full)
317                 r_refdef.vrect.y = 0;
318         else 
319                 r_refdef.vrect.y = (h - r_refdef.vrect.height)/2;
320
321         r_refdef.fov_x = scr_fov.value;
322         r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
323 }
324
325
326 /*
327 =================
328 SCR_SizeUp_f
329
330 Keybinding command
331 =================
332 */
333 void SCR_SizeUp_f (void)
334 {
335         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
336         vid.recalc_refdef = 1;
337 }
338
339
340 /*
341 =================
342 SCR_SizeDown_f
343
344 Keybinding command
345 =================
346 */
347 void SCR_SizeDown_f (void)
348 {
349         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
350         vid.recalc_refdef = 1;
351 }
352
353 //============================================================================
354
355 void gl_screen_start(void)
356 {
357         scr_ram = Draw_PicFromWad ("ram");
358         scr_net = Draw_PicFromWad ("net");
359         scr_turtle = Draw_PicFromWad ("turtle");
360 }
361
362 void gl_screen_shutdown(void)
363 {
364 }
365
366 void gl_screen_newmap(void)
367 {
368 }
369
370 /*
371 ==================
372 SCR_Init
373 ==================
374 */
375 void GL_Screen_Init (void)
376 {
377
378         Cvar_RegisterVariable (&scr_fov);
379         Cvar_RegisterVariable (&scr_viewsize);
380         Cvar_RegisterVariable (&scr_conspeed);
381         Cvar_RegisterVariable (&scr_showram);
382         Cvar_RegisterVariable (&scr_showturtle);
383         Cvar_RegisterVariable (&scr_showpause);
384         Cvar_RegisterVariable (&scr_centertime);
385         Cvar_RegisterVariable (&scr_printspeed);
386         Cvar_RegisterVariable (&showfps);
387         Cvar_RegisterVariable (&r_render);
388         Cvar_RegisterVariable (&r_brightness);
389         Cvar_RegisterVariable (&r_contrast);
390 #ifdef NORENDER
391         r_render.value = 0;
392 #endif
393
394 //
395 // register our commands
396 //
397         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
398         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
399         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
400
401         scr_initialized = true;
402
403         R_RegisterModule("GL_Screen", gl_screen_start, gl_screen_shutdown, gl_screen_newmap);
404 }
405
406
407
408 /*
409 ==============
410 SCR_DrawRam
411 ==============
412 */
413 void SCR_DrawRam (void)
414 {
415         if (!scr_showram.value)
416                 return;
417
418         if (!r_cache_thrash)
419                 return;
420
421         Draw_Pic (32, 0, scr_ram);
422 }
423
424 /*
425 ==============
426 SCR_DrawTurtle
427 ==============
428 */
429 void SCR_DrawTurtle (void)
430 {
431         static int      count;
432         
433         if (!scr_showturtle.value)
434                 return;
435
436         if (cl.frametime < 0.1)
437         {
438                 count = 0;
439                 return;
440         }
441
442         count++;
443         if (count < 3)
444                 return;
445
446         Draw_Pic (0, 0, scr_turtle);
447 }
448
449 /*
450 ==============
451 SCR_DrawNet
452 ==============
453 */
454 void SCR_DrawNet (void)
455 {
456         if (realtime - cl.last_received_message < 0.3)
457                 return;
458         if (cls.demoplayback)
459                 return;
460
461         Draw_Pic (64, 0, scr_net);
462 }
463
464 /*
465 ==============
466 DrawPause
467 ==============
468 */
469 void SCR_DrawPause (void)
470 {
471         qpic_t  *pic;
472
473         if (!scr_showpause.value)               // turn off for screenshots
474                 return;
475
476         if (!cl.paused)
477                 return;
478
479         pic = Draw_CachePic ("gfx/pause.lmp");
480         Draw_Pic ( (vid.width - pic->width)/2, 
481                 (vid.height - 48 - pic->height)/2, pic);
482 }
483
484
485
486 /*
487 ==============
488 SCR_DrawLoading
489 ==============
490 */
491 /*
492 void SCR_DrawLoading (void)
493 {
494         qpic_t  *pic;
495
496         if (!scr_drawloading)
497                 return;
498                 
499         pic = Draw_CachePic ("gfx/loading.lmp");
500         Draw_Pic ( (vid.width - pic->width)/2, 
501                 (vid.height - 48 - pic->height)/2, pic);
502 }
503 */
504
505
506
507 //=============================================================================
508
509
510 /*
511 ==================
512 SCR_SetUpToDrawConsole
513 ==================
514 */
515 void SCR_SetUpToDrawConsole (void)
516 {
517         Con_CheckResize ();
518         
519         //if (scr_drawloading)
520         //      return;         // never a console with loading plaque
521
522 // decide on the height of the console
523         con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
524
525         if (con_forcedup)
526         {
527                 scr_conlines = vid.height;              // full screen
528                 scr_con_current = scr_conlines;
529         }
530         else if (key_dest == key_console)
531                 scr_conlines = vid.height/2;    // half screen
532         else
533                 scr_conlines = 0;                               // none visible
534         
535         if (scr_conlines < scr_con_current)
536         {
537                 scr_con_current -= scr_conspeed.value*host_realframetime;
538                 if (scr_conlines > scr_con_current)
539                         scr_con_current = scr_conlines;
540
541         }
542         else if (scr_conlines > scr_con_current)
543         {
544                 scr_con_current += scr_conspeed.value*host_realframetime;
545                 if (scr_conlines < scr_con_current)
546                         scr_con_current = scr_conlines;
547         }
548 }
549         
550 /*
551 ==================
552 SCR_DrawConsole
553 ==================
554 */
555 void SCR_DrawConsole (void)
556 {
557         if (scr_con_current)
558         {
559                 Con_DrawConsole (scr_con_current, true);
560                 clearconsole = 0;
561         }
562         else
563         {
564                 if (key_dest == key_game || key_dest == key_message)
565                         Con_DrawNotify ();      // only draw notify in game
566         }
567 }
568
569
570 /* 
571 ============================================================================== 
572  
573                                                 SCREEN SHOTS 
574  
575 ============================================================================== 
576 */ 
577
578 /*
579 ================== 
580 SCR_ScreenShot_f
581 ================== 
582 */
583 void SCR_ScreenShot_f (void) 
584 {
585         byte            *buffer;
586         char            filename[80]; 
587         char            checkname[MAX_OSPATH];
588         int                     i;
589 // 
590 // find a file name to save it to 
591 // 
592         strcpy(filename,"dp0000.tga");
593                 
594         for (i=0 ; i<=9999 ; i++) 
595         { 
596                 filename[2] = (i/1000)%10 + '0'; 
597                 filename[3] = (i/ 100)%10 + '0'; 
598                 filename[4] = (i/  10)%10 + '0'; 
599                 filename[5] = (i/   1)%10 + '0';
600                 sprintf (checkname, "%s/%s", com_gamedir, filename);
601                 if (Sys_FileTime(checkname) == -1)
602                         break;  // file doesn't exist
603         } 
604         if (i==10000)
605         {
606                 Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n"); 
607                 return;
608         }
609
610         buffer = qmalloc(glwidth*glheight*3);
611         glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer); 
612         Image_WriteTGARGB_preflipped(filename, glwidth, glheight, buffer);
613
614         qfree(buffer);
615         Con_Printf ("Wrote %s\n", filename);
616 }
617
618
619 //=============================================================================
620
621
622 /*
623 ===============
624 SCR_BeginLoadingPlaque
625
626 ================
627 */
628 /*
629 void SCR_BeginLoadingPlaque (void)
630 {
631         S_StopAllSounds (true);
632
633 //      if (cls.state != ca_connected)
634 //              return;
635 //      if (cls.signon != SIGNONS)
636 //              return;
637         
638 // redraw with no console and the loading plaque
639 //      Con_ClearNotify ();
640 //      scr_centertime_off = 0;
641 //      scr_con_current = 0;
642
643         scr_drawloading = true;
644         SCR_UpdateScreen ();
645
646 //      scr_disabled_for_loading = true;
647 //      scr_disabled_time = realtime;
648 }
649 */
650
651 /*
652 ===============
653 SCR_EndLoadingPlaque
654
655 ================
656 */
657 /*
658 void SCR_EndLoadingPlaque (void)
659 {
660 //      scr_disabled_for_loading = false;
661         scr_drawloading = false;
662         Con_ClearNotify ();
663 }
664 */
665
666 //=============================================================================
667
668 char    *scr_notifystring;
669
670 void SCR_DrawNotifyString (void)
671 {
672         char    *start;
673         int             l;
674         int             x, y;
675
676         start = scr_notifystring;
677
678         y = vid.height*0.35;
679
680         do      
681         {
682         // scan the width of the line
683                 for (l=0 ; l<40 ; l++)
684                         if (start[l] == '\n' || !start[l])
685                                 break;
686                 x = (vid.width - l*8)/2;
687                 // LordHavoc: speedup
688 //              for (j=0 ; j<l ; j++, x+=8)
689 //                      Draw_Character (x, y, start[j]);        
690                 Draw_String (x, y, start, l);
691
692                 y += 8;
693
694                 while (*start && *start != '\n')
695                         start++;
696
697                 if (!*start)
698                         break;
699                 start++;                // skip the \n
700         } while (1);
701 }
702
703 //=============================================================================
704
705 void DrawCrosshair(int num);
706 void GL_Set2D (void);
707
708 void GL_BrightenScreen(void)
709 {
710         float f;
711
712         if (r_brightness.value < 0.1f)
713                 Cvar_SetValue("r_brightness", 0.1f);
714         if (r_brightness.value > 5.0f)
715                 Cvar_SetValue("r_brightness", 5.0f);
716
717         if (r_contrast.value < 0.2f)
718                 Cvar_SetValue("r_contrast", 0.2f);
719         if (r_contrast.value > 1.0f)
720                 Cvar_SetValue("r_contrast", 1.0f);
721
722         if (!(lighthalf && !hardwaregammasupported) && r_brightness.value < 1.01f && r_contrast.value > 0.99f)
723                 return;
724
725         if (!r_render.value)
726                 return;
727
728         glDisable(GL_TEXTURE_2D);
729         glEnable(GL_BLEND);
730         f = r_brightness.value;
731         // only apply lighthalf using software color correction if hardware is not available (speed reasons)
732         if (lighthalf && !hardwaregammasupported)
733                 f *= 2;
734         if (f >= 1.01f)
735         {
736                 glBlendFunc (GL_DST_COLOR, GL_ONE);
737                 glBegin (GL_TRIANGLES);
738                 while (f >= 1.01f)
739                 {
740                         if (f >= 2)
741                                 glColor3f (1, 1, 1);
742                         else
743                                 glColor3f (f-1, f-1, f-1);
744                         glVertex2f (-5000, -5000);
745                         glVertex2f (10000, -5000);
746                         glVertex2f (-5000, 10000);
747                         f *= 0.5;
748                 }
749                 glEnd ();
750         }
751         if (r_contrast.value <= 0.99f)
752         {
753                 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
754                 if (lighthalf && hardwaregammasupported)
755                         glColor4f (0.5, 0.5, 0.5, 1 - r_contrast.value);
756                 else
757                         glColor4f (1, 1, 1, 1 - r_contrast.value);
758                 glBegin (GL_TRIANGLES);
759                 glVertex2f (-5000, -5000);
760                 glVertex2f (10000, -5000);
761                 glVertex2f (-5000, 10000);
762                 glEnd ();
763         }
764         glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
765
766         glEnable (GL_CULL_FACE);
767         glEnable (GL_DEPTH_TEST);
768         glDisable(GL_BLEND);
769         glEnable(GL_TEXTURE_2D);
770 }
771
772 /*
773 ==================
774 SCR_UpdateScreen
775
776 This is called every frame, and can also be called explicitly to flush
777 text to the screen.
778
779 LordHavoc: due to my rewrite of R_WorldNode, it no longer takes 256k of stack space :)
780 ==================
781 */
782 void GL_Finish(void);
783 void R_Clip_DisplayBuffer(void);
784 void SCR_UpdateScreen (void)
785 {
786         double  time1 = 0, time2;
787
788         if (r_speeds.value)
789                 time1 = Sys_DoubleTime ();
790
791         VID_UpdateGamma(false);
792
793         if (scr_disabled_for_loading)
794         {
795                 /*
796                 if (realtime - scr_disabled_time > 60)
797                 {
798                         scr_disabled_for_loading = false;
799                         Con_Printf ("load failed.\n");
800                 }
801                 else
802                 */
803                         return;
804         }
805
806         if (!scr_initialized || !con_initialized)
807                 return;                         // not initialized yet
808
809
810         GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
811         
812         //
813         // determine size of refresh window
814         //
815         if (oldfov != scr_fov.value)
816         {
817                 oldfov = scr_fov.value;
818                 vid.recalc_refdef = true;
819         }
820
821         if (oldscreensize != scr_viewsize.value)
822         {
823                 oldscreensize = scr_viewsize.value;
824                 vid.recalc_refdef = true;
825         }
826
827         if (vid.recalc_refdef)
828                 SCR_CalcRefdef();
829
830         if (r_render.value)
831         {
832                 glClearColor(0,0,0,0);
833                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
834         }
835
836 //
837 // do 3D refresh drawing, and then update the screen
838 //
839         SCR_SetUpToDrawConsole();
840
841         V_RenderView();
842
843         GL_Set2D();
844
845         R_Clip_DisplayBuffer();
846
847         SCR_DrawRam();
848         SCR_DrawNet();
849         SCR_DrawTurtle();
850         SCR_DrawPause();
851         SCR_CheckDrawCenterString();
852         Sbar_Draw();
853         SHOWLMP_drawall();
854
855         if (crosshair.value)
856                 DrawCrosshair(crosshair.value - 1);
857
858         if (cl.intermission == 1)
859                 Sbar_IntermissionOverlay();
860         else if (cl.intermission == 2)
861                 Sbar_FinaleOverlay();
862
863         SCR_DrawConsole();      
864         M_Draw();
865
866 //      if (scr_drawloading)
867 //              SCR_DrawLoading();
868
869         if (showfps.value)
870         {
871                 static double currtime;
872                 double newtime;
873                 char temp[32];
874                 int calc;
875                 newtime = Sys_DoubleTime();
876                 calc = (int) ((1.0 / (newtime - currtime)) + 0.5);
877                 sprintf(temp, "%4i fps", calc);
878                 currtime = newtime;
879                 Draw_String(vid.width - (8*8), vid.height - sb_lines - 8, temp, 9999);
880         }
881
882         // LordHavoc: only print info if renderer is being used
883         if (r_speeds2.value && cl.worldmodel != NULL)
884         {
885                 int i, j, lines, y;
886                 lines = 1;
887                 for (i = 0;r_speeds2_string[i];i++)
888                         if (r_speeds2_string[i] == '\n')
889                                 lines++;
890                 y = vid.height - sb_lines - lines * 8 - 8;
891                 i = j = 0;
892                 while (r_speeds2_string[i])
893                 {
894                         j = i;
895                         while (r_speeds2_string[i] && r_speeds2_string[i] != '\n')
896                                 i++;
897                         if (i - j > 0)
898                                 Draw_String(0, y, r_speeds2_string + j, i - j);
899                         if (r_speeds2_string[i] == '\n')
900                                 i++;
901                         y += 8;
902                 }
903         }
904
905         V_UpdateBlends();
906
907         GL_BrightenScreen();
908
909         GL_Finish();
910
911         if (r_speeds.value)
912         {
913                 time2 = Sys_DoubleTime ();
914                 Con_Printf ("%3i ms  %4i wpoly %4i epoly %4i transpoly %4i lightpoly %4i BSPnodes %4i BSPleafs %4i BSPfaces %4i models %4i bmodels %4i sprites %4i particles %3i dlights\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys, currenttranspoly, c_light_polys, c_nodes, c_leafs, c_faces, c_models, c_bmodels, c_sprites, c_particles, c_dlights);
915         }
916         GL_EndRendering ();
917 }
918
919 // for profiling, this is separated
920 void GL_Finish(void)
921 {
922         if (!r_render.value)
923                 return;
924         glFinish ();
925 }
926