Streamed IO and LOD City Demo

Announce new projects or updates of Irrlicht Engine related tools, games, and applications.
Also check the Wiki
omaremad
Competition winner
Posts: 1027
Joined: Fri Jul 15, 2005 11:30 pm
Location: Cairo,Egypt

Streamed IO and LOD City Demo

Post by omaremad »

I have made a streamed LOD scene node that handles memory usage and LOD of meshes and textures it loads asynchronously on a separate thread, i didn't not modify the irrlicht loaders, just some small ponts to make it work across all the drivers safely (most of the logic is at node level). i just want you guys to test it to see if it is really safe.

Just move away from the cube to change lod level (also nearer), only one mesh and one texture are in memory at anyone time.

you will see this in your os memory monitor and also the fact that irrlicht has to reload previously deleted data. IO is streamed and the lod change wont take place till the io is compeletly done for the required LOD level (useful for big detail changes ie: from 5 megs to 20 megs)

It also says streaming twice at each lod switch because i have two scenenodes in the same place to test thread safety while lod'ing multiple meshes at the same time.

http://www.mediafire.com/?sharekey=9ce4 ... b9a8902bda

just unzip and open terminal

do
./demo
to run. (make sure you have pthreads installed which is most people)

Sorry about the simple demo im short on time, just imagine it being applied to city buildings for something like GTA.
Last edited by omaremad on Sat Oct 25, 2008 6:24 pm, edited 2 times in total.
"Irrlicht is obese"

If you want modern rendering techniques learn how to make them or go to the engine next door =p
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

I'd love to try this out but don't have Linux, any chance of a windows version? I tried downloading in the hope that you'd provided the source but to no avail (maybe because you've edited the engine).
Image Image Image
rogerborg
Admin
Posts: 3590
Joined: Mon Oct 09, 2006 9:36 am
Location: Scotland - gonnae no slag aff mah Engleesh
Contact:

Post by rogerborg »

Ditto; I'd love to try this out on Windows, and more importantly, see the source.
Please upload candidate patches to the tracker.
Need help now? IRC to #irrlicht on irc.freenode.net
How To Ask Questions The Smart Way
omaremad
Competition winner
Posts: 1027
Joined: Fri Jul 15, 2005 11:30 pm
Location: Cairo,Egypt

Post by omaremad »

The windows implmentation of pthreads is a bit different (mutexes dont seem to pause other threads :shock:) so i only included one streamed building. Sorry for how crap the demo is... it would look nicer with an actual finished model.

The low LOD has a red 256 texture, high LOD has a white 1024 texture, im guessing mipmapping can be disabled to reduce texture wastage since we are streaming the LOD for textures too.

Polycounts are double (see window caption and console for proof of streaming) Also if you have multi cpu the cpu meters in task manger should spike.

Source wont be released till i get time to fix this bug and also test on other peoples cpu's.

http://www.mediafire.com/file/my2mnfwyzn2/thread.zip <windows
"Irrlicht is obese"

If you want modern rendering techniques learn how to make them or go to the engine next door =p
omaremad
Competition winner
Posts: 1027
Joined: Fri Jul 15, 2005 11:30 pm
Location: Cairo,Egypt

Post by omaremad »

Also i just found it leaks memory... anway the diffrence between low LOD and high LOD memory is constant.
"Irrlicht is obese"

If you want modern rendering techniques learn how to make them or go to the engine next door =p
rogerborg
Admin
Posts: 3590
Joined: Mon Oct 09, 2006 9:36 am
Location: Scotland - gonnae no slag aff mah Engleesh
Contact:

Post by rogerborg »

omaremad wrote:Source wont be released till i get time to fix this bug and also test on other peoples cpu's.
Oh, I'll wait until then.
Please upload candidate patches to the tracker.
Need help now? IRC to #irrlicht on irc.freenode.net
How To Ask Questions The Smart Way
omaremad
Competition winner
Posts: 1027
Joined: Fri Jul 15, 2005 11:30 pm
Location: Cairo,Egypt

Post by omaremad »

If you are interested in the concept its like this:

Requirement:
Make minimum number of irrlicht calls from a thread other than the main one (bc of irr is not thread safe)

So what really needs to be threaded?
IO<- so slow esp with step by step reading and pasing.

So i use a mutex on the smgr->getMesh(), this allows one mesh and its textures to be loaded at one time a "global mutex", there is however some complications... one being no opengl calls are allowed from other threads unless they own the context.

