And apologies for the wide-reaching subject name as I could not find anything more specific.
A bit of context: working on an open world, we rely heavily on level instancing during production so our artists do not have to always have the entire open world resources loaded at once.
There is “logical” level instancing for a part of the world that is a level from a gameplay point of view. But in these levels we also nest packed level instances. These exist only for editing performance reasons so once again our artists do not have to load everything when they only need to edit e.g. a building (pretty much the size of these). It also helps with PIE performances as the geometry drawing is instanced.
These packed level instances boundaries are assembled by hand and considered “disposable”: they are created, the actors possibly moved around, but the level might get broken/repacked at any time with no consequences. They are unpacked during cooking and repacked with more performance oriented settings (such as not packing together actors from differerent cells etc.)
We have developed a plugin to streamline packing/breaking large sets of geometry so the artists can easily do their editing but we hit a few issues:
FPackedLevelActorBuilder does not look very customisable considering the visibility of some of its members, the fact that e.g. FPackedLevelActorISMBuilder is in the private folder etc., is that wanted?
Due to these limitations we ended up going through the regular ULevelInstanceSubsystem::CreateLevelInstanceFrom() API but it seems once again not very customisable with some fields of FNewLevelInstanceParams not being honoured (e.g. LevelPackageName, bPromptForSave)
Considering these even the file Engine/Private/LevelInstance/LevelInstanceSubsystem.cpp itself actually seems to have undergone a bad merge, see the ending of +1425 : CreateNewStreamingLevelParams.TemplateWorld = CreationParams.TemplateWorld,
Are those expected behaviours?
More specifically with our usage, due to the fact that these packed levels are “disposable” we try to delete both the BPP and the level (umap) assets when breaking them, as they will not be reused. But destroying the umap asset is extremely slow and error prone: even with a test map with 64 identical cubes it takes 5s (mostly gathering referencers) and it definitely does not scale sublinearly.
Is this process expected to be this slow?
Moreover after breaking the packed level instances it looks like there are dangling references although none can be found through the usual referencers research APIs.
Eventually as a workaround we settled on deleting the BBP asset after the breaking process so it can be replaced afterwards if a new packing occur. The level is untouched.
Is this a sound workaround, to overwrite the umap asset when packing again with a new BPP?
The workflow you describe is pretty much what the team had in mind when they originally designed the LevelInstance\PackedLevelActor system. In the current system, the PLAs are meant to be kept as single units while the LevelInstance preserve the separate actors. The embedded behavior of LevelInstance results the actors becoming part of the main world so they can be assigned to different cells.
On top of this. we introduced the Runtime Cell Transformers (in 5.5) which add extra optimization possibilities that are applied at the cell generation time. The RCTs are optimizing the content of each cell by aggregating the instances of the same mesh under ISMs (UWorldPartitionRuntimeCellTransformerISM) and converting compatible content to lighter runtime constructs that reduce the pressure on the data injection system (Add\Remove to World) and also reduces the total number of UObjects in the system which improves the garbage collection times (when using FastGeo).
It would be interesting to get more details about the extra use cases that you are trying to build with the plugin. As far as the different classes and methods you mentioned, we simply didn’t think that licensees would want to write their own tools to manage the LIs & PLAs. Knowing about your needs would be useful to comment more.
Thanks for noting the typo in LevelInstanceSubsystem.cpp. The code should behave as expected as it is but it will be safer to fix it to avoid problems related to future edits of this code.
Regarding the deletion of the assets, this is normal behavior with the editor. The code makes sure that the assets are not being used before deleting and will offer to pass replacement assets if they are. This is a case where you should delete the files directly from Windows Explorer.
With these informations and the additional chat we had we can definitely move forward on this issue.
One minor thing remains though, related to the thing we noticed in LevelInstanceSubsystem.cpp: it seems that some parameters from FNewLevelInstanceParams are not carried all the way during the level instance creation process.
Typically even with these parameters we do get dialog boxes asking for level names etc.
As stated above this is minor but prevents streamlined “scripted” level instance creation, which is what got me to have a look at this code in the first place.
Lastly and regarding what we exchanged about bounds: in ULevelInstanceSubsystem::CreateLevelInstanceFrom() there is a “special case” handling invalid actor location boxes that does funky stuff in the case of certain groups.
Sadly I have not been able to put together a minimal example as it only happens in some (quite heavy) levels. We will ping you if ever we can pinpoint it later.
I logged a feature request regarding FNewLevelInstanceParams and ULevelInstanceSubsystem::CreateLevelInstanceFrom(). I’m not sure if the team will be able to get to it soon. If you end up customizing the engine, I would invite you to submit a Pull Request.