IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

worker fait baisser FPS


Sujet :

JavaScript

  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut worker fait baisser FPS
    Bonjour

    J'ai un worker, qui dessine une image sur des canvas.

    C'est un code simplifié, en vrai je passe à mon worker l'url du png qu'il doit dessiner à chaque postMessage.

    index.html
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
     
            <link rel="stylesheet" href="https://cdn.webix.com/edge/webix.css" type="text/css">
            <script src="https://cdn.webix.com/edge/webix.js" type="text/javascript"></script>
     
        </head>
    <body>
     
     
     
        <style>
            
        body {
            overflow: hidden;
        }
     
        </style>
     
        <div id="anchor" style="position:absolute"></div>
     
        <script>
        
            let world = // fold
            { 
                _frames : 0,
                _now : undefined,
                _before : new Date(),
     
                update:function() {
                    
                    // do some things
                    
                    world._now = new Date();
                    
                    if (world._now - world._before > 1000) {
                        $$("framerate").define("value", world._frames);
                        $$("framerate").refresh();
                        world._before = world._now;
                        world._frames = 0;
                    } else {
                        world._frames++;
                    }
                    
                },
     
                loop:function() {
                    
                    world.update();
                    
                    requestAnimationFrame(world.loop);
                },
            };
            
            webix.ready(function() {
                    
                // window avec text en haut à droite
                webix.ui( // fold
                {
                    view:"window",
                    head:false,
                    autofit:true,
                    zIndex:2000,
                    body:{
                        id:'framerate',
                        view:"text", 
                        value:"", 
                        readonly:true,
                        labelWidth:0,  
                        width:100,
                        tooltip:'FPS',
                    },
                    css:{
                        'background-color': 'transparent !important',
                        'box-shadow': 'none !important'
                    },
                    borderless:true,
                    
                }).show({ x:window.innerWidth-108, y:7 });
        
                world.loop();
                
                var worker = new Worker('./vorker.js');
                
                worker.postMessage({type:'preload', arr:[{key:"myKey", url:'url_de_mon_png'}]})
                
                // je prépare les canvas en amont pour être bien sûr que ce n'est pas ça qui fait chuter mes FPS
                let canvases = [];
                let data = [];
                            
                for (let i=0; i<2000; i++) {
                    canvases[i] = document.createElement('canvas');
                    document.getElementById("anchor").appendChild(canvases[i]);
                    canvases[i].style.position = "absolute";
                    canvases[i].style.left = Math.random() * 500 + "px";
                    canvases[i].style.top = Math.random() * 500 + "px";
                    data.push(canvases[i].transferControlToOffscreen());
                }
                
                document.onkeydown = function checkKey(e) {
                    
                    if (e.keyCode == 65) // A
                        worker.postMessage({type:'draw', canvases:data, key:"myKey"}, data);
                    
                }
            });
            
     
        </script>
     
    </body>
    </html>

    vorker.js
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    self._preloadedImages = [];
     
    self.addEventListener('message', async event => {
     
        if (event.data.type === 'preload') // fold
        {
            let toLoad = event.data.arr.length;
     
            for (let i=0; i<event.data.arr.length; i++) {
     
                fetch(event.data.arr[i].url)
                    .then(function (response) {
                        return response.blob();
                    })
                    .then(function (blob) {
                        return createImageBitmap(blob);
                    })
                    .then(function (imgBitMap) {
                        self._preloadedImages[event.data.arr[i].key] = imgBitMap; 
                        toLoad -= 1;
                        if (toLoad == 0) 
                            console.log('loaded');
                    })
                }
        } 
     
        else if (event.data.type === 'draw') 
        {
            let t = new Date();
     
            let canvases = event.data.canvases;
     
            const img = self._preloadedImages[event.data.key];
     
            for (let i=0; i<canvases.length; i++) {
     
                let canvas = canvases[i];
     
                canvas.width = img.width;
        	    canvas.height = img.height;
     
                canvas.getContext('2d').drawImage(
                    img,
                    0,                                              // soruce image X
                    0,                                              // sourice image y
                    img.width,                                      // source width
                    img.height,                                     // source height
                    0,                                              // destination x
                    0,                                              // destination y
                    img.width,                                      // destination width
                    img.height                                      // destination height
                );
            }
     
            console.log('temps wrker', new Date() - t);
     
        }
    });
    Sur des petites opérations, le worker fait très bien le travail, mais si j'ai 2000 images à dessiner (environ des images de 60x60.. Pour l'instant !), le framerate de mon thread principal est affecté, j'ai des baisses jusqu'à 20 FPS. J'ai déjà essayé de passer les offscreenCanvas en amont également, en pensant que c'était le passage en référence de data qui mettait la pagaille, mais en fait non.

    Serais-je arrivé à la limite des workers, déjà ?

  2. #2
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    Bonjour,
    je passe à mon worker l'url du png qu'il doit dessiner à chaque postMessage.
    je crois que tu te méprends sur l'utilisation des web Workers, ils n'ont pas accès au DOM entre autres.
    Côté worker :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    self.onmessage = function(e) {
      // je fais ce qu'il y a à faire, ouverture fichier, traitement ...
     
      // je renvoie le résultat
      self.postMessage(result);
    };
    et côté main.js par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    const worker = new Worker("vorker.js");
    worker.onmessage = function(e) {
        console.log("Merçi : " + e.data);
    }
    worker.postMessage("Envoi de data ...");
    ...c'est un gros raccourci

    Utilisation des web workers

  3. #3
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut
    Salut,

    Je sais que les workers n'ont pas accès au DOM Mais je sais aussi qu'ils peuvent avoir accès aux canvas de DOM si tu les passes en référence au worker, sur lequel le worker peut dessiner en thread séparé donc https://konvajs.org/docs/sandbox/Web_Worker.html

    Théoriquement donc, quelle que soit l'opération que le worker fait sur le canvas, ce n'est pas sensé influencer le déroulé du thread principal... Du coup je ne comprends pas pourquoi le FPS de mon thread principal baisse

  4. #4
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    effectivement, lu trop vite !

    J'ai fais un rapide constat en testant ton code, c'est effectivement bien la création des multiples
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    data.push(canvases[i].transferControlToOffscreen());
    qui plombe le total.

    Tu as vraiment besoin de 2000 canvas, dans la démo que tu as mis il n'y en a qu'un !

    Attention également, tu ne peux passer qu'une fois un OffscreenCanvas et ton code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if (e.keyCode == 65) // A
      worker.postMessage({type:'draw', canvases:data, key:"myKey"}, data);
    le fait à chaque appui sur la touche A.

  5. #5
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut
    Salut !

    En fait j'ai pris le parti de ne pas suivre le schéma classique d'une loop de jeu, qui n'utilise qu'un seul canvas, qui efface les parties significatives à chaque loop, et redessine ce qui doit l'être. C'était trop de calcul (machine, je veux dire), j'ai donc opté pour le schéma suivant : chaque entité (batiment, personnage) est dessinée sur son propre canvas une fois pour toute, le z-index de chaque canvas est calculé en fonction de sa position à l'écran, et mis à jour à chaque loop pour les personnages. Les avantages : une boucle de jeu très simple, juste des z-index et des positions à mettre à jour, et puis, entre effacer l'ancienne position d'un pnj et le redessiner 3 pixels plus loin, ou juste déplacer un canvas de 3px, le temps de calcul est franchement pas du tout le même, donc mon ordi me dit merci... Ce dernier paragraphe mériterait une discussion à lui tout seul je pense

    Du coup, la box d'un tile faisant environ 60 x 30 pixels, et mon écran faisant 1280 x 800, je me retrouve avec un potentiel nombre d'entités à afficher à l'écran d'environ 1200, selon mes savants calculs... Ce n'est pas 2000 mais je voulais prendre une marge haute, et puis toute façons la chute de FPS est la même avec 1000 canvas

    Le problème, c'est qu'au chargement du jeu, le PC se retrouve avec une grosse charge d'entités à dessiner...

    As tu des idées d'optimisation pour ce que je viens d'écrire ? A vrai dire ça fait quelques mois que j'ai arrêté d'ajouter des nouvelles fonctionnalités au machin, je veux d'abord régler mon histoire de FPS lol

  6. #6
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    En fait j'ai pris le parti de ne pas suivre le schéma classique d'une loop de jeu, qui n'utilise qu'un seul canvas
    On utilise rarement un seul canvas, souvent un « canvas background », un « canvas scene », celui-là est amené à être souvent repaint, un « canvas foreground ».

    On peut effectivement ensuite regrouper certains éléments.


    entre effacer l'ancienne position d'un pnj et le redessiner 3 pixels plus loin, ou juste déplacer un canvas de 3px, le temps de calcul est franchement pas du tout le même,
    ce n'est pas forcément le temps de calcul qui peut être préjudiciable, c'est le moteur de rendu qui est maitre en la matière et déplacer un élément DOM n'est pas forcément gratuit.


    Du coup, la box d'un tile faisant environ 60 x 30 pixels, et mon écran faisant 1280 x 800, je me retrouve avec un potentiel nombre d'entités à afficher à l'écran d'environ 1200, selon mes savants calculs...
    je tourne autour de 600 à la louche ce qui n'est pas insurmontable !


    As tu des idées d'optimisation pour ce que je viens d'écrire ?
    Je pense que des articles sont déjà parus sur cela et une façon d'accélérer certaines compositions tordue et de jouer avec les getImageData et putImageData, on travail sur la manipulation des « pixels ».
    Pixel manipulation with canvas

Discussions similaires

  1. Réponses: 1
    Dernier message: 03/06/2016, 22h06
  2. Réponses: 7
    Dernier message: 09/05/2010, 00h36
  3. Réponses: 0
    Dernier message: 09/07/2009, 13h39
  4. Réponses: 0
    Dernier message: 09/07/2009, 13h39
  5. Div qui fait baisser une autre div
    Par kate59 dans le forum Mise en page CSS
    Réponses: 4
    Dernier message: 25/01/2009, 21h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo