realXtend / tundra

realXtend Tundra SDK, a 3D virtual world application platform.
www.realxtend.org
Apache License 2.0
84 stars 70 forks source link

Implement support Ogre instancing #678

Closed jonnenauha closed 11 years ago

jonnenauha commented 11 years ago

I'm proposing we add Ogre instancing support to Tundra. Starting this feature request issue here to get some discussion going on.

Ogre instancing Ogre instancing will patch a optimal amount of near each other instances together and draw/process them in sequence. As far as I have understood it this reduces the amount of texture/material/mesh state changes in Ogres internal render loop, hence reducing CPU load.

It does frustrum culling per batch and afaik per instance inside the batch. This is why its good to have near by instances in the same batch, but there are functions in the intancing manager that automate this ordering. Instancing also has CPU (and possibly GPU) side memory benefits, aka spends less memory. I haven't dived in yet on the shader aspect and what is expected there, I ran my tests with the Ogre example instancing shaders/materials. Before I realized to do so one thing was made clear; that moving objects wont work if you are not using special instancing shaders (as said haven't looked into it why this is).

Prototyping I've been curious about this for a while now. I have made preliminary prototyping at my free time to get an idea how the Tundra integration could be made. The advantages for scenes that have a single mesh asset repeated many times are clear.

Here is a screenshot with additional info and profilers of my proto runs with Tundra: https://dl.dropboxusercontent.com/u/3589544/meshmoon/rocket/instancing-test.jpg

Tundra implementation ideas There are two main ways to handle this 1) We extend EC_Mesh to have new instancing related attributes. 2) We make a new EC_InstancedMesh that is dedicated for this sort of thing.

My prototyping involved creating a OgreMeshAsset::CreateInstance(const AssetReferenceList &materials) function that is called by EC_Mesh when it wants an instance of the mesh asset. For me it makes sense that its OgreMeshAsset that manages the various components related to creating instances. This centralizes ownership of the needed Ogre::InstanceManagers and creating instances with particular materials. This can of course be changed to EC_Mesh if it does not make sense for a IAsset to produce ogre objects, however this is comparable to QtUiAsset producing QWidgets that we already have now. Maybe at the same time we could refactor OgreMeshAsset to produce Ogre::Entity* too.

I also expanded EC_Mesh so that it can be in one of two modes, using a Ogre::InstancedEntity* or Ogre::Entity*. Some aspects of the internals wont change at all like attaching it to the scene node that is parented to the EC_Placeable scene node. Instancing would be transparent to the way we move objects with the EC_Placeable and EC_Mesh transforms.

What would need to change if making sure the used materials have correct kind of instancing vertex and fragment shaders, or at least produce errors/warnings if they don't. All materials that will be used will need to be loaded before instances are created vs. now we add the mesh to scene and drop materials as they are loaded. A Ogre::InstancedEntitys material cannot be changed after creation. Using a empty material will crash Ogre, so all materials must be in loaded (or if failing we pass in our load error material) before creation.


So, does anyone have experience with Ogre instancing? What do you think how this support should be added at the Tundra level, what code goes where, EC_Mesh or new component? I could have a go at a proper implementation once we get things ironed out here.

jonnenauha commented 11 years ago

Please do comment. Especially lord rex @cadaver when you have free time at your hands to read my rant :)

peterclemenko commented 11 years ago

While some things would likely break, it would probably be best to make instanced the default. This is something that would be needed long term anyway, and most modern engines handle by default. It will also make it less of a pain down the road for new people to come on so they don't ask why it's so slow when it's using a mesh, rather than an instanced mesh. It might be useful to make a script however to port existing scenes to use instancing in order to help with compatibility issues however.

jonnenauha commented 11 years ago

I think we can't make instancing default at least at the start. There are also disadvantages instancing brings: it limits the texture coord count you can use (seems some techniques allows 3), the materials you use are more complex (you need to know what kind of vertex/fragment shaders to use), for skeletal animated things there are bone weight limitations (at least one technique only allowed 1 weight per bone which is afaik a low number for complex animations), if you don't have high enough instance count using instancing will be more heavy as it pre-allocates InstancedEntities once a new batch is created, certain instancing techniques in Ogre require certain hardware features to work (you need a quite recent GPU to get the max benefit or to make things work at all) etc. I'm sure there are more :) but most of these are a problem even if its not the default behavior.

