A menudo tiendo a hacer muchas optimizaciones prematuras cuando trato con gráficos. Hay algunos principios que siempre trato de seguir:
- Mantenga el número de componentes D3D al mínimo. (Estados de renderizado, buffers, sombreadores, etc.)
- Solo unir componentes si es absolutamente necesario. (No limitado ya, etc.)
- Especialice los componentes tanto como sea posible. (Solo configure BindFlags necesarios, etc.)
Esto me llevó a construir envoltorios muy elaborados para administrar los componentes creados y el estado actual de las tuberías. Esto no solo consume mucho de mi valioso tiempo de desarrollo, sino que también agrega otra gran capa de complejidad.
Y lo peor de todo: ni siquiera sé si vale la pena.
Algunas de mis consideraciones de optimización ya pueden implementarse en un nivel inferior y solo las estoy replicando, además de perder tiempo en la CPU. Otras consideraciones pueden ser completamente innecesarias, ya que el efecto sobre el rendimiento es insignificante.
Entonces mis preguntas son:
- ¿Cuáles de las pautas anteriores son válidas y en qué medida debo seguirlas?
- ¿Cómo maneja la GPU los cambios de estado?
- ¿Qué sucede si cambio un estado que nunca se usa? (No se realiza ninguna llamada de sorteo mientras está activa).
- ¿Cuáles son las sanciones de rendimiento reales por vincular los diferentes componentes?
- ¿Qué otras consideraciones de rendimiento deben hacerse?
Por favor, no me diga simplemente que no debería importarme el rendimiento hasta que alcance los límites reales. Si bien eso es obviamente cierto desde un punto de vista práctico, estoy principalmente interesado en la teoría. De alguna manera necesito combatir el impulso de construir el marco gráfico óptimo y no creo que pueda hacerlo con la habitual "conferencia de optimización prematura".
Administrar componentes
Actualmente estoy escribiendo aplicaciones DirectX 11 en C # usando SlimDX como contenedor administrado. Es un contenedor de muy bajo nivel y mi abstracción actual se basa en él.
Hay algunas ventajas obvias cuando se usa una abstracción Direct3D. Configurar el entorno, cargar sombreadores, configurar constantes y dibujar una malla es mucho más simple y utiliza mucho menos código. Además, dado que gestiona la creación y eliminación de la mayoría de los componentes, pueden reutilizarse automáticamente en todas partes y evito casi por completo las pérdidas de memoria.
- ¿Cómo gestiona habitualmente todos los componentes y recursos gráficos?
- ¿Me puede recomendar algún contenedor administrado que haga algo similar a mi ejemplo a continuación?
Aquí hay un ejemplo de mi implementación actual. Estoy bastante contento con la interfaz. Tiene suficiente flexibilidad para mis necesidades y es muy simple de usar y comprender:
// 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();
}