Mengambil keuntungan dari multithreading antara loop game dan openGL

10

Berbicara dalam konteks gim berbasis openGL renderer:

Mari kita asumsikan ada dua utas:

  1. Memperbarui logika dan fisika game, dll. Untuk objek dalam game

  2. Membuat panggilan draw openGL untuk setiap objek game berdasarkan data dalam objek game (utas 1 yang terus diperbarui)

Kecuali jika Anda memiliki dua salinan dari masing-masing objek game dalam keadaan saat ini dari permainan Anda harus menjeda Thread 1 sementara Thread 2 melakukan draw draw jika tidak, objek game akan diperbarui di tengah-tengah draw draw untuk objek tersebut, yang tidak diinginkan!

Tetapi menghentikan thread 1 untuk membuat panggilan draw dengan aman dari thread 2 membunuh seluruh tujuan multithreading / concurrency

Apakah ada pendekatan yang lebih baik untuk ini selain menggunakan ratusan atau ribuan atau menyinkronkan objek / pagar sehingga arsitektur multicore dapat dieksploitasi untuk kinerja?

Saya tahu saya masih bisa menggunakan multithreading untuk memuat tekstur dan mengkompilasi shader untuk objek yang belum menjadi bagian dari kondisi gim saat ini, tetapi bagaimana cara melakukannya untuk objek yang aktif / terlihat tanpa menyebabkan konflik dengan menggambar dan memperbarui?

Bagaimana jika saya menggunakan kunci sinkronisasi terpisah di setiap objek game? Dengan cara ini utas apa pun hanya akan memblokir pada satu objek (case ideal) daripada untuk seluruh siklus pembaruan / undian! Tetapi seberapa mahal mengambil kunci pada setiap objek (game mungkin memiliki seribu objek)?

Allahjane
sumber
1. Dengan asumsi hanya akan ada dua utas yang tidak skala dengan baik, 4 core sangat umum dan hanya akan meningkat. 2. Jawaban strategi praktik terbaik: terapkan Segregasi Tanggung Jawab Kueri Perintah dan Model Model / Komponen Entitas Aktor. 3. Jawaban realistis: cukup dengan cara C ++ tradisional dengan kunci dan barang-barang.
Den
Satu data umum, satu kunci.
Justin
@ cukup seperti yang saya katakan dengan kunci sinkronisasi. Satu-satunya keuntungan dari multithreading akan dibunuh secara brutal di siang hari, utas pembaruan harus menunggu sampai utas membuat panggilan untuk semua objek, maka utas akan menunggu sampai loop pembaruan selesai memperbarui barang. ! yang lebih buruk daripada pendekatan berulir tunggal
Allahjane
1
Sepertinya pendekatan multithreaded dalam game dengan grafis async api (misalnya openGL) masih menjadi topik aktif dan tidak ada solusi standar atau mendekati sempurna ini
Allahjane
1
Datastore yang umum untuk mesin Anda dan renderer Anda seharusnya tidak menjadi objek game Anda. Seharusnya ada beberapa representasi yang dapat diproses pemberi render secepat mungkin.
Justin

Jawaban:

13

Pendekatan yang Anda jelaskan, menggunakan kunci, akan sangat tidak efisien dan kemungkinan besar lebih lambat daripada menggunakan utas tunggal. Pendekatan lain untuk menyimpan salinan data di setiap utas mungkin akan bekerja dengan baik "kecepatan-bijaksana", tetapi dengan biaya memori yang rumit dan kompleksitas kode untuk menjaga salinan dalam sinkronisasi.

Ada beberapa pendekatan alternatif untuk ini, salah satu solusi populer untuk rendering multi-threaded adalah dengan menggunakan buffer-ganda perintah. Ini terdiri dari menjalankan back-end renderer di utas terpisah, di mana semua panggilan dan komunikasi dengan rendering API dilakukan. Thread front-end yang menjalankan logika game berkomunikasi dengan renderer back-end melalui buffer perintah (buffer ganda). Dengan pengaturan ini, Anda hanya memiliki satu titik sinkronisasi pada penyelesaian bingkai. Sementara front-end mengisi satu buffer dengan perintah render, back-end mengkonsumsi yang lain. Jika kedua utas seimbang, tidak ada yang harus kelaparan. Namun pendekatan ini bersifat suboptimal, karena memperkenalkan latensi pada frame yang diberikan, ditambah, driver OpenGL kemungkinan sudah melakukan ini dalam prosesnya sendiri, sehingga peningkatan kinerja harus diukur dengan cermat. Itu juga hanya menggunakan dua core, paling-paling. Pendekatan ini telah digunakan dalam beberapa game yang sukses, seperti Doom 3 dan Quake 3

Pendekatan yang lebih scalable yang membuat penggunaan multi-core CPU lebih baik adalah yang didasarkan pada tugas-tugas independen , di mana Anda menjalankan permintaan asinkron yang dilayani di utas sekunder, sedangkan utas yang memecat permintaan berlanjut dengan beberapa pekerjaan lain. Tugas idealnya tidak memiliki dependensi dengan utas lainnya, untuk menghindari kunci (juga menghindari data bersama / global seperti wabah!). Arsitektur berbasis tugas lebih dapat digunakan di bagian permainan yang dilokalkan, seperti animasi komputasi, penelusuran path AI, pembuatan prosedural, pemuatan dinamis alat peraga adegan, dll. Game secara alami penuh peristiwa, sebagian besar jenis peristiwa bersifat asink, mudah untuk membuatnya berjalan di utas terpisah.

Akhirnya, saya sarankan membaca:

Glampert
sumber
hanya penasaran! bagaimana Anda tahu Doom 3 menggunakan pendekatan ini? Saya pikir pengembang tidak pernah membiarkan ada teknik!
Allahjane
4
@Allahjane, Doom 3 adalah Open Source . Di tautan yang saya berikan, Anda akan menemukan ulasan tentang keseluruhan arsitektur permainan. Dan Anda salah, ya jarang menemukan game Open Source sepenuhnya, tetapi pengembang biasanya mengekspos teknik dan trik mereka di blog, makalah, dan acara seperti GDC .
Glampert
1
Hmm. Ini adalah bacaan yang luar biasa! terima kasih telah membuat saya sadar akan hal ini! Anda mendapatkan tanda centang :)
Allahjane