Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1426 connectés 

  FORUM HardWare.fr
  Programmation
  C#/.NET managed

  C# : System.Drawing et performances...

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

C# : System.Drawing et performances...

n°1344531
Arjuna
Aircraft Ident.: F-MBSD
Posté le 11-04-2006 à 21:24:58  profilanswer
 

Salut, j'ai un "léger" souci de performances avec System.Drawing
 
Bon, ok, je cherche le diable à bosser en plein écran sur mon 24" mais bon...
 
Le but du jeu, c'est d'avoir à l'écran un truc fluide à l'écran, et sans clignotements.
 
J'ai suivi cet article (cet section "Double Buffering Technique The .NET Way" ) afin de virer les clignottements. Ca marche bien, et autant c'est rapide et sans consommation CPU dans une fenêtre de la taille par défaut, autant quand je passe en plein écran... 100% CPU sur un AMD 3500+ et ça oublie de dessiner toutes les 10ms... C'est plutôt toutes les 20/30ms...
 
(ok, je laisserai peut-être pas un ticker de 10ms, ça sert pas à grand chose maintenant que les gens sont pour la plupart en CRT à 60 ou 75Hz max)
 
Après tests (viré tous mes éléments à l'affichage sauf 1, et shooté tous mes traîtements pour remplacer par un déplacement d'une image de 10x10 de façon rectiligne), ça ramme toujours à donf.
Il semble donc que ce soit le coup du "DrawImage" de mon image tampon qui fout tout à plat. D'après l'article, c'est censé être performant, et malgré certaines optimisations que j'ai tenté d'apporter, ça ramme toujours autant.
 
D'après l'auteur, passer par la lib Win32 est plus lent, et surtout c'est plus gore, donc j'ai pas testé (et aucune envie de le faire).
 
Y'a un bout que j'ai mal recopié ? Ou des méga-conneries dans mon code et j'en suis pas conscient ?
L'article date de 2002... Je suppose que depuis, ça a dû évoluer... Il doit se baser sur la version 1.0 du FrameWork, et on en est à la 2.0... Y'a une meilleure solution qui a vu le jour depuis ?
 
Voici les bouts de mon code qui sont concernés :
 

