![]() |
Multimedia »
GDI »
General
Intermediate
Tearing-free drawing with GDIBy wheregoneDrawing with VSYNC locked and low CPU usage |
VC6, VC7, VC7.1, Windows, Visual Studio, GDI, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||

This article demonstrates Tearing free drawing with low CPU usage and GDI window properties.
Tearing - A tear occurs when a page flip or blt happens at the wrong time. For example, if a page flips while the monitor scan line is in the middle of displaying a surface, as represented by the dashed line in the preceding figure, a tear occurs. The tear can be avoided by timing the flip to occur only after the entire surface has been displayed (as in the lower example of the figure). A tear can also occur when blitting to a surface that is in the process of being displayed. For more details, please read this page.
Is it possible to draw without tearing, but with GDI window characteristic, and low CPU usage? Let's see.
Generally, the display refresh rate could be from 60 to 120Hz. or 1/60 down to 1/120 second. DirectX function ::WaitForVerticalBlank is just to wait for the VSYNC interval, but it is by Polling. That is a little ridiculous. Since the windows system is not a real time operating system, that function consume a lot of CPU resource but sometimes it still misses VSYNC.
Here I use both multimedia Timer and DirectX function to track the vertical sync. The main point is starting a multimedia Timer with 2ms interval. In the multimedia Timer callback handler, use function ::GetScanLine to get the current scan line number, then judge whether it is the time to draw. I use cdx library to simplify using DirectX function ::GetScanLine, so that please link your code with cdxlib.
...
Screen = new CDXScreen(); // Start the instance
if (Screen==NULL)
{
return -2;
}
cdx_DD = Screen->GetDD();
HWND hOWin = GetForegroundWindow();
SetPriorityClass(
GetCurrentProcess(),
HIGH_PRIORITY_CLASS );
pTheWnd = new COsdWnd;
if(!pTheWnd->Initialize(hInstance))
return -3;
SetForegroundWindow(hOWin);
...
...
...
StartTimer(2, FALSE, 0); // Start mmTimer when entry.
...fall into messages dispatcher
StopTimer(); // Stop mmTimmer when exit.
/////////////////////////////////////////////////////////////////// // start mmTimer /////////////////////////////////////////////////////////////////// public: bool StartTimer(UINT period, bool oneShot, UINT resolution) { bool res = false; MMRESULT result; TIMECAPS tc; if (TIMERR_NOERROR == timeGetDevCaps(&tc,sizeof(TIMECAPS))) { m_timerRes = min(max(tc.wPeriodMin,resolution),tc.wPeriodMax); timeBeginPeriod(m_timerRes); } else return false; result = timeSetEvent( period, m_timerRes, mmTimerProc, 0, (oneShot ? TIME_ONESHOT : TIME_PERIODIC) ); // Kill with Sync? if (NULL != result) { m_timerId = (UINT)result; res = true; } return res; }
////////////////////////////////////////////////////////////////////// // stop mmTimer ////////////////////////////////////////////////////////////////////// public: bool StopTimer() { MMRESULT result; result = timeKillEvent(m_timerId); if (TIMERR_NOERROR == result) m_timerId = 0; if (0 != m_timerRes) { timeEndPeriod(m_timerRes); m_timerRes = 0; } return TIMERR_NOERROR == result; }
/////////////////////////////////////////////////////////////////////// // mmTimer CALLBACK handler // From the trace data, I think mmTimer Callback is driven // by Semaphore with counting. // And it won't overrun. In other words, the Callback // function won't be re-entried. /////////////////////////////////////////////////////////////////////// void CALLBACK mmTimerProc(UINT id,UINT msg,DWORD dwUser,DWORD dw1,DWORD dw2) { cdx_DD->GetScanLine( &gdwScanLine ); XYTrace(TraceError, _T("ScanLine = %d "), gdwScanLine ); if (pTheWnd->m_bOnScreenUp) { if (!gbUsed && (gdwScanLine > pTheWnd->m_ScreenHalfHeight) ) { pTheWnd->ForceFrame(); gbUsed = TRUE; } if (gdwScanLine < gdwScanLineLastTime) { gbUsed = FALSE; } gdwScanLineLastTime = gdwScanLine; } else { if ( gdwScanLine < gdwScanLineLastTime ) { pTheWnd->ForceFrame(); } gdwScanLineLastTime = gdwScanLine; } }And do not handle the
WM_PAINT message, real drawing codes goes like the following. /////////////////////////////////////////////////////////////////// // Force draw a new frame /////////////////////////////////////////////////////////////////// public: void ForceFrame() { PaintWithMemDC(m_hWnd); } //////////////////////////////////////////////////////////////////// // Paint with MemDC //////////////////////////////////////////////////////////////////// public: inline void PaintWithMemDC( HWND hWnd ) { if (m_MemDC == NULL) return; HDC hDC = GetDC(hWnd); if (! BitBlt( hDC, // handle to destination DC 0, // x-coord of destination upper-left corner 0, // y-coord of destination upper-left corner WIN_WIDTH, // width of destination rectangle WIN_HEIGHT, // height of destination rectangle m_MemDC, // handle to source DC m_ScrollCnt, // x-coordinate of source upper-left corner 0, // y-coordinate of source upper-left corner SRCCOPY // raster operation code )) { PostMessage(m_hWnd, WM_CLOSE, 0, 0); } m_ScrollCnt += m_ScrollSpeed; if (m_ScrollCnt > m_StrLen + WIN_WIDTH) { m_ScrollCnt = 0; StopTimer(); CreateMemDC(hWnd); // re create the new memDC SafeStartTimer(); } ReleaseDC(hWnd, hDC); } ////////////////////////////////////////////////////////////////////// // On Left Button Up , at the same time when windows position moved. ////////////////////////////////////////////////////////////////////// private: void OnLBtnUp( WINDOWPOS* lpwp) { int cy = GetSystemMetrics(SM_CYSCREEN); m_ScreenHalfHeight = cy/2; if ( (UINT)(lpwp->y + WIN_HEIGHT) < (m_ScreenHalfHeight) ) { m_bOnScreenUp = TRUE; gbUsed = FALSE; } else { m_bOnScreenUp = FALSE; } }
To run the demo, you may need Japanese Fonts. The demo effect is very good on Dell 8200 (P4 1.8G, JW2000, Ti200), flicker-free, tearing-free, low CPU usage and with GDI windows properties. Notice that the opaque mode has the lowest CPU usage because the copy bitblt function is the basic function of display card. This VSYNC detection method could be used in DirectX windows program, but it is only an idea by now.
History
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 3 Jul 2003 Editor: Chris Maunder |
Copyright 2003 by wheregone Everything else Copyright © CodeProject, 1999-2009 Web19 | Advertise on the Code Project |