Próbuję wykonać instancję sprzętową, ale napotykam dziwny problem z wydajnością. Średnia liczba klatek na sekundę wynosi około 45, ale jest bardzo niestabilna.
- Okna
- SynchronizeWithVerticalRetrace = false
- IsFixedTimeStep = false
- PresentationInterval = PresentInterval.Immediate
Poniższy obraz pokazuje mój zmierzony czas (z Stopwatch
). Najwyższy wykres to czas spędzony w Draw
metodzie, a dolny wykres to czas od końca Draw
do początkuUpdate
Skoki są prawie dokładnie w odległości 1 sekundy i zawsze wynoszą 2,3,4 lub 5 razy więcej niż zwykle. Ramki bezpośrednio po kolcu nie zajmują w ogóle czasu. Sprawdziłem, że to nie jest śmieciarz.
Obecnie tworzę siatkę składającą się z 12 trójkątów i 36 wierzchołków jako listę trójkątów (wiem, że nie jest optymalna, ale służy tylko do testowania) z 1 milionem wystąpień. Jeśli wsadowo wywołam losowanie instancji w małych częściach po 250 wystąpień, problem zostanie złagodzony, ale użycie procesora znacznie wzrośnie. Przebieg powyżej wynosi 10000 instancji na każde wywołanie losowania, co jest znacznie łatwiejsze na jednostce centralnej.
Jeśli uruchomię grę na pełnym ekranie, dolny wykres prawie nie istnieje, ale ten sam problem występuje teraz w Draw
metodzie.
Oto przegląd wewnątrz PIX , który nie ma dla mnie żadnego sensu. Wydaje się, że w przypadku niektórych klatek renderowanie nie jest wykonywane ...
Masz pomysł, co może być tego przyczyną?
EDYCJA : zgodnie z żądaniem, odpowiednie części kodu renderowania
A CubeBuffer
jest tworzony i inicjowany, a następnie wypełniany kostkami. Jeśli liczba kostek przekracza określony limit, CubeBuffer
tworzona jest nowa i tak dalej. Każdy bufor rysuje wszystkie wystąpienia w jednym wywołaniu.
Informacje potrzebne tylko raz to static
(wierzchołek, bufor indeksu i deklaracja wierzchołków; jak dotąd nie ma to znaczenia). Tekstura ma wymiary 512 x 512
Remis()
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();
}
}
źródło
Odpowiedzi:
Zgaduję, że twoja wydajność jest związana z GPU. Po prostu prosisz urządzenie graficzne, aby wykonało więcej pracy na jednostkę czasu niż jest w stanie obsłużyć; 36 milionów wierzchołków na ramkę to całkiem przyzwoita liczba, a instancja sprzętowa może faktycznie zwiększyć ilość pracy przetwarzania niezbędną po stronie układu GPU równania. Narysuj mniej wielokątów.
Dlaczego zmniejszenie wielkości partii rozwiązuje problem? Ponieważ powoduje to, że procesor przetwarza ramkę dłużej, co oznacza, że spędza mniej czasu siedząc w środku,
Present()
czekając, aż GPU zakończy renderowanie. Tak myślę, że robi to podczas przerwy na końcu twoichDraw()
połączeń.Powód określonego czasu luk jest trudniejszy do odgadnięcia bez zrozumienia całego kodu, ale nie jestem pewien, czy to ważne. Wykonuj więcej pracy na procesorze lub mniej na GPU, aby obciążenie było mniej nierówne.
Więcej informacji można znaleźć w tym artykule na blogu Shawna Hargreavesa.
źródło
IsFixedTimeStep
ustawieniu nafalse
, jeśli gra działa zbyt wolno, XNA będzie dzwonićUpdate()
wiele razy z rzędu, aby nadrobić zaległości, celowo odrzucając ramki w tym procesie. CzyIsRunningSlowly
w tych ramkach jest ustawiona wartość true? Co do dziwnego wyczucia czasu - zastanawiam się trochę. Czy pracujesz w trybie okienkowym? Czy zachowanie utrzymuje się w trybie pełnoekranowym?IsFixedTimeStep=true
. Dolny wykres pokazuje czas między końcem mojego losowania a początkiem wywołania aktualizacji następnej ramki. Ramki nie są upuszczane, wywołuję metody losowania i płacę za nie cenę procesora (górny wykres). To samo zachowanie na pełnym ekranie i we wszystkich rozdzielczościach.Myślę, że masz problem ze śmieciami ... może tworzysz / niszczysz wiele obiektów i że skoki są rutynową funkcją śmieciarek ...
pamiętaj, aby ponownie użyć wszystkich struktur pamięci ... i nie używaj zbyt często nowych
źródło