Problem concerning update frame periodically

Started by
4 comments, last by 991060 19 years, 6 months ago
Hi all: What I want to do is to update the frame every short interval (like 0.1s, that'll gimme 100FPS) while keeping the cpu usage low. I know how to satisfy the first goal, but have no idea on how to satisfy the senond at the same time. I'm using MFC, and someone told me I can override the CWinApp::Run() method to control how frequently my main rendering procedure is called, this is how I did it:

int H3DEffectApp::Run()
{
	// from CWinApp::Run(), SRC can be found in \Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc\appcore.cpp
	if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
	{
		// Not launched /Embedding or /Automation, but has no main window!
		TRACE(traceAppMsg, 0, "Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");
		AfxPostQuitMessage(0);
	}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	// from CWinThread::Run(), SRC can be found in  \Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc\thrdcore.cpp
	
	ASSERT_VALID(this);
	_AFX_THREAD_STATE* pState = AfxGetThreadState();

	// for tracking the idle time state
	BOOL bIdle = TRUE;
	LONG lIdleCount = 0;
	
	for (;;)
	{
		static DWORD lastTime = 0;
		DWORD currTime = timeGetTime();
		if((currTime - lastTime)>10)
		{
			PostMessage(renderWindowH, WM_PAINT, 0, 0);
			lastTime = currTime;
		}

		// phase1: check to see if we can do idle work
		while (bIdle && !::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
		{
			if (!OnIdle(lIdleCount++)) // call OnIdle while in bIdle state
				bIdle = FALSE; // assume "no idle" state
		}

		// phase2: pump messages while available
		do
		{
			// pump message, but quit on WM_QUIT
			if (!PumpMessage())
				return ExitInstance();

			// reset "no idle" state after pumping "normal" message
			//if (IsIdleMessage(&m_msgCur))
			if (IsIdleMessage(&(pState->m_msgCur)))
			{
				bIdle = TRUE;
				lIdleCount = 0;
			}

		} 
		while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
	}
}



most of the code is borrowed from MFC's installed cpp, I just added the following:

static DWORD lastTime = 0;
		DWORD currTime = timeGetTime();
		if((currTime - lastTime)>10)
		{
			PostMessage(renderWindowH, WM_PAINT, 0, 0);
			lastTime = currTime;
		}



renderWindowH is the HWND of the window of which I want to re-render every 0.1s. The problem is that the rendering only happens when there's some windows message generated, such as mouse is moved, window is resized, etc. But if I remove the "if" statement, just use "PostMessage(renderWindowH, WM_PAINT, 0, 0);" instead, the window is re-rendered correctly(just very fast, I have no control on it), and the cpu usage is nearly 100%, as expected. Now can someone please tell me why the "if" test stops window from being re-rendered? Moreover, if anyone has better method to solve my problem from another perspective, I'd like to hear about it, thanks for reading.
The sweet of life sucks.
Advertisement
Sending 100 WM_PAINT's every second is not a good idea, if you want to update the screen then simply do so. Just call the draw function from the mainloop. Don't tell windows to tell you to draw only so you can pick up the action later & do so, that's just silly. I'm not familiar with mfc, but if you are making a game then (or something that updates very reguarily like that) then the best way to do it is to use a main loop, not run off of the winproc. Create a min frame function & have all your drawing actions etc in there. Each loop in the main function check the timing to see if the desired frame delay has passes & if so run your main function directly from there. Don't bomb down the system.
I don't know how well windows would cope with that many messages (probally fine), but either way its a very awkward way of doing what otherwise should be a simple task.
I hope I explained this well & I apologize for not directly answering your question
_______________________________ ________ _____ ___ __ _`By offloading cognitive load to the computer, programmers are able to design more elegant systems' - Unununium OS regarding Python
First of all, you should override OnIdle rather than Run. That is what OnIdle is there for and will fix your immediate problem.

Your bigger problem is that Win32 and MFC are not designed to run at a frame rate of 100 fps while keeping CPU usage as low as possible. Normally, you would call SetTimer to set up a timer that calls a callback function 100 times per second. The callback function would invalidate the window, generating a WM_PAINT message and causing a frame to be rendered. The rest of the time, the program is sitting idle waiting for messages.

However, there is a maximum rate, which is about 60 times per second (and might vary depending on the OS). That means that if you want to run at 100 fps, you can't use the timer callbacks and you have to poll a timer constantly.

If you don't want your program to hog CPU resources, then you can try polling a timer and calling Sleep to relinquish time until it is time to render the next frame. That will probably get you very close to what you are trying to do. Again, your results may vary depending on the OS.

John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:Original post by ProPuke
Sending 100 WM_PAINT's every second is not a good idea

Why not? It doesn't seem to cause any problems in Win XP.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:Original post by JohnBolton
Quote:Original post by ProPuke
Sending 100 WM_PAINT's every second is not a good idea

Why not? It doesn't seem to cause any problems in Win XP.


It's the fact that it's wasteful. There's no point. You're telling Windows to tell you to paint. Why not just freakin paint?

WM_PAINT exists so that the Windows GDI subsystem can notify you when it's hijacked some of your screen space, and so that you can have a chance to reclaim it.
daerid@gmail.com
Well, sending WM_PAINT is just a quick implementation, it's not that slow, on my system, it can render as high as 1000 frames a second, if I don't control the message generating rate.
I also tried OnIdle, that was my first try actually. As I said, I can satisfy the first goal, but couldn't keep cpu usage low at the same time. Because by using OnIdle, I'd have to call OnIdle very frequently to check the timing information, that's killing the cpu. SetTimer might be a good choice, I just thought it's not accurate enough, I think I need to rethink about it for a while.
To make it clear, I'm not making a game, it's just a windows program which means running the main loop and checking timing info in it is not acceptable, otherwise I would do it in the first place, it obvious involves much less work than now.
The sweet of life sucks.

This topic is closed to new replies.

Advertisement