Code :
  1. /* Classe Form1 */
  2. private void Form1_Load(object sender, EventArgs ea)
  3. {
  4.     e = new Engine(this.CreateGraphics());
  5.     e.size = this.Size;
  6.     launch();
  7.     t = new Timer();
  8.     t.Interval = 10;
  9.     t.Tick +=new EventHandler(Ticker);
  10.     t.Start();
  11. }
  12. public void launch()
  13. {
  14.     s1 = new AntSprite(new PointF(0f, 0f), 0);
  15.     e.addItem(s1);
  16. }
  17. private void Form1_ReSize(object sender, EventArgs ea)
  18. {
  19.     e.size = this.Size;
  20.     e.g = this.CreateGraphics();
  21. }
  22. private void Ticker(object sender, EventArgs ea)
  23. {
  24.     e.PreRender();
  25.     PointF pos = s1.position;
  26.     pos.X += .1f;
  27.     pos.Y += .05f;
  28.     s1.position = pos;
  29. //    s1.direction = (s1.direction + (Math.PI / 360)) % (Math.PI * 2);
  30.     e.Render();
  31. }
  32. /* Classe AntSprite */
  33. public class AntSprite : EngineItem
  34. {
  35.     public AntSprite(PointF position, double direction)
  36.     {
  37.         this.position = position;
  38.         this.direction = direction;
  39.         System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
  40.         resources.ApplyResources(this, "$this" );
  41.         this.picture = (Image)resources.GetObject("ant" );
  42.     }
  43. }
  44. /* Classe EngineItem */
  45. public abstract class EngineItem
  46. {
  47.     /// <summary>
  48.     /// This abstract class represents an item to draw on the 2D Engine
  49.     /// </summary>
  50.     /// <remarks>Direction is a radians angle</remarks>
  51.     public PointF position;
  52.     public double direction;
  53.     public Image picture;
  54.     /// <summary>
  55.     /// Render the item on the 2D Engine.
  56.     /// </summary>
  57.     /// <param name="graphics">Graphics of the control to fill with the item</param>
  58.     public void Render(Graphics graphics)
  59.     {
  60.         graphics.DrawImage(this.picture, new PointF((this.position.X - ((float)this.picture.Width / 2f)), (this.position.Y - ((float)this.picture.Height / 2f))));
  61.         if (this.direction >= 0)
  62.         {
  63.             graphics.DrawLine(Pens.Red, this.position, new PointF(this.position.X + 10f * (float)Math.Cos(this.direction - (Math.PI / 4d)), this.position.Y + 10f * (float)Math.Sin(this.direction - (Math.PI / 4d))));
  64.             graphics.DrawLine(Pens.Red, this.position, new PointF(this.position.X + 10f * (float)Math.Cos(this.direction + (Math.PI / 4d)), this.position.Y + 10f * (float)Math.Sin(this.direction + (Math.PI / 4d))));
  65.         }
  66.     }
  67. }
  68. /* Classe Engine */
  69.     public class Engine
  70.     {
  71.         private Graphics _g;
  72.         private Graphics _offScreenG;
  73.         private Bitmap _bmp;
  74.         private List<EngineItem> _items = new List<EngineItem>();
  75.         public Size size
  76.         {
  77.             set
  78.             {
  79.                 this._bmp = new Bitmap(value.Width, value.Height);
  80.                 this._offScreenG = Graphics.FromImage(this._bmp);
  81.                 this._offScreenG.Clear(Color.Wheat);
  82.             }
  83.         }
  84.         public Graphics g
  85.         {
  86.             get
  87.             {
  88.                 return this._g;
  89.             }
  90.             set
  91.             {
  92.                 this._g = value;
  93.             }
  94.         }
  95.         public Engine(Graphics g)
  96.         {
  97.             this._g = g;
  98.         }
  99.         ~Engine()
  100.         {
  101.             this._offScreenG.Dispose();
  102.             this._offScreenG = null;
  103.             this._bmp.Dispose();
  104.             this._bmp = null;
  105.             this._items.RemoveRange(0, this._items.Count);
  106.             this._g.Dispose();
  107.             this._g = null;
  108.         }
  109.         public void PreRender()
  110.         {
  111.             EngineItem ei = this._items[0];
  112.             this._offScreenG.Clip = new Region(new RectangleF(new PointF(ei.position.X - 15f, ei.position.Y - 15f), new SizeF(30f, 30f)));
  113.             this._offScreenG.Clear(Color.Wheat);
  114. /*
  115.             foreach (EngineItem ei in this._items)
  116.             {
  117.                 this._offScreenG.Clip = new Region(new RectangleF(new PointF(ei.position.X - 15f, ei.position.Y - 15f), new SizeF(30f, 30f)));
  118.                 this._offScreenG.Clear(Color.Wheat);
  119.             }
  120. */
  121.         }
  122.         public void Render()
  123.         {
  124.             this._offScreenG.ResetClip();
  125.             this._items[0].Render(this._offScreenG);
  126. /*
  127.             foreach (EngineItem ei in this._items)
  128.             {
  129.                 ei.Render(this._offScreenG);
  130.             }
  131. */
  132.             this._g.DrawImage(this._bmp, 0, 0);
  133.         }
  134.         public void addItem(EngineItem item)
  135.         {
  136.             this._items.Add(item);
  137.         }
  138.         public void delItem(EngineItem item)
  139.         {
  140.             this._items.Remove(item);
  141.         }
  142.     }


 
En bref, plutôt que de recréer un Graphics new à chaque ittération du "Render", je lui efface les zones des anciens sprites dans le "PreRender" via des Clip, puis je redessine les sprites une fois qu'ils ont bougé.
Ceci dit, ça ne change rien :D
A priori, c'est bien la recopie du gros Bitmap temporaire qui plombe tout.


Message édité par Arjuna le 11-04-2006 à 21:30:12
mood
Publicité
Posté le 11-04-2006 à 21:24:58  profilanswer
 

n°1344680
Arjuna
Aircraft Ident.: F-MBSD
Posté le 12-04-2006 à 07:52:31  profilanswer
 

Bon ben euh ils font pareil... :sweat:
 
http://www.codeproject.com/cs/medi [...] rawing.asp

n°1344709
FlorentG
Posté le 12-04-2006 à 09:14:51  profilanswer
 

T'as essayé de profiler pour voir ?

n°1344738
Arjuna
Aircraft Ident.: F-MBSD
Posté le 12-04-2006 à 09:37:39  profilanswer
 

Euh... :??: :D

n°1344742
FlorentG
Posté le 12-04-2006 à 09:41:18  profilanswer
 
n°1344797
Arjuna
Aircraft Ident.: F-MBSD
Posté le 12-04-2006 à 10:42:37  profilanswer
 

