.NET Redessiner à 60 FPS?

Certaines animations s’exécutant dans une fenêtre de contexte OpenGL, je dois donc les redessiner constamment. Alors je suis venu avec le code suivant:

void InitializeRedrawTimer() { var timer = new Timer(); timer.Interval = 1000 / 60; timer.Tick += new EventHandler(timer_Tick); timer.Start(); } void timer_Tick(object sender, EventArgs e) { glWin.Draw(); } 

Cela ne me donne que 40 FPS cependant. Si je règle l’intervalle à 1 ms cependant, je peux atteindre 60. Alors, où sont passées les 20 ms restantes? Est-ce simplement dû à une mauvaise précision du chronomètre ou quoi? Que faire si je souhaite que mon programme s’exécute aussi rapidement que possible? Existe-t-il un moyen d’appeler en permanence Draw Func?

Vous pouvez essayer de mettre en place une boucle de jeu.

http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx

La boucle de base (légèrement modifiée par rapport à sa version originale et à la version du nouveau SDK pour faciliter la lecture):

 public void MainLoop() { // Hook the application's idle event System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle); System.Windows.Forms.Application.Run(myForm); } private void OnApplicationIdle(object sender, EventArgs e) { while (AppStillIdle) { // Render a frame during idle time (no messages are waiting) UpdateEnvironment(); Render3DEnvironment(); } } private bool AppStillIdle { get { NativeMethods.Message msg; return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); } } //And the declarations for those two native methods members: [StructLayout(LayoutKind.Sequential)] public struct Message { public IntPtr hWnd; public WindowMessage msg; public IntPtr wParam; public IntPtr lParam; public uint time; public System.Drawing.Point p; } [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously [DllImport(“User32.dll”, CharSet=CharSet.Auto)] public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags); 

Simple, élégant, efficace. Aucune allocation supplémentaire, aucune collection supplémentaire, cela fonctionne. L’événement Idle se déclenche s’il n’ya aucun message dans la file d’attente, puis le gestionnaire continue à boucler en boucle jusqu’à ce qu’un message apparaisse, auquel cas il s’arrête .. Une fois que tous les messages sont géré, l’événement inactif est à nouveau déclenché et le processus recommence.

Ce lien en décrit un qui utilise l’événement Idle de l’application. Cela peut être utile. Vous pouvez simplement effectuer un petit test de temps ou dormir pour le ralentir aux fps souhaités. Essayez d’utiliser la classe System.Diagnostics.StopWatch pour obtenir le chronomètre le plus précis.

J’espère que cela aide un peu.

Selon ce que vous faites , jetez un coup d’œil à WPF et à son support d’animation, ce pourrait être une meilleure solution que d’écrire vos propres optimisations dans WinForms.

Pensez également à utiliser DirectX car il dispose désormais d’un wrapper .NET et est très rapide, mais il est plus difficile d’utiliser WPF ou WinForms.

Pour appeler en permanence votre fonction draw:

  • Faire le dessin dans la méthode repeindre
  • Ajouter à la fin de la méthode repaint faire un BeginInvoke
  • Dans le délégué que vous avez passé à BeginInvoke, invalide le contrôle que vous dessinez.

Cependant, vos utilisateurs peuvent ne pas aimer que vous tuiez leur ordinateur!

Application.Idle est également une autre bonne option. Cependant, j’aime bien le fait que Windows ralentira le nombre de messages WmPaint envoyés en cas de besoin.

La timer de Windows Forms est finalement limitée en résolution à la résolution d’horloge système standard de votre ordinateur. Cela varie d’un ordinateur à l’autre, mais est généralement compris entre 1 ms et 20 ms.

Dans WinForms, chaque fois que vous demandez un intervalle de timer en code, Windows garantit uniquement que votre timer sera appelée plus souvent que le nombre de millisecondes spécifié. Cela ne garantit pas que votre timer sera appelée exactement aux millisecondes spécifiées.

En effet, Windows est un système d’exploitation à temps partagé et non en temps réel. D’autres processus et threads seront programmés pour s’exécuter simultanément et votre thread sera donc obligé d’attendre régulièrement l’utilisation du processeur. Par conséquent, vous ne devriez pas écrire de code de timer WinForms qui repose sur des intervalles ou des délais exacts .

Dans votre cas, définir l’intervalle sur 1 ms signifie simplement à Windows de déclencher cette timer aussi souvent que possible. Comme vous avez pu le constater, c’est nettement moins souvent que 1 ms (60 ips = environ 17 ms).

Si vous avez besoin d’un minuteur plus précis et de résolution supérieure, l’API Windows fournit des timers haute résolution / performances. Si votre matériel le prend en charge, voir Comment utiliser le minuteur haute résolution . L’inconvénient est que l’utilisation de l’UC de votre application sera augmentée pour prendre en charge les performances les plus élevées.

Globalement, je pense que vous feriez mieux de mettre en œuvre une boucle de jeu indépendante du matériel / système. De cette manière, l’animation perçue de votre scène apparaît constante, quel que soit le taux de trame réel obtenu. Pour plus d’informations ces articles donnent un bon fond.

J’ai remarqué dans l’exemple de code donné dans la ligne:

 timer.Interval = 1000 / 60; 

Il utilise l’opération de la division entière, donc le résultat sera arrondi à l’entier et ne sera pas précisément de 16,6666ms, mais de 17ms. Par conséquent, le minuteur effectue des graduations plus grandes que l’actualisation de l’écran.