Multithread error with textures

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
oleo
Posts: 27
Joined: Fri Dec 16, 2005 9:50 am

Multithread error with textures

Post by oleo »

Hi!
I'm developing a 3D Viewer with IRRLicht. The scene must be manipulated by other threads, running without blocking rendering. I've red this topic but I haven't understood if it's possible to use multithreads programming with irrlicht.
By now I've seen one problem... this istruction

Code: Select all

driver->getTexture("wall.bmp");
executed outside the viewer threads, produces a segmentation fault. Moreover I've discovered that if you run the same code inside the viewer thread before running that outside no segmentation fault is returned... Why? Is it a bug?
Thanks for help,
Fabio.
hybrid

Post by hybrid »

I did not check the code, yet, but getTexture should be basically thread-safe. It's more likely that your second thread executes in a different working directory such that it cannot access the texture.
oleo
Posts: 27
Joined: Fri Dec 16, 2005 9:50 am

Post by oleo »

The second thread executes in the same directory. The previous code returns segmentation fault also with the file full path like

Code: Select all

driver->getTexture("/home/fabio/threads1/debug/wall.bmp");
hybrid

Post by hybrid »

Compile your Irrlicht version with flag -g, then set 'ulimit -c unlimited' and debug with 'gsb ./yourapp core'. You should get much more detailed informations on where exactly you app crashes. It could be related to the file system. Are you really sure that all your pointers are properly initialized (can also be checked with gdb). Check that all information is accessible within the second thread.
oleo
Posts: 27
Joined: Fri Dec 16, 2005 9:50 am

Post by oleo »

Ok, now I'm going to try... but how can you explain that if I load the texture before in my viewer thread I've no segmentation fault?
Thanks a lot for your answer!!! :)
Fabio.
hybrid

Post by hybrid »

In the command lines above it should be gdb not gsb :!: Sorry!
when debugging you should use 'bt' to show the stack trace and get the methods called just before crashing. Also check all parameters. A 0x0 indicates uninitialized pointers (sometimes also just numbers which are very different to all other parameter pointers, e.g. all parameters starting with 0x80... but one is 0x40...).
Well, the segmentation fault can have several causes. Besides not properly initialized data (e.g. due to a missing copy constructor) it could also be a reader/writer clash. Inserting a texture does sort the texture array which could cause some trouble, also accessing the FileSystem handle might raise problems. If everything is in one thread you do not have these problems so everything is save. But trying to provide some simple multithread apps will definitely help to track down architectural glitches which might be fixed in the future to make Irrlicht more reliable and also allow a decent amount of multithreaded code in apps.
We are also using multiple threads in our project, but only to handle network I/O which accesses user data. When it comes to irrlicht structures everthing is done in the main thread.
oleo
Posts: 27
Joined: Fri Dec 16, 2005 9:50 am

Post by oleo »

hybrid wrote:Compile your Irrlicht version with flag -g, then set 'ulimit -c unlimited'
Where 'ulimit -c unlimited' should be set?
hybrid

Post by hybrid »

On the command line. It's a command which removes possible limits of the core size. Due to textures and graphic memory the cores threon by Irrlicht apps can be some hundred megabytes. Usual systems set core sizes to 0 or 10MB, so you have to remove the limit. Just execute the command as shown, then as a second command execute the debugger.
oleo
Posts: 27
Joined: Fri Dec 16, 2005 9:50 am

Post by oleo »

I'm using Gentoo and I've installed irrlicht with emerge... now I'm trying to recompile it with the g flag...
I follow your suggest skipping the first operation of recompiling irrlicht... this is the result:

Code: Select all

fabio2@lisa ~/Kdevelop_Projects/threads1/debug/src $ gdb ./threads1 core.26969

[CUT]

