XNA - دمج الكائنات للحصول على أداء أفضل في الرسم؟

StackOverflow https://stackoverflow.com/questions/1637086

  •  06-07-2019
  •  | 
  •  

سؤال

قرأت في مكان ما أنه عند عرض الكثير من الكائنات ثلاثية الأبعاد، يمكن دمجها لتكوين شبكة عملاقة بحيث يتم إجراء مكالمة سحب واحدة فقط.لذلك ترك الاقتباس:"وحدة معالجة الرسومات تقوم بسحرها"، في حين أن وحدة المعالجة المركزية مجانية للمكالمات الأخرى من الرسم.

لذا، بالنسبة لسؤالي، هل سيكون من الممكن القيام بذلك في بيئة ثنائية الأبعاد مع أداء في عين الاعتبار؟

على سبيل المثال، لنفترض أن لدينا نظامًا بسيطًا للبلاطات، وبدلاً من إجراء طلب سحب لكل بلاطة معروضة، سيتم إجراء ذلك بدلاً من ذلك دمج جميع البلاط لتكوين كائن كبير ثم إجراء السحب عليه.

إن أي نظرة ثاقبة حول هذا الأمر - سواء كانت نصائح أو روابط أو غير ذلك - موضع تقدير كبير نظرًا لعدم وجود خبرة سابقة لدي في أداء الرسومات.

يحرر: آسف لتفسيري الفقراء.أقوم بإنشاء محرك بلاط للاستخدام الشخصي وأريد أن يكون متعدد الاستخدامات قدر الإمكان.لذلك أريد التحسين فقط في حالة اضطراري إلى رسم الكثير من المربعات في المستقبل القريب.

أنا يفعل استخدم ورقة تجانب ولكن ما قصدته بالسؤال هو ما إذا كان دمج جميع التجانبات التي سيتم سحبها من الورقة في Texture2D الجديد سيكتسب أداءً.على سبيل المثال:

لدينا بلاط مقاس 128 × 72 للرسم على الشاشة.بدلاً من المرور عبر جميع المربعات واستدعاء السحب لكل مربع سيتم رسمه، نقوم بدمج جميع المربعات في كائن جديد بحجم 1280 × 720 ونرسمه.بهذه الطريقة سيتم استدعاء الأسلوب draw() فقط مرة واحدة لكل إطار.لي سؤال كان هذا إذا كان سيؤدي إلى تحسين الأداء لأنه سيؤدي إلى دمج الكائنات ثلاثية الأبعاد في شبكة واحدة عند القيام بالعرض ثلاثي الأبعاد.

لأن المعلومات التي جمعتها هي أن استدعاء draw() يعد بمثابة خنزير للأداء ويجب أن يتم استدعاؤه بأقل قدر ممكن.هل من أحد يؤكد أو ينفي؟:)

هل كانت مفيدة؟

المحلول

ليس لدي أي خبرة في XNA و3D، ولكن سأقدم لك بعض النصائح للألعاب ثنائية الأبعاد.لقد أمضيت بعض الوقت في إنشاء محرك بلاط في XNA في بداية هذا العام وتساءلت عن نفس الشيء.أعتقد أن الإجابة المختصرة هنا هي نعم، فدمج المربعات في كائن أكبر يعد فكرة جيدة إذا كنت قلقًا بشأن الأداء.ومع ذلك، هناك إجابة أطول بكثير إذا كنت مهتمًا.

وبدون ذلك، لا تعرف إذا كنت تنجح!

الآن، فيما يتعلق بالألعاب ثنائية الأبعاد، تعلمت أنني رأيت أداءً أفضل بكثير في محرك البلاط الخاص بي كلما قللت من تبديل الأنسجة.على سبيل المثال، لنفترض أن لدي بلاطًا عشبيًا وبلاطًا من الحصى.إذا كان هذان نسيجان منفصلان في الذاكرة، وقمت برسم بلاط عشبي، ثم بلاط حصى، ثم بلاط عشبي على الشاشة، فسوف تقوم وحدة معالجة الرسومات بتحميل نسيج العشب، ثم تقوم بإيقاف تشغيله لتحميل نسيج الحصى، ثم تقوم بتبديل نسيج العشب مرة أخرى لرسم بلاط عشبي آخر.هذا يقتل الأداء بسرعة كبيرة!أسهل طريقة للتغلب على ذلك هي الحصول على ورقة مجسمة، حيث تضع بلاط العشب والحصى في نسيج واحد، ثم تطلب من SpriteBatch أن ترسم من منطقة مختلفة على نفس النسيج في كل مرة.

شيء آخر يجب أخذه في الاعتبار هو عدد المربعات التي سترسمها على الشاشة مرة واحدة.لا أستطيع أن أتذكر على وجه التحديد، ولكنني كنت أرسم آلاف المربعات مرة واحدة (في عرض مصغر).لقد لاحظت أنه عندما استخدمت مربعات أكبر ورسمت عددًا أقل منها، كما تقترح في سؤالك، تحسن هذا الأداء أيضًا.لم يكن هذا تحسنًا كبيرًا مثل ما وصفته في الفقرة الأخيرة، وما زلت أشجعك على قياس تغييرات الأداء الناتجة عن عمليات التنفيذ المختلفة.أيضًا، إذا كنت ترسم فقط عشرات أو بضع مئات من المربعات، فقد لا يكون من المفيد محاولة تحسين ذلك الآن (انظر الفقرة الثانية).

