diff --git a/src/libANGLE/renderer/metal/TextureMtl.mm b/src/libANGLE/renderer/metal/TextureMtl.mm --- a/src/libANGLE/renderer/metal/TextureMtl.mm +++ b/src/libANGLE/renderer/metal/TextureMtl.mm @@ -1162,15 +1162,24 @@ angle::Result TextureMtl::createViewFromBaseToMaxLevel() { ASSERT(mNativeTextureStorage); + + // Clamp the requested base level into the native storage's actual mip + // range. On the pBuffer / IOSurface path the texture is not immutable, so + // gl::TextureState::getEffectiveBaseLevel() can return a value beyond the + // 1-mip storage; passing that to createMipsView() would hand an + // out-of-range NSRange to -[MTLTexture newTextureViewWithPixelFormat: + // textureType:levels:slices:], which is undefined behaviour. + const uint32_t effectiveBase = + std::clamp(mState.getEffectiveBaseLevel(), mNativeTextureStorage->getBaseGLLevel(), + mNativeTextureStorage->getMaxSupportedGLLevel()); + uint32_t maxLevel = std::min(mNativeTextureStorage->getMaxSupportedGLLevel(), mState.getEffectiveMaxLevel()); - - // In edge case where base level > max level, clamp up to base level. - maxLevel = std::max(maxLevel, mState.getEffectiveBaseLevel()); + maxLevel = std::max(maxLevel, effectiveBase); mtl::TextureRef nativeViewFromBaseToMaxLevelRef; if (maxLevel == mNativeTextureStorage->getMaxSupportedGLLevel() && - mState.getEffectiveBaseLevel() == mNativeTextureStorage->getBaseGLLevel()) + effectiveBase == mNativeTextureStorage->getBaseGLLevel()) { // If base & max level are the same in mNativeTextureStorage, we don't need // a dedicated view. Furthermore, Intel driver has some bugs when sampling a view @@ -1179,13 +1188,13 @@ } else { - uint32_t baseToMaxLevels = maxLevel - mState.getEffectiveBaseLevel() + 1; + uint32_t baseToMaxLevels = maxLevel - effectiveBase + 1; nativeViewFromBaseToMaxLevelRef = - mNativeTextureStorage->createMipsView(mState.getEffectiveBaseLevel(), baseToMaxLevels); + mNativeTextureStorage->createMipsView(effectiveBase, baseToMaxLevels); } mViewFromBaseToMaxLevel = std::make_unique( - nativeViewFromBaseToMaxLevelRef, mState.getEffectiveBaseLevel(), + nativeViewFromBaseToMaxLevelRef, effectiveBase, mNativeTextureStorage->getFormat()); // Recreate in bindToShader()