Gagap XNA secara berkala

10

Saya mencoba melakukan pemasangan perangkat keras tetapi saya memukul beberapa masalah kinerja yang aneh. Framerate rata-rata sekitar 45, tapi sangat berombak.

  • Terang
  • SynchronizeWithVerticalRetrace = false
  • IsFixedTimeStep = false
  • PresentationInterval = PresentInterval.Immediate

Gambar di bawah ini menunjukkan waktu pengukuran saya (dengan Stopwatch). Grafik paling atas adalah waktu yang dihabiskan dalam Drawmetode dan grafik bawah adalah waktu dari akhir Drawhingga awalUpdate Gambar dan waktu xna

Paku hampir persis 1 detik terpisah dan selalu 2,3,4 atau 5 kali dari waktu biasanya. Bingkai segera setelah lonjakan tidak membutuhkan waktu sama sekali. Saya telah memeriksa bahwa itu bukan pemulung.

Saat ini saya sedang memasang mesh yang terdiri dari 12 segitiga dan 36 simpul sebagai daftar segitiga (saya tahu itu tidak optimal, tetapi itu hanya untuk pengujian) dengan 1 juta instance. Jika saya mengumpulkan panggilan penarikan instans ke dalam bagian-bagian kecil dari 250 contoh masing-masing masalah teratasi, tetapi penggunaan cpu meningkat secara signifikan. Proses di atas adalah pada contoh 10.000 per panggilan undian, yang jauh lebih mudah di cpu.

Jika saya menjalankan game dalam layar penuh, grafik bawah hampir tidak ada, tetapi masalah yang sama terjadi sekarang dalam Drawmetode.

Berikut ini adalah run di dalam PIX , yang tidak masuk akal bagi saya sama sekali. Tampaknya untuk beberapa frame tidak ada rendering yang dilakukan ...

Ada ide, apa yang menyebabkan ini?

EDIT : Seperti yang diminta, bagian yang relevan dari kode render

A CubeBufferdibuat dan diinisialisasi, lalu diisi dengan kubus. Jika jumlah kubus di atas batas tertentu, yang baru CubeBufferdibuat, dan sebagainya. Setiap buffer menarik semua instance dalam satu panggilan.

Informasi yang diperlukan hanya sekali saja static(vertex, indeks buffer dan deklarasi vertex; meskipun sejauh ini tidak ada perbedaan). Teksturnya 512x512

Seri()

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();
 }
}
Darcara
sumber
Saya tidak yakin apakah itu relevan dengan waktu dari akhir pengundian hingga awal pembaruan, karena mereka tidak terkait erat (mis. Banyak pembaruan dapat terjadi di antara 2 pengundian jika permainan berjalan lambat, yang harus terjadi karena Anda tidak menjalankan pada 60 fps). Mereka bahkan mungkin berjalan di utas terpisah (tapi saya tidak yakin tentang ini).
Zonko
Saya tidak memiliki petunjuk atm nyata, tetapi jika ia bekerja dengan batching yang lebih kecil tampaknya ada masalah dengan kode batching Anda, posting kode XNA dan HLSL yang relevan sehingga kita dapat melihat lebih dekat padanya @Zonko dengan IsFixedTimeStep = Salah ada pembaruan 1: 1 / buat panggilan
Daniel Carlsson
Berikut adalah penjelasan mengapa gagap ini terjadi dari Shawn Hargreaves (di tim xna): forums.create.msdn.com/forums/p/9934/53561.aspx#53561
NexAddo

Jawaban:

3

Saya menduga bahwa kinerja Anda terikat GPU. Anda hanya meminta perangkat grafis Anda untuk melakukan lebih banyak pekerjaan per unit waktu daripada yang mampu ditangani; 36 juta simpul per bingkai adalah angka yang cukup baik, dan pemasangan perangkat keras sebenarnya dapat meningkatkan jumlah pekerjaan pemrosesan yang diperlukan pada sisi GPU dari persamaan tersebut. Gambar poligon lebih sedikit.

Mengapa mengurangi ukuran bets membuat masalah hilang? Karena itu membuat CPU membutuhkan waktu lebih lama untuk memproses sebuah bingkai, yang berarti menghabiskan lebih sedikit waktu Present()untuk menunggu di dalam menunggu GPU untuk menyelesaikan rendering. Itulah yang saya pikir sedang dilakukan selama jeda di akhir Draw()panggilan Anda .

Alasan di balik waktu spesifik celah lebih sulit untuk ilahi tanpa memahami seluruh kode, tapi saya juga tidak yakin itu penting. Lakukan lebih banyak pekerjaan pada CPU, atau kurangi kerja pada GPU, sehingga beban kerja Anda tidak merata.

Lihat artikel ini di blog Shawn Hargreaves untuk informasi lebih lanjut.

Cole Campbell
sumber
2
Sudah pasti GPU terikat. Aplikasi ini pada dasarnya adalah tolok ukur, untuk menjelajahi berbagai metode menggambar. Ukuran batch yang lebih kecil dengan jumlah simpul yang sama akan memakan waktu lebih lama pada CPU, tetapi beban GPU harus sama, bukan? Setidaknya saya akan mengharapkan waktu yang konsisten antara frame, tergantung pada beban (yang tidak berubah di antara frame sama sekali) dan tidak seperti interval lag dan instan (atau tanpa rendering, lihat PIX).
Darcara
Jika saya menafsirkan grafik Anda dengan benar, frame yang diberikan langsung adalah bagian dari fungsionalitas XNA Framework. Dengan IsFixedTimeStepset ke false, Jika game berjalan terlalu lambat, XNA akan memanggil Update()beberapa kali berturut-turut untuk mengejar ketinggalan, dengan sengaja menjatuhkan frame dalam proses. Apakah IsRunningSlowlydisetel ke true selama bingkai ini? Adapun waktu yang aneh - itu membuat saya sedikit heran. Apakah Anda menjalankan dalam mode berjendela? Apakah perilaku tetap dalam mode layar penuh?
Cole Campbell
panggilan catchup hanya terjadi pada IsFixedTimeStep=true. Grafik bawah menunjukkan waktu antara akhir pengundian saya dan awal panggilan pembaruan dari frame berikutnya. Frame tidak jatuh, saya memanggil metode draw dan membayar harga CPU untuk mereka (grafik atas). Perilaku yang sama dalam layar penuh dan melintasi resolusi.
Darcara
Anda benar, kesalahan saya. Saya khawatir saya sudah kehabisan ide saya pada saat ini.
Cole Campbell
2

Saya pikir Anda memiliki masalah sampah ... mungkin Anda membuat / menghancurkan banyak objek dan bahwa paku adalah rutinitas pengumpul sampah yang bekerja ...

pastikan untuk menggunakan kembali semua struktur memori Anda ... dan jangan terlalu sering menggunakan 'baru'

Blau
sumber
Sudah memeriksa itu di ProcessExplorer dan CLRProfiler, dan gc berjalan seperti sekali setiap 10 detik dan tidak sepanjang 75ms.
Darcara
1
Anda pasti tidak ingin membuat RasterizerState, BlendState, dan DepthStencilState baru setiap frame terlepas dari apakah itu penyebab perlambatan render Anda. Ini pasti tidak akan membantu, sesuai artikel ini blogs.msdn.com/b/shawnhar/archive/2010/04/02/… Anda harus membuat status yang akan Anda gunakan sekali di muat dan mengajukan permohonan kembali jika diperlukan.
Permainan dadoo