Estou tentando instalar o hardware, mas estou encontrando algum problema de desempenho estranho. A taxa de quadros média é de cerca de 45, mas é extremamente instável.
- Windowed
- SynchronizeWithVerticalRetrace = false
- IsFixedTimeStep = false
- PresentationInterval = PresentInterval.Immediate
A imagem abaixo mostra meu tempo medido (com Stopwatch
). O gráfico superior é o tempo gasto no Draw
método e o gráfico inferior é o tempo desde o final Draw
até o início deUpdate
Os picos têm quase exatamente um segundo de intervalo e sempre são 2,3,4 ou 5 vezes o tempo normal. Os quadros imediatamente após o pico não demoram nada. Eu verifiquei que não é o coletor de lixo.
Atualmente, estou instanciando uma malha que consiste em 12 triângulos e 36 vértices como uma lista de triângulos (eu sei que não é o ideal, mas é apenas para teste) com 1 milhão de instâncias. Se eu fizer um lote, as instâncias de chamadas de instanciamento em pequenas partes de 250 instâncias, cada um, o problema é aliviado, mas o uso da CPU aumenta significativamente. A execução acima é de 10000 instâncias por chamada de empate, o que é muito mais fácil na CPU.
Se eu rodar o jogo em tela cheia, o gráfico inferior é quase inexistente, mas o mesmo problema ocorre agora no Draw
método.
Aqui está uma corrida dentro do PIX , que não faz sentido para mim. Parece que para alguns quadros não há renderização feita ...
Alguma idéia, o que pode estar causando isso?
EDIT : conforme solicitado, as partes relevantes do código de renderização
A CubeBuffer
é criado e inicializado e preenchido com cubos. Se a quantidade de cubos estiver acima de um determinado limite, um novo CubeBuffer
será criado e assim por diante. Cada buffer desenha todas as instâncias em uma chamada.
As informações necessárias apenas uma vez são static
(vértice, buffer de índice e declaração de vértice; embora não faça diferença até agora). A textura é 512x512
Desenhar()
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();
}
}
fonte
Respostas:
Suponho que seu desempenho seja vinculado à GPU. Você está simplesmente pedindo ao seu dispositivo gráfico que trabalhe mais por unidade de tempo do que é capaz de lidar; 36 milhões de vértices por quadro é um número bastante decente, e a instalação de hardware pode realmente aumentar a quantidade de trabalho de processamento necessário no lado da equação da GPU. Desenhe menos polígonos.
Por que reduzir o tamanho do lote faz com que o problema desapareça? Porque a CPU demora mais para processar um quadro, o que significa que está gastando menos tempo
Present()
aguardando a GPU concluir a renderização. É o que acho que está fazendo durante essa lacuna no final de suasDraw()
ligações.A razão por trás do momento específico das lacunas é mais difícil de adivinhar sem entender todo o código, mas também não tenho certeza de que seja importante. Faça mais trabalho na CPU ou menos na GPU, para que sua carga de trabalho seja menos desigual.
Veja este artigo no blog de Shawn Hargreaves para obter mais informações.
fonte
IsFixedTimeStep
definido comofalse
, se o jogo estiver rodando muito lentamente, o XNA chamaráUpdate()
várias vezes seguidas para recuperar o atraso, eliminando deliberadamente quadros no processo. ÉIsRunningSlowly
definido como verdadeiro durante esses quadros? Quanto ao momento estranho - isso me faz pensar um pouco. Você está executando no modo janela? O comportamento persiste no modo de tela cheia?IsFixedTimeStep=true
. O gráfico inferior mostra o tempo entre o final do meu sorteio e o início da chamada de atualização do próximo quadro. Os quadros não são descartados, chamo os métodos de desenho e pago o preço da CPU por eles (gráfico superior). Mesmo comportamento em tela cheia e em resoluções.Eu acho que você tem um problema com o lixo ... talvez você esteja criando / destruindo muitos objetos e que os picos sejam a rotina do coletor de lixo funcionando ...
certifique-se de reutilizar todas as suas estruturas de memória ... e não use 'novo' com muita frequência
fonte