So what i do is only load the textures to ram using the IO thread (simple modification to nulldriver.cpp getTexture() ) and have a textureQue in the driver which uploads the data to the gpu when the main thread finishes the current frame.

Once the data is loaded the io thread signals the main thread to switch the mesh pointers in the custom scenenode and delete the old mesh and its textures(monitors refrence count to see if its used by other nodes).
"Irrlicht is obese"

If you want modern rendering techniques learn how to make them or go to the engine next door =p
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

That looks pretty cool, no observable slow down when it changes LOD. So presumably when you're streaming in the new data you're removing and deleting the old stuff yeah?

Looks good with one building though i wonder if it would suffer a bit with trying to do a whole city at all? Might have to handle lots of LOD changes at once but maybe you just need to design the LOD switching so it happens minimally.
Image Image Image
night_hawk
Posts: 153
Joined: Mon Mar 03, 2008 8:42 am
Location: Suceava - Romania
Contact:

Post by night_hawk »

It works just as expected and it's seamless. Nice.
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Post by christianclavet »

That is really nice.

For the load stream feature, I would have to test this with a bigger scene to check. Right now the mesh is so small, that I don't notice the streaming loading process (also the camera is behind the mesh so we have to turn around to see it)

The LOD is really working well.

good work!
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

JP wrote:That looks pretty cool, no observable slow down when it changes LOD. So presumably when you're streaming in the new data you're removing and deleting the old stuff yeah?

Looks good with one building though i wonder if it would suffer a bit with trying to do a whole city at all? Might have to handle lots of LOD changes at once but maybe you just need to design the LOD switching so it happens minimally.
Yeah, if you wanted to read more on it, then you could read the whitepaper by Insomniac Games on there streaming system. They basically stream in data when at least 1-4 fragments would be visible on screen (to reduce the pop in.) And they also set a time for how long the object would have to be non-visible before it was removed from memory.
TheQuestion = 2B || !2B
rogerborg
Admin
Posts: 3590
Joined: Mon Oct 09, 2006 9:36 am
Location: Scotland - gonnae no slag aff mah Engleesh
Contact:

Post by rogerborg »

Halifax wrote:And they also set a time for how long the object would have to be non-visible before it was removed from memory.
That's a curiously passive strategy. If you're truly concerned about memory, then you really have to know how much memory you've got available, and free up as necessary to stay within the limit.

The corollary of that is that since freeing is a relatively cheap operation compared to (re)loading, if know when you have to free, then you don't free anything until you have to.
Please upload candidate patches to the tracker.
Need help now? IRC to #irrlicht on irc.freenode.net
How To Ask Questions The Smart Way
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

rogerborg wrote:
Halifax wrote:And they also set a time for how long the object would have to be non-visible before it was removed from memory.
That's a curiously passive strategy. If you're truly concerned about memory, then you really have to know how much memory you've got available, and free up as necessary to stay within the limit.

The corollary of that is that since freeing is a relatively cheap operation compared to (re)loading, if know when you have to free, then you don't free anything until you have to.
Ah yes rogerborg, you caught me slipping. All textures do include a stale timer for how long it hasn't been visible, but it is only used when textures need to be removed. And the only time that happens is when a new texture has to be streamed in.
TheQuestion = 2B || !2B
omaremad
Competition winner
Posts: 1027
Joined: Fri Jul 15, 2005 11:30 pm
Location: Cairo,Egypt

Post by omaremad »

Manged to get some time. I used valgrind to get rid of the leaks, it only leaks now if you close the application mid-streaming which shouldn't be a problem(the application's ram is reclaimed as it dies).

here is the source, it will not run on hardware accelrated modes unless you modify irrlicht with some simple modifications, but the code should work fine in software modes with a normal irrlicht (untested)

Its probably not very useful in its current form (ill make a proper release when i have time), bit i think you guys are interested in the source.


Irrlicht and pthreads are needed of course.

Code: Select all

#include <irrlicht.h>
#include <iostream>

using namespace irr;
#include <pthread.h>

scene::ISceneManager* smgr;
//a struct to carry arguemnts across threads
struct arguments
{
    c8* name;
    scene::IAnimatedMesh* oldMesh;
    scene::IAnimatedMesh** smesh;
    core::aabbox3d<f32>* sBox;
    scene::ISceneManager* ssmgr;
    volatile bool* switching;

};
//lod struct for setting distances and meshes
struct lodLevel
{
    c8* meshName;
    float distance;
};

