RenderLog.cpp (11404B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 #pragma hdrstop 29 #include "tr_local.h" 30 31 /* 32 ================================================================================================ 33 Contains the RenderLog implementation. 34 35 TODO: Emit statistics to the logfile at the end of views and frames. 36 ================================================================================================ 37 */ 38 39 idCVar r_logLevel( "r_logLevel", "2", CVAR_INTEGER, "1 = blocks only, 2 = everything", 1, 2 ); 40 41 static const int LOG_LEVEL_BLOCKS_ONLY = 1; 42 static const int LOG_LEVEL_EVERYTHING = 2; 43 44 const char * renderLogMainBlockLabels[] = { 45 ASSERT_ENUM_STRING( MRB_NONE, 0 ), 46 ASSERT_ENUM_STRING( MRB_BEGIN_DRAWING_VIEW, 1 ), 47 ASSERT_ENUM_STRING( MRB_FILL_DEPTH_BUFFER, 2 ), 48 ASSERT_ENUM_STRING( MRB_DRAW_INTERACTIONS, 3 ), 49 ASSERT_ENUM_STRING( MRB_DRAW_SHADER_PASSES, 4 ), 50 ASSERT_ENUM_STRING( MRB_FOG_ALL_LIGHTS, 5 ), 51 ASSERT_ENUM_STRING( MRB_DRAW_SHADER_PASSES_POST, 6 ), 52 ASSERT_ENUM_STRING( MRB_DRAW_DEBUG_TOOLS, 7 ), 53 ASSERT_ENUM_STRING( MRB_CAPTURE_COLORBUFFER, 8 ), 54 ASSERT_ENUM_STRING( MRB_POSTPROCESS, 9 ), 55 ASSERT_ENUM_STRING( MRB_GPU_SYNC, 10 ), 56 ASSERT_ENUM_STRING( MRB_END_FRAME, 11 ), 57 ASSERT_ENUM_STRING( MRB_BINK_FRAME, 12 ), 58 ASSERT_ENUM_STRING( MRB_BINK_NEXT_FRAME, 13 ), 59 ASSERT_ENUM_STRING( MRB_TOTAL, 14 ), 60 ASSERT_ENUM_STRING( MRB_MAX, 15 ) 61 }; 62 63 extern uint64 Sys_Microseconds(); 64 /* 65 ================================================================================================ 66 67 PIX events on all platforms 68 69 ================================================================================================ 70 */ 71 72 73 /* 74 ================================================ 75 pixEvent_t 76 ================================================ 77 */ 78 struct pixEvent_t { 79 char name[256]; 80 uint64 cpuTime; 81 uint64 gpuTime; 82 }; 83 84 idCVar r_pix( "r_pix", "0", CVAR_INTEGER, "print GPU/CPU event timing" ); 85 86 static const int MAX_PIX_EVENTS = 256; 87 // defer allocation of this until needed, so we don't waste lots of memory 88 pixEvent_t * pixEvents; // [MAX_PIX_EVENTS] 89 int numPixEvents; 90 int numPixLevels; 91 static GLuint timeQueryIds[MAX_PIX_EVENTS]; 92 93 /* 94 ======================== 95 PC_BeginNamedEvent 96 97 FIXME: this is not thread safe on the PC 98 ======================== 99 */ 100 void PC_BeginNamedEvent( const char *szName, ... ) { 101 #if 0 102 if ( !r_pix.GetBool() ) { 103 return; 104 } 105 if ( !pixEvents ) { 106 // lazy allocation to not waste memory 107 pixEvents = (pixEvent_t *)Mem_ClearedAlloc( sizeof( *pixEvents ) * MAX_PIX_EVENTS, TAG_CRAP ); 108 } 109 if ( numPixEvents >= MAX_PIX_EVENTS ) { 110 idLib::FatalError( "PC_BeginNamedEvent: event overflow" ); 111 } 112 if ( ++numPixLevels > 1 ) { 113 return; // only get top level timing information 114 } 115 if ( !qglGetQueryObjectui64vEXT ) { 116 return; 117 } 118 119 GL_CheckErrors(); 120 if ( timeQueryIds[0] == 0 ) { 121 qglGenQueriesARB( MAX_PIX_EVENTS, timeQueryIds ); 122 } 123 qglFinish(); 124 qglBeginQueryARB( GL_TIME_ELAPSED_EXT, timeQueryIds[numPixEvents] ); 125 GL_CheckErrors(); 126 127 pixEvent_t *ev = &pixEvents[numPixEvents++]; 128 strncpy( ev->name, szName, sizeof( ev->name ) - 1 ); 129 ev->cpuTime = Sys_Microseconds(); 130 #endif 131 } 132 133 /* 134 ======================== 135 PC_EndNamedEvent 136 ======================== 137 */ 138 void PC_EndNamedEvent() { 139 #if 0 140 if ( !r_pix.GetBool() ) { 141 return; 142 } 143 if ( numPixLevels <= 0 ) { 144 idLib::FatalError( "PC_EndNamedEvent: level underflow" ); 145 } 146 if ( --numPixLevels > 0 ) { 147 // only do timing on top level events 148 return; 149 } 150 if ( !qglGetQueryObjectui64vEXT ) { 151 return; 152 } 153 154 pixEvent_t *ev = &pixEvents[numPixEvents-1]; 155 ev->cpuTime = Sys_Microseconds() - ev->cpuTime; 156 157 GL_CheckErrors(); 158 qglEndQueryARB( GL_TIME_ELAPSED_EXT ); 159 GL_CheckErrors(); 160 #endif 161 } 162 163 /* 164 ======================== 165 PC_EndFrame 166 ======================== 167 */ 168 void PC_EndFrame() { 169 #if 0 170 if ( !r_pix.GetBool() ) { 171 return; 172 } 173 174 int64 totalGPU = 0; 175 int64 totalCPU = 0; 176 177 idLib::Printf( "----- GPU Events -----\n" ); 178 for ( int i = 0 ; i < numPixEvents ; i++ ) { 179 pixEvent_t *ev = &pixEvents[i]; 180 181 int64 gpuTime = 0; 182 qglGetQueryObjectui64vEXT( timeQueryIds[i], GL_QUERY_RESULT, (GLuint64EXT *)&gpuTime ); 183 ev->gpuTime = gpuTime; 184 185 idLib::Printf( "%2d: %1.2f (GPU) %1.3f (CPU) = %s\n", i, ev->gpuTime / 1000000.0f, ev->cpuTime / 1000.0f, ev->name ); 186 totalGPU += ev->gpuTime; 187 totalCPU += ev->cpuTime; 188 } 189 idLib::Printf( "%2d: %1.2f (GPU) %1.3f (CPU) = total\n", numPixEvents, totalGPU / 1000000.0f, totalCPU / 1000.0f ); 190 memset( pixEvents, 0, numPixLevels * sizeof( pixEvents[0] ) ); 191 192 numPixEvents = 0; 193 numPixLevels = 0; 194 #endif 195 } 196 197 198 /* 199 ================================================================================================ 200 201 idRenderLog 202 203 ================================================================================================ 204 */ 205 206 idRenderLog renderLog; 207 208 #if !defined( STUB_RENDER_LOG ) 209 210 /* 211 ======================== 212 idRenderLog::idRenderLog 213 ======================== 214 */ 215 idRenderLog::idRenderLog() { 216 activeLevel = 0; 217 indentString[0] = '\0'; 218 indentLevel = 0; 219 logFile = NULL; 220 221 frameStartTime = 0; 222 closeBlockTime = 0; 223 logLevel = 0; 224 } 225 226 /* 227 ======================== 228 idRenderLog::StartFrame 229 ======================== 230 */ 231 void idRenderLog::StartFrame() { 232 if ( r_logFile.GetInteger() == 0 ) { 233 return; 234 } 235 236 // open a new logfile 237 indentLevel = 0; 238 indentString[0] = '\0'; 239 activeLevel = r_logLevel.GetInteger(); 240 241 struct tm *newtime; 242 time_t aclock; 243 244 char ospath[ MAX_OSPATH ]; 245 246 char qpath[128]; 247 sprintf( qpath, "renderlogPC_%04i.txt", r_logFile.GetInteger() ); 248 idStr finalPath = fileSystem->RelativePathToOSPath( qpath ); 249 sprintf( ospath, "%s", finalPath.c_str() ); 250 /* 251 for ( int i = 0; i < 9999 ; i++ ) { 252 char qpath[128]; 253 sprintf( qpath, "renderlog_%04i.txt", r_logFile.GetInteger() ); 254 idStr finalPath = fileSystem->RelativePathToOSPath( qpath ); 255 fileSystem->RelativePathToOSPath( qpath, ospath, MAX_OSPATH ,FSPATH_BASE ); 256 if ( !fileSystem->FileExists( finalPath.c_str() ) ) { 257 break; // use this name 258 } 259 } 260 */ 261 262 common->SetRefreshOnPrint( false ); // problems are caused if this print causes a refresh... 263 264 if ( logFile != NULL ) { 265 fileSystem->CloseFile( logFile ); 266 logFile = NULL; 267 } 268 269 logFile = fileSystem->OpenFileWrite( ospath ); 270 if ( logFile == NULL ) { 271 idLib::Warning( "Failed to open logfile %s", ospath ); 272 return; 273 } 274 idLib::Printf( "Opened logfile %s\n", ospath ); 275 276 // write the time out to the top of the file 277 time( &aclock ); 278 newtime = localtime( &aclock ); 279 const char *str = asctime( newtime ); 280 logFile->Printf( "// %s", str ); 281 logFile->Printf( "// %s\n\n", com_version.GetString() ); 282 283 frameStartTime = Sys_Microseconds(); 284 closeBlockTime = frameStartTime; 285 OpenBlock( "Frame" ); 286 } 287 288 /* 289 ======================== 290 idRenderLog::EndFrame 291 ======================== 292 */ 293 void idRenderLog::EndFrame() { 294 PC_EndFrame(); 295 296 if ( logFile != NULL ) { 297 if ( r_logFile.GetInteger() == 1 ) { 298 Close(); 299 } 300 // log is open, so decrement r_logFile and stop if it is zero 301 r_logFile.SetInteger( r_logFile.GetInteger() - 1 ); 302 idLib::Printf( "Frame logged.\n" ); 303 return; 304 } 305 } 306 307 /* 308 ======================== 309 idRenderLog::Close 310 ======================== 311 */ 312 void idRenderLog::Close() { 313 if ( logFile != NULL ) { 314 CloseBlock(); 315 idLib::Printf( "Closing logfile\n" ); 316 fileSystem->CloseFile( logFile ); 317 logFile = NULL; 318 activeLevel = 0; 319 } 320 } 321 322 /* 323 ======================== 324 idRenderLog::OpenMainBlock 325 ======================== 326 */ 327 void idRenderLog::OpenMainBlock( renderLogMainBlock_t block ) { 328 } 329 330 /* 331 ======================== 332 idRenderLog::CloseMainBlock 333 ======================== 334 */ 335 void idRenderLog::CloseMainBlock() { 336 } 337 338 /* 339 ======================== 340 idRenderLog::OpenBlock 341 ======================== 342 */ 343 void idRenderLog::OpenBlock( const char *label ) { 344 // Allow the PIX functionality even when logFile is not running. 345 PC_BeginNamedEvent( label ); 346 347 if ( logFile != NULL ) { 348 LogOpenBlock( RENDER_LOG_INDENT_MAIN_BLOCK, label, NULL ); 349 } 350 } 351 352 /* 353 ======================== 354 idRenderLog::CloseBlock 355 ======================== 356 */ 357 void idRenderLog::CloseBlock() { 358 PC_EndNamedEvent(); 359 360 if ( logFile != NULL ) { 361 LogCloseBlock( RENDER_LOG_INDENT_MAIN_BLOCK ); 362 } 363 } 364 365 /* 366 ======================== 367 idRenderLog::Printf 368 ======================== 369 */ 370 void idRenderLog::Printf( const char *fmt, ... ) { 371 if ( activeLevel <= LOG_LEVEL_BLOCKS_ONLY ) { 372 return; 373 } 374 375 if ( logFile == NULL ) { 376 return; 377 } 378 379 va_list marker; 380 logFile->Printf( "%s", indentString ); 381 va_start( marker, fmt ); 382 logFile->VPrintf( fmt, marker ); 383 va_end( marker ); 384 // logFile->Flush(); this makes it take waaaay too long 385 } 386 387 /* 388 ======================== 389 idRenderLog::LogOpenBlock 390 ======================== 391 */ 392 void idRenderLog::LogOpenBlock( renderLogIndentLabel_t label, const char * fmt, va_list args ) { 393 394 uint64 now = Sys_Microseconds(); 395 396 if ( logFile != NULL ) { 397 if ( now - closeBlockTime >= 1000 ) { 398 logFile->Printf( "%s%1.1f msec gap from last closeblock\n", indentString, ( now - closeBlockTime ) * ( 1.0f / 1000.0f ) ); 399 } 400 logFile->Printf( "%s", indentString ); 401 logFile->VPrintf( fmt, args ); 402 logFile->Printf( " {\n" ); 403 } 404 405 Indent( label ); 406 407 if ( logLevel >= MAX_LOG_LEVELS ) { 408 idLib::Warning( "logLevel %d >= MAX_LOG_LEVELS", logLevel ); 409 } 410 411 412 logLevel++; 413 } 414 415 /* 416 ======================== 417 idRenderLog::LogCloseBlock 418 ======================== 419 */ 420 void idRenderLog::LogCloseBlock( renderLogIndentLabel_t label ) { 421 closeBlockTime = Sys_Microseconds(); 422 423 assert( logLevel > 0 ); 424 logLevel--; 425 426 Outdent( label ); 427 428 if ( logFile != NULL ) { 429 } 430 } 431 432 #else // !STUB_RENDER_LOG 433 434 /* 435 ======================== 436 idRenderLog::OpenBlock 437 ======================== 438 */ 439 void idRenderLog::OpenBlock( const char *label ) { 440 PC_BeginNamedEvent( label ); 441 } 442 443 /* 444 ======================== 445 idRenderLog::CloseBlock 446 ======================== 447 */ 448 void idRenderLog::CloseBlock() { 449 PC_EndNamedEvent(); 450 } 451 452 #endif // !STUB_RENDER_LOG