1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 #include "tr_local.h"
23
24 volatile renderCommandList_t *renderCommandList;
25
26 volatile qboolean renderThreadActive;
27
28
29 /*
30 =====================
31 R_PerformanceCounters
32 =====================
33 */
R_PerformanceCounters(void)34 void R_PerformanceCounters( void ) {
35 if ( !r_speeds->integer ) {
36 // clear the counters even if we aren't printing
37 Com_Memset( &tr.pc, 0, sizeof( tr.pc ) );
38 Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) );
39 return;
40 }
41
42 if (r_speeds->integer == 1) {
43 ri.Printf (PRINT_ALL, "%i/%i shaders/surfs %i leafs %i verts %i/%i tris %.2f mtex %.2f dc\n",
44 backEnd.pc.c_shaders, backEnd.pc.c_surfaces, tr.pc.c_leafs, backEnd.pc.c_vertexes,
45 backEnd.pc.c_indexes/3, backEnd.pc.c_totalIndexes/3,
46 R_SumOfUsedImages()/(1000000.0f), backEnd.pc.c_overDraw / (float)(glConfig.vidWidth * glConfig.vidHeight) );
47 } else if (r_speeds->integer == 2) {
48 ri.Printf (PRINT_ALL, "(patch) %i sin %i sclip %i sout %i bin %i bclip %i bout\n",
49 tr.pc.c_sphere_cull_patch_in, tr.pc.c_sphere_cull_patch_clip, tr.pc.c_sphere_cull_patch_out,
50 tr.pc.c_box_cull_patch_in, tr.pc.c_box_cull_patch_clip, tr.pc.c_box_cull_patch_out );
51 ri.Printf (PRINT_ALL, "(md3) %i sin %i sclip %i sout %i bin %i bclip %i bout\n",
52 tr.pc.c_sphere_cull_md3_in, tr.pc.c_sphere_cull_md3_clip, tr.pc.c_sphere_cull_md3_out,
53 tr.pc.c_box_cull_md3_in, tr.pc.c_box_cull_md3_clip, tr.pc.c_box_cull_md3_out );
54 } else if (r_speeds->integer == 3) {
55 ri.Printf (PRINT_ALL, "viewcluster: %i\n", tr.viewCluster );
56 } else if (r_speeds->integer == 4) {
57 if ( backEnd.pc.c_dlightVertexes ) {
58 ri.Printf (PRINT_ALL, "dlight srf:%i culled:%i verts:%i tris:%i\n",
59 tr.pc.c_dlightSurfaces, tr.pc.c_dlightSurfacesCulled,
60 backEnd.pc.c_dlightVertexes, backEnd.pc.c_dlightIndexes / 3 );
61 }
62 }
63 else if (r_speeds->integer == 5 )
64 {
65 ri.Printf( PRINT_ALL, "zFar: %.0f\n", tr.viewParms.zFar );
66 }
67 else if (r_speeds->integer == 6 )
68 {
69 ri.Printf( PRINT_ALL, "flare adds:%i tests:%i renders:%i\n",
70 backEnd.pc.c_flareAdds, backEnd.pc.c_flareTests, backEnd.pc.c_flareRenders );
71 }
72
73 Com_Memset( &tr.pc, 0, sizeof( tr.pc ) );
74 Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) );
75 }
76
77
78 /*
79 ====================
80 R_InitCommandBuffers
81 ====================
82 */
R_InitCommandBuffers(void)83 void R_InitCommandBuffers( void ) {
84 glConfig.smpActive = qfalse;
85 if ( r_smp->integer ) {
86 ri.Printf( PRINT_ALL, "Trying SMP acceleration...\n" );
87 if ( GLimp_SpawnRenderThread( RB_RenderThread ) ) {
88 ri.Printf( PRINT_ALL, "...succeeded.\n" );
89 glConfig.smpActive = qtrue;
90 } else {
91 ri.Printf( PRINT_ALL, "...failed.\n" );
92 }
93 }
94 }
95
96 /*
97 ====================
98 R_ShutdownCommandBuffers
99 ====================
100 */
R_ShutdownCommandBuffers(void)101 void R_ShutdownCommandBuffers( void ) {
102 // kill the rendering thread
103 if ( glConfig.smpActive ) {
104 GLimp_WakeRenderer( NULL );
105 glConfig.smpActive = qfalse;
106 }
107 }
108
109 /*
110 ====================
111 R_IssueRenderCommands
112 ====================
113 */
114 int c_blockedOnRender;
115 int c_blockedOnMain;
116
R_IssueRenderCommands(qboolean runPerformanceCounters)117 void R_IssueRenderCommands( qboolean runPerformanceCounters ) {
118 renderCommandList_t *cmdList;
119
120 cmdList = &backEndData[tr.smpFrame]->commands;
121 assert(cmdList);
122 // add an end-of-list command
123 *(int *)(cmdList->cmds + cmdList->used) = RC_END_OF_LIST;
124
125 // clear it out, in case this is a sync and not a buffer flip
126 cmdList->used = 0;
127
128 if ( glConfig.smpActive ) {
129 // if the render thread is not idle, wait for it
130 if ( renderThreadActive ) {
131 c_blockedOnRender++;
132 if ( r_showSmp->integer ) {
133 ri.Printf( PRINT_ALL, "R" );
134 }
135 } else {
136 c_blockedOnMain++;
137 if ( r_showSmp->integer ) {
138 ri.Printf( PRINT_ALL, "." );
139 }
140 }
141
142 // sleep until the renderer has completed
143 GLimp_FrontEndSleep();
144 }
145
146 // at this point, the back end thread is idle, so it is ok
147 // to look at its performance counters
148 if ( runPerformanceCounters ) {
149 R_PerformanceCounters();
150 }
151
152 // actually start the commands going
153 if ( !r_skipBackEnd->integer ) {
154 // let it start on the new batch
155 if ( !glConfig.smpActive ) {
156 RB_ExecuteRenderCommands( cmdList->cmds );
157 } else {
158 GLimp_WakeRenderer( cmdList );
159 }
160 }
161 }
162
163
164 /*
165 ====================
166 R_SyncRenderThread
167
168 Issue any pending commands and wait for them to complete.
169 After exiting, the render thread will have completed its work
170 and will remain idle and the main thread is free to issue
171 OpenGL calls until R_IssueRenderCommands is called.
172 ====================
173 */
R_SyncRenderThread(void)174 void R_SyncRenderThread( void ) {
175 if ( !tr.registered ) {
176 return;
177 }
178 R_IssueRenderCommands( qfalse );
179
180 if ( !glConfig.smpActive ) {
181 return;
182 }
183 GLimp_FrontEndSleep();
184 }
185
186 /*
187 ============
188 R_GetCommandBuffer
189
190 make sure there is enough command space, waiting on the
191 render thread if needed.
192 ============
193 */
R_GetCommandBuffer(int bytes)194 void *R_GetCommandBuffer( int bytes ) {
195 renderCommandList_t *cmdList;
196
197 cmdList = &backEndData[tr.smpFrame]->commands;
198
199 // always leave room for the end of list command
200 if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) {
201 if ( bytes > MAX_RENDER_COMMANDS - 4 ) {
202 ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes );
203 }
204 // if we run out of room, just start dropping commands
205 return NULL;
206 }
207
208 cmdList->used += bytes;
209
210 return cmdList->cmds + cmdList->used - bytes;
211 }
212
213
214 /*
215 =============
216 R_AddDrawSurfCmd
217
218 =============
219 */
R_AddDrawSurfCmd(drawSurf_t * drawSurfs,int numDrawSurfs)220 void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ) {
221 drawSurfsCommand_t *cmd;
222
223 cmd = R_GetCommandBuffer( sizeof( *cmd ) );
224 if ( !cmd ) {
225 return;
226 }
227 cmd->commandId = RC_DRAW_SURFS;
228
229 cmd->drawSurfs = drawSurfs;
230 cmd->numDrawSurfs = numDrawSurfs;
231
232 cmd->refdef = tr.refdef;
233 cmd->viewParms = tr.viewParms;
234 }
235
236
237 /*
238 =============
239 RE_SetColor
240
241 Passing NULL will set the color to white
242 =============
243 */
RE_SetColor(const float * rgba)244 void RE_SetColor( const float *rgba ) {
245 setColorCommand_t *cmd;
246
247 if ( !tr.registered ) {
248 return;
249 }
250 cmd = R_GetCommandBuffer( sizeof( *cmd ) );
251 if ( !cmd ) {
252 return;
253 }
254 cmd->commandId = RC_SET_COLOR;
255 if ( !rgba ) {
256 static float colorWhite[4] = { 1, 1, 1, 1 };
257
258 rgba = colorWhite;
259 }
260
261 cmd->color[0] = rgba[0];
262 cmd->color[1] = rgba[1];
263 cmd->color[2] = rgba[2];
264 cmd->color[3] = rgba[3];
265 }
266
267
268 /*
269 =============
270 RE_StretchPic
271 =============
272 */
RE_StretchPic(float x,float y,float w,float h,float s1,float t1,float s2,float t2,qhandle_t hShader)273 void RE_StretchPic ( float x, float y, float w, float h,
274 float s1, float t1, float s2, float t2, qhandle_t hShader ) {
275 stretchPicCommand_t *cmd;
276
277 if (!tr.registered) {
278 return;
279 }
280 cmd = R_GetCommandBuffer( sizeof( *cmd ) );
281 if ( !cmd ) {
282 return;
283 }
284 cmd->commandId = RC_STRETCH_PIC;
285 cmd->shader = R_GetShaderByHandle( hShader );
286 cmd->x = x;
287 cmd->y = y;
288 cmd->w = w;
289 cmd->h = h;
290 cmd->s1 = s1;
291 cmd->t1 = t1;
292 cmd->s2 = s2;
293 cmd->t2 = t2;
294 }
295
296 #define MODE_RED_CYAN 1
297 #define MODE_RED_BLUE 2
298 #define MODE_RED_GREEN 3
299 #define MODE_MAX MODE_RED_GREEN
300
R_SetColorMode(GLboolean * rgba,stereoFrame_t stereoFrame,int colormode)301 void R_SetColorMode(GLboolean *rgba, stereoFrame_t stereoFrame, int colormode)
302 {
303 rgba[0] = rgba[1] = rgba[2] = rgba[3] = GL_TRUE;
304
305 if(colormode > MODE_MAX)
306 {
307 if(stereoFrame == STEREO_LEFT)
308 stereoFrame = STEREO_RIGHT;
309 else if(stereoFrame == STEREO_RIGHT)
310 stereoFrame = STEREO_LEFT;
311
312 colormode -= MODE_MAX;
313 }
314
315 if(stereoFrame == STEREO_LEFT)
316 rgba[1] = rgba[2] = GL_FALSE;
317 else if(stereoFrame == STEREO_RIGHT)
318 {
319 rgba[0] = GL_FALSE;
320
321 if(colormode == MODE_RED_BLUE)
322 rgba[1] = GL_FALSE;
323 else if(colormode == MODE_RED_GREEN)
324 rgba[2] = GL_FALSE;
325 }
326 }
327
328
329 /*
330 ====================
331 RE_BeginFrame
332
333 If running in stereo, RE_BeginFrame will be called twice
334 for each RE_EndFrame
335 ====================
336 */
RE_BeginFrame(stereoFrame_t stereoFrame)337 void RE_BeginFrame( stereoFrame_t stereoFrame ) {
338 drawBufferCommand_t *cmd = NULL;
339 colorMaskCommand_t *colcmd = NULL;
340
341 if ( !tr.registered ) {
342 return;
343 }
344 glState.finishCalled = qfalse;
345
346 tr.frameCount++;
347 tr.frameSceneNum = 0;
348
349 //
350 // do overdraw measurement
351 //
352 if ( r_measureOverdraw->integer )
353 {
354 if ( glConfig.stencilBits < 4 )
355 {
356 ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits );
357 ri.Cvar_Set( "r_measureOverdraw", "0" );
358 r_measureOverdraw->modified = qfalse;
359 }
360 else if ( r_shadows->integer == 2 )
361 {
362 ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" );
363 ri.Cvar_Set( "r_measureOverdraw", "0" );
364 r_measureOverdraw->modified = qfalse;
365 }
366 else
367 {
368 R_SyncRenderThread();
369 qglEnable( GL_STENCIL_TEST );
370 qglStencilMask( ~0U );
371 qglClearStencil( 0U );
372 qglStencilFunc( GL_ALWAYS, 0U, ~0U );
373 qglStencilOp( GL_KEEP, GL_INCR, GL_INCR );
374 }
375 r_measureOverdraw->modified = qfalse;
376 }
377 else
378 {
379 // this is only reached if it was on and is now off
380 if ( r_measureOverdraw->modified ) {
381 R_SyncRenderThread();
382 qglDisable( GL_STENCIL_TEST );
383 }
384 r_measureOverdraw->modified = qfalse;
385 }
386
387 //
388 // texturemode stuff
389 //
390 if ( r_textureMode->modified ) {
391 R_SyncRenderThread();
392 GL_TextureMode( r_textureMode->string );
393 r_textureMode->modified = qfalse;
394 }
395
396 //
397 // gamma stuff
398 //
399 if ( r_gamma->modified ) {
400 r_gamma->modified = qfalse;
401
402 R_SyncRenderThread();
403 R_SetColorMappings();
404 }
405
406 // check for errors
407 if ( !r_ignoreGLErrors->integer )
408 {
409 int err;
410
411 R_SyncRenderThread();
412 if ((err = qglGetError()) != GL_NO_ERROR)
413 ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!\n", err);
414 }
415
416 if (glConfig.stereoEnabled) {
417 if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
418 return;
419
420 cmd->commandId = RC_DRAW_BUFFER;
421
422 if ( stereoFrame == STEREO_LEFT ) {
423 cmd->buffer = (int)GL_BACK_LEFT;
424 } else if ( stereoFrame == STEREO_RIGHT ) {
425 cmd->buffer = (int)GL_BACK_RIGHT;
426 } else {
427 ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame );
428 }
429 }
430 else
431 {
432 if(r_anaglyphMode->integer)
433 {
434 if(r_anaglyphMode->modified)
435 {
436 // clear both, front and backbuffer.
437 qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
438 qglClearColor(0.0f, 0.0f, 0.0f, 1.0f);
439
440 qglDrawBuffer(GL_FRONT);
441 qglClear(GL_COLOR_BUFFER_BIT);
442 qglDrawBuffer(GL_BACK);
443 qglClear(GL_COLOR_BUFFER_BIT);
444
445 r_anaglyphMode->modified = qfalse;
446 }
447
448 if(stereoFrame == STEREO_LEFT)
449 {
450 if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
451 return;
452
453 if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) )
454 return;
455 }
456 else if(stereoFrame == STEREO_RIGHT)
457 {
458 clearDepthCommand_t *cldcmd;
459
460 if( !(cldcmd = R_GetCommandBuffer(sizeof(*cldcmd))) )
461 return;
462
463 cldcmd->commandId = RC_CLEARDEPTH;
464
465 if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) )
466 return;
467 }
468 else
469 ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame );
470
471 R_SetColorMode(colcmd->rgba, stereoFrame, r_anaglyphMode->integer);
472 colcmd->commandId = RC_COLORMASK;
473 }
474 else
475 {
476 if(stereoFrame != STEREO_CENTER)
477 ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame );
478
479 if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
480 return;
481 }
482
483 if(cmd)
484 {
485 cmd->commandId = RC_DRAW_BUFFER;
486
487 if(r_anaglyphMode->modified)
488 {
489 qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
490 r_anaglyphMode->modified = qfalse;
491 }
492
493 if (!Q_stricmp(r_drawBuffer->string, "GL_FRONT"))
494 cmd->buffer = (int)GL_FRONT;
495 else
496 cmd->buffer = (int)GL_BACK;
497 }
498 }
499
500 tr.refdef.stereoFrame = stereoFrame;
501 }
502
503
504 /*
505 =============
506 RE_EndFrame
507
508 Returns the number of msec spent in the back end
509 =============
510 */
RE_EndFrame(int * frontEndMsec,int * backEndMsec)511 void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) {
512 swapBuffersCommand_t *cmd;
513
514 if ( !tr.registered ) {
515 return;
516 }
517 cmd = R_GetCommandBuffer( sizeof( *cmd ) );
518 if ( !cmd ) {
519 return;
520 }
521 cmd->commandId = RC_SWAP_BUFFERS;
522
523 R_IssueRenderCommands( qtrue );
524
525 // use the other buffers next frame, because another CPU
526 // may still be rendering into the current ones
527 R_ToggleSmpFrame();
528
529 if ( frontEndMsec ) {
530 *frontEndMsec = tr.frontEndMsec;
531 }
532 tr.frontEndMsec = 0;
533 if ( backEndMsec ) {
534 *backEndMsec = backEnd.pc.msec;
535 }
536 backEnd.pc.msec = 0;
537 }
538
539 /*
540 =============
541 RE_TakeVideoFrame
542 =============
543 */
RE_TakeVideoFrame(int width,int height,byte * captureBuffer,byte * encodeBuffer,qboolean motionJpeg)544 void RE_TakeVideoFrame( int width, int height,
545 byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg )
546 {
547 videoFrameCommand_t *cmd;
548
549 if( !tr.registered ) {
550 return;
551 }
552
553 cmd = R_GetCommandBuffer( sizeof( *cmd ) );
554 if( !cmd ) {
555 return;
556 }
557
558 cmd->commandId = RC_VIDEOFRAME;
559
560 cmd->width = width;
561 cmd->height = height;
562 cmd->captureBuffer = captureBuffer;
563 cmd->encodeBuffer = encodeBuffer;
564 cmd->motionJpeg = motionJpeg;
565 }
566