Ouais, mais c'est quoi au juste ?
 
J'ai VS 2005 Team Suite Tester / (ou Developer) chais pas au juste
-> Et j'ai tout un tas d'assistants qui me permettent d'auditer mon code pour voir s'il est rapide, bien écrit et tout ça. C'est la même chose ou ça n'a rien à voir ? En fait, je bute sur le mot "profiling" ;)

n°1344929
FlorentG
Posté le 12-04-2006 à 13:14:46  profilanswer
 

Genre tu lances nProf, t'ouvres ton .exe, t'exécute normalement ton machin, puis tu quitte. Là il t'affichera tout le déroulement de ton prog, avec le temps pris par chaque fonction. Comme ça tu pourra voir laquelle est celle qui prend le plus de temps

n°1344990
Arjuna
Aircraft Ident.: F-MBSD
Posté le 12-04-2006 à 14:27:25  profilanswer
 

ben c'est tout vu, c'est le "drawimage" qui recopie le bitmap du double buffer dans le graphics de ma form. mes tests confirment ce point à 100%, aucun doute possible.
 
mais c'est justement là le problème : comment réduire ce temps au minimum possible ?
 
je peux modifier en tentant de jouer avec la zone à redessiner, mais rapidement les éléments qui bougent vont prendre toute la fenêtre, donc à la base ça va plus alourdir les traîtements qu'autrechose, pour un gain vraiment faible...

n°1345051
chagarou
Posté le 12-04-2006 à 15:08:24  profilanswer
 
n°1345053
FlorentG
Posté le 12-04-2006 à 15:08:37  profilanswer
 

J'ai failli le proposer :D

mood
Publicité
Posté le 12-04-2006 à 15:08:37  profilanswer
 

n°1345055
FlorentG
Posté le 12-04-2006 à 15:08:54  profilanswer
 

Sinon ch'ais pas, en utilisant l'évènement OnPaint plutôt :??:

n°1345231
Arjuna
Aircraft Ident.: F-MBSD
Posté le 12-04-2006 à 16:35:52  profilanswer
 


DirectDraw n'existe plus, et c'est remplacé par GDI+ :o
 
Et je vais pas me repalucher un moteur 3D pour faire de la 2D :o

n°1345232
Arjuna
Aircraft Ident.: F-MBSD
Posté le 12-04-2006 à 16:37:28  profilanswer
 

FlorentG a écrit :

Sinon ch'ais pas, en utilisant l'évènement OnPaint plutôt :??:


Tu ferais ça comment ? Parceque là, moi j'ai pas d'idée, à la base il se lance au moment du render, mais pas si je fais pas un invalidate() manuel (si je vais pas le render)
Et le invalidate() ça fait encore pire : non seulement ça ramme, mais ça fait du flickering même avec le double buffer :o

n°1345521
Arjuna
Aircraft Ident.: F-MBSD
Posté le 12-04-2006 à 20:46:53  profilanswer
 

Pour en revenir au problème de performances, j'ai donc pas pris "nProf", puisque VS 2005 a déjà tout ce qu'il faut.
 
Voici le résultat (grosse n'image) :
http://www.manga-torii.com/images/performance.PNG
 
Donc, comme je disais, en "exclusive", l'EXE principal bouffe seulement... 0% de l'occupation CPU globale, et le moteur 2D 1,768%.
Par contre, en "inclusive", on voit bien en effet que le moteur 2D bouffe 83,929%.
 
Quand je rentre dans le détail, et que je regarde les fonctions appelée/appelantes, c'est bien le "DrawImage" qui est directement dans "Engine.Render()" qui bouffe 66,667% alors que les multiples appels dans "EngineItem.Render()" ne bouffent en tout que 9% environ...
 
J'ai donc bien une couille dans le potage avec cette histoire de double buffer : autant la génération de l'image "shadow" est rapide et ne pose pas de problème de performances, autant la simple recopie de cette image dans le Graphics de ma forme fait tout s'effondrer.
 
A noter que j'ai fait le test en mode fenêtré, avec une petite fenêtre. En plein écran ça doit être largement pire/flagrant étant donné que ça arrive à ralentir toute l'application.

n°1345568
FlorentG
Posté le 12-04-2006 à 21:31:31  profilanswer
 

P'tain si j'avais le temps, j'aurais donné un essai :( Peut-être demain soir ou vendredi j'peux essayer d'essayer...

n°1345649
Arjuna
Aircraft Ident.: F-MBSD
Posté le 12-04-2006 à 23:33:53  profilanswer
 

C'est pas un problème. Merci si tu peux me filer un coup de main, mais sinon c'est pas grave ;)
 
