Page 1 of 1

Loading LODs on demand

Posted: Wed Jul 31, 2019 11:51 pm
by xissburg
I am not sure this is a good idea or not to optimize how resources are managed in a relatively large world. Not sure how other engines handle it but I was thinking about the possibility of loading LODs for a mesh as needed, and also the possibility of having an empty LOD where nothing is rendered when the object is too far. That would require making Ogre::SubMesh::mVao private and instead have an object that manages access to the Vaos. You'd query the vao for the current LOD and if it's not loaded it would return the closest available LOD and would begin loading the vao for the LOD that was requested so it will be available some time in the future the next time it is requested. It could also return null which means nothing would be rendered. It could also unload unused (least recently used) LODs on a "memory warning" event or something. This 'vao manager' would contain a reference to an interface which implements the loading of new LODs from file.

I have implemented LOD support in Ogre_glTF (using the MSFT_lod extension since there's no official LOD support yet) and made the necessary changes in Ogre 2.1 as well ("manual LODs") ( including support for morph target LODs. So I am thinking about the possibility of loading geometry from a glTF file as needed instead of simply loading everything at once.

Is this a good method or not? What else could be done here?


Re: Loading LODs on demand

Posted: Thu Aug 01, 2019 3:33 am
by dark_sylinc
I'm not sure why you think mVao should be private. Don't we have Renderable::getVaos already?

Your idea sounds good, you just need to be careful that mVao sits in a hot spot thus you can affect performance:
  • Beware that we don't support meshes with different vertex formats e.g. if LOD0 has 3 sets of UVs, all LODs must declare 3 sets of UVs. This is because we cache Hlms hashes at Renderable level (and it may be a little expensive to do it per LOD?). This is admittedly inconvenient because "level of detail" usually means "drop those extra UVs, the user cannot see all that detail". Same applies to the rest of vertex attr (normals, tangents, etc)
  • We already support disabling an object past certain distance. See MovableObject::setRenderingDistance
  • A nullptr mVao would be strongly discouraged, because:
    • Existing code would have to retrofit to ensure nullptrs don't cause crashes (both Ogre's and advanced user's)
    • For every submesh we render, we must perform if( vao ){ render(); }. A branch means more branch (mis)prediction, more cache misses, less objects we can render on screen.
    • A much better alternative is to use a dummy Vao that has the same vertex format layout but has 0 vertices to render. This is legal (may break on a few buggy Android drivers, probably) and would serve the same purpose without penalizing regular rendering. You could even share this Vao across other objects that have the same vertex format. It's like a "proxy replacement for nullptr"
Regarding mesh streaming, user 0xC0DEFACE has managed to implement a custom solution where the NULLRenderSystem is used in a worker thread to load meshes, then the memory is memcpy to the real active RenderSystem. Sounds a little hacky but it may be worth contacting him for ideas, and he was quite knowledgeable of the problem.

Re: Loading LODs on demand

Posted: Thu Aug 01, 2019 6:40 pm
by xissburg
dark_sylinc wrote:
Thu Aug 01, 2019 3:33 am
I'm not sure why you think mVao should be private. Don't we have Renderable::getVaos already?
Ogre::SubMesh::mVao is public ... h#lines-75. The idea is that it would load the vao for each LOD lazily so mVao would have to be encapsulated inside another object. If a vao is requested for an LOD that's not yet loaded, it would return an empty vao or one of the available vaos that's closer to the LOD you asked and would start loading the requested LOD.

I'll search for 0xC0DEFACE's posts.


Re: Loading LODs on demand

Posted: Thu Aug 01, 2019 7:32 pm
by dark_sylinc
Beware that the array in Renderable::mVaoPerLod is a hard copy of the array in SubMesh::mVaos

Although the VertexArrayObject pointers are shared, if you swap entries in SubMesh::mVaos or resize it, those changes won't be seen by the SubItems.

The reason for this is that although SubItems share the same Vaos, other Renderable implementation may have their own unique Vaos.

I guess it could be possible to change Renderable::mVaoPerLod into a pointer, that can either be owned by Renderable, or point to a shared array, like SubMesh::mVaos. But beware that's another level of indirection for RenderQueues to parse per Renderable.

Perhaps the extra level of indirection can be avoided with some advanced pointer manipulation though, since the actual data is in SubMesh::mVaos.mData. As long as you promise to never resize the Vaos, it should be possible to have both Arrays point to the same SubMesh::mVaos.mData.

Speaking of LODs, it may be worth exploring if it's just better to have LODs swap at a higher level (e.g. swap Items instead of Vaos). This allows much greater flexibility e.g. LODs that have different materials like absence of normal mapping, or even swapping an entire Item for an Impostor.
The questions are how to do Item swapping as clean, fast and easy as possible