#0  0xb7b5cf4b in strtof_l () from /lib/libc.so.6
(gdb) bt
#0  0xb7b5cf4b in strtof_l () from /lib/libc.so.6
#1  0xb7b5a527 in __strtod_internal () from /lib/libc.so.6
#2  0xb7f5b59e in closestFit () from /usr/lib/libGLU.so.1
#3  0xb7f5e068 in gluBuild2DMipmaps () from /usr/lib/libGLU.so.1
#4  0x0807e7bd in irr::video::COpenGLTexture::copyTexture() ()
#5  0x0807e29b in irr::video::COpenGLTexture::COpenGLTexture(irr::video::IImage*, bool) ()
#6  0x08078e20 in irr::video::COpenGLDriver::createDeviceDependentTexture(irr::video::IImage*) ()
#7  0x08081744 in irr::video::CNullDriver::loadTextureFromFile(irr::io::IReadFile*) ()
#8  0x080815d3 in irr::video::CNullDriver::getTexture(char const*) ()
#9  0x0804bbcf in Viewer::SetTestScene() (this=0x8167f28) at /home/fabio/Kdevelop_Projects/threads1/src/Viewer.cpp:92
#10 0x0804b447 in main (argc=1, argv=0xbfedde04) at /home/fabio/Kdevelop_Projects/threads1/src/threads1.cpp:21
oleo
Posts: 27
Joined: Fri Dec 16, 2005 9:50 am

Post by oleo »

