#pragma once #include "GraphicsTags.hpp" #include "Ires.hpp" #include "RcPtr.hpp" #include "Utils.hpp" #include #include #include #include #include // TODO move to variable after pattern matching is in the language // Forward declarations class Shader; class IresShader; struct ShaderVariable { enum Kind { KD_Math, KD_Sampler, }; std::string name; Kind kind; GLuint location; Tags::VertexElementSemantic semantic = Tags::VES_Generic; virtual void ShowInfo() const = 0; protected: ShaderVariable(Kind kind) : kind{ kind } {} }; struct ShaderMathVariable : public ShaderVariable { GLenum scalarType; int arrayLength; int width; int height; ShaderMathVariable() : ShaderVariable(KD_Math) {} virtual void ShowInfo() const override; }; struct ShaderSamplerVariable : public ShaderVariable { GLenum type; int arrayLength; ShaderSamplerVariable() : ShaderVariable(KD_Sampler) {} virtual void ShowInfo() const override; }; struct ShaderThingId { enum Kind { KD_Input, KD_Output, KD_Uniform, KD_Invalid, }; Kind kind = KD_Invalid; int index = 0; bool IsValid() const; }; struct ShaderInfo { robin_hood::unordered_map things; std::vector inputs; std::vector outputs; std::vector> uniforms; GLuint FindInputLocation(Tags::VertexElementSemantic semantic); GLuint FindOutputLocation(Tags::VertexElementSemantic semantic); ShaderVariable* FindVariable(const ShaderThingId& thing); }; class Shader : public RefCounted { friend class IresShader; private: IresShader* mIres = nullptr; ShaderInfo mInfo; GLuint mProgram = 0; public: GLuint autofillLoc_Transform = Tags::kInvalidLocation; GLuint autofillLoc_Time = Tags::kInvalidLocation; GLuint autofillLoc_DeltaTime = Tags::kInvalidLocation; public: Shader(); ~Shader(); Shader(const Shader&) = delete; Shader& operator=(const Shader&) = delete; Shader(Shader&&) = default; Shader& operator=(Shader&&) = default; enum ErrorCode { EC_Success, /// Generated when Init*() functions are called on an already initialized Shader object. EC_AlreadyInitialized, /// Generated when the one-source-file text contains invalid or duplicate shader variants. EC_InvalidShaderVariant, EC_FileIoFailed, EC_CompilationFailed, EC_LinkingFailed, }; struct ShaderSources { std::string_view vertex; std::string_view geometry; std::string_view tessControl; std::string_view tessEval; std::string_view fragment; }; /// Create shader by compiling each shader source file separately and then combining them together /// into a Shader object. ErrorCode InitFromSources(const ShaderSources& sources); /// The given text will be split into different shader sections according to #type directives, /// and combined to form a Shader object. /// For OpenGL, this process involves compililng each section separately and then linking them /// together. /// /// There are a directive for each shader type /// - `#type vertex`: Vertex shader /// - `#type geometry`: Geometry shader /// - `#type tessellation_control`: Tessellation control shader /// - `#type tessellation_evaluation`: Tessellation evaluation shader /// - `#type fragment`: Fragment shader ErrorCode InitFromSource(std::string_view source); /// Rebuild info object using OpenGL shader introspection API. Requires OpenGL 4.3 or above. Overrides existing info object. bool GatherInfoShaderIntrospection(); const ShaderInfo& GetInfo() const { return mInfo; } ShaderInfo& GetInfo() { return mInfo; } /// If not empty, this name must not duplicate with any other shader object in the process. GLuint GetProgram() const { return mProgram; } IresShader* GetIres() const { return mIres; } bool IsValid() const; }; // Initialized in main() inline RcPtr gDefaultShader; class IresShader : public IresObject { private: RcPtr mInstance; std::string mSourceFile; public: IresShader(); Shader* GetInstance() const; void InvalidateInstance(); void ShowEditor(EditorInstance& editor) override; void Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const override; void Read(IresLoadingContext& ctx, const rapidjson::Value& value) override; };