Hello tous le monde,
Dans le cadre de mon petit moteur 2D en C# que je développe sur mon temps libre, je suis en train d'écrire un moteur de rendu simple pour afficher des sprites liés à des matériaux. La question que je me pose, c'est quelle est la meilleure stratégie pour créer les vertex buffers ? J'ai plusieurs stratégies en tête :
1) Je créé un vertex buffer par sprite, quitte à appeler device.setVertexBuffer() un grand nombre de fois par frame. On aurait très approximativement ceci :
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 class Bitmap { public Material Material{ get{ return material_; } set{ material_ = value;} } public Sprite Sprite{ get{ return sprite_;} set{ sprite_ = value; UpdateVertexBuffer(); } } public Bitmap(string materialPath, Sprite sprite) { material_ = Load(materialPath); sprite_ = sprite; UpdateVertexBuffer(); } public void Destroy() { Device.ReleaseVertexBuffer(vertexBuffer_); } public void Draw(Renderer renderer) { renderer.SetVertexBuffer(vertexBuffer_); renderer.SetMaterial(material_); renderer.DrawQuad(); } private void UpdateVertexBuffer() { if (vertexBuffer_ == null) { vertexBuffer_ = Device.CreateVertexBuffer(4); } vertexBuffer_.Lock(); vertexBuffer_[0] = sprite_.topLeft; vertexBuffer_[1] = sprite_.topRight; vertexBuffer_[2] = sprite_.bottomLeft; vertexBuffer_[3] = sprite_.bottomRight; vertexBuffer_.Unlock(); } private Material material_; private Sprite sprite_; private VertexBuffer vertexBuffer_; }
Ça me paraît être un gaspillage énorme de créer un VB pour seulement 4 sommets, et le coût d'appel de SetVertexBuffer dans le Draw me paraît énorme...
2) Je créé un très grand vertex buffer (de plusieurs Mo), et chaque Bitmap peut réserver des sommets dans celui-ci :
Là c'est beaucoup mieux, puisqu'on a besoin que d'un unique buffer dynamique ! Oui mais... Que se passe-t-il dans la méthode Destroy ? Il faudrait implémenter un moyen de libérer les vertex réservé par le Device, mais ça me semble compliqué, car entre temps, on peut avoir réservé d'autres vertex dans d'autre Bitmap, et on risque d'avoir tous les problèmes liés à la gestion mémoire (fragmentation, réallocation...)
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 class Bitmap { public Material Material{ get{ return material_; } set{ material_ = value;} } public Sprite Sprite{ get{ return sprite_;} set{ sprite_ = value; UpdateVertexBuffer(false); } } public Bitmap(string materialPath, Sprite sprite) { material_ = Load(materialPath); sprite_ = sprite; UpdateVertexBuffer(true); } public void Destroy() { // que faire ici ? } public void Draw(Renderer renderer) { renderer.SetMaterial(material_); renderer.DrawQuad(vertexBufferStartIdx_); } private void UpdateVertexBuffer(bool createVB) { if (createVB) { vertexBufferStartIdx_ = Device.ReserveVertex(4); } VertexBufferFragment buffer = Device.LockVertexBuffer(vertexBufferStartIdx_); buffer[0] = sprite_.topLeft; buffer[1] = sprite_.topRight; buffer[2] = sprite_.bottomLeft; buffer[3] = sprite_.bottomRight; Device.UnlockVertexBuffer(); } private Material material_; private Sprite sprite_; private uint vertexBufferStartIdx_; }
3) J'ai une 3e idée, mais je ne sais pas si elle efficace : il s'agit de n'avoir qu'un unique vertex buffer de 4 sommets dans le programme, de coordonnées (0,0), (1,0), (0,1) et (1,1), et de jouer sur les rotations/translations/scale dans le vertex shader pour afficher correctement ma texture.
Cette solution n'est pas mal, mais à chaque appel de DrawQuads (donc des milliers de fois par frame), on va devoir envoyer 5 float à la carte graphique... Le coût n'est pas négligeable.
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 class Bitmap { public Material Material{ get{ return material_; } set{ material_ = value;} } public Sprite Sprite{ get{ return sprite_;} set{ sprite_ = value; } } public Bitmap(string materialPath, Sprite sprite) { material_ = Load(materialPath); sprite_ = sprite; } public void Destroy() { } public void Draw(Renderer renderer) { renderer.SetMaterial(material_); renderer.DrawQuad(sprite.Scale, sprite.Rotation, sprite.Translation); // les différents paramètres vont être utilisé par le vertex shader } private Material material_; private Sprite sprite_; }
Voilà, j'espère que je me suis bien exprimé, tout conseil ou critique sont les bienvenus, je suis un peu débutant dans la programmation rendu et je ne sais pas trop par où partir.
Sinon je sais ce que vous allez me dire, la partie rendu d'un moteur 2D n'est jamais critique, et je ne devrais me poser la question du meilleur choix seulement si j'ai des problèmes de performance, mais je pose la question plus par curiosité que par nécessité. De plus, je programme un jeu qui demande d'afficher des milliers de sprites à l'écran, donc j'aurais peut-être des problèmes de perfs au final
ps : j'utilise XNA, mais je prévoie de passer sous Mono sous peu.
Partager