yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
b118451e3
master
1// slang-downstream-compiler.cpp 2#include "slang-downstream-compiler-util.h" 3 4#include "../core/slang-blob.h" 5#include "../core/slang-char-util.h" 6#include "../core/slang-common.h" 7#include "../core/slang-io.h" 8#include "../core/slang-shared-library.h" 9#include "../core/slang-string-util.h" 10#include "../core/slang-type-text-util.h" 11#include "slang-com-helper.h" 12 13#ifdef SLANG_VC 14#include "windows/slang-win-visual-studio-util.h" 15#endif 16 17#include "slang-dxc-compiler.h" 18#include "slang-fxc-compiler.h" 19#include "slang-gcc-compiler-util.h" 20#include "slang-glslang-compiler.h" 21#include "slang-llvm-compiler.h" 22#include "slang-metal-compiler.h" 23#include "slang-nvrtc-compiler.h" 24#include "slang-tint-compiler.h" 25#include "slang-visual-studio-compiler-util.h" 26 27namespace Slang 28{ 29 30/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompilerInfos !!!!!!!!!!!!!!!!!!!!!!*/ 31 32struct DownstreamCompilerInfos 33{ 34DownstreamCompilerInfo infos [int (SLANG_PASS_THROUGH_COUNT_OF )]; 35 36static DownstreamCompilerInfos _calcInfos (); 37static DownstreamCompilerInfos s_infos ; 38}; 39 40/* static */ DownstreamCompilerInfos DownstreamCompilerInfos ::_calcInfos () 41{ 42typedef DownstreamCompilerInfo Info ; 43typedef Info ::SourceLanguageFlag SourceLanguageFlag ; 44 45DownstreamCompilerInfos infos ; 46 47infos .infos [int (SLANG_PASS_THROUGH_CLANG )]= 48Info (SourceLanguageFlag ::CPP |SourceLanguageFlag ::C ); 49infos .infos [int (SLANG_PASS_THROUGH_VISUAL_STUDIO )]= 50Info (SourceLanguageFlag ::CPP |SourceLanguageFlag ::C ); 51infos .infos [int (SLANG_PASS_THROUGH_GCC )]= 52Info (SourceLanguageFlag ::CPP |SourceLanguageFlag ::C ); 53infos .infos [int (SLANG_PASS_THROUGH_LLVM )]= 54Info (SourceLanguageFlag ::CPP |SourceLanguageFlag ::C ); 55 56infos .infos [int (SLANG_PASS_THROUGH_NVRTC )]= Info (SourceLanguageFlag ::CUDA ); 57 58infos .infos [int (SLANG_PASS_THROUGH_DXC )]= Info (SourceLanguageFlag ::HLSL ); 59infos .infos [int (SLANG_PASS_THROUGH_FXC )]= Info (SourceLanguageFlag ::HLSL ); 60infos .infos [int (SLANG_PASS_THROUGH_GLSLANG )]= Info (SourceLanguageFlag ::GLSL ); 61infos .infos [int (SLANG_PASS_THROUGH_SPIRV_OPT )]= Info (SourceLanguageFlag ::SPIRV ); 62 63return infos ; 64} 65 66/* static */ DownstreamCompilerInfos DownstreamCompilerInfos ::s_infos = 67DownstreamCompilerInfos ::_calcInfos (); 68 69/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompilerInfo !!!!!!!!!!!!!!!!!!!!!!*/ 70 71/* static */ const DownstreamCompilerInfo & DownstreamCompilerInfo ::getInfo ( 72SlangPassThrough compiler ) 73{ 74return DownstreamCompilerInfos ::s_infos .infos [int (compiler )]; 75} 76 77/* static */ bool DownstreamCompilerInfo ::canCompile ( 78SlangPassThrough compiler , 79SlangSourceLanguage sourceLanguage ) 80{ 81const auto & info = getInfo (compiler ); 82return (info .sourceLanguageFlags & (SourceLanguageFlags (1 ) <<int (sourceLanguage )))!= 0 ; 83} 84 85/* !!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompilerUtil !!!!!!!!!!!!!!!!!!!!!!*/ 86 87static DownstreamCompilerMatchVersion _calcCompiledVersion () 88{ 89DownstreamCompilerMatchVersion matchVersion ; 90 91#if SLANG_VC 92matchVersion = WinVisualStudioUtil ::getCompiledVersion (); 93#elif SLANG_CLANG 94matchVersion .type = SLANG_PASS_THROUGH_CLANG ; 95matchVersion .matchVersion .set (Index (__clang_major__ ),Index (__clang_minor__ )); 96#elif SLANG_GCC 97matchVersion .type = SLANG_PASS_THROUGH_GCC ; 98matchVersion .matchVersion .set (Index (__GNUC__ ),Index (__GNUC_MINOR__ )); 99#else 100// TODO(JS): Hmmm None is not quite the same as unknown. It works for now, but we might want to 101// have a distinct enum for unknown. 102matchVersion .type = SLANG_PASS_THROUGH_NONE ; 103#endif 104 105return matchVersion ; 106} 107 108 109DownstreamCompilerMatchVersion DownstreamCompilerUtil ::getCompiledVersion () 110{ 111static DownstreamCompilerMatchVersion s_version = _calcCompiledVersion (); 112return s_version ; 113} 114 115/* static */ IDownstreamCompiler * DownstreamCompilerUtil ::findCompiler ( 116const DownstreamCompilerSet * set , 117MatchType matchType , 118const DownstreamCompilerDesc & desc ) 119{ 120List < IDownstreamCompiler *> compilers ; 121set -> getCompilers (compilers ); 122return findCompiler (compilers ,matchType ,desc ); 123} 124 125/* static */ IDownstreamCompiler * DownstreamCompilerUtil ::findCompiler ( 126const List < IDownstreamCompiler *>& compilers , 127MatchType matchType , 128const DownstreamCompilerDesc & desc ) 129{ 130if (compilers .getCount () <=0 ) 131 { 132return nullptr ; 133 } 134 135Int bestIndex = -1 ; 136 137const SlangPassThrough compilerType = desc .type ; 138 139Int maxVersionValue = 0 ; 140Int minVersionDiff = 0x7fffffff ; 141 142Int descVersionValue = desc .getVersionValue (); 143 144// If we don't have version set, then anything 0 or above is good enough, and just take newest 145if (descVersionValue == 0 ) 146 { 147maxVersionValue = -1 ; 148matchType = MatchType ::Newest ; 149 } 150 151for (Index i = 0 ;i < compilers .getCount ();++ i ) 152 { 153IDownstreamCompiler * compiler = compilers [i ]; 154auto compilerDesc = compiler -> getDesc (); 155 156if (compilerType == compilerDesc .type ) 157 { 158const Int versionValue = compilerDesc .getVersionValue (); 159switch (matchType ) 160 { 161case MatchType ::MinGreaterEqual : 162 { 163auto diff = descVersionValue - versionValue ; 164if (diff >=0 && diff < minVersionDiff ) 165 { 166bestIndex = i ; 167minVersionDiff = diff ; 168 } 169break ; 170 } 171case MatchType ::MinAbsolute : 172 { 173auto diff = descVersionValue - versionValue ; 174diff = (diff >=0 ) ?diff :- diff ; 175if (diff < minVersionDiff ) 176 { 177bestIndex = i ; 178minVersionDiff = diff ; 179 } 180break ; 181 } 182case MatchType ::Newest : 183 { 184if (versionValue > maxVersionValue ) 185 { 186maxVersionValue = versionValue ; 187bestIndex = i ; 188 } 189break ; 190 } 191 } 192 } 193 } 194 195return (bestIndex >=0 ) ?compilers [bestIndex ] :nullptr ; 196} 197 198/* static */ IDownstreamCompiler * DownstreamCompilerUtil ::findCompiler ( 199const List < IDownstreamCompiler *>& compilers , 200const DownstreamCompilerDesc & desc ) 201{ 202for (auto compiler :compilers ) 203 { 204if (compiler -> getDesc ()== desc ) 205 { 206return compiler ; 207 } 208 } 209return nullptr ; 210} 211 212/* static */ IDownstreamCompiler * DownstreamCompilerUtil ::findCompiler ( 213const List < IDownstreamCompiler *>& compilers , 214SlangPassThrough type , 215const SemanticVersion & version ) 216{ 217DownstreamCompilerDesc desc ; 218desc .type = type ; 219desc .version = version ; 220return findCompiler (compilers ,desc ); 221} 222 223/* static */ void DownstreamCompilerUtil ::findVersions ( 224const List < IDownstreamCompiler *>& compilers , 225SlangPassThrough type , 226List < SemanticVersion >& outVersions ) 227{ 228for (auto compiler :compilers ) 229 { 230auto desc = compiler -> getDesc (); 231 232if (desc .type == type ) 233 { 234outVersions .add (desc .version ); 235 } 236 } 237} 238 239/* static */ IDownstreamCompiler * DownstreamCompilerUtil ::findClosestCompiler ( 240const List < IDownstreamCompiler *>& compilers , 241const DownstreamCompilerMatchVersion & matchVersion ) 242{ 243List < SemanticVersion > versions ; 244 245findVersions (compilers ,matchVersion .type ,versions ); 246 247if (versions .getCount ()> 0 ) 248 { 249if (versions .getCount ()== 1 ) 250 { 251// Must be that one 252return findCompiler (compilers ,matchVersion .type ,versions [0 ]); 253 } 254 255// Okay lets find the best one 256auto bestVersion = MatchSemanticVersion ::findAnyBest ( 257versions .getBuffer (), 258versions .getCount (), 259matchVersion .matchVersion ); 260 261// If one is found use it 262if (bestVersion .isSet ()) 263 { 264return findCompiler (compilers ,matchVersion .type ,bestVersion ); 265 } 266 } 267 268 { 269// TODO(JS): 270// NOTE! This may not really be appropriate, because LLVM is *not* interchangable with 271// a 'normal' C++ compiler as cannot access standard libraries/headers. 272// So `slang-llvm` can't be used for 'host' code. 273 274// These compilers should be usable interchangably. The order is important, as the first one 275// that matches will be used, so LLVM is used before CLANG or GCC if appropriate 276const SlangPassThrough compatiblePassThroughs []= { 277SLANG_PASS_THROUGH_LLVM , 278SLANG_PASS_THROUGH_CLANG , 279SLANG_PASS_THROUGH_GCC , 280 }; 281 282// Check the version is one of the compatible types 283if (makeConstArrayView (compatiblePassThroughs ).indexOf (matchVersion .type ) >=0 ) 284 { 285// Try each compatible type in turn 286for (auto passThrough :compatiblePassThroughs ) 287 { 288versions .clear (); 289findVersions (compilers ,passThrough ,versions ); 290 291if (versions .getCount ()> 0 ) 292 { 293// Get the latest version (as we have no way to really compare) 294auto latestVersion = 295SemanticVersion ::getLatest (versions .getBuffer (),versions .getCount ()); 296return findCompiler (compilers ,matchVersion .type ,latestVersion ); 297 } 298 } 299 } 300 } 301 302return nullptr ; 303} 304 305/* static */ IDownstreamCompiler * DownstreamCompilerUtil ::findClosestCompiler ( 306const DownstreamCompilerSet * set , 307const DownstreamCompilerMatchVersion & matchVersion ) 308{ 309List < IDownstreamCompiler *> compilers ; 310set -> getCompilers (compilers ); 311return findClosestCompiler (compilers ,matchVersion ); 312} 313 314/* static */ void DownstreamCompilerUtil ::updateDefault ( 315DownstreamCompilerSet * set , 316SlangSourceLanguage sourceLanguage ) 317{ 318IDownstreamCompiler * compiler = nullptr ; 319 320switch (sourceLanguage ) 321 { 322case SLANG_SOURCE_LANGUAGE_CPP : 323case SLANG_SOURCE_LANGUAGE_C : 324 { 325// Find the compiler closest to the compiler this was compiled with 326if (!compiler ) 327 { 328compiler = findClosestCompiler (set ,getCompiledVersion ()); 329 } 330break ; 331 } 332case SLANG_SOURCE_LANGUAGE_CUDA : 333 { 334DownstreamCompilerDesc desc ; 335desc .type = SLANG_PASS_THROUGH_NVRTC ; 336compiler = findCompiler (set ,MatchType ::Newest ,desc ); 337break ; 338 } 339default : 340break ; 341 } 342 343set -> setDefaultCompiler (sourceLanguage ,compiler ); 344} 345 346/* static */ void DownstreamCompilerUtil ::updateDefaults (DownstreamCompilerSet * set ) 347{ 348for (Index i = 0 ;i < Index (SLANG_SOURCE_LANGUAGE_COUNT_OF );++ i ) 349 { 350updateDefault (set ,SlangSourceLanguage (i )); 351 } 352} 353 354/* static */ void DownstreamCompilerUtil ::setDefaultLocators ( 355DownstreamCompilerLocatorFunc outFuncs [int (SLANG_PASS_THROUGH_COUNT_OF )]) 356{ 357outFuncs [int (SLANG_PASS_THROUGH_VISUAL_STUDIO )]= & VisualStudioCompilerUtil ::locateCompilers ; 358outFuncs [int (SLANG_PASS_THROUGH_CLANG )]= & GCCDownstreamCompilerUtil ::locateClangCompilers ; 359outFuncs [int (SLANG_PASS_THROUGH_GCC )]= & GCCDownstreamCompilerUtil ::locateGCCCompilers ; 360outFuncs [int (SLANG_PASS_THROUGH_NVRTC )]= & NVRTCDownstreamCompilerUtil ::locateCompilers ; 361outFuncs [int (SLANG_PASS_THROUGH_DXC )]= & DXCDownstreamCompilerUtil ::locateCompilers ; 362outFuncs [int (SLANG_PASS_THROUGH_FXC )]= & FXCDownstreamCompilerUtil ::locateCompilers ; 363outFuncs [int (SLANG_PASS_THROUGH_GLSLANG )]= & GlslangDownstreamCompilerUtil ::locateCompilers ; 364outFuncs [int (SLANG_PASS_THROUGH_SPIRV_OPT )]= & SpirvOptDownstreamCompilerUtil ::locateCompilers ; 365outFuncs [int (SLANG_PASS_THROUGH_LLVM )]= & LLVMDownstreamCompilerUtil ::locateCompilers ; 366outFuncs [int (SLANG_PASS_THROUGH_SPIRV_DIS )]= & SpirvDisDownstreamCompilerUtil ::locateCompilers ; 367outFuncs [int (SLANG_PASS_THROUGH_METAL )]= & MetalDownstreamCompilerUtil ::locateCompilers ; 368outFuncs [int (SLANG_PASS_THROUGH_TINT )]= & TintDownstreamCompilerUtil ::locateCompilers ; 369} 370 371static String _getParentPath (const String & path ) 372{ 373// If we can get the canonical path, we'll do that before getting the parent 374String canonicalPath ; 375if (SLANG_SUCCEEDED (Path ::getCanonical (path ,canonicalPath ))) 376 { 377return Path ::getParentDirectory (canonicalPath ); 378 } 379else 380 { 381return Path ::getParentDirectory (path ); 382 } 383} 384 385static SlangResult _findPaths ( 386const String & path , 387const char * libraryName , 388String & outParentPath , 389String & outLibraryPath ) 390{ 391// Try to determine what the path is by looking up the path type 392SlangPathType pathType ; 393if (SLANG_SUCCEEDED (Path ::getPathType (path ,& pathType ))) 394 { 395if (pathType == SLANG_PATH_TYPE_DIRECTORY ) 396 { 397outParentPath = path ; 398outLibraryPath = Path ::combine (outParentPath ,libraryName ); 399 } 400else 401 { 402SLANG_ASSERT (pathType == SLANG_PATH_TYPE_FILE ); 403 404outParentPath = _getParentPath (path ); 405outLibraryPath = path ; 406 } 407 408return SLANG_OK ; 409 } 410 411// If this failed the path could be to a shared library, but we may need to convert to the 412// shared library filename first 413const String sharedLibraryFilePath = SharedLibrary ::calcPlatformPath (path .getUnownedSlice ()); 414if (SLANG_SUCCEEDED (Path ::getPathType (sharedLibraryFilePath ,& pathType ))&& 415pathType == SLANG_PATH_TYPE_FILE ) 416 { 417// We pass in the shared library path, as canonical paths can sometimes only apply to 418// pre-existing objects. 419outParentPath = _getParentPath (sharedLibraryFilePath ); 420// The original path should work as is for the SharedLibrary load. Notably we don't use the 421// sharedLibraryFilePath as this is the wrong name to do a SharedLibrary load with. 422outLibraryPath = path ; 423 424return SLANG_OK ; 425 } 426 427return SLANG_FAIL ; 428} 429 430/* static */ SlangResult DownstreamCompilerUtil ::loadSharedLibrary ( 431const String & path , 432ISlangSharedLibraryLoader * loader , 433const char * const * dependentNames , 434const char * inLibraryName , 435ComPtr < ISlangSharedLibrary >& outSharedLib ) 436{ 437String parentPath ; 438String libraryPath ; 439 440// If a path is passed in lets, try and determine what kind of path it is. 441if (path .getLength ()) 442 { 443if (SLANG_FAILED (_findPaths (path ,inLibraryName ,parentPath ,libraryPath ))) 444 { 445// We have a few scenarios here. 446// 1) The path could be the shared library/dll filename, that will be found through some 447// operating system mechanism 2) That the shared library is *NOT* on the filesystem 448// directly (the loader does something different) 3) Permissions or some other mechanism 449// stops the lookup from working 450 451// We should probably assume that the path means something, else why set it. 452// It's probably less likely that it is a directory that we can't detect - as if it's a 453// directory as part of an app it's permissions should allow detection, or be made to 454// allow it. 455 456// All this being the case we should probably assume that it is the shared library name. 457libraryPath = path ; 458 459// Attempt to get a parent. If there isn't one this will be empty, which will mean it 460// will be ignored, which is probably what we want if path is just a shared library name 461parentPath = Path ::getParentDirectory (libraryPath ); 462 } 463 } 464 465// Keep all dependent libs in scope, before we load the library we want 466List < ComPtr < ISlangSharedLibrary >> dependentLibs ; 467 468// Try to load any dependent libs from the parent path 469if (dependentNames ) 470 { 471for (const char * const * cur = dependentNames ;* cur ;++ cur ) 472 { 473const char * dependentName = * cur ; 474ComPtr < ISlangSharedLibrary > lib ; 475if (parentPath .getLength ()) 476 { 477String dependentPath = Path ::combine (parentPath ,dependentName ); 478loader -> loadSharedLibrary (dependentPath .getBuffer (),lib .writeRef ()); 479 } 480else 481 { 482loader -> loadSharedLibrary (dependentName ,lib .writeRef ()); 483 } 484 485if (lib ) 486 { 487dependentLibs .add (lib ); 488 } 489 } 490 } 491 492if (libraryPath .getLength ()) 493 { 494// If we hare a library path use that 495return loader -> loadSharedLibrary (libraryPath .getBuffer (),outSharedLib .writeRef ()); 496 } 497else 498 { 499// Else just use the name that was passed in. 500return loader -> loadSharedLibrary (inLibraryName ,outSharedLib .writeRef ()); 501 } 502} 503 504/* static */ void DownstreamCompilerUtil ::appendAsText ( 505const DownstreamCompilerDesc & desc , 506StringBuilder & out ) 507{ 508out <<TypeTextUtil ::getPassThroughAsHumanText (desc .type ); 509 510// Append the version if there is a version 511if (desc .version .isSet ()) 512 { 513out <<" " ; 514out <<desc .version .m_major ; 515out <<"." ; 516out <<desc .version .m_minor ; 517 } 518} 519 520}// namespace Slang