Düzenli aralıklarla XNA kekemeliği


10

Donanım örneklemesi yapmaya çalışıyorum ama bazı garip performans sorunlarına çarpıyorum. Ortalama kare hızı yaklaşık 45'tir, ancak son derece dalgalı.

  • Pencereli
  • SynchronizeWithVerticalRetrace = yanlış
  • IsFixedTimeStep = yanlış
  • PresentationInterval = PresentInterval.Immediate

Aşağıdaki resim ölçülen zamanlamamı (ile Stopwatch) göstermektedir. En üstteki grafik, Drawyöntemde harcanan süredir ve en alttaki grafik, sonundan Drawbaşlangıcına kadar geçen süredir.Update Çizim ve xna zamanlaması

Sivri uçlar neredeyse tam olarak 1 saniye aralıklıdır ve her zaman normal zamanın 2,3,4 veya 5 katıdır. Başaktan hemen sonra gelen çerçeveler hiç zaman almaz. Çöp toplayıcı olmadığını kontrol ettim.

Şu anda 1 üçgen örnekle 12 üçgen ve 36 köşeden oluşan bir kafes oluşturuyorum (bunun optimal olmadığını biliyorum, ama sadece test etmek için). Örnekleme çağrılarını 250 örneklik küçük parçalara topluyorsam, her sorun hafifletilir, ancak cpu kullanımı önemli ölçüde artar. Yukarıdaki çalışma, çekiliş başına 10000 cpu'dadır, bu da cpu'da çok daha kolaydır.

Oyunu tam ekran olarak çalıştırırsam, alt grafik neredeyse yok, ancak aynı sorun şimdi Drawyöntemde ortaya çıkıyor.

İşte PIX'in içinde , benim için hiç mantıklı olmayan bir koşu . Görünüşe göre bazı kareler için render yok ...

Herhangi bir fikir, buna ne sebep olabilir?

EDIT : İstendiği gibi, render kodunun ilgili bölümleri

A CubeBufferoluşturulur ve başlatılır, daha sonra küplerle doldurulur. Küp miktarı belirli bir sınırın üzerindeyse, yeni CubeBufferbir tane oluşturulur, vb. Her arabellek tüm örnekleri tek bir çağrıda çizer.

Sadece bir kez ihtiyaç duyulan bilgiler static(tepe noktası, dizin arabellek ve tepe noktası bildirimi; şimdiye kadar hiçbir fark yaratmasa da). Doku 512x512

Çizmek()

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();
 }
}

Çekilişin sonundan güncellemenin başlamasına kadar geçen zamanla ilgili olup olmadığından emin değilim, çünkü güçlü bir şekilde bağlı değiller (yani oyun yavaş çalışıyorsa 2 çekiliş arasında birçok güncelleme gerçekleşebilir, ki siz çalışmıyorsunuz. 60 fps'de). Hatta ayrı iş parçacıkları da çalıştırabilir (ama bundan emin değilim).
Zonko

Hiçbir gerçek ipucu atm var, ama daha küçük toplu iş ile toplu kod ile ilgili bir sorun görünür, ilgili XNA ve HLSL kodunu göndermek böylece daha yakından bakabilirsiniz @Zonko ile IsFixedTimeStep = False 1: 1 güncelleme var / telefon görüşmesi
Daniel Carlsson

İşte bu kekemelik (XNA takımında) Shawn Hargreaves dan olur neden için bir açıklama var: forums.create.msdn.com/forums/p/9934/53561.aspx#53561
NexAddo

Yanıtlar:


3

Performansınızın GPU'ya bağlı olduğunu tahmin ediyorum. Grafik cihazınızdan, her bir birim zaman için işleyebileceğinden daha fazla iş yapmasını istersiniz; Çerçeve başına 36 milyon köşe oldukça iyi bir sayıdır ve donanım örnekleme aslında denklemin GPU tarafında gerekli işleme işi miktarını artırabilir. Daha az çokgen çizin.

Toplu iş boyutunu azaltmak neden sorunu ortadan kaldırıyor? CPU'nun bir çerçeveyi işlemesi daha uzun sürdüğü Present()için, GPU'nun oluşturmayı bitirmesini beklemek için oturmak için daha az zaman harcıyor . Draw()Aramalarınızın sonunda bu boşluk sırasında yaptıklarını düşünüyorum .

Boşlukların belirli zamanlamasının arkasındaki neden, kodun tamamını anlamadan ilahi olmak daha zordur, ancak bunun da önemli olduğundan emin değilim. CPU üzerinde daha fazla çalışma veya GPU üzerinde daha az çalışma yapın, böylece iş yükünüz daha az düzensiz olur.

Daha fazla bilgi için Shawn Hargreaves'in blogundaki bu makaleye bakın .


2
Kesinlikle GPU'ya bağlı. Uygulamaya, farklı çizim yöntemlerini keşfetmek için, aslında bir kriterdir. Aynı miktarda köşeye sahip daha küçük bir toplu iş boyutu CPU'da daha uzun sürebilir, ancak GPU yükü aynı olmalıdır, değil mi? En azından yüke bağlı olarak çerçeveler arasında tutarlı bir süre beklerim (yükler arasında hiç değişmez) ve bu tür düzenli gecikme ve anlık aralıklar (veya oluşturma yok, bkz. PIX).
Darcara

Grafiklerinizi doğru şekilde yorumluyorsam, anında oluşturulan kareler XNA Framework'ün işlevselliğinin bir parçasıdır. Olarak IsFixedTimeStepayarlandığında false, Oyun çok yavaş çalışıyorsa, XNA Update()yakalamak için arka arkaya birkaç kez çağıracak ve işlemdeki kareleri kasıtlı olarak düşecektir . IsRunningSlowlyBu kareler sırasında true olarak ayarlanmış mı ? Garip zamanlamaya gelince - bu beni biraz meraklandırıyor. Pencereli modda mı çalışıyorsunuz? Davranış tam ekran modunda devam ediyor mu?
Cole Campbell

yakalama çağrıları yalnızca tarihinde gerçekleşir IsFixedTimeStep=true. Alttaki grafik, çizimimin sonu ile bir sonraki karenin güncelleme çağrısının başlangıcı arasındaki süreyi gösterir. Çerçeveler düşmüyor, çizme yöntemlerini çağırıyorum ve onlar için CPU fiyatını ödüyorum (üst grafik). Tam ekran ve çözünürlükler arasında aynı davranış.
Darcara

Haklısın, benim hatam. Korkarım bu noktada fikirlerimi tükettim.
Cole Campbell

2

Sanırım bir çöp probleminiz var ... belki de birçok nesneye yaratıyorsunuz / yok ediyorsunuz ve sivri çöp toplayıcı rutinleri çalışıyor ...

tüm bellek yapılarınızı yeniden kullandığınızdan emin olun ... ve 'yeni' yi çok sık kullanmayın


ProcessExplorer ve CLRProfiler'da zaten kontrol edildi ve gc, her 10 saniyede bir gibi çalışıyor ve neredeyse 75ms kadar uzun değil.
Darcara

1
Kesinlikle oluşturma işleminizin yavaşlamasına neden olup olmadığına bakılmaksızın her kareyi yeni bir RasterizerState, BlendState ve DepthStencilState oluşturmak istemezsiniz. Bu makaleye göre kesinlikle yardımcı olmayacaktır blogs.msdn.com/b/shawnhar/archive/2010/04/02/… Yükte bir kez kullanacağınız durumu oluşturmanız ve gerektiğinde yeniden uygulamanız gerekir.
dadoo Games
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.