1 /*========================== begin_copyright_notice ============================ 2 3 Copyright (C) 2017-2021 Intel Corporation 4 5 SPDX-License-Identifier: MIT 6 7 ============================= end_copyright_notice ===========================*/ 8 9 #include "AdaptorCommon/customApi.hpp" 10 #include <mutex> 11 12 #if defined(_WIN32 )|| defined( _WIN64 ) 13 #include "Windows.h" 14 #include <direct.h> 15 #include <wtypes.h> 16 #include <process.h> 17 #endif 18 19 #include "common/debug/Debug.hpp" 20 #include "common/Stats.hpp" 21 #include "common/igc_regkeys.hpp" 22 #include "common/SysUtils.hpp" 23 #include "common/secure_string.h" // strcpy_s() 24 #include "Probe/Assertion.h" 25 26 #if defined(IGC_DEBUG_VARIABLES) 27 #include "3d/common/iStdLib/File.h" 28 #endif 29 30 31 namespace { 32 33 #ifndef CL_NUMBER 34 #define CL_NUMBER "<undefined>" 35 #endif 36 37 #define _STRINGIFY(x) #x 38 #define STRINGIFY(x) _STRINGIFY(x) 39 40 #if defined( _DEBUG ) 41 # if defined( _INTERNAL ) 42 # define CONFIGURATION DebugInternal 43 # else 44 # define CONFIGURATION Debug 45 # endif 46 #else 47 # if defined( _INTERNAL ) 48 # define CONFIGURATION ReleaseInternal 49 # else 50 # define CONFIGURATION Release 51 # endif 52 #endif 53 54 #if defined( _WIN64 ) 55 # define BITWIDTH x64 56 #elif defined( _WIN32 ) 57 # define BITWIDTH Win32 58 #else 59 # define BITWIDTH Unknown 60 #endif 61 62 // We don't want this static string in the RELEASE builds because it can be discovered using the 63 // Windows SysInternals Strings tool. We don't want to leak such information. Unfortunately this 64 // means that RELEASE builds can't be queried for their version information. There is no middle ground. 65 #if defined( _DEBUG ) || defined( _INTERNAL ) 66 static const char g_cBuildInfo[] = 67 "CL: " STRINGIFY(CL_NUMBER) ", " 68 "CONFIGURATION: " STRINGIFY(CONFIGURATION) " " STRINGIFY(BITWIDTH) "\0"; 69 #else 70 static const char g_cBuildInfo[] = "\0"; 71 #endif 72 73 #undef CONFIGURATION 74 75 static bool g_debugFlags[ static_cast<int>( IGC::Debug::DebugFlag::END ) ] = {0}; 76 77 static struct 78 { 79 bool dumpODS; 80 bool dumpFile; 81 } g_dumpFlags[ static_cast<int>( IGC::Debug::DumpType::END ) ] = { }; 82 83 std::string g_shaderCorpusName; 84 std::string g_shaderOutputFolder; 85 std::string g_shaderOutputName; 86 87 } 88 89 namespace IGC 90 { 91 namespace Debug 92 { str(DebugFlag value)93 EnumStr IGC_DEBUG_API_CALL str(DebugFlag value) 94 { 95 #define CASE(x) case DebugFlag::x: return STRINGIFY(x) 96 switch (value) 97 { 98 CASE(DUMPS); 99 CASE(DUMP_AFTER_PASSES); 100 CASE(DUMP_TO_OUTS); 101 CASE(DUMP_TO_OUTPUTDEBUGSTRING); 102 CASE(OPTIMIZATION_STATS); 103 CASE(TIME_STATS_SUM); 104 CASE(TIME_STATS_PER_SHADER); 105 CASE(TIME_STATS_COARSE); 106 CASE(TIME_STATS_PER_PASS); 107 CASE(MEM_STATS); 108 CASE(MEM_STATS_DETAIL); 109 CASE(SHADER_QUALITY_METRICS); 110 CASE(SIMD8_ONLY); 111 CASE(SIMD16_ONLY); 112 CASE(SIMD32_ONLY); 113 CASE(VISA_OUTPUT); 114 CASE(VISA_BINARY); 115 CASE(VISA_DUMPCOMMONISA); 116 CASE(VISA_NOSCHEDULE); 117 CASE(VISA_DOTALL); 118 CASE(VISA_SLOWPATH); 119 CASE(VISA_NOBXMLENCODER); 120 default : IGC_ASSERT_EXIT_MESSAGE(0, "unknown DebugFlag"); return "<unknown>"; 121 } 122 #undef CASE 123 } 124 str(DumpType value)125 EnumStr IGC_DEBUG_API_CALL str(DumpType value) 126 { 127 #define CASE(x) case DumpType::x: return STRINGIFY(x) 128 switch (value) 129 { 130 CASE(ASM_TEXT); 131 CASE(ASM_BC); 132 CASE(TRANSLATED_IR_TEXT); 133 CASE(TRANSLATED_IR_BC); 134 CASE(PASS_IR_TEXT); 135 CASE(PASS_IR_BC); 136 CASE(OptIR_TEXT); 137 CASE(OptIR_BC); 138 CASE(VISA_TEXT); 139 CASE(VISA_BC); 140 CASE(GENX_ISA_TEXT); 141 CASE(GENX_ISA_BC); 142 CASE(LLVM_OPT_STAT_TEXT); 143 CASE(TIME_STATS_TEXT); 144 CASE(TIME_STATS_CSV); 145 default : IGC_ASSERT_EXIT_MESSAGE(0, "unknown DumpType"); return "<unknown>"; 146 } 147 #undef CASE 148 } 149 str(DumpLoc value)150 EnumStr IGC_DEBUG_API_CALL str(DumpLoc value) 151 { 152 #define CASE(x) case DumpLoc::x: return STRINGIFY(x) 153 switch (value) 154 { 155 CASE(ODS); 156 CASE(FILE); 157 default : IGC_ASSERT_EXIT_MESSAGE(0, "unknown DumpLoc"); return "<unknown>"; 158 } 159 #undef CASE 160 } 161 SetCompilerOption(OptionFlag flag,debugString s)162 void IGC_DEBUG_API_CALL SetCompilerOption(OptionFlag flag, debugString s) 163 { 164 #if defined(IGC_DEBUG_VARIABLES) 165 switch(flag) 166 { 167 #define DECLARE_IGC_REGKEY(dataType, regkeyName, defaultValue, description, releaseMode) \ 168 case OptionFlag::OPTION_##regkeyName: \ 169 strcpy_s(g_RegKeyList.regkeyName.m_string, sizeof(debugString), s); \ 170 break; 171 #include "common/igc_regkeys.h" 172 #undef DECLARE_IGC_REGKEY 173 default: 174 break; 175 } 176 #endif 177 } 178 SetCompilerOption(OptionFlag flag,int value)179 void IGC_DEBUG_API_CALL SetCompilerOption( OptionFlag flag, int value ) 180 { 181 #if defined(IGC_DEBUG_VARIABLES) 182 switch(flag) 183 { 184 #define DECLARE_IGC_REGKEY(dataType, regkeyName, defaultValue, description, releaseMode) \ 185 case OptionFlag::OPTION_##regkeyName: \ 186 g_RegKeyList.regkeyName.m_Value = value; \ 187 break; 188 #include "common/igc_regkeys.h" 189 #undef DECLARE_IGC_REGKEY 190 default: 191 break; 192 } 193 #endif 194 } 195 SetCompilerOptionValue(const char * flagName,int value)196 extern "C" void IGC_DEBUG_API_CALL SetCompilerOptionValue( const char* flagName, int value ) 197 { 198 #if defined(IGC_DEBUG_VARIABLES) 199 if (!flagName) 200 { 201 return; 202 } 203 204 SRegKeyVariableMetaData* pRegKeyVariable = (SRegKeyVariableMetaData*)&g_RegKeyList; 205 unsigned NUM_REGKEY_ENTRIES = sizeof(SRegKeysList) / sizeof(SRegKeyVariableMetaData); 206 for (DWORD i = 0; i < NUM_REGKEY_ENTRIES; i++) 207 { 208 const char* name = pRegKeyVariable[i].GetName(); 209 210 if (!strcmp(flagName, name)) 211 { 212 pRegKeyVariable[i].m_Value = value; 213 break; 214 } 215 } 216 #endif 217 } 218 SetCompilerOptionString(const char * flagName,debugString s)219 extern "C" void IGC_DEBUG_API_CALL SetCompilerOptionString( const char* flagName, debugString s ) 220 { 221 #if defined(IGC_DEBUG_VARIABLES) 222 if (!flagName) 223 { 224 return; 225 } 226 227 SRegKeyVariableMetaData* pRegKeyVariable = (SRegKeyVariableMetaData*)&g_RegKeyList; 228 unsigned NUM_REGKEY_ENTRIES = sizeof(SRegKeysList) / sizeof(SRegKeyVariableMetaData); 229 for (DWORD i = 0; i < NUM_REGKEY_ENTRIES; i++) 230 { 231 const char* name = pRegKeyVariable[i].GetName(); 232 233 if (!strcmp(flagName, name)) 234 { 235 strcpy_s(pRegKeyVariable[i].m_string,sizeof(debugString), s); 236 break; 237 } 238 } 239 #endif 240 } 241 SetDebugFlag(DebugFlag flag,bool enabled)242 void IGC_DEBUG_API_CALL SetDebugFlag( DebugFlag flag, bool enabled ) 243 { 244 IGC_ASSERT_EXIT_MESSAGE((0 <= static_cast<int>(flag)), "range sanity check"); 245 IGC_ASSERT_EXIT_MESSAGE((static_cast<int>(flag) < static_cast<int> (DebugFlag::END)), "range sanity check"); 246 247 #if defined( _DEBUG ) || defined( _INTERNAL ) 248 g_debugFlags[ static_cast<int>( flag ) ] = enabled; 249 #else 250 (void) flag; 251 (void) enabled; 252 #endif 253 } 254 GetDebugFlag(DebugFlag flag)255 bool IGC_DEBUG_API_CALL GetDebugFlag( DebugFlag flag ) 256 { 257 IGC_ASSERT_EXIT_MESSAGE((0 <= static_cast<int>(flag)), "range sanity check"); 258 IGC_ASSERT_EXIT_MESSAGE((static_cast<int>(flag) < static_cast<int> (DebugFlag::END)), "range sanity check"); 259 260 #if defined( _DEBUG ) || defined( _INTERNAL ) 261 262 #if defined(_WIN32 )|| defined( _WIN64 ) 263 //Disable Dump for OS Applications 264 if ( (DebugFlag::VISA_OUTPUT == flag) || 265 (DebugFlag::VISA_BINARY == flag) || 266 (DebugFlag::VISA_DUMPCOMMONISA == flag) 267 ) 268 { 269 if (GetModuleHandleA("dwm.exe") || GetModuleHandleA("explorer.exe")) 270 { 271 return false; 272 } 273 274 } 275 #endif 276 return g_debugFlags[ static_cast<int>(flag) ]; 277 #else 278 (void) flag; 279 return false; 280 #endif 281 } 282 SetDumpFlag(DumpType type,DumpLoc loc,bool enabled)283 void IGC_DEBUG_API_CALL SetDumpFlag( DumpType type, DumpLoc loc, bool enabled ) 284 { 285 IGC_ASSERT_EXIT_MESSAGE((0 <= static_cast<int>(type)), "range sanity check"); 286 IGC_ASSERT_EXIT_MESSAGE((static_cast<int>(type) < static_cast<int> (DumpType::END)), "range sanity check"); 287 288 #if defined( _DEBUG ) || defined( _INTERNAL ) 289 switch (loc) 290 { 291 case DumpLoc::ODS : g_dumpFlags[ static_cast<int>(type) ].dumpODS = enabled; break; 292 case DumpLoc::FILE : g_dumpFlags[ static_cast<int>(type) ].dumpFile = enabled; break; 293 default : IGC_ASSERT_EXIT_MESSAGE(0, "unreachable"); break; 294 } 295 #else 296 (void) type; 297 (void) loc; 298 (void) enabled; 299 #endif 300 } 301 GetDumpFlag(DumpType type,DumpLoc loc)302 bool IGC_DEBUG_API_CALL GetDumpFlag( DumpType type, DumpLoc loc ) 303 { 304 IGC_ASSERT_EXIT_MESSAGE((0 <= static_cast<int>(type)), "range sanity check"); 305 IGC_ASSERT_EXIT_MESSAGE((static_cast<int>(type) < static_cast<int> (DumpType::END)), "range sanity check"); 306 307 #if defined( _DEBUG ) || defined( _INTERNAL ) 308 309 #if defined(_WIN32 )|| defined( _WIN64 ) 310 //Disable Dump for OS Applications 311 if (GetModuleHandleA("dwm.exe") || GetModuleHandleA("explorer.exe")) 312 { 313 return false; 314 } 315 #endif 316 switch (loc) 317 { 318 case DumpLoc::ODS : return g_dumpFlags[ static_cast<int>(type) ].dumpODS; 319 case DumpLoc::FILE : return g_dumpFlags[ static_cast<int>(type) ].dumpFile; 320 default : IGC_ASSERT_EXIT_MESSAGE(0, "unreachable"); return false; 321 } 322 #else 323 (void) type; 324 (void) loc; 325 return false; 326 #endif 327 } 328 SetShaderCorpusName(CorpusName name)329 void IGC_DEBUG_API_CALL SetShaderCorpusName( CorpusName name ) 330 { 331 #if defined( _DEBUG ) || defined( _INTERNAL ) 332 g_shaderCorpusName = name; 333 #else 334 (void) name; 335 #endif 336 } 337 GetShaderCorpusName()338 CorpusName IGC_DEBUG_API_CALL GetShaderCorpusName() 339 { 340 #if defined( _DEBUG ) || defined( _INTERNAL ) 341 return g_shaderCorpusName.c_str(); 342 #else 343 return ""; 344 #endif 345 } 346 SetShaderOutputFolder(OutputFolderName name)347 void IGC_DEBUG_API_CALL SetShaderOutputFolder( OutputFolderName name ) 348 { 349 #if defined(IGC_DEBUG_VARIABLES) 350 g_shaderOutputFolder = name; 351 #endif 352 } SetShaderOutputName(OutputName name)353 void IGC_DEBUG_API_CALL SetShaderOutputName( OutputName name ) 354 { 355 #if defined(IGC_DEBUG_VARIABLES) 356 g_shaderOutputName = name; 357 #endif 358 } 359 GetBaseIGCOutputFolder()360 OutputFolderName IGC_DEBUG_API_CALL GetBaseIGCOutputFolder() 361 { 362 #if defined(IGC_DEBUG_VARIABLES) 363 static std::mutex m; 364 std::lock_guard<std::mutex> lck(m); 365 static std::string IGCBaseFolder; 366 if(IGCBaseFolder != "") 367 { 368 return IGCBaseFolder.c_str(); 369 } 370 # if defined(_WIN64) || defined(_WIN32) 371 if (!IGC_IS_FLAG_ENABLED(DumpToCurrentDir) && !IGC_IS_FLAG_ENABLED(DumpToCustomDir)) 372 { 373 bool needMkdir = 374 IGC_IS_FLAG_ENABLED(DumpLLVMIR) || 375 IGC_IS_FLAG_ENABLED(EnableCosDump) || 376 IGC_IS_FLAG_ENABLED(EnableVISAOutput) || 377 IGC_IS_FLAG_ENABLED(EnableVISABinary) || 378 IGC_IS_FLAG_ENABLED(EnableVISADumpCommonISA) || 379 GetDebugFlag(DebugFlag::DUMP_AFTER_PASSES) || 380 GetDebugFlag(DebugFlag::VISA_OUTPUT) || 381 GetDebugFlag(DebugFlag::VISA_BINARY) || 382 GetDebugFlag(DebugFlag::VISA_DUMPCOMMONISA) || 383 IGC_IS_FLAG_ENABLED(EnableCapsDump) || 384 IGC_IS_FLAG_ENABLED(ShaderOverride); 385 386 char dumpPath[256]; 387 388 sprintf_s(dumpPath, "c:\\Intel\\IGC\\"); 389 390 if(GetFileAttributesA(dumpPath) != FILE_ATTRIBUTE_DIRECTORY && needMkdir) 391 { 392 _mkdir(dumpPath); 393 } 394 395 // Make sure we can write in the dump folder as the app may be sandboxed 396 if(needMkdir) 397 { 398 int tmp_id = _getpid(); 399 std::string testFilename = std::string(dumpPath) + "testfile" + std::to_string(tmp_id); 400 HANDLE testFile = 401 CreateFileA(testFilename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL); 402 if(testFile == INVALID_HANDLE_VALUE) 403 { 404 char temppath[256]; 405 if(GetTempPathA(sizeof(temppath), temppath) != 0) 406 { 407 sprintf_s(dumpPath, "%sIGC\\", temppath); 408 } 409 } 410 else 411 { 412 CloseHandle(testFile); 413 } 414 } 415 416 if(GetFileAttributesA(dumpPath) != FILE_ATTRIBUTE_DIRECTORY && needMkdir) 417 { 418 _mkdir(dumpPath); 419 } 420 421 IGCBaseFolder = dumpPath; 422 } 423 else if (IGC_IS_FLAG_ENABLED(DumpToCustomDir)) 424 { 425 std::string dumpPath = "c:\\Intel\\IGC\\"; // default if something goes wrong 426 const char* custom_dir = IGC_GET_REGKEYSTRING(DumpToCustomDir); 427 if (custom_dir != nullptr && strlen(custom_dir) > 0) 428 { 429 dumpPath = custom_dir; 430 } 431 432 char pathBuf[256]; 433 iSTD::CreateAppOutputDir(pathBuf, 256, dumpPath.c_str(), false, false, false); 434 435 IGCBaseFolder = pathBuf; 436 } 437 #elif defined ANDROID 438 439 if(IGC_IS_FLAG_ENABLED(DumpToCurrentDir)) 440 return ""; 441 IGCBaseFolder = "/sdcard/intel/igc/"; 442 443 #elif !defined __APPLE__ 444 if (!IGC_IS_FLAG_ENABLED(DumpToCustomDir)) 445 { 446 IGCBaseFolder = "/tmp/IntelIGC/"; 447 } 448 else 449 { 450 std::string dumpPath = "/tmp/IntelIGC/"; // default if something goes wrong 451 const char* custom_dir = IGC_GET_REGKEYSTRING(DumpToCustomDir); 452 if (custom_dir != nullptr && strlen(custom_dir) > 0) 453 { 454 dumpPath = custom_dir; 455 dumpPath += "/"; 456 } 457 458 char pathBuf[256]; 459 iSTD::CreateAppOutputDir(pathBuf, 256, dumpPath.c_str(), false, false, false); 460 461 IGCBaseFolder = pathBuf; 462 } 463 #endif 464 return IGCBaseFolder.c_str(); 465 #else 466 return ""; 467 #endif 468 } 469 GetShaderOverridePath()470 OutputFolderName IGC_DEBUG_API_CALL GetShaderOverridePath() 471 { 472 if(IGC_IS_FLAG_ENABLED(ShaderOverride)) 473 { 474 static std::mutex m; 475 std::lock_guard<std::mutex> lck(m); 476 static std::string overridePath; 477 if(overridePath == "") 478 { 479 overridePath = std::string(GetBaseIGCOutputFolder()) + "ShaderOverride/"; 480 } 481 return overridePath.c_str(); 482 } 483 return ""; 484 } 485 GetShaderOutputFolder()486 OutputFolderName IGC_DEBUG_API_CALL GetShaderOutputFolder() 487 { 488 #if defined(IGC_DEBUG_VARIABLES) 489 static std::mutex m; 490 std::lock_guard<std::mutex> lck(m); 491 if(g_shaderOutputFolder != "") 492 { 493 return g_shaderOutputFolder.c_str(); 494 } 495 # if defined(_WIN64) || defined(_WIN32) 496 if (!IGC_IS_FLAG_ENABLED(DumpToCurrentDir) && !IGC_IS_FLAG_ENABLED(DumpToCustomDir)) 497 { 498 char dumpPath[256]; 499 sprintf_s(dumpPath, "%s", GetBaseIGCOutputFolder()); 500 char appPath[MAX_PATH] = { 0 }; 501 // check a process id and make an adequate directory for it: 502 503 if (::GetModuleFileNameA(NULL, appPath, sizeof(appPath)-1)) 504 { 505 std::string appPathStr = std::string(appPath); 506 int pos = appPathStr.find_last_of("\\") + 1; 507 508 if (IGC_IS_FLAG_ENABLED(ShaderDumpPidDisable)) 509 { 510 sprintf_s(dumpPath, "%s%s\\", dumpPath, appPathStr.substr(pos, MAX_PATH).c_str()); 511 } 512 else 513 { 514 sprintf_s(dumpPath, "%s%s_%d\\", dumpPath, appPathStr.substr(pos, MAX_PATH).c_str(), _getpid()); 515 } 516 } 517 else 518 { 519 sprintf_s(dumpPath, "%sunknownProcess_%d\\", dumpPath, _getpid()); 520 } 521 522 if (GetFileAttributesA(dumpPath) != FILE_ATTRIBUTE_DIRECTORY) 523 { 524 _mkdir(dumpPath); 525 } 526 527 g_shaderOutputFolder = dumpPath; 528 } 529 else if (IGC_IS_FLAG_ENABLED(DumpToCustomDir)) 530 { 531 char pathBuf[256]; 532 iSTD::CreateAppOutputDir(pathBuf, 256, GetBaseIGCOutputFolder(), false, true, !IGC_IS_FLAG_ENABLED(ShaderDumpPidDisable)); 533 g_shaderOutputFolder = pathBuf; 534 } 535 #elif defined ANDROID 536 537 if (IGC_IS_FLAG_ENABLED(DumpToCurrentDir)) 538 539 return ""; 540 541 542 543 if (!SysUtils::CreateDir(GetBaseIGCOutputFolder(), true, IGC_IS_FLAG_DISABLED(ShaderDumpPidDisable), &g_shaderOutputFolder)) 544 545 g_shaderOutputFolder = ""; 546 547 #elif !defined __APPLE__ 548 if (!IGC_IS_FLAG_ENABLED(DumpToCurrentDir) && g_shaderOutputFolder == "" && !IGC_IS_FLAG_ENABLED(DumpToCustomDir)) 549 { 550 bool needMkdir = false; 551 552 if (IGC_IS_FLAG_ENABLED(DumpLLVMIR) || 553 IGC_IS_FLAG_ENABLED(EnableCosDump) || 554 IGC_IS_FLAG_ENABLED(EnableVISAOutput) || 555 IGC_IS_FLAG_ENABLED(EnableVISABinary) || 556 IGC_IS_FLAG_ENABLED(EnableVISADumpCommonISA) || 557 GetDebugFlag(DebugFlag::DUMP_AFTER_PASSES) || 558 GetDebugFlag(DebugFlag::VISA_OUTPUT) || 559 GetDebugFlag(DebugFlag::VISA_BINARY) || 560 GetDebugFlag(DebugFlag::VISA_DUMPCOMMONISA) || 561 IGC_IS_FLAG_ENABLED(EnableCapsDump)) 562 { 563 needMkdir = true; 564 } 565 566 char path[MAX_PATH] = { 0 }; 567 bool pidEnabled = IGC_IS_FLAG_DISABLED(ShaderDumpPidDisable); 568 569 if (needMkdir) 570 { 571 iSTD::CreateAppOutputDir( 572 path, 573 MAX_PATH, 574 GetBaseIGCOutputFolder(), 575 false, 576 true, 577 pidEnabled); 578 } 579 580 581 g_shaderOutputFolder = path; 582 } 583 else if (IGC_IS_FLAG_ENABLED(DumpToCustomDir)) 584 { 585 char pathBuf[256]; 586 iSTD::CreateAppOutputDir(pathBuf, 256, GetBaseIGCOutputFolder(), false, false, false); 587 g_shaderOutputFolder = pathBuf; 588 } 589 590 #endif 591 return g_shaderOutputFolder.c_str(); 592 #else 593 return ""; 594 #endif 595 } 596 GetFunctionDebugFile()597 OutputName IGC_DEBUG_API_CALL GetFunctionDebugFile() 598 { 599 if (IGC_GET_FLAG_VALUE(SelectiveFunctionControl) != 0) 600 { 601 static std::mutex m; 602 std::lock_guard<std::mutex> lck(m); 603 static std::string functionDebugFilePath; 604 if (functionDebugFilePath == "") 605 { 606 functionDebugFilePath = std::string(GetBaseIGCOutputFolder()) + "FunctionDebug.txt"; 607 } 608 return functionDebugFilePath.c_str(); 609 } 610 return ""; 611 } 612 GetShaderOutputName()613 OutputName IGC_DEBUG_API_CALL GetShaderOutputName() 614 { 615 #if defined(IGC_DEBUG_VARIABLES) 616 return g_shaderOutputName.c_str(); 617 #else 618 return ""; 619 #endif 620 } 621 GetVersionInfo()622 VersionInfo IGC_DEBUG_API_CALL GetVersionInfo() 623 { 624 return g_cBuildInfo; 625 } 626 } 627 } 628