// Copyright (C) 2009-2021, 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 #include namespace anki { // Forward class ShaderProgramParser; class ShaderProgramParserVariant; /// @addtogroup shader_compiler /// @{ /// @memberof ShaderProgramParser class ShaderProgramParserMutator { friend ShaderProgramParser; public: ShaderProgramParserMutator(GenericMemoryPoolAllocator alloc) : m_name(alloc) , m_values(alloc) { } CString getName() const { return m_name; } ConstWeakArray getValues() const { return m_values; } private: StringAuto m_name; DynamicArrayAuto m_values; }; /// @memberof ShaderProgramParser class ShaderProgramParserVariant { friend class ShaderProgramParser; public: ~ShaderProgramParserVariant() { for(String& s : m_sources) { s.destroy(m_alloc); } } CString getSource(ShaderType type) const { return m_sources[type]; } private: GenericMemoryPoolAllocator m_alloc; Array m_sources; }; /// 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 rewrite_mutation NAME_A VALUE0 NAME_B VALUE1 [NAME_C VALUE3...] to /// NAME_A VALUE4 NAME_B VALUE5 [NAME_C VALUE6...] /// #pragma anki start {vert | tessc | tesse | geom | frag | comp | rgen | ahit | chit | miss | int | call} /// #pragma anki end /// #pragma anki library "name" /// #pragma anki ray_type NUMBER /// #pragma anki reflect NAME /// /// Only the "anki input" should be in an ifdef-like guard. For everything else it's ignored. class ShaderProgramParser { public: ShaderProgramParser(CString fname, ShaderProgramFilesystemInterface* fsystem, GenericMemoryPoolAllocator alloc, const ShaderCompilerOptions& compilerOptions); ShaderProgramParser(const ShaderProgramParser&) = delete; // Non-copyable ~ShaderProgramParser(); ShaderProgramParser& operator=(const ShaderProgramParser&) = delete; // Non-copyable /// Parse the file and its includes. ANKI_USE_RESULT Error parse(); /// Given a mutation convert it to something acceptable. This will reduce the variants. /// @return true if the mutation was rewritten. Bool rewriteMutation(WeakArray mutation) const; /// Get the source (and a few more things) given a list of mutators. ANKI_USE_RESULT Error generateVariant(ConstWeakArray mutation, ShaderProgramParserVariant& variant) const; ConstWeakArray getMutators() const { return m_mutators; } ShaderTypeBit getShaderTypes() const { return m_shaderTypes; } U64 getHash() const { ANKI_ASSERT(m_codeSourceHash != 0); return m_codeSourceHash; } CString getLibraryName() const { return m_libName; } U32 getRayType() const { return m_rayType; } const StringListAuto& getSymbolsToReflect() const { return m_symbolsToReflect; } /// Generates the common header that will be used by all AnKi shaders. static void generateAnkiShaderHeader(ShaderType shaderType, const ShaderCompilerOptions& compilerOptions, StringAuto& header); private: using Mutator = ShaderProgramParserMutator; class MutationRewrite { public: class Record { public: U32 m_mutatorIndex = MAX_U32; MutatorValue m_valueFrom = getMaxNumericLimit(); MutatorValue m_valueTo = getMaxNumericLimit(); Bool operator!=(const Record& b) const { return !(m_mutatorIndex == b.m_mutatorIndex && m_valueFrom == b.m_valueFrom && m_valueTo == b.m_valueTo); } }; DynamicArrayAuto m_records; MutationRewrite(GenericMemoryPoolAllocator alloc) : m_records(alloc) { } }; static const U32 MAX_INCLUDE_DEPTH = 8; GenericMemoryPoolAllocator m_alloc; StringAuto m_fname; ShaderProgramFilesystemInterface* m_fsystem = nullptr; StringListAuto m_codeLines = {m_alloc}; ///< The code. StringAuto m_codeSource = {m_alloc}; U64 m_codeSourceHash = 0; DynamicArrayAuto m_mutators = {m_alloc}; DynamicArrayAuto m_mutationRewrites = {m_alloc}; ShaderTypeBit m_shaderTypes = ShaderTypeBit::NONE; Bool m_insideShader = false; ShaderCompilerOptions m_compilerOptions; StringAuto m_libName = {m_alloc}; U32 m_rayType = MAX_U32; StringListAuto m_symbolsToReflect = {m_alloc}; ANKI_USE_RESULT Error parseFile(CString fname, U32 depth); ANKI_USE_RESULT Error parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth); ANKI_USE_RESULT Error parseInclude(const StringAuto* begin, const StringAuto* end, CString line, CString fname, U32 depth); ANKI_USE_RESULT Error parsePragmaMutator(const StringAuto* begin, const StringAuto* end, CString line, CString fname); ANKI_USE_RESULT Error parsePragmaStart(const StringAuto* begin, const StringAuto* end, CString line, CString fname); ANKI_USE_RESULT Error parsePragmaEnd(const StringAuto* begin, const StringAuto* end, CString line, CString fname); ANKI_USE_RESULT Error parsePragmaRewriteMutation(const StringAuto* begin, const StringAuto* end, CString line, CString fname); ANKI_USE_RESULT Error parsePragmaLibraryName(const StringAuto* begin, const StringAuto* end, CString line, CString fname); ANKI_USE_RESULT Error parsePragmaRayType(const StringAuto* begin, const StringAuto* end, CString line, CString fname); ANKI_USE_RESULT Error parsePragmaReflect(const StringAuto* begin, const StringAuto* end, CString line, CString fname); void tokenizeLine(CString line, DynamicArrayAuto& tokens) const; static Bool tokenIsComment(CString token) { return token.getLength() >= 2 && token[0] == '/' && (token[1] == '/' || token[1] == '*'); } static Bool mutatorHasValue(const ShaderProgramParserMutator& mutator, MutatorValue value); }; /// @} } // end namespace anki