Android Crash When Using SetCustomData on Hierarchical Instanced StaticMesh and Instanced StaticMesh

UPDATE: This crash occurs with ISM as well as HISM.

I have a project where I’m using a UHierarchicalInstancedStaticMeshComponent (HISM) to render 100K+ cubes at once - where each cube should have a different color.

As of 4.25, HISM and ISM added SetCustomData**(…)** and SetCustomDataValue(…) functions which allow you to reference PerInstanceCustomData in the HISM’s material graph.

The code I’m using to setup the HISM:



void ARemoteDisplayActor::ConstructVoxelDisplay() {
   // load the voxel material
   LoadVoxelMaterial();
   // load the voxel mesh for rendering
   LoadVoxelMesh();
   // now create the HISM voxel mesh
   voxelMesh = CreateDefaultSubobject<UHierarchicalInstancedStaticMeshComponent>(TEXT("VoxelMesh"));
}

void ARemoteDisplayActor::SetupVoxelDisplay() {
    // attach it to our root component
   voxelMesh->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
   // set the collision on the voxel mesh to have no collision
   voxelMesh->bDisableCollision = true;
   voxelMesh->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName, true);
   // set the number of custom data channels per instance (3, RGB) or (0)
   voxelMesh->NumCustomDataFloats = showVoxelColor ? 3 : 0;
   // set the material that we loaded (or NULL if it could not be found
   // for default material)
   voxelMesh->SetMaterial(0, voxelMaterial);
   // set the rendered mesh for the instances
   voxelMesh->SetStaticMesh(voxelRenderMesh);
   // setup the material accordingly:
   // set the height map coloring according to showVoxelColor
   voxelMesh->SetCustomPrimitiveDataFloat(0, showVoxelColor ? 0.0 : 1.0);
   // set the height colormap range (meters)
   voxelMesh->SetCustomPrimitiveDataFloat(1, 2.0);
   // set the height colormap midpoint
   voxelMesh->SetCustomPrimitiveDataFloat(2, 0.5);
}

void ARemoteDisplayActor::LoadVoxelMaterial() {
   static ConstructorHelpers::FObjectFinder<UMaterial> material(TEXT("/roshi/DataDisplay/M_VoxelMaterial"));
   if (material.Object) {
      voxelMaterial = (UMaterialInterface*) material.Object;
      UE_LOG(LogTemp, Warning, TEXT("Loaded /roshi/DataDisplay/M_VoxelMaterial"));
   } else {
      UE_LOG(LogTemp, Warning, TEXT("Warning: could not load /roshi/DataDisplay/M_VoxelMaterial"));
   }
}

void ARemoteDisplayActor::LoadVoxelMesh() {
   static ConstructorHelpers::FObjectFinder<UStaticMesh> mesh(TEXT("/Engine/EngineMeshes/Cube"));
   if (mesh.Object) {
      voxelRenderMesh = (UStaticMesh*) mesh.Object;
      UE_LOG(LogTemp, Warning, TEXT("Loaded /Engine/EngineMeshes/Cube"));
   } else {
      UE_LOG(LogTemp, Warning, TEXT("Warning: could not load Engine/EngineMeshes/Cube"));
   }
}


And then at runtime to change the HISM instances / colors:



double start, end;
start = FPlatformTime::Seconds();

FTransform t;
FVector scale{ voxelSize, voxelSize, voxelSize };
size_t numVoxels = voxelLocations.Num();
bool teleportUpdate = true;
bool setRenderStateDirty = false;
bool transformIsWorldSpace = false;
// update the instances here
size_t index = 0;
UE_LOG(LogTemp, Warning, TEXT("Updating %d existing instances"), voxelMesh->GetInstanceCount());
// update voxels we already have
for (; index < voxelMesh->GetInstanceCount(); index++) {
   // break if we have more voxels than we currently have meshes
   if (index >= numVoxels) break;
   // set the transform
   t.SetTranslationAndScale3D(voxelLocations[index], scale);
   voxelMesh->UpdateInstanceTransform(index, t,
                                                                  transformIsWorldSpace,
                                                                  setRenderStateDirty,
                                                                  teleportUpdate);
   if (showVoxelColor) {
      // set the color
      FLinearColor color = voxelColors[index];
      // TArray<float> customData{color.R, color.G, color.B};
      // voxelMesh->SetCustomData(index, customData, setRenderStateDirty);
      voxelMesh->SetCustomDataValue(index, 0, color.R, setRenderStateDirty);
      voxelMesh->SetCustomDataValue(index, 1, color.G, setRenderStateDirty);
      voxelMesh->SetCustomDataValue(index, 2, color.B, setRenderStateDirty);
   }
}
// add new voxels if we have more than we have meshes
UE_LOG(LogTemp, Warning, TEXT("Adding %d new instances"), numVoxels - index);
for (; index < numVoxels; index++) {
   // create the new mesh instance
   t.SetTranslationAndScale3D(voxelLocations[index], scale);
   voxelMesh->AddInstance(t);
   if (showVoxelColor) {
      // set the color
      FLinearColor color = voxelColors[index];
      // TArray<float> customData{color.R, color.G, color.B};
      // voxelMesh->SetCustomData(index, customData, setRenderStateDirty);
      voxelMesh->SetCustomDataValue(index, 0, color.R, setRenderStateDirty);
      voxelMesh->SetCustomDataValue(index, 1, color.G, setRenderStateDirty);
      voxelMesh->SetCustomDataValue(index, 2, color.B, setRenderStateDirty);
   }
}
// remove voxels we don't need anymore - TODO: probably should
// just hide them and save them for later
TArray<int32> instancesToRemove;
for (; index < voxelMesh->GetInstanceCount(); index++) {
   instancesToRemove.Add(index);
}
UE_LOG(LogTemp, Warning, TEXT("Removing %d unneeded instances"), instancesToRemove.Num());
voxelMesh->RemoveInstances(instancesToRemove);
// now mark the render state as dirty!
voxelMesh->MarkRenderStateDirty();
// measure performance
end = FPlatformTime::Seconds();
UE_LOG(LogTemp, Warning, TEXT("Voxel Creation took %f seconds for %d voxels"), end - start, voxelMesh->GetInstanceCount());


