从XRCpuImage转换并旋转Texture2D至RawImage,运行约30秒后崩溃
问题排查:AR图像旋转后运行崩溃
问题描述
通过XRCpuImage获取相机帧,转换为Texture2D后显示在RawImage上,图像出现旋转偏移。使用自定义rotateTexture函数旋转后显示正常,但应用运行约30秒后崩溃。
代码片段
using System; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.UI; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; using Unity.Collections; public class CPU_RawImage : MonoBehaviour { Texture2D m_CameraTexture; GameObject DR_layer; [SerializeField] [Tooltip("The ARCameraManager which will produce frame events.")] ARCameraManager m_CameraManager; /// <summary> /// Get or set the <c>ARCameraManager</c>. /// </summary> public ARCameraManager cameraManager { get => m_CameraManager; set => m_CameraManager = value; } [SerializeField] RawImage m_RawCameraImage; /// <summary> /// The UI RawImage used to display the image on screen. /// </summary> public RawImage rawCameraImage { get => m_RawCameraImage; set => m_RawCameraImage = value; } XRCpuImage.Transformation m_Transformation = XRCpuImage.Transformation.MirrorX; void OnEnable() { if (m_CameraManager != null) { m_CameraManager.frameReceived += OnCameraFrameReceived; } } void OnDisable() { if (m_CameraManager != null) { m_CameraManager.frameReceived -= OnCameraFrameReceived; } } //Rotate Funktion private Texture2D rotateTexture(Texture2D originalTexture, bool clockwise) { Color32[] original = originalTexture.GetPixels32(); Color32[] rotated = new Color32[original.Length]; int w = originalTexture.width; int h = originalTexture.height; int iRotated, iOriginal; for (int j = 0; j < h; ++j) { for (int i = 0; i < w; ++i) { iRotated = (i + 1) * h - j - 1; iOriginal = clockwise ? original.Length - 1 - (j * w + i) : j * w + i; rotated[iRotated] = original[iOriginal]; } } Texture2D rotatedTexture = new Texture2D(h, w); rotatedTexture.SetPixels32(rotated); rotatedTexture.Apply(); return rotatedTexture; } unsafe void UpdateCameraImage(int[] pixel) { if (!cameraManager.TryAcquireLatestCpuImage(out XRCpuImage image)) { return; } var format = TextureFormat.RGBA32; if (m_CameraTexture == null || m_CameraTexture.width != image.width || m_CameraTexture.height != image.height) { m_CameraTexture = new Texture2D(image.width, image.height, format, false); } var conversionParams = new XRCpuImage.ConversionParams(image, format, m_Transformation); var rawTextureData = m_CameraTexture.GetRawTextureData<byte>(); try { image.Convert(conversionParams, new IntPtr(rawTextureData.GetUnsafePtr()), rawTextureData.Length); } finally { image.Dispose(); } m_CameraTexture.Apply(); //rotate Texture clockwise //m_CameraTexture = rotateTexture(m_CameraTexture, true); m_RawCameraImage.texture = m_CameraTexture; } void Start(){ DR_layer = GameObject.Find("RawImage"); } void OnCameraFrameReceived(ARCameraFrameEventArgs eventArgs) { int[] pixel = {200,1000}; UpdateCameraImage(pixel); } }
崩溃原因及修复方案
1. 核心问题:内存泄漏
启用rotateTexture调用后,每帧都会创建新的Texture2D对象,但旧的Texture2D未被销毁。AR相机通常以30-60帧/秒运行,30秒内会生成数千个未回收的纹理对象,导致内存耗尽触发崩溃。
2. 优化方案:
方案一:使用XRCpuImage内置旋转(推荐)
无需自定义旋转函数,直接修改XRCpuImage.Transformation参数,让图像在转换阶段直接完成旋转和镜像,效率更高且无内存问题:
// 根据需求组合旋转和镜像选项,示例为水平镜像+顺时针旋转90度 XRCpuImage.Transformation m_Transformation = XRCpuImage.Transformation.MirrorX | XRCpuImage.Transformation.Rotate90Clockwise;
XRCpuImage.Transformation支持的组合选项:
Rotate90Clockwise/Rotate180/Rotate90CounterClockwise:旋转方向MirrorX/MirrorY:镜像翻转
方案二:复用旋转后的Texture2D
如果必须保留自定义旋转逻辑,需复用纹理对象,避免重复创建:
// 新增成员变量存储旋转后的纹理 Texture2D m_RotatedTexture; private Texture2D rotateTexture(Texture2D originalTexture, bool clockwise) { int w = originalTexture.width; int h = originalTexture.height; // 仅在尺寸变化时重新创建纹理,否则复用已有对象 if (m_RotatedTexture == null || m_RotatedTexture.width != h || m_RotatedTexture.height != w) { if (m_RotatedTexture != null) Destroy(m_RotatedTexture); m_RotatedTexture = new Texture2D(h, w); } Color32[] original = originalTexture.GetPixels32(); Color32[] rotated = new Color32[original.Length]; int iRotated, iOriginal; for (int j = 0; j < h; ++j) { for (int i = 0; i < w; ++i) { iRotated = (i + 1) * h - j - 1; iOriginal = clockwise ? original.Length - 1 - (j * w + i) : j * w + i; rotated[iRotated] = original[iOriginal]; } } m_RotatedTexture.SetPixels32(rotated); m_RotatedTexture.Apply(); return m_RotatedTexture; } // 在UpdateCameraImage中直接赋值旋转后的纹理,无需替换原m_CameraTexture m_RawCameraImage.texture = rotateTexture(m_CameraTexture, true);
方案三:销毁旧纹理(临时应急)
若需临时保留原有逻辑,至少在创建新纹理前销毁旧对象:
// 在rotateTexture返回前销毁传入的原纹理(需确保原纹理不再被使用) Destroy(originalTexture);
此方案会导致原m_CameraTexture频繁销毁重建,性能较差,仅作临时应急使用。
3. 额外优化点
- 移除无用的
pixel参数:UpdateCameraImage中的int[] pixel未被使用,可删除简化代码。 - 删除无效变量:
DR_layer变量未被使用,可移除Start方法中的DR_layer = GameObject.Find("RawImage");语句。
内容的提问来源于stack exchange,提问作者znerl
相关产品推荐
相关产品推荐

