CPU - Aliran data memori GPU [ditutup]

16

Saya seorang programmer grafis pemula dan saya bertanya-tanya baru-baru ini - bagaimana data model (jerat dan bahan) mengalir dari aplikasi (memori CPU) ke kartu grafis (memori GPU?)? Katakanlah saya memiliki model statis (misalnya bangunan) yang saya muat dan siapkan sekali dan tidak berubah sepanjang umur aplikasi.

  • Apakah datanya dikirim ke memori GPU hanya sekali dan duduk di sana selamanya?
  • Ketika model benar-benar dirender setiap frame, apakah prosesor GPU harus mengambil datanya setiap kali dari memori GPU? Yang saya maksud adalah - jika saya memiliki 2 model yang dirender beberapa kali masing-masing - apakah penting jika saya pertama kali merender yang pertama beberapa kali dan kemudian yang kedua beberapa kali atau jika saya merender yang pertama hanya sekali, yang kedua hanya sekali dan terus menyisipkannya seperti itu? Saya dapat menyebut pertanyaan ini "aliran data GPU internal" dalam pengertian ini.
  • Jelas kartu grafis memiliki RAM terbatas - ketika itu tidak dapat menampung semua data model yang diperlukan untuk rendering 1 frame. Saya kira itu terus mengambil (sebagian) dari RAM CPU setiap frame, apakah itu benar?

Saya tahu ada banyak buku dan hal tentang ini di internet, tetapi mungkin Anda memiliki beberapa panduan umum cepat tentang bagaimana mengelola aliran data ini (kapan harus mengirim apa dan berapa banyak, kapan dan bagaimana membuat)?

Sunting: Saya lupa membuat satu perbedaan: ada yang mengirim data ke GPU dan ada pengaturan / pengikatan buffer seperti saat ini . Apakah yang terakhir menyebabkan aliran data?

Sunting2: Setelah membaca posting Raxvan saya ingin membedakan beberapa tindakan:

  • pembuatan buffer dengan inisialisasi (seperti katanya saya dapat menyimpan data dalam CPU ram atau GPU)
  • pembaruan data buffer (yang saya yakini mudah saat data disimpan dalam ram CPU dan membutuhkan pengambilan dari GPU ke ram CPU (dan kemudian kembali) ketika disimpan dalam ram GPU)
  • mengikat buffer sebagai aktif (apakah itu hanya cara untuk memberitahu API bahwa saya ingin buffer ini di -render dalam panggilan undian berikutnya dan tidak melakukan apa-apa dengan sendirinya ?)
  • Panggilan undian API (di sini saya ingin mendengar dari Anda apa yang sebenarnya terjadi di sana)
NPS
sumber
Saya bukan ahli dengan cara apa pun, tetapi jika Anda menggunakan OpenGL modern (yaitu tidak langsung) dengan VAO dan VBO, maka data dikirim ke GPU dan disimpan dalam VRAM setiap kali Anda menggunakan salah satu keluarga perintah glBuffer. Kemudian, setiap kali Anda menggambar, simpul yang relevan diambil dari VRAM dan diberikan. Jika model yang bergerak, Anda cenderung menyimpannya secara statis dan menggunakan matriks untuk berpindah dari ruang model ke ruang dunia / kamera. Adapun poin terakhir, saya tidak tahu apa yang terjadi jika Anda kehabisan RAM. Dugaan saya adalah bahwa jika Anda kehabisan VRAM, maka data tidak terkirim, mungkin dengan kode kesalahan.
Polar
@Polar - tidak persis. GL sebenarnya tidak menentukan memori tempat objek penyangga disimpan, dan bahkan bebas untuk memindahkannya saat runtime berdasarkan pola penggunaan. GL4.4 membahas hal ini agak, tetapi mencatat bahwa pada akhirnya yang terbaik yang dapat diberikannya adalah "salah satu dari hal-hal yang konyol"; lihat opengl.org/registry/specs/ARB/buffer_storage.txt dan terutama Masalah 2 dan 9.
Maximus Minimus
1
@ JimmyShelter Ah, terima kasih - alangkah baiknya jika kita memiliki lebih sedikit "hal-hal yang konyol" dan spesifikasi yang lebih konkret.
Polar
@Polar - Yang menjengkelkan adalah ARB_buffer_storage bisa menghindari memasukkan petunjuk lain, tetapi para desainer melewatkan kesempatan itu. Oh well, mungkin 4,5 akhirnya akan beres.
Maximus Minimus
2
Harap jangan mengedit pertanyaan Anda untuk "menanggapi" jawaban. Kirim pertanyaan baru sebagai gantinya.

