Wiki

Clone wiki

Llwyth / Llwyth Ogre3D Porting Notes

Llwyth Ogre3D 1.10 - 2.0 Porting Guide

As of fb4e3c9, Llwyth is using 1.10, as 2.0 has recently be released to a more stable state, I will be porting the engine to make use of new features, such as instanced entities, better frustrum culling, and synchronous threading. This is in effort to keep up to date to move towards the currently in unstable state Ogre3D 2.1, which has significant changes to shader and material management (HMLS), and much improved GL3+ support (As older GL rendersystem will have been dropped).

Engine Changes

1.10 - 2.0

SceneManager Declaration: Needs to specify threading technique

Ogre::SceneManager* pSceneMgr = root.createSceneManager(
   Ogre::ST_GENERIC,
   1,
   Ogre::INSTANCING_CULLING_SINGLETHREAD
);

List of Instance Culling Types (InstancingThreadedCullingMethod) can be changed at runtime!:

  • INSTANCING_CULLING_SINGLE: Don't multithread instanced entities frustrum culling. Only InstanceBatch/Entities frustrum culling is threaded.

  • INSTANCING_CULLING_SINGLETHREAD: When scene does not use HW Basic or HW VTF, or few instanced entities overall compared to normal entities.

  • INSTANCING_CULLING_THREADED: High use of HW Basic / HW VTF (For SCENE_STATIC instances too). High use of PASS_SCENE (PSSM shadows/multiple light sources/adv. compositing).

Compositors/Workspaces: Rather than using viewport/render targets every frame.

#include "OGRE/Compositor/OgreCompositorManager2.h"
...
Ogre::CompositorManager2* pCompositorManager = root.getCompositorManager2();
const Ogre::String workspaceName = "scene workspace";
pCompositorManager->createBasicWorkspaceDef(workspaceName, Ogre::ColourValue::Black);
pCompositorManager->addWorkspace(pSceneMgr, pRenderWin, pCamera, workspaceName, true);

Lights: Need to have parent sceneNode, as they no longer have inherited scenenode components.

Ogre::SceneNode *lightNode = pSceneMgr->getRootSceneNode()->createChildSceneNode();
lightNode->setPosition(20, 80, 50);
lightNode->attachObject(pSceneMgr->createLight());

Shadow Nodes: Now inherited from new compositor node, only depth shadow maps are supported.

// Standard focused shadow node
compositor_node_shadow myShadowNode
{
    technique focused
    shadow_map 0 2048 2048 PF_FLOAT16_R light 0
    //Render shadow map "0"
    shadow_map 0
    {
        pass clear { colour_value 1 1 1 1 }
        pass render_scene
        {
            rq_first 0
            rq_last max
        }
    }
}

// Shadow map atlas (experimental, implement after stability check) ~25% memory save
compositor_node_shadow myShadowNode
{
    technique pssm
    //Render 1st closest light, splits 0 1 & 2, and 2nd light into myAtlas
    shadow_map myAtlas 2048 2048 PF_FLOAT16_R light 0 split 0 viewport 0 0 0.5 0.5
    shadow_atlas myAtlas light 0 split 1 viewport 0 0.5 0.5 0.5
    shadow_atlas myAtlas light 0 split 2 viewport 0.5 0 0.5 0.5
    technique focused
    shadow_atlas myAtlas light 1 viewport 0.5 0.5 0.5 0.5
    /* ... */
}

Shaders: We can still use Shiny as our shader manager, as we do not typically need anything too extravagant yet...

Instancing: Can now use both software and hardware instancing on entities, this means significant performance increase in duplicated meshes (and segments/bones), and can be batched according to bounding boxes.

  • HW VTF

  • HW VTF LUT (Look Up Table): Good for crowds with shared animation sets

mSceneMgr->createInstanceManager( "InstanceMgr", "MyMesh.mesh",
            ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME,
            InstanceManager::HWInstancingVTF,
            numInstancesPerBatch, IM_USEALL|IM_VTFBONEMATRIXLOOKUP );
  • HW Basic: fastest instancing technique, Does not support animations, good for use in static geometry (rocks/trees in higher LOD), etc.

  • Instancing in 2.0 also allows for custom parameters to be sent for each instance manager, which could be used for setting colours of units.

  • Allows for reduced RAM consumption on submeshes by using:

// Create InstanceManagers
std::vector<InstanceManager*> instanceManagers;
MeshPtr mesh = MeshManager::getSingleton().load( "myMesh.mesh" );
for( uint16 i=0; i<mesh->getNumSubMeshes(); ++i )
{
    InstanceManager *mgr =
        mSceneMgr->createInstanceManager( "MyManager" + StringConverter::toString( i ),
                    "myMesh.mesh",
                ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME,
                    InstanceManager::HWInstancingVTF, numInstancePerBatch,
                    flags, i );
    instanceManagers.push_back( mgr );
}

// Share entity transforms with idx 0 base mesh
SceneNode *sceneNode; //Asumed to be valid ptr
std::vector<InstancedEntity*> instancedEntities;
for( size_t i=0; i<instanceManagers.size(); ++i )
{
    InstancedEntity *ent = instanceManagers[i]->createInstancedEntity( "MyMaterial" );

    if( i != 0 )
        instancedEntities[0]->shareTransformWith( ent );

    sceneNode->attachObject( ent );
    instancedEntities.push_back( ent );
}

...

// Destroy Instances
SceneNode *sceneNode; //Asumed to be valid ptr
std::vector<InstancedEntity*> instancedEntities;
for( size_t i=0; i<instanceManagers.size(); ++i )
    instanceManagers[i]->destroyInstancedEntity( instancedEntities[i] );

mSceneMgr->getRootSceneNode()->removeAndDestroyChild( sceneNode );
  • Batch defragmenting: Choose appropriate technique:
    • Avoid Defragmenting: Create static objects in order of proximity (must be static), Create batches by areas (ie. terrain pages) so that the whole batch is removed cleanly when unloaded.
    • Defragment periodically using: InstanceManager::defragmentBatches( bool optimizeCulling ), test performance accordingly.

Synchronous Threading: Ogre 2.0 allows threading, where workers must complete for the parent to proceed. Workers are not polled during renderOneFrame().

mSceneMgr = mRoot->createSceneManager(ST_GENERIC, numThreads,
                        INSTANCING_CULLING_THREADED,
                        "ExampleSMInstance");

Can also be used for custom tasks (like physics, networking, large render systems, etc):

#include <Threading/OgreUniformScalableTask.h>
class MyThreadedTask : public Ogre::UniformScalableTask
{
public:
    virtual void execute( size_t threadId, size_t numThreads )
    {
        ...
    }
};
...
// Some usage code...
MyThreadedTask myThreadedTask;
sceneMgr->executeUserScalableTask( myThreadedTask, true );   // Block or leave to process for bool
...
// Do work here
...
sceneMgr->waitForPendingUserScalableTask();

2.0 - 2.1

TBD

Porting Notes

Updated