c++ D3D 11 DXGI Present in another thread - window not responding

Started by
3 comments, last by Sawiner 11 years, 2 months ago

Hi guys.

I'm trying to make a simple game loop, where the

usual window message pump is in the initial thread,

while the directx rendering is at another thread.

The code is something like that:


// The next method runs at the initial thread
// it is called after DirectX and win32 initialization.
int Run()
{
	std::thread tRender(&ThreadProc_Render);
	if (tRender.joinable())
		tRender.detach();

	MSG msg;
	while (this->m_bRun)
	{
		BOOL bGetMsg(::GetMessage(&msg, NULL, 0, 0));


		if (bGetMsg == -1) // Error
		{
			this->m_bRun = false;
		}
		else if (bGetMsg == 0) // WM_QUIT
			this->m_bRun = false;
		else
		{
			// ::TranslateMessage(&msg);
			::DispatchMessage(&msg);
		}
	}

	return (msg.wParam);
}


ThreadProc_Render:


void ThreadProc_Render()
{
	StopWatch sw;

	// Render Loop
	while (pWnd->m_bRun)
	{
		uint32 nMS = sw.Restart();
		HRESULT hr(gpSwapChain->Present(0, 0));

		// ::Sleep(15);
		std::this_thread::yield();
	}
}

in case you're wondering, StopWatch is a class of mine. Restart()

sets the watch old time to now, and return the different between the old and the new time.

The problem is, the window it self is not responding (can't move or resize it)

It seems as if the other thread is taking all the GetMessage() thread resources.

However, as you can see, the function call to sleep with 15 ms is commented out.

when it's not commented out, the program runs fine.

Also, if I comment out the Present() call, same thing - window seem to "work" again

(can resize, move).

Also, when looking at task manager, the application doesn't seem to consume

high cpu at all.

As far as I can see, it looks like a blocking issue. The thing is, there is no blocking at all.

I even tried replacing GetMessage() wit PeekMessage(),

and calling 'yield' when there are no messages. That gave me the same result though,

window stuck unless Present() line is commented out (or Sleep isn't).

Why is this even happening? What am I missing here?

Advertisement
Your threads are both trying to access the screen, it looks like your render loop is faster so its calling present nonstop blocking the other thread from accessing it. My suggestion is to keep them in the same thread. I don't think you would benefit from using separate threads for this. Otherwise, you should look up mutexes or something, so the render function doesn't block the other thread from accessing the window. You could try creating a global boolean, setting it to true while the windows thread processes messages, then setting it to false after, and in your render thread, only present the screen when that global is false. But it would probably be more efficient and accurate if you just presented your screen in the same thread

By the way, this could leave bugs that are hard to catch and fix. The windows thread might try changing the window the same time as the render thread calls present, which could cause a noticeable glitch or even crash

iedoc - thanks for your reply.

I was wondering though, what makes the message loop lock the screen? I "watched" the messages,

and most of the time the messages the GetMessage() was stuck on were something like WM_NCACTIVATE

and WM_MOUSEFIRST - that is, messages that aren't related to paint.

Although, assuming the message pump does lock the screen, I agree with you - rendering from

another thread would be dangerous.

I have to say though, the documentation about DXGI and multi-threading make me thinks

as if what i'm trying to accomplish should be possible;

take a look here:

http://msdn.microsoft.com/en-us/library/windows/desktop/ee417025(v=vs.85).aspx#multithreading_and_dxgi

The multi-threading part there talks about deadlocks (which I have avoided, so far anyway).

That is, it implicity tells you it's ok to call Present() from another thread.

Few questions then:

1. so what am I missing here? how am I suppose to write a game that call Present() from another thread,

in a variable time step, without vsync (that is, fast as possible) ?

2. Assuming I do keep the Present() call in the UI thread, and does the rendering

in another thread, how do I know which parts Present() will draw and which not?

I know I can use synchronization primitives such as mutex, I am talking about a case

where I wouldn't use one, and wouldn't use D3D11_CREATE_DEVICE_SINGLETHREADED flag as well,

meaning I can call the methods from multiple threads.

p.s.

I also just found out if i'm calling Present(1, 0), it's working as well.

As far as I understood, this is vsync.

so as far as I can tell, the problem is when Present() need to actually draw(),

which happens a lot more without vsync (since vsync waits, if I understand correctly).

However, I do not wish to use vsync. I wrote this to hopefully, give some more helpful information.

Its not the message loop locking the screen actually, its the present call. When you call present(0, 0), no vsync, it will swap buffers and write the front buffer to the window as fast as possible. Since present needs to access the window, and your render loop is pretty much only the present call, the window has no time to process the windows messages, so basically the message queue gets backed up since none of the messages have time to get processed, and that's why you can't do anything with the screen, like closing or resizing or moving it, those are all windows messaged that have to wait for the window to process them, but the window is too busy processing the present call. I can't remember if the present call will be put into a message queue (probably not because that would cause the game to become really unresponsive I think) or if it just stalls the thread until it has access to the window. But either way, your render thread will call present so fast so the window just doesn't have time to do anything else but render the front buffer to the screen.


When you call present(1,0), vsync, the window will have a little time to process other window messages while the present call waits for the signal from the monitor or however vsync actually works.the point is you are giving a little time for the window to process other messages.


Remember directx needs a window handle to know which window to draw on. The windows message system sends messages to windows via handles, so if directx is using the window it has the handle to (present), then the messages sent by the windows OS (close, resize, move)will have yo wait to be processed by that window until directx lets go of it. Obviously (maybe not so obvious) directx and windows use some sort of internal mutex or locking system so when a window is being used, everything else that needs to access that window must wait, and we can't assume they wait in line, because if that was the case, then at least some of your windows messages would be processed between each time the window rendered the front buffer for directx. But we know none of the windows messages are being processed by the window, and only the present call being processed, which means the present call cuts in line and will be processed by the window before the messages in the windows message queue.


That last paragraph has a lot of assumptions though, so don't take it as correct without some research if you could even follow that, I could be wrong

iedoc - first of all, that makes a lot of sense. Thanks for that information.

That being said though, how is the render loop in another thread suppose to look like then?

Assuming i'm looking for a variable time step (so I can't base the loop on time), and no vsync.

I tried looking into getting the current monitor refresh rate, then limiting

the amount of draw calls to that, however as far as I can tell, there is no "secure"

(one that will always bring a result) method of doing such thing.

For example, EnumDisplaySettings() and DEVMODE structure ( http://msdn.microsoft.com/en-us/library/windows/desktop/dd183565(v=vs.85).aspx )

, say that if the value is 0, it uses the adapter default refresh rate. However, I can't seem to find a way to get

the adapter default refresh rate.

Also, is that a good idea? if not, how else should I do such thing?

This topic is closed to new replies.

Advertisement