8Fermer10
bearbecueLe 13/02/2012 à 20:44
2)
bah la GoldenCrystal a bien resume, pas grand chose a dire de plus en fait smile

T'as certains qui aiment bien utiliser ce qu'ils appellent des 'uber-shader', qui sont des gros shaders de porc, faisables depuis les shader models qui limitent plus trop le nombre d'instructions, et qui gerent tous les types de materiaux et de lights de leurs scenes...

mais rien ne t'empeche d'avoir plein de shaders differents que tu switche, evidemment.

aussi, t'as typiquement ce qui s'appelle un 'render state' associe a chaque draw-call. (1 draw-call = ce qui affiche une liste de triangles un appel a par exemple Draw(Indexed)Primitive dans DirectX, ou glDraw(Range)Elements / glDrawArrays dans OpenGL).

pour afficher un truc, il te faut:
- un framebuffer binde
- les renderstates qui vont bien
- les shaders bindes qui vont bien.
- les uniforms bindes aux shaders
- les texture samplers bindes aux shaders (texture + sampler state)
- les vertex buffers bindes aux shaders
- un draw call

on appelle tout ce qui n'est pas un draw call dans la liste d'au dessus, un state change.

les state changes, c'est couteux, certains plus que d'autres.
le cout peut aussi varier en fonction de la nature des donnees que tu set, en plus du type de state-change.

par exemple quand tu binde un vertex buffer, suivant son mode de residence en memoire, ca va declencher un upload depuis la system-memory jusqu'en video-memory, et ca, c'est.. heu.. super lent grin si t'as un gros vertex buffer.
mais ca c'est un sujet a part entiere, alors jvais ptet pas trop m'attarder dessus grin

BREF

or donc, tout ca, ca coute cher. notamment l'upload d'un nouveau shader. pas trop parceque le shader represente beaucoup de donnees a uploader, mais surtout parceque ca invalide plein de trucs.
idem pour les textures.

rebinder a chaque drawcall le shader, les textures, les uniforms, les vertex buffers, bah.. ca fait mal niveau perfs.

c'est entre autres pour ca qu'un seul gros shaders de porc, dans certains cas, ca a ses avantages.

ceci dit c'est particulierement goret, et les perfs du dit shader peuvent etre particulierement mauvaises si t'as envie de gerer des effets sympas.

donc typiquement, la plupart des moteurs trient leurs drawcalls par state-change.
genre ils groupent tous les drawcalls qui ont le meme shader ensembles.
puis au sein de tous les draw calls d'un meme shader, ceux qui partagent les memes textures, etc...
du coup ils changent de shaders qu'une fois tous les 'n' drawcalls.

une version plus elegante, et a la mode, est d'avoir un gros ID pour chaque drawcall, 32 ou 64 bits:

par exemple, hh utilise ce layout la (copier/coller direct du header)

//----------------------------------------------------------------------------
//
//	Key layout:
//
//	64              48                                              0
//	|---------------------------------------------------------------|
//	|        |      |           24          |           24          |
//	|---------------------------------------------------------------|
//	   Scene   Pass           Depth                Material ID
//
//	depths are the distance from the viewpoint to the object. they are always positive.
//	they are packed in cropped fp32 format in the 24 bits as follows:
//
//	hh_u32	depth24 = (FpToBits(fpVal) & 0x7FFFFF80) >> 7
//	float	depth = FpFromBits<float>(depth24 << 7);
//
//	static const hh_u64	depthKeyMask = 0x0000FFFFFF000000ULL;
//	hh_u64	depth24_64 = ((hh_u64)(FpToBits(fpVal) & 0x7FFFFF80)) << (1 + 16);
//
//	update depth:
//	key = (key & ~depthKeyMask) | depth24_64;
//
//----------------------------------------------------------------------------


bon, c'est pas non plus _forcement_ un bon exemple a reprendre tel quel grin

la du coup, ca trie tous les batch d'abord par scene, puis par passe (opaques/transparentes), puis par profondeur (d'avant en arriere pour les batch opaques, d'arriere en avant pour les batch transparents), puis par ID de materiau (qui comprend les renderstates, les textures, les uniforms..)

ca pourrait etre mieux en utilisant moins de bits pour les scenes/pass, et puis de bits pour le material ID, et en utilisant certains bits du material ID pour encoder des renderstates partagees par != materiaux.. enfin bref.

(question 3 incoming /o/)