Performansı " sınırlar " açısından düşünmeyi seviyorum . Oldukça karmaşık, birbirine bağlı bir sistemi kavramsallaştırmanın kullanışlı bir yoludur. Bir performans sorununuz olduğunda, "Hangi sınırları vuruyorum?" (Veya: "CPU / GPU bağlı mıyım?")
Birden fazla seviyeye ayırabilirsiniz. En üst düzeyde CPU ve GPU var. CPU'ya bağlı (GPU oturumu boşta CPU bekliyor) veya GPU'ya bağlı (CPU GPU'da bekliyor olabilir) olabilir. İşte konuyla ilgili iyi bir blog yazısı .
Daha fazla parçalayabilirsiniz. Açık CPU tarafında, CPU zaten önbellekte verilere tüm döngüleri kullanıyor olabilir. Veya bellek sınırlı olabilir , CPU boşta kalır ve verilerin ana bellekten gelmesini bekler ( bu nedenle veri düzeninizi optimize edin ). Onu daha da bozabilirsin.
(XNA ile ilgili performansa geniş bir genel bakış yaparken, normalde ucuz olsa da, bir referans türünün ( class
değil struct
) tahsisinin, özellikle Xbox 360'ta çok fazla döngü yakacak olan çöp toplayıcısını tetikleyebileceğine işaret edeceğim. . buraya bakın ) detaylar için.
Açık GPU tarafında, ben sizi işaret ederek dışarı başlayacağız bu mükemmel blog post detaylarını çok. Boru hattında çılgın bir ayrıntı düzeyi istiyorsanız , bu blog yayınları dizisini okuyun . ( İşte daha basit olanı ).
Basitçe söylemek gerekirse, büyük olanlardan bazıları şunlardır: " doldurma sınırı " (backbuffer'a kaç piksel yazabilirsiniz - genellikle ne kadar fazla çekime sahip olabilirsiniz), " gölgelendirici sınırı " ( gölgelendiricilerinizin ne kadar karmaşık olabileceği ve bunlardan ne kadar veri aktarabileceğinizi), " doku getirme / doku bant genişliği sınırı " (ne kadar doku verilerine erişebileceğiniz).
Ve şimdi, CPU ve GPU'nun etkileşime girmesi gereken (çeşitli API'ler ve sürücüler aracılığıyla) büyük olana (gerçekten sorduğunuz şey bu) geliyoruz. Gevşek olarak " parti limiti " ve " bant genişliği " vardır. ( Daha önce bahsettiğim dizinin birinci bölümünün kapsamlı ayrıntılara girdiğini unutmayın .)
Ancak, temel olarak, bir toplu iş ( zaten bildiğiniz gibi ), GraphicsDevice.Draw*
işlevlerden birini (veya XNA'nın bir kısmını, SpriteBatch
sizin için yapar gibi ) çağırdığınızda gerçekleşir . Şüphesiz daha önce okumuş olduğunuz gibi, her kare için bunlardan birkaç bin * alırsınız . Bu bir CPU sınırıdır - bu nedenle diğer CPU kullanımınızla rekabet eder. Temel olarak sürücü, çizmesini söylediklerinizle ilgili her şeyi paketliyor ve GPU'ya gönderiyor.
Ve sonra GPU'nun bant genişliği var . Buraya ne kadar ham veri aktarabileceğinizdir. Bu, toplu işlerle birlikte gelen tüm durum bilgilerini içerir - oluşturma durumunu ve gölgelendirici sabitlerini / parametrelerini ayarlamaktan (dünya / görünüm / proje matrisleri gibi şeyleri içerir), DrawUser*
işlevleri kullanırken köşelere kadar her şey . Aynı zamanda herhangi bir çağrı içerir SetData
ve GetData
vb dokular, köşe tamponları, üzerinde
Bu noktada diyebileceğim her şeyin SetData
(dokular, tepe noktası ve dizin arabellekleri vb.) Yanı sıra Effect
s - GPU belleğinde kaldığını söylemeliyim . GPU'ya sürekli olarak gönderilmez. Bu verileri referans alan bir çizim komutu, söz konusu verilere bir işaretçi ile gönderilir.
(Ayrıca: yalnızca ana iş parçacığından çizim komutları gönderebilirsiniz, ancak SetData
herhangi bir iş parçacığında yapabilirsiniz .)
XNA Biraz onun hale devlet sınıfları (şeyler karmaşıklaştırır BlendState
, DepthStencilState
vs.). Bu durum veri edilir (her partideki) çizim çağrısının başına gönderdi. % 100 emin değilim, ama tembel olarak gönderildiği izlenimi altındayım (sadece değişen durum gönderir). Her iki durumda da, devlet değişiklikleri bir partinin maliyetine göre serbest olana göre ucuzdur.
Son olarak, söylenecek son şey dahili GPU boru hattıdır . Hala okumaya ihtiyaç duyduğu verilere yazarak ya da hala yazması gereken verileri okuyarak onu temizlemek zorunda bırakmak istemezsiniz. Bir boru hattı yıkaması, işlemlerin bitmesini beklediği anlamına gelir, böylece verilere erişildiğinde her şey tutarlı bir durumda olur.
Dikkat edilmesi gereken iki özel durum şunlardır: GetData
Dinamik bir şey çağırmak - özellikle RenderTarget2D
de GPU'nun yazabileceği bir şey. Bu performans için son derece kötü - yapma.
Diğer durum SetData
köşe / dizin arabelleklerini çağırıyor . Bunu sık sık yapmanız gerekiyorsa, DynamicVertexBuffer
(ayrıca DynamicIndexBuffer
) kullanın . Bunlar GPU'nun sık sık değişeceklerini bilmelerini ve boru hattının yıkamasını önlemek için dahili olarak tamponlama sihirleri yapmasını sağlar.
(Ayrıca, dinamik arabelleklerin DrawUser*
yöntemlerden daha hızlı olduğunu unutmayın - ancak gereken maksimum boyutta önceden atanmaları gerekir.)
... Ve XNA performansı hakkında bildiğim her şey :)