فقط لكي تعلم أنني لا أختلق هذا الأمر بشكل كامل، إليك رابط إلى مشاركة من Shawn Hargreaves حول تبديل النسيج، وأوراق الصور المتحركة، وما إلى ذلك.من المحتمل أن تكون هناك منشورات أفضل في منتديات XNA بالإضافة إلى مدونة Shawn Hargreaves إذا بحثت عن الموضوع.

http://forums.xna.com/forums/p/24254/131437.aspx#131437

تحديث:

منذ أن قمت بتحديث سؤالك، اسمحوا لي أن أقوم بتحديث مشاركتي.لقد قررت فقط قياس بعض العينات لإعطائك فكرة عن الاختلافات في الأداء.في وظيفة Draw() لدي ما يلي:

        GraphicsDevice.Clear(Color.CornflowerBlue);

        Stopwatch sw = new Stopwatch();
        sw.Start();

        spriteBatch.Begin();

#if !DEBUG
        spriteBatch.Draw(tex, new Rectangle(0, 0, 
                         GraphicsDevice.Viewport.Width,
                         GraphicsDevice.Viewport.Height), 
                         Color.White);            
#else
        for (int i = 0; i < 128; i++)
            for (int j = 0; j < 72; j++)
            {
                Rectangle r = new Rectangle(i * 10, j * 10, 10, 10);
                spriteBatch.Draw(tex, r, r, Color.White);
            }
#endif
        spriteBatch.End();

        sw.Stop();

        if (draws > 60)
        {
            numTicks += sw.ElapsedTicks;
        }
        draws++;

        if (draws % 100 == 0)
            Console.WriteLine("avg ticks: " + numTicks / (draws - 60));

        base.Draw(gameTime);

ما عليك سوى إسقاط علامة التعجب في عبارة "#if !DEBUG" للتبديل بين الطريقتين.لقد تخطيت أول 60 عملية سحب لأنها تضمنت بعض الإعدادات الأولية (لست متأكدًا حقًا من ماهيتها) وكانت تحرف المتوسطات.لقد قمت بتنزيل صورة واحدة بحجم 1280 × 720، وبالنسبة لحالة الاختبار العليا قمت برسمها مرة واحدة فقط.بالنسبة لحالة الاختبار السفلية، قمت برسم صورة مصدر واحدة في مربعات، بحجم 128 × 72 كما سألت عنها في سؤالك.وهنا النتائج.

رسم صورة واحدة:

avg ticks: 68566
avg ticks: 73668
avg ticks: 82659
avg ticks: 81654
avg ticks: 81104
avg ticks: 84664
avg ticks: 86626
avg ticks: 88211
avg ticks: 87677
avg ticks: 86694
avg ticks: 86713
avg ticks: 88116
avg ticks: 89380
avg ticks: 92158

رسم البلاط 128x72:

avg ticks: 7902592
avg ticks: 8052819
avg ticks: 8012696
avg ticks: 8008819
avg ticks: 7985545
avg ticks: 8028217
avg ticks: 8046837
avg ticks: 8291755
avg ticks: 8309384
avg ticks: 8336120
avg ticks: 8320260
avg ticks: 8322150
avg ticks: 8381845
avg ticks: 8364629

كما ترون، هناك اختلاف في الحجم هناك، لذا فهو مهم جدًا.من السهل جدًا اختبار هذا النوع من الأشياء، وأوصيك بتشغيل معاييرك الخاصة لإعدادك المحدد لتأخذ في الاعتبار شيئًا ربما فاتني.

نصائح أخرى

كلما قلّت الحاجة إلى تبديل الأنسجة على بطاقة الرسومات، كان الأداء أفضل.يمكنك الفرز حسب المادة في طريقة SpriteBatch.Begin() إذا كنت ترغب في ذلك بحيث تقوم بطاقة الرسومات بتبديل الأنسجة بأقل قدر ممكن.

أضع البلاط/العفاريت في الأوراق عندما يكون ذلك منطقيًا.على سبيل المثال، قد يكون حرف واحد في ورقة وقد تكون طبقة واحدة من البلاط في نفس الورقة.لقد نجح هذا بشكل جيد حتى الآن.

أود أن أقول أنه لا تقم بالتحسين حتى تضطر إلى ذلك.إذا كانت ألعابك تعمل بالسرعة الكافية، فلماذا تهتم بالتحسين.

Rectangle r = new Rectangle(i * 10, j * 10, 10, 10);

سيؤدي إنشاء العديد من الكائنات الجديدة إلى استدعاء مجموعة البيانات المهملة التي يمكن أن تبطئ كثيرًا!لا ينبغي عليك تخصيص كائنات جديدة في حلقات كبيرة:

الأداء ليس ضرورة عالمية أو وصية من الله؛إنها وسيلة لتحقيق غاية.إذا كان أداء ما لديك جيدًا بما فيه الكفاية، فلن تستفيد فعليًا من تحسين أدائه.إن الحفاظ على "أفضل أداء ممكن في جميع الأوقات" هو تعريف التحسين المبكر، وسوف يسبب المزيد من الصداع أكثر مما يمنعه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top