[Solved]Bad performance with custom scene node

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
MartinVee
Posts: 139
Joined: Tue Aug 02, 2016 3:38 pm
Location: Québec, Canada

[Solved]Bad performance with custom scene node

Post by MartinVee »

I'm trying to create a custom scene node object to tailor it to our specific needs and I noticed that I was getting subpar performance. I still consider myself a beginner in 3D development, so I thought it was some error on my part.

Skip ahead a few debugging sessions...

As a test, I created 1,000 Billboard Scene Node ; I get around 450 fps. I copied the Irrlicht's CBillboardSceneNode files to my project, renamed the class to CBillboardSceneNode2, and tried to create a thousand instances of that class ; I get around 140 fps.

There has to be something going on. Why am I getting these kind of poor performance if I'm using the exact same class as the engine?

EDIT :

Here's my code, in case it's relevant. As I said, CBillboardSceneNode2 is an exact copy of Irrlicht's CBillboardSceneNode.cpp/.h with the class renamed to CBillboardSceneNode2 (see line 75).

Code: Select all

 
#include <windows.h>
#include <irrlicht.h>
#include "CBillboardSceneNode2.h"
 
 
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
 
#ifdef _IRR_WINDOWS_
  #pragma comment(lib, "Irrlicht.lib")
  #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
 
#define NB_STRESS_TEST (1000)
 
#define SCREEN_WIDTH (1024)
#define SCREEN_HEIGHT (768)
 
 
void SetWindowCaptionString(core::stringw* strCaption, s32 FPS, const core::stringw &strDriverName)
{
  *strCaption = L"Irrlicht Stress Test Program -  [";
  *strCaption += strDriverName;
  *strCaption += "] FPS:";
  *strCaption += FPS;
}
 
