yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
7d15d388d
master
1// slang-nvrtc-compiler.cpp 2#include "slang-nvrtc-compiler.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-semantic-version.h" 9#include "../core/slang-shared-library.h" 10#include "../core/slang-string-slice-pool.h" 11#include "../core/slang-string-util.h" 12#include "slang-artifact-associated-impl.h" 13#include "slang-artifact-desc-util.h" 14#include "slang-artifact-diagnostic-util.h" 15#include "slang-artifact-util.h" 16#include "slang-com-helper.h" 17 18namespace nvrtc 19{ 20 21typedef enum 22{ 23NVRTC_SUCCESS = 0 , 24NVRTC_ERROR_OUT_OF_MEMORY = 1 , 25NVRTC_ERROR_PROGRAM_CREATION_FAILURE = 2 , 26NVRTC_ERROR_INVALID_INPUT = 3 , 27NVRTC_ERROR_INVALID_PROGRAM = 4 , 28NVRTC_ERROR_INVALID_OPTION = 5 , 29NVRTC_ERROR_COMPILATION = 6 , 30NVRTC_ERROR_BUILTIN_OPERATION_FAILURE = 7 , 31NVRTC_ERROR_NO_NAME_EXPRESSIONS_AFTER_COMPILATION = 8 , 32NVRTC_ERROR_NO_LOWERED_NAMES_BEFORE_COMPILATION = 9 , 33NVRTC_ERROR_NAME_EXPRESSION_NOT_VALID = 10 , 34NVRTC_ERROR_INTERNAL_ERROR = 11 35}nvrtcResult ; 36 37typedef struct _nvrtcProgram * nvrtcProgram ; 38 39// clang-format off 40#define SLANG_NVRTC_FUNCS (x ) \ 41 x(const char*, nvrtcGetErrorString, (nvrtcResult result)) \ 42 x(nvrtcResult, nvrtcVersion, (int *major, int *minor)) \ 43 x(nvrtcResult, nvrtcCreateProgram, (nvrtcProgram *prog, const char *src, const char *name, int numHeaders, const char * const *headers, const char * const *includeNames)) \ 44 x(nvrtcResult, nvrtcDestroyProgram, (nvrtcProgram *prog)) \ 45 x(nvrtcResult, nvrtcCompileProgram, (nvrtcProgram prog, int numOptions, const char * const *options)) \ 46 x(nvrtcResult, nvrtcGetPTXSize, (nvrtcProgram prog, size_t *ptxSizeRet)) \ 47 x(nvrtcResult, nvrtcGetPTX, (nvrtcProgram prog, char *ptx)) \ 48 x(nvrtcResult, nvrtcGetProgramLogSize, (nvrtcProgram prog, size_t *logSizeRet)) \ 49 x(nvrtcResult, nvrtcGetProgramLog, (nvrtcProgram prog, char *log))\ 50 x(nvrtcResult, nvrtcAddNameExpression, (nvrtcProgram prog, const char * const name_expression)) \ 51 x(nvrtcResult, nvrtcGetLoweredName, (nvrtcProgram prog, const char *const name_expression, const char** lowered_name)) 52// clang-format on 53 54}// namespace nvrtc 55 56namespace Slang 57{ 58using namespace nvrtc ; 59 60static SlangResult _asResult (nvrtcResult res ) 61{ 62switch (res ) 63 { 64case NVRTC_SUCCESS : 65 { 66return SLANG_OK ; 67 } 68case NVRTC_ERROR_OUT_OF_MEMORY : 69 { 70return SLANG_E_OUT_OF_MEMORY ; 71 } 72case NVRTC_ERROR_PROGRAM_CREATION_FAILURE : 73case NVRTC_ERROR_INVALID_INPUT : 74case NVRTC_ERROR_INVALID_PROGRAM : 75 { 76return SLANG_FAIL ; 77 } 78case NVRTC_ERROR_INVALID_OPTION : 79 { 80return SLANG_E_INVALID_ARG ; 81 } 82case NVRTC_ERROR_COMPILATION : 83case NVRTC_ERROR_BUILTIN_OPERATION_FAILURE : 84case NVRTC_ERROR_NO_NAME_EXPRESSIONS_AFTER_COMPILATION : 85case NVRTC_ERROR_NO_LOWERED_NAMES_BEFORE_COMPILATION : 86case NVRTC_ERROR_NAME_EXPRESSION_NOT_VALID : 87 { 88return SLANG_FAIL ; 89 } 90case NVRTC_ERROR_INTERNAL_ERROR : 91 { 92return SLANG_E_INTERNAL_FAIL ; 93 } 94default : 95return SLANG_FAIL ; 96 } 97} 98 99class NVRTCDownstreamCompiler :public DownstreamCompilerBase 100{ 101public : 102typedef DownstreamCompilerBase Super ; 103 104// IDownstreamCompiler 105virtual SLANG_NO_THROW SlangResult SLANG_MCALL 106compile (const CompileOptions & options ,IArtifact ** outArtifact )SLANG_OVERRIDE ; 107virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased ()SLANG_OVERRIDE {return false; } 108virtual SLANG_NO_THROW bool SLANG_MCALL 109canConvert (const ArtifactDesc & from ,const ArtifactDesc & to )SLANG_OVERRIDE ; 110virtual SLANG_NO_THROW SlangResult SLANG_MCALL 111convert (IArtifact * from ,const ArtifactDesc & to ,IArtifact ** outArtifact )SLANG_OVERRIDE ; 112 113/// Must be called before use 114SlangResult init (ISlangSharedLibrary * library ); 115 116NVRTCDownstreamCompiler () {} 117 118protected : 119struct ScopeProgram 120 { 121ScopeProgram (NVRTCDownstreamCompiler * compiler ,nvrtcProgram program ) 122 :m_compiler (compiler ),m_program (program ) 123 { 124 } 125 ~ScopeProgram () {m_compiler -> m_nvrtcDestroyProgram (& m_program ); } 126NVRTCDownstreamCompiler * m_compiler ; 127nvrtcProgram m_program ; 128 }; 129 130SlangResult _findCUDAIncludePath (String & outPath ); 131SlangResult _getCUDAIncludePath (String & outIncludePath ); 132 133SlangResult _findOptixIncludePath (String & outIncludePath ); 134SlangResult _getOptixIncludePath (String & outIncludePath ); 135 136SlangResult _maybeAddHalfSupport (const CompileOptions & options ,CommandLine & ioCmdLine ); 137SlangResult _maybeAddOptixSupport (const CompileOptions & options ,CommandLine & ioCmdLine ); 138 139#define SLANG_NVTRC_MEMBER_FUNCS (ret ,name ,params ) ret(*m_##name) params; 140 141SLANG_NVRTC_FUNCS (SLANG_NVTRC_MEMBER_FUNCS ); 142 143// Holds list of paths passed in where cuda_fp16.h is found. Does *NOT* 144// include cuda_fp16.h. 145List < String > m_cudaFp16FoundPaths ; 146 147bool m_cudaIncludeSearched = false; 148// Holds location of where include (for cuda_fp16.h) is found. 149String m_cudaIncludePath ; 150 151// Holds list of paths passed in where optix.h is found. Does *NOT* include 152// optix.h. 153List < String > m_optixFoundPaths ; 154 155bool m_optixIncludeSearched = false; 156// Holds location of where include (for optix.h) is found. 157String m_optixIncludePath ; 158 159ComPtr < ISlangSharedLibrary > m_sharedLibrary ; 160}; 161 162#define SLANG_NVRTC_RETURN_ON_FAIL (x ) \ 163 { \ 164 nvrtcResult _res = x; \ 165 if (_res != NVRTC_SUCCESS) \ 166 return _asResult(_res); \ 167 } 168 169SlangResult NVRTCDownstreamCompiler ::init (ISlangSharedLibrary * library ) 170{ 171#define SLANG_NVTRC_GET_FUNC (ret ,name ,params ) \ 172 m_##name = (ret(*) params)library->findFuncByName(#name); \ 173 if (m_##name == nullptr) \ 174 return SLANG_FAIL; 175 176SLANG_NVRTC_FUNCS (SLANG_NVTRC_GET_FUNC ) 177 178m_sharedLibrary = library ; 179 180m_desc .type = SLANG_PASS_THROUGH_NVRTC ; 181 182int major ,minor ; 183m_nvrtcVersion (& major ,& minor ); 184m_desc .version .set (major ,minor ); 185return SLANG_OK ; 186} 187 188static SlangResult _parseLocation ( 189SliceAllocator & allocator , 190const UnownedStringSlice & in , 191ArtifactDiagnostic & outDiagnostic ) 192{ 193const Index startIndex = in .indexOf ('(' ); 194 195if (startIndex >=0 ) 196 { 197outDiagnostic .filePath = allocator .allocate (in .begin (),in .begin ()+ startIndex ); 198UnownedStringSlice remaining (in .begin ()+ startIndex + 1 ,in .end ()); 199const Int endIndex = remaining .indexOf (')' ); 200 201UnownedStringSlice lineText = 202UnownedStringSlice (remaining .begin (),remaining .begin ()+ endIndex ); 203 204Int line ; 205SLANG_RETURN_ON_FAIL (StringUtil ::parseInt (lineText ,line )); 206outDiagnostic .location .line = line ; 207 } 208else 209 { 210outDiagnostic .location .line = 0 ; 211outDiagnostic .filePath = allocator .allocate (in ); 212 } 213return SLANG_OK ; 214} 215 216static bool _isDriveLetter (char c ) 217{ 218return (c >='a' && c <='z' )|| (c >='A' && c <='Z' ); 219} 220 221static bool _hasDriveLetter (const UnownedStringSlice & line ) 222{ 223return line .getLength ()> 2 && line [1 ]== ':' && _isDriveLetter (line [0 ]); 224} 225 226static SlangResult _parseNVRTCLine ( 227SliceAllocator & allocator , 228const UnownedStringSlice & line , 229ArtifactDiagnostic & outDiagnostic ) 230{ 231typedef ArtifactDiagnostic Diagnostic ; 232typedef ArtifactDiagnostic ::Severity Severity ; 233 234outDiagnostic .stage = Diagnostic ::Stage ::Compile ; 235 236List < UnownedStringSlice > split ; 237if (_hasDriveLetter (line )) 238 { 239// The drive letter has :, which confuses things, so skip that and then fix 240// up first entry 241UnownedStringSlice lineWithoutDrive (line .begin ()+ 2 ,line .end ()); 242StringUtil ::split (lineWithoutDrive ,':' ,split ); 243split [0 ]= UnownedStringSlice (line .begin (),split [0 ].end ()); 244 } 245else 246 { 247StringUtil ::split (line ,':' ,split ); 248 } 249 250if (split .getCount () >=3 ) 251 { 252// tests/cuda/cuda-compile.cu(7): warning: variable "c" is used before its 253// value is set 254const auto split1 = split [1 ].trim (); 255 256Severity severity = Severity ::Unknown ; 257 258if (split1 == toSlice ("error" )|| split1 == toSlice ("catastrophic error" )) 259 { 260severity = Severity ::Error ; 261 } 262else if (split1 == toSlice ("warning" )) 263 { 264severity = Severity ::Warning ; 265 } 266else 267 { 268// Fall back position to try and determine if this really is some kind of 269// error/warning without succeeding when it's due to some other property 270// of the output diagnostics. 271// 272// Anything ending with " warning:" or " error:" in effect. 273 274// We can expand to include character after as this is split1, as must be 275// followed by at a minimum : (as the split has at least 3 parts). 276const UnownedStringSlice expandSplit1 (split1 .begin (),split1 .end ()+ 1 ); 277 278if (expandSplit1 .endsWith (toSlice (" error:" ))) 279 { 280severity = Severity ::Error ; 281 } 282else if (expandSplit1 .endsWith (toSlice (" warning:" ))) 283 { 284severity = Severity ::Warning ; 285 } 286 } 287 288if (severity != Severity ::Unknown ) 289 { 290// The text is everything following the : after the warning. 291UnownedStringSlice text (split [2 ].begin (),split .getLast ().end ()); 292 293// Trim whitespace at start and end 294text = text .trim (); 295 296// Set the diagnostic 297outDiagnostic .severity = severity ; 298outDiagnostic .text = allocator .allocate (text ); 299SLANG_RETURN_ON_FAIL (_parseLocation (allocator ,split [0 ],outDiagnostic )); 300 301return SLANG_OK ; 302 } 303 304// TODO(JS): Note here if it's not possible to determine a line as being the 305// main diagnostics we fall through to it potentially being a note. 306// 307// That could mean a valid diagnostic (from NVRTCs point of view) is 308// ignored/noted, because this code can't parse it. Ideally that situation 309// would lead to an error such that we can detect and things will fail. 310// 311// So we might want to revisit this determination in the future. 312 } 313 314// There isn't a diagnostic on this line 315if (line .getLength ()== 0 || line .trim ().getLength ()== 0 ) 316 { 317return SLANG_E_NOT_FOUND ; 318 } 319 320// We'll assume it's info, associated with a previous line 321outDiagnostic .severity = Severity ::Info ; 322outDiagnostic .text = allocator .allocate (line ); 323 324return SLANG_OK ; 325} 326 327/* An implementation of Path::Visitor that can be used for finding NVRTC shared 328* library installations. */ 329struct NVRTCPathVisitor :Path ::Visitor 330{ 331struct Candidate 332 { 333typedef Candidate ThisType ; 334 335bool operator== (const ThisType & rhs )const 336 { 337return path == rhs .path && version == rhs .version ; 338 } 339bool operator!= (const ThisType & rhs )const {return !(* this == rhs ); } 340 341static Candidate make (const String & path ,const SemanticVersion & version ) 342 { 343Candidate can ; 344can .version = version ; 345can .path = path ; 346return can ; 347 } 348String path ; 349SemanticVersion version ; 350 }; 351 352Index findVersion (const SemanticVersion & version )const 353 { 354const Index count = m_candidates .getCount (); 355for (Index i = 0 ;i < count ;++ i ) 356 { 357if (m_candidates [i ].version == version ) 358 { 359return i ; 360 } 361 } 362return -1 ; 363 } 364 365static bool _orderCandiate (const Candidate & a ,const Candidate & b ) 366 { 367return a .version < b .version ; 368 } 369void sortCandidates () {m_candidates .sort (_orderCandiate ); } 370 371#if SLANG_WINDOWS_FAMILY 372SlangResult getVersion (const UnownedStringSlice & filename ,SemanticVersion & outVersion ) 373 { 374// Versions on windows of the form 375// nvrtc64_110_2.dll 376// 11 - Major 377// 0 Minor 378// 2 Patch 379Index endIndex = filename .indexOf ('.' ); 380endIndex = (endIndex < 0 ) ?filename .getLength () :endIndex ; 381 382// If we have a version slice, split it 383UnownedStringSlice versionSlice = UnownedStringSlice ( 384filename .begin ()+ m_prefix .getLength (), 385filename .begin ()+ endIndex ); 386 387if (versionSlice .getLength () <=0 ) 388 { 389return SLANG_E_NOT_FOUND ; 390 } 391Int patch = 0 ; 392UnownedStringSlice majorMinorSlice ; 393 { 394List < UnownedStringSlice > slices ; 395StringUtil ::split (versionSlice ,'_' ,slices ); 396if (slices .getCount () >=2 ) 397 { 398// We don't bother checking for error here, if it's not parsable, it 399// will be 0 400StringUtil ::parseInt (slices [1 ],patch ); 401 } 402majorMinorSlice = slices [0 ]; 403 } 404 405if (majorMinorSlice .getLength ()< 2 ) 406 { 407// Must be a major and minor 408return SLANG_FAIL ; 409 } 410 411UnownedStringSlice majorSlice = majorMinorSlice .head (majorMinorSlice .getLength ()- 1 ); 412UnownedStringSlice minorSlice = 413majorMinorSlice .subString (majorMinorSlice .getLength ()- 1 ,1 ); 414 415Int major ; 416Int minor ; 417 418SLANG_RETURN_ON_FAIL (StringUtil ::parseInt (majorSlice ,major )); 419SLANG_RETURN_ON_FAIL (StringUtil ::parseInt (minorSlice ,minor )); 420 421outVersion = SemanticVersion (int (major ),int (minor ),int (patch )); 422return SLANG_OK ; 423 } 424#else 425// How the path is constructed depends on platform 426// https://docs.nvidia.com/cuda/nvrtc/index.html 427// TODO(JS): Handle version number depending on the platform - it's different 428// for Windows/OSX/Linux 429SlangResult getVersion (const UnownedStringSlice & filename ,SemanticVersion & outVersion ) 430 { 431SLANG_UNUSED (filename ); 432SLANG_UNUSED (outVersion ); 433return SLANG_E_NOT_IMPLEMENTED ; 434 } 435 436#endif 437 438void accept (Path ::Type type ,const UnownedStringSlice & filename )SLANG_OVERRIDE 439 { 440// Lets make sure it start's with nvrtc, but not worry about case 441if (type == Path ::Type ::File ) 442 { 443// If there is a defined extension, make sure it has it 444if (m_postfix .getLength ()&& filename .getLength () >=m_postfix .getLength ()) 445 { 446// We test without case - really for windows 447UnownedStringSlice filenamePostfix = 448filename .tail (filename .getLength ()- m_postfix .getLength ()); 449if (!filenamePostfix .caseInsensitiveEquals (m_postfix .getUnownedSlice ())) 450 { 451return ; 452 } 453 } 454 455if (filename .getLength () >=m_prefix .getLength ()&& 456filename .subString (0 ,m_prefix .getLength ()) 457 .caseInsensitiveEquals (m_prefix .getUnownedSlice ())) 458 { 459SemanticVersion version ; 460// If it produces an error, just use 0.0.0 461if (SLANG_FAILED (getVersion (filename ,version ))) 462 { 463version = SemanticVersion (); 464 } 465 466// We may want to add multiple versions, if they are in different 467// locations - as there may be multiple entries in the PATH, and only 468// one works. We'll only know which works by loading 469 470#if 0 471// We already found this version, so let's not add it again 472if (findVersion (version ) >=0 ) 473 { 474return ; 475 } 476#endif 477 478// Strip to make a shared library name 479UnownedStringSlice sharedLibraryName = 480filename .tail (m_prefix .getLength ()- m_sharedLibraryStem .getLength ()); 481sharedLibraryName = filename .head (filename .getLength ()- m_postfix .getLength ()); 482 483auto candidate = 484Candidate ::make (Path ::combine (m_basePath ,sharedLibraryName ),version ); 485 486// If we already have this candidate, then skip 487if (m_candidates .indexOf (candidate ) >=0 ) 488 { 489return ; 490 } 491 492// Add to the list of candidates 493m_candidates .add (candidate ); 494 } 495 } 496 } 497 498SlangResult findInDirectory (const String & path ) 499 { 500m_basePath = path ; 501return Path ::find (path ,nullptr ,this ); 502 } 503 504bool hasCandidates ()const {return m_candidates .getCount ()> 0 ; } 505 506NVRTCPathVisitor (const UnownedStringSlice & sharedLibraryStem ) 507 :m_sharedLibraryStem (sharedLibraryStem ) 508 { 509// Work out the prefix and postfix of the shader 510StringBuilder buf ; 511SharedLibrary ::appendPlatformFileName (sharedLibraryStem ,buf ); 512const Index index = buf .indexOf (sharedLibraryStem ); 513SLANG_ASSERT (index >=0 ); 514 515m_prefix = buf .getUnownedSlice ().head (index + sharedLibraryStem .getLength ()); 516m_postfix = buf .getUnownedSlice ().tail (index + sharedLibraryStem .getLength ()); 517 } 518 519String m_prefix ; 520String m_postfix ; 521String m_basePath ; 522String m_sharedLibraryStem ; 523 524List < Candidate > m_candidates ; 525}; 526 527template < typename T > 528SLANG_FORCE_INLINE static void _unusedFunction (const T & func ) 529{ 530SLANG_UNUSED (func ); 531} 532 533#define SLANG_UNUSED_FUNCTION (x ) _unusedFunction(x) 534 535static UnownedStringSlice _getNVRTCBaseName () 536{ 537#if SLANG_WINDOWS_FAMILY && SLANG_PTR_IS_64 538return UnownedStringSlice ::fromLiteral ("nvrtc64_" ); 539#else 540return UnownedStringSlice ::fromLiteral ("nvrtc" ); 541#endif 542} 543 544// Candidates are in m_candidates list. Will be ordered from the oldest to 545// newest (in version number) 546static SlangResult _findNVRTC (NVRTCPathVisitor & visitor ) 547{ 548// First try the instance path (if supported on platform) 549 { 550StringBuilder instancePath ; 551if (SLANG_SUCCEEDED (PlatformUtil ::getInstancePath (instancePath ))) 552 { 553visitor .findInDirectory (instancePath ); 554 } 555 } 556 557// If we don't have a candidate, try CUDA_PATH 558if (!visitor .hasCandidates ()) 559 { 560StringBuilder buf ; 561if (SLANG_SUCCEEDED (PlatformUtil ::getEnvironmentVariable ( 562UnownedStringSlice ::fromLiteral ("CUDA_PATH" ), 563buf ))) 564 { 565// Look for candidates in the directory 566visitor .findInDirectory (Path ::combine (buf ,"bin" )); 567 } 568 } 569 570// If we haven't we go searching through PATH 571if (!visitor .hasCandidates ()) 572 { 573List < UnownedStringSlice > splitPath ; 574 575StringBuilder buf ; 576if (SLANG_SUCCEEDED ( 577PlatformUtil ::getEnvironmentVariable (UnownedStringSlice ::fromLiteral ("PATH" ),buf ))) 578 { 579// Split so we get individual paths 580List < UnownedStringSlice > paths ; 581StringUtil ::split (buf .getUnownedSlice (),';' ,paths ); 582 583// We use a pool to make sure we only check each path once 584StringSlicePool pool (StringSlicePool ::Style ::Empty ); 585 586// We are going to search the paths in order 587for (const auto & path :paths ) 588 { 589// PATH can have the same path multiple times. If we have already 590// searched this path, we don't need to again 591if (!pool .has (path )) 592 { 593pool .add (path ); 594 595Path ::split (path ,splitPath ); 596 597// We could search every path, but here we restrict to paths that look 598// like CUDA installations. It's a path that contains a CUDA directory 599// and has bin 600if (splitPath .indexOf ("CUDA" ) >=0 && 601splitPath [splitPath .getCount ()- 1 ].caseInsensitiveEquals ( 602UnownedStringSlice ::fromLiteral ("bin" ))) 603 { 604// Okay lets search it 605visitor .findInDirectory (path ); 606 } 607 } 608 } 609 } 610 } 611 612// Put into version order with oldest first. 613visitor .sortCandidates (); 614 615return SLANG_OK ; 616} 617 618static const UnownedStringSlice g_fp16HeaderName = UnownedStringSlice ::fromLiteral ("cuda_fp16.h" ); 619static const UnownedStringSlice g_optixHeaderName = UnownedStringSlice ::fromLiteral ("optix.h" ); 620 621SlangResult _findFileInIncludePath ( 622const String & path , 623const UnownedStringSlice & filename , 624String & outPath ) 625{ 626if (File ::exists (Path ::combine (path ,filename ))) 627 { 628outPath = path ; 629return SLANG_OK ; 630 } 631 632 { 633String includePath = Path ::combine (path ,"include" ); 634if (File ::exists (Path ::combine (includePath ,filename ))) 635 { 636outPath = includePath ; 637return SLANG_OK ; 638 } 639 } 640 641 { 642String cudaIncludePath = Path ::combine (path ,"CUDA/include" ); 643if (File ::exists (Path ::combine (cudaIncludePath ,filename ))) 644 { 645outPath = cudaIncludePath ; 646return SLANG_OK ; 647 } 648 } 649 650return SLANG_E_NOT_FOUND ; 651} 652 653SlangResult NVRTCDownstreamCompiler ::_findCUDAIncludePath (String & outPath ) 654{ 655outPath = String (); 656 657// Try looking up from a symbol. This will work as long as the nvrtc is loaded 658// somehow from a dll/sharedlibrary And the header is included from there 659 { 660String libPath = SharedLibraryUtils ::getSharedLibraryFileName ((void * )m_nvrtcCreateProgram ); 661if (libPath .getLength ()) 662 { 663String parentPath = Path ::getParentDirectory (libPath ); 664 665if (SLANG_SUCCEEDED (_findFileInIncludePath (parentPath ,g_fp16HeaderName ,outPath ))) 666 { 667return SLANG_OK ; 668 } 669 670// See if the shared library is in the SDK, as if so we know how to find 671// the includes 672// TODO(JS): 673// This directory structure is correct for windows perhaps could be 674// different elsewhere. 675 { 676List < UnownedStringSlice > pathSlices ; 677Path ::split (parentPath .getUnownedSlice (),pathSlices ); 678 679// This -2 split holds the version number. 680const auto pathSplitCount = pathSlices .getCount (); 681if (pathSplitCount >=3 && pathSlices [pathSplitCount - 1 ]== toSlice ("bin" )&& 682pathSlices [pathSplitCount - 3 ]== toSlice ("CUDA" )) 683 { 684// We want to make sure that one of these paths is CUDA... 685const auto sdkPath = Path ::getParentDirectory (parentPath ); 686 687if (SLANG_SUCCEEDED (_findFileInIncludePath (sdkPath ,g_fp16HeaderName ,outPath ))) 688 { 689return SLANG_OK ; 690 } 691 } 692 } 693 } 694 } 695 696// Try CUDA_PATH environment variable 697 { 698StringBuilder buf ; 699if (SLANG_SUCCEEDED (PlatformUtil ::getEnvironmentVariable ( 700UnownedStringSlice ::fromLiteral ("CUDA_PATH" ), 701buf ))) 702 { 703String includePath = Path ::combine (buf ,"include" ); 704 705if (File ::exists (Path ::combine (includePath ,g_fp16HeaderName ))) 706 { 707outPath = includePath ; 708return SLANG_OK ; 709 } 710 } 711 } 712 713#if SLANG_LINUX_FAMILY 714List < String > candidatePaths ; 715candidatePaths .add ("/usr/local/include" ); 716candidatePaths .add ("/usr/local/cuda/include" ); 717candidatePaths .add ("/usr/include" ); 718 719for (const String & includePath :candidatePaths ) 720 { 721if (File ::exists (Path ::combine (includePath ,g_fp16HeaderName ))) 722 { 723outPath = includePath ; 724return SLANG_OK ; 725 } 726 } 727#endif 728return SLANG_E_NOT_FOUND ; 729} 730 731SlangResult NVRTCDownstreamCompiler ::_getCUDAIncludePath (String & outPath ) 732{ 733if (!m_cudaIncludeSearched ) 734 { 735m_cudaIncludeSearched = true; 736 737SLANG_ASSERT (m_cudaIncludePath .getLength ()== 0 ); 738 739_findCUDAIncludePath (m_cudaIncludePath ); 740 } 741 742outPath = m_cudaIncludePath ; 743return m_cudaIncludePath .getLength () ?SLANG_OK :SLANG_E_NOT_FOUND ; 744} 745 746SlangResult NVRTCDownstreamCompiler ::_findOptixIncludePath (String & outPath ) 747{ 748outPath = String (); 749 750// First try to find OptiX headers in the local external/optix-dev/include 751// directory relative to the executable path 752 { 753StringBuilder instancePathBuilder ; 754if (SLANG_SUCCEEDED (PlatformUtil ::getInstancePath (instancePathBuilder ))) 755 { 756// Get executable path, then go up to project root 757// instancePathBuilder already contains the bin directory path 758// Executable is in build/Debug/bin or build/Release/bin 759// Go up 3 levels: bin -> Debug/Release -> build -> project root 760String binPath = instancePathBuilder ; 761String buildTypeDir = Path ::getParentDirectory (binPath ); 762String buildDir = Path ::getParentDirectory (buildTypeDir ); 763String projectRoot = Path ::getParentDirectory (buildDir ); 764 765String localOptixPath = 766Path ::combine (Path ::combine (projectRoot ,"external/optix-dev" ),"include" ); 767String optixHeader = Path ::combine (localOptixPath ,g_optixHeaderName ); 768 769if (File ::exists (optixHeader )) 770 { 771outPath = localOptixPath ; 772return SLANG_OK ; 773 } 774 } 775 } 776List < String > rootPaths ; 777 778#if SLANG_WINDOWS_FAMILY 779const char * searchPattern = "OptiX SDK *" ; 780StringBuilder builder ; 781if (SLANG_SUCCEEDED (PlatformUtil ::getEnvironmentVariable ( 782UnownedStringSlice ::fromLiteral ("PROGRAMDATA" ), 783builder ))) 784 { 785rootPaths .add (Path ::combine (builder ,"NVIDIA Corporation" )); 786 } 787#else 788const char * searchPattern = "NVIDIA-OptiX-SDK-*" ; 789StringBuilder builder ; 790if (SLANG_SUCCEEDED ( 791PlatformUtil ::getEnvironmentVariable (UnownedStringSlice ::fromLiteral ("HOME" ),builder ))) 792 { 793rootPaths .add (builder ); 794 } 795#endif 796 797struct OptixHeaders 798 { 799String path ; 800SemanticVersion version ; 801 }; 802 803// Visitor to find Optix headers. 804struct Visitor :public Path ::Visitor 805 { 806const String & rootPath ; 807List < OptixHeaders >& optixPaths ; 808Visitor (const String & rootPath ,List < OptixHeaders >& optixPaths ) 809 :rootPath (rootPath ),optixPaths (optixPaths ) 810 { 811 } 812void accept (Path ::Type type ,const UnownedStringSlice & path )SLANG_OVERRIDE 813 { 814if (type != Path ::Type ::Directory ) 815return ; 816 817OptixHeaders optixPath ; 818#if SLANG_WINDOWS_FAMILY 819// Paths are expected to look like ".\OptiX SDK X.X.X" 820auto versionString = path .subString (path .lastIndexOf (' ' )+ 1 ,path .getLength ()); 821#else 822// Paths are expected to look like "./NVIDIA-OptiX-SDK-X.X.X-suffix" 823auto versionString = path .subString (0 ,path .lastIndexOf ('-' )); 824versionString = 825versionString .subString (path .lastIndexOf ('-' )+ 1 ,versionString .getLength ()); 826#endif 827if (SLANG_SUCCEEDED (SemanticVersion ::parse (versionString ,'.' ,optixPath .version ))) 828 { 829optixPath .path = Path ::combine (Path ::combine (rootPath ,path ),"include" ); 830String optixHeader = Path ::combine (optixPath .path ,g_optixHeaderName ); 831if (File ::exists (optixHeader )) 832 { 833optixPaths .add (optixPath ); 834 } 835 } 836 } 837 }; 838 839List < OptixHeaders > optixPaths ; 840 841for (const String & rootPath :rootPaths ) 842 { 843Visitor visitor (rootPath ,optixPaths ); 844Path ::find (rootPath ,searchPattern ,& visitor ); 845 } 846 847// Find newest version 848const OptixHeaders * newest = nullptr ; 849for (Index i = 0 ;i < optixPaths .getCount ();++ i ) 850 { 851if (!newest || optixPaths [i ].version > newest -> version ) 852 { 853newest = & optixPaths [i ]; 854 } 855 } 856 857if (newest ) 858 { 859outPath = newest -> path ; 860return SLANG_OK ; 861 } 862 863return SLANG_E_NOT_FOUND ; 864} 865 866SlangResult NVRTCDownstreamCompiler ::_getOptixIncludePath (String & outPath ) 867{ 868if (!m_optixIncludeSearched ) 869 { 870m_optixIncludeSearched = true; 871 872SLANG_ASSERT (m_optixIncludePath .getLength ()== 0 ); 873 874_findOptixIncludePath (m_optixIncludePath ); 875 } 876 877outPath = m_optixIncludePath ; 878return m_optixIncludePath .getLength () ?SLANG_OK :SLANG_E_NOT_FOUND ; 879} 880 881SlangResult NVRTCDownstreamCompiler ::_maybeAddHalfSupport ( 882const DownstreamCompileOptions & options , 883CommandLine & ioCmdLine ) 884{ 885if ((options .flags & DownstreamCompileOptions ::Flag ::EnableFloat16 )== 0 ) 886 { 887return SLANG_OK ; 888 } 889 890// First check if we know if one of the include paths contains cuda_fp16.h 891for (const auto & includePath :options .includePaths ) 892 { 893if (m_cudaFp16FoundPaths .indexOf (includePath ) >=0 ) 894 { 895// Okay we have an include path that we know works. 896// Just need to enable HALF in prelude 897ioCmdLine .addArg ("-DSLANG_CUDA_ENABLE_HALF" ); 898return SLANG_OK ; 899 } 900 } 901 902// Let's see if one of the paths finds cuda_fp16.h 903for (const auto & curIncludePath :options .includePaths ) 904 { 905const String includePath = asString (curIncludePath ); 906const String checkPath = Path ::combine (includePath ,g_fp16HeaderName ); 907if (File ::exists (checkPath )) 908 { 909m_cudaFp16FoundPaths .add (includePath ); 910// Just need to enable HALF in prelude 911ioCmdLine .addArg ("-DSLANG_CUDA_ENABLE_HALF" ); 912return SLANG_OK ; 913 } 914 } 915 916String includePath ; 917SLANG_RETURN_ON_FAIL (_getCUDAIncludePath (includePath )); 918 919// Add the found include path 920ioCmdLine .addArg ("-I" ); 921ioCmdLine .addArg (includePath ); 922 923ioCmdLine .addArg ("-DSLANG_CUDA_ENABLE_HALF" ); 924 925return SLANG_OK ; 926} 927 928SlangResult NVRTCDownstreamCompiler ::_maybeAddOptixSupport ( 929const DownstreamCompileOptions & options , 930CommandLine & ioCmdLine ) 931{ 932// First check if we know if one of the include paths contains optix.h 933for (const auto & includePath :options .includePaths ) 934 { 935if (m_optixFoundPaths .indexOf (includePath ) >=0 ) 936 { 937// Okay we have an include path that we know works. 938// Just need to enable OptiX in prelude 939ioCmdLine .addArg ("-DSLANG_CUDA_ENABLE_OPTIX" ); 940return SLANG_OK ; 941 } 942 } 943 944// Let's see if one of the paths finds optix.h 945for (const auto & curIncludePath :options .includePaths ) 946 { 947const String includePath = asString (curIncludePath ); 948const String checkPath = Path ::combine (includePath ,g_optixHeaderName ); 949if (File ::exists (checkPath )) 950 { 951m_optixFoundPaths .add (includePath ); 952// Just need to enable OptiX in prelude 953ioCmdLine .addArg ("-DSLANG_CUDA_ENABLE_OPTIX" ); 954return SLANG_OK ; 955 } 956 } 957 958String includePath ; 959SLANG_RETURN_ON_FAIL (_getOptixIncludePath (includePath )); 960 961// Add the found include path 962ioCmdLine .addArg ("-I" ); 963ioCmdLine .addArg (includePath ); 964 965ioCmdLine .addArg ("-DSLANG_CUDA_ENABLE_OPTIX" ); 966 967return SLANG_OK ; 968} 969 970SlangResult NVRTCDownstreamCompiler ::compile ( 971const DownstreamCompileOptions & inOptions , 972IArtifact ** outArtifact ) 973{ 974if (!isVersionCompatible (inOptions )) 975 { 976// Not possible to compile with this version of the interface. 977return SLANG_E_NOT_IMPLEMENTED ; 978 } 979 980CompileOptions options = getCompatibleVersion (& inOptions ); 981 982// This compiler can only deal with a single artifact 983if (options .sourceArtifacts .count != 1 ) 984 { 985return SLANG_FAIL ; 986 } 987 988IArtifact * sourceArtifact = options .sourceArtifacts [0 ]; 989 990CommandLine cmdLine ; 991 992// --dopt option is only available in CUDA 11.7 and later 993bool hasDoptOption = m_desc .version >=SemanticVersion (11 ,7 ); 994 995switch (options .debugInfoType ) 996 { 997case DebugInfoType ::None : 998 { 999break ; 1000 } 1001default : 1002 { 1003cmdLine .addArg ("--device-debug" ); 1004if (hasDoptOption ) 1005 { 1006cmdLine .addArg ("--dopt=on" ); 1007 } 1008break ; 1009 } 1010case DebugInfoType ::Maximal : 1011 { 1012cmdLine .addArg ("--device-debug" ); 1013cmdLine .addArg ("--generate-line-info" ); 1014if (hasDoptOption ) 1015 { 1016cmdLine .addArg ("--dopt=on" ); 1017 } 1018break ; 1019 } 1020 } 1021 1022// Don't seem to have such a control, so ignore for now 1023// switch (options.optimizationLevel) 1024//{ 1025// default: break; 1026//} 1027 1028switch (options .floatingPointMode ) 1029 { 1030case FloatingPointMode ::Default : 1031break ; 1032case FloatingPointMode ::Precise : 1033 { 1034break ; 1035 } 1036case FloatingPointMode ::Fast : 1037 { 1038cmdLine .addArg ("--use_fast_math" ); 1039break ; 1040 } 1041 } 1042 1043// Add defines 1044for (const auto & define :options .defines ) 1045 { 1046StringBuilder builder ; 1047builder <<"-D" ; 1048builder <<asStringSlice (define .nameWithSig ); 1049if (define .value .count ) 1050 { 1051builder <<"=" <<asStringSlice (define .value ); 1052 } 1053 1054cmdLine .addArg (builder ); 1055 } 1056 1057// Add includes 1058for (const auto & include :options .includePaths ) 1059 { 1060cmdLine .addArg ("-I" ); 1061cmdLine .addArg (asString (include )); 1062 } 1063 1064SLANG_RETURN_ON_FAIL (_maybeAddHalfSupport (options ,cmdLine )); 1065 1066// Neither of these options are strictly required, for general use of nvrtc, 1067// but are enabled to make use withing Slang work more smoothly 1068 { 1069// Require c++17, the default at the time of writing, since we share 1070// some functionality between slang itself and the compiled code 1071cmdLine .addArg ("-std=c++17" ); 1072 1073// Disable all warnings 1074// This is arguably too much - but nvrtc does not appear to have a mechanism 1075// to switch off individual warnings. I tried the -Xcudafe mechanism but 1076// that does not appear to work for nvrtc 1077cmdLine .addArg ("-w" ); 1078 } 1079 1080 { 1081// The lowest supported CUDA architecture version supported 1082// by any version of NVRTC we support is `compute_30`. 1083// 1084SemanticVersion version (3 ); 1085 1086// Newer releases of NVRTC only support newer CUDA architectures. 1087if (m_desc .version .m_major > 12 || 1088 (m_desc .version .m_major == 12 && m_desc .version .m_minor >=8 )) 1089 { 1090// NVRTC 12.8+ warns about architectures prior to compute_75 being deprecated 1091// The exact warning message is: 1092// nvrtc 12.8: nvrtc: warning : Architectures prior to '<compute/sm>_75' are 1093// deprecated and may be removed in a future release 1094version = SemanticVersion (7 ,5 ); 1095 } 1096else if (m_desc .version .m_major == 12 ) 1097 { 1098// NVRTC 12.0 supports `compute_50` and up 1099version = SemanticVersion (5 ,0 ); 1100 } 1101else if (m_desc .version .m_major == 11 ) 1102 { 1103// NVRTC in CUDA 11 only supports `compute_35` and up 1104// (with everything before `compute_52` being deprecated). 1105version = SemanticVersion (3 ,5 ); 1106 } 1107 1108// If constructs used in the code to be compield require 1109// a higher architecture version than the minimum, then 1110// we will set the version to the highest version listed 1111// among the requirements. 1112// 1113for (const auto & capabilityVersion :options .requiredCapabilityVersions ) 1114 { 1115if (capabilityVersion .kind == DownstreamCompileOptions ::CapabilityVersion ::Kind ::CUDASM ) 1116 { 1117if (capabilityVersion .version > version ) 1118 { 1119version = capabilityVersion .version ; 1120 } 1121 } 1122 } 1123 1124StringBuilder builder ; 1125builder <<"-arch=compute_" ; 1126builder <<version .m_major ; 1127 1128SLANG_ASSERT (version .m_minor >=0 && version .m_minor <=9 ); 1129builder <<char ('0' + version .m_minor ); 1130 1131cmdLine .addArg (builder ); 1132 } 1133 1134List < const char *> headers ; 1135List < const char *> headerIncludeNames ; 1136 1137// If compiling for OptiX, we need to add the appropriate search paths to the 1138// command line. 1139// 1140if (options .pipelineType == PipelineType ::RayTracing ) 1141 { 1142SLANG_RETURN_ON_FAIL (_maybeAddOptixSupport (options ,cmdLine )); 1143 } 1144 1145// Add any compiler specific options 1146// NOTE! If these clash with any previously set options (as set via other 1147// flags) compilation might fail. 1148if (options .compilerSpecificArguments .count > 0 ) 1149 { 1150for (auto compilerSpecificArg :options .compilerSpecificArguments ) 1151 { 1152const char * const arg = compilerSpecificArg ; 1153cmdLine .addArg (arg ); 1154 } 1155 } 1156 1157SLANG_ASSERT (headers .getCount ()== headerIncludeNames .getCount ()); 1158 1159ComPtr < ISlangBlob > sourceBlob ; 1160SLANG_RETURN_ON_FAIL (sourceArtifact -> loadBlob (ArtifactKeep ::Yes ,sourceBlob .writeRef ())); 1161 1162auto sourcePath = ArtifactUtil ::findPath (sourceArtifact ); 1163 1164StringBuilder storage ; 1165auto sourceContents = SliceUtil ::toTerminatedCharSlice (storage ,sourceBlob ); 1166 1167nvrtcProgram program = nullptr ; 1168nvrtcResult res = m_nvrtcCreateProgram ( 1169& program , 1170sourceContents , 1171String (sourcePath ).getBuffer (), 1172 (int )headers .getCount (), 1173headers .getBuffer (), 1174headerIncludeNames .getBuffer ()); 1175if (res != NVRTC_SUCCESS ) 1176 { 1177return _asResult (res ); 1178 } 1179ScopeProgram scope (this ,program ); 1180 1181List < const char *> dstOptions ; 1182dstOptions .setCount (cmdLine .m_args .getCount ()); 1183for (Index i = 0 ;i < cmdLine .m_args .getCount ();++ i ) 1184 { 1185dstOptions [i ]= cmdLine .m_args [i ].getBuffer (); 1186 } 1187 1188res = m_nvrtcCompileProgram (program ,int (dstOptions .getCount ()),dstOptions .getBuffer ()); 1189 1190auto artifact = ArtifactUtil ::createArtifactForCompileTarget (options .targetType ); 1191auto diagnostics = ArtifactDiagnostics ::create (); 1192 1193ArtifactUtil ::addAssociated (artifact ,diagnostics ); 1194 1195ComPtr < ISlangBlob > blob ; 1196 1197diagnostics -> setResult (_asResult (res )); 1198 1199 { 1200String rawDiagnostics ; 1201 1202size_t logSize = 0 ; 1203SLANG_NVRTC_RETURN_ON_FAIL (m_nvrtcGetProgramLogSize (program ,& logSize )); 1204 1205if (logSize ) 1206 { 1207char * dst = rawDiagnostics .prepareForAppend (Index (logSize )); 1208SLANG_NVRTC_RETURN_ON_FAIL (m_nvrtcGetProgramLog (program ,dst )); 1209 1210// If there is a terminating zero remove it, as the rawDiagnostics 1211// string will already contain one. 1212logSize -= size_t (logSize > 0 && dst [logSize - 1 ]== 0 ); 1213 1214rawDiagnostics .appendInPlace (dst ,Index (logSize )); 1215 1216diagnostics -> setRaw (SliceUtil ::asCharSlice (rawDiagnostics )); 1217 } 1218 1219SliceAllocator allocator ; 1220 1221// Get all of the lines 1222List < UnownedStringSlice > lines ; 1223StringUtil ::calcLines (rawDiagnostics .getUnownedSlice (),lines ); 1224 1225// Remove any trailing empty lines 1226while (lines .getCount ()&& lines .getLast ().getLength ()== 0 ) 1227 { 1228lines .removeLast (); 1229 } 1230 1231// Find the index searching from last line, that is blank 1232// indicating the end of the output 1233Index lastIndex = lines .getCount (); 1234 1235// Look for the first blank line after this point. 1236// We'll assume any information after that blank line to the end of the 1237// diagnostic is compilation summary information. 1238for (Index i = lastIndex - 1 ;i >=0 ;-- i ) 1239 { 1240if (lines [i ].getLength ()== 0 ) 1241 { 1242lastIndex = i ; 1243break ; 1244 } 1245 } 1246 1247// Parse the diagnostics here 1248for (auto line :makeConstArrayView (lines .getBuffer (),lastIndex )) 1249 { 1250ArtifactDiagnostic diagnostic ; 1251SlangResult lineRes = _parseNVRTCLine (allocator ,line ,diagnostic ); 1252 1253if (SLANG_SUCCEEDED (lineRes )) 1254 { 1255// We only allow info diagnostics after a 'regular' diagnostic. 1256if (diagnostic .severity == ArtifactDiagnostic ::Severity ::Info && 1257diagnostics -> getCount ()== 0 ) 1258 { 1259continue ; 1260 } 1261 1262diagnostics -> add (diagnostic ); 1263 } 1264else if (lineRes != SLANG_E_NOT_FOUND ) 1265 { 1266// If there is an error exit 1267// But if SLANG_E_NOT_FOUND that just means this line couldn't be 1268// parsed, so ignore. 1269return lineRes ; 1270 } 1271 } 1272 1273// If it has a compilation error.. and there isn't already an error set 1274// set as failed. 1275if (SLANG_SUCCEEDED (diagnostics -> getResult ())&& 1276diagnostics -> hasOfAtLeastSeverity (ArtifactDiagnostic ::Severity ::Error )) 1277 { 1278diagnostics -> setResult (SLANG_FAIL ); 1279 } 1280 } 1281 1282if (res == nvrtc::NVRTC_SUCCESS ) 1283 { 1284// We should parse the log to set up the diagnostics 1285size_t ptxSize ; 1286SLANG_NVRTC_RETURN_ON_FAIL (m_nvrtcGetPTXSize (program ,& ptxSize )); 1287 1288List < uint8_t > ptx ; 1289ptx .setCount (Index (ptxSize )); 1290 1291SLANG_NVRTC_RETURN_ON_FAIL (m_nvrtcGetPTX (program , (char * )ptx .getBuffer ())); 1292 1293artifact -> addRepresentationUnknown (ListBlob ::moveCreate (ptx )); 1294 } 1295 1296* outArtifact = artifact .detach (); 1297return SLANG_OK ; 1298} 1299 1300bool NVRTCDownstreamCompiler ::canConvert (const ArtifactDesc & from ,const ArtifactDesc & to ) 1301{ 1302return ArtifactDescUtil ::isDisassembly (from ,to )|| ArtifactDescUtil ::isDisassembly (to ,from ); 1303} 1304 1305SlangResult NVRTCDownstreamCompiler ::convert ( 1306IArtifact * from , 1307const ArtifactDesc & to , 1308IArtifact ** outArtifact ) 1309{ 1310if (!canConvert (from -> getDesc (),to )) 1311 { 1312return SLANG_FAIL ; 1313 } 1314 1315// PTX is 'binary like' and 'assembly like' so we allow conversion either way 1316// We do it by just getting as a blob and sharing that blob. 1317// A more sophisticated implementation could proxy to the original artifact, 1318// but this is simpler, and probably fine in most scenarios. 1319ComPtr < ISlangBlob > blob ; 1320SLANG_RETURN_ON_FAIL (from -> loadBlob (ArtifactKeep ::Yes ,blob .writeRef ())); 1321 1322auto artifact = ArtifactUtil ::createArtifact (to ); 1323artifact -> addRepresentationUnknown (blob ); 1324 1325* outArtifact = artifact .detach (); 1326return SLANG_OK ; 1327} 1328 1329static SlangResult _findAndLoadNVRTC ( 1330ISlangSharedLibraryLoader * loader , 1331ComPtr < ISlangSharedLibrary >& outLibrary ) 1332{ 1333#if SLANG_WINDOWS_FAMILY && SLANG_PTR_IS_64 1334 1335// We only need to search 64 bit versions on windows 1336NVRTCPathVisitor visitor (_getNVRTCBaseName ()); 1337SLANG_RETURN_ON_FAIL (_findNVRTC (visitor )); 1338 1339// We want to start with the newest version... 1340for (Index i = visitor .m_candidates .getCount ()- 1 ;i >=0 ;-- i ) 1341 { 1342const auto & candidate = visitor .m_candidates [i ]; 1343if (SLANG_SUCCEEDED ( 1344loader -> loadSharedLibrary (candidate .path .getBuffer (),outLibrary .writeRef ()))) 1345 { 1346return SLANG_OK ; 1347 } 1348 } 1349 1350#else 1351SLANG_UNUSED (loader ); 1352SLANG_UNUSED (outLibrary ); 1353 1354SLANG_UNUSED_FUNCTION (_getNVRTCBaseName ); 1355SLANG_UNUSED_FUNCTION (_findNVRTC ); 1356#endif 1357 1358// This is an official-ish list of versions is here: 1359// https://developer.nvidia.com/cuda-toolkit-archive 1360 1361// Filenames for NVRTC 1362// https://docs.nvidia.com/cuda/nvrtc/index.html 1363// 1364// From this it appears on platforms other than windows the SharedLibrary name 1365// should be nvrtc which is already tried, so we can give up now. 1366return SLANG_E_NOT_FOUND ; 1367} 1368 1369/* static */ SlangResult NVRTCDownstreamCompilerUtil ::locateCompilers ( 1370const String & path , 1371ISlangSharedLibraryLoader * loader , 1372DownstreamCompilerSet * set ) 1373{ 1374ComPtr < ISlangSharedLibrary > library ; 1375 1376// If the user supplies a path to their preferred version of NVRTC, 1377// we just use this. 1378if (path .getLength ()!= 0 ) 1379 { 1380SLANG_RETURN_ON_FAIL (loader -> loadSharedLibrary (path .getBuffer (),library .writeRef ())); 1381 } 1382else 1383 { 1384// As a catch-all for non-Windows platforms, we search for 1385// a library simply named `nvrtc` (well, `libnvrtc`) which 1386// is expected to match whatever the user has installed. 1387// 1388// On Windows an installation could place the version of nvrtc it uses in 1389// the same directory as the slang binary, such that it's loaded. Using this 1390// name also allows a ISlangSharedLibraryLoader to easily identify what is 1391// required and perhaps load a specific version 1392if (SLANG_FAILED (loader -> loadSharedLibrary ("nvrtc" ,library .writeRef ()))) 1393 { 1394// Try something more sophisticated to locate NVRTC 1395SLANG_RETURN_ON_FAIL (_findAndLoadNVRTC (loader ,library )); 1396 } 1397 } 1398 1399SLANG_ASSERT (library ); 1400if (!library ) 1401 { 1402return SLANG_FAIL ; 1403 } 1404 1405auto compiler = new NVRTCDownstreamCompiler ; 1406ComPtr < IDownstreamCompiler > compilerIntf (compiler ); 1407SLANG_RETURN_ON_FAIL (compiler -> init (library )); 1408 1409set -> addCompiler (compilerIntf ); 1410return SLANG_OK ; 1411} 1412 1413}// namespace Slang