Заїкання XNA через рівні проміжки часу


10

Я намагаюся зробити апаратні інстанції, але я стикаюся з якоюсь дивною проблемою продуктивності. Середня частота кадрів - близько 45, але це надзвичайно спритно.

  • Віконний
  • SynchronizeWithVerticalRetrace = false
  • IsFixedTimeStep = false
  • PresentationInterval = PresentInterval.Immediate

На зображенні нижче показано моє вимірювання часу (з Stopwatch). Найвищий графік - це час, проведений у Drawметоді, а нижній - час від кінця Drawдо початкуUpdate Намалюйте та хна часу

Шипи майже рівно за 1 секунду і завжди у 2,3,4 або 5 разів перевищують звичайний час. Кадри, що безпосередньо слідують за шипом, зовсім не займають часу. Я перевірив, що це не смітник.

Наразі я створюю сітку, що складається з 12 трикутників і 36 вершин як список трикутників (я знаю, що це не оптимально, але це лише для тестування) з 1 мільйоном екземплярів. Якщо я збираю миттєві дзвінки з вилученням невеликих частин по 250 екземплярів, кожна проблема усувається, але використання процесора значно збільшується. Виконання вище - 10000 екземплярів за виклик, який набагато простіше на процесорі.

Якщо я запускаю гру на повноекранному екрані, нижній графік майже не існує, але така ж проблема виникає і в Drawметоді.

Ось пробіг всередині PIX , який для мене взагалі не має сенсу. Здається, для деяких кадрів рендерінг не зроблений ...

Будь-яка ідея, що може бути причиною цього?

EDIT : За потребою, відповідні частини коду візуалізації

А CubeBufferстворюється та ініціалізується, потім заповнюється кубиками. Якщо кількість кубів перевищує певну межу, створюється нова CubeBufferтощо. Кожен буфер малює всі екземпляри за один виклик.

Інформація, необхідна лише один раз, є static(вершина, буфер індексів і декларація вершин; хоча це поки що не має жодної різниці). Текстура - 512x512

Нічия ()

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

Шейдер

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

Я не впевнений, чи це стосується часу від кінця розіграшу до початку оновлення, оскільки вони не сильно пов'язані (тобто багато оновлень можуть відбуватися між двома нічиями, якщо гра запускається повільно, що повинно бути так, оскільки ви не працюєте при 60 кадрів в секунду). Вони можуть навіть працювати в окремих потоках (але я не впевнений у цьому).
Зонко

У мене немає реального підказки atm, але якщо він працює з меншими партіями, його, мабуть, проблема з вашим пакетним кодом, опублікуйте відповідний код XNA та HLSL, щоб ми могли уважніше придивитись до нього @ Zonko з IsFixedTimeStep = False є оновлення 1: 1 / нічия дзвінків
Даніель Карлссон

Ось пояснення, чому таке заїкання відбувається від Шона Харгрівса (у команді xna): forums.create.msdn.com/forums/p/9934/53561.aspx#53561
NexAddo

Відповіді:


3

Я здогадуюсь, що ваша продуктивність пов'язана з GPU. Ви просто просите ваш графічний пристрій виконати більше роботи за одиницю часу, ніж він здатний обробляти; 36 мільйонів вершин на кадр - це досить пристойне число, а апаратне інстанція може насправді збільшити обсяг роботи, необхідної на графічному рівні рівняння. Намалюйте менше багатокутників.

Чому зменшення розміру партії усуває проблему? Оскільки це дозволяє процесору зайняти більше часу для обробки кадру, а це означає, що він витрачає менше часу, сидячи всередині, Present()чекаючи, поки GPU закінчить візуалізацію. Це я думаю, що це робиться під час цього розриву наприкінці ваших Draw()дзвінків.

Причину, що стоїть у конкретних строках прогалин, важче визначити, не розуміючи цілого коду, але я також не впевнений, що це важливо. Робіть більше роботи над процесором або менше працюйте на графічному процесорі, щоб ваші навантаження були менш нерівномірними.

Дивіться цю статтю в блозі Shawn Hargreaves для отримання додаткової інформації.


2
Це, безумовно, пов'язане з GPU. Додаток, по суті, є орієнтиром для вивчення різних методів малювання. Менший розмір партії з однаковою кількістю намальованих вершин зайняв би більше процесора, але завантаження GPU повинно бути однаковим, ні? Принаймні, я б очікував послідовного часу між кадрами, залежно від навантаження (яке взагалі не змінюється між кадрами) та не таких регулярних інтервалів затримки та миттєвого (або відсутність візуалізації, див. PIX).
Даркара

Якщо я правильно інтерпретую ваші графіки, миттєво надані кадри є частиною функціоналу XNA Framework. Якщо IsFixedTimeStepвстановлено значення " falseЯкщо гра запускається занадто повільно", XNA зателефонує Update()кілька разів поспіль, щоб наздогнати її, свідомо скидаючи кадри в процесі. Чи IsRunningSlowlyвстановлено значення true під час цих кадрів? Що стосується дивних термінів - це мене змушує трохи замислитися. Ви працюєте у віконному режимі? Чи зберігається поведінка в повноекранному режимі?
Коул Кемпбелл

виклики підключення трапляються лише на IsFixedTimeStep=true. Нижній графік показує час між кінцем моєї розіграші та початком виклику оновлення наступного кадру. Кадри не скидаються, я закликаю методи малювання і плачу за них ціну ЦП (верхній графік). Однакова поведінка на повноекранному екрані та в різних роздільних здатностях.
Даркара

Ти маєш рацію, моя помилка. Боюся, що я вичерпав свої ідеї в цей момент.
Коул Кемпбелл

2

Я думаю, у вас є проблема зі сміттям ... можливо, ви створюєте / знищуєте багато об’єктів, і шипи - це звичайний режим збирання сміття ...

не забудьте використати всі ваші структури пам'яті ... і не використовуйте "нові" занадто часто


Вже перевірено, що в ProcessExplorer і CLRProfiler, і gc працює як раз на 10 секунд і не майже до 75 мс.
Даркара

1
Ви точно не хочете створювати нові RasterizerState, BlendState та DepthStencilState кожен кадр незалежно від того, є причиною уповільнення рендерінгу чи ні. Це, безумовно, не допоможе, згідно з цією статтею blogs.msdn.com/b/shawnhar/archive/2010/04/02/… Ви повинні створити стан, який ви будете використовувати один раз під час завантаження, і повторно застосувати їх при необхідності.
dadoo Games
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.