#pragma once #include "EditorAttachment.hpp" #include "GraphicsTags.hpp" #include "RcPtr.hpp" #include "Utils.hpp" #include #include #include #include #include #include // TODO move to variable after pattern matching is in the language // TODO migrate shader editor to Ires // Forward declarations class Shader; 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 { private: std::string mName; ShaderInfo mInfo; std::unique_ptr mEditorAttachment; GLuint mProgram = 0; public: GLuint autofillLoc_Transform = Tags::kInvalidLocation; GLuint autofillLoc_Time = Tags::kInvalidLocation; GLuint autofillLoc_DeltaTime = Tags::kInvalidLocation; public: Shader(std::string name = ""); ~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); EditorAttachment* GetEditorAttachment() const { return mEditorAttachment.get(); } void SetEditorAttachment(EditorAttachment* attachment) { mEditorAttachment.reset(attachment); } void GetDesignatedMetadataPath(char* buffer, int bufferSize); std::filesystem::path GetDesignatedMetadataPath(); /// 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. const std::string& GetName() const { return mName; } GLuint GetProgram() const { return mProgram; } bool IsAnnoymous() const; bool IsValid() const; bool SaveMetadataToFile(const std::filesystem::path& filePath) const; /// Overrides existing info object. bool LoadMetadataFromFile(const std::filesystem::path& filePath); }; class ShaderManager { public: static inline ShaderManager* instance = nullptr; private: robin_hood::unordered_map> mShaders; public: void DiscoverShaders(); const auto& GetShaders() const { return mShaders; } Shader* FindShader(std::string_view name); };