Informasi tentang rendering, batch, kartu grafis, kinerja dll. + XNA?

12

Saya tahu judulnya agak kabur tetapi sulit untuk menggambarkan apa yang sebenarnya saya cari, tapi begini saja.

Ketika datang ke rendering CPU, kinerja sebagian besar mudah untuk diperkirakan dan langsung, tetapi ketika datang ke GPU karena kurangnya informasi latar belakang teknis, saya tidak mengerti. Saya menggunakan XNA jadi alangkah baiknya jika teori bisa dikaitkan dengan itu.

Jadi yang sebenarnya ingin saya ketahui adalah, apa yang terjadi kapan dan di mana (CPU / GPU) ketika Anda melakukan tindakan menggambar tertentu? Apa itu batch? Apa pengaruh efek, proyeksi dll? Apakah data tetap ada pada kartu grafis atau ditransfer pada setiap langkah? Ketika ada pembicaraan tentang bandwidth, apakah Anda berbicara tentang bandwidth internal kartu grafis, atau pipa dari CPU ke GPU?
Catatan: Saya sebenarnya tidak mencari informasi tentang bagaimana proses menggambar, itu urusan GPU, saya tertarik pada semua overhead yang mendahului itu.

Saya ingin memahami apa yang terjadi ketika saya melakukan tindakan X, untuk menyesuaikan arsitektur dan praktik saya dengan itu.

Setiap artikel (mungkin dengan contoh kode), informasi, tautan, tutorial yang memberikan lebih banyak wawasan tentang cara menulis game yang lebih baik sangat dihargai. Terima kasih :)

Aidiakapi
sumber
2
Meskipun ini awalnya XNA, saya telah menambahkan tag DirectX, karena itu adalah teknologi yang mendasarinya - ini mungkin membantu Anda mendapatkan jawaban yang lebih baik. Lihat juga jawaban ini yang mungkin memberi Anda titik awal yang baik.
Andrew Russell
@AndrewRussell Terima kasih banyak :). Sebenarnya saya sudah membaca berbagai artikel tentang topik termasuk yang satu itu. Tapi itu belum mencakup semua yang ingin saya ketahui.
Aidiakapi

Jawaban:

20

Saya suka berpikir tentang kinerja dalam hal " batas ". Ini adalah cara praktis untuk membuat konsep sistem yang cukup rumit dan saling berhubungan. Ketika Anda memiliki masalah kinerja, Anda mengajukan pertanyaan: "Apa batasan yang saya pukul?" (Atau: "Apakah saya CPU / GPU terikat?")

Anda dapat memecahnya menjadi beberapa tingkatan. Pada level tertinggi Anda memiliki CPU dan GPU. Anda mungkin terikat dengan CPU (GPU duduk diam menunggu CPU), atau terikat GPU (CPU sedang menunggu di GPU). Berikut adalah posting blog yang bagus tentang topik tersebut.

Anda dapat memecahnya lebih lanjut. Di sisi CPU , Anda mungkin menggunakan semua siklus pada data yang sudah ada di cache CPU. Atau Anda mungkin terbatas memori , membiarkan CPU menunggu data masuk dari memori utama ( jadi optimalkan tata letak data Anda ). Anda masih bisa memecahnya lebih lanjut.

(Sementara saya melakukan tinjauan luas kinerja mengenai XNA, saya akan menunjukkan bahwa alokasi tipe referensi ( classtidak struct), walaupun biasanya murah, dapat memicu pengumpul sampah, yang akan membakar banyak siklus - terutama pada Xbox 360 . Lihat di sini untuk detail).

Di sisi GPU , saya akan mulai dengan mengarahkan Anda ke posting blog yang luar biasa ini yang memiliki banyak detail. Jika Anda ingin tingkat detail yang gila pada saluran pipa, baca seri posting blog ini . ( Ini yang lebih sederhana ).

Sederhananya, beberapa yang besar adalah: " batas pengisian " (berapa banyak piksel yang dapat Anda tulis ke pembuat backbuffer - sering berapa banyak overdraw yang Anda miliki), " batas shader " (seberapa rumitnya shader Anda dapat dan berapa banyak data yang Anda dapat mendorong melalui mereka), " tekstur-fetch / batas tekstur-bandwidth " (berapa banyak data tekstur yang dapat Anda akses).