I’m just using the simple cube mesh that comes with the engine, and a simple material that uses the PerInstanceCustomData (or allows using the z-height as the color):

](filedata/fetch?id=1773755&d=1591738632)

When running on Windows, everything works (both with and without voxel color). On Android, if I do not show voxel color (and use the z-based colormapping instead) it works just fine, I’m able to show generate and render 100K cubes just fine.

However, when I configure my actor to show the voxel color (meaning I set NumCustomDataFloats to 3 and call SetCustomDataValue), I get this crash when running in Android:



D/UE4 : [2020.06.09-21.18.31:778][137]LogTemp: Warning: Got point cloud message!
D/UE4 : [2020.06.09-21.18.31:778][137]LogTemp: Warning: frame id: base_link
D/UE4 : [2020.06.09-21.18.31:778][137]LogTemp: Warning: shape: 100000 x 1
D/UE4 : [2020.06.09-21.18.31:778][137]LogTemp: Warning: point step: 24
D/UE4 : [2020.06.09-21.18.31:778][137]LogTemp: Warning: row step: 2400000
D/UE4 : [2020.06.09-21.18.31:799][138]LogTemp: Warning: Updating 0 existing instances
D/UE4 : [2020.06.09-21.18.31:799][138]LogTemp: Warning: Adding 100000 new instances
D/UE4 : [2020.06.09-21.18.32:240][138]LogTemp: Warning: Removing 0 unneeded instances
D/UE4 : [2020.06.09-21.18.32:240][138]LogTemp: Warning: Voxel Creation took 0.440840 seconds for 100000 voxels
F/DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
F/DEBUG : Build fingerprint: 'google/taimen/taimen:10/QQ2A.200501.001.B3/6396602:user/release-keys'
F/DEBUG : Revision: 'rev_10'
F/DEBUG : ABI: 'arm'
F/DEBUG : Timestamp: 2020-06-09 16:18:32-0500
F/DEBUG : pid: 9009, tid: 9094, name: RHIThread >>> com.permobil.MobileRoshiCPP <<<
F/DEBUG : uid: 10778
F/DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8
F/DEBUG : Cause: null pointer dereference
F/DEBUG : r0 00000010 r1 00000000 r2 93ef5b00 r3 ffffffff
F/DEBUG : r4 00000000 r5 00000000 r6 b10e5000 r7 bd8cc840
F/DEBUG : r8 97ecd730 r9 00000002 r10 be7ad410 r11 b04d8df0
F/DEBUG : ip bd8b1f4c sp b04d8da8 lr b7eb4d3c pc bcb3dab4
F/DEBUG :
F/DEBUG : backtrace:
F/DEBUG : #00 pc 0aafcab4 /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FOpenGLDynamicRHI::CommitGraphicsResourceTablesInner()+612) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #01 pc 0ab01644 /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FOpenGLDynamicRHI::RHIDrawIndexedPrimitive(FRHIIndexBuffer*, int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int)+1860) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #02 pc 07248f00 /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FRHICommandDrawIndexedPrimitive::Execute(FRHICommandListBase&)+48) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #03 pc 0724d244 /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FRHICommandListExecutor::ExecuteInner_DoExecute(FRHICommandListBase&)+1956) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #04 pc 0729c19c /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FExecuteRHIThreadTask::DoTask(ENamedThreads::Type, TRefCountPtr<FGraphEvent> const&)+800) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #05 pc 0729b940 /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (_ZN10TGraphTaskI21FExecuteRHIThreadTaskE11ExecuteTaskER6TArrayIP14FBaseGraphTask22TSizedDefaultAllocatorILi32EEEN13ENamedThreads4TypeE+700) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #06 pc 05d72bc8 /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FNamedTaskThread::ProcessTasksNamedThread(int, bool)+2144) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #07 pc 05d71950 /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FNamedTaskThread::ProcessTasksUntilQuit(int)+108) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #08 pc 0731496c /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FRHIThread::Run()+92) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #09 pc 05e6fff4 /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FRunnableThreadPThread::Run()+152) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #10 pc 05d6d044 /data/app/com.permobil.MobileRoshiCPP-U21ev8TlrZNQQJIY6rFU1Q==/lib/arm/libUE4.so (FRunnableThreadPThread::_ThreadProc(void*)+80) (BuildId: d3463a1e87d5a5cbfd4b8b6a9a937f8566e45766)
F/DEBUG : #11 pc 000a5ea3 /apex/com.android.runtime/lib/bionic/libc.so (__pthread_start(void*)+20) (BuildId: f9f8835945b295b609712530651a95ae)
F/DEBUG : #12 pc 00060773 /apex/com.android.runtime/lib/bionic/libc.so (__start_thread+30) (BuildId: f9f8835945b295b609712530651a95ae)


Since it’s the same code running on both platforms, it must mean that either the feature (setting custom data per instance) is broken on Android or is not supported on Android. For reference, I’m running this code on a Pixel 2 XL. As far as I’ve been able to tell (from documentation and release notes) there is no platform specificity to this feature so I believe this is a bug.

Note: this also happens with UInstancedStaticMeshComponent (ISM) so it is not unique to the HISM.