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 it's 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