XNA заикается через равные промежутки времени

10

Я пытаюсь сделать аппаратное копирование, но у меня странная проблема с производительностью. Средняя частота кадров составляет около 45, но это очень изменчивый.

  • Оконный
  • SynchronizeWithVerticalRetrace = false
  • IsFixedTimeStep = false
  • PresentationInterval = PresentInterval.Immediate

На изображении ниже показано мое измеренное время (с Stopwatch). Самый верхний график - это время, проведенное в Drawметоде, а нижний график - это время от конца Drawдо началаUpdate Ничья и время xna

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

В настоящее время я создаю сетку, состоящую из 12 треугольников и 36 вершин в виде списка треугольников (я знаю, что это не оптимально, но только для тестирования) с 1 миллионом экземпляров. Если я сделаю групповые вызовы отрисовки по 250 экземпляров, каждая проблема будет облегчена, но использование ЦП значительно возрастет. Приведенный выше прогон составляет 10000 экземпляров на вызов отрисовки, что намного проще для процессора.

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

Вот бег внутри PIX , который не имеет никакого смысла для меня вообще. Похоже, что для некоторых кадров не выполняется рендеринг ...

Есть идеи, что может быть причиной этого?

РЕДАКТИРОВАТЬ : По запросу соответствующие части кода рендеринга

A CubeBufferсоздается и инициализируется, затем заполняется кубиками. Если количество кубиков превышает определенный предел, создается новый CubeBufferи т. Д. Каждый буфер рисует все экземпляры за один вызов.

Информация, необходимая только один раз: static(вершина, индексный буфер и объявление вершины; хотя пока это не имеет значения). Текстура 512х512

Рисовать()

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
источник
Я не уверен, относится ли это ко времени от конца розыгрыша до начала обновления, поскольку они не сильно связаны между собой (т. Е. Многие обновления могут происходить между двумя розыгрышами, если игра запускается медленно, что должно иметь место, поскольку вы не запускаете при 60 кадрах в секунду). Они могут даже работать в отдельных потоках (но я не уверен в этом).
Зонко
У меня нет никакой реальной подсказки, но если он работает с более мелкими пакетами, очевидно, это проблема вашего пакетного кода, опубликуйте соответствующий код XNA и HLSL, чтобы мы могли более внимательно посмотреть на него @Zonko с IsFixedTimeStep = False, обновление 1: 1 / ничья звонит
Даниэль Карлссон
Вот объяснение, почему это заикание происходит от Шона Харгривза (из команды xna): forums.create.msdn.com/forums/p/9934/53561.aspx#53561
NexAddo

Ответы:

3

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

Почему уменьшение размера партии устраняет проблему? Поскольку процессору требуется больше времени для обработки кадра, а это означает, что он тратит меньше времени на Present()ожидание завершения обработки графическим процессором. Это то, что я думаю, что делает во время этого перерыва в конце ваших Draw()звонков.

Причину временной задержки пробелов труднее угадать, не понимая весь код, но я не уверен, что это тоже важно. Делайте больше работы с процессором или меньше работы с графическим процессором, чтобы ваша рабочая нагрузка была менее неравномерной.

Смотрите эту статью в блоге Шона Харгривза для получения дополнительной информации.

Коул Кэмпбелл
источник
2
Это определенно связано с GPU. Приложение по сути является эталоном для изучения различных методов рисования. Меньший размер пакета с тем же количеством нарисованных вершин потребует больше времени на CPU, но загрузка GPU должна быть такой же, не так ли? По крайней мере, я бы ожидал, что между кадрами будет одинаковое время, в зависимости от нагрузки (которая вообще не меняется между кадрами), а не от таких регулярных интервалов задержки и мгновенности (или отсутствия рендеринга, см. PIX).
Даркара
Если я правильно интерпретирую ваши графики, мгновенно отображаемые кадры являются частью функциональности XNA Framework. Если IsFixedTimeStepустановлено значение « falseЕсли игра работает слишком медленно», XNA будет вызывать Update()несколько раз подряд, чтобы наверстать упущенное, умышленно отбрасывая кадры в процессе. IsRunningSlowlyУстановлено ли значение true во время этих кадров? Что касается странного времени - это заставляет меня задуматься немного. Вы работаете в оконном режиме? Сохраняется ли поведение в полноэкранном режиме?
Коул Кэмпбелл
вызовы происходят только на IsFixedTimeStep=true. Нижний график показывает время между окончанием моего розыгрыша и началом вызова обновления следующего кадра. Кадры не сбрасываются, я вызываю draw-методы и плачу за них цену процессора (верхний график). Такое же поведение в полноэкранном режиме и в разных разрешениях.
Даркара
Ты прав, моя ошибка. Боюсь, я исчерпал свои идеи на данный момент.
Коул Кэмпбелл
2

Я думаю, что у вас есть проблема с мусором ... возможно, вы создаете / уничтожаете много объектов, и что шипы - это обычная работа сборщика мусора ...

не забудьте повторно использовать все ваши структуры памяти ... и не используйте «новый» слишком часто

Blau
источник
Уже проверил это в ProcessExplorer и CLRProfiler, и gc работает, как раз в 10 секунд, а не так долго, как 75 мс.
Даркара
1
Вы определенно не хотите создавать новые RasterizerState, BlendState и DepthStencilState каждый кадр, независимо от того, является ли это причиной замедления рендеринга. Это определенно не поможет, согласно этой статье blogs.msdn.com/b/shawnhar/archive/2010/04/02/… Вы должны создать состояние, которое вы будете использовать один раз при загрузке, и повторно применять их при необходимости.
dadoo Games