Jawaban:

12

Apakah datanya dikirim ke memori GPU hanya sekali dan duduk di sana selamanya?

Biasanya ya, tetapi pengemudi bebas untuk melakukan apa yang "optimal", data mungkin disimpan di VRAM atau RAM atau bisa saja di-cache di sini adalah sebuah atricle yang menjelaskan apa yang sebenarnya terjadi dengan aliran VBO .

Sebagai contoh jika itu ditandai sebagai buffer openGL dinamis (misalnya VBO), itu lebih cenderung disimpan dalam RAM. GPU menggunakan akses memori langsung (DMA) untuk mengakses ram secara langsung tanpa intervensi CPU, ini dikendalikan oleh pengontrol DMA dalam kartu grafis dan driver grafis dan dijalankan dalam mode kernel.

Ketika model benar-benar dirender setiap frame, apakah prosesor GPU harus mengambil datanya setiap kali dari memori GPU, bahkan jika suatu model merender beberapa kali berurutan?

Sama seperti CPU, GPU diizinkan untuk memesan ulang instruksi GPU dan operasi akses memori , (baca: eksekusi tidak berjalan ) sehingga kemungkinan besar GPU akan menangani skenario yang Anda sebutkan dengan mengakses memori yang ada dalam cache (biasanya diakses baru-baru ini ), tapi terkadang tidak bisa melakukan ini.

Jelas kartu grafis memiliki RAM terbatas - ketika itu tidak dapat menampung semua data model yang diperlukan untuk rendering 1 frame. Saya kira itu terus mengambil (sebagian) dari RAM CPU setiap frame, apakah itu benar?

Anda tidak ingin ini terjadi. Tetapi terlepas dari itu terjadi GPU akan mulai memindahkan memori antara RAM dan VRAM (prosesor perintah pada GPU yang bertanggung jawab untuk ini) yang akan membuat rendering lebih lambat yang akan membuat GPU berhenti, karena perlu menunggu data untuk disalin dari / ke V / RAM.

Ada yang mengirim data ke GPU dan ada pengaturan / pengikatan buffer seperti saat ini. Apakah yang terakhir menyebabkan aliran data?

GPU berisi buffer perintah , dan semua perintah API dikirimkan ke buffer ini, perhatikan bahwa ini dapat terjadi secara bersamaan dengan data yang sedang disalin ke GPU. The cincin perintah buffer antrian komunikasi antara CPU dan GPU , perintah apapun bahwa kebutuhan akan dieksekusi kebutuhan untuk diserahkan ke antrian sehingga dapat execulated oleh GPU. Sama seperti operasi yang mengikat buffer baru perlu diserahkan ke GPU sehingga dapat mengakses beberapa lokasi memori.

Itulah salah satu alasan glBegin / glEnd tidak digunakan lagi, mengirimkan perintah baru membutuhkan sinkronisasi antrian (menggunakan pagar / penghalang memori).

masukkan deskripsi gambar di sini

Adapun poin Anda yang lain:

Buat buffer dengan inisialisasi

Anda dapat mengalokasikan buffer tanpa inisialisasi dan menyimpannya untuk digunakan nanti. Atau Anda dapat mengalokasikannya sebagai buffer dan menyalin data pada saat yang sama (berbicara tentang level API).

