// // Copyright Contributors to the MaterialX Project // SPDX-License-Identifier: Apache-2.0 // #ifndef MATERIALX_SLANGPROGRAM_H #define MATERIALX_SLANGPROGRAM_H /// @file /// Slang code program #include #include #include #include #include #include MATERIALX_NAMESPACE_BEGIN using SlangFramebufferPtr = std::shared_ptr; using SlangProgramPtr = std::shared_ptr; class SlangProgram { public: static SlangProgramPtr create(SlangContextPtr context); ~SlangProgram(); /// @name Shader code setup /// @{ /// Set up code stages to validate based on an input hardware shader. /// @param shader Hardware shader to use void setStages(ShaderPtr shader); /// Set the code stages based on a list of stage strings. /// Refer to the ordering of stages as defined by a HwShader. /// Does not clear stages. /// @param stage Name of the shader stage. /// @param sourceCode Source code of the shader stage. void addStage(const string& stage, const string& sourceCode); /// Get source code string for a given stage. /// @return Shader stage string. String is empty if not found. const string& getStageSourceCode(const string& stage) const; /// Clear out any existing stages void clearStages(); /// Return the shader, if any, used to generate this program. ShaderPtr getShader() const { return _shader; } /// @brief Builds the program defined by vertex and pixel stages. /// Will throw on failure compilation failure. Upon success, /// rootObject is created and uniform values can be bound to it. /// @return true when rootObject has been created successfully. bool build(); struct UniformInput { /// Variable name, with `parent.child` name pattern for individual struct members. std::string name; /// All cursors to which to write the value (could be in both fragment and vertex shader cbuffers). std::vector slangCursors; /// Byte size of the slang type, cached from slangCursors. size_t slangByteSize; /// Default value to set onto SlangRHI descriptor. If not valid, all memory will be set to 0. ConstValuePtr slangDefaultValue; /// Only valid when shader is attached and used for printing. /// Element path (if any) std::string mxElementPath; /// Unit std::string mxUnit; /// Colorspace std::string mxColorspace; }; /// Program input structure shared pointer type using UniformInputPtr = std::shared_ptr; /// Program input shaded pointer map type using UniformInputMap = std::unordered_map; struct VertexInput { /// Index of the buffer to bind input to unsigned bufferIndex; /// Semantic name used to bind the input std::string valueSemanticName; /// TODO: The type does not match between Slang's getSemanticIndex and SlangRHI's InputElementDesc::semanticIndex. /// Opting for the SlangRHI type, but in the future this will probably turn into slang::SlangIndex uint32_t valueSemanticIndex = 0; /// Slang reflection type slang::TypeLayoutReflection* slangTypeLayout = nullptr; /// Input type string. Will only be non-empty if initialized stages with a HwShader string typeString; }; /// Program input structure shared pointer type using VertexInputPtr = std::shared_ptr; /// Program input shaded pointer map type using VertexInputMap = std::unordered_map; /// Get list of program input uniforms. /// The program must have been built first. const UniformInputMap& getUniformsList(); /// Get list of program vertex stage inputs. /// The program must have been built first. const VertexInputMap& getVertexInputsList(); /// Set the pass encoder and render state to bind further mesh/partition void bind() { } void bind(SlangRenderPassEncoder* passEncoder, SlangRenderState* renderState) { _passEncoder = passEncoder; _renderState = renderState; } void unbind() { _passEncoder = nullptr; _renderState = nullptr; } /// Return true if a uniform with the given name is present. bool hasUniform(const string& name); /// Bind a value to the uniform with the given name. void bindUniform(const string& name, ConstValuePtr value, bool errorIfMissing = true); protected: /// Binds all vertex inputs for a given mesh and caches them for the next call void bindVertexInputs(MeshPtr mesh, rhi::RenderState& renderState); public: /// Bind the partition (create index buffer), return the buffer and cache it. /// Binds it to the RenderState, if that is set. rhi::ComPtr bindPartition(MeshPartitionPtr part); /// Draw a previously bound partition void drawPartition(MeshPartitionPtr partition); /// Bind the mesh (creating input buffers if necessary) to the renderState. void bindMesh(MeshPtr mesh, rhi::RenderState& renderState); void bindMesh(MeshPtr mesh) { bindMesh(mesh, *_renderState); } /// Must be preceded by bindPartition() call for the same partition. /// TODO: Evaluate if this is actually necessary or should be a single call. rhi::ComPtr getIndexBuffer(MeshPartitionPtr partition); /// Clears all the bindMesh and bindPartition results. void unbindGeometry(); /// Bind all default input textures. Not env maps and not those already bound by bindTexture calls. void bindTextures(ImageHandlerPtr imageHandler); /// Bind a specific texture explicitly, will remove binding in `bindTextures` and will not be overriden by it. void bindTexture(ImageHandlerPtr imageHandler, const std::string& shaderTextureName, ImagePtr imagePtr, const ImageSamplingProperties& samplingProperties); /// Removes the explicit binding of a texture, the following call to `bindTextures` will bind the default one. void unbindTexture(const std::string& shaderTextureName) { _explicitBoundImages.erase(shaderTextureName); } void bindLighting(LightHandlerPtr lightHandler, ImageHandlerPtr imageHandler); void bindViewInformation(CameraPtr camera); void bindTimeAndFrame(float time = 0.0f, float frame = 1.0f); enum class PipelineKind2 { Default = 0, // automatic alpha blending (based on isTransparent) and no culling AlphaOn = (1 << 0), // alpha blending turned on AlphaOff = (1 << 1), // alpha turned on/off based on the transparency in the shader CullFront = (1 << 2), CullBack = (1 << 3), Wireframe = (1 << 4) }; rhi::ComPtr getPipeline(PipelineKind2 kind, rhi::Format colorFormat); rhi::ComPtr getPipeline(PipelineKind2 kind, const SlangFramebufferPtr& framebuffer); /// Binds the pipeline and the rootObject to it. Does not deep copy the rootObject (uniforms), /// until after void bindPipeline(rhi::IRenderPassEncoder* passEncoder, rhi::ComPtr pipeline) { passEncoder->bindPipeline(pipeline, _rootObject); } SlangShaderObject* getRootObject() { return _rootObject; } public: void clearBuiltData(); void printUniforms(std::ostream& outputStream); void printVertexInputs(std::ostream& outputStream); /// Returns if alpha blending is enabled. bool isTransparent() const { return _alphaBlendingEnabled; } public: static constexpr unsigned int UNDEFINED_SLANG_RESOURCE_ID = 0; protected: SlangProgram(SlangContextPtr context); void bindUniformDefaults(); bool isBuilt() const { return (bool) _rootObject; } void bindUniformGeomprops(MeshPtr mesh); const VertexInputMap& updateVertexInputList(); const UniformInputMap& updateUniformsList(); struct PipelineKey { PipelineKind2 kind; rhi::Format format; bool operator<(const PipelineKey& rhs) const { return std::tie(kind, format) < std::tie(rhs.kind, rhs.format); } }; public: std::string _name; SlangContextPtr _context; rhi::ComPtr _rootObject = nullptr; std::map> _pipelines; StringMap _stages; ShaderPtr _shader; slang::ProgramLayout* _slangReflection; rhi::ComPtr _shaderProgram; VertexInputMap _vertexInputsList; UniformInputMap _uniformList; std::map _explicitBoundImages; std::map> _indexBuffers; std::map> _vertexInputBuffers; rhi::ComPtr _inputLayout; MeshPtr _boundMesh; bool _alphaBlendingEnabled = false; SlangRenderPassEncoder* _passEncoder = nullptr; SlangRenderState* _renderState = nullptr; }; SLANG_RHI_ENUM_CLASS_OPERATORS(SlangProgram::PipelineKind2) MATERIALX_NAMESPACE_END #endif