arguments argt;

//allows one streamer thread at a time.
volatile bool switching=false;

void isswitching(bool in)
{

    switching=in;

};

class CStrreamedLODSceneNode : public scene::ISceneNode
{
    pthread_t thread;

    core::aabbox3d<f32> Box;
    video::SMaterial Material;
    scene::IAnimatedMesh* mesh;
    scene::IAnimatedMesh* streamedMesh;
    scene::ISceneManager* mgr;

    int currentLOD;

    core::array<lodLevel> levels;
    volatile bool switchMeshes;
    bool ready;

public:

    CStrreamedLODSceneNode(scene::ISceneNode* parent, scene::ISceneManager* smgr, s32 id)
            : scene::ISceneNode(parent, smgr, id)
    {
        mgr=smgr;
        Material.Wireframe = false;
        Material.Lighting = false;
        //Material.MaterialType=video::EMT_LIGHTMAP;
        mesh=mgr->getMesh("2.obj");
        mesh->grab();


        lodLevel Low;
        Low.meshName="2.obj";
        Low.distance=0;

        lodLevel High;
        High.meshName="1.obj";
        High.distance=500;

        levels.push_back(Low);
        levels.push_back(High);
        currentLOD=0;
        Box=mesh->getMesh(0)->getBoundingBox();
        switchMeshes=false;
        ready=true;

    }

    virtual ~CStrreamedLODSceneNode()
    {
        mesh->drop();
    }

    virtual void OnRegisterSceneNode()
    {
        if (IsVisible)
        {
            SceneManager->registerNodeForRendering(this);
        }

        ISceneNode::OnRegisterSceneNode();
    }


    virtual void render()
    {
        //LOD logic
        handleLOD();
        video::IVideoDriver* driver = SceneManager->getVideoDriver();

        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);

        //If there is freshly streamed data use it instead of current data
        if (switchMeshes)
            switchData();

        for (int i =0; i<mesh->getMesh(0)->getMeshBufferCount();i++)
        {
            mesh->getMesh(0)->getMeshBuffer(i)->getMaterial().Lighting=false;
            driver->setMaterial(mesh->getMesh(0)->getMeshBuffer(i)->getMaterial());
            driver->drawMeshBuffer(mesh->getMesh(0)->getMeshBuffer(i));
        }

    }


    virtual const core::aabbox3d<f32>& getBoundingBox() const
    {
        return Box;
    }

    virtual u32 getMaterialCount() const
    {
        return 1;
    }

    virtual video::SMaterial& getMaterial(u32 i)
    {
        return Material;
    }

    void handleLOD()
    {
        //NB makesure your mesh is centered around the origin, if not use the bounding box
        //centre instead of getPosition()
        core::vector3df camPos= mgr->getActiveCamera()->getPosition();
        float distance = core::vector3df(camPos-getPosition()).getLength();
        int requiredLevel=0;

        for (int i=0; i<levels.size();i++)
        {
            if (distance>levels[i].distance)
            {
                requiredLevel=i;

            }

        }
        if (requiredLevel!=currentLOD)
        {
            //If failed then another node instance is streaming.
            //Streaming will happen the consequent frame since currentLOD is still != requiredLevel
            if (streamMesh(levels[requiredLevel].meshName))
                currentLOD=requiredLevel;

        }

    }

    bool streamMesh(c8* name)
    {


        argt.name = name;
        argt.oldMesh = mesh;
        argt.smesh = &streamedMesh;
        argt.sBox= &Box;
        argt.ssmgr = this->mgr;
        argt.switching=&switchMeshes;

        //while (CStrreamedLODSceneNode::switching){};
        if (!switching)
        {
            ready = false;
            this->mgr->getMesh(name);
            pthread_create( &thread, NULL, callback, (void*)&argt);
            return true;
        }
        else return false;

    }

    void switchData()
    {


        mesh->drop();

        if (mesh->getReferenceCount()==1)
        {

            for (int i =0; i<mesh->getMesh(0)->getMeshBufferCount();i++)
            {
                //clean out unsed textures (by layer)
                for (int t=0;t<4;t++)
                {
                    video::ITexture* tex=mesh->getMesh(0)->getMeshBuffer(i)->getMaterial().getTexture(t);
                    if (tex)
                    {
                        int cnt=tex->getReferenceCount();
                        if (cnt==1)
                            mgr->getVideoDriver()->removeTexture(mesh->getMesh(0)->getMeshBuffer(i)->getMaterial().getTexture(t));
                    }
                }
            }
            mgr->getMeshCache()->removeMesh(mesh);
        }

        mesh=streamedMesh;

        switchMeshes=false;

        ready = true;

    }


    static void* callback(void* iarg)
    {
        isswitching(true);
        arguments ar;
        ar=*(arguments*)iarg;
        printf("Streaming\n");

        scene::IAnimatedMesh* tempMesh = ar.ssmgr->getMesh(ar.name);
        *ar.smesh=tempMesh;

        tempMesh->grab();

        isswitching(false);
        *ar.switching=true;

        if (!pthread_detach( pthread_self()) )
            return NULL;
    }


};

