// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #pragma once #include #include #include #include namespace anki { // Forward class ShaderParser; /// @addtogroup shader_compiler /// @{ /// @memberof ShaderParser class ShaderParserMutator { public: ShaderCompilerString m_name; ShaderCompilerDynamicArray m_values; }; /// @memberof ShaderParser class ShaderParserGhostStructMember { public: ShaderCompilerString m_name; ShaderVariableDataType m_type; U32 m_offset = kMaxU32; Array m_defaultValues = {}; }; /// @memberof ShaderParser class ShaderParserGhostStruct { public: ShaderCompilerDynamicArray m_members; ShaderCompilerString m_name; }; /// @memberof ShaderParser class ShaderParserTechnique { public: ShaderCompilerString m_name; ShaderTypeBit m_shaderTypes = ShaderTypeBit::kNone; Array m_activeMutators = {}; }; /// This is a special preprocessor that run before the usual preprocessor. Its purpose is to add some meta information /// in the shader programs. /// /// It supports the following expressions: /// #include {<> | ""} /// #pragma once /// #pragma anki mutator NAME VALUE0 [VALUE1 [VALUE2 ...]] /// #pragma anki skip_mutation MUTATOR0 VALUE0 [MUTATOR1 VALUE1 [MUTATOR2 VALUE2 ...]] /// #pragma anki technique [NAME] STAGE0 [STAGE1 ...] [mutators [MUTATOR0 [MUTATOR1 ...]]] /// #pragma anki extra_compiler_args ARG0 [ARG1 [ARG2...]] /// /// #pragma anki struct NAME /// # pragma anki member TYPE NAME [DEFAULT_VALUE0 [DEFAULT_VALUE1 ...]] /// ... /// #pragma anki struct_end /// /// None of the pragmas should be in an ifdef-like guard. It's ignored. class ShaderParser { public: ShaderParser(CString fname, ShaderCompilerFilesystemInterface* fsystem, ConstWeakArray defines); ShaderParser(const ShaderParser&) = delete; // Non-copyable ~ShaderParser(); ShaderParser& operator=(const ShaderParser&) = delete; // Non-copyable /// Parse the file and its includes. Error parse(); /// Returns true if the mutation should be skipped. Bool skipMutation(ConstWeakArray mutation) const; /// Get the source (and a few more things) given a list of mutators. void generateVariant(ConstWeakArray mutation, const ShaderParserTechnique& technique, ShaderType shaderType, ShaderCompilerString& source) const; ConstWeakArray getMutators() const { return m_mutators; } U64 getHash() const { ANKI_ASSERT(m_hash != 0); return m_hash; } ConstWeakArray getGhostStructs() const { return m_ghostStructs; } ConstWeakArray getTechniques() const { return m_techniques; } ConstWeakArray getExtraCompilerArgs() const { return m_extraCompilerArgsCString; } /// Generates the common header that will be used by all AnKi shaders. static void generateAnkiShaderHeader(ShaderType shaderType, ShaderCompilerString& header); private: using Mutator = ShaderParserMutator; using Member = ShaderParserGhostStructMember; using GhostStruct = ShaderParserGhostStruct; using Technique = ShaderParserTechnique; class PartialMutationSkip { public: ShaderCompilerDynamicArray m_partialMutation; }; static constexpr U32 kMaxIncludeDepth = 8; ShaderCompilerString m_fname; ShaderCompilerFilesystemInterface* m_fsystem = nullptr; ShaderCompilerDynamicArray m_defineNames; ShaderCompilerDynamicArray m_defineValues; U64 m_hash = 0; ShaderCompilerStringList m_sourceLines; ShaderCompilerString m_source; ShaderCompilerDynamicArray m_techniques; ShaderCompilerDynamicArray m_mutators; ShaderCompilerDynamicArray m_skipMutations; ShaderCompilerDynamicArray m_ghostStructs; Bool m_insideStruct = false; ShaderCompilerDynamicArray m_extraCompilerArgs; ShaderCompilerDynamicArray m_extraCompilerArgsCString; Error parseFile(CString fname, U32 depth); Error parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth, U32 lineNumber); Error parseInclude(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname, U32 depth); Error parsePragmaMutator(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname); Error parsePragmaTechnique(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname); Error parsePragmaSkipMutation(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname); Error parsePragmaStructBegin(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname); Error parsePragmaStructEnd(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname); Error parsePragmaMember(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname); Error parseExtraCompilerArgs(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname); void tokenizeLine(CString line, ShaderCompilerDynamicArray& tokens) const; static Bool tokenIsComment(CString token) { return token.getLength() >= 2 && token[0] == '/' && (token[1] == '/' || token[1] == '*'); } static Bool mutatorHasValue(const ShaderParserMutator& mutator, MutatorValue value); static ShaderCompilerString sanitizeFilename(CString fname) { ShaderCompilerString s = fname; s.replaceAll("\\", "\\\\"); return s; } Error checkNoActiveStruct() const { if(m_insideStruct) { ANKI_SHADER_COMPILER_LOGE("Unsupported \"pragma anki\" inside \"pragma anki struct\""); return Error::kUserData; } return Error::kNone; } Error checkActiveStruct() const { if(!m_insideStruct) { ANKI_SHADER_COMPILER_LOGE("Expected a \"pragma anki struct\" to open"); return Error::kUserData; } return Error::kNone; } }; /// @} } // end namespace anki