Need Help Understanding SubScenes

I’m just now experimenting with SubScenes, and I’m finding they don’t work the way I thought.

My understanding of them previously was that clicking “Close” on a Subscene would run it’s hierarchy through the GameObject Conversion process, and then save out an entity-only representation of the scene. That’s how I thought they were achieving such fast load times.

Now I see that’s not the case - the contents of SubScenes still get run through the conversion process when they are loaded at runtime. When I click “Close” on a SubScene in the editor, it does not appear to be run through conversion (or at least my GameObjectConversionSystems, which function well at runtime, are not updated).

This means they can’t just be saving out an Entity-only version of the scene. GameObjects and MonoBehaviours are a part of the conversion process.

So, now I’m confused to both the function and purpose of SubScenes. If they aren’t saving out an Entity representation, then they aren’t what I thought they were. Would anyone mind giving a good explanation?


P.S. Just want to give a shoutout to Argon’s thorough blog post about SubScenes, here: https://gametorrahod.com/game-object-conversion-and-subscene/. Alas, I must have some fundamental misunderstandings about them, because I still couldn’t quite grok it from that post.

Bump. If someone can summarize how SubScenes work, I would really appreciate it. I’m having trouble tracking down the information online.

The articles and talks I’ve found seem to suggest the (wrong) thing I originally thought: that SubScenes save out an Entity representation of the scene (as simple binary data), and that the GameObjects associated with the a Subscene are run through a conversion process when you close the SubScene.

But some simple tests are showing that not be true. So I’m flummoxed.

I see that the SubScene MonoBehaviour has a ‘_SceneEntities’ List, but it’s not serialized:

https://docs.unity3d.com/Packages/com.unity.entities@0.0/api/Unity.Scenes.SubScene.html

I’m not sure much information can actually be gleaned from that, though, since Subscenes are probably handled uniquely as assets. This MonoBehaviour probably isn’t doing a crucial part of the lifting. But hey, it’s a least some official Unity documentation on the subject! :stuck_out_tongue:

Subscenes are one of the areas that we’re currently missing documentation for, unfortunately. Let me write up a few lines that I hope help to understand subscenes.
A subscene is a MonoBehaviour on a GameObject that references another scene. This scene that we are referencing is converted to its entity representation and becomes an EntityScene. This is one way in which the conversion workflow is used.
A subscene can exist in different states: It is either open, closed, or unloaded. By default, all subscenes are loaded into the closed state. This means that we load the entities representation (i.e. the converted data) into memory and display that. There are a few complications around that: What if the subscene has not been converted yet, for example? In that case the entities from that subscene will only stream in once the import has finished in the background. When you hit play, we wait for all subscenes to have finished their import, so you might experience a stall there if you have recently modified subscenes. It is important to note here that a scene is not immediately reconverted when you close it and the conversion of that scene does not happen in the main Unity process - there is a separate asset import worker process in the background that is handling that.
An unloaded subscene on the other hand is not loaded in memory at all. This means you cannot see it and its entities aren’t present.
Then finally there are open subscenes. When you open a subscene, we load the scene as a regular Untiy scene with GameObjects in it, so you can modify it, but we also convert all GameObjects in that scene in real time. This is one application of LiveLink (so you are also using LiveLink when you have an open subscene in the editor, independent of any builds or players etc.) and this is conversion actually happens in the main Unity process on the main thread. What exactly you see in the editor and whether that conversion actually happens when you open the subscene is controlled by the options in the DOTS menu in the toolbar. If you untick “Live Conversion in edit mode”, no conversion will happen when you open a subscene and you will only see game objects everywhere. If this option is ticked, however, there are two other options below it: Either you view the authoring state, or you view the live game state. In both cases the Game view showing entities. When viewing the authoring state, the scene view shows GameObjects. When viewing the live game state, it also shows entities. The naming of these menu items is not exactly intuitive and it’s been on my mind for some time to finally change them.
So, when you run a game in a player a subscene will only ever load as entity data. In fact, unless you are manually using “GameObjectConversionUtility” no conversion happens in a player.

Does that help? :slight_smile:

Very much, thank you!

armed with this new information, I’ll go back and double check my setup to make sure I don’t have something ticked that would mess with the expected results.

is it safe to believe the following?:

Assuming the correct LiveLink settings: Once you Close an open subscene, a background process to run those GameObjects through the normal conversion process is scheduled on a background thread. The result of that process is an entity only scene representation, which is then serialized in a binary format. In editor, if you enter play mode before the conversion has occurred, the Editor will wait until that process is done before playing.

Subscene conversion never happens at runtime - only in an editor process. Only entities are ever loaded from a subscene.

is that correct?

Nice to get some more info.

It would be great to get some well written documentation for SubScenes along with pretty pictures & diagrams to help digest it at the end of a long day.

That’s almost correct. Subscene conversion never happens at runtime, yes. The conversion of the scene doesn’t just happen on a background thread, it’s an entirely different process. You can see it if you try to attach with a C# debugger from Rider, for example: There’s another Unity instance running that is called the Asset Import Worker. This one is actually running the import. This import process of a scene is also kicked off in other circumstances, for example we have to reconvert the subscene in the background when you change a prefab that is contained in the subscene, even when the subscene is closed or not even loaded. Also, this background importing is independent of any LiveLink settings - those only affect open subscenes.

Yeah, I agree. It’s always something else that comes up, but we actually have someone working on documentation right now. No ETA, unfortunately.