int main()
{
    // let user select driver type

    video::E_DRIVER_TYPE driverType;

    printf("Please select the driver you want for this example:\n"\
           " (c) OpenGL 1.5\n"\
           " (d) Software Renderer\n (e) Burning's Software Renderer\n"\
           " (f) NullDevice\n (otherKey) exit\n\n");

    char i;
    std::cin >> i;

    switch (i)
    {

    case 'c':
        driverType = video::EDT_OPENGL;
        break;
    case 'd':
        driverType = video::EDT_SOFTWARE;
        break;
    case 'e':
        driverType = video::EDT_BURNINGSVIDEO;
        break;
    case 'f':
        driverType = video::EDT_NULL;
        break;
    default:
        return 0;
    }

    // create device

    IrrlichtDevice *device =
        createDevice(driverType, core::dimension2d<s32>(640, 480), 32, false);

    if (device == 0)
        return 1; // could not create selected driver.

    // create engine and camera

    device->setWindowCaption(L"Streamed io and LOD -Omaremad");

    video::IVideoDriver* driver = device->getVideoDriver();
    smgr = device->getSceneManager();

    smgr->addCameraSceneNodeFPS(0)->setPosition(core::vector3df(100,100,100));


    CStrreamedLODSceneNode *myNode =
        new CStrreamedLODSceneNode(smgr->getRootSceneNode(), smgr,4);

    CStrreamedLODSceneNode *myNode2 =
        new CStrreamedLODSceneNode(smgr->getRootSceneNode(), smgr,5);




    myNode2->setScale(core::vector3df(50,50,50));
    myNode->setScale(core::vector3df(50,50,50));


    u32 frames=0;
    while (device->run())
    {
        driver->beginScene(true, true, video::SColor(0,100,0,100));

        smgr->drawAll();

        driver->endScene();
        if (++frames==100)
        {
            core::stringw str = L"Irrlicht Engine [";
            str += driver->getName();
            str += L"] FPS: ";
            str += (s32)driver->getFPS();

            device->setWindowCaption(str.c_str());
            frames=0;
        }
    }


    myNode->drop();
    myNode2->drop();
    device->drop();
    pthread_exit(0);
    return 0;
}

"Irrlicht is obese"

If you want modern rendering techniques learn how to make them or go to the engine next door =p
omaremad
Competition winner
Posts: 1027
Joined: Fri Jul 15, 2005 11:30 pm
Location: Cairo,Egypt

Post by omaremad »

Here is a demo shows its application to a city.

The meshes were made automtaically in blender using discombulator script, and exported using irrb(i modfied it with a vertex dictionary so i can export vert colors). The meshes are subdevided once and saved in the ascii .irrmesh format to add more bulk to them (to stress the streaming). Normals made in InsaneBump :wink:

On windows texture creation in opengl/dx is very slow so i added a third thread to handle slow driver commands.

thread one: rendering-always running
thread two: streams the meshes and textures and adds them to the upload to gpu list - created when needed.
thread three: creates and uploads gpu textures-always running

The shader is a single light version of TGMS one

I really like this bit he used, gives a nice colour tint.

Code: Select all

   
    "   if(color.r>1.0){color.gb+=color.r-1.0;}"
    "   if(color.g>1.0){color.rb+=color.g-1.0;}"
    "   if(color.b>1.0){color.rg+=color.b-1.0;}"
http://www.mediafire.com/file/r0qhzyyumho/gta.zip
vista only
"Irrlicht is obese"

If you want modern rendering techniques learn how to make them or go to the engine next door =p
Post Reply