J'essaie de faire du matériel, mais je rencontre un problème de performance étrange. Le taux de rafraîchissement moyen est d'environ 45, mais il est extrêmement saccadé.
- Fenêtre
- SynchronizeWithVerticalRetrace = false
- IsFixedTimeStep = false
- PresentationInterval = PresentInterval.Immediate
L'image ci-dessous montre mon timing mesuré (avec Stopwatch
). Le graphique supérieur représente le temps passé dans la Draw
méthode et le graphique inférieur représente le temps écoulé entre la fin Draw
et le début deUpdate
Les pointes sont presque exactement à 1 seconde d'intervalle et sont toujours 2,3,4 ou 5 fois le temps habituel. Les images suivant immédiatement la pointe ne prennent pas de temps du tout. J'ai vérifié que ce n'est pas le garbage collector.
J'installe actuellement un maillage composé de 12 triangles et 36 sommets sous forme de liste de triangles (je sais que ce n'est pas optimal, mais c'est juste pour les tests) avec 1 million d'instances. Si je regroupe les appels de tirage instanciés en petites parties de 250 instances chacune, le problème est atténué, mais l'utilisation du processeur augmente considérablement. L'exécution ci-dessus est à 10000 instances par appel de tirage, ce qui est beaucoup plus facile sur le processeur.
Si je lance le jeu en plein écran, le graphique du bas est presque inexistant, mais le même problème se produit maintenant dans la Draw
méthode.
Voici une course à l'intérieur de PIX , qui n'a aucun sens pour moi. Il semble que pour certaines images, aucun rendu ne soit effectué ...
Une idée, qu'est-ce qui pourrait causer ça?
EDIT : comme demandé, les parties pertinentes du code de rendu
A CubeBuffer
est créé et initialisé, puis rempli de cubes. Si le nombre de cubes est supérieur à une certaine limite, un nouveau CubeBuffer
est créé, etc. Chaque tampon dessine toutes les instances en un seul appel.
Les informations nécessaires une seule fois le sont static
(sommet, tampon d'index et déclaration de sommet; bien que cela ne fasse aucune différence jusqu'à présent). La texture est 512x512
Dessiner()
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);
CubeBuffer
[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();
}
}
la source
Réponses:
Je suppose que vos performances sont liées au GPU. Vous demandez simplement à votre périphérique graphique de faire plus de travail par unité de temps qu'il ne peut en gérer; 36 millions de sommets par image est un nombre assez décent, et l'instanciation matérielle peut en fait augmenter la quantité de travail de traitement nécessaire du côté GPU de l'équation. Dessinez moins de polygones.
Pourquoi la réduction de la taille du lot fait-elle disparaître le problème? Parce que le processeur prend plus de temps pour traiter une image, ce qui signifie qu'il passe moins de temps à
Present()
attendre à l' intérieur du GPU pour terminer le rendu. C'est ce que je pense qu'il fait pendant cet écart à la fin de vosDraw()
appels.La raison derrière le timing spécifique des lacunes est plus difficile à deviner sans comprendre l'ensemble du code, mais je ne suis pas sûr que ce soit important non plus. Faites plus de travail sur le CPU, ou moins de travail sur le GPU, afin que votre charge de travail soit moins inégale.
Consultez cet article sur le blog de Shawn Hargreaves pour plus d'informations.
la source
IsFixedTimeStep
réglé surfalse
, si le jeu est trop lent, XNA appelleraUpdate()
plusieurs fois de suite pour rattraper son retard, laissant tomber délibérément cadres dans le processus. EstIsRunningSlowly
défini sur true pendant ces images? Quant au timing étrange - cela me fait un peu me demander. Courez-vous en mode fenêtré? Le comportement persiste-t-il en mode plein écran?IsFixedTimeStep=true
. Le graphique du bas montre le temps entre la fin de mon tirage et le début de l'appel de mise à jour de l'image suivante. Les frames ne sont pas abandonnées, j'appelle les méthodes de dessin et je paie le prix du CPU pour elles (graphique du haut). Même comportement en plein écran et dans toutes les résolutions.Je pense que vous avez un problème d'ordures ... peut-être que vous créez / détruisez de nombreux objets et que les pointes sont la routine du ramasse-miettes qui fonctionne ...
assurez-vous de réutiliser toutes vos structures de mémoire ... et n'utilisez pas trop souvent de «nouveau»
la source