int main(int argc, const char* argv[])
{
  IrrlichtDevice *device = createDevice(video::EDT_DIRECT3D9, dimension2d<u32>(SCREEN_WIDTH, SCREEN_HEIGHT), 16, false, false, false, eventReceiver);
  int iFPS = 0, iLastFPS = 0;
  core::stringw strWindowCaption;
 
  if(device)
  {
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    IGUIEnvironment* guienv = device->getGUIEnvironment();
 
    // Declare a screensize object using the values from the driver
    const core::dimension2d<u32> screenSize = driver->getScreenSize();
 
    // Get the camera node
    ICameraSceneNode *cam = (scene::ICameraSceneNode *)smgr->getSceneNodeFromType(scene::ESNT_CAMERA);
 
    // If the camera doesn't exists (which is the case here, because the program just started)
    if(cam == NULL)
    {
      // Add a camera node with the top-left corner pointing at (0,0,0), and a field of view of 100 units, which will be used as a percentage to properly align the 2D pixels on screen.
      cam = device->getSceneManager()->addCameraSceneNode(0, irr::core::vector3df((f32)screenSize.Width/2, -((f32)(screenSize.Height/2)), -1), irr::core::vector3df((f32)screenSize.Width / 2, -((f32)(screenSize.Height / 2)), 100));
    }
 
    // Create a projection matrix to translate a 3D view to the exact screen size, and an orthographic field of view of 1000 units.
    matrix4 projectionMatrix;
    projectionMatrix.buildProjectionMatrixOrthoLH((f32)screenSize.Width, (f32)screenSize.Height, 0.0f, 1000.0f);
 
    // Apply the projection matrix to the camera.
    cam->setProjectionMatrix(projectionMatrix, true);
 
    // The world and the view needs to be reset to the identity matrix, in order to remove any matrix transformation for field of view.
    driver->setTransform(video::ETS_WORLD, core::matrix4());
    driver->setTransform(video::ETS_VIEW, core::matrix4());
 
    video::ITexture* testTexture = driver->getTexture("./Resources/Test.png");
 
    ISceneNode* parentNode = smgr->addEmptySceneNode(0);
    ISceneNode* testNodes[NB_STRESS_TEST];
 
    for(int iNode = 0; iNode < NB_STRESS_TEST; iNode++)
    {
//      testNodes[iNode] = new CBillboardSceneNode2(parentNode, smgr, 0, core::vector3df(((int)testTexture->getSize().Width / 2), 0 - ((int)testTexture->getSize().Height / 2), 0), core::dimension2d<f32>((f32)testTexture->getSize().Width, (f32)testTexture->getSize().Height));
      testNodes[iNode] = smgr->addBillboardSceneNode(parentNode, core::dimension2d<f32>((f32)testTexture->getSize().Width, (f32)testTexture->getSize().Height));
      testNodes[iNode]->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL);
      testNodes[iNode]->setMaterialTexture(0, testTexture);
      testNodes[iNode]->getMaterial(0).AntiAliasing = video::EAAM_FULL_BASIC;
      testNodes[iNode]->getMaterial(0).TextureLayer[0].BilinearFilter = false;
 
      testNodes[iNode]->setMaterialFlag(video::EMF_LIGHTING, false);
      testNodes[iNode]->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, false);
      testNodes[iNode]->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
      testNodes[iNode]->setMaterialFlag(video::EMF_USE_MIP_MAPS, false);
    }
    parentNode->setVisible(true);
 
    while(device->run())
    {
      /********/
      /* DRAW */
      /********/
 
      driver->beginScene(true, true, SColor(255, 0, 0, 0));
 
      driver->enableMaterial2D(true);
      for(int i = 0; i < NB_STRESS_TEST; i++)
      {
        int X = (rand() % (screenSize.Width - testTexture->getSize().Width));
        int Y = 0 - (rand() % (screenSize.Height - testTexture->getSize().Height));
        testNodes[i]->setPosition(core::vector3df(X + ((int)testTexture->getSize().Width / 2), Y - ((int)testTexture->getSize().Height / 2), 0));
      }
      driver->enableMaterial2D(false);
 
      smgr->drawAll();
      guienv->drawAll();
 
      driver->endScene();
 
      /******************/
      /* WINDOW CAPTION */
      /******************/
 
      // Get the FPS and build the window caption with it.
      iFPS = driver->getFPS();
      if(iLastFPS != iFPS)
      {
        SetWindowCaptionString(&strWindowCaption, iFPS, driver->getName());
        device->setWindowCaption(strWindowCaption.c_str());
        iLastFPS = iFPS;
      }
    }
  }
 
  device->drop();
 
  return 0;
}
 
Last edited by MartinVee on Thu Aug 25, 2016 3:40 pm, edited 1 time in total.
MartinVee
Posts: 139
Joined: Tue Aug 02, 2016 3:38 pm
Location: Québec, Canada

Re: Bad performance with custom scene node

Post by MartinVee »

Solved, and I'm completely ashamed to admit it. :oops:

I'm using Irrlicht Release library ; my project was compiled as Debug.

Moral of the story : when testing for performance, always make sure you're building a release version.
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: [Solved]Bad performance with custom scene node

Post by Mel »

There are other ways to improve performance. For instance, try to create every billboard on a single meshbuffer, that way should really improve your performance, as the billboards are updated ourside the videocard one by one, uploading as many as you need in a single mesh may better your performance even more. It is not as crazy as it sounds to have 15000 billboards like that :)
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
MartinVee
Posts: 139
Joined: Tue Aug 02, 2016 3:38 pm
Location: Québec, Canada

Re: [Solved]Bad performance with custom scene node

Post by MartinVee »

Mel, it seems like a very good idea! Do you have a sample code from which I could start digging into this?
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: [Solved]Bad performance with custom scene node

Post by Mel »

Take a look at the particle systems doParticleSystem() method, they are built on the fly. You need to update the billboards on every frame, so, eventhough you create the index buffer once, the vertex buffer needs to be updated constantly
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
Post Reply