Page 1 of 1

loading within another thread on Linux using POSIX

Posted: Sat Feb 09, 2008 10:14 am
by radubolovan
After many crashes and no correct functionality, I've did it! :)
But I had to patch Irrlicht engine with two functions (in CIrrDeviceLinux class):
in private area:

Code: Select all

        GLXWindow glxWin2;
        GLXContext Context2;
then in public area:

Code: Select all

        GLXContext const& createNewContext();;
        void releaseContext( GLXContext const& a_context );
in constructor:

Code: Select all

        glxWin2 = 0;
        Context2 = 0;
Then the functions:

Code: Select all

GLXContext const& CIrrDeviceLinux::createNewContext()
{
	if( Context2 != 0 )
	{
		GLXContext cntx;
		return cntx;
	}

	const int MAX_SAMPLES = 16;
	int visualAttrBuffer[] =
	{
		GLX_RENDER_TYPE, GLX_RGBA_BIT,
		GLX_RED_SIZE, 4,
		GLX_GREEN_SIZE, 4,
		GLX_BLUE_SIZE, 4,
		GLX_ALPHA_SIZE, 4,
		GLX_DEPTH_SIZE, 16,
		GLX_DOUBLEBUFFER, GL_TRUE,
		GLX_STENCIL_SIZE, 1,
		GLX_SAMPLE_BUFFERS_ARB, GL_TRUE,
		GLX_SAMPLES_ARB, MAX_SAMPLES,
		None
	};

	int nitems = 0;
	GLXFBConfig* configList = glXGetFBConfigs( display, screennr, &nitems );
	Context2 = glXCreateNewContext( display, *configList, GLX_RGBA_TYPE, Context, True );//create a new context with common resources with first one
	glxWin2 = glXCreateWindow( display, *configList, RootWindow( display, visual->screen ), NULL );//create a window
	glXMakeContextCurrent( display, glxWin2, glxWin2, Context2 );//making second context current for this thread in specific window

	return Context2;
}

void CIrrDeviceLinux::releaseContext( GLXContext const& a_context )
{
	if( Context2 != a_context )
		return;

	glXDestroyContext( display, Context2 );
	glXDestroyWindow( display, glxWin2 );
}
Then in my app I have a:

Code: Select all

static bool m_bScreenshotEnable;
and the draw function:

Code: Select all

m_pIrrDriver->beginScene( true, true, SColor( 255, 0, 0, 0 ) );
if( DSIrrApp::m_bScreenshotEnable )
{
    drawLoadingScreen();//just a function for drawing something
}
else
{
    drawAll();//a function whitch draws all stuff
}
m_pIrrDriver->endScene();
And the threads stuff - a function:

Code: Select all

void* startLoadingThread( void *ptr )
{
	tThreadData* data = (tThreadData*)ptr;

	GLXContext context = ((CIrrDeviceLinux*)g_pIrrInterface->m_pIrrDevice)->createNewContext();

	DSIrrApp::m_bLoadingScreen = true;

	loadScene();

	DSIrrApp::m_bLoadingScreen = false;

	((CIrrDeviceLinux*)g_pIrrInterface->m_pIrrDevice)->releaseContext( context );

	dsfree( data->fileName );
	dsdelete( data );

	return NULL;
}
tThreadData is a structure:

Code: Select all

typedef struct _tThreadData
{
	_tThreadData()
	{
		pIrrDriver = NULL;
		fileName = NULL;
	}
	_tThreadData( IVideoDriver* pID, char* fN )
	{
		pIrrDriver = pID;
		fileName = fN;
	}

	IVideoDriver* pIrrDriver;
	char* fileName;
}tThreadData;
And a testing part:

Code: Select all

//somwhere in the code
tThreadData* data = dsnew tThreadData( g_pIrrInterface->m_pIrrDriver, ascii );
					int threadRet = pthread_create( &g_pLoadingThread, NULL, startLoadingThread, (void*)data );