pembaruan data buffer

Anda dapat menggunakan glMapBuffer untuk memperbarui memori di sisi GPU. apakah memori akan disalin dari / ke RAM sebenarnya bukan standar, dan akan sangat bervariasi tergantung pada Vendor, tipe dan driver GPU.

Panggilan draw API (di sini saya ingin mendengar dari Anda apa yang sebenarnya terjadi di sana).

Poin kedua saya dalam pertanyaan utama mencakup ini.

mengikat buffer sebagai aktif (apakah ini hanya cara untuk memberi tahu API bahwa saya ingin buffer ini> di-render dalam draw call berikutnya dan tidak melakukan apa-apa dengan sendirinya?)

Anggap mengikat menggunakan thispointer dalam bahasa berorientasi objek apa pun, meskipun tidak sepenuhnya sama, setiap panggilan API konsekuen akan relatif terhadap buffer mengikat itu.

concept3d
sumber
3

Secara umum batas dan keterlibatan cpu versus gpu spesifik pada platform, tetapi kebanyakan dari mereka mengikuti model ini: cpu memiliki beberapa ram, gpu juga dan Anda dapat memindahkan memori (dalam beberapa kasus ram dibagi tetapi untuk Demi kesederhanaan mari kita tetap memisahkan domba).

Poin pertama : Data yang Anda inisialisasi Anda dapat memilih untuk menyimpannya di ram CPU atau pada ram GPU, dan merupakan keuntungan untuk keduanya. Ketika Anda membuat sesuatu, GPU harus melakukan tugas berat sehingga jelas bahwa data yang sudah ada di GPU mem akan memberikan kinerja yang lebih baik. untuk CPU, pertama-tama harus mengirim data ke GPU (yang dapat memilih untuk menyimpannya sebentar) dan kemudian melakukan rendering.

Poin kedua : Ada banyak trik dalam rendering tetapi cara utama dilakukan adalah dengan poligon. Pada bingkai, GPU akan membuat objek yang terbuat dari poligon satu per satu dan setelah selesai GPU akan mengirim gambar ke layar. Tidak ada konsep seperti objek, hanya ada poligon dan cara Anda menyatukannya akan membuat gambar. tugas GPU adalah memproyeksikan poligon tersebut dari 3d ke 2d dan menerapkan efek (jika diinginkan). Poligon hanya berjalan dengan cara CPU-> GPU-> LAYAR atau GPU-> LAYAR secara langsung (jika poligon sudah ada di ram GPU)

Poin ketiga : Ketika Anda membuat animasi misalnya, lebih baik menjaga data tetap dekat dengan cpu karena di sana ia melakukan pengangkatan berat, tidak akan optimal untuk menyimpan data dalam GPU, pindahkan ke CPU dan kembalikan setiap frame. Ada banyak contoh lain seperti ini untuk dihitung, tetapi secara umum semua data akan tetap dekat dengan siapa pun yang melakukan perhitungan. Biasanya Anda ingin memindahkan data sebanyak mungkin ke ram GPU untuk mendapatkan kinerja.

Pengiriman data aktual ke gpu dilakukan oleh API yang Anda gunakan (directx / opengl atau lainnya) dan konsep pengikatan dan hal-hal seperti ini hanyalah abstraksi sehingga API memahami apa yang ingin Anda lakukan.