Là je viens de tout essayer, pas moyen de trouver ce qui cloche.
 
Une âme charitable m'a filé les sources d'un moteur 2D basé sur D3D. D'expérience, parceque j'avais déjà bidouillé un peu avec D3D, ça va solutionner mon problème. Mais j'aurais bien voulu trouver ce qui déconnait avec GDI, c'est pas normal que ça ramme comme ça.
 
Je crois que j'ai tout tenté là, pas moyen de trouver ce qui cloche. J'ai clippé tout ce que je pouvais, boundé comme un malade, jusqu'à ce que plus rien ne marche... Mais que pouic, ça n'a rien changé.
 
J'ai suivi à la lettre les warning d'audit du code de VS... Au point d'avoir une application ne voulant plus démarrer faute de permissions... J'ai quasiment plus aucun "new" de goret inutile (genre un new d'un Region -type extrêment gourmand en mémoire- dans un while). Bref, tout mon code est à priori optimisé au maximum de ce que je pouvais faire, mais j'ai pas vu un pouillème de différence :D
 
(bon, je vous rassure j'ai pas passé toute la soirée dessus... 2 heures max, c'est déjà trop vu le résultat :D)

n°1345651
Arjuna
Aircraft Ident.: F-MBSD
Posté le 12-04-2006 à 23:40:25  profilanswer
 

Si tu veux partir de ce que j'ai fais pour trouver ce qui cloche...
 
http://www.manga-torii.com/images/ant.zip (1,1 Mo - projet complet, programme + moteur 2D. J'ai pas fait de nettoyage)
-- Je viens de voir que j'aurais pu virer mon "Visual Studio Performance Report" avant de zipper... Il fait 10 Mo :D
 
T'étonne pas du truc, à la base c'est pour faire un "simulateur de fourmis", genre 1000 trucs qui se balladent à l'écran et cherchent quelquechose puis retournent d'où ils viennent avec, afin de me familiariser avec différentes techniques de findingpath et de gestion des collisions.
 
Eh oui, le but du jeu c'était pas de me prendre la tête avec GDI... Mais si ça ramme avant même de faire le moindre bout d'algo de FindingPath, c'est même pas la peine :o


Message édité par Arjuna le 12-04-2006 à 23:42:33
n°1359114
Slay
Posté le 04-05-2006 à 00:10:00  profilanswer
 

tu le compiles en mode debug ?  
Parce que j'ai déja remarqué d'énorme différence si on utilise le mode debug ( c'est beaucoup beaucoup plus lent :D )

n°1359407
Arjuna
Aircraft Ident.: F-MBSD
Posté le 04-05-2006 à 12:03:39  profilanswer
 

Ben mes tests étaient avec VS Express. Et j'ai pas trouvé comment compiler en release avec cette version.
 
Ce soir, j'aurai enfin les DVD MSDN qu'on attend depuis 3 mois, verrai ce que ça donne avec.

n°1690854
lag78
Posté le 22-02-2008 à 16:41:39  profilanswer
 

Bonjour,
 
j'ai le même problème et je me suis rendu compte que la technique du double buffering était inutile dans mon cas également.
Après de nombreux tests et avoir logger les temps d'éxécution des méthodes, j'ai l'impression qu'il y a un temps d'attente incomprésible lors du premier appel à des fonctions tels que DrawImage (probableùment du à l'initialisation de composant ou chargement de DLL).
 
J'appele plusieurs fois la méthode Drawimage avec la même image (toujours sur le même fond) et à la même position : premier appel de 0,1 à 0,2 s tous les suivants (une centaine) moins de : 0,0001 s.  
 
Ce que je ne comprend pas c'est que si je fais une boucle par dessus ces appels, je me tape pour chaque boucle cet appel 0,1 à 0,2 s suivi d'une centaine de 0,0001 s !


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C#/.NET managed

  C# : System.Drawing et performances...

 

Sujets relatifs
Champs date system sous windev 9.0[SQL2000] Subite chute des performances d'une requete
Date system[prog system avancee] - trouver les infos dans le format elf
? Parallel Distributed Management System ?[CSS] PNG & Transparence: les performances !
[SqlServer] Accès multiples et performancesSystem.Text.Encoding en VBA Word : possible ?
Content Management System (CMS)System,Array
Plus de sujets relatifs à : C# : System.Drawing et performances...


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR