일정한 간격으로 XNA 말더듬


10

하드웨어 인스 턴싱을 시도하고 있지만 이상한 성능 문제가 발생했습니다. 평균 프레임 속도는 약 45이지만 매우 고르지 않습니다.

  • SynchronizeWithVerticalRetrace = 거짓
  • IsFixedTimeStep = false
  • PresentationInterval = PresentInterval.Immediate

아래 이미지는 측정 된 타이밍을 표시합니다 ( Stopwatch). 최상위 그래프는 Draw분석법에 소요 된 시간 이고 하단 그래프는 종료 시점부터 Draw시작 시점까지의 시간입니다.Update 그리기 및 xna 타이밍

스파이크는 거의 정확히 1 초 간격이며 항상 일반적인 시간의 2, 3, 4 또는 5 배입니다. 스파이크 직후의 프레임은 시간이 전혀 걸리지 않습니다. 가비지 수집기가 아닌지 확인했습니다.

현재 12 개의 삼각형과 36 개의 정점으로 구성된 메쉬를 삼각형 목록으로 인스턴스화하고 있습니다 (최적의 것은 아니지만 테스트 용입니다). 인스턴스화 드로우 콜을 250 인스턴스의 작은 부분으로 일괄 처리하면 문제가 완화되지만 CPU 사용량이 크게 증가합니다. 위의 실행은 그리기 호출 당 10000 인스턴스이며 CPU에서 훨씬 쉽습니다.

게임을 전체 화면으로 실행하면 하단 그래프가 거의 존재하지 않지만 방법에서 동일한 문제가 발생합니다 Draw.

여기 PIX 내부의 실행이 있습니다 . 렌더링이없는 일부 프레임의 경우 ...

어떤 생각,이 원인은 무엇입니까?

편집 : 요청에 따라 렌더링 코드의 관련 부분

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

큐브 버퍼

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

강하게 연결되어 있지 않기 때문에 무승부 종료에서 업데이트 시작까지의 시간과 관련이 있는지 확실하지 않습니다 (즉, 게임이 느리게 실행되면 2 무승부 사이에 많은 업데이트가 발생할 수 있습니다. 60fps에서). 그들은 심지어 별도의 스레드에서 실행될 수도 있습니다 (그러나 나는 확실하지 않습니다).
Zonko

나는 실제 단서가 없지만 작은 배치로 작동하면 배치 코드에 문제가있는 것으로 보이는 경우 관련 XNA 및 HLSL 코드를 게시하여 IsFixedTimeStep = False로 @Zonko를 더 자세히 볼 수 있습니다 .1 : 1 업데이트가 있습니다. / draw 전화
다니엘 칼슨

이 끊김 현상이 xna 팀의 Shawn Hargreaves에서 발생하는 이유에 대한 설명은 다음과 같습니다. forums.create.msdn.com/forums/p/9934/53561.aspx#53561
NexAddo

답변:


3

성능이 GPU에 국한된 것 같습니다. 단순히 처리 할 수있는 것보다 단위 시간당 더 많은 작업을 수행하도록 그래픽 장치에 요청하는 것입니다. 프레임 당 3 천 6 백만 개의 정점은 꽤 괜찮은 숫자이며 하드웨어 인스 턴싱은 실제로 GPU 측면에서 필요한 처리 작업량을 늘릴 수 있습니다. 더 적은 다각형을 그립니다.

배치 크기를 줄이면 문제가 해결되는 이유는 무엇입니까? CPU가 프레임을 처리하는 데 시간이 오래 걸리기 때문에 Present()GPU가 렌더링을 완료하기 를 기다리는 시간이 줄어 듭니다 . 그것이 당신의 Draw()전화 가 끝날 때 그 간격 동안 내가하고 있다고 생각 합니다.

간격의 특정 타이밍 뒤에있는 이유는 코드 전체를 이해하지 않고 신성하기가 더 어렵지만 중요하지는 않습니다. 워크로드가 고르지 않도록 CPU에서 더 많은 작업을 수행하거나 GPU에서 더 적은 작업을 수행하십시오.

자세한 내용 은 Shawn Hargreaves의 블로그 에서이 기사 를 참조하십시오.


2
확실히 GPU에 바인딩되어 있습니다. 이 앱은 기본적으로 다른 드로잉 방법을 탐색하기위한 벤치 마크입니다. 동일한 정점을 가진 더 작은 배치 크기는 CPU에서 더 오래 걸리지 만 GPU로드는 동일해야합니다. 적어도 나는 프레임에 전혀 일정한 시간 간격을 두지 않고 (프레임간에 전혀 변하지 않는)로드에 따라 프레임간에 일정한 시간을 기대할 것입니다 (또는 렌더링 없음, PIX 참조).
Darcara

그래프를 올바르게 해석하면 즉시 렌더링 된 프레임은 XNA Framework 기능의 일부입니다. 로 IsFixedTimeStep설정 false하면 게임이 너무 느리게 실행되면 XNA는 Update()여러 번 연속해서 호출 하여 프로세스에서 프레임을 삭제합니다. 되어 IsRunningSlowly이러한 프레임 중에 true로 설정? 이상한 타이밍에 관해서는-조금 궁금해합니다. 창 모드에서 실행 중입니까? 동작이 전체 화면 모드로 유지됩니까?
Cole Campbell

캐치 업 통화는에서만 발생합니다 IsFixedTimeStep=true. 하단 그래프는 내 추첨이 끝나고 다음 프레임의 업데이트 호출이 시작되는 시점 사이의 시간을 보여줍니다. 프레임이 삭제되지 않고 그리기 방법을 호출하고 CPU 가격을 지불합니다 (상단 그래프). 전체 화면 및 전체 해상도에서 동일한 동작.
Darcara

네 말이 맞아 내 실수 이 시점에서 아이디어를 다 써 버린 것 같습니다.
Cole Campbell

2

가비지 문제가 있다고 생각합니다 ... 아마도 많은 객체를 생성 / 파괴하고 있으며 스파이크는 가비지 수집기 루틴입니다.

모든 메모리 구조를 재사용하고 '신규'를 너무 자주 사용하지 마십시오.


ProcessExplorer 및 CLRProfiler에서 이미 확인했으며 gc가 10 초마다 한 번씩 실행되며 75ms 정도 걸리지 않습니다.
Darcara

1
렌더링 속도 저하 원인인지 여부에 관계없이 모든 프레임마다 새 RasterizerState, BlendState 및 DepthStencilState를 만들고 싶지 않습니다. 이 기사 blogs.msdn.com/b/shawnhar/archive/2010/04/02/…에 따라 확실히 도움이되지는 않습니다 .로드 할 때 한 번 사용할 상태를 생성하고 필요할 때 다시 적용해야합니다.
dadoo 게임
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.