If you want, you can read how this got started here, http://www.ogre3d.org/forums/viewtopic.php?f=2&t=50360.
Story short: on an Athlon dual core runnings XP SP2 and with an nvidia 7950GT, I was experiencing severely laggy input from the mouse and keyboard. This was noticable at 50-60 FPS and terrible at 25-30 FPS. Since I'm working on a first person shooter, any control lag is unacceptable.
I eliminated various possible causes and pin-pointed it as some kind of GPU issue. As best I can tell, if the game loop calls renderOneFrame when the GPU command buffer is full there is a sudden huge drop in control responsiveness, even though the FPS gives no hint. Any time-wasting CPU delay (like Sleep(n) or a pointless while loop) that allows the GPU to catch up fixed the lag, but this was impossible to implement as a solution because it's impossible to always predict the correct amount of time-wasting needed.
I have found a fix to this problem, although only for DirectX so far. To get rid of the lag, it is necessary to flush the GPU command buffer. Since I don't know of any way to do this from inside Ogre normally, it requires modifying and recompiling the RenderSystem_Direct3D9.dll.
I doubt this solution is optimal. My understanding is that flushing the GPU command buffer is kind of extreme and hurts performance. I'm no expert on D3D either so perhaps there are other issues with this solution. Hopefully those more knowledgeable can offer some guidance here.
Anyhow, heres the fix.
In OgreD3D9RenderWindow.cpp, find SwapBuffers. Make the following change, which adds an extra function call and an extra function. Of course, add the new emptyGPUCommandBuffer() function to the headers as well.
Code: Select all
void D3D9RenderWindow::swapBuffers( bool waitForVSync )
{
// access device through driver
LPDIRECT3DDEVICE9 mpD3DDevice = mDriver->getD3DDevice();
if( mpD3DDevice )
{
HRESULT hr;
if (mIsSwapChain)
{
hr = mpSwapChain->Present(NULL, NULL, NULL, NULL, 0);
}
else
{
hr = mpD3DDevice->Present( NULL, NULL, 0, NULL );
}
if( D3DERR_DEVICELOST == hr )
{
SAFE_RELEASE(mpRenderSurface);
static_cast<D3D9RenderSystem*>(
Root::getSingleton().getRenderSystem())->_notifyDeviceLost();
}
else if( FAILED(hr) )
OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Error Presenting surfaces", "D3D9RenderWindow::swapBuffers" );
emptyGPUCommandBuffer() ;
}
}
// mkultra333, force GPU command buffer to empty.
void D3D9RenderWindow::emptyGPUCommandBuffer()
{
LPDIRECT3DDEVICE9 mpD3DDevice = mDriver->getD3DDevice();
if(mpD3DDevice)
{
IDirect3DQuery9* pEventQuery=NULL ;
mpD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery) ;
if(pEventQuery!=NULL)
{
pEventQuery->Issue(D3DISSUE_END) ;
while(S_FALSE == pEventQuery->GetData(NULL, 0, D3DGETDATA_FLUSH)) ;
}
}
}
And note, even if you've never got this bug yourself, it's possible that other people who use your app might. Something to keep in mind if you're writing a game you expect others with unknown hardware to play.
Edit: Added "if(pEventQuery!=NULL)" safety check.