Mulithreading and Render Loop

Started by
8 comments, last by NotAYakk 17 years, 6 months ago
I’m not used to work with threads so I could sure need advice. I'm developing a graphical application, using C++ and DirectX. I got one part, the main rendering loop that is going to run every frame and another background loop which not need to be processed as often. The loops feed each other with data. I worked out following scheme for the processing: Shared data: Sysmem_Surface Sysmem_Texture FillData Main Rendering Loop: if( bRenderToSurface == true ) { Render to GPU_Surface Copy GPU_Surface to Sysmem_Surface Set bRenderToSurface = false } if( bUpdateGraphicCard == true ) { Update constant table with FillData Copy Sysmem_Texture to GPU_texture Set bUpdateGraphicCard = false } Ordinary render code… Background Loop: Set bRenderToSurface = true Wait for Sysmem_Surface to be filled Process algorithm (takes long time) Prepare FillData and Sysmem_Texture that should be written to graphic card Set bUpdateGraphicCard = true I was thinking of putting the background loop in some kind of thread structure with low priority. Is this a good setting, comments, critics. How should I implement the threads; with BackgroundWorker, managed thread or something else? How do I share variables between threads? How do I make the background thread wait for the main loop? Anything else that I should think of? Thank you for your time :) [Edited by - 51mon on October 6, 2006 2:22:02 PM]
Advertisement
I provided the thread with a title.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Quote:Original post by LessBread
I provided the thread with a title.


Thanks, It got messed up somehow
Have a class that encapsulates the communication between the Background Loop thread and the rest of the app.

It should have methods something like:
// RenderToSurface API:// Render code API:bool RenderToSurfaceRequested();RenderToSurfaceData* GetRenderToSurfaceData();void RenderToSurfaceDataDone(bool failed=false);// Background thread API:void RequestRenderToSurface(RenderToSurfaceData*);bool WaitForRenderToSurfaceData(); // note: CAN FAIL -- if the app shuts down, for example// UpdateGraphicCard API:// Render code API:bool UpdateGraphicCardRequested();UpdateGraphicCardData* GetUpdateGraphicCardData();void UpdateGraphicCardDataDone(bool failed=false);// Background thread API:void RequestGraphicCardUpdate(UpdateGraphicCardData*);bool WaitForUpdateGraphicCardData();


If you want to get fancy, these methods can be written once and templated. But that is more complex than you need the first time you write it.

The class contains:
A "UpdateGraphicCardData*" and a "RenderToSurfaceData*" -- both are typically NULL, and only non-null when the WorkerThread places a request.

Two Mutexs, one for GraphicCard and one for RenderToSurface -- each mutex gets locked when an appropriate Request is put in, and unlocked when the appropriate Done functions are called.

The WaitFor functions block on the Mutex, but they release it immediately.

The pattern of use looks like:
// render thread:if (the_object->MessageRequested()) {  MessageData* data = the_object->GetMessageData();  do_the_funkey_data_processing(data);  the_object->MessageDone();}// background thread:MessageData* data = &whateverthe_object->RequestData(data);// maybe do some other thingsthe_object->WaitForData();


Simple multi-threaded message passing.
Thanks NotAYakk I will check that up.

Anyway I was playing around a bit with managed threads, but still in C++. Here is an example from msdn:
using namespace System;using namespace System::Threading;public ref class ThreadWork{public:   static void DoWork()   {      for ( int i = 0; i < 3; i++ )      {         Console::WriteLine( "Working thread..." );         Thread::Sleep( 100 );      }   }};int main(){   ThreadStart^ myThreadDelegate = gcnew ThreadStart( &ThreadWork::DoWork );   Thread^ myThread = gcnew Thread( myThreadDelegate );   myThread->Start();   for ( int i = 0; i < 3; i++ )   {      Console::WriteLine( "In main." );      Thread::Sleep( 100 );   }}


I got compilation error complaining about it can't find "namespace System" and the compiler didn't seam to understand much about the code. Do I have to include some special file or change some setting? I tried to link to mscorlib.dll, but I don't know if this is right?
Managed C++ is a bastard hybrid of C++ and C#, just so you know. For the most part, people only program in managed C++ when they want to live on the border of a C++ project and a C# project.

Are you working on a C# project, a C++ project, or living on the border between the two? Or something else?
Quote:Original post by NotAYakk
Managed C++ is a bastard hybrid of C++ and C#, just so you know. For the most part, people only program in managed C++ when they want to live on the border of a C++ project and a C# project.

Are you working on a C# project, a C++ project, or living on the border between the two? Or something else?


I have an ordinary C++ DirectX application based on the DirectX template, mostly function oriented. I looked through the documentation about managed C++ threads and thought it looked nice, but maybe it’s just a bunch of problems?
OK I made it like this
HANDLE hScatteringLightThread;DWORD dwScatteringLightId;// Shared variables...DWORD WINAPI ScatteringLightProc( LPVOID lpParam ) {	while(1)	{		HRESULT hr;		bRenderToSurface = true;		//SuspendThread(hScatteringLightThread);		Sleep(INFINITE);		// A lot of calculations...		bUpdateGraphicCard = true;	}	return 0;}HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext ){	// Code...	hScatteringLightThread = CreateThread( NULL, 0, ScatteringLightProc, NULL, 0, &dwScatteringLightId);	// Code...}void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext ){	if(bRenderToSurface)	{		// Render to surface...		bRenderToSurface = false;		ResumeThread(hScatteringLightThread);	}	if(bUpdateGraphicCard)	{			// Update graphic card...		bUpdateGraphicCard = false;	}	// Rest of the rendering loop...}void CALLBACK OnDestroyDevice( void* pUserContext ){	DWORD dwExitCode;	GetExitCodeThread(hScatteringLightThread,&dwExitCode);	TerminateThread(hScatteringLightThread,dwExitCode);	CloseHandle(hScatteringLightThread);	// Code...}


But I got errors, when using Sleep(INFINITE); the thread never wakes up and SuspendThread(hScatteringLightThread); work a little bit but is highly errorus. Anybody that could see what I'm doing wrong, the code worked fine when it was in a single thread, although a bit slow. How do I make Sleep wake?

[Edited by - 51mon on October 7, 2006 9:29:54 AM]
Quote:Original post by 51mon
Thanks NotAYakk I will check that up.

Anyway I was playing around a bit with managed threads, but still in C++. Here is an example from msdn:
*** Source Snippet Removed ***

I got compilation error complaining about it can't find "namespace System" and the compiler didn't seam to understand much about the code. Do I have to include some special file or change some setting? I tried to link to mscorlib.dll, but I don't know if this is right?



You need to have either created it as a C++ managed project, or enabled C++/CLI ( or Managed C++, whatever they are calling it these days ) in your project settings or Managed C++ code will not compile.
Do not use SuspendThread, ResumeThread or Sleep. Those are threading primitives of last resort. Don't use them unless you really know what you are doing -- and the fact that you reached for them for this problem is evidence that you don't know what you are doing.

...

Use EVENTs (automatic or manual), WaitForMultipleObjects or WaitForSingleObject, and SEMAPHOREs. I usually use the win32 API after it has been wrapped in a level or two of nice abstraction, so I don't know the win32 calls off by heart.

...

When you say Sleep(INFINITE) you are asking for the thread to go to sleep and never wake up -- why wouldn't it go to sleep and never wake up? :p

This topic is closed to new replies.

Advertisement