// Copyright (C) 2009-2020, 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} /// #pragma anki end /// /// Only the "anki input" should be in an ifdef-like guard. For everything else it's ignored. class ShaderProgramParser : public NonCopyable { public: ShaderProgramParser(CString fname, ShaderProgramFilesystemInterface* fsystem, GenericMemoryPoolAllocator alloc, const GpuDeviceCapabilities& gpuCapabilities, const BindlessLimits& bindlessLimits); ~ShaderProgramParser(); /// 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; } 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}; DynamicArrayAuto m_mutators = {m_alloc}; DynamicArrayAuto m_mutationRewrites = {m_alloc}; ShaderTypeBit m_shaderTypes = ShaderTypeBit::NONE; Bool m_insideShader = false; GpuDeviceCapabilities m_gpuCapabilities; BindlessLimits m_bindlessLimits; 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); 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