g_pLoadingThread is just a:

Code: Select all

pthread_t g_pLoadingThread;
and of course:

Code: Select all

#include <pthread.h>
#include <unistd.h>
Enjoy! ;-)

Posted: Mon Feb 11, 2008 4:47 pm
by lester
Man, you certainly deserve a monument :)

Posted: Tue Feb 12, 2008 12:16 am
by BlindSide
Thats pretty awesome.

Re: loading within another thread on Linux using POSIX

Posted: Tue Feb 12, 2008 10:46 am
by rogerborg

Code: Select all

GLXContext const& CIrrDeviceLinux::createNewContext()
{
	if( Context2 != 0 )
	{
		GLXContext cntx;
		return cntx;
	}
You're returning a reference to a stack object. If your intention is to return an invalid reference, then you'd be better off returning a 0 pointer rather than a garbage reference.

Code: Select all

void CIrrDeviceLinux::releaseContext( GLXContext const& a_context )
{
	if( Context2 != a_context )
		return;
You're comparing Context2 against garbage, which is risky. A 0 pointer would be more explicit. And passing a const & is disingenuous, as you are destroying the object that it references, just via another reference.

Irrlicht is explicitly not thread safe, so I hope that your analysis involved more than just running this and seeing that it didn't crash under a success path.

Posted: Thu Feb 14, 2008 11:34 am
by radubolovan
@rogerborg
The ideea is to avoid creating a new context over an existing one and not to distroy an context which doesn't exist.
1) the checks should be done outside irrlicht eg.:

Code: Select all

GLXContext myContext = ((CIrrDeviceLinux*)driver->createNewContext();
if( myContext != 0 )
{
    //make something with context
}
else
{
    printf( "creating context error\n" );
    //TODO: free data initialized
    return;
}

//some code here and loading stuff

//and just:
((CIrrDeviceLinux*)driver->releaseContext( myContext );//try not to use myContext variable after this line of code ;-)
b)because both functions work with const&, this shouldn't be risky.
The ideea of & is not to create copies of memmory and verify the exact one created!!!
The ideea of const is to avoid writting in its memory.
2) Yes, maybe this is better:

Code: Select all

GLXContext const* CIrrDeviceLinux::createNewContext()
{
    /* this is ok because in constructor is: Context2 = 0;
       and after creation it is pozitive*/
   if( Context2 != 0 )
   {
      return NULL;
   }
   //creation of context here
   return &Context2;
}
void CIrrDeviceLinux::releaseContext( GLXContext* a_context )
{
   if( Context2 != *a_context )
      return;
   //destroy the context here
   a_context = NULL;
}
But anyway both versions work well, once again, the user must do the checks, not me.
And you have right about this:

Code: Select all

if( Context2 != 0 )
   {
      GLXContext cntx;
      return cntx;
   }
This is better:

Code: Select all

if( Context2 != 0 )
   {
      return (GLXContext)0;
   } 
Enjoy

Posted: Thu Feb 14, 2008 11:39 am
by radubolovan
Is there anyone who can port the code for other S.O. (eg. Windows)?
here: http://en.wikipedia.org/wiki/POSIX it sais that POSIX works on different platforms, but I don't know how to create a window and a OpenGL context in Windows :(

Posted: Thu Feb 14, 2008 2:14 pm
by lester
radubolovan

Forget about it. POSIX on Win named Xenix is dead officially.

Posted: Mon Feb 25, 2008 5:09 pm
by vvv
lester wrote:radubolovan

Forget about it. POSIX on Win named Xenix is dead officially.
You are completely wrong.

1. According to POSIX THREADS in windows: http://sourceware.org/pthreads-win32/ - so code example is useful for windows too (I use pthreads in my project)

2. According to Xenix you are wrong too. Check this out:
http://technet.microsoft.com/en-us/inte ... 80242.aspx

It's native POSIX layer for Win32 from Microsoft.

irrlicht can be multithreaded with this patch ?

Posted: Tue Feb 26, 2008 9:16 pm
by muenalan
I just came across fences when a have a separate irrlicht render thread. The window was not painting any more if the while(-run()) loop is in another thread (boost thread).

Does this mean irrlicht was not capable of these things, *BUT* this modification shall fix it ?

Posted: Thu May 29, 2008 10:38 pm
by radubolovan
I've found if the window doesn't recieve an event (from mouse or keyboard), the next line was not executed:

Code: Select all

Context2 = glXCreateNewContext( display, *configList, GLX_RGBA_TYPE, Context, True );
so the next code should resolve this problem;

Code: Select all

GLXContext const& CIrrDeviceLinux::createNewContext()
{
	if( Context2 != 0 )
	{
		GLXContext cntx;
		return cntx;
	}

	const int MAX_SAMPLES = 16;
	int visualAttrBuffer[] =
	{
		GLX_RGBA, GL_TRUE,
		GLX_RED_SIZE, 4,
		GLX_GREEN_SIZE, 4,
		GLX_BLUE_SIZE, 4,
		GLX_ALPHA_SIZE, 4,
		GLX_DEPTH_SIZE, 16,
		GLX_DOUBLEBUFFER, GL_TRUE,
		GLX_STENCIL_SIZE, 1,
		None
	};

	int nitems = 0;
	GLXFBConfig* configList = glXGetFBConfigs( display, screennr, &nitems );

	XEvent event;
	XPutBackEvent( display, &event );

	Context2 = glXCreateNewContext( display, *configList, GLX_RGBA_TYPE, Context, True );
	glxWin2 = glXCreateWindow( display, *configList, RootWindow( display, visual->screen ), NULL );
	glXMakeContextCurrent( display, glxWin2, glxWin2, Context2 );

	return Context2;
}

Posted: Fri May 30, 2008 10:16 am
by radubolovan
This is still happening :(
The problem is that the window doesn't recieve an event ... I will see if can be resolved... maybe I should define the event.
Keep in touch.

Posted: Fri May 30, 2008 7:43 pm
by radubolovan
I've founnd that this problem is occurred when the wndow lose focus ;) so ...
in .h:

Code: Select all

bool m_bTryToCreateNewContext;
in .cpp:
in constructor:

Code: Select all

m_bTryToCreateNewContext = false;
in createNewContext() function:

Code: Select all

if( Context2 != 0 )
	{
		GLXContext cntx;
		return cntx;
	}

	m_bTryToCreateNewContext = true;

	const int MAX_SAMPLES = 16;
	int visualAttrBuffer[] =
	{
		GLX_RGBA, GL_TRUE,
		GLX_RED_SIZE, 4,
		GLX_GREEN_SIZE, 4,
		GLX_BLUE_SIZE, 4,
		GLX_ALPHA_SIZE, 4,
		GLX_DEPTH_SIZE, 16,
		GLX_DOUBLEBUFFER, GL_TRUE,
		GLX_STENCIL_SIZE, 1,
		None
	};

	int nitems = 0;
	GLXFBConfig* configList = glXGetFBConfigs( display, screennr, &nitems );
	Context2 = glXCreateNewContext( display, *configList, GLX_RGBA_TYPE, Context, True );
	glxWin2 = glXCreateWindow( display, *configList, RootWindow( display, visual->screen ), NULL );
	glXMakeContextCurrent( display, glxWin2, glxWin2, Context2 );

	return Context2;
in run() function at the beginning:

Code: Select all

os::Timer::tick();

	if( m_bTryToCreateNewContext )
	{
		WindowActive = true;
	}
and finaly in the releaseContext() function

Code: Select all

if( Context2 != a_context )
		return;

	glXDestroyContext( display, Context2 );
	glXDestroyWindow( display, glxWin2 );
	m_bTryToCreateNewContext = false;
Now it's all ok! :D