using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; #if XNA_4 using DeviceEventHandler = System.EventHandler; #else using DeviceEventHandler = System.EventHandler; #endif namespace Nuclex.Graphics.SpecialEffects.Demo { /// /// Game component that tracks and displays the game's frame rate /// public class FpsComponent : GameComponent, IDrawable { /// Triggered when the component's drawing order changes public event DeviceEventHandler DrawOrderChanged { add { } remove { } } /// Triggered when the component's visible property changes public event DeviceEventHandler VisibleChanged { add { } remove { } } /// Initializes a new frame rate tracking component /// /// Graphics device service the counter uses for rendering /// public FpsComponent( IGraphicsDeviceService graphicsDeviceService ) : base(null) { this.graphicsDeviceService = graphicsDeviceService; this.frameTimes = new Queue(); Visible = true; } /// Immediately releases all resources used by the instance /// Whether the call was initiated by user code protected override void Dispose(bool calledByUser) { if(calledByUser) { if(this.spriteBatch != null) { this.spriteBatch.Dispose(); this.spriteBatch = null; } } } /// /// Allows the game component to perform any initialization it needs to /// before starting to run. This is where it can query for any required /// services and load content. /// public override void Initialize() { base.Initialize(); this.contentManager = new ContentManager( GraphicsDeviceServiceHelper.MakePrivateServiceProvider( this.graphicsDeviceService ), "Content" ); this.lucidaFont = this.contentManager.Load("Lucida"); this.spriteBatch = new SpriteBatch(this.graphicsDeviceService.GraphicsDevice); } /// Determines the drawing order of this component public int DrawOrder { get; private set; } /// Whether the component will draw itself public bool Visible { get; private set; } /// Current number of frames per second achieved public float Fps { get { return this.currentFps; } } /// Called when the game component should draw itself /// Provides a snapshot of timing values. public void Draw(GameTime gameTime) { updateFrameTimes(gameTime); calculateFps(gameTime); if(Visible) { #if XNA_4 this.spriteBatch.Begin(); #else this.spriteBatch.Begin( SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState ); #endif try { string fpsText = string.Format("FPS: {0:0.##}", this.currentFps); this.spriteBatch.DrawString( this.lucidaFont, fpsText, new Vector2(10.0f, 10.0f), Color.Red ); } finally { this.spriteBatch.End(); } } } /// Updates the frame times array /// Current snapshot of the game's timing values private void updateFrameTimes(GameTime gameTime) { long nowTicks = gameTime.TotalGameTime.Ticks; // Remove all frame times older than one second, but do not empty // the queue. long oneSecondAgoTicks = nowTicks - TimeSpan.TicksPerSecond; while(frameTimes.Count > 1) { if(this.frameTimes.Peek() >= oneSecondAgoTicks) { break; } this.frameTimes.Dequeue(); } // Add the new frame time to the queue this.frameTimes.Enqueue(nowTicks); } /// Recalculates the frames per second /// Snapshot of the game's timing values private void calculateFps(GameTime gameTime) { if(this.frameTimes.Count < 2) { this.currentFps = 0.0f; return; } long oldestTicks = this.frameTimes.Peek(); long nowTicks = gameTime.TotalGameTime.Ticks; long delta = nowTicks - oldestTicks; long frameCount = this.frameTimes.Count - 1; this.currentFps = (float)(frameCount * TimeSpan.TicksPerSecond) / (float)delta; } /// Shared content manager containing the game's font private ContentManager contentManager; /// Graphics device service through which rendering takes place private IGraphicsDeviceService graphicsDeviceService; /// Font used for the FPS counter private SpriteFont lucidaFont; /// Sprite batch used to draw the FPS counter private SpriteBatch spriteBatch; /// Stores the times at which a frame was drawn private Queue frameTimes; /// Current frames per second being drawn private float currentFps; } } // namespace Nuclex.Graphics.SpecialEffects.Demo