Edit untuk hasil edit:

  • buffer creation with initialisation: itu seperti perbedaan antara int a = new int[10]dan a[0] = 0,a[1] = 1.... etc ketika Anda membuat buffer Anda memberikan ruang untuk data dan ketika Anda init data Anda meletakkan barang-barang yang Anda inginkan di sana.

  • buffer data updatejika ada di cpu ram maka Anda vertex * verticesdapat bermain dengannya, jika tidak ada di sana, Anda harus memindahkannya dari GPU vertex * vertices = map(buffer_id);(peta adalah fungsi mitologis yang harus memindahkan data dari GPU ke CPU ram, ia juga memiliki oposite buffer_id = create_buffer(vertices);

  • binding the buffer as activeitu hanya sebuah konsep yang mereka sebut bindingrendering adalah proses yang kompleks dan itu seperti memanggil fungsi dengan 10.000 parameter. Binding hanyalah istilah yang mereka gunakan untuk memberi tahu buffer mana yang digunakan. Tidak ada keajaiban nyata di balik istilah ini, itu tidak mengkonversi atau memindahkan atau merealokasi buffer, hanya memberitahu pengemudi bahwa pada panggilan imbang berikutnya menggunakan buffer ini.

  • API draw callSetelah semua penjilidan mengikat dan mengatur ini adalah tempat di mana karet bertemu jalan. Panggilan undian akan mengambil semua data (atau id yang mengarah ke data) yang Anda tentukan, mengirimkannya ke GPU (jika perlu) dan memberi tahu GPU untuk mulai menghitung angka-angkanya. Ini tidak sepenuhnya benar pada semua platform ada banyak perbedaan, tetapi untuk hal yang sederhana undian akan memberitahu GPU untuk .... menggambar.

Raxvan
sumber
2

Jawaban yang paling benar adalah, itu tergantung pada bagaimana Anda memprogramnya, tetapi ini adalah hal yang baik untuk dikhawatirkan. Sementara GPU telah menjadi sangat cepat, bandwidth ke dan dari RAM GPU tidak, dan akan menjadi hambatan Anda yang paling membuat frustrasi.

Apakah datanya dikirim ke memori GPU hanya sekali dan duduk di sana selamanya?

Semoga saja iya. Untuk kecepatan rendering, Anda ingin sebanyak mungkin data disimpan di GPU, alih-alih mengirim ulang setiap frame. VBO melayani tujuan yang tepat ini. Ada VBO statis dan dinamis, yang pertama terbaik untuk model statis, dan yang terakhir terbaik untuk model yang simpulnya akan mengubah setiap frame (katakanlah, sistem partikel). Bahkan ketika datang ke VBO dinamis, Anda tidak ingin mengirim ulang semua simpul setiap frame; hanya yang berubah.

Dalam kasus bangunan Anda, data titik hanya akan duduk di sana, dan satu-satunya hal yang berubah adalah matriks Anda (model / dunia, proyeksi, dan tampilan).

Dalam kasus sistem partikel, saya membuat VBO dinamis yang cukup besar untuk menyimpan jumlah maksimum partikel yang pernah ada untuk sistem itu. Setiap frame saya mengirim data untuk partikel yang dipancarkan frame itu, bersama dengan beberapa seragam, dan itu saja. Ketika saya menggambar, saya bisa menentukan titik awal dan akhir di VBO itu, jadi saya tidak perlu menghapus data partikel. Saya hanya bisa mengatakan jangan menggambar itu.

Ketika model benar-benar dirender setiap frame, apakah prosesor GPU harus mengambil datanya setiap kali dari memori GPU? Yang saya maksud adalah - jika saya memiliki 2 model yang dirender beberapa kali masing-masing - apakah penting jika saya pertama kali merender yang pertama beberapa kali dan kemudian yang kedua beberapa kali atau jika saya merender yang pertama hanya sekali, yang kedua hanya sekali dan terus menyisipkannya seperti itu?

Tindakan mengirim beberapa panggilan undian alih-alih hanya satu adalah batas yang jauh lebih besar. Lihat render yang dipasang; mungkin banyak membantu Anda dan membuat jawaban untuk pertanyaan ini tidak berguna. Saya punya beberapa masalah driver dengan itu yang saya belum berhasil, tetapi jika Anda bisa membuatnya bekerja, maka masalah terpecahkan.

Jelas kartu grafis memiliki RAM terbatas - ketika itu tidak dapat menampung semua data model yang diperlukan untuk rendering 1 frame. Saya kira itu terus mengambil (sebagian) dari RAM CPU setiap frame, apakah itu benar?

Anda tidak ingin kehabisan RAM GPU. Jika Anda melakukannya, maka ubah hal-hal yang tidak Anda lakukan. Dalam skenario yang sangat hipotetis bahwa Anda benar-benar kehabisan, itu mungkin akan crash entah bagaimana, tetapi saya belum pernah melihat itu terjadi jadi saya jujur ​​tidak tahu.

Saya lupa membuat satu perbedaan: ada yang mengirim data ke GPU dan ada pengaturan / pengikatan buffer seperti saat ini. Apakah yang terakhir menyebabkan aliran data?

Tidak ada aliran data yang signifikan, tidak. Ada beberapa biaya untuk itu, tetapi itu berlaku untuk setiap baris kode yang Anda tulis. Mencari tahu berapa biaya Anda, sekali lagi, untuk apa membuat profil.

pembuatan buffer dengan inisialisasi

Jawaban Raxvan terdengar bagus, tetapi itu tidak cukup akurat. Di OpenGL, membuat buffer tidak menyimpan ruang apa pun. Jika Anda ingin memesan ruang tanpa melewatkan data apa pun, Anda dapat memanggil glBufferData dan hanya memberikan nol. (Lihat bagian catatan di sini .)

pembaruan data buffer

Saya kira maksud Anda glBufferData, atau fungsi lain seperti itu, kan? Di sinilah transfer data nyata terjadi. (Kecuali Anda lulus nol, seperti yang saya katakan di paragraf terakhir.)

mengikat buffer sebagai aktif (apakah itu hanya cara untuk memberitahu API bahwa saya ingin buffer ini di-render dalam draw call berikutnya dan tidak melakukan apa-apa dengan sendirinya?)

Ya, tetapi bisa melakukan sedikit lebih dari itu. Sebagai contoh, jika Anda mengikat VAO (objek array verteks), kemudian mengikat VBO, VBO itu terikat ke VAO. Kemudian, jika Anda mengikat VAO itu lagi, dan memanggil glDrawArrays, ia akan tahu VBO apa yang harus digambar.

Perhatikan bahwa sementara banyak tutorial membuat Anda membuat VAO untuk setiap VBO, saya telah diberitahu bahwa ini bukan tujuan penggunaannya. Seharusnya Anda membuat satu VAO dan menggunakannya dengan setiap VBO yang memiliki atribut yang sama. Saya belum mencoba ini, jadi saya tidak bisa mengatakan dengan pasti apakah ini lebih baik atau lebih buruk.

Panggilan draw API

Apa yang terjadi di sini cukup mudah (dari sudut pandang kami). Katakanlah Anda mengikat VAO, lalu panggil glDrawArrays. Anda menentukan titik awal, dan jumlah, dan itu menjalankan vertex shader Anda untuk setiap vertex dalam rentang itu, yang pada gilirannya melewati outputnya ke bawah garis. Seluruh proses itu adalah esai lain.

Icy Defiance
sumber
"lalu masalah terpecahkan" Ya, instancing akan banyak membantu tetapi tanpa itu saya masih harus melakukan panggilan draw untuk setiap objek. Amout yang sama dalam kedua kasus. Jadi saya ingin tahu apakah pesanan itu penting.
NPS
@NPS - Itu penting beberapa . Jika mereka dipesan sehingga Anda tidak harus terus mengganti binding Anda, ya, itu mungkin akan menjadi jumlah yang sangat kecil lebih cepat. Tetapi jika Anda harus menyortirnya, itu mungkin akan jauh, jauh lebih mahal. Ada terlalu banyak variabel yang bergantung pada implementasi Anda untuk mengatakan lebih dari itu.
Icy Defiance