#pragma once #include "EditorAttachment.hpp" #include "GraphicsTags.hpp" #include "RcPtr.hpp" #include #include #include #include #include #include // TODO move to variable after pattern matching is in the language // Forward declarations class Shader; struct ShaderVariable { enum Kind { KD_Math, KD_Sampler, }; std::string name; Kind kind; GLuint location; protected: ShaderVariable(Kind kind) : kind{ kind } {} }; struct ShaderMathVariable : public ShaderVariable { GLenum scalarType; int arrayLength; int width; int height; ShaderMathVariable() : ShaderVariable(KD_Math) {} }; struct ShaderSamplerVariable : public ShaderVariable { GLenum type; int arrayLength; ShaderSamplerVariable() : ShaderVariable(KD_Sampler) {} }; struct ShaderThingId { enum Kind { KD_Input, KD_Output, KD_Uniform, KD_Invalid, }; Kind kind = KD_Invalid; int index = 0; bool IsValid() const; }; struct ShaderInfo { struct InputOutputThing { ShaderMathVariable variable; Tags::VertexElementSemantic semantic = Tags::VES_Generic; }; absl::flat_hash_map things; std::vector inputs; std::vector outputs; std::vector> uniforms; ShaderVariable* FindVariable(const ShaderThingId& thing); bool SaveToFile(const std::filesystem::path& filePath) const; bool LoadFromFile(const std::filesystem::path& filePath); void LoadLocations(const Shader& ownerShader); }; class Shader : public RefCounted { private: std::string mName; std::unique_ptr mInfo; std::unique_ptr mEditorAttachment; GLuint mHandle = 0; public: Shader(std::string name = ""); ~Shader(); Shader(const Shader&) = delete; Shader& operator=(const Shader&) = delete; Shader(Shader&&) = default; Shader& operator=(Shader&&) = default; enum ErrorCode { Success, /// Generated when Init*() functions are called on an already initialized Shader object. ShaderAlreadyCreated, /// Generated when the one-source-file text contains invalid or duplicate shader variants. InvalidShaderVariant, FileIOFailed, CompilationFailed, 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(); bool CreateEmptyInfoIfAbsent(); bool GatherInfoIfAbsent(); ShaderInfo* GetInfo() const; /// If not empty, this name must not duplicate with any other shader object in the process. const std::string& GetName() const; GLuint GetProgram() const; bool IsValid() const; }; class ShaderManager { public: static inline ShaderManager* instance = nullptr; private: absl::flat_hash_map> mShaders; public: void DiscoverShaders(); const auto& GetShaders() const { return mShaders; } Shader* FindShader(std::string_view name); };