1 votes

Comportement "intéressant" de Flash/Flex : Augmentation du temps pour dessiner 10 000 triangles

Il s'agit d'un programme de test pour mesurer le temps qu'il faut à Flash pour dessiner un triangle 10 000 fois.

L'application (code ci-dessous) appelle runTimeTest() toutes les N secondes, ce qui dessine le triangle 10 000 fois.

Maintenant, la partie "intéressante" : chaque appel prend de plus en plus de processeur jusqu'à ce qu'après le 6ème ou le 7ème appel, on arrive à "l'infini" (voir la capture d'écran du TaskManager).

Bug bizarre ou explicable ?

// create a Canvas to draw on

var c:Canvas = new Canvas();

function onApplicationComplete(e:Event)
{
    c.width = width;
    c.height = height;

    c.x = 0;
    c.y = 0;

    addChild(c);

    // set up a timer to run every 10 seconds

    var timer:Timer = new Timer(10000);
    timer.addEventListener(TimerEvent.TIMER, runTimeTest);
    timer.start();
}

function runTimeTest(e:Event)
{
    // this is called every 10 seconds

    var x0:int, x1:int, x2:int;
    var y0:int, y1:int, y2:int;

    // set triangle vertices

    x0 = 100; y0 = 500;
    x1 = 120; y1 = 515;
    x2 = 155; y2 = 500;

    // draw a filled triangle 10,000 times

    for (var i:int = 0; i < 10000; ++i)
    {
        c.graphics.beginFill( 0xff0000, 1 );
        c.graphics.moveTo( x0, y0 );

        c.graphics.lineTo( x1, y1 );
        c.graphics.lineTo( x2, y2 );
        c.graphics.lineTo( x0, y0 );

        c.graphics.endFill();
    }
}

i34.tinypic.com/jutpwo.png

3voto

Sly_cardinal Points 3109

"Et si vous appelez c.graphics.clear() au début de runTimeTest ?"
James Ward

Oui, l'ajout de c.graphics.clear() a réglé le problème.
Matt

Flash conserve toutes les commandes de dessin (c'est-à-dire les graphiques) jusqu'à ce que vous appeliez graphics.clear(). Cela vous permet de dessiner sur plusieurs images sans avoir à tout dessiner en même temps.

La raison pour laquelle il faut de plus en plus de temps pour dessiner les triangles est que chaque fois que vous appelez runTimeTest(), Flash doit dessiner un extra 10 000 triangles.

Ainsi, la première fois qu'il est appelé, Flash dessine 10 000 triangles, la deuxième fois que vous appelez runTimeTest(), il doit dessiner les 10 000 premiers triangles (parce que vous n'avez pas effacé le canevas), puis les 10 000 seconds triangles.

La solution, comme le souligne Glenn, est de vider la toile avant de dessiner chaque série de triangles. De cette façon, Flash ne doit dessiner que la série de triangles en cours.

Si, pour une raison quelconque, vous devez afficher un nombre absurde d'éléments à l'écran, vous pouvez tirer parti de la mise en cache des bitmaps : si vous dessinez les graphiques sur un Canvas/Sprite différent à chaque fois, Flash ne doit afficher chaque graphique qu'une seule fois (les autres sont mis en cache en mémoire dans les coulisses).

par exemple

private function drawTriangles():void
{
    var x0:int, x1:int, x2:int;
    var y0:int, y1:int, y2:int;

    // set triangle vertices

    x0 = 100; y0 = 500;
    x1 = 120; y1 = 515;
    x2 = 155; y2 = 500;

    // This is the important part.
    // Draw 10,000 triangles only on this new sprite.
    var c:Sprite = new Sprite();
    c.cacheAsBitmap = true;
    addChild(c);

    for (var i:int = 0; i < 10000; ++i)
    {
        c.graphics.beginFill( 0xff0000, 1 );
        c.graphics.moveTo( x0, y0 );

        c.graphics.lineTo( x1, y1 );
        c.graphics.lineTo( x2, y2 );
        c.graphics.lineTo( x0, y0 );

        c.graphics.endFill();
    }

}

Avec cette méthode, vous remarquerez qu'il faut à peu près le même temps pour dessiner les triangles à chaque fois (car Flash ne dessine en fait que le même nombre de choses à chaque fois).

Notez simplement que la mise en cache des bitmaps présente les plus grands avantages lorsque vous avez affaire à des graphiques largement statiques.

En général, ne dessinez que ce dont vous avez besoin et faites un graphics.clear() si vous n'avez pas besoin de conserver ce qui était précédemment sur le canevas.


En réponse au commentaire de Matt :

Voulez-vous dire que si endFill() était no appelé après chaque triangle (comme c'est le cas actuellement) ?

Cela n'aurait aucun avantage en termes de performances, cela changerait simplement ce qui a été dessiné. Si vous dessinez sur la même zone pendant la même section beginFill/endFill, la section dessinée sera "inversée" à chaque fois.

C'est un peu difficile à expliquer sans un exemple...

Modifions votre fonction triangle pour qu'elle dessine deux triangles dans la même section beginFill/endFill :

    c.graphics.beginFill( 0xff0000, 1 );
    for (var i:int = 0; i < 2; ++i)
    {
        c.graphics.moveTo( x0, y0 );

        c.graphics.lineTo( x1, y1 );
        c.graphics.lineTo( x2, y2 );
        c.graphics.lineTo( x0, y0 );    
    }
    c.graphics.endFill();

Si vous exécutez cela, vous ne verrez rien à l'écran !

C'est parce que vous dessinez deux fois sur la même zone et qu'elle est inversée (rendue transparente) - Flash continue techniquement à la dessiner, mais vous ne pouvez pas la voir.

Si vous modifiez le nombre d'itérations à 3, vous verrez à nouveau le triangle (parce qu'il bascule à chaque fois de visible -> invisible -> visible).

Cette page l'explique assez bien (voir la section "Winding" à environ un tiers du chemin) - bien qu'elle parle spécifiquement de Flash Player 10, la même idée s'applique aux versions précédentes, nous n'avions simplement pas le contrôle à l'époque :) http://www.flashperfection.com/tutorials/Flash-Player-10-Drawing-API-10877.html

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X