/* * Copyright (C) 2023 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #ifndef ANTKEEPER_GL_SHADER_TEMPLATE_HPP #define ANTKEEPER_GL_SHADER_TEMPLATE_HPP #include #include #include #include #include #include #include #include #include namespace gl { /** * Template used to for generating one or more shader variants from a single source. * * Shader templates support the following preprocessor directives: * * * `#pragma vertex`: Replaced with `#define __VERTEX__` when generating vertex shader objects. * * `#pragma fragment`: Replaced with `#define __FRAGMENT__` when generating fragment shader objects. * * `#pragma geometry`: Replaced with `#define __GEOMETRY__` when generating geometry shader objects. * * `#pragma define `: Will be replaced with `#define ` if its definition is passed to the shader template. * * @see gl::shader_stage * @see gl::shader_object * @see gl::shader_program */ class shader_template { public: /// Container of definitions used to generate `#pragma define ` directives. using dictionary_type = std::unordered_map; /** * Constructs a shader template and sets its source code. * * @param source_code Shader template source code. */ /// @{ explicit shader_template(const text_file& source_code); explicit shader_template(text_file&& source_code); /// @} /** * Constructs a shader template and sets its source code. * * @param source_code Shader template source code. * @param include_files Shader template include files. * * @note This constuctor is used to keep the loaded include files cached. */ shader_template(text_file&& source_code, std::vector>&& include_files); /** * Constructs an empty shader template. */ constexpr shader_template() noexcept = default; /** * Replaces the source code of the shader template. * * @param source_code Shader template source code. */ /// @{ void source(const text_file& source_code); void source(text_file&& source_code); /// @} /** * Configures shader object source code for a given shader stage and template dictionary. * * @param stage Shader stage of the shader object to generate. Instances of `#pragma ` in the template source will be replaced with `#define ____`. * @param definitions Container of definitions used to replace `#pragma define ` directives. * * @return Configured shader object source code. */ [[nodiscard]] std::string configure(gl::shader_stage stage, const dictionary_type& definitions = {}) const; /** * Configures and compiles a shader object. * * @param stage Shader stage of the shader object to generate. Instances of `#pragma ` in the template source will be replaced with `#define ____`. * @param definitions Container of definitions used to replace `#pragma define ` directives. * * @return Compiled shader object. * * @exception std::runtime_error Any exceptions thrown by gl::shader_object. */ [[nodiscard]] std::unique_ptr compile(gl::shader_stage stage, const dictionary_type& definitions = {}) const; /** * Configures and compiles shader objects, then links them into a shader program. Shader object stages are determined according to the presence of `#pragma ` directives. * * @param definitions Container of definitions used to replace `#pragma define ` directives. * * @return Linked shader program. * * @exception std::runtime_error Any exceptions thrown by gl::shader_object or gl::shader_program. * * @see has_vertex_directive() const * @see has_fragment_directive() const * @see has_geometry_directive() const */ [[nodiscard]] std::unique_ptr build(const dictionary_type& definitions = {}) const; /// Returns `true` if the template source contains one or more `#pragma vertex` directive. [[nodiscard]] inline bool has_vertex_directive() const noexcept { return !m_vertex_directives.empty(); } /// Returns `true` if the template source contains one or more `#pragma fragment` directive. [[nodiscard]] inline bool has_fragment_directive() const noexcept { return !m_fragment_directives.empty(); } /// Returns `true` if the template source contains one or more `#pragma geometry` directive. [[nodiscard]] inline bool has_geometry_directive() const noexcept { return !m_geometry_directives.empty(); } /** * Returns `true` if the template source contains one or more instance of `#pragma define `. * * @param key Definition key. */ [[nodiscard]] bool has_define_directive(const std::string& key) const; /// Returns a hash of the template source code. [[nodiscard]] inline constexpr std::size_t hash() const noexcept { return m_hash; } private: void find_directives(); void rehash(); void replace_stage_directives(gl::shader_stage stage) const; void replace_define_directives(const dictionary_type& definitions) const; mutable text_file m_template_source; std::unordered_set m_vertex_directives; std::unordered_set m_fragment_directives; std::unordered_set m_geometry_directives; std::multimap m_define_directives; std::size_t m_hash{0}; std::vector> m_include_files; }; } // namespace gl #endif // ANTKEEPER_GL_SHADER_TEMPLATE_HPP