TriAxis-Games / RealtimeMeshComponent

Unreal Engine 5 plugin component for rendering runtime generated content.
https://rmc.triaxis.games/
Other
1.56k stars 410 forks source link

Crash on Android when creating section, assertion failed for check(RMCSubsystem). #209

Closed kass-kass closed 3 years ago

kass-kass commented 3 years ago

Found a bug when trying to run RMC on Android (Oculus Quest is the hardware I'm using, not sure if the same crash happens on other devices).

I have a simple BP that makes a box from a hard-coded list of verts/tris, simplest thing I could think of, and calls "create section from components" on a default RMCStatic.

This runs on PC fine. However, the Android ASTC packaged build crashes when the "create section" node gets called, giving critical error in the log: Assertion failed: RMCSubsystem [File:D:/Unreal Projects/RMC_OculusQuest/Plugins/RuntimeMeshComponent/Source/RuntimeMeshComponent/Private/RuntimeMesh.cpp] [Line: 909]

The line in question is simply check(RMCSubsystem);

I have the latest GitHub source, and default 4.26.2 from the epic launcher. Did anyone encounter this issue? Here are the nodes, and the box that gets created: Capture Captur1e

This is all in a completely empty project, to make sure this is exactly what causes the issue. These nodes are the only non-default-engine code that runs.

Here is the whole project (2MB zip file): RMC_OculusQuest.zip

Here is the full log, with the callstack for the critical error at the very end: RMC_OculusQuest.log

kass-kass commented 3 years ago

Update: tested the same project on a normal Android mobile phone, with no VR or Oculus involved. Same crash.

So this is definitely an Android problem, maybe general UE mobile, but I can't test on iOS, so at least Android for sure.

Essentially this makes RMC unusable on mobile for 4.26

The project settings for the mobile build are very standard, here they are just in case (ini file, but renamed to txt to upload): DefaultEngine.txt

kass-kass commented 3 years ago

Another bit of information: this is not an engine bug, as creating an empty subsystem and getting it in exactly the same way works fine.

Details: This is the function that crashes with a failed assertion (starts at line 906 in RuntimeMesh.cpp):

URuntimeMeshComponentEngineSubsystem* URuntimeMesh::GetEngineSubsystem()
{
    URuntimeMeshComponentEngineSubsystem* RMCSubsystem = GEngine->GetEngineSubsystem<URuntimeMeshComponentEngineSubsystem>();
    check(RMCSubsystem); // RMCSubsystem is nullptr here somehow on Android.
    return RMCSubsystem;
}

So the first line in the function gets a nullptr from the standard UE GetEngineSubsystem function for some reason. However, creating another engine subsystem class (i.e. UMyEngineSubsystem), and getting it with the exact same templated GetEngineSubsystem function on an actor's BeginPlay does not return a nullptr.

This means it's either something specific to URuntimeMesh::GetEngineSubsystem(), such as never actually getting created, or it getting garbage collected; or something wrong with getting a subsystem that is part of a plugin module, as opposed to a game module.

The call that crashes happens after Engine subsystems get initialized, as logging something in Initialize() prints way before the crash in the log. So it's strange that the GetEngineSubsystem returns nullptr, as clearly a valid subsystem gets created at one point before that.

Will continue investigating and updating here.

Alex-Folts commented 3 years ago

Thre is alternative getenginesubsystem method https://github.com/EpicGames/UnrealEngine/blob/c3caf7b6bf12ae4c8e09b606f10a09776b4d1f38/Engine/Source/Runtime/Engine/Classes/Engine/Engine.h#L3207 try use it, i.e. change RuntimeMesh code to: URuntimeMeshComponentEngineSubsystem* RMCSubsystem = (URuntimeMeshComponentEngineSubsystem)GEngine->GetEngineSubsystemBase(URuntimeMeshComponentEngineSubsystem);

kass-kass commented 3 years ago

Thre is alternative getenginesubsystem method https://github.com/EpicGames/UnrealEngine/blob/c3caf7b6bf12ae4c8e09b606f10a09776b4d1f38/Engine/Source/Runtime/Engine/Classes/Engine/Engine.h#L3207 try use it, i.e. change RuntimeMesh code to: URuntimeMeshComponentEngineSubsystem* RMCSubsystem = (URuntimeMeshComponentEngineSubsystem)GEngine->GetEngineSubsystemBase(URuntimeMeshComponentEngineSubsystem);

Thank you! I tried it out, but still crashes.

The syntax I used is URuntimeMeshComponentEngineSubsystem* RMCSubsystem = Cast<URuntimeMeshComponentEngineSubsystem>( GEngine->GetEngineSubsystemBase(URuntimeMeshComponentEngineSubsystem::StaticClass()) );

kass-kass commented 3 years ago

This issue gets weirder the more I look into it.

Getting with GetEngineSubsystem an empty newly-made subsytem I called UMyEngineSubsystem worked fine, but getting the URuntimeMeshComponentEngineSubsystem just returned nullptr, when done in BeginPlay of any actor. That is very strange, as BeginPlay should happen after Engine subsystems get initialized (as it's part of the World, which initializes after Engine).

Logging on initialize in both subsystems revealed that UMyEngineSubsystem's function override for Initialize() gets called, but URuntimeMeshComponentEngineSubsystem's doesn't. It just never gets initialized.

The only difference I saw is one subsystem was part of a plugin, and one of a game module, so I converted the RuntimeMeshComponent module from a plugin to a game module. All of a sudden, everything works. The subsystem gets initialized, it can be gotten with GetEngineSubsystem, no crash on Android, everything works.

To convert a module from a plugin to a game module, these were the steps:

  1. Moved the RuntimeMeshComponent source subfolder from Plugins\RuntimeMeshComponent\Source into the \Source folder of the project.
  2. Deleted the Plugins\RuntimeMeshComponent folder entirely.
  3. Changed the RuntimeMeshComponentPlugin.cpp line from IMPLEMENT_MODULE(FRuntimeMeshComponentPlugin, RuntimeMeshComponent); to IMPLEMENT_GAME_MODULE( FDefaultGameModuleImpl, RuntimeMeshComponent);.
  4. Added "RuntimeMeshComponent" to the ExtraModuleNames.AddRange line of ProjectName.target.cs, to the PublicDependencyModuleNames.AddRange line of the ProjectName.build.cs (the primary game module), and to the "Modules": in the ProjectName.uproject.

So after discovering this, I assumed it might be an engine bug with making engine subsystems in plugin modules. But it's not. I made a new empty plugin, made a new engine subsystem in it, and tried to get it on BeginPlay the same way. It works fine, on PC and on Android.

So there's something specific about the URuntimeMeshComponentEngineSubsystem, which makes it never initialize only when part of a plugin, only on Android, only in 4.26. I can't really find any explanation, I looked through the code so many times, but it seems like the subsystem is declared in a very standard way, and should by all means be initialized automatically by the Engine. Yet it doesn't happen.

Moddingear commented 3 years ago

Fixed in 7765cc4ccdb9d6929e11335b5d17c3d30b0c2c4d