A relevant bit of the log is this here:
09-14 12:15:00.266 29813 30341 E AndroidRuntime: FATAL EXCEPTION: UnityMain
09-14 12:15:00.266 29813 30341 E AndroidRuntime: Process: com.DefaultCompany.FathomlandAR, PID: 29813
09-14 12:15:00.266 29813 30341 E AndroidRuntime: java.lang.Error: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
09-14 12:15:00.266 29813 30341 E AndroidRuntime: Version '2020.3.12f1 (b3b2c6512326)', Build type 'Release', Scripting Backend 'mono', CPU 'armeabi-v7a'
09-14 12:15:00.266 29813 30341 E AndroidRuntime: Build fingerprint: 'samsung/z3qcsw/z3q:11/RP1A.200720.012/G988WVLU2DUG1:user/release-keys'
09-14 12:15:00.266 29813 30341 E AndroidRuntime: Revision: '15'
09-14 12:15:00.266 29813 30341 E AndroidRuntime: ABI: 'arm'
09-14 12:15:00.266 29813 30341 E AndroidRuntime: Timestamp: 2021-09-14 12:15:00-0700
09-14 12:15:00.266 29813 30341 E AndroidRuntime: pid: 29813, tid: 30341, name: UnityMain >>> com.DefaultCompany.FathomlandAR <<<
09-14 12:15:00.266 29813 30341 E AndroidRuntime: uid: 10299
09-14 12:15:00.266 29813 30341 E AndroidRuntime: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
09-14 12:15:00.266 29813 30341 E AndroidRuntime: Cause: null pointer dereference
09-14 12:15:00.266 29813 30341 E AndroidRuntime: r0 00000000 r1 0000000b r2 00000000 r3 00000001
09-14 12:15:00.266 29813 30341 E AndroidRuntime: r4 9431d5d0 r5 b81fb754 r6 80808080 r7 00000000
09-14 12:15:00.266 29813 30341 E AndroidRuntime: r8 e314d8b0 r9 00000000 r10 00000000 r11 a9323bf0
09-14 12:15:00.266 29813 30341 E AndroidRuntime: ip a935abdc sp b81fb750 lr a932c321 pc 00000000
09-14 12:15:00.266 29813 30341 E AndroidRuntime:
09-14 12:15:00.266 29813 30341 E AndroidRuntime: managed backtrace:
09-14 12:15:00.266 29813 30341 E AndroidRuntime: #00 (wrapper managed-to-native) UnityEngine.XR.ARCore.ARCoreSessionSubsystem/NativeApi:UnityARCore_session_update (UnityEngine.ScreenOrientation,UnityEngine.Vector2Int,intptr,UnityEngine.XR.ARSubsystems.Feature)
09-14 12:15:00.266 29813 30341 E AndroidRuntime: #01 UnityEngine.XR.ARCore.ARCoreSessionSubsystem/ARCoreProvider:Update (UnityEngine.XR.ARSubsystems.XRSessionUpdateParams,UnityEngine.XR.ARSubsystems.Configuration) <0xef>
09-14 12:15:00.266 29813 30341 E AndroidRuntime: #02 UnityEngine.XR.ARSubsystems.XRSessionSubsystem:Update (UnityEngine.XR.ARSubsystems.XRSessionUpdateParams) <0x1d3>
09-14 12:15:00.266 29813 30341 E AndroidRuntime: #03 UnityEngine.XR.ARFoundation.ARSession:Update () <0x14f>
09-14 12:15:00.266 29813 30341 E AndroidRuntime: #04 (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)
09-14 12:15:00.266 29813 30341 E AndroidRuntime:
09-14 12:15:00.266 29813 30341 E AndroidRuntime: at UnityEngine.XR.ARCore.ARCoreSessionSubsystem.NativeApi.UnityARCore_session_update (UnityEngine.ScreenOrientation,UnityEngine.Vector2Int,intptr,UnityEngine.XR.ARSubsystems.Feature)(Native Method)
09-14 12:15:00.266 29813 30341 E AndroidRuntime: at UnityEngine.XR.ARCore.ARCoreSessionSubsystem.ARCoreProvider.Update (UnityEngine.XR.ARSubsystems.XRSessionUpdateParams,UnityEngine.XR.ARSubsystems.Configuration)(0xef:239)
09-14 12:15:00.266 29813 30341 E AndroidRuntime: at UnityEngine.XR.ARSubsystems.XRSessionSubsystem.Update (UnityEngine.XR.ARSubsystems.XRSessionUpdateParams)(0x1d3:467)
09-14 12:15:00.266 29813 30341 E AndroidRuntime: at UnityEngine.XR.ARFoundation.ARSession.Update ()(0x14f:335)
09-14 12:15:00.266 29813 30341 E AndroidRuntime: at System.Object.runtime_invoke_void__this__ (object,intptr,intptr,intptr)(Native Method)
The first thing I notice is that you are running a 32 bit ARM build.
AFAIK ARCore only supports 64bit officially. As a first step, you will probably have to switch the scripting backend from mono to IL2CPP and enable 64 bit builds in your player settings for Android.
At this point I would be extremely thankful for some advice from @tdmowrer or @todds_unity though. I apologize for nagging by tagging you here, but I am facing a related issue which has been a nightmare to troubleshoot. It is a major blocker for my master’s thesis.
First, I’d like to bring to your attention that this is the third forum post regarding segmentation faults in ARCore on Samsung handsets in the space of a few days, see: ARFoundation application crashes randomly and my (now outdated) post here: SegFault in XRCpuImage.Convert . It’s probably a conicidence, but it might be worth keeping an eye on.
Now to my issue: Any code that copies the data originating from XRCpuImage.Convert() in my project seems to tread on a potential landmine, causing a segmentation fault like the one OP is facing. These faults are very common and crash my app in most cases after only a few seconds. They are, however, intermittent and might even not happen at all every once in a while. I am unable to discern any pattern in the crashes so far, despite having had over a hundred since I started troubleshooting. So I assume the faults are likely, but random.
The segmentation faults occur in several functions in plugins, e.g. OpenCV4Unity’s MatUtils.copyToMat(). It also used to happen in this (seemingly perfectly safe?) function here:
public static Texture2D CloneTexture2D(Texture2D source)
{
Texture2D copyTexture = new Texture2D(source.width, source.height, source.graphicsFormat, 0);
Graphics.CopyTexture(source, copyTexture);
return copyTexture;
}
But ONLY when trying to clone a Texture2D created like this:
unsafe bool EmitFullCPUImage(XRCpuImage image, int frameNumber)
{
Texture2D texture = null;
TextureFormat format = TextureFormat.RGBA32;
XRCpuImage.ConversionParams conversionParams = new XRCpuImage.ConversionParams(
image, format, XRCpuImage.Transformation.MirrorX);
conversionParams.outputDimensions = new Vector2Int(image.width, image.height);
int size = image.GetConvertedDataSize(conversionParams);
using (NativeArray<byte> buffer = new NativeArray<byte>(size, Allocator.Temp))
{
if (buffer.IsCreated && (buffer.Length == size))
{
//Debug.Log("CONVOP18#93: XRCpuImage.Convert() attempted in XRCamera.EmitFullCPUImageSync() with w=" + image.width + "; h=" + image.height + "; size=" + size + "; actual buffer length=" + buffer.Length + " - Frame: " + frameNumber);
image.Convert(conversionParams, new IntPtr(buffer.GetUnsafePtr()), buffer.Length);
texture = new Texture2D(
conversionParams.outputDimensions.x,
conversionParams.outputDimensions.y,
conversionParams.outputFormat,
false);
texture.LoadRawTextureData(buffer);
texture.Apply();
//emit frame
OnNewCPUtexture?.Invoke(texture, frameNumber);
return true;
}
else
{
Debug.LogWarning("CONVOP18#93: NativeArray buffer creation failed - Frame: " + frameNumber);
return false;
}
}
}
Previously, I used to Convert() twice from each new XRCpuImage – both times with different resolutions – and the XRCpuImage in question was potentially kept around for a while. To eliminate these two potential error causes, I simplified the code in question as shown in the following. Note that this did NOT fix the issue. It only changed the function in which the segfaults occur.
This is how I handle the XRCpuImages now:
private void SampleFrame()
{
Frame frame = new Frame(); //not relevant here
XRCameraIntrinsics intrinsics;
if (!this.cameraManager.TryGetIntrinsics(out intrinsics))
{
frame.Dispose();
return;
}
XRCpuImage XRimage = new XRCpuImage();
if (!this.cameraManager.TryAcquireLatestCpuImage(out XRimage))
{
XRimage.Dispose();
frame.Dispose();
return;
}
frameNumber += 1;
try
{
int width = XRimage.width;
int height = XRimage.height;
byte[] rawImgData = ReadXRCPUimage(XRimage, TextureFormat.RGBA32);
//done with the XR image, get rid of it ASAP
XRimage.Dispose();
//add missing info to frame
Vector2Int targetDims = GetTrackingImageSize(width, height);
frame.intrinsicData = ToVLIntrinsic(intrinsics, targetDims);
frame.extrinsicData = GetCurrentExtrinsicData();
//Pack raw image data and everything needed to convert it to a VL image for the tracking manager
CPUframeData data = new CPUframeData(
rawImgData,
width,
height,
targetDims.x,
targetDims.y,
TextureFormat.RGBA32,
frameNumber
);
//send to coordinator -> image added there, then pushed to trackingmanager
OnNewCPUimageData?.Invoke(data, frame);
}
catch (InvalidOperationException e)
{
frame.Dispose();
XRimage.Dispose();
if (e.Source == "ARFoundationRemote.Editor")
{
Debug.LogWarning(e);
}
else
{
//unexpected failure. throw so I notice this.
throw e;
}
return;
}
}
Where the actual readout is done here:
unsafe byte[] ReadXRCPUimage(XRCpuImage image, TextureFormat targetTextureFormat)
{
XRCpuImage.ConversionParams conversionParams = new XRCpuImage.ConversionParams(
image, targetTextureFormat, XRCpuImage.Transformation.MirrorX);
conversionParams.outputDimensions = new Vector2Int(image.width, image.height);
int size = image.GetConvertedDataSize(conversionParams);
byte[] managedBuffer = new byte[size];
using (NativeArray<byte> tmpBuffer = new NativeArray<byte>(size, Allocator.Temp))
{
image.Convert(conversionParams, new IntPtr(tmpBuffer.GetUnsafePtr()), tmpBuffer.Length);
tmpBuffer.CopyTo(managedBuffer);
} //native array gone
return managedBuffer;
}
Segmentation faults now occur here (the handler for my event OnNewCPUimageData) in lines 7,11 and 13 – again, specifically when the data originating from the XRCpuImage is copied somewhere else:
private void ProcessNewCPUFrame(CPUframeData data, Frame vlFrame)
{
int frameNumber = data.FrameNumber;
if (data.Format == TextureFormat.RGBA32)
{
//convert byte[] to mat & downscale to target size for tracking
Mat tmp = ImageUtils.ResizeMat(ImageUtils.ByteArray2Mat_RGBA32(data.ManagedPixelBuffer, data.Height, data.Width), data.VLtargetWidth, data.VLtargetHeight);
//flip vertically
ImageUtils.FlipTopBottom(ref tmp, ref tmp);
//convert back to byte[]
byte[] rawImgForVL = ImageUtils.Mat2ByteArray_RGBA32(tmp);
//convert the raw byte array to a VL image & add it to the Frame befroe passing it to the tracker
vlFrame.image = Visometry.VisionLib.SDK.Core.API.Native.Image.CreateFromBuffer(rawImgForVL, data.VLtargetWidth, data.VLtargetHeight);
syncTrackingManager.Push(vlFrame, frameNumber);
//create full frame Texture2D & store in buffer
Texture2D fullFrameTexture = new Texture2D(data.Width, data.Height, data.Format, false);
fullFrameTexture.LoadRawTextureData(data.ManagedPixelBuffer);
fullFrameTexture.Apply(); //copy from CPU to GPU
fullFrames.AddFrame(fullFrameTexture, frameNumber);
}
else
{
Debug.LogWarning("Data has wrong Texture format. Ensure you are providing data in TextureFormat RGBA32. Frame " + frameNumber + " skipped.");
}
}
I have attached the helpers used in lines 7 & 11 for further reference, but they end in MatUtils.copyToMat() and MatUtils.copyFromMat(), respectively, which are imported from a DLL
I have also attached a tombstone from one of my latest crashes for reference.
I am desperate for any insight into how I might be able to get rid of this bug.
7507034–925399–tombstone_00.txt (382 KB)
7507034–925400–Helpers_ProcessNewCPUFrame.cs (1.12 KB)