Metal着色器叠加Overlay时水平拉伸2倍的问题排查
问题原因分析
你的Overlay被水平拉伸2倍,且GPU捕获显示Y/UV分量存在左右重复的图像,核心问题出在Metal纹理与原CVPixelBuffer的格式/布局不匹配,具体可能是以下几点:
- 纹理像素格式不匹配:原CVPixelBuffer是
kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange(10位4:2:2格式),但你将Y纹理创建为r16Unorm、UV纹理创建为rg16Unorm(16位格式),导致Metal错误解析内存中的10位数据,将原本的单帧图像解析为重复的两部分。 - 纹理宽度参数错误:创建Y/UV纹理时,错误设置了宽度值(比如将Y纹理宽度设为960而非1920),同时纹理的
wrapS模式为MTLWrapModeRepeat,导致线程调度时超过纹理宽度的坐标被重复映射,最终写入重复的图像内容。 - CVPixelBuffer字节行对齐问题:原像素缓冲区的Y/UV平面字节行间距与Metal纹理的行字节数不匹配,导致内存读取时出现偏移,进而产生图像重复。
解决方案
1. 严格匹配纹理与像素缓冲区的格式
从CVPixelBuffer创建Metal纹理时,使用对应10位的像素格式:
- Y平面(
kCVPixelFormatType_10BitY)对应Metal格式:MTLPixelFormatR10Unorm - UV平面(
kCVPixelFormatType_10BitCbCr)对应Metal格式:MTLPixelFormatRG10Unorm
2. 正确设置纹理的宽高参数
创建纹理时,直接从CVPixelBuffer获取平面的真实宽高:
// 获取Y平面的宽高 NSInteger yWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); NSInteger yHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); // UV平面的宽高 NSInteger uvWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1); NSInteger uvHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1); // 创建Y纹理描述符 MTLTextureDescriptor *yDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR10Unorm width:yWidth height:yHeight mipmapped:NO]; yDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; MTLTexture *videoY = [device newTextureWithDescriptor:yDesc]; // 将CVPixelBuffer的Y平面内容复制到纹理 CVPixelBufferLockBaseAddress(pixelBuffer, 0); [videoY replaceRegion:MTLRegionMake2D(0, 0, yWidth, yHeight) mipmapLevel:0 withBytes:CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0) bytesPerRow:CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)]; CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
3. 禁用纹理重复模式
确保纹理的wrapS和wrapT设置为MTLWrapModeClampToEdge,避免超出纹理范围的坐标被重复映射:
yDesc.wrapS = MTLWrapModeClampToEdge; yDesc.wrapT = MTLWrapModeClampToEdge; uvDesc.wrapS = MTLWrapModeClampToEdge; uvDesc.wrapT = MTLWrapModeClampToEdge;
4. 辅助检查着色器逻辑
添加UV坐标边界检查,避免写入超出UV纹理范围的内容:
uint2 uvCoord = uint2(gid.x / 2, gid.y / 2); if (uvCoord.x >= videoUV.get_width() || uvCoord.y >= videoUV.get_height()) return;
额外验证步骤
- 用GPU Capture工具确认Y/UV纹理的宽高和像素格式与设置一致;
- 单独渲染Overlay纹理,确认其显示正常无拉伸;
- 渲染未修改的原视频帧,排除源视频本身的拉伸问题。
内容的提问来源于stack exchange,提问作者Lars Rönnbäck
相关产品推荐
相关产品推荐

