Arjuna Aircraft Ident.: F-MBSD | 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 :
- /* Classe Form1 */
- private void Form1_Load(object sender, EventArgs ea)
- {
- e = new Engine(this.CreateGraphics());
- e.size = this.Size;
- launch();
- t = new Timer();
- t.Interval = 10;
- t.Tick +=new EventHandler(Ticker);
- t.Start();
- }
- public void launch()
- {
- s1 = new AntSprite(new PointF(0f, 0f), 0);
- e.addItem(s1);
- }
- private void Form1_ReSize(object sender, EventArgs ea)
- {
- e.size = this.Size;
- e.g = this.CreateGraphics();
- }
- private void Ticker(object sender, EventArgs ea)
- {
- e.PreRender();
- PointF pos = s1.position;
- pos.X += .1f;
- pos.Y += .05f;
- s1.position = pos;
- // s1.direction = (s1.direction + (Math.PI / 360)) % (Math.PI * 2);
- e.Render();
- }
- /* Classe AntSprite */
- public class AntSprite : EngineItem
- {
- public AntSprite(PointF position, double direction)
- {
- this.position = position;
- this.direction = direction;
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
- resources.ApplyResources(this, "$this" );
- this.picture = (Image)resources.GetObject("ant" );
- }
- }
- /* Classe EngineItem */
- public abstract class EngineItem
- {
- /// <summary>
- /// This abstract class represents an item to draw on the 2D Engine
- /// </summary>
- /// <remarks>Direction is a radians angle</remarks>
- public PointF position;
- public double direction;
- public Image picture;
- /// <summary>
- /// Render the item on the 2D Engine.
- /// </summary>
- /// <param name="graphics">Graphics of the control to fill with the item</param>
- public void Render(Graphics graphics)
- {
- graphics.DrawImage(this.picture, new PointF((this.position.X - ((float)this.picture.Width / 2f)), (this.position.Y - ((float)this.picture.Height / 2f))));
- if (this.direction >= 0)
- {
- 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))));
- 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))));
- }
- }
- }
- /* Classe Engine */
- public class Engine
- {
- private Graphics _g;
- private Graphics _offScreenG;
- private Bitmap _bmp;
- private List<EngineItem> _items = new List<EngineItem>();
- public Size size
- {
- set
- {
- this._bmp = new Bitmap(value.Width, value.Height);
- this._offScreenG = Graphics.FromImage(this._bmp);
- this._offScreenG.Clear(Color.Wheat);
- }
- }
- public Graphics g
- {
- get
- {
- return this._g;
- }
- set
- {
- this._g = value;
- }
- }
- public Engine(Graphics g)
- {
- this._g = g;
- }
- ~Engine()
- {
- this._offScreenG.Dispose();
- this._offScreenG = null;
- this._bmp.Dispose();
- this._bmp = null;
- this._items.RemoveRange(0, this._items.Count);
- this._g.Dispose();
- this._g = null;
- }
- public void PreRender()
- {
- EngineItem ei = this._items[0];
- this._offScreenG.Clip = new Region(new RectangleF(new PointF(ei.position.X - 15f, ei.position.Y - 15f), new SizeF(30f, 30f)));
- this._offScreenG.Clear(Color.Wheat);
- /*
- foreach (EngineItem ei in this._items)
- {
- this._offScreenG.Clip = new Region(new RectangleF(new PointF(ei.position.X - 15f, ei.position.Y - 15f), new SizeF(30f, 30f)));
- this._offScreenG.Clear(Color.Wheat);
- }
- */
- }
- public void Render()
- {
- this._offScreenG.ResetClip();
- this._items[0].Render(this._offScreenG);
- /*
- foreach (EngineItem ei in this._items)
- {
- ei.Render(this._offScreenG);
- }
- */
- this._g.DrawImage(this._bmp, 0, 0);
- }
- public void addItem(EngineItem item)
- {
- this._items.Add(item);
- }
- public void delItem(EngineItem item)
- {
- this._items.Remove(item);
- }
- }
|
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
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
|