नियमित अंतराल पर XNA हकलाना


10

मैं हार्डवेयर इंस्ट्रक्शन करने की कोशिश कर रहा हूं लेकिन मैं कुछ अजीब परफॉर्मेंस प्रॉब्लम मार रहा हूं। औसत फ्रैमरेट लगभग 45 है, लेकिन यह बेहद तड़का हुआ है।

  • विंडोड
  • सिंक्रोनाइज़डिथवर्थिकल रीट्रेस = गलत
  • IsFixedTimeStep = false
  • प्रस्तुतिइंटरवल = PresentInterval.Immediate

नीचे दी गई छवि मेरी मापी गई समय (के साथ Stopwatch) दिखाती है । सबसे ऊपरी ग्राफ Drawविधि में बिताया गया समय है और नीचे का ग्राफ Drawशुरुआत से अंत तक का समय हैUpdate ड्रा और xna टाइमिंग

स्पाइक्स लगभग 1 सेकंड अलग हैं और हमेशा सामान्य समय से 2,3,4 या 5 गुना अधिक होते हैं। स्पाइक के तुरंत बाद के फ्रेम को बिल्कुल भी समय नहीं लगता है। मैंने जाँच की है कि यह कचरा संग्रहकर्ता नहीं है।

मैं वर्तमान में एक त्रिकोण सूची के रूप में 12 त्रिकोण और 36 कोने से मिलकर एक जाल बना रहा हूं (मुझे पता है कि यह इष्टतम नहीं है, लेकिन यह परीक्षण के लिए है) 1 मिलियन उदाहरणों के साथ। अगर मैं इंस्टेंस ड्रॉइंग 250 कॉल के छोटे हिस्सों में कॉल करता हूं, तो प्रत्येक समस्या कम हो जाती है, लेकिन सीपीयू का उपयोग काफी बढ़ जाता है। उपर्युक्त रन 10000 कॉल प्रति ड्रॉ कॉल पर है, जो सीपीयू पर बहुत आसान है।

अगर मैं फुलस्क्रीन में गेम चलाता हूं तो नीचे का ग्राफ लगभग न के बराबर है, लेकिन Drawविधि में भी यही समस्या है ।

यहाँ PIX के अंदर एक रन है , जिससे मुझे कोई मतलब नहीं है। ऐसा लगता है कि कुछ फ़्रेमों के लिए कोई प्रतिपादन नहीं किया गया है ...

किसी भी विचार, यह क्या कारण हो सकता है?

संपादित करें : अनुरोध के अनुसार, रेंडर कोड के प्रासंगिक अंश

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

मुझे यकीन नहीं है कि यह समय से पहले से अद्यतन की शुरुआत के लिए प्रासंगिक है, क्योंकि वे दृढ़ता से जुड़े नहीं हैं (यानी कई अपडेट 2 ड्रॉ के बीच हो सकते हैं यदि खेल धीरे-धीरे चलता है, जो तब होना चाहिए जब आप नहीं चल रहे हों। 60 एफपीएस पर)। वे अलग-अलग थ्रेड में भी दौड़ सकते हैं (लेकिन मुझे इस बारे में निश्चित नहीं है)।
ज़ोनको

मेरे पास कोई वास्तविक सुराग नहीं है, लेकिन अगर यह छोटे बैचिंग के साथ काम करता है तो यह आपके बैचिंग कोड के साथ स्पष्ट रूप से एक समस्या है, संबंधित XNA और HLSL कोड को पोस्ट करें ताकि हम इसे अधिक बारीकी से देख सकें @Zonko के साथ IsFixedTimeStep / False 1: 1 अपडेट / कॉल करें
डैनियल कार्लसन

यहाँ इस बात का स्पष्टीकरण दिया गया है कि यह हकलाना शॉन हैरग्रेव्स
xna

जवाबों:


3

मैं अनुमान लगा रहा हूं कि आपका प्रदर्शन GPU- बाध्य है। आप बस अपने ग्राफिक्स डिवाइस को प्रति यूनिट समय से अधिक काम करने के लिए कह रहे हैं क्योंकि यह संभालने में सक्षम है; फ्रेम प्रति 36 मिलियन कोने एक बहुत ही सभ्य संख्या है, और हार्डवेयर इंस्टेंसेस वास्तव में समीकरण के GPU पक्ष पर आवश्यक प्रसंस्करण कार्य की मात्रा बढ़ा सकते हैं। कम बहुभुज बनाएं।