Any update on that doc?

…any update on the doc?

Hi, I would need some further clarification. It seems that the subscenes are linked when loaded, so they will retain changes made during runtime for the remainder of the session (objects being moved, deleted), right? Is there any way to “roll back” the subscene to its initial state?
Also, is it possible to “pre-generate” Entities in subscenes in the editor in order to “bake” a procedurally generated world? (Or would I have to generate them traditionally and group them into subscenes when done…? I would like to avoid this due to the very high number of objects I’m working with).

Hello! any update on the documentation process?

I’ve been reading this whole post but I’m still very confused on how the SubScenes work, specially because I’m not sure the link between this and DOTS.

Anyways, hope docs are get sorted out soon.

Thanks

Thanks for that explanation, it was helpful.

I’m still really confused about the entity indexing though.

Quick background - I wanted to use subscenes, but found out that whenever I tried to spawn an entity from prefab at runtime, it would not work in release-build-mode because the resources for the spawned items weren’t loading. So I started using the Addressables package and that worked, although addressables and subscenese don’t play well together because they cause duplicate resources in memory. Recently, I was told that instead of using the Addressables package, I could preload entities in a subscene and make a map of them and then use that map to spawn entities at runtime.

So I made a dummy project and attempted to build such a paradigm, but I am discovering that the entity indexes are nothing like what I expected. In the dummy project, the idea is to simply spawn two different entities periodically.

First of all, during the conversion in the editor, the two entities get stored into the map as index 3 and 4.
8303640--1089213--upload_2022-7-22_14-36-23.png

However, when I look at the DOTS Hierarchy, the two prefab entities are indexed 8 and 10 instead:
8303640--1089216--upload_2022-7-22_14-37-14.png

So I did not expect that. To make it even stranger, after pressing the “Play” button in the editor, the two prefab entity indexes changed yet again to 16 and 13.
8303640--1089219--upload_2022-7-22_14-38-48.png

Well, needless to say, the spawner looks up the ids in the map and tries to clone entity 3 and 4 (which don’t exist) when it really should be cloning 16 and 13.

I put the whole dummy project in a git repo here: https://gitlab.com/lclemens/buggy . Is anyone else doing this type of thing? What am I doing wrong, and why do the entity indexes change during conversion and again after play starts?

I don’t know about the entity indexes… I guessed they where just first come first serve… Maybe they should be deterministic, but if you add a new Object to the scene this could always offset all your indexes.

Normal hierarchy SubScene (opened for display purposes, normally closed):
8305689--1089483--upload_2022-7-23_21-44-6.png

The CarDisplayPrefabBuilder prefab has this component
8305689--1089486--upload_2022-7-23_21-45-34.png

Which links the prefab I want to use to an (unique) Enum value “Car”. A system collects all these PrefabBuilderComponents and puts the prefabs in a NativeHashmap with the enum as key.

The Entity that needs to spawn this prefab just holds the enum value :
8305689--1089489--upload_2022-7-23_21-49-12.png

Since not all prefabs might be available in the first frame I check before using it. In the IsVisibleEnemySystem :

ref_PrefabReferenceSystem = World.GetExistingSystem<PrefabReferenceSystem>();

                if (ref_PrefabReferenceSystem.allPrefabs.Count() == 0)
                {               
                    return;
                }

For use with Burst I put the ‘allPrefabs hashmap’ in a local variable (once filled it never changes in my case).

Works in editor and in build, but you need to manually link the prefabs and the enums.

I used to use enums but I’m trying to get away from those because they break when you reorder, rename, delete, or insert.

Ah, so you add the prefab “builders” directly to your subscene. Do you add a prefab tag to them then? Because if you don’t, then when any of your systems attempts to query for all spawners in the scene for example, the query would also return your builder one too.

The Builders are only alive for 1 frame, as soon as the system adds the Prefab Entity to the (Allocator.Persistent) hashmap they are destroyed. The prefab entity they hold has a prefab tag, since it is not instanced just referenced.
But because it is referenced in a Builder entity that is in a subscene, it too will be converted before runtime.

I guess its also not destroyed since the prefab reference is not in the linkedEntitiesGroup, since its not a child.

So I finally found a good solution after being stuck on this for days.

I figured out a way to avoid placing the prefabs directly in the scene and then deleting them. I guess I prefer it slightly more, but either way works.

Your write-up tipped me off to the idea that I needed to build the map at runtime instead of conversion time - many thanks.

I pushed the new code to https://gitlab.com/lclemens/buggy and it seems to work fine now.

@s_schoener I have a question about the binary storage of subscenes.

Say I have a prefab with a tree mesh and a mesh renderer and a material + texture. Now lets say I have 10 different prefabs that all use the same mesh and materials as the previously mentioned prefab. And now I put 50 instances of each prefab (500 total) into a subscene. Does the mesh and material get duplicated 500 times making the subscene file and memory footprint 500 times larger than a subscene with only on tree prefab in it? Or does it get duplicated 10 times (1 for each prefab variant) making it 10x larger? Or is the subscene storage format smart enough to only store the mesh and material one time and reuse it for all the instances? Or am I confused and maybe the subscene binary format doesn’t store meshes and materials at all and they get stored somewhere else?

(I am interested in the amount of storage required for the subscene files that get shipped with the game as well as runtime memory usage)

Does anyone know the answer to that last question?

I did learn one thing - textures don’t get compressed in subscenes, so if you have a bunch of them, the subscene cache file can become massive.