J'ai souvent tendance à faire beaucoup d'optimisation prématurée lorsque je traite des graphiques. Il y a quelques principes que j'essaie toujours de suivre:
- Gardez le nombre de composants D3D au minimum. (États de rendu, tampons, shaders, etc.)
- Ne liez les composants qu'en cas de nécessité absolue. (Pas déjà lié, etc.)
- Spécialisez les composants autant que possible. (Définissez uniquement les BindFlags nécessaires, etc.)
Cela m'a amené à construire des wrappers très élaborés pour gérer les composants créés et l'état de pipelage actuel. Non seulement cela consomme beaucoup de mon précieux temps de développement, mais cela ajoute également une autre grande couche de complexité.
Et le pire de tout: je ne sais même pas si cela en vaut la peine.
Certaines de mes considérations d'optimisation peuvent déjà être implémentées à un niveau inférieur et je ne fais que les reproduire, ce qui me fait perdre du temps sur le CPU. D'autres considérations peuvent être totalement inutiles, car l'effet sur les performances est négligeable.
Mes questions sont donc:
- Lesquelles des directives ci-dessus sont valables et dans quelle mesure dois-je les suivre?
- Comment le GPU gère-t-il les changements d'état?
- Que se passe-t-il si je change un état qui n'est jamais utilisé? (Aucun appel de tirage n'est effectué pendant qu'il est actif.)
- Quelles sont les pénalités de performances réelles pour lier les différents composants différents?
- Quelles autres considérations de performances devraient être prises?
S'il vous plaît, ne me dites pas simplement que je ne devrais pas me soucier des performances jusqu'à ce que j'atteigne les limites réelles. Bien que cela soit évidemment vrai d'un point de vue pratique, je m'intéresse principalement à la théorie. J'ai en quelque sorte besoin de lutter contre l'envie de construire le cadre graphique optimal et je ne pense pas que je puisse le faire avec la "conférence d'optimisation prématurée" habituelle.
Gérer les composants
J'écris actuellement des applications DirectX 11 en C # en utilisant SlimDX comme wrapper managé. C'est un wrapper de très bas niveau et mon abstraction actuelle est construite dessus.
Il existe certains avantages évidents lors de l'utilisation d'une abstraction Direct3D. Configurer l'environnement, charger des shaders, définir des constantes et dessiner un maillage est beaucoup plus simple et utilise beaucoup moins de code. De plus, comme il gère la création et l'élimination de la plupart des composants, ils peuvent être automatiquement réutilisés partout et j'évite presque complètement les fuites de mémoire.
- Comment gérez-vous généralement tous les composants et ressources graphiques?
- Pouvez-vous recommander des wrappers gérés faisant quelque chose de similaire à mon exemple ci-dessous?
Voici un exemple de mon implémentation actuelle. Je suis assez content de l'interface. Il a suffisamment de flexibilité pour mes besoins et est très simple à utiliser et à comprendre:
// Init D3D environment
var window = new RenderForm();
var d3d = new Direct3D(window, GraphicsSettings.Default);
var graphics = new GraphicsManager(d3d.Device);
// Load assets
var mesh = GeometryPackage.FromFile(d3d, "teapot.gp");
var texture = Texture.FromFile(d3d, "bricks.dds");
// Render states
graphics.SetViewports(new Viewport(0, 0, 800, 600);
graphics.SetRasterizer(wireFrame: false, culling: CullMode.Back);
graphics.SetDepthState(depthEnabled: true, depthWriteEnabled: true);
graphics.SetBlendState(BlendMethod.Transparency);
// Input layout
graphics.SetLayout("effect.fx", "VS", "vs_4_0",
new InputElement("POSITION", 0, Format.R32G32B32_Float, 0),
new InputElement("TEXCOORD", 0, Format.R32G32_Float, 0)
);
// Vertex shader
graphics.SetShader(Shader.Vertex, "effect.fx", "VS", "vs_4_0");
graphics.SetConstants(Shader.Vertex, 0, 4, stream => stream.Write(wvpMatrix));
// Pixel shader
graphics.SetShader(Shader.Pixel, "effect.fx", "PS", "ps_4_0");
graphics.SetTexture(Shader.Pixel, 0, texture);
graphics.SetSampler(Shader.Pixel, 0, Sampler.AnisotropicWrap);
graphics.SetConstants(Shader.Pixel, 0, 1, stream => stream.Write(new Color4(1, 0, 1, 0);
d3d.Run(() =>
{
// Draw and present
d3d.BackBuffer.Clear(new Color4(1, 0, 0.5f, 1));
graphics.SetOutput(d3d.BackBuffer);
graphics.Draw(mesh);
d3d.Present();
}