web-dev-qa-db-fra.com

Steady_Clock sautant entre les mises à jour dans la boucle principale du jeu

En essayant de mettre au point une boucle de jeu solide dans SFML, je suis tombé sur un problème que je n'arrive pas à résoudre. J'ai pu supprimer tout le code SFML et voir toujours le problème avec clock() in time.h. Puis je suis allé plus loin et je vois toujours le problème avec std::chrono::steady_clock.

Le problème: De façon assez constante, je vois que la quantité de travail pouvant être effectuée entre les mises à jour est négligée. Chaque mise à jour devrait prendre 1/60ème de seconde, et le reste du temps est passée à Draw() et faire autant de tirages que possible . Parfois, le nombre de tirages tombe à 0 ou 1 sans raison apparente. Cela bouillonne jusqu'à l'application réelle sous la forme d'un bégaiement perceptible. Hormis les "sauts", le nombre de tirages effectués est très cohérent.

Voici une image (notez le saut dans le temps de mise à jour et le baisse de tirage): Sortie console du problème

Un code:

#include <iostream>
#include <time.h>
#include <chrono>

using namespace std;
using namespace std::chrono;

void Draw()
{
    //for (int i = 0; i < 1000000; i++);
}

int main()
{
    steady_clock::time_point update_time;
    steady_clock::time_point update_next;
    int update_rate = 16666666; // 60 times a second (nanosecs)
    int updates;
    int max_updates = 5;
    int draws = 0;
    update_next = steady_clock::now();

    while (true)
    {
        updates = 0;
        update_time = steady_clock::now();
        while (duration_cast<nanoseconds>(update_time - update_next) > nanoseconds(update_rate) && updates++ < max_updates)
        {
            if (draws <= 1) {
                cout << "!!!!!!!!!!!!!ERROR!!!!!!!!!!!!!" << endl;
            }
            cout << "UPDATE - ";
            cout << "Draws: " << draws 
                 << " - UT - UN: " << duration_cast<nanoseconds>(update_time - update_next).count()
                 << endl;

            draws = 0;
            update_next += nanoseconds(update_rate);
        }
        draws++;
        Draw();
    }

    return 0;
}
  • Peut-être y a-t-il quelque chose que je ne comprends pas dans les applications typiques? Windows doit-il détourner les cycles du processeur de temps en temps?
  • J'ai vu ce problème avec steady_clock, clock et dans une application SFML complète dans laquelle le travail est effectué pendant Update et Draw
  • Je suppose que l'horloge SFML utilise probablement time.h clock
  • D'après mes tests, les contrôles max_updates n'ont rien à voir avec ce problème (je ne pense pas qu'ils causent le problème)

Le fait d'avoir vu cela avec quelques minuteries différentes m'amène à penser que quelque chose ne va pas dans mon implémentation ou mon système. Cet exemple a été exécuté dans VS, mais je l'ai également vu dans un fichier exécutable autonome. Jouer avec le taux de mise à jour ou la quantité de travail effectué en tirage au sort peut aider à le faire apparaître pour vous.


Après avoir testé mes processus d'arrière-plan, j'ai remarqué une corrélation étrange. Ce problème de saut ne survient que lorsque le lecteur Web Spotify est ouvert chromé et se produit une fois par seconde environ.

J'ai trouvé ce post qui pourrait être lié: https://community.spotify.com/t5/Other-Partners-Web-Player-etc/Web-Player-on-Chrome-causes-lag-stutter/ td-p/4587103

11
S. Turnage

Peut-être y a-t-il quelque chose que je ne comprends pas dans les applications typiques? Windows doit-il détourner les cycles du processeur de temps en temps?

Oui absolument. Windows exécute plusieurs processus en même temps. Maintenant, votre application arrive et exécute ce qui est essentiellement une boucle de rotation occupée. À un moment donné, le système d'exploitation risque de ne pas définir les priorités plus longtemps que prévu, car cela ressemble à un calcul long, et le système d'exploitation doit accorder à d'autres processus une part équitable du temps processeur.

En règle générale, vous ne devez pas vous fier à l'appel de votre routine de dessin un nombre exact de fois par seconde, et l'horloge maîtresse de votre jeu doit pouvoir gérer les sauts d'images. Je ne connais pas bien SFML, je ne peux donc pas en parler.

Cependant, j'ai de l'expérience avec l'audio en temps réel (et la vidéo d'ailleurs) s'exécutant dans des boucles dépassant 1000 mises à jour par seconde. Vous pouvez améliorer le partage du temps de boucle de votre jeu en définissant la priorité du fil sur THREAD_PRIORITY_HIGHEST ou THREAD_PRIORITY_TIME_CRITICAL (voir SetThreadPriority ).

Pour que cela soit efficace, vous devez également être une application bien comportée et effectuer périodiquement une sorte d’attente. Waiting permet au système d’exécuter les tâches qui lui sont nécessaires pour traiter d’autres processus (plusieurs d’entre eux ayant également une priorité élevée et souvent supérieurs à ceux que vous pourrez forcer en tant qu’application utilisateur).

L'attente est l'attente avant votre prochain cycle de tirage. Plutôt que de faire tourner votre chronomètre avec une utilisation de base à 100%, calculez simplement combien de temps vous êtes prêt à attendre et appelez std::this_thread::sleep_for . Rappelez-vous que la seule garantie est que le sommeil sera de au moins le montant que vous spécifiez. Cela peut absolument et sera plus que cela. Mais je vous recommande de commencer par là et de faire quelques expériences.

8
paddy

En plus de la réponse de @ paddy, je vous recommande de vous pencher sur les timesteps fixes . Si cela ne vaut pas la peine d'implémenter, vous devez également noter que SFML a Window.setFramerateLimit() . Ce n'est pas très précis mais la plupart des jeux simples n'ont pas besoin d'une précision significative.

3
bruglesco

J'ai utilisé une boucle de rotation plus un rendement pour 1 boucle de régulation KHz avec de bons résultats, mais attendez-vous à une échéance non respectée (une fois par milliers de cycles et des temps de sommeil longs).

1
PeppeDx

À l’origine, le problème semblait provenir de mon antivirus, mais je pense que j’ai en fait réduit le problème pour que le lecteur Web soit ouvert et provoque une pointe de performances de 1/s. Je ne suis pas sûr de savoir pourquoi.

0
S. Turnage