क्यों बैच आकार को कम करने से समस्या दूर हो जाती है? क्योंकि यह सीपीयू को एक फ्रेम को संसाधित करने में अधिक समय लेता है, जिसका अर्थ है कि यह Present()रेंडरिंग खत्म करने के लिए GPU के इंतजार में बैठकर कम समय बिता रहा है। मुझे लगता है कि यह आपके Draw()कॉल के अंत में उस अंतराल के दौरान कर रहा है ।

अंतराल के विशिष्ट समय के पीछे कारण पूरे कोड को समझने के बिना परमात्मा के लिए कठिन है, लेकिन मुझे यकीन नहीं है कि यह महत्वपूर्ण है, या तो। CPU पर अधिक काम करें, या GPU पर कम काम करें, ताकि आपका कार्यभार कम असमान न हो।

अधिक जानकारी के लिए शॉन हरग्रेव्स के ब्लॉग पर यह लेख देखें ।


2
यह निश्चित रूप से GPU बाध्य है। विभिन्न ड्राइंग विधियों का पता लगाने के लिए ऐप अनिवार्य रूप से एक बेंचमार्क है। एक ही बैच के आकार के साथ खींचे गए छोटे आकार का CPU पर अधिक समय लगेगा, लेकिन GPU- लोड समान होना चाहिए, नहीं? कम से कम मैं फ्रेम के बीच एक सुसंगत समय की उम्मीद करता हूं, जो लोड पर निर्भर करता है (जो फ्रेम के बीच बिल्कुल नहीं बदलता है) और लैग और तत्काल के ऐसे नियमित अंतराल (या कोई प्रतिपादन नहीं, PIX देखें)।
दाराका

अगर मैं आपके ग्राफ़ की सही व्याख्या कर रहा हूं, तो तुरन्त प्रस्तुत किए गए फ्रेम XNA फ्रेमवर्क की कार्यक्षमता का हिस्सा हैं। IsFixedTimeStepसेट के साथ false, यदि खेल बहुत धीरे-धीरे चल रहा है, तो XNA Update()एक पंक्ति में कई बार कॉल करेगा , ताकि प्रक्रिया में जानबूझकर फ्रेम गिर जाए। क्या IsRunningSlowlyइन फ्रेम के दौरान सही है? अजीब समय के लिए - यह मुझे थोड़ा आश्चर्यचकित करता है। क्या आप विंडो मोड में चल रहे हैं? क्या पूर्ण स्क्रीन मोड में व्यवहार जारी रहता है?
कोल कैंपबेल

कैचअप-कॉल केवल पर होते हैं IsFixedTimeStep=true। नीचे का ग्राफ़ मेरे ड्रॉ के अंत और अगले फ्रेम के अपडेट कॉल की शुरुआत के बीच का समय दर्शाता है। फ़्रेम को गिराया नहीं जाता है, मैं ड्रॉ-विधियों को कॉल करता हूं और उनके लिए सीपीयू की कीमत (शीर्ष ग्राफ) का भुगतान करता हूं। फुलस्क्रीन और संकल्पों में समान व्यवहार।
दाराका

तुम सही हो, मेरी गलती है। मुझे डर है कि मैंने इस बिंदु पर अपने विचारों को समाप्त कर दिया है।
कोल कैंपबेल

2

मुझे लगता है कि आपको एक कचरा समस्या है ... हो सकता है कि आप कई वस्तुओं का निर्माण / विनाश कर रहे हों और यह कि कचरा कचरा संग्राहक कार्य कर रहे हैं ...

अपनी सभी मेमोरी संरचनाओं का पुन: उपयोग करना सुनिश्चित करें ... और अक्सर 'नए' का उपयोग न करें


पहले से ही जाँच की गई कि ProcessExplorer और CLRProfiler में, और gc हर 10 सेकंड में एक बार चल रहा है और लगभग 75ms तक नहीं।
दाराका जूल

1
आप निश्चित रूप से एक नया RasterizerState, BlendState और DepthStencilState हर फ्रेम बनाना नहीं चाहते हैं, भले ही यह आपके रेंडरिंग मंदी का कारण हो या न हो। यह निश्चित रूप से मदद नहीं करेगा, जैसा कि इस लेख के अनुसार blogs.msdn.com/b/shawnhar/archive/2010/04/02// आपको उस राज्य को बनाना चाहिए जिसका आप एक बार लोड पर उपयोग कर रहे होंगे और जरूरत पड़ने पर उन्हें पुन: लागू करेंगे।
दादू गेम्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.