But once we get things mapped it could even be a sensible default. At least at the point where we can detect during runtime to switch instancing off if your skeletal animations/materials are not proper for it.

And yeah there should be tools. I just made a scene optimizer for our Meshmoon Rocket client that automatically converts textures to CRN/DDS, resizes textures to nearest pow2, generates mipmaps and zip bundles all your scene assets. I could very well see this tool also detecting if you have a big number of similar meshes in the scene with same material sets and suggest to switch instancing on (and would generate a proper material for you using the current one as a base). We are very interested in these kind of end-user/artist/author tools at Meshmoon and intend to make these kind of things a reality.

cadaver commented 11 years ago

Excellent prototyping!

My suggestion is that since InstanceManagers are tied to an Ogre SceneManager, the OgreWorld (which is our wrapper for Ogre SceneManager) should handle ownership of InstanceManagers. This would require creating a search key based on the mesh asset and the material assets to find the proper instancemanager to add instances to, or to create a new on. When a mesh component's materials change, it needs to deregister itself from its current instancemanager, and add itself to a new one. Furthermore, it needs to track the loaded status of materials and not attempt to instance itself before all are loaded.

From user perspective it would be most convenient to have an "enableInstancing" bool attribute in EC_Mesh. However, there are many codepaths & components which access EC_Mesh and assume certain things like being able to change materials directly, or animate the mesh, and furthermore EC_Mesh is already rather complicated.

I would suggest starting (cautiously) with adding the instancing functionality to EC_Mesh, but if at any point the code risks becoming unmanageable, then rather create a new EC_InstancedMesh component.

Due to Ogre's lack of robustness we cannot expect to do things we would be able to do with a modern render engine. I agree that in an ideal world instancing should be default, or even automatic, but that would require a different material abstraction, where the engine chooses proper shaders to use, or very robust automatic conversion of materials.

For starters I recommend that skeletal meshes are not attempted to be instanced. This can be implemented later if it is feasible, at all.

jonnenauha commented 11 years ago

Yes it's true we do use mesh->OgreEntity()->stuff() etc. directly all around the codebase. I will start trying to implement this cleanly in EC_Mesh and see how it goes. I think just checking the codebase that nothing uses the Ogre::Entity* blindly without null checks should get things in check, its already done well inside EC_Mesh itself. There will be some additional code for especially material handling (changing them at runtime after component creation) but i think that it should be possible to keep it clean.

I agree OgreWorld is a good place for the manager book keeping. I will do it like that. And also I will start by only supporting EC_Mesh that dont define skeleton ref.

@cadaver Do you think we should auto inject the proper vextex/fragment programs into the materials upon instancing if they are not there already? And should we bail out of instancing if the materials already are using some other shaders? This might be a nice addition to automating the support, artists would not have to copy paste the instancing shaders to materials. We can inspect the shaders from the material asset as its fully loaded before instancing can be done. I would need to make in memory copies of the materials if I do need to inject shaders, as another mesh without instancing might be using that same material.

cadaver commented 11 years ago

Yes, autoinjecting the shaders would be good, and gives experience for other similar manipulations, like injecting a skinning vertex shader (issue #459)

We probably should have an instancing path also in the SuperShader.

Automodifying materials can however get complex quite fast and has a lot of possibility for errors, and in case of Ogre, crashes.

jonnenauha commented 11 years ago

Started my work on a new branch https://github.com/Adminotech/tundra/commit/607593afc8ad1e98004fe123adc6318720ec4db7 Lets see how it progresses from there :)

jonnenauha commented 11 years ago

I've sent a pull request if people are interested https://github.com/realXtend/naali/pull/680. Its not fully done yet but wanted to get it under review as fast as possible so we can catch things early together.

cadaver commented 11 years ago

Pull request #680 is now merged.