yum-slop/modular_slang
write individual HLSL modules/libraries in slang
git clone https://git.yummers.dev/yum-slop/modular_slang
30c5fa6
master
1#include <array> 2#include <filesystem> 3#include <cctype> 4#include <fstream> 5#include <iomanip> 6#include <iterator> 7#include <iostream> 8#include <limits> 9#include <sstream> 10#include <string> 11#include <string_view> 12#include <unordered_set> 13#include <vector> 14#include <utility> 15 16#include "absl/status/status.h" 17#include "absl/status/statusor.h" 18 19#include <slang.h> 20#include <slang-com-ptr.h> 21 22namespace fs= std::filesystem; 23 24using ::slang::CompilerOptionEntry ; 25using ::slang::CompilerOptionName ; 26using ::slang::createGlobalSession ; 27using ::slang::DeclReflection ; 28using ::slang::FunctionReflection ; 29using ::slang::IBlob ; 30using ::slang::ICompileRequest ; 31using ::slang::IGlobalSession ; 32using ::slang::IModule ; 33using ::slang::ISession ; 34using ::slang::SessionDesc ; 35using ::slang::TargetDesc ; 36 37template < typename T > 38using ComPtr = ::Slang ::ComPtr < T > ; 39 40// Print any diagnostics carried by a Slang blob with optional context information. 41void printDiagnostics (const char * context ,IBlob * diagnostics ) { 42if (!diagnostics ) { 43return ; 44 } 45 46 std::size_t size = diagnostics -> getBufferSize (); 47if (size == 0 ) { 48return ; 49 } 50 51 std::string_view text (static_cast < const char *> (diagnostics -> getBufferPointer ()),size ); 52if (!text .empty ()&& text .back ()== '\0' ) { 53text .remove_suffix (1 ); 54 } 55 56if (text .empty ()) { 57return ; 58 } 59 60if (context && * context ) { 61 std::cerr <<context <<" diagnostics:" << std::endl ; 62 } 63 64 std::cerr .write (text .data (),text .size ()); 65if (text .back ()!= '\n' ) { 66 std::cerr << std::endl ; 67 } 68} 69 70// Helper to convert Slang API results into absl::Status values. 71absl::Status checkSlangResult (const char * context ,SlangResult res ,IBlob * diagnostics = nullptr ) { 72printDiagnostics (context ,diagnostics ); 73 74if (SLANG_FAILED (res )) { 75 std::ostringstream message ; 76message << (context && * context ?context :"Slang call" ) 77 <<" failed with SlangResult " <<res 78 <<" (0x" << std::hex <<res << std::dec <<')' ; 79return absl::InternalError (message .str ()); 80 } 81 82return absl::OkStatus (); 83} 84 85absl::Status writeTextFile (const fs::path & path , std::string_view contents ) { 86 std::ofstream file (path , std::ios::binary ); 87if (!file ) { 88 std::ostringstream msg ; 89msg <<"Failed to open " <<path <<" for writing." ; 90return absl::InternalError (msg .str ()); 91 } 92 93file .write (contents .data (),static_cast < std::streamsize > (contents .size ())); 94file .close (); 95 96if (!file ) { 97 std::ostringstream msg ; 98msg <<"Failed to write " <<path ; 99return absl::InternalError (msg .str ()); 100 } 101 102return absl::OkStatus (); 103} 104 105void addCompilerOption (std::vector < CompilerOptionEntry >& options ,CompilerOptionName name ) { 106CompilerOptionEntry entry = {}; 107entry .name = name ; 108entry .value .intValue0 = 1 ; 109options .push_back (entry ); 110} 111 112struct FunctionInfo { 113 std::string name ; 114}; 115 116struct IncludeGuardInfo { 117bool present = false; 118 std::string macro ; 119 std::string ifndefLine ; 120 std::string defineLine ; 121 std::string endifLine ; 122}; 123 124struct ModuleRequest { 125 fs::path modulePath ; 126 std::string moduleName ; 127 std::string searchPath ; 128 fs::path outputPath ; 129}; 130 131std::string trim (std::string_view text ) { 132 std::size_t start = 0 ; 133 std::size_t end = text .size (); 134 135while (start < end && std::isspace (static_cast < unsigned char > (text [start ]))) { 136++ start ; 137 } 138 139while (end > start && std::isspace (static_cast < unsigned char > (text [end - 1 ]))) { 140-- end ; 141 } 142 143return std::string (text .substr (start ,end - start )); 144} 145 146bool isTopLevelFunction (DeclReflection * functionDecl ) { 147if (!functionDecl ) { 148return false; 149 } 150 151using Kind = DeclReflection ::Kind ; 152for (DeclReflection * parent = functionDecl -> getParent ();parent ; 153parent = parent -> getParent ()) { 154switch (parent -> getKind ()) { 155case Kind ::Module : 156case Kind ::Namespace : 157return true; 158case Kind ::Generic : 159continue ; 160default : 161return false; 162 } 163 } 164 165return false; 166} 167 168 169std::unordered_set < std::string > findPublicFunctionNames (const fs::path & sourcePath ) { 170 std::unordered_set < std::string > names ; 171 172 std::ifstream input (sourcePath , std::ios::binary ); 173if (!input ) { 174return names ; 175 } 176 177 std::string source ((std::istreambuf_iterator < char > (input )), std::istreambuf_iterator < char > ()); 178const std::size_t length = source .size (); 179 180 std::size_t index = 0 ; 181bool publicPending = false; 182 std::string candidate ; 183int templateDepth = 0 ; 184 185while (index < length ) { 186char c = source [index ]; 187 188if (c == '/' && index + 1 < length ) { 189char next = source [index + 1 ]; 190if (next == '/' ) { 191index += 2 ; 192while (index < length && source [index ]!= '\n' ) { 193++ index ; 194 } 195continue ; 196 } 197if (next == '*' ) { 198index += 2 ; 199while (index + 1 < length && !(source [index ]== '*' && source [index + 1 ]== '/' )) { 200++ index ; 201 } 202if (index + 1 < length ) { 203index += 2 ; 204 } 205continue ; 206 } 207 } 208 209if (c == '"' || c == '\'' ) { 210char quote = c ; 211++ index ; 212while (index < length ) { 213char current = source [index ]; 214if (current == '\\' ) { 215index += 2 ; 216continue ; 217 } 218if (current == quote ) { 219++ index ; 220break ; 221 } 222++ index ; 223 } 224continue ; 225 } 226 227if (std::isalpha (static_cast < unsigned char > (c ))|| c == '_' ) { 228 std::size_t startToken = index ; 229++ index ; 230while (index < length ) { 231char ch = source [index ]; 232if (std::isalnum (static_cast < unsigned char > (ch ))|| ch == '_' ) { 233++ index ; 234 }else { 235break ; 236 } 237 } 238 std::string token = source .substr (startToken ,index - startToken ); 239if (token == "public" ) { 240publicPending = true; 241candidate .clear (); 242templateDepth = 0 ; 243 }else if (publicPending && templateDepth == 0 ) { 244candidate = token ; 245 } 246continue ; 247 } 248 249if (publicPending ) { 250if (c == '<' ) { 251++ templateDepth ; 252++ index ; 253continue ; 254 } 255if (c == '>' ) { 256if (templateDepth > 0 ) { 257-- templateDepth ; 258 } 259++ index ; 260continue ; 261 } 262if (c == '(' ) { 263if (!candidate .empty ()&& templateDepth == 0 ) { 264names .insert (candidate ); 265 } 266publicPending = false; 267candidate .clear (); 268templateDepth = 0 ; 269++ index ; 270continue ; 271 } 272if (c == ';' || c == '{' || c == '}' ) { 273publicPending = false; 274candidate .clear (); 275templateDepth = 0 ; 276++ index ; 277continue ; 278 } 279 } 280 281++ index ; 282 } 283 284return names ; 285} 286 287 288 289// Recursively gather function declarations defined in the supplied Slang module. 290void collectFunctionInfos ( 291DeclReflection * decl , 292const std::unordered_set < std::string >& publicFunctions , 293 std::vector < FunctionInfo >& functions , 294 std::unordered_set < std::string >& seenNames ) { 295if (!decl ) { 296return ; 297 } 298 299using Kind = DeclReflection ::Kind ; 300 301switch (decl -> getKind ()) { 302case Kind ::Func : 303if (auto * functionReflection = decl -> asFunction ()) { 304if (const char * name = functionReflection -> getName ()) { 305bool isPublic = publicFunctions .find (name )!= publicFunctions .end (); 306 307if (* name && isPublic && seenNames .insert (name ).second && isTopLevelFunction (decl )) { 308 std::cerr <<"Discovered entry point: " <<name << std::endl ; 309functions .push_back ({name }); 310 } 311 } 312 } 313break ; 314case Kind ::Generic : 315if (auto * genericDecl = decl -> asGeneric ()) { 316collectFunctionInfos ( 317genericDecl -> getInnerDecl (), 318publicFunctions , 319functions , 320seenNames ); 321 } 322break ; 323default : 324break ; 325 } 326 327for (auto * child :decl -> getChildren ()) { 328collectFunctionInfos (child ,publicFunctions ,functions ,seenNames ); 329 } 330} 331 332 333IncludeGuardInfo detectIncludeGuard (const fs::path & sourcePath ) { 334IncludeGuardInfo info ; 335 336 std::ifstream input (sourcePath ); 337if (!input ) { 338return info ; 339 } 340 341 std::vector < std::string > lines ; 342 std::string line ; 343while (std::getline (input ,line )) { 344lines .push_back (line ); 345 } 346 347 std::size_t ifndefIndex = std::numeric_limits < std::size_t > ::max (); 348for (std::size_t i = 0 ;i < lines .size ();++ i ) { 349 std::string trimmed = trim (lines [i ]); 350if (trimmed .rfind ("#ifndef" ,0 )== 0 ) { 351 std::istringstream stream (trimmed ); 352 std::string directive ; 353 std::string macro ; 354stream >>directive >>macro ; 355if (!macro .empty ()) { 356info .macro = macro ; 357info .ifndefLine = lines [i ]; 358ifndefIndex = i ; 359 } 360break ; 361 } 362 } 363 364if (info .macro .empty ()) { 365return info ; 366 } 367 368for (std::size_t i = ifndefIndex + 1 ;i < lines .size ();++ i ) { 369 std::string trimmed = trim (lines [i ]); 370if (trimmed .rfind ("#define" ,0 )== 0 ) { 371 std::istringstream stream (trimmed ); 372 std::string directive ; 373 std::string macro ; 374stream >>directive >>macro ; 375if (macro == info .macro ) { 376info .defineLine = lines [i ]; 377break ; 378 } 379 } 380 } 381 382if (info .defineLine .empty ()) { 383info = IncludeGuardInfo {}; 384return info ; 385 } 386 387for (std::size_t i = lines .size ();i -- > 0 ;) { 388 std::string trimmed = trim (lines [i ]); 389if (trimmed .rfind ("#endif" ,0 )== 0 ) { 390info .endifLine = lines [i ]; 391break ; 392 } 393 } 394 395if (info .endifLine .empty ()) { 396info = IncludeGuardInfo {}; 397return info ; 398 } 399 400info .present = true; 401return info ; 402} 403 404absl::StatusOr < ModuleRequest > parseModuleRequest (int argc ,char ** argv ) { 405const char * programName = (argc > 0 && argv ) ?argv [0 ] :"modular_slang" ; 406 407if (argc < 2 || !argv ) { 408 std::ostringstream usage ; 409usage <<"Usage: " <<programName <<" <module.slang>" ; 410return absl::InvalidArgumentError (usage .str ()); 411 } 412 413ModuleRequest request ; 414request .modulePath = fs::absolute (argv [1 ]); 415 416if (!fs::exists (request .modulePath )) { 417 std::ostringstream msg ; 418msg <<"Module not found: " <<request .modulePath ; 419return absl::NotFoundError (msg .str ()); 420 } 421 422if (request .modulePath .extension ()!= ".slang" ) { 423 std::ostringstream msg ; 424msg <<"Expected a .slang file: " <<request .modulePath ; 425return absl::InvalidArgumentError (msg .str ()); 426 } 427 428request .moduleName = request .modulePath .stem ().string (); 429request .searchPath = request .modulePath .has_parent_path () 430 ?request .modulePath .parent_path ().string () 431 : fs::current_path ().string (); 432request .outputPath = request .modulePath ; 433request .outputPath .replace_extension (".hlsl" ); 434 435return request ; 436} 437 438std::vector < CompilerOptionEntry > makeCommonOptions () { 439 std::vector < CompilerOptionEntry > options ; 440addCompilerOption (options ,CompilerOptionName ::DisableNonEssentialValidations ); 441addCompilerOption (options ,CompilerOptionName ::NoHLSLBinding ); 442addCompilerOption (options ,CompilerOptionName ::NoMangle ); 443addCompilerOption (options ,CompilerOptionName ::NoHLSLPackConstantBufferElements ); 444addCompilerOption (options ,CompilerOptionName ::PlainFunctionEntryPoints ); 445return options ; 446} 447 448void configureTargetDesc ( 449IGlobalSession * globalSession , 450 std::vector < CompilerOptionEntry >& targetOptions , 451TargetDesc & outDesc ) { 452outDesc = {}; 453outDesc .format = SLANG_HLSL ; 454outDesc .profile = globalSession -> findProfile ("lib_6_6" ); 455outDesc .flags = SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM ; 456outDesc .compilerOptionEntries = targetOptions .data (); 457outDesc .compilerOptionEntryCount = static_cast < uint32_t > (targetOptions .size ()); 458} 459 460void configureSessionDesc ( 461const TargetDesc & targetDesc , 462const ModuleRequest & request , 463 std::vector < CompilerOptionEntry >& sessionOptions , 464 std::array < const char * ,1 >& searchPathStorage , 465SessionDesc & outDesc ) { 466searchPathStorage [0 ]= request .searchPath .c_str (); 467 468outDesc = {}; 469outDesc .targets = & targetDesc ; 470outDesc .targetCount = 1 ; 471outDesc .searchPaths = searchPathStorage .data (); 472outDesc .searchPathCount = static_cast < uint32_t > (searchPathStorage .size ()); 473outDesc .compilerOptionEntries = sessionOptions .data (); 474outDesc .compilerOptionEntryCount = static_cast < uint32_t > (sessionOptions .size ()); 475} 476 477absl::StatusOr < ComPtr < IModule >> loadSlangModule (ISession * session ,const std::string & moduleName ) { 478ComPtr < IModule > module ; 479ComPtr < IBlob > diagnostics ; 480module = session -> loadModule (moduleName .c_str (),diagnostics .writeRef ()); 481 482const std::string context = "loadModule: " + moduleName ; 483printDiagnostics (context .c_str (),diagnostics ); 484 485if (!module ) { 486 std::ostringstream msg ; 487msg <<"Failed to load module '" <<moduleName <<"'." ; 488return absl::InternalError (msg .str ()); 489 } 490 491return module ; 492} 493 494struct EntryPointsResult { 495 std::vector < FunctionInfo > functions ; 496 std::unordered_set < std::string > publicFunctions ; 497}; 498 499absl::StatusOr < EntryPointsResult > collectEntryPoints ( 500IModule * module , 501const std::string & moduleName , 502const fs::path & sourcePath ) { 503 std::vector < FunctionInfo > functions ; 504 std::unordered_set < std::string > seenNames ; 505 506 std::unordered_set < std::string > publicFunctions = findPublicFunctionNames (sourcePath ); 507if (publicFunctions .empty ()) { 508 std::ostringstream msg ; 509msg <<"No public functions found in " <<sourcePath .string () <<'.' ; 510return absl::NotFoundError (msg .str ()); 511 } 512 513DeclReflection * moduleReflection = module ?module -> getModuleReflection () :nullptr ; 514if (!moduleReflection ) { 515 std::ostringstream msg ; 516msg <<"Failed to retrieve reflection data for module '" 517 <<moduleName <<"'." ; 518return absl::InternalError (msg .str ()); 519 } 520 521collectFunctionInfos (moduleReflection ,publicFunctions ,functions ,seenNames ); 522 523if (functions .empty ()) { 524 std::ostringstream msg ; 525msg <<"No public functions found in module '" <<moduleName <<"'." ; 526return absl::NotFoundError (msg .str ()); 527 } 528 529return EntryPointsResult {functions ,publicFunctions }; 530} 531 532absl::StatusOr < ComPtr < ICompileRequest >> createCompileRequest ( 533ISession * session , 534const ModuleRequest & request , 535const TargetDesc & targetDesc , 536const std::vector < FunctionInfo >& functions ) { 537ComPtr < ICompileRequest > compileRequest ; 538if (absl::Status status = checkSlangResult ( 539"ISession::createCompileRequest" , 540session -> createCompileRequest (compileRequest .writeRef ())); 541 !status .ok ()) { 542return status ; 543 } 544 545compileRequest -> setCodeGenTarget (SLANG_HLSL ); 546compileRequest -> setTargetProfile (0 ,targetDesc .profile ); 547compileRequest -> setTargetFlags (0 ,SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM ); 548compileRequest -> setMatrixLayoutMode (SLANG_MATRIX_LAYOUT_ROW_MAJOR ); 549compileRequest -> setLineDirectiveMode (SLANG_LINE_DIRECTIVE_MODE_NONE ); 550 551compileRequest -> addSearchPath (request .searchPath .c_str ()); 552 553const int translationUnitIndex = compileRequest -> addTranslationUnit ( 554SLANG_SOURCE_LANGUAGE_SLANG , 555request .moduleName .c_str ()); 556compileRequest -> addTranslationUnitSourceFile ( 557translationUnitIndex , 558request .modulePath .string ().c_str ()); 559 560for (const FunctionInfo & func :functions ) { 561const int entryPointIndex = compileRequest -> addEntryPoint ( 562translationUnitIndex , 563func .name .c_str (), 564SLANG_STAGE_DISPATCH ); 565if (entryPointIndex < 0 ) { 566 std::ostringstream msg ; 567msg <<"Failed to register entry point '" <<func .name <<"'." ; 568return absl::InternalError (msg .str ()); 569 } 570 } 571 572return compileRequest ; 573} 574 575absl::StatusOr < std::string > collectGeneratedHlsl (ICompileRequest * compileRequest ,const std::string & moduleName ) { 576SlangResult compileResult = compileRequest -> compile (); 577ComPtr < IBlob > diagnostics ; 578compileRequest -> getDiagnosticOutputBlob (diagnostics .writeRef ()); 579if (absl::Status status = checkSlangResult ( 580"ICompileRequest::compile" ,compileResult ,diagnostics .get ()); 581 !status .ok ()) { 582return status ; 583 } 584 585ComPtr < IBlob > targetCodeBlob ; 586if (absl::Status status = checkSlangResult ( 587"ICompileRequest::getTargetCodeBlob" , 588compileRequest -> getTargetCodeBlob (0 ,targetCodeBlob .writeRef ())); 589 !status .ok ()) { 590return status ; 591 } 592 593if (!targetCodeBlob || targetCodeBlob -> getBufferSize ()== 0 ) { 594 std::ostringstream msg ; 595msg <<"No HLSL was generated for module '" <<moduleName <<"'." ; 596return absl::InternalError (msg .str ()); 597 } 598 599return std::string ( 600static_cast < const char *> (targetCodeBlob -> getBufferPointer ()), 601static_cast < std::size_t > (targetCodeBlob -> getBufferSize ())); 602} 603 604std::string removeNvapiInclude (std::string hlslSource ) { 605const std::string guardToken = "#ifdef SLANG_HLSL_ENABLE_NVAPI" ; 606const std::string endifToken = "#endif" ; 607 608 std::size_t searchPos = 0 ; 609while (true) { 610const std::size_t blockStart = hlslSource .find (guardToken ,searchPos ); 611if (blockStart == std::string::npos ) { 612break ; 613 } 614 615 std::size_t blockEnd = hlslSource .find (endifToken ,blockStart ); 616if (blockEnd == std::string::npos ) { 617break ; 618 } 619blockEnd += endifToken .size (); 620 621while (blockEnd < hlslSource .size ()&& 622 (hlslSource [blockEnd ]== '\r' || hlslSource [blockEnd ]== '\n' )) { 623++ blockEnd ; 624 } 625 626hlslSource .erase (blockStart ,blockEnd - blockStart ); 627searchPos = blockStart ; 628 } 629 630return hlslSource ; 631} 632 633 634std::string applyIncludeGuard (const std::string & hlslSource ,const IncludeGuardInfo & includeGuard ) { 635if (!includeGuard .present ) { 636return hlslSource ; 637 } 638 639const std::string guardIfndefToken = "#ifndef " + includeGuard .macro ; 640const std::string guardDefineToken = "#define " + includeGuard .macro ; 641const bool alreadyGuarded = 642hlslSource .find (guardIfndefToken )!= std::string::npos && 643hlslSource .find (guardDefineToken )!= std::string::npos ; 644 645if (alreadyGuarded ) { 646return hlslSource ; 647 } 648 649 std::string body = hlslSource ; 650if (!body .empty ()&& body .back ()!= '\n' ) { 651body += '\n' ; 652 } 653 654 std::ostringstream wrapped ; 655wrapped <<includeGuard .ifndefLine <<'\n' ; 656wrapped <<includeGuard .defineLine <<'\n' ; 657wrapped <<'\n' ; 658wrapped <<body ; 659if (!body .empty ()&& body .back ()!= '\n' ) { 660wrapped <<'\n' ; 661 } 662wrapped <<includeGuard .endifLine ; 663if (!includeGuard .endifLine .empty ()&& includeGuard .endifLine .back ()!= '\n' ) { 664wrapped <<'\n' ; 665 } 666 667return wrapped .str (); 668} 669 670absl::Status run (int argc ,char ** argv ) { 671 absl::StatusOr < ModuleRequest > requestOr = parseModuleRequest (argc ,argv ); 672if (!requestOr .ok ()) { 673return requestOr .status (); 674 } 675ModuleRequest request = std::move (requestOr ).value (); 676 677ComPtr < IGlobalSession > globalSession ; 678if (absl::Status status = checkSlangResult ( 679"createGlobalSession" , 680createGlobalSession (globalSession .writeRef ())); 681 !status .ok ()) { 682return status ; 683 } 684 685auto commonOptions = makeCommonOptions (); 686 687 std::vector < CompilerOptionEntry > targetOptions = commonOptions ; 688TargetDesc targetDesc ; 689configureTargetDesc (globalSession .get (),targetOptions ,targetDesc ); 690 691 std::vector < CompilerOptionEntry > sessionOptions = commonOptions ; 692SessionDesc sessionDesc ; 693 std::array < const char * ,1 > searchPaths {}; 694configureSessionDesc (targetDesc ,request ,sessionOptions ,searchPaths ,sessionDesc ); 695 696ComPtr < ISession > session ; 697if (absl::Status status = checkSlangResult ( 698"IGlobalSession::createSession" , 699globalSession -> createSession (sessionDesc ,session .writeRef ())); 700 !status .ok ()) { 701return status ; 702 } 703 704 absl::StatusOr < ComPtr < IModule >> libraryModuleOr = 705loadSlangModule (session .get (),request .moduleName ); 706if (!libraryModuleOr .ok ()) { 707return libraryModuleOr .status (); 708 } 709ComPtr < IModule > libraryModule = std::move (libraryModuleOr ).value (); 710 711 absl::StatusOr < EntryPointsResult > entryPointsOr = 712collectEntryPoints (libraryModule .get (),request .moduleName ,request .modulePath ); 713if (!entryPointsOr .ok ()) { 714return entryPointsOr .status (); 715 } 716EntryPointsResult entryPoints = std::move (entryPointsOr ).value (); 717 718 absl::StatusOr < ComPtr < ICompileRequest >> compileRequestOr = 719createCompileRequest (session .get (),request ,targetDesc ,entryPoints .functions ); 720if (!compileRequestOr .ok ()) { 721return compileRequestOr .status (); 722 } 723ComPtr < ICompileRequest > compileRequest = std::move (compileRequestOr ).value (); 724 725 absl::StatusOr < std::string > hlslSourceOr = 726collectGeneratedHlsl (compileRequest .get (),request .moduleName ); 727if (!hlslSourceOr .ok ()) { 728return hlslSourceOr .status (); 729 } 730 std::string hlslSource = std::move (hlslSourceOr ).value (); 731 std::string filteredHlsl = removeNvapiInclude (hlslSource ); 732 733 fs::path rawOutputPath = request .outputPath ; 734rawOutputPath .replace_extension (".raw.hlsl" ); 735if (absl::Status status = writeTextFile (rawOutputPath ,hlslSource ); 736 !status .ok ()) { 737return status ; 738 } 739 740IncludeGuardInfo includeGuard = detectIncludeGuard (request .modulePath ); 741 std::string finalHlsl = applyIncludeGuard (filteredHlsl ,includeGuard ); 742if (absl::Status status = writeTextFile (request .outputPath ,finalHlsl ); 743 !status .ok ()) { 744return status ; 745 } 746 747 std::cerr <<"Generated HLSL written to " <<request .outputPath << std::endl ; 748return absl::OkStatus (); 749} 750 751int main (int argc ,char ** argv ) { 752 absl::Status status = run (argc ,argv ); 753if (!status .ok ()) { 754 std::cerr <<status .message () << std::endl ; 755return 1 ; 756 } 757 758return 0 ; 759}