25 using System.Collections.Generic;
26 using System.Reflection;
27 using SiliconStudio.Core.Diagnostics;
28 using SiliconStudio.Core.Mathematics;
29 using SiliconStudio.Paradox.Games.Time;
30 using SiliconStudio.Paradox.Graphics;
31 using SiliconStudio.Core;
32 using SiliconStudio.Core.Serialization.Assets;
34 namespace SiliconStudio.
Paradox.Games
43 private readonly Dictionary<object, ProfilingKey> updateProfilers =
new Dictionary<object, ProfilingKey>();
44 private readonly Dictionary<object, ProfilingKey> drawProfilers =
new Dictionary<object, ProfilingKey>();
45 private readonly
GameTime updateTime;
49 private readonly
int[] lastUpdateCount;
50 private readonly
float updateCountAverageSlowLimit;
51 private readonly GamePlatform gamePlatform;
53 private TimeSpan singleFrameUpdateTime;
57 private bool isEndRunRequired;
58 private bool isExiting;
59 private bool suppressDraw;
61 private TimeSpan totalUpdateTime;
62 private TimeSpan totalDrawTime;
63 private readonly TimeSpan maximumElapsedTime;
64 private TimeSpan accumulatedElapsedGameTime;
65 private TimeSpan lastFrameElapsedGameTime;
66 private int nextLastUpdateCountIndex;
67 private bool drawRunningSlowly;
68 private bool forceElapsedTimeToZero;
74 private bool isMouseVisible;
76 internal bool SlowDownDrawCalls;
78 private Vector3 virtualResolution;
82 #region Constructors and Destructors
90 Log = GlobalLogger.GetLogger(this.GetType().GetTypeInfo().Name);
95 totalUpdateTime =
new TimeSpan();
97 IsFixedTimeStep =
false;
98 maximumElapsedTime = TimeSpan.FromMilliseconds(500.0);
99 TargetElapsedTime = TimeSpan.FromTicks(10000000 / 60);
100 lastUpdateCount =
new int[4];
101 nextLastUpdateCountIndex = 0;
106 const int BadUpdateCountTime = 2;
107 var maxLastCount = 2 * Math.Min(BadUpdateCountTime, lastUpdateCount.Length);
108 updateCountAverageSlowLimit = (float)(maxLastCount + (lastUpdateCount.Length - maxLastCount)) / lastUpdateCount.Length;
115 Asset.Serializer.SerializerContextTags.Set(ServiceRegistry.ServiceRegistryKey, Services);
121 gamePlatform = GamePlatform.Create(
this);
122 gamePlatform.Activated += gamePlatform_Activated;
123 gamePlatform.Deactivated += gamePlatform_Deactivated;
124 gamePlatform.Exiting += gamePlatform_Exiting;
125 gamePlatform.WindowCreated += GamePlatformOnWindowCreated;
128 Services.AddService(typeof(
IGame),
this);
138 #region Public Events
164 #region Public Properties
197 public float DrawInterpolationFactor {
get;
private set; }
237 if (graphicsDeviceService == null)
239 throw new InvalidOperationException(
"GraphicsDeviceService is not yet initialized");
242 return graphicsDeviceService.GraphicsDevice;
250 public TimeSpan InactiveSleepTime {
get; set; }
256 public bool IsActive {
get;
private set; }
262 public bool IsFixedTimeStep {
get; set; }
268 public bool IsDrawDesynchronized {
get; set; }
270 public bool EarlyExit {
get; set; }
276 public bool IsMouseVisible
280 return isMouseVisible;
285 isMouseVisible = value;
288 Window.IsMouseVisible = value;
302 public bool IsRunning {
get;
private set; }
314 public TimeSpan TargetElapsedTime {
get; set; }
324 if (gamePlatform != null)
326 return gamePlatform.MainWindow;
334 public Vector3 VirtualResolution
336 get {
return virtualResolution; }
339 if(virtualResolution == value)
342 virtualResolution = value;
344 var handler = VirtualResolutionChanged;
354 internal EventHandler<GameUnhandledExceptionEventArgs> UnhandledExceptionInternal
356 get {
return UnhandledException; }
359 #region Public Methods and Operators
375 forceElapsedTimeToZero =
true;
376 drawRunningSlowly =
false;
377 Array.Clear(lastUpdateCount, 0, lastUpdateCount.Length);
378 nextLastUpdateCountIndex = 0;
381 internal void InitializeBeforeRun()
386 using (var profile = Profiler.Begin(GameProfilingKeys.GameInitialize))
389 graphicsDeviceManager.CreateDevice();
393 if (graphicsDeviceService == null)
395 throw new InvalidOperationException(
"No GraphicsDeviceService found");
399 if (graphicsDeviceService.GraphicsDevice == null)
401 throw new InvalidOperationException(
"No GraphicsDevice found");
407 LoadContentInternal();
414 updateTime.Reset(totalUpdateTime);
418 using (Profiler.Begin(GameProfilingKeys.GameUpdate))
423 singleFrameUpdateTime += updateTimer.ElapsedTime;
431 Log.Error(
"Unexpected exception", ex);
445 throw new InvalidOperationException(
"Cannot run this instance while it is already running");
450 if (graphicsDeviceManager == null)
452 throw new InvalidOperationException(
"No GraphicsDeviceManager found");
462 Context.RequestedWidth = graphicsDeviceManagerImpl.PreferredBackBufferWidth;
463 Context.RequestedHeight = graphicsDeviceManagerImpl.PreferredBackBufferHeight;
464 Context.RequestedBackBufferFormat = graphicsDeviceManagerImpl.PreferredBackBufferFormat;
465 Context.RequestedDepthStencilFormat = graphicsDeviceManagerImpl.PreferredDepthStencilFormat;
467 gamePlatform.Run(Context);
469 if (gamePlatform.IsBlockingRun)
477 isEndRunRequired =
true;
482 if (!isEndRunRequired)
515 Utilities.Sleep(InactiveSleepTime);
528 var elapsedAdjustedTime = timer.ElapsedTimeWithPause;
530 if (forceElapsedTimeToZero)
532 elapsedAdjustedTime = TimeSpan.Zero;
533 forceElapsedTimeToZero =
false;
536 if (elapsedAdjustedTime > maximumElapsedTime)
538 elapsedAdjustedTime = maximumElapsedTime;
541 bool suppressNextDraw =
true;
543 var singleFrameElapsedTime = elapsedAdjustedTime;
550 if (Math.Abs(elapsedAdjustedTime.Ticks - TargetElapsedTime.Ticks) < (TargetElapsedTime.Ticks >> 6))
552 elapsedAdjustedTime = TargetElapsedTime;
556 accumulatedElapsedGameTime += elapsedAdjustedTime;
559 updateCount = (int)(accumulatedElapsedGameTime.Ticks/TargetElapsedTime.Ticks);
561 if (IsDrawDesynchronized)
563 drawLag = accumulatedElapsedGameTime.Ticks%TargetElapsedTime.Ticks;
564 suppressNextDraw =
false;
566 else if (updateCount == 0)
573 lastUpdateCount[nextLastUpdateCountIndex] = updateCount;
574 float updateCountMean = 0;
575 for (
int i = 0; i < lastUpdateCount.Length; i++)
577 updateCountMean += lastUpdateCount[i];
580 updateCountMean /= lastUpdateCount.Length;
581 nextLastUpdateCountIndex = (nextLastUpdateCountIndex + 1)%lastUpdateCount.Length;
584 drawRunningSlowly = updateCountMean > updateCountAverageSlowLimit;
587 accumulatedElapsedGameTime =
new TimeSpan(accumulatedElapsedGameTime.Ticks - (updateCount*TargetElapsedTime.Ticks));
588 singleFrameElapsedTime = TargetElapsedTime;
592 Array.Clear(lastUpdateCount, 0, lastUpdateCount.Length);
593 nextLastUpdateCountIndex = 0;
594 drawRunningSlowly =
false;
597 bool beginDrawSuccessful =
false;
600 beginDrawSuccessful = BeginDraw();
603 for (lastFrameElapsedGameTime = TimeSpan.Zero; updateCount > 0 && !isExiting; updateCount--)
605 updateTime.Update(totalUpdateTime, singleFrameElapsedTime, singleFrameUpdateTime, drawRunningSlowly,
true);
608 UpdateAndProfile(updateTime);
613 suppressNextDraw &= suppressDraw;
614 suppressDraw =
false;
618 lastFrameElapsedGameTime += singleFrameElapsedTime;
619 totalUpdateTime += singleFrameElapsedTime;
625 singleFrameUpdateTime += updateTimer.ElapsedTime;
630 if (!suppressNextDraw)
632 totalDrawTime = TimeSpan.FromTicks(totalUpdateTime.Ticks + drawLag);
633 DrawInterpolationFactor = (float)drawLag/(
float)TargetElapsedTime.Ticks;
637 singleFrameUpdateTime = TimeSpan.Zero;
641 if (beginDrawSuccessful)
643 using (Profiler.Begin(GameProfilingKeys.GameEndDraw))
654 Log.Error(
"Unexpected exception", ex);
659 private void CheckEndRun()
661 if (isExiting && IsRunning && isEndRunRequired)
678 if ((graphicsDeviceManager != null) && !graphicsDeviceManager.BeginDraw())
701 this.GameSystems.CopyTo(array, 0);
702 for (
int i = 0; i < array.Length; i++)
704 var disposable = array[i] as IDisposable;
705 if (disposable != null)
707 disposable.Dispose();
711 var disposableGraphicsManager = graphicsDeviceManager as IDisposable;
712 if (disposableGraphicsManager != null)
714 disposableGraphicsManager.Dispose();
717 DisposeGraphicsDeviceEvents();
719 if (gamePlatform != null)
721 gamePlatform.Release();
736 GameSystems.Draw(gameTime);
746 GraphicsDevice.SetRenderTarget(GraphicsDevice.DepthStencilBuffer, GraphicsDevice.BackBuffer);
753 if (graphicsDeviceManager != null)
755 graphicsDeviceManager.EndDraw(present);
768 SetupGraphicsDeviceEvents();
770 GameSystems.Initialize();
773 internal virtual void LoadContentInternal()
775 GameSystems.LoadContent();
778 internal bool IsExiting()
790 var handler = Activated;
804 var handler = Deactivated;
818 var handler = Exiting;
827 EventHandler<EventArgs> handler = WindowCreated;
834 private void GamePlatformOnWindowCreated(
object sender,
EventArgs eventArgs)
836 IsMouseVisible =
true;
856 GameSystems.UnloadContent();
867 GameSystems.Update(gameTime);
870 private void UpdateAndProfile(
GameTime gameTime)
873 using (Profiler.Begin(GameProfilingKeys.GameUpdate))
878 singleFrameUpdateTime += updateTimer.ElapsedTime;
881 private void gamePlatform_Activated(
object sender,
EventArgs e)
890 private void gamePlatform_Deactivated(
object sender,
EventArgs e)
899 private void gamePlatform_Exiting(
object sender,
EventArgs e)
904 private void DrawFrame()
906 if (SlowDownDrawCalls && (UpdateTime.FrameCount & 1) == 1)
912 if (!profilingDraw.IsInitialized)
914 profilingDraw = Profiler.Begin(GameProfilingKeys.GameDrawFPS);
918 profilingDraw.CheckIfEnabled();
920 if (!isExiting && GameSystems.IsFirstUpdateDone && !
Window.IsMinimized)
922 drawTime.Update(totalDrawTime, lastFrameElapsedGameTime, singleFrameUpdateTime, drawRunningSlowly,
true);
924 if (drawTime.FramePerSecondUpdated)
927 profilingDraw.Mark(
"Frame = {0}, Update = {1:0.000}ms, Draw = {2:0.000}ms, FPS = {3:0.00}", drawTime.FrameCount, updateTime.TimePerFrame.TotalMilliseconds, drawTime.TimePerFrame.TotalMilliseconds, drawTime.FramePerSecond);
930 using (Profiler.Begin(GameProfilingKeys.GameDraw))
938 lastFrameElapsedGameTime = TimeSpan.Zero;
942 private void SetupGraphicsDeviceEvents()
948 if (graphicsDeviceService == null)
950 throw new InvalidOperationException(
"Unable to create a IGraphicsDeviceService");
953 if (graphicsDeviceService.GraphicsDevice == null)
955 throw new InvalidOperationException(
"Unable to find a GraphicsDevice instance");
960 graphicsDeviceService.DeviceCreated += graphicsDeviceService_DeviceCreated;
961 graphicsDeviceService.DeviceResetting += graphicsDeviceService_DeviceResetting;
962 graphicsDeviceService.DeviceReset += graphicsDeviceService_DeviceReset;
963 graphicsDeviceService.DeviceDisposing += graphicsDeviceService_DeviceDisposing;
966 private void DisposeGraphicsDeviceEvents()
968 if (graphicsDeviceService != null)
970 graphicsDeviceService.DeviceCreated -= graphicsDeviceService_DeviceCreated;
971 graphicsDeviceService.DeviceResetting -= graphicsDeviceService_DeviceResetting;
972 graphicsDeviceService.DeviceReset -= graphicsDeviceService_DeviceReset;
973 graphicsDeviceService.DeviceDisposing -= graphicsDeviceService_DeviceDisposing;
977 private void graphicsDeviceService_DeviceCreated(
object sender,
EventArgs e)
981 LoadContentInternal();
985 private void graphicsDeviceService_DeviceDisposing(
object sender,
EventArgs e)
996 private void graphicsDeviceService_DeviceReset(
object sender,
EventArgs e)
1000 resumeManager.OnReload();
1001 resumeManager.OnRecreate();
1004 private void graphicsDeviceService_DeviceResetting(
object sender,
EventArgs e)
1008 resumeManager.OnDestroyed();
Service providing method to access GraphicsDevice life-cycle.
A profiler state contains information of a portion of code being profiled. See remarks.
This provides timing information similar to System.Diagnostics.Stopwatch but an update occuring only ...
GameBase()
Initializes a new instance of the GameBase class.
EventHandler< EventArgs > Deactivated
Occurs when [deactivated].
Interface providing services to deal with the virtual resolution of the game. The virtual resolution ...
virtual void OnDeactivated(object sender, EventArgs args)
Raises the Deactivated event. Override this method to add code to handle when the game loses focus...
void ResetElapsedTime()
Resets the elapsed time counter.
EventHandler< GameUnhandledExceptionEventArgs > UnhandledException
void SuppressDraw()
Prevents calls to Draw until the next Update.
void Run(GameContext gameContext=null)
Call this method to initialize the game, begin running the game loop, and start processing events for...
virtual void BeginRun()
Called after all components are initialized but before the first update in the game loop...
RenderTarget BackBuffer
Gets the back buffer sets by the current Presenter setup on this device.
EventHandler< EventArgs > WindowCreated
Occurs when [window created].
void Tick()
Updates the game's clock and calls Update and Draw.
Interface of the asset manager.
Represents a three dimensional mathematical vector.
Manages the GraphicsDevice lifecycle.
Base class for a framework component.
virtual bool ShowMissingRequirementMessage(Exception exception)
This is used to display an error message if there is no suitable graphics device or sound card...
Defines the interface for an object that manages a GraphicsDevice.
Defines a generic game system.
virtual void OnWindowCreated()
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
virtual void OnActivated(object sender, EventArgs args)
Raises the Activated event. Override this method to add code to handle when the game gains focus...
EventHandler< EventArgs > VirtualResolutionChanged
override void Destroy()
Disposes of object resources.
virtual bool BeginDraw()
Starts the drawing of a frame. This method is followed by calls to Draw and EndDraw.
Current timing used for variable-step (real time) or fixed-step (game time) games.
virtual void UnloadContent()
Called when graphics resources need to be unloaded. Override this method to unload any game-specific ...
virtual void Draw(GameTime gameTime)
Reference page contains code sample.
virtual void EndRun()
Called after the game loop has stopped running before exiting.
A collection of game components.
Contains context used to render the game (Control for WinForm, a DrawingSurface for WP8...
Parameters used when launching an application.
EventHandler< EventArgs > Exiting
Occurs when [exiting].
Base implementation for IServiceRegistry
GameSystemState
Describes state of the GameSystemCollection.
virtual void Update(GameTime gameTime)
Reference page contains links to related conceptual articles.
virtual void OnExiting(object sender, EventArgs args)
Raises an Exiting event. Override this method to add code to handle when the game is exiting...
void Exit()
Exits the game.
Output message to log right away.
virtual void EndDraw(bool present)
Ends the drawing of a frame. This method is preceeded by calls to Draw and BeginDraw.
EventHandler< EventArgs > Activated
Occurs when [activated].
virtual void Initialize()
Called after the Game and GraphicsDevice are created, but before LoadContent. Reference page contains...