Estoy tratando de hacer instancias de hardware pero me encuentro con un extraño problema de rendimiento. La velocidad de fotogramas promedio es de alrededor de 45, pero es extremadamente entrecortada.
- Ventana
- SynchronizeWithVerticalRetrace = false
- IsFixedTimeStep = false
- PresentationInterval = PresentInterval.Immediate
La imagen a continuación muestra mi tiempo medido (con Stopwatch
). El gráfico superior es el tiempo empleado en el Draw
método y el gráfico inferior es el tiempo desde el final Draw
hasta el comienzo deUpdate
Los picos están separados casi exactamente 1 segundo y siempre son 2,3,4 o 5 veces el tiempo habitual. Los cuadros que siguen inmediatamente a la espiga no tardan en absoluto. He comprobado que no es el recolector de basura.
Actualmente estoy instalando una malla que consta de 12 triángulos y 36 vértices como una lista de triángulos (sé que no es óptima, pero es solo para probar) con 1 millón de instancias. Si agrupo las llamadas de sorteo de instancias en pequeñas partes de 250 instancias cada una, el problema se alivia, pero el uso de la CPU aumenta significativamente. La ejecución anterior es de 10000 instancias por llamada de sorteo, que es mucho más fácil en la CPU.
Si ejecuto el juego en pantalla completa, el gráfico inferior es casi inexistente, pero el mismo problema ocurre ahora en el Draw
método.
Aquí hay una ejecución dentro de PIX , que no tiene ningún sentido para mí. Parece que para algunos cuadros no se realiza ninguna representación ...
¿Alguna idea de lo que podría estar causando esto?
EDITAR : según lo solicitado, las partes relevantes del código de representación
A CubeBuffer
se crea e inicializa, luego se llena con cubos. Si la cantidad de cubos está por encima de cierto límite, CubeBuffer
se crea uno nuevo , y así sucesivamente. Cada búfer dibuja todas las instancias en una llamada.
La información que se necesita solo una vez es static
(vértice, búfer de índice y declaración de vértice; aunque hasta ahora no hace ninguna diferencia). La textura es 512x512
Dibujar()
device.Clear(Color.DarkSlateGray);
device.RasterizerState = new RasterizerState() { };
device.BlendState = new BlendState { };
device.DepthStencilState = new DepthStencilState() { DepthBufferEnable = true };
//samplerState=new SamplerState() { AddressU = TextureAddressMode.Mirror, AddressV = TextureAddressMode.Mirror, Filter = TextureFilter.Linear };
device.SamplerStates[0] = samplerState
effect.CurrentTechnique = effect.Techniques["InstancingTexColorLight"];
effect.Parameters["xView"].SetValue(cam.viewMatrix);
effect.Parameters["xProjection"].SetValue(projectionMatrix);
effect.Parameters["xWorld"].SetValue(worldMatrix);
effect.Parameters["cubeTexture"].SetValue(texAtlas);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
pass.Apply();
foreach (var buf in CubeBuffers)
buf.Draw();
base.Draw(gameTime);
CuboBuffer
[StructLayout(LayoutKind.Sequential)]
struct InstanceInfoOpt9
{
public Matrix World;
public Vector2 Texture;
public Vector4 Light;
};
static VertexBuffer geometryBuffer = null;
static IndexBuffer geometryIndexBuffer = null;
static VertexDeclaration instanceVertexDeclaration = null;
VertexBuffer instanceBuffer = null;
InstanceInfoOpt9[] Buffer = new InstanceInfoOpt9[MaxCubeCount];
Int32 bufferCount=0
Init()
{
if (geometryBuffer == null)
{
geometryBuffer = new VertexBuffer(Device, typeof (VertexPositionTexture), 36, BufferUsage.WriteOnly);
geometryIndexBuffer = new IndexBuffer(Device, typeof (Int32), 36, BufferUsage.WriteOnly);
vertices = new[]{...}
geometryBuffer.SetData(vertices);
indices = new[]{...}
geometryIndexBuffer.SetData(indices);
var instanceStreamElements = new VertexElement[6];
instanceStreamElements[0] = new VertexElement(sizeof (float)*0, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 1);
instanceStreamElements[1] = new VertexElement(sizeof (float)*4, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 2);
instanceStreamElements[2] = new VertexElement(sizeof (float)*8, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 3);
instanceStreamElements[3] = new VertexElement(sizeof (float)*12, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 4);
instanceStreamElements[4] = new VertexElement(sizeof (float)*16, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 5);
instanceStreamElements[5] = new VertexElement(sizeof (float)*18, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 6);
instanceVertexDeclaration = new VertexDeclaration(instanceStreamElements);
}
instanceBuffer = new VertexBuffer(Device, instanceVertexDeclaration, MaxCubeCount, BufferUsage.WriteOnly);
instanceBuffer.SetData(Buffer);
bindings = new[]
{
new VertexBufferBinding(geometryBuffer),
new VertexBufferBinding(instanceBuffer, 0, 1),
};
}
AddRandomCube(Vector3 pos)
{
if(cubes.Count >= MaxCubeCount)
return null;
Vector2 tex = new Vector2(rnd.Next(0, 16), rnd.Next(0, 16))
Vector4 l= new Vector4((float)rnd.Next(), (float)rnd.Next(), (float)rnd.Next(), (float)rnd.Next());
var cube = new InstanceInfoOpt9(Matrix.CreateTranslation(pos),tex, l);
Buffer[bufferCount++] = cube;
return cube;
}
Draw()
{
Device.Indices = geometryIndexBuffer;
Device.SetVertexBuffers(bindings);
Device.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0, 36, 0, 12, bufferCount);
}
Shader
float4x4 xView;
float4x4 xProjection;
float4x4 xWorld;
texture cubeTexture;
sampler TexColorLightSampler = sampler_state
{
texture = <cubeTexture>;
mipfilter = LINEAR;
minfilter = LINEAR;
magfilter = LINEAR;
};
struct InstancingVSTexColorLightInput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};
struct InstancingVSTexColorLightOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float4 Light : TEXCOORD1;
};
InstancingVSTexColorLightOutput InstancingVSTexColorLight(InstancingVSTexColorLightInput input, float4x4 instanceTransform : TEXCOORD1, float2 instanceTex : TEXCOORD5, float4 instanceLight : TEXCOORD6)
{
float4x4 preViewProjection = mul (xView, xProjection);
float4x4 preWorldViewProjection = mul (xWorld, preViewProjection);
InstancingVSTexColorLightOutput output;
float4 pos = input.Position;
pos = mul(pos, transpose(instanceTransform));
pos = mul(pos, preWorldViewProjection);
output.Position = pos;
output.Light = instanceLight;
output.TexCoord = float2((input.TexCoord.x / 16.0f) + (1.0f / 16.0f * instanceTex.x),
(input.TexCoord.y / 16.0f) + (1.0f / 16.0f * instanceTex.y));
return output;
}
float4 InstancingPSTexColorLight(InstancingVSTexColorLightOutput input) : COLOR0
{
float4 color = tex2D(TexColorLightSampler, input.TexCoord);
color.r = color.r * input.Light.r;
color.g = color.g * input.Light.g;
color.b = color.b * input.Light.b;
color.a = color.a * input.Light.a;
return color;
}
technique InstancingTexColorLight
{
pass Pass0
{
VertexShader = compile vs_3_0 InstancingVSTexColorLight();
PixelShader = compile ps_3_0 InstancingPSTexColorLight();
}
}
fuente
Respuestas:
Supongo que su rendimiento está vinculado a la GPU. Simplemente le está pidiendo a su dispositivo gráfico que haga más trabajo por unidad de tiempo del que es capaz de manejar; 36 millones de vértices por cuadro es un número bastante decente, y las instancias de hardware en realidad pueden aumentar la cantidad de trabajo de procesamiento necesario en el lado de la GPU de la ecuación. Dibuja menos polígonos.
¿Por qué reducir el tamaño del lote hace que el problema desaparezca? Porque hace que la CPU tarde más en procesar un marco, lo que significa que pasa menos tiempo sentado
Present()
esperando a que la GPU termine de renderizarse. Eso es lo que creo que está haciendo durante esa brecha al final de susDraw()
llamadas.La razón detrás del momento específico de las brechas es más difícil de adivinar sin comprender todo el código, pero tampoco estoy seguro de que sea importante. Haga más trabajo en la CPU, o menos trabajo en la GPU, para que su carga de trabajo sea menos desigual.
Consulte este artículo en el blog de Shawn Hargreaves para obtener más información.
fuente
IsFixedTimeStep
ajuste afalse
, si el juego se ejecuta muy lentamente, XNA llamaráUpdate()
varias veces seguidas para ponerse al día, dejando caer fotogramas deliberadamente en el proceso. ¿SeIsRunningSlowly
establece en verdadero durante estos fotogramas? En cuanto al momento extraño, me hace pensar un poco. ¿Estás ejecutando en modo ventana? ¿El comportamiento persiste en modo de pantalla completa?IsFixedTimeStep=true
. El gráfico inferior muestra el tiempo entre el final de mi sorteo y el comienzo de la llamada de actualización del siguiente cuadro. Los marcos no se descartan, llamo a los métodos de extracción y pago el precio de la CPU por ellos (gráfico superior). Mismo comportamiento en pantalla completa y en todas las resoluciones.Creo que tiene un problema de basura ... tal vez está creando / destruyendo muchos objetos y que los picos son la rutina del recolector de basura que funciona ...
asegúrese de reutilizar todas sus estructuras de memoria ... y no use 'nuevo' con demasiada frecuencia
fuente