XNA定期停顿


10

我正在尝试进行硬件实例化,但是遇到了一些奇怪的性能问题。平均帧率约为45,但非常不稳定。

  • 开窗的
  • SynchronizeWithVerticalRetrace =假
  • IsFixedTimeStep = false
  • PresentationInterval = PresentInterval.Immediate

下图显示了我测量的时间(带有Stopwatch)。最上面的图是在该Draw方法中花费的时间,最下面的图是从结束Draw到开始的时间。Update 绘图和XNA时序

尖峰几乎相隔1秒,并且总是通常时间的2、3、4或5倍。尖峰之后的帧完全没有时间。我已经检查过它不是垃圾收集器。

我目前正在实例化一个由12个三角形和36个顶点组成的网格作为三角形列表(我知道这不是最佳选择,但仅用于测试),具有100万个实例。如果我将实例化绘图调用分批处理到250个实例的小部分,则每个问题都会得到缓解,但CPU使用率会大大增加。上面的运行是每个draw调用10000个实例,在cpu上要容易得多。

如果我以全屏模式运行游戏,则几乎不存在底部图形,但是该Draw方法现在会出现相同的问题。

这是PIX内部的运行,对我来说根本没有意义。似乎有些帧没有完成渲染...

任何想法,可能是什么原因造成的?

编辑:根据要求,渲染代码的相关部分

CubeBuffer创建并初始化A ,然后填充多维数据集。如果多维数据集的数量超过某个限制,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);

立方体缓冲区

[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 fps)。它们甚至可能在单独的线程中运行(但是我不确定)。
Zonko

我没有真正的线索atm,但是如果它可以进行较小的批量处理,则显然是您的批处理代码存在问题,请发布相关的XNA和HLSL代码,以便我们可以通过IsFixedTimeStep = False来更仔细地查看@Zonko,有1:1更新/抽奖
丹尼尔·卡尔森

以下是解释为什么发生这种口吃的原因:Shawn Hargreaves(在xna团队中):forums.create.msdn.com/forums/p/9934/53561.aspx#53561
NexAddo 2012年

Answers:


3

我猜您的性能受GPU限制。您只是在要求图形设备每单位时间完成比其处理能力更多的工作。每帧3600万个顶点是一个相当不错的数目,硬件实例化实际上可以增加等式的GPU端所需的处理工作量。绘制更少的多边形。

为什么减小批量大小会使问题消失?因为这会使CPU处理帧花费更长的时间,这意味着Present()等待GPU完成渲染所需的时间更少。我想这就是您在Draw()通话结束时在这段时间里所做的事情。

在不了解整个代码的情况下,很难确定差距具体时间的原因,但是我不确定它是否也很重要。在CPU上进行更多工作,或在GPU上进行更少的工作,以减少工作负载的不均匀性。

有关更多信息,请参见Shawn Hargreaves博客上的这篇文章


2
绝对是GPU限制。该应用程序本质上是一个基准,可以探索不同的绘制方法。较小的批处理大小和相同数量的顶点绘制将在CPU上花费更长的时间,但是GPU负载应该相同,不是吗?至少我希望帧之间的时间一致,这取决于负载(在帧之间根本不改变),而不是这样的延迟和瞬时间隔(或没有渲染,请参见PIX)。
达卡拉

如果我正确地解释了您的图形,则即时渲染的框架是XNA Framework功能的一部分。随着IsFixedTimeStep设置为false,如果在游戏运行太慢,XNA会调用Update()多次连续追赶,在这个过程中故意丢弃帧。难道IsRunningSlowly这些帧期间设置为true?至于奇怪的时机-这的确使我有些疑惑。您是否以窗口模式运行?该行为是否在全屏模式下仍然存在?
科尔·坎贝尔

追加电话仅在发生IsFixedTimeStep=true。底部的图显示了我画图结束与下一帧更新调用开始之间的时间。框架没有掉落,我调用绘制方法并为它们支付CPU价格(上图)。全屏和不同分辨率下的行为相同。
达卡拉

你是对的,我的错。恐怕我在这一点上已经用尽了我的想法。
科尔·坎贝尔

2

我认为您遇到了垃圾问题...也许您正在创建/销毁许多对象,并且尖峰是垃圾收集器例程的工作原理...

确保重用所有内存结构...并且不要过于频繁地使用“ new”


已经在ProcessExplorer和CLRProfiler中进行了检查,并且gc每10秒运行一次,而时间不超过75ms。
达卡拉

1
您绝对不希望每帧都创建一个新的RasterizerState,BlendState和DepthStencilState,无论它是否是导致渲染速度下降的原因。根据本文blogs.msdn.com/b/shawnhar/archive/2010/04/02/…,这绝对没有帮助。您应该创建要在加载后使用一次的状态,并在需要时重新应用它们。
dadoo Games 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.