123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <CLI/CLI.hpp>
- #include "AzslcReflection.h"
- #include "AzslcEmitter.h"
- #include "AzslcHomonymVisitor.h"
- #include "Texture2DMSto2DCodeMutator.h"
- #include <cstddef>
- #include <filesystem>
- namespace StdFs = std::filesystem;
- // versioning
- // Correspond to the supported version of the AZSL language.
- #define AZSLC_MAJOR "1"
- // For large features or milestones. Minor version allows for breaking changes. Existing tests can change.
- #define AZSLC_MINOR "8" // last change: introduction of class inheritance
- // For small features or bug fixes. They cannot introduce breaking changes. Existing tests shouldn't change.
- #define AZSLC_REVISION "18" // last change: automatic option ranks
- namespace AZ::ShaderCompiler
- {
- DiagnosticStream verboseCout;
- DiagnosticStream warningCout(std::cerr);
- Endl azEndl;
- using MapOfStringViewToSetOfString = map<string_view, set<string>>;
- template <typename TypeClassFilterPredicate = std::nullptr_t>
- void VisitTokens(const antlr4::Recognizer* recognizer,
- MapOfStringViewToSetOfString& acceptedToken, set<string>& notTypes1, // out
- TypeClassFilterPredicate tcFilter = nullptr)
- {
- // loop over all keywords
- const auto& vocabulary = recognizer->getVocabulary();
- size_t maxToken = vocabulary.getMaxTokenType();
- for (size_t ii = 0; ii < maxToken; ++ii)
- {
- string token = vocabulary.getLiteralName(ii);
- token = Trim(token, "\"'"); // because AntlR gives us e.g "'float'"
- if (!token.empty()) // empty when there is a complex rule (not a straight unconditional keyword)
- {
- TypeClass tc = AnalyzeTypeClass(TentativeName{token});
- bool accept = true;
- if constexpr (!is_same_v<std::nullptr_t, std::remove_reference_t<decltype(tcFilter)>>)
- {
- accept = tcFilter(tc);
- }
- if (tc == TypeClass::IsNotType)
- {
- notTypes1.insert(token);
- }
- if (accept)
- {
- acceptedToken[TypeClass::ToStr(tc)].emplace(std::move(token));
- }
- }
- }
- }
- // out argument: classifiedTokens
- // filter: is a predicate for condition to check to pass registration
- template <typename FilterFunction>
- void ClassifyAllTokens(const azslLexer* lexer, MapOfStringViewToSetOfString& classifiedTokens, FilterFunction filter)
- {
- set<string> notTypes1;
- VisitTokens(lexer, classifiedTokens, notTypes1, filter);
- // now. because of names such as StructuredBuffer or matrix, need to have a generic appendix to mean something,
- // they will be classified as IsNotType. So we need to re-attempt analysis by appending something parseable.
- // get a scalar typename from the scalar class:
- string someScalar = *classifiedTokens[TypeClass::ToStr(TypeClass::Scalar)].begin();
- // now we'll use it to construct parseable generic type expressions
- enum class RetryStateMachine
- {
- OneTypeGenericParameter,
- OneTypeAndDimensionGenericParameters,
- OneTypeAndTwoDimensionsGenericParameters,
- End,
- };
- constexpr auto isNotTypeKey = TypeClass::ToStr(TypeClass::IsNotType);
- // completely delete all IsNotType from the classification, and redo it all over.
- set<string> notTypes = std::move(classifiedTokens[isNotTypeKey]);
- classifiedTokens.erase(isNotTypeKey);
- SetMerge(notTypes, notTypes1);
- for (const string& token : notTypes)
- {
- RetryStateMachine state = RetryStateMachine::OneTypeGenericParameter;
- TypeClass tc;
- do
- {
- string attemptedTypeName = token;
- switch (state)
- {
- case RetryStateMachine::OneTypeGenericParameter:
- attemptedTypeName += "<" + someScalar + ">";
- state = RetryStateMachine::OneTypeAndDimensionGenericParameters;
- break;
- case RetryStateMachine::OneTypeAndDimensionGenericParameters:
- attemptedTypeName += "<" + someScalar + ",1>";
- state = RetryStateMachine::OneTypeAndTwoDimensionsGenericParameters;
- break;
- case RetryStateMachine::OneTypeAndTwoDimensionsGenericParameters:
- attemptedTypeName += "<" + someScalar + "1,1>";
- state = RetryStateMachine::End;
- break;
- case RetryStateMachine::End:
- default:
- // save as is
- break;
- }
- tc = AnalyzeTypeClass(TentativeName{attemptedTypeName});
- } while (tc == TypeClass::IsNotType && state != RetryStateMachine::End);
- // re-register at the correct place (if passes filter):
- bool accept = true;
- if constexpr (!is_same_v<std::nullptr_t, std::remove_reference_t<decltype(filter)>>)
- {
- accept = filter(tc);
- }
- if (accept)
- {
- classifiedTokens[TypeClass::ToStr(tc)].insert(token);
- }
- } // end for
- }
- bool IsKeyword(const antlr4::Recognizer* r, antlr4::Token* token)
- {
- MapOfStringViewToSetOfString byTypeClass;
- set<string> notTypes;
- VisitTokens(r, byTypeClass, notTypes);
- bool notType = notTypes.find(token->getText()) != notTypes.end();
- bool notIdentifier = r->getVocabulary().getSymbolicName(token->getType()) != "Identifier";
- bool firstIsalpha(isalpha(token->getText()[0]));
- return notType && notIdentifier && firstIsalpha;
- }
- void DumpClassifiedTokensToYaml(const MapOfStringViewToSetOfString& classifiedTokens)
- {
- for (const auto& [category, tokens] : classifiedTokens)
- {
- std::cout << category << ":\n";
- for (const string& tokenName : tokens)
- {
- std::cout << " - \"" << tokenName << "\"\n";
- }
- }
- }
- // outputs information about language keywords related to predefined types
- void DumpPredefinedVocabulary(const azslLexer* lexer)
- {
- MapOfStringViewToSetOfString classifiedTokens;
- ClassifyAllTokens(lexer, classifiedTokens /*out*/, [](TypeClass tc) { return IsPredefinedType(tc); });
- DumpClassifiedTokensToYaml(classifiedTokens);
- }
- //! iterates on tokens and build the line number mapping (from preprocessor line directives)
- void ConstructLineMap(vector<std::unique_ptr<Token>>* allTokens, PreprocessorLineDirectiveFinder* lineFinder)
- {
- string lastNonEmptyFileName = lineFinder->m_physicalSourceFileName;
- for (auto& token : *allTokens) // auto& because each element is a unique_ptr we can't copy
- {
- if (token->getType() == azslLexer::LineDirective)
- {
- LineDirectiveInfo directiveInfo{ 0, 0 };
- const auto lineText = token->getText();
- // the sharp
- // | any whitespaces
- // | / optional line token
- // | | | decimal
- // custom raw string | | | | optional filename between quotes
- // delimiter ──┐ | | | | |
- std::regex lineRegex(R"__(#\s*(line\s+)?\s*(\d+)\s*("(.*)")?)__");
- auto matchBegin = std::sregex_iterator(lineText.begin(), lineText.end(), lineRegex);
- // there can be only 1 match, and it HAS to match since AntlR lexer already matched.
- auto& groups = *matchBegin; // 4 groups: [0] is the whole line. [1] is the first parenthesized group, [2] the 2nd etc
- directiveInfo.m_physicalTokenLine = token->getLine();
- directiveInfo.m_forcedLineNumber = std::atoi(groups[2].str().c_str());
- directiveInfo.m_containingFilename = groups[4];
- if (directiveInfo.m_containingFilename.empty())
- {
- // if we don't have a filename specified on the line, it means the last seen filename is still active.
- // storing it this way simplifies the lookup algorithm using this data.
- directiveInfo.m_containingFilename = lastNonEmptyFileName;
- }
- else
- {
- lastNonEmptyFileName = directiveInfo.m_containingFilename;
- }
- lineFinder->PushLineDirective(directiveInfo);
- }
- }
- if (lineFinder->m_lineMap.find(1) == lineFinder->m_lineMap.end())
- {
- // if we have no line directives on line 1, add one before the file's first line (at 0), that can always be found by Infimum
- lineFinder->PushLineDirective({0, 1, lineFinder->m_physicalSourceFileName});
- }
- }
- }
- namespace AZ::ShaderCompiler::Main
- {
- using namespace AZ::ShaderCompiler;
- /// This function will support the --ast option. It uses an AntlR facility and prettifies it.
- void PrintAst(tree::ParseTree* tree, azslParser& parser)
- {
- // not sure why wstring, but I'm going along the AntlR's doc example.
- std::wstring s = antlrcpp::s2ws(tree->toStringTree(&parser));
- // hopefully easy to read indentator
- std::wstring curindent = L"";
- for (wchar_t c : s)
- {
- if (c == L'(')
- {
- std::wcout << "\n";
- curindent += L" ";
- std::wcout << curindent << c;
- }
- else if (c == L')')
- {
- std::wcout << c << "\n";
- curindent = curindent.substr(0, std::max(2_sz, curindent.size()) - 2);
- std::wcout << curindent;
- }
- else
- {
- std::wcout << c;
- }
- }
- std::wcout << std::endl; // flush
- }
- /// this function supports the --visitsym option
- // @symbolMqn starting point of symbol homonyms graph discovery. Mqn: mangled qualified name
- void PrintVisitSymbol(IntermediateRepresentation& ir, string_view symbolMqn, RelationshipExtentFlag visitOptions)
- {
- IdAndKind* symbol = ir.GetIdAndKindInfo(QualifiedNameView{symbolMqn});
- if (!symbol)
- {
- std::cerr << "Error: symbol " << symbolMqn.data() << " not found. To list all symbols use --dumpsym option.\n";
- return;
- }
- std::cout << "Symbol found. kind: " << Kind::ToStr(ir.GetKind(symbol->first)) << ". Homonyms list:\n";
- HomonymVisitor hv{[&ir](QualifiedNameView qnv) { return ir.GetKindInfo({{qnv}}); }};
- hv(symbol->first,
- [](const Seenat &at, RelationshipExtent category)
- {
- std::cout << "- {categ: " << RelationshipExtent::ToStr(category)
- << ", id: " << Decorate("'", at.m_referredDefinition.GetName())
- << ", at: ':" << at.m_where.m_line << ":" << at.m_where.m_charPos + 1 << "'"
- << ", token#: " << at.m_where.m_focusedTokenId << "}\n";
- },
- visitOptions);
- }
- void ParseWarningLevel(const std::array<bool, Warn::EndEnumeratorSentinel_>& args, DiagnosticStream& warningConfig)
- {
- for (auto level : Warn::Enumerate{})
- {
- bool active = args[level];
- if (active)
- {
- if (level >= Warn::Wx)
- {
- warningConfig.SetAsErrorLevel(level);
- }
- else
- {
- warningConfig.SetRevealedWarningLevel(level);
- }
- }
- }
- }
- }
- namespace AZ
- {
- string_view GetFileLeafName(string_view path)
- {
- return Slice(path, path.find_last_of("/\\") + 1, -1);
- }
- inline void DoAsserts()
- {
- #ifndef NDEBUG
- using namespace AZ::Tests;
- DoAsserts2();
- DoAsserts4();
- DoAsserts5();
- DoAsserts6();
- #endif
- }
- }
- int main(int argc, const char* argv[])
- {
- using namespace AZ;
- using namespace AZ::ShaderCompiler::Main;
- CLI::App cli{ "Amazon Shader Language Compiler" };
- bool printVersion = false;
- cli.add_flag("--version", printVersion, "Prints version information");
- std::string inputFile;
- cli.add_option("(- | FILE)", inputFile, "Input file (pass - to read from stdin).");
- std::string output;
- cli.add_option("-o", output, "Output file (writes to stdout if omitted).");
- bool uniqueIdx = false;
- cli.add_flag("--unique-idx", uniqueIdx, "Use unique indices for all registers. e.g. b0, t0, u0, s0 becomes b0, t1, u2, s3. Use on platforms that don't differentiate registers by resource type.");
-
- bool cbBody = false;
- cli.add_flag("--cb-body", cbBody, "Emit ConstantBuffer body rather than using <T>.");
- bool rootSig = false;
- cli.add_flag("--root-sig", rootSig, "Emit RootSignature for parameter binding in the shader. --namespace must also be used to select a specific API.");
- int rootConst = 0;
- auto rootConstOpt = cli.add_option("--root-const", rootConst, "Maximum size in bytes of the root constants buffer.");
- bool padRootConst = false;
- cli.add_flag("--pad-root-const", padRootConst, "Automatically append padding data to the root constant CB to keep it aligned to a 16-byte boundary.");
- bool Zpc = false;
- cli.add_flag("--Zpc", Zpc, "Pack matrices in column-major order (default). Cannot be specified together with -Zpr.");
- bool Zpr = false;
- cli.add_flag("--Zpr", Zpr, "Pack matrices in row-major order. Cannot be specified together with -Zpc.");
- bool packDx12 = false;
- cli.add_flag("--pack-dx12", packDx12, "Pack buffers using strict DX12 packing rules. If not specified AZSLc will use relaxed packing rules.");
- bool packVulkan = false;
- cli.add_flag("--pack-vulkan", packVulkan, "Pack buffers using strict Vulkan packing rules (Vector-relaxed std140 for uniforms and std430 for storage buffers).");
- bool packOpenGL = false;
- cli.add_flag("--pack-opengl", packOpenGL, "Pack buffers using strict OpenGL packing rules (Vector-strict std140 for uniforms and std430 for storage buffers).");
- std::vector<std::string> namespaces;
- cli.add_option("--namespace", namespaces,
- "Activate an attribute namespace. May be used multiple times to activate multiple namespaces. "
- "Activating a namespace may also activate corresponding API-specific features, like dx for DirectX 12, vk for Vulkan, and mt for Metal.");
- bool ia = false;
- cli.add_flag("--ia", ia, "Output a list of vs entries with their Input Assembler layouts *and* a list of CS entries and their numthreads.");
- bool om = false;
- cli.add_flag("--om", om, "Output the Output Merger layout instead of the shader code.");
- bool srg = false;
- cli.add_flag("--srg", srg, "Output the Shader Resource Group layout instead of the shader code.");
- bool options = false;
- cli.add_flag("--options", options, "Output the list of available shader options for this shader.");
- bool dumpsym = false;
- cli.add_flag("--dumpsym", dumpsym, "Dump symbols.");
- bool syntax = false;
- cli.add_flag("--syntax", syntax, "Check syntax (no output means no complaints).");
- bool semantic = false;
- cli.add_flag("--semantic", semantic, "Check semantics (no output means no complaints).");
- bool ast = false;
- cli.add_flag("--ast", ast, "Output the abstract syntax tree.");
- bool bindingdep = false;
- cli.add_flag("--bindingdep", bindingdep, "Output binding dependencies (what entry points access what external resources).");
- std::string visitName;
- cli.add_option("--visitsym", visitName, "Output the locations of all relationships of the supplied symbol name.");
- bool full = false;
- cli.add_flag("--full", full, "Output the shader code, IA layout, OM layout, SRG layout, the list of available shader options, and the binding dependencies.");
- bool stripUnusedSrgs = false;
- cli.add_flag("--strip-unused-srgs", stripUnusedSrgs, "Strips unused SRGs.");
- bool noMS = false;
- cli.add_flag("--no-ms", noMS, "Transforms usage of Texture2DMS/Texture2DMSArray and related functions and semantics into plain Texture2D/Texture2DArray "
- "equivalents. This is useful for allowing shader authors to easily write AZSL code that can be compiled into alternatives to work with both a "
- "multisample render pipeline and a non-MS render pipeline.");
- bool noAlignmentValidation = false;
- cli.add_flag("--no-alignment-validation", noAlignmentValidation, "Skips checking for potential alignment issues related to differences between dxil and spirv."
- "By default, potential alignment discrepancies will fail compilation.");
- bool visitDirectReferences = false;
- cli.add_flag("-d", visitDirectReferences, "(Option of --visitsym) Visit direct references.");
- bool visitOverloadSet = false;
- cli.add_flag("-v", visitOverloadSet, "(Option of --visitsym) Visit overload-set.");
- bool visitFamily = false;
- cli.add_flag("-f", visitFamily, "(Option of --visitsym) Visit family.");
- bool visitRecursively = false;
- cli.add_flag("-r", visitRecursively, "(Option of --visitsym) Visit recursively.");
- bool listPredefined = false;
- cli.add_flag("--listpredefined", listPredefined, "Output a list of all predefined types in AZSLang.");
- int maxSpaces = std::numeric_limits<int>::max();
- auto maxSpacesOpt = cli.add_option("--max-spaces", maxSpaces, "Will choose register spaces that do not extend past this limit.");
- std::array<bool, Warn::EndEnumeratorSentinel_> warningOpts;
- for (const auto e : Warn::Enumerate{})
- {
- warningOpts[e] = false;
- }
- cli.add_flag("--W0", warningOpts[Warn::EnumType::W0], "Suppresses all warnings.");
- cli.add_flag("--W1", warningOpts[Warn::EnumType::W1], "Activate severe warnings (default).");
- cli.add_flag("--W2", warningOpts[Warn::EnumType::W2], "Activate warnings that may be significant.");
- cli.add_flag("--W3", warningOpts[Warn::EnumType::W3], "Activate low-confidence diagnostic warnings.");
- cli.add_flag("--Wx", warningOpts[Warn::EnumType::Wx], "Treat activated warnings as errors.");
- cli.add_flag("--Wx1", warningOpts[Warn::EnumType::Wx1], "Treat level-1 warnings as errors.");
- cli.add_flag("--Wx2", warningOpts[Warn::EnumType::Wx2], "Treat level-2 and below warnings as errors.");
- cli.add_flag("--Wx3", warningOpts[Warn::EnumType::Wx3], "Treat level-3 and below warnings as errors.");
- std::string minDescriptors;
- cli.add_option("--min-descriptors", minDescriptors, "Comma-separated list of limits corresponding to "
- "<set,space,sampler,texture,buffer> descriptors. Emits a warning if a count overshoots a limit. Use -1 to specify \"no limit\".");
-
- bool verbose = false;
- cli.add_flag("--verbose", verbose);
- DoAsserts();
- int processReturnCode = 0;
- try
- {
- CLI11_PARSE(cli, argc, argv);
- // Major.Minor.Revision
- auto versionString = string{"AZSL Compiler " AZSLC_MAJOR "." AZSLC_MINOR "." AZSLC_REVISION " "} + GetCurrentOsName().data();
- if (printVersion)
- {
- std::cout << versionString << std::endl;
- return 0;
- }
- if (listPredefined)
- {
- verboseCout.m_on = false;
- ANTLRInputStream is;
- azslLexer lexer{&is};
- DumpPredefinedVocabulary(&lexer);
- return 0;
- }
- verboseCout.m_on = verbose;
- bool useStdin = inputFile == '-';
- // we need to scope a stream object here, to be able to bind a polymorphic reference to it
- std::ifstream ifs;
- if (!useStdin)
- {
- ifs = std::ifstream{ inputFile }; // try to open as file
- }
- std::istream& in{useStdin ? std::cin : ifs};
- if (!in.good())
- {
- throw std::runtime_error("input file could not be opened");
- }
- if (rootSig && namespaces.empty())
- {
- throw std::runtime_error("--root-sig requested but no API was selected. Use a --namespace option as well.");
- }
- const string inputFileName = useStdin ? "" : inputFile;
- PreprocessorLineDirectiveFinder lineFinder;
- lineFinder.m_physicalSourceFileName = useStdin ? "stdin" : inputFile;
- // setup the line finder address on the exception system so that errors are canonically mutated to "virtual line space"
- AzslcException::s_lineFinder = &lineFinder;
- bool useOutputFile = !output.empty();
- const string outputFileName = output;
- ANTLRInputStream input(in);
- azslLexer lexer(&input);
- CommonTokenStream tokens(&lexer);
- IntermediateRepresentation ir(&lexer);
- auto allTokens = lexer.getAllTokens();
- if (lexer.getNumberOfSyntaxErrors() > 0)
- {
- throw std::runtime_error("syntax errors present");
- }
- ConstructLineMap(&allTokens, &lineFinder);
- lexer.reset();
- AzslParserEventListener azslParserEventListener;
- azslParser parser(&tokens);
- parser.removeErrorListeners();
- azslParserEventListener.m_isKeywordPredicate = IsKeyword;
- parser.addErrorListener(&azslParserEventListener);
- tree::ParseTree *tree = parser.compilationUnit();
- if (ast)
- {
- PrintAst(tree, parser);
- syntax = true; // ast print is a syntax only build.
- }
- if (parser.getNumberOfSyntaxErrors() > 0)
- {
- throw std::runtime_error("grammatic errors present");
- }
- if (syntax)
- { // if we are here with no exception then the syntax pass is valid.
- }
- else // continue with semantic, and later emission
- {
- if (!useStdin)
- {
- StdFs::path inSource{ inputFile };
- ir.m_metaData.m_insource = StdFs::absolute(inSource).lexically_normal().generic_string();
- }
- tree::ParseTreeWalker walker;
- Texture2DMSto2DCodeMutator texture2DMSto2DCodeMutator(&ir, &tokens);
- SemaCheckListener semanticListener{&ir};
- warningCout.m_onErrorCallback = [](string_view message) {
- throw AzslcException{WX_WARNINGS_AS_ERRORS, "as-error", string{message}};
- };
- ParseWarningLevel(warningOpts, warningCout);
- bool nonValidativeOptions[] = {full, ia, om, srg, options, dumpsym, ast, bindingdep, !visitName.empty(), stripUnusedSrgs};
- bool anyNonValidativeOption = std::any_of(std::begin(nonValidativeOptions), std::end(nonValidativeOptions), [](bool opt) { return opt; });
- semanticListener.m_silentPrintExtensions = !semantic || verbose; // print-extensions are useful for interested parties; but not normal operation.
- if (noMS)
- {
- semanticListener.m_functionCallMutator = &texture2DMSto2DCodeMutator;
- }
- warningCout.m_on = !anyNonValidativeOption; // warnings are interesting for emission, and explicit semantic check modes.
- // Enable attribute namespaces
- std::for_each(namespaces.begin(), namespaces.end(),
- [&](const string& space) { ir.AddAttributeNamespaceFilter(space); });
- UnboundedArraysValidator::Options unboundedArraysValidationOptions;
- unboundedArraysValidationOptions.m_useUniqueIndicesEnabled = uniqueIdx;
- if (*maxSpacesOpt)
- {
- unboundedArraysValidationOptions.m_maxSpaces = maxSpaces;
- }
- ir.m_sema.m_unboundedArraysValidator.SetOptions(unboundedArraysValidationOptions);
- // semantic logic and validation
- walker.walk(&semanticListener, tree);
- Options emitOptions;
- emitOptions.m_useUniqueIndices = uniqueIdx;
- emitOptions.m_emitConstantBufferBody = cbBody;
- emitOptions.m_emitRootSig = rootSig;
- emitOptions.m_padRootConstantCB = padRootConst;
- emitOptions.m_skipAlignmentValidation = noAlignmentValidation;
- if (*rootConstOpt)
- {
- emitOptions.m_rootConstantsMaxSize = rootConst;
- }
- if (!minDescriptors.empty())
- {
- sscanf(minDescriptors.c_str(), "%d,%d,%d,%d,%d",
- &emitOptions.m_minAvailableDescriptors.m_descriptorsTotal,
- &emitOptions.m_minAvailableDescriptors.m_spaces,
- &emitOptions.m_minAvailableDescriptors.m_samplers,
- &emitOptions.m_minAvailableDescriptors.m_textures,
- &emitOptions.m_minAvailableDescriptors.m_buffers);
- }
- if (*maxSpacesOpt)
- {
- emitOptions.m_maxSpaces = maxSpaces;
- }
- if (Zpc && Zpr)
- {
- throw std::runtime_error("Cannot specify --Zpr and --Zpc together, use --help to get usage information");
- }
- else if (Zpr)
- {
- emitOptions.m_forceMatrixRowMajor = true;
- emitOptions.m_forceEmitMajor = true;
- }
- else if (Zpc)
- {
- emitOptions.m_forceMatrixRowMajor = false; // Default
- emitOptions.m_forceEmitMajor = true;
- }
- if (packDx12)
- {
- emitOptions.m_packConstantBuffers = AZ::ShaderCompiler::Packing::Layout::DirectXPacking;
- emitOptions.m_packDataBuffers = AZ::ShaderCompiler::Packing::Layout::DirectXStoragePacking;
- }
- if (packVulkan)
- {
- emitOptions.m_packConstantBuffers = AZ::ShaderCompiler::Packing::Layout::RelaxedStd140Packing;
- emitOptions.m_packDataBuffers = AZ::ShaderCompiler::Packing::Layout::RelaxedStd430Packing;
- }
- if (packOpenGL)
- {
- emitOptions.m_packConstantBuffers = AZ::ShaderCompiler::Packing::Layout::StrictStd140Packing;
- emitOptions.m_packDataBuffers = AZ::ShaderCompiler::Packing::Layout::StrictStd430Packing;
- }
- // middle end logic
- MiddleEndConfiguration middleEndConfigration{emitOptions.m_rootConstantsMaxSize,
- emitOptions.m_packConstantBuffers,
- emitOptions.m_packDataBuffers,
- emitOptions.m_forceMatrixRowMajor,
- emitOptions.m_padRootConstantCB,
- emitOptions.m_skipAlignmentValidation};
- ir.MiddleEnd(middleEndConfigration, &lineFinder);
- if (noMS)
- {
- texture2DMSto2DCodeMutator.RunMiddleEndMutations();
- }
- // If ir fails to find any root members in the source, overwrite the m_numOfRootConstants to 0
- if (ir.m_rootConstantStructUID.m_name == "")
- {
- emitOptions.m_rootConstantsMaxSize = 0;
- }
- // intermediate state validation
- ir.Validate();
- if (stripUnusedSrgs)
- {
- ir.RemoveUnusedSrgs();
- }
- if (dumpsym)
- {
- DumpSymbols(ir);
- }
- else if (!visitName.empty())
- {
- using RE = RelationshipExtent;
- RelationshipExtentFlag visitOptions{RE::Self}; // at least self. + optional things as listed here-under
- array<pair<bool, RE::EnumType>, 4> optToRelation = {{{visitDirectReferences, RE::Reference},
- {visitFamily, RE::Family},
- {visitOverloadSet, RE::OverloadSet},
- {visitRecursively, RE::Recursive}}};
- for (auto &&possibleOption : optToRelation)
- {
- visitOptions |= possibleOption.first ? possibleOption.second : RE::EnumType(0);
- }
- PrintVisitSymbol(ir, visitName, visitOptions);
- }
- else if (!semantic) // do emission
- {
- verboseCout << "--Emission/Reflection--\n";
- std::ofstream mainOutFile;
- if (useOutputFile)
- {
- mainOutFile = std::ofstream(outputFileName);
- if (!mainOutFile.good())
- {
- throw std::runtime_error("output file could not be opened");
- }
- }
- std::ostream& out{useOutputFile ? mainOutFile : std::cout};
- CodeReflection reflecter{&ir, &tokens, out};
- // Lambda to create an output stream and perform an output action
- auto prepareOutputAndCall = [&](const string &suffix, std::function<void(CodeReflection&)> action)
- {
- string outputName;
- if (useOutputFile)
- {
- outputName = string(GetFileNameWithoutExtension(outputFileName));
- }
- else
- {
- outputName = string(GetFileNameWithoutExtension(inputFileName));
- }
- outputName = outputName + "." + suffix + ".json";
- std::ofstream outFile = std::ofstream(outputName);
- if (!outFile.good())
- {
- throw std::runtime_error("output file '" + outputName + "' could not be opened");
- }
- std::ostream& out{outFile};
- CodeReflection reflecter{&ir, &tokens, out};
- action(reflecter);
- };
- if (full)
- { // Combine the default emission and the ia, om, srg, options, bindingdep commands
- CodeEmitter emitter{&ir, &tokens, out, &lineFinder};
- if (noMS)
- {
- emitter.SetCodeMutator(&texture2DMSto2DCodeMutator);
- }
- emitter << "// HLSL emission by " << versionString << "\n";
- emitter.Run(emitOptions);
- prepareOutputAndCall("ia", [&](CodeReflection& r) { r.DumpShaderEntries(); });
- prepareOutputAndCall("om", [&](CodeReflection& r) { r.DumpOutputMergerLayout(); });
- prepareOutputAndCall("srg", [&](CodeReflection& r) { r.DumpSRGLayout(emitOptions, &lineFinder); });
- prepareOutputAndCall("options", [&](CodeReflection& r) { r.DumpVariantList(emitOptions); });
- prepareOutputAndCall("bindingdep", [&](CodeReflection& r) { r.DumpResourceBindingDependencies(emitOptions); });
- }
- else if (ia)
- { // Reflect the Input Assembler layout and the Compute shader entries
- reflecter.DumpShaderEntries();
- }
- else if (om)
- { // Reflect the Input Assembler layout
- reflecter.DumpOutputMergerLayout();
- }
- else if (srg)
- { // Reflect the Shader Resource Groups layout
- reflecter.DumpSRGLayout(emitOptions, &lineFinder);
- }
- else if (options)
- { // Reflect the list of available variant options for this shader
- reflecter.DumpVariantList(emitOptions);
- }
- else if (bindingdep)
- {
- reflecter.DumpResourceBindingDependencies(emitOptions);
- }
- else
- { // Emit the shader source code
- CodeEmitter emitter{&ir, &tokens, out, &lineFinder};
- if (noMS)
- {
- emitter.SetCodeMutator(&texture2DMSto2DCodeMutator);
- }
- emitter << "// HLSL emission by " << versionString << "\n";
- emitter.Run(emitOptions);
- }
- }
- }
- }
- catch (const exception& e)
- {
- OutputNestedAndException(e);
- processReturnCode = 1;
- }
- catch (...)
- {
- std::cerr << "Unknown exception" << std::endl;
- processReturnCode = 1;
- }
- return processReturnCode;
- }
|