After recompiling with -g flag the result is the same of my previous post... :(
hybrid

Post by hybrid »

Yes, it idd already contain the symbols, so no changes are ok. However, the crash is somewhat strange. It's deep inside the GLU library and all parameters are checked such that no uninitialized data should be passed.
There are no pthread calls in the stack, so is this really threaded, and are all necessary things initialized?
oleo
Posts: 27
Joined: Fri Dec 16, 2005 9:50 am

Post by oleo »

The code is really threaded!!! I can do keyboard I/O (in particular "I") in the terminal window during the rendering of IRRLicht window...
I'm going to quickly explain my code:
  1. - the main program creates an object named "Viewer"
    - the "Viewer" is the second thread
    - the "Viewer" class creates a IRRLicht windows and has a public method "SetTestScene()" that should draw some test stuff like example 04.
    - the Viewer class is an implementation of a Thread class created by a friend of mine that has 2 public methods: start() and stop(). When start is called the code inside the "work()" protected method is executed.
Main

Code: Select all

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <iostream>
#include <cstdlib>
#include "Viewer.h"

using namespace std;

int main(int argc, char *argv[])
{
  Viewer* myViewer = new Viewer(); //Here starts the 2nd thread
  myViewer->SetTestScene();
  cout << "Hello, world!" << endl;
  return 0;
}

Viewer.h

Code: Select all

class Viewer : public Thread
  {
  public:
      Viewer();
      ~Viewer();
      void SetTestScene();
 
    protected:
      void work() throw();    //When the thread is start()ed this method runs...
                                     //It's like the thread "main()"...

    private:
      scene::ISceneManager* smgr;
      IrrlichtDevice* device;
      video::IVideoDriver* driver;
      ConditionVariable cond;
      bool cond_value;

      void Initialize();
  };


Viewer.cpp

Code: Select all

#include <stdio.h>
#include <wchar.h>

#include "Viewer.h"
#include "Thread.h"

#pragma comment(lib, "Irrlicht.lib")

using namespace irr;

Viewer::Viewer() : cond_value(false)
	{
	start();
	}

Viewer::~Viewer()
	{
	stop();
	join();
	}

void Viewer::Initialize()
{
		device = 0;
		device = createDevice(video::EDT_OPENGL, core::dimension2d<s32>(640, 480),16, false, false, false);
		driver = device->getVideoDriver();
		smgr = device->getSceneManager();

//Without these instructions I have the segm fault in the SetTestScene()
// 		video::ITexture* _text1 = driver->getTexture("wall.bmp");
// 		video::ITexture* _text2 = driver->getTexture("t351sml.jpg");
// 		video::ITexture* _text3 = driver->getTexture("sydney.bmp");

}

void Viewer::SetTestScene() //The same scene of example 04
{
 	cond.lock();
	cond.wait(cond_value);

 	video::ITexture* texture1 = driver->getTexture("wall.bmp");
 	video::ITexture* texture2 = driver->getTexture("t351sml.jpg");
 	video::ITexture* texture3 = driver->getTexture("sydney.bmp");

	scene::ISceneNode* node = 0;
	node = smgr->addTestSceneNode();
	node->setPosition(core::vector3df(0,0,30));
	node->setMaterialTexture(0, texture1);
	
	scene::ISceneNode* n = smgr->addTestSceneNode();
	n->setMaterialTexture(0, texture2);

	scene::ISceneNodeAnimator* anim = smgr->createFlyCircleAnimator(core::vector3df(0,0,30), 20.0f);
	n->addAnimator(anim);
	anim->drop();
	scene::IAnimatedMeshSceneNode* anms = smgr->addAnimatedMeshSceneNode(smgr->getMesh("sydney.md2"));

	if (n)
	{
		anim = smgr->createFlyStraightAnimator(core::vector3df(100,0,60), 
			core::vector3df(-100,0,60), 10000, true);
		anms->addAnimator(anim);
		anim->drop();
		anms->setMaterialFlag(video::EMF_LIGHTING, false);
		anms->setFrameLoop(320, 360);
		anms->setAnimationSpeed(30);
		anms->setRotation(core::vector3df(0,180.0f,0));
		anms->setMaterialTexture(0, texture3);
	}
	cond.unlock();
}

void Viewer::work() throw()  //The main of second thread...
{
	Initialize();

	smgr->addCameraSceneNodeFPS(0, 100.0f, 100.0f);
	device->getCursorControl()->setVisible(false);
	
	while(device->run())
	{
		cond.lock();
			driver->beginScene(true, true, Video::SColor(255,113,113,133));
			smgr->drawAll();
			driver->endScene();
		cond_value = true;
		cond.signal();
		cond.unlock();
	}
	device->drop();
}
Thread.h

Code: Select all

#ifndef Thread_h
#define Thread_h

#include <pthread.h>

class Thread
{
public:
  virtual ~Thread() {}
  
  void start();
  void stop();
  void join();
       
protected:
  virtual void work() throw() = 0;
   
private:  
  pthread_t threadId;
  static void* threadHandler(void* arg);
};

 
class ConditionVariable
  {
  public:
    ConditionVariable() { pthread_mutex_init(&mtx,NULL); pthread_cond_init(&cond,NULL); }
    ~ConditionVariable() { pthread_cond_destroy(&cond); pthread_mutex_destroy(&mtx); }
    
    void lock()   { pthread_mutex_lock(&mtx); }
    
    void unlock() { pthread_mutex_unlock(&mtx); }
    
    void signal() { pthread_cond_signal(&cond); }
    
    void wait( const volatile bool& condition );  
    
  private:
    pthread_mutex_t mtx;
    pthread_cond_t cond;
   };  
#endif // Thread_h
Thread.cpp

Code: Select all

#include "Thread.h"

void Thread::start()
    {
    pthread_create(&threadId, NULL, &Thread::threadHandler, this);
    }
    
void Thread::stop()
    {
    pthread_cancel(threadId);
    }

void Thread::join()
    {
    pthread_join(threadId, NULL);
    }


void* Thread::threadHandler(void* arg)
{
  Thread* t = static_cast<Thread*>(arg);
   
  t->work();
  
  return NULL;
} 


void ConditionVariable::wait( const volatile bool& condition )   
 {
 while ( !condition )
   pthread_cond_wait( &cond, &mtx );
 }
hybrid

Post by hybrid »

Hmm, maybe it's a race condition where one of the threads executes before Initialize() has completed. I think you should put the contents of that method into the constructor to have everything properly set up before the threading starts.
BTW: Due to your large lock regions you have completely serialized all of your threads. You won't win anything from this setup (except for the integration of threading techniques).
oleo
Posts: 27
Joined: Fri Dec 16, 2005 9:50 am

Post by oleo »

Probably this kind of sw architecture is wrong... Now I have a new idea to let more thread communicate with my viewer...
There will be a data blackboard where all threads write things to be displayed and where my viewer will read that things...
In this way all IRRLicht functions will be done by Viewer thread.
Thanks for your help!!!
Fabio.
Post Reply