Irrlicht far too slow (most likely I'm too dumb)

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
ooxi
Posts: 3
Joined: Fri Sep 25, 2015 7:10 pm

Irrlicht far too slow (most likely I'm too dumb)

Post by ooxi »

Hi,


I have a simple application derived from Tutorial 3: Custom Scene Node [1]. I'm only rendering 1200 cubes (each cube has 12 triangles thus 14400 triangles in total) which all share one texture and I get about 7fps when rendering in OpenGL on Linux. I tried the buring video software renderer and get an equal number of fps. Therefore I thought my OpenGL driver is broken but glmark2-es2 [2] has no less than a 100 to 500 fps (depending on the concete test). What's wrong with my (very simple) program?

Code: Select all

#include <cstdint>
#include <random>
#include <stdexcept>
 
#include <irrlicht.h>
#include "driverChoice.h"
 
using namespace irr;
 
 
 
 
 
typedef enum {
    UPPER_LEFT,
    UPPER_RIGHT,
    LOWER_LEFT,
    LOWER_RIGHT
} Corner;
 
 
 
class GtaTextureIndex {
 
    public:
        static uint32_t const ROWS = 31;
        static uint32_t const COLUMNS = 32;
 
    private:
        uint32_t const row;
        uint32_t const column;
 
 
        static uint32_t row_offset(Corner corner) {
            return (UPPER_LEFT == corner) || (UPPER_RIGHT == corner)
                ? 0 : 1;
        }
 
        static uint32_t column_offset(Corner corner) {
            return (UPPER_LEFT == corner) || (LOWER_LEFT == corner)
                ? 0 : 1;
        }
            
 
 
    public:
        GtaTextureIndex(uint32_t const row, uint32_t const column) : row(row), column(column) {
            if (row >= ROWS) {
                throw std::runtime_error(std::string("`row' invalid"));
            }
            if (column >= COLUMNS) {
                throw std::runtime_error(std::string("`column' invalid"));
            }
        }
 
        float u(Corner corner) const {
            return (float)(column + column_offset(corner)) / COLUMNS;
        }
 
        float v(Corner corner) const {
            return (float)(row + row_offset(corner)) / ROWS;
        }
};
 
 
 
 
 
class Square : public scene::ISceneNode {
 
    private:
        core::aabbox3d<f32> Box;
        video::S3DVertex Vertices[4];
        video::SMaterial Material;
 
    public:
 
        Square(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, GtaTextureIndex const& texture_idx) : scene::ISceneNode(parent, mgr, id) {
            Material.Wireframe = false;
            Material.Lighting = false;
            Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
 
            video::SColor white = video::SColor(255,255,255,255);
 
            Vertices[0] = video::S3DVertex(-10,+10,0,   1,1,0,  white, texture_idx.u(UPPER_LEFT), texture_idx.v(UPPER_LEFT));   // A
            Vertices[1] = video::S3DVertex(+10,+10,0,   1,0,0,  white, texture_idx.u(UPPER_RIGHT), texture_idx.v(UPPER_RIGHT)); // B
            Vertices[2] = video::S3DVertex(-10,-10,0,   0,1,1,  white, texture_idx.u(LOWER_LEFT), texture_idx.v(LOWER_LEFT));   // C
            Vertices[3] = video::S3DVertex(+10,-10,0,   0,0,1,  white, texture_idx.u(LOWER_RIGHT), texture_idx.v(LOWER_RIGHT)); // D
 
            Box.reset(Vertices[0].Pos);
 
            for (s32 i=1; i<4; ++i) {
                Box.addInternalPoint(Vertices[i].Pos);
            }
        }
 
        virtual void OnRegisterSceneNode() {
 
            if (IsVisible) {
                SceneManager->registerNodeForRendering(this);
            }
 
            ISceneNode::OnRegisterSceneNode();
        }
 
        virtual void render() {
            u16 indices[] = {   0,2,3, 2,1,3, 1,0,3, 2,0,1  };
            video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
            driver->setMaterial(Material);
            driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
            driver->drawVertexPrimitiveList(&Vertices[0], 4, &indices[0], 4, video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
        }
 
        virtual const core::aabbox3d<f32>& getBoundingBox() const {
            return Box;
        }
 
        virtual u32 getMaterialCount() const {
            return 1;
        }
 
        virtual video::SMaterial& getMaterial(u32 i) {
            return Material;
        }
 
};
 
 
 
 
 
class Cube : public scene::ISceneNode {
 
    private:
        core::aabbox3d<f32> Box;
 
        Square front;
        Square back;
        Square left;
        Square right;
        Square top;
        Square bottom;
 
    public:
 
        Cube(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
                    GtaTextureIndex const& front_texture_idx,
                    GtaTextureIndex const& back_texture_idx,
                    GtaTextureIndex const& left_texture_idx,
                    GtaTextureIndex const& right_texture_idx,
                    GtaTextureIndex const& top_texture_idx,
                    GtaTextureIndex const& bottom_texture_idx
                ) :
                    scene::ISceneNode(parent, mgr, id),
                    front(this, mgr, id + 1, front_texture_idx),
                    back(this, mgr, id + 2, back_texture_idx),
                    left(this, mgr, id + 3, left_texture_idx),
                    right(this, mgr, id + 4, right_texture_idx),
                    top(this, mgr, id + 5, top_texture_idx),
                    bottom(this, mgr, id + 6, bottom_texture_idx)
                {
 
            addChild(&front);
            addChild(&back);
            addChild(&left);
            addChild(&right);
            addChild(&top);
            addChild(&bottom);
 
 
            front.setPosition(core::vector3df(0, 0, -10));
            back.setPosition(core::vector3df(0, 0, +10));
 
            left.setRotation(core::vector3df(0, -90, 0));
            left.setPosition(core::vector3df(-10, 0, 0));
 
            right.setRotation(core::vector3df(0, +90, 0));
            right.setPosition(core::vector3df(+10, 0, 0));
 
            top.setRotation(core::vector3df(-90, 0, 0));
            top.setPosition(core::vector3df(0, -10, 0));
 
            bottom.setRotation(core::vector3df(+90, 0, 0));
            bottom.setPosition(core::vector3df(0, +10, 0));
        }
 
 
        virtual void OnRegisterSceneNode() {
 
            if (IsVisible) {
                SceneManager->registerNodeForRendering(this);
            }
 
            ISceneNode::OnRegisterSceneNode();
        }
 
        virtual void render() {
            front.render();
            back.render();
            left.render();
            right.render();
            top.render();
            bottom.render();
        }
 
        virtual const core::aabbox3d<f32>& getBoundingBox() const {
            return front.getBoundingBox();
        }
 
        virtual u32 getMaterialCount() const {
            return 6;
        }
 
        virtual video::SMaterial& getMaterial(u32 i) {
            switch (i) {
                case 0: return front.getMaterial(0);
                case 1: return back.getMaterial(0);
                case 2: return left.getMaterial(0);
                case 3: return right.getMaterial(0);
                case 4: return top.getMaterial(0);
                case 5: return bottom.getMaterial(0);
            }
 
            throw std::range_error("Invalid material index");
        }
};
 
 
 
 
 
int main()
{
    // ask user for driver
    video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (driverType==video::EDT_COUNT)
        return 1;
 
    // create device
 
    IrrlichtDevice *device = createDevice(driverType,
            core::dimension2d<u32>(1600, 800), 32, false);
        
    if (device == 0)
        return 1; // could not create selected driver.
 
    // create engine and camera
 
    device->setWindowCaption(L"Custom Scene Node - Irrlicht Engine Demo");
 
    video::IVideoDriver* driver = device->getVideoDriver();
    scene::ISceneManager* smgr = device->getSceneManager();
 
//    smgr->addCameraSceneNode(0, core::vector3df(0,0,-400), core::vector3df(0,0,0));
//    smgr->addCameraSceneNodeMaya();
    smgr->addCameraSceneNodeFPS();
 
    video::ITexture* texture = driver->getTexture("../media/chross_gta2.png");
 
    std::random_device rd;
    std::mt19937 gen(rd());
 
    std::uniform_int_distribution<> row(0, GtaTextureIndex::ROWS - 1);
    std::uniform_int_distribution<> column(0, GtaTextureIndex::COLUMNS - 1);
 
    for (ssize_t x = -10; x < 10; ++x) {
        for (ssize_t y = -10; y < 10; ++y) {
            for (ssize_t z = 0; z < 3; ++z) {
 
    Cube *myNode = new Cube(smgr->getRootSceneNode(), smgr, 666,
        GtaTextureIndex(row(gen), column(gen)),
        GtaTextureIndex(row(gen), column(gen)),
        GtaTextureIndex(row(gen), column(gen)),
        GtaTextureIndex(row(gen), column(gen)),
        GtaTextureIndex(row(gen), column(gen)),
        GtaTextureIndex(row(gen), column(gen))
    );
    myNode->setMaterialTexture(0, texture);
    myNode->setPosition(core::vector3df(x * 40, y * 40, z * 40));
 
    scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(core::vector3df(0.8f, 0, 0.8f));
 
    if(anim && false) {
        myNode->addAnimator(anim);
 
        anim->drop();
        anim = 0;
    }
 
    myNode->drop();
    myNode = 0; // As I shouldn't refer to it again, ensure that I can't
 
            }
        }
    }
 
 
    u32 frames=0;
    while(device->run())
    {
        driver->beginScene(true, true, video::SColor(0,100,100,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;
        }
    }
 
    device->drop();
    
    return 0;
}
 
The texture I'm using is [3]


-- ooxi



[1] http://irrlicht.sourceforge.net/docu/example003.html
[2] http://packages.ubuntu.com/de/vivid/glmark2-es2
[3] http://subvein.chross.de/gts/chross_gta2.png
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: Irrlicht far too slow (most likely I'm too dumb)

Post by mongoose7 »

You individually render the six sides of each of 1200 cubes = 7200 draw calls. I don't know the gold standard for draw calls, but I would try to reduce the number significantly.
Kojack
Posts: 67
Joined: Sun Jan 20, 2008 2:39 am

Re: Irrlicht far too slow (most likely I'm too dumb)

Post by Kojack »

ooxi wrote:I'm only rendering 1200 cubes (each cube has 12 triangles thus 14400 triangles in total)
Actually it looks like double that. Each side of the cube is being rendered with 4 triangles (two front facing, two back facing), so there's 24 triangles per cube and effectively no back face culling happening (it's happening, but cancelled out by double the geometry)
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Irrlicht far too slow (most likely I'm too dumb)

Post by CuteAlien »

Aside from render-calls... getting same speed for OpenGL and Burnings means that hardware acceleration does not work. Check first if your driver has it enabled with:
glxinfo | grep direct
That should say: direct rendering: Yes

Also check if there is any console output about errors.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Irrlicht far too slow (most likely I'm too dumb)

Post by hendu »

The usual rule of thumb is aim for less than 1k draw calls for the entire scene.
ooxi
Posts: 3
Joined: Fri Sep 25, 2015 7:10 pm

Re: Irrlicht far too slow (most likely I'm too dumb)

Post by ooxi »

CuteAlien wrote:Aside from render-calls... getting same speed for OpenGL and Burnings means that hardware acceleration does not work. Check first if your driver has it enabled with:
glxinfo | grep direct
That should say: direct rendering: Yes

Also check if there is any console output about errors.
That was my first guess, too! But direct rendering is enabled and no errors regarding rendering are logged:
$ glxinfo | grep direct
direct rendering: Yes
 
$ ./game
Please select the driver you want:
 (a) OpenGL 1.x/2.x/3.x
 (b) Direct3D 9.0c
 (c) Direct3D 8.1
 (d) Burning's Video
 (e) Software Renderer
 (f) NullDriver
a
Irrlicht Engine version 1.8.1
Linux 3.19.0-15-generic #15-Ubuntu SMP Thu Apr 16 23:32:37 UTC 2015 x86_64
Using renderer: OpenGL 2.1
Mesa DRI Intel(R) G41 : Intel Open Source Technology Center
OpenGL driver version is 1.2 or better.
GLSL version: 1.2
Loaded texture: /game/media/chross_gta2.png
Quit message received.

But if I make far too many render calls, how can I reduce the amout? I tried to follow the tutorial as closely as possible
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Irrlicht far too slow (most likely I'm too dumb)

Post by hendu »

Intel G41? That's a really slow Intel card from 2008. Better use a low resolution, and light or no effects at all.

For reducing your calls, search this forum for batching.
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: Irrlicht far too slow (most likely I'm too dumb)

Post by chronologicaldot »

Before you go looking around for batching, I recommend first rewriting your class. It can be optimized. A few things you need to consider though:
1) How many draw calls are being made?
2) How many times am I reconstructing data?
3) Is the structure I'm using the right one for this job?

In answer to those questions...
1) You're making 6 draw calls. Others already noted this. If you condense everything into one class and perform a single draw. Ideally, you want a single drawVertexPrimitivesList() (and yes, you can draw a large mesh buffer with this call). If you keep all your vertices and faces together, you can do this. That brings up the next question's answer:
2) Don't repeatedly reconstruct data. You keep reconstructing your indices list. This is unnecessary. Do it once and save it. Especially since this list will become larger when you condense everything.
3) I've already sort of covered this, but the structure isn't really the right one. While it makes sense conceptually to separate cubes into spheres, this isn't CSS3. Putting things together is faster.

You can also do this as a mesh. Check out CGeometryCreator.cpp for how to create a cube mesh. It's not hard conceptually, but it's a good start for understanding irrlicht meshes.

Notably, while I believe this is set to default, you should also ensure the engine has backface culling on (unless you're drawing transparent objects).
Post Reply