FPS frustrations
Assuming D3D9 (please confirm), are you doing vsync with D3DPRESENT_INTERVAL_DEFAULT or with D3DPRESENT_INTERVAL_ONE? If the former, it will use a low-resolution timer for syncing, which may explain what you're seeing - if so, try switching to the latter and see what happens.
Yes I'm pretty sure its D3D9 - the most recent DirectX that supports C#.
I'll have to check my code tonight - thanks for the tip, I'll report back.
In my past I've dabbled with xna which is c# and d3d9 based and this is an issue. You have to carefully plan your variables and scope to ensure that gc doesn't kick in when you don't want it to at unexpected times in the game loop...
I'm very familiar with garbage collection in C# and I don't believe thats my issue. I've designed scopes specifically to minimize garbage collection, and besides, my profiler would show any GC time that occurs.
It was a good suggestion though, one of the first things I thought of too.
Edit:
I guess there is one place that I dont have visibility - the call to device.present(). This is the black box that takes all the time to wait for VSYNC + render. If garbage collection is happening within this timeframe, I'm not sure what that would look like. I suppose its possible that garbage collection is happening, and only happening during that function call, and also taking more than 16 milliseconds, and occurring for dozens / hundreds of frames in a row, but only while that function is being invoked and not ouside of the function... but really none of that makes any sense to me. And besides, I just dont have that much garbage to clean up.
Doesn't "DEFAULT" mean vsync enabled and "ONE" is vsync disabled?
For D3D9:
D3DPRESENT_INTERVAL_DEFAULT means vsync is enabled but uses the default low-res system timer.
D3DPRESENT_INTERVAL_ONE means vsync is enabled but uses timeBeginPeriod for a higher res timer.
D3DPRESENT_INTERVAL_IMMEDIATE means vsync is disabled.
For D3D10+ vsync interval is a parameter to the Present call. I assume that the OP isn't using D3D10+ because he mentions "device.Present"; on 10+ Present is a method of the swap chain.
For D3D8- things get weird, but let's hope that the OP isn't on one of those versions.
Ok So I checked and I was in fact using PresentInterval.Default. I changed it to PresentInterval.One and tested it out for a while. Unfortunately the issue still happens, I don't see any difference. I guess that rules one thing out.
I use a Sleep(0) in my projects, to give the scheduler time for other tasks if needed, I've never seen it affect the framerate negatively, but it makes my Windows more responsive so to speak: by doing it you allow Windows to allot time for other things.. If you don't do it, and your project runs a very fast loop you will hog CPU time..
Also, my engine is using a high res timer when not running in vsync, which skips the update() and render() parts if 1/60 seconds has not passed since last update, and Sleep(0) ensures that the "idle period" in my engine doesn't hog too much juice (since when not updating and rendering it will run in a very short loop (and very fast), checking for next time slot plus windows messages). This works very well for me!
Maybe you could try some of these things, and see if it helps or makes it worse?
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686298%28v=vs.85%29.aspx