// // 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. // // blocklayoutMetal.cpp: // Implementation for metal block layout classes and methods. // #include "libANGLE/renderer/metal/blocklayoutMetal.h" #include "common/mathutil.h" #include "common/utilities.h" #include "compiler/translator/blocklayout.h" namespace rx { namespace mtl { // Sizes and types are available at // https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf // Section 2 size_t GetMetalSizeForGLType(GLenum type) { switch (type) { case GL_BOOL: return 1; case GL_BOOL_VEC2: return 2; case GL_BOOL_VEC3: case GL_BOOL_VEC4: return 4; case GL_FLOAT: return 4; case GL_FLOAT_VEC2: return 8; case GL_FLOAT_VEC3: case GL_FLOAT_VEC4: return 16; case GL_FLOAT_MAT2: // 2x2 return 16; case GL_FLOAT_MAT3: // 3x4 return 48; case GL_FLOAT_MAT4: // 4x4 return 64; case GL_FLOAT_MAT2x3: // 2x4 return 32; case GL_FLOAT_MAT3x2: // 3x2 return 24; case GL_FLOAT_MAT2x4: // 2x4 return 32; case GL_FLOAT_MAT4x2: // 4x2 return 32; case GL_FLOAT_MAT3x4: // 3x4 return 48; case GL_FLOAT_MAT4x3: // 4x4 return 64; case GL_INT: return 4; case GL_INT_VEC2: return 8; case GL_INT_VEC3: case GL_INT_VEC4: return 16; case GL_UNSIGNED_INT: return 4; case GL_UNSIGNED_INT_VEC2: return 8; case GL_UNSIGNED_INT_VEC3: case GL_UNSIGNED_INT_VEC4: return 16; case GL_SAMPLER_2D: case GL_SAMPLER_2D_RECT_ANGLE: case GL_SAMPLER_3D: case GL_SAMPLER_CUBE: case GL_SAMPLER_CUBE_MAP_ARRAY: case GL_SAMPLER_2D_ARRAY: case GL_SAMPLER_EXTERNAL_OES: case GL_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: case GL_INT_SAMPLER_BUFFER: case GL_INT_SAMPLER_2D: case GL_INT_SAMPLER_3D: case GL_INT_SAMPLER_CUBE: case GL_INT_SAMPLER_CUBE_MAP_ARRAY: case GL_INT_SAMPLER_2D_ARRAY: case GL_INT_SAMPLER_2D_MULTISAMPLE: case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: case GL_UNSIGNED_INT_SAMPLER_2D: case GL_UNSIGNED_INT_SAMPLER_3D: case GL_UNSIGNED_INT_SAMPLER_CUBE: case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: case GL_SAMPLER_2D_SHADOW: case GL_SAMPLER_BUFFER: case GL_SAMPLER_CUBE_SHADOW: case GL_SAMPLER_2D_ARRAY_SHADOW: case GL_IMAGE_2D: case GL_INT_IMAGE_2D: case GL_UNSIGNED_INT_IMAGE_2D: case GL_IMAGE_3D: case GL_INT_IMAGE_3D: case GL_UNSIGNED_INT_IMAGE_3D: case GL_IMAGE_2D_ARRAY: case GL_INT_IMAGE_2D_ARRAY: case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: case GL_IMAGE_CUBE: case GL_INT_IMAGE_CUBE: case GL_UNSIGNED_INT_IMAGE_CUBE: case GL_IMAGE_CUBE_MAP_ARRAY: case GL_INT_IMAGE_CUBE_MAP_ARRAY: case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: case GL_IMAGE_BUFFER: case GL_INT_IMAGE_BUFFER: case GL_UNSIGNED_INT_SAMPLER_BUFFER: case GL_UNSIGNED_INT_IMAGE_BUFFER: case GL_UNSIGNED_INT_ATOMIC_COUNTER: case GL_SAMPLER_VIDEO_IMAGE_WEBGL: case GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT: UNREACHABLE(); break; default: UNREACHABLE(); break; } return 0; } size_t GetMetalAlignmentForGLType(GLenum type) { switch (type) { case GL_BOOL: return 1; case GL_BOOL_VEC2: return 2; case GL_BOOL_VEC3: case GL_BOOL_VEC4: return 4; case GL_FLOAT: return 4; case GL_FLOAT_VEC2: return 8; case GL_FLOAT_VEC3: case GL_FLOAT_VEC4: return 16; case GL_FLOAT_MAT2: return 8; case GL_FLOAT_MAT3: return 16; case GL_FLOAT_MAT4: return 16; case GL_FLOAT_MAT2x3: return 16; case GL_FLOAT_MAT3x2: return 8; case GL_FLOAT_MAT2x4: return 16; case GL_FLOAT_MAT4x2: return 8; case GL_FLOAT_MAT3x4: return 16; case GL_FLOAT_MAT4x3: return 16; case GL_INT: return 4; case GL_INT_VEC2: return 8; case GL_INT_VEC3: return 16; case GL_INT_VEC4: return 16; case GL_UNSIGNED_INT: return 4; case GL_UNSIGNED_INT_VEC2: return 8; case GL_UNSIGNED_INT_VEC3: case GL_UNSIGNED_INT_VEC4: return 16; case GL_SAMPLER_2D: case GL_SAMPLER_2D_RECT_ANGLE: case GL_SAMPLER_3D: case GL_SAMPLER_CUBE: case GL_SAMPLER_CUBE_MAP_ARRAY: case GL_SAMPLER_2D_ARRAY: case GL_SAMPLER_EXTERNAL_OES: case GL_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: case GL_INT_SAMPLER_BUFFER: case GL_INT_SAMPLER_2D: case GL_INT_SAMPLER_3D: case GL_INT_SAMPLER_CUBE: case GL_INT_SAMPLER_CUBE_MAP_ARRAY: case GL_INT_SAMPLER_2D_ARRAY: case GL_INT_SAMPLER_2D_MULTISAMPLE: case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: case GL_UNSIGNED_INT_SAMPLER_2D: case GL_UNSIGNED_INT_SAMPLER_3D: case GL_UNSIGNED_INT_SAMPLER_CUBE: case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: case GL_SAMPLER_2D_SHADOW: case GL_SAMPLER_BUFFER: case GL_SAMPLER_CUBE_SHADOW: case GL_SAMPLER_2D_ARRAY_SHADOW: case GL_IMAGE_2D: case GL_INT_IMAGE_2D: case GL_UNSIGNED_INT_IMAGE_2D: case GL_IMAGE_3D: case GL_INT_IMAGE_3D: case GL_UNSIGNED_INT_IMAGE_3D: case GL_IMAGE_2D_ARRAY: case GL_INT_IMAGE_2D_ARRAY: case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: case GL_IMAGE_CUBE: case GL_INT_IMAGE_CUBE: case GL_UNSIGNED_INT_IMAGE_CUBE: case GL_IMAGE_CUBE_MAP_ARRAY: case GL_INT_IMAGE_CUBE_MAP_ARRAY: case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: case GL_IMAGE_BUFFER: case GL_INT_IMAGE_BUFFER: case GL_UNSIGNED_INT_SAMPLER_BUFFER: case GL_UNSIGNED_INT_IMAGE_BUFFER: case GL_UNSIGNED_INT_ATOMIC_COUNTER: case GL_SAMPLER_VIDEO_IMAGE_WEBGL: case GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT: UNREACHABLE(); break; default: UNREACHABLE(); break; } return 0; } size_t GetMTLBaseAlignment(GLenum variableType, bool isRowMajor) { return mtl::GetMetalAlignmentForGLType(variableType); } void MetalAlignmentVisitor::visitVariable(const sh::ShaderVariable &variable, bool isRowMajor) { size_t baseAlignment = GetMTLBaseAlignment(variable.type, isRowMajor); mCurrentAlignment = std::max(mCurrentAlignment, baseAlignment); } BlockLayoutEncoderMTL::BlockLayoutEncoderMTL() : BlockLayoutEncoder() {} void BlockLayoutEncoderMTL::getBlockLayoutInfo(GLenum type, const std::vector &arraySizes, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut) { size_t baseAlignment = 0; int matrixStride = 0; int arrayStride = 0; if (gl::IsMatrixType(type)) { baseAlignment = static_cast(mtl::GetMetalAlignmentForGLType(type)); matrixStride = static_cast(mtl::GetMetalAlignmentForGLType(type)); if (!arraySizes.empty()) { arrayStride = static_cast(mtl::GetMetalSizeForGLType(type)); } } else if (!arraySizes.empty()) { baseAlignment = static_cast(mtl::GetMetalAlignmentForGLType(type)); arrayStride = static_cast(mtl::GetMetalSizeForGLType(type)); } else { baseAlignment = mtl::GetMetalAlignmentForGLType(type); } align(baseAlignment); *matrixStrideOut = matrixStride; *arrayStrideOut = arrayStride; } sh::BlockMemberInfo BlockLayoutEncoderMTL::encodeType(GLenum type, const std::vector &arraySizes, bool isRowMajorMatrix) { int arrayStride; int matrixStride; getBlockLayoutInfo(type, arraySizes, isRowMajorMatrix, &arrayStride, &matrixStride); const sh::BlockMemberInfo memberInfo( type, static_cast(mCurrentOffset), static_cast(arrayStride), static_cast(matrixStride), gl::ArraySizeProduct(arraySizes), isRowMajorMatrix); assert(memberInfo.offset >= 0); advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride, matrixStride); return memberInfo; } sh::BlockMemberInfo BlockLayoutEncoderMTL::encodeArrayOfPreEncodedStructs( size_t size, const std::vector &arraySizes) { const unsigned int innerArraySizeProduct = gl::InnerArraySizeProduct(arraySizes); const unsigned int outermostArraySize = gl::OutermostArraySize(arraySizes); // The size of struct is expected to be already aligned appropriately. const size_t arrayStride = size * innerArraySizeProduct; // Aggregate blockMemberInfo types not needed: only used by the Metal bakcend. const sh::BlockMemberInfo memberInfo(GL_INVALID_ENUM, static_cast(mCurrentOffset), static_cast(arrayStride), -1, gl::ArraySizeProduct(arraySizes), false); angle::base::CheckedNumeric checkedOffset(arrayStride); checkedOffset *= outermostArraySize; checkedOffset += mCurrentOffset; mCurrentOffset = checkedOffset.ValueOrDie(); return memberInfo; } size_t BlockLayoutEncoderMTL::getCurrentOffset() const { angle::base::CheckedNumeric checkedOffset(mCurrentOffset); return checkedOffset.ValueOrDie(); } void BlockLayoutEncoderMTL::enterAggregateType(const sh::ShaderVariable &structVar) { align(getBaseAlignment(structVar)); } void BlockLayoutEncoderMTL::exitAggregateType(const sh::ShaderVariable &structVar) { align(getBaseAlignment(structVar)); } size_t BlockLayoutEncoderMTL::getShaderVariableSize(const sh::ShaderVariable &structVar, bool isRowMajor) { size_t currentOffset = mCurrentOffset; mCurrentOffset = 0; sh::BlockEncoderVisitor visitor("", "", this); enterAggregateType(structVar); TraverseShaderVariables(structVar.fields, isRowMajor, &visitor); exitAggregateType(structVar); size_t structVarSize = getCurrentOffset(); mCurrentOffset = currentOffset; return structVarSize; } void BlockLayoutEncoderMTL::advanceOffset(GLenum type, const std::vector &arraySizes, bool isRowMajorMatrix, int arrayStride, int matrixStride) { if (!arraySizes.empty()) { angle::base::CheckedNumeric checkedOffset(arrayStride); checkedOffset *= gl::ArraySizeProduct(arraySizes); checkedOffset += mCurrentOffset; mCurrentOffset = checkedOffset.ValueOrDie(); } else if (gl::IsMatrixType(type)) { angle::base::CheckedNumeric checkedOffset; checkedOffset = mtl::GetMetalSizeForGLType(type); checkedOffset += mCurrentOffset; mCurrentOffset = checkedOffset.ValueOrDie(); } else { angle::base::CheckedNumeric checkedOffset(mCurrentOffset); checkedOffset += mtl::GetMetalSizeForGLType(type); mCurrentOffset = checkedOffset.ValueOrDie(); } } size_t BlockLayoutEncoderMTL::getBaseAlignment(const sh::ShaderVariable &shaderVar) const { if (shaderVar.isStruct()) { MetalAlignmentVisitor visitor; TraverseShaderVariables(shaderVar.fields, false, &visitor); return visitor.getBaseAlignment(); } return GetMTLBaseAlignment(shaderVar.type, shaderVar.isRowMajorLayout); } size_t BlockLayoutEncoderMTL::getTypeBaseAlignment(GLenum type, bool isRowMajorMatrix) const { return GetMTLBaseAlignment(type, isRowMajorMatrix); } } // namespace mtl } // namespace rx