11
votes

Pourquoi le DratringContext.Drawtext de WPF est-il si cher?

dans WPF (4.0) Ma liste de liste (à l'aide d'un site virtualisateurStackpanel) contient 500 éléments. Chaque élément est d'un type personnalisé xxx

maintenant, lors de la déplacement de la barre de défilement de la liste de liste de laboche de manière à ce que chaque élément soit créé au moins une fois que l'utilisation du RAM dépasse 500 Mo après un certain temps. Et puis - après un certain temps - remonte à Ca 250 Mo mais reste à ce niveau. Fuite de mémoire ? Je pensais que l'avantage d'une virtualistetackpanel est que les visuels qui ne sont pas nécessaires / visibles sont disposés ...

Quoi qu'il en soit, cette utilisation de RAM extreme n'apparaît que lors du dessin du texte à l'aide de "Drawtext". Dessiner d'autres objets tels que "Drawellipse" ne consomme pas tant de mémoire.

Y a-t-il un moyen plus efficace de dessiner de nombreux éléments de texte que d'utiliser "Drawtext" "" Drawtext "?

Voici l'échantillon complet (créez simplement un Nouveau projet d'application WPF et remplacer le code de la fenêtre1): (Je sais qu'il y a du flowdocument and fixdocument, mais ils ne sont pas alternatives) Xaml: xxx

et la fenêtre1.xaml.cs: xxx


2 commentaires

Vous pouvez essayer la greweométrie. Qui est relativement léger. msdn.microsoft.com/en-us/library/ms742199.aspx D'autre part. Je dois dire. Drawtext est relativement moins pesé. Je ne sais pas pourquoi il prend que beaucoup de ressources. Avez-vous des échantillons pour le scénario ci-dessus?


DrawingContext.Drawglyph semble être beaucoup plus rapide que le tructué.


3 Réponses :


1
votes

Bien que cela ne vous soit pas tout à fait utile pour vous, mon expérience avec VirtualizingStackpanel n'est-elle pas que celle-ci dispose d'objets non valables, mais qu'il permet aux objets non tenus d'être disposés à récupérer la mémoire lorsque l'application nécessite plus de mémoire, qui devrait aboutir à votre montgoling à la mémoire de la mémoire lorsqu'il y a une mémoire disponible.

est-il possible que dc.drawtext déclenche une buildgeométrie () pour chaque objet formatedText et que vous pouvez apporter cela à l'extérieur de la boucle? Je ne sais pas combien de travail de travail est, mais il est possible que le DrawingContext ne soit que capable d'accepter la géométrie et que l'appel de buildgeométrie est appelé inutilement 999 fois dans votre échantillon. Regardez:

http://msdn.microsoft.com/ EN-US / Bibliothèque / System.Windows.Media.formattedtext.aspx

Pour voir s'il existe d'autres optimisations que vous pouvez faire.

Pouvez-vous produire des données de profil de mémoire et certaines données de synchronisation dans vos boucles pour donner un sentiment de ralentissement, ou la mémoire augmente de manière non linéaire pendant la boucle?


2 commentaires

C'est pourquoi DrawingContext.Drawglyphe est plus rapide que Thintext: il incendie Buildgemeotry une seule fois sur la création. Mais le désadvnatage est que les personnages ont l'air plus flou que par le tirage.


@fritz, Drawglyphrun produit un texte floue car il ne s'aligne pas aux pixels de l'appareil pour vous. Vous pouvez l'aligner vous-même avec une combinaison de gylphrun.chatalignmentbox () et DrawingContext.pushguidelineexet () .



9
votes

Un gros contributeur est le fait (basé sur mon expérience avec Glyphrun que je pense être utilisé dans les coulisses) qu'il utilise au moins 2 looks de dictionnaire par caractère pour obtenir l'indice et la largeur du glyphe. Un piratage que j'ai utilisé dans mon projet a été inscrit au décalage entre la valeur ASCII et l'indice de glyphe pour des caractères alphanumériques pour la police que j'utilisais. J'ai ensuite utilisé cela pour calculer les indices de glyphe pour chaque caractère plutôt que de faire lever le dictionnaire. Cela m'a donné une vitesse décente. De plus, le fait que je puisse réutiliser la glyphe, déplacez-le avec une transformation de traduction sans tout recalculer ou ces recherches de dictionnaire. Le système ne peut pas faire ce piratage dessus, car il n'est pas assez générique d'être utilisé dans tous les cas. J'imagine qu'un hack similaire pourrait être fait pour d'autres polices. Je n'ai testé que avec Arial, d'autres polices pourraient être indexées différemment. Peut être capable d'aller encore plus rapidement avec une police mono-espacée puisque vous pourrez peut-être supposer que les largeurs de glyphe seraient toutes les mêmes et que vous ne recherchez que si vous ne regardez plutôt qu'un par caractère, mais je n'ai pas testé cela.

L'autre contributeur ralentissement est ce petit code, je n'ai pas encore compris comment le pirater. typeface.ecteurGetglyphtypeface (sortie glyphtypeface);

Voici mon code pour mon hack alphanumérique Arial (compatibilité avec d'autres caractères inconnus) xxx


0 commentaires

1
votes

J'ai trouvé la solution d'User638350 pour être très utile; Dans mon cas, j'utilise une seule taille de police afin que les optimisations suivantes ont réduit le temps à moins de 0,0000 sur 20 000 images de 0,0060ms chaque image. La majeure partie du ralentissement provient de «trygetglyphtypeface» et de «cutanés anticipations» et ces deux sont donc mis en cache. En outre, ajout de calculer une position de décalage et de suivre une largeur totale.

    private static Dictionary<ushort,double> _glyphWidths = new Dictionary<ushort, double>();
    private static GlyphTypeface _glyphTypeface;
    public static GlyphRun CreateGlyphRun(string text, double size, Point position)
    {
        if (_glyphTypeface == null)
        {
            Typeface typeface = new Typeface("Arial");
            if (!typeface.TryGetGlyphTypeface(out _glyphTypeface))
                throw new InvalidOperationException("No glyphtypeface found");                
        }

        ushort[] glyphIndexes = new ushort[text.Length];
        double[] advanceWidths = new double[text.Length];

        var totalWidth = 0d;
        double glyphWidth;

        for (int n = 0; n < text.Length; n++)
        {
            ushort glyphIndex = (ushort)(text[n] - 29);
            glyphIndexes[n] = glyphIndex;

            if (!_glyphWidths.TryGetValue(glyphIndex, out glyphWidth))
            {
                glyphWidth = _glyphTypeface.AdvanceWidths[glyphIndex] * size;
                _glyphWidths.Add(glyphIndex, glyphWidth);
            }
            advanceWidths[n] = glyphWidth;
            totalWidth += glyphWidth;
        }

        var offsetPosition = new Point(position.X - (totalWidth / 2), position.Y - 10 - size);

        GlyphRun glyphRun = new GlyphRun(_glyphTypeface, 0, false, size, glyphIndexes, offsetPosition, advanceWidths, null, null, null, null, null, null);

        return glyphRun;
    }


0 commentaires