// // Copyright 2023 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // ProgramExecutableMtl.h: Implementation of ProgramExecutableImpl. #ifndef LIBANGLE_RENDERER_MTL_PROGRAMEXECUTABLEMTL_H_ #define LIBANGLE_RENDERER_MTL_PROGRAMEXECUTABLEMTL_H_ #include "libANGLE/ProgramExecutable.h" #include "libANGLE/renderer/ProgramExecutableImpl.h" #include "libANGLE/renderer/metal/mtl_buffer_pool.h" #include "libANGLE/renderer/metal/mtl_command_buffer.h" #include "libANGLE/renderer/metal/mtl_common.h" #include "libANGLE/renderer/metal/mtl_msl_utils.h" #include "libANGLE/renderer/metal/mtl_resources.h" #include "libANGLE/renderer/metal/mtl_state_cache.h" namespace rx { class ContextMtl; struct UBOConversionInfo { UBOConversionInfo(const std::vector &stdInfo, const std::vector &metalInfo, size_t stdSize, size_t metalSize) : _stdInfo(stdInfo), _metalInfo(metalInfo), _stdSize(stdSize), _metalSize(metalSize) { _needsConversion = _calculateNeedsConversion(); } const std::vector &stdInfo() const { return _stdInfo; } const std::vector &metalInfo() const { return _metalInfo; } size_t stdSize() const { return _stdSize; } size_t metalSize() const { return _metalSize; } bool needsConversion() const { return _needsConversion; } private: std::vector _stdInfo, _metalInfo; size_t _stdSize, _metalSize; bool _needsConversion; bool _calculateNeedsConversion() { // If we have a different number of fields then we need conversion if (_stdInfo.size() != _metalInfo.size()) { return true; } for (size_t i = 0; i < _stdInfo.size(); ++i) { // If the matrix is transposed if (_stdInfo[i].isRowMajorMatrix) { return true; } // If we have a bool if (gl::VariableComponentType(_stdInfo[i].type) == GL_BOOL) { return true; } // If any offset information is different if (!(_stdInfo[i] == _metalInfo[i])) { return true; } } return false; } }; struct ProgramArgumentBufferEncoderMtl { void reset(ContextMtl *contextMtl); mtl::AutoObjCPtr> metalArgBufferEncoder; mtl::BufferPool bufferPool; }; constexpr size_t kFragmentShaderVariants = 4; // Represents a specialized shader variant. For example, a shader variant with fragment coverage // mask enabled and a shader variant without. struct ProgramShaderObjVariantMtl { void reset(ContextMtl *contextMtl); mtl::AutoObjCPtr> metalShader; // UBO's argument buffer encoder. Used when number of UBOs used exceeds number of allowed // discrete slots, and thus needs to encode all into one argument buffer. ProgramArgumentBufferEncoderMtl uboArgBufferEncoder; // Store reference to the TranslatedShaderInfo to easy querying mapped textures/UBO/XFB // bindings. const mtl::TranslatedShaderInfo *translatedSrcInfo; }; // State for the default uniform blocks. struct DefaultUniformBlockMtl final : private angle::NonCopyable { DefaultUniformBlockMtl(); ~DefaultUniformBlockMtl(); // Shadow copies of the shader uniform data. angle::MemoryBuffer uniformData; // Since the default blocks are laid out in std140, this tells us where to write on a call // to a setUniform method. They are arranged in uniform location order. std::vector uniformLayout; }; class ProgramExecutableMtl : public ProgramExecutableImpl { public: ProgramExecutableMtl(const gl::ProgramExecutable *executable); ~ProgramExecutableMtl() override; void destroy(const gl::Context *context) override; angle::Result load(ContextMtl *contextMtl, gl::BinaryInputStream *stream); void save(gl::BinaryOutputStream *stream); void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; void setUniform2fv(GLint location, GLsizei count, const GLfloat *v) override; void setUniform3fv(GLint location, GLsizei count, const GLfloat *v) override; void setUniform4fv(GLint location, GLsizei count, const GLfloat *v) override; void setUniform1iv(GLint location, GLsizei count, const GLint *v) override; void setUniform2iv(GLint location, GLsizei count, const GLint *v) override; void setUniform3iv(GLint location, GLsizei count, const GLint *v) override; void setUniform4iv(GLint location, GLsizei count, const GLint *v) override; void setUniform1uiv(GLint location, GLsizei count, const GLuint *v) override; void setUniform2uiv(GLint location, GLsizei count, const GLuint *v) override; void setUniform3uiv(GLint location, GLsizei count, const GLuint *v) override; void setUniform4uiv(GLint location, GLsizei count, const GLuint *v) override; void setUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override; void setUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override; void setUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override; void setUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override; void setUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override; void setUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override; void setUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override; void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override; void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override; void getUniformfv(const gl::Context *context, GLint location, GLfloat *params) const override; void getUniformiv(const gl::Context *context, GLint location, GLint *params) const override; void getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const override; bool hasFlatAttribute() const { return mProgramHasFlatAttributes; } // Calls this before drawing, changedPipelineDesc is passed when vertex attributes desc and/or // shader program changed. angle::Result setupDraw(const gl::Context *glContext, mtl::RenderCommandEncoder *cmdEncoder, const mtl::RenderPipelineDesc &pipelineDesc, bool pipelineDescChanged, bool forceTexturesSetting, bool uniformBuffersDirty); private: friend class ProgramMtl; void reset(ContextMtl *context); template void setUniformMatrixfv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); template void getUniformImpl(GLint location, T *v, GLenum entryPointType) const; template void setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType); void saveTranslatedShaders(gl::BinaryOutputStream *stream); void loadTranslatedShaders(gl::BinaryInputStream *stream); void saveShaderInternalInfo(gl::BinaryOutputStream *stream); void loadShaderInternalInfo(gl::BinaryInputStream *stream); void saveInterfaceBlockInfo(gl::BinaryOutputStream *stream); angle::Result loadInterfaceBlockInfo(gl::BinaryInputStream *stream); void saveDefaultUniformBlocksInfo(gl::BinaryOutputStream *stream); angle::Result loadDefaultUniformBlocksInfo(mtl::Context *context, gl::BinaryInputStream *stream); void linkUpdateHasFlatAttributes(const gl::SharedCompiledShaderState &vertexShader); angle::Result initDefaultUniformBlocks( mtl::Context *context, const gl::ShaderMap &shaders); angle::Result resizeDefaultUniformBlocksMemory(mtl::Context *context, const gl::ShaderMap &requiredBufferSize); void initUniformBlocksRemapper(const gl::SharedCompiledShaderState &shader); mtl::BufferPool *getBufferPool(ContextMtl *context, gl::ShaderType shaderType); angle::Result getSpecializedShader(ContextMtl *context, gl::ShaderType shaderType, const mtl::RenderPipelineDesc &renderPipelineDesc, id *shaderOut); angle::Result commitUniforms(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder); angle::Result updateTextures(const gl::Context *glContext, mtl::RenderCommandEncoder *cmdEncoder, bool forceUpdate); angle::Result updateUniformBuffers(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder, const mtl::RenderPipelineDesc &pipelineDesc); angle::Result updateXfbBuffers(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder, const mtl::RenderPipelineDesc &pipelineDesc); angle::Result legalizeUniformBufferOffsets(ContextMtl *context); angle::Result bindUniformBuffersToDiscreteSlots(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder, gl::ShaderType shaderType); angle::Result encodeUniformBuffersInfoArgumentBuffer(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder, gl::ShaderType shaderType); bool mProgramHasFlatAttributes; gl::ShaderMap mDefaultUniformBlocks; std::unordered_map mUniformBlockConversions; // Translated metal shaders: gl::ShaderMap mMslShaderTranslateInfo; // Translated metal version for transform feedback only vertex shader: // - Metal doesn't allow vertex shader to write to both buffers and to stage output // (gl_Position). Need a special version of vertex shader that only writes to transform feedback // buffers. mtl::TranslatedShaderInfo mMslXfbOnlyVertexShaderInfo; // Compiled native shader object variants: // - Vertex shader: One with emulated rasterization discard, one with true rasterization // discard, one without. mtl::RenderPipelineRasterStateMap mVertexShaderVariants; // - Fragment shader: Combinations of sample coverage mask and depth write enabled states. std::array mFragmentShaderVariants; // Cached references of current shader variants. gl::ShaderMap mCurrentShaderVariants; gl::ShaderBitSet mDefaultUniformBlocksDirty; gl::ShaderBitSet mSamplerBindingsDirty; // Scratch data: // Legalized buffers and their offsets. For example, uniform buffer's offset=1 is not a valid // offset, it will be converted to legal offset and the result is stored in this array. std::vector> mLegalizedOffsetedUniformBuffers; // Stores the render stages usage of each uniform buffer. Only used if the buffers are encoded // into an argument buffer. std::vector mArgumentBufferRenderStageUsages; uint32_t mShadowCompareModes[mtl::kMaxShaderSamplers]; gl::ShaderMap> mDefaultUniformBufferPools; }; angle::Result CreateMslShaderLib(mtl::Context *context, gl::InfoLog &infoLog, mtl::TranslatedShaderInfo *translatedMslInfo, const std::map &substitutionMacros); } // namespace rx #endif // LIBANGLE_RENDERER_MTL_PROGRAMEXECUTABLEMTL_H_