Dan, sekarang, kita sampai pada yang besar - yang sebenarnya Anda tanyakan - di mana CPU dan GPU harus berinteraksi (melalui berbagai API dan driver). Secara longgar ada " batas batch " dan " bandwidth ". (Perhatikan bahwa bagian satu dari seri yang saya sebutkan sebelumnya masuk ke ekstensif rincian.)

Tapi, pada dasarnya, batch ( seperti yang sudah Anda ketahui ) terjadi setiap kali Anda memanggil salah satu GraphicsDevice.Draw*fungsi (atau bagian dari XNA, seperti SpriteBatch, melakukan ini untuk Anda). Seperti yang sudah pasti Anda baca, Anda mendapatkan beberapa ribu * dari ini per frame. Ini adalah batas CPU - sehingga bersaing dengan penggunaan CPU Anda yang lain. Ini pada dasarnya driver mengemas segala sesuatu tentang apa yang Anda perintahkan untuk menggambar, dan mengirimkannya ke GPU.

Dan kemudian ada bandwidth ke GPU. Ini adalah berapa banyak data mentah yang dapat Anda transfer ke sana. Ini mencakup semua informasi status yang sesuai dengan batch - semuanya mulai dari pengaturan status rendering dan konstanta shader / parameter (yang mencakup hal-hal seperti matriks dunia / tampilan / proyek), hingga simpul saat menggunakan DrawUser*fungsi. Ini juga termasuk panggilan ke SetDatadan GetDatapada tekstur, buffer vertex, dll.

Pada titik ini saya harus mengatakan bahwa apa pun yang dapat Anda panggil SetData(tekstur, simpul dan buffer indeks, dll), serta Effects - tetap ada dalam memori GPU. Ini tidak terus-menerus dikirim kembali ke GPU. Perintah draw yang mereferensikan data hanya dikirim dengan pointer ke data tersebut.

(Juga: Anda hanya bisa mengirim perintah menggambar dari utas utama, tetapi Anda bisa SetDatapada utas apa pun.)

XNA mempersulit hal-hal yang agak dengan kelas keadaan render ( BlendState, DepthStencilState, dll). Data status ini dikirim per panggilan undian (dalam setiap batch). Saya tidak 100% yakin, tetapi saya mendapat kesan bahwa itu dikirim dengan malas (hanya mengirimkan status yang berubah). Either way, perubahan negara murah ke titik gratis, relatif terhadap biaya satu batch.

Akhirnya, hal terakhir yang disebutkan adalah saluran internal GPU . Anda tidak ingin memaksanya untuk menyiram dengan menulis ke data yang masih perlu dibaca, atau membaca data yang masih perlu ditulis. Pipeline flush berarti menunggu operasi untuk selesai, sehingga semuanya dalam keadaan konsisten ketika data diakses.

Dua kasus khusus yang harus diwaspadai adalah: Menyerukan GetDatasesuatu yang dinamis - khususnya pada RenderTarget2DGPU yang dapat ditulisi. Ini sangat buruk untuk kinerja - jangan lakukan itu.

Kasus lainnya adalah memanggil SetDatabuffer vertex / indeks. Jika Anda perlu sering melakukan ini, gunakan DynamicVertexBuffer(juga DynamicIndexBuffer). Ini memungkinkan GPU untuk mengetahui bahwa mereka akan sering berubah, dan untuk melakukan sihir penyangga secara internal untuk menghindari flush pipeline.

(Perhatikan juga bahwa buffer dinamis lebih cepat daripada DrawUser*metode - tetapi mereka harus dialokasikan sebelumnya pada ukuran maksimum yang diperlukan.)

... Dan itu semua yang saya tahu tentang kinerja XNA :)

Andrew Russell
sumber
Terima kasih banyak! Ini adalah persis apa yang saya cari dan berharap untuk :).
Aidiakapi
1
Beberapa ratus batch per frame terdengar terlalu pesimis. Aturan praktis yang selalu saya dengar adalah 2K hingga 3K batch per frame. Beberapa game telah diketahui dapat mencapai 10K pada PC, tetapi saya pikir itu membutuhkan kehati-hatian untuk mencapainya.
Nathan Reed
Benar sekali. Angka "beberapa ratus" berasal dari kertas "batch batch" - yang mencantumkan "25k batch / s @ 100% dari CPU 1GHz". Tapi kertas itu sekarang berumur satu dekade, dan driver dan CPU telah meningkat secara signifikan sejak saat itu. Saya akan memperbarui ini (dan yang lainnya) untuk membaca "beberapa ribu".
Andrew Russell