Saya sedang menulis game di C ++ menggunakan OpenGL.
Bagi mereka yang tidak tahu, dengan OpenGL API Anda melakukan banyak panggilan ke hal-hal seperti glGenBuffers
dan glCreateShader
lain-lain. Jenis pengembalian ini GLuint
adalah pengidentifikasi unik untuk apa yang baru saja Anda buat. Benda yang diciptakan hidup dengan memori GPU.
Mengingat Memori GPU terkadang terbatas, Anda tidak ingin membuat dua hal yang sama ketika digunakan oleh banyak objek.
Misalnya, Shader. Anda menautkan Program Shader dan kemudian Anda memiliki GLuint
. Setelah selesai dengan Shader, Anda harus menelepon glDeleteShader
(atau sesuatu yang mempengaruhi).
Sekarang, katakanlah saya memiliki hierarki kelas yang dangkal seperti:
class WorldEntity
{
public:
/* ... */
protected:
ShaderProgram* shader;
/* ... */
};
class CarEntity : public WorldEntity
{
/* ... */
};
class PersonEntity: public WorldEntity
{
/* ... */
};
Kode apa pun yang pernah saya lihat akan mengharuskan semua Konstruktor memiliki ShaderProgram*
izin untuk disimpan dalam WorldEntity
. ShaderProgram
adalah kelas saya yang merangkum pengikatan GLuint
ke keadaan shader saat ini dalam konteks OpenGL serta beberapa hal bermanfaat lainnya yang perlu Anda lakukan dengan Shader.
Masalah yang saya miliki dengan ini adalah:
- Ada banyak parameter yang diperlukan untuk membangun a
WorldEntity
(pertimbangkan bahwa mungkin ada mesh, shader, sekelompok tekstur dll, yang semuanya dapat dibagikan, sehingga dilewatkan sebagai petunjuk) - Apa pun yang menciptakan
WorldEntity
kebutuhan untuk mengetahui apaShaderProgram
yang dibutuhkan - Ini mungkin membutuhkan semacam kelas tegukan
EntityManager
yang tahu contoh apa yangShaderProgram
harus dilewatkan ke entitas yang berbeda.
Jadi sekarang karena ada Manager
kelas-kelas yang perlu mendaftar sendiri EntityManager
bersama dengan ShaderProgram
instance yang mereka butuhkan, atau saya perlu keledai besar switch
di manajer yang perlu saya perbarui untuk setiap WorldEntity
jenis turunan baru .
Pikiran pertama saya adalah membuat ShaderManager
kelas (saya tahu, Manajer buruk) yang saya lewati dengan referensi atau penunjuk ke WorldEntity
kelas sehingga mereka dapat membuat apa pun yang ShaderProgram
mereka inginkan, melalui ShaderManager
dan ShaderManager
dapat melacak ShaderProgram
s yang sudah ada , sehingga dapat kembalikan yang sudah ada atau buat yang baru jika perlu.
(Saya bisa menyimpan ShaderProgram
s melalui hash dari nama file ShaderProgram
kode sumber aktual s)
Jadi sekarang:
- Saya sekarang meneruskan pointer ke
ShaderManager
bukanShaderProgram
, jadi masih ada banyak parameter - Saya tidak memerlukan
EntityManager
, entitas itu sendiri akan tahu apa yangShaderProgram
harus dibuat, danShaderManager
akan menangani yang sebenarnyaShaderProgram
. - Tapi sekarang saya tidak tahu kapan
ShaderManager
bisa dengan aman menghapusShaderProgram
yang dimilikinya.
Jadi sekarang saya telah menambahkan penghitungan referensi ke ShaderProgram
kelas saya yang menghapus internal GLuint
melalui glDeleteProgram
dan saya tidak melakukannya ShaderManager
.
Jadi sekarang:
- Objek dapat membuat apa pun
ShaderProgram
yang dibutuhkannya - Tapi sekarang ada duplikat
ShaderProgram
karena tidak ada Manajer eksternal yang melacak
Akhirnya saya datang untuk membuat satu dari dua keputusan:
1. Kelas Statis
A static class
yang dipanggil untuk membuat ShaderProgram
s. Itu membuat trek internal ShaderProgram
berdasarkan hash dari nama file - ini berarti saya tidak perlu lagi melewati pointer atau referensi ke ShaderProgram
atau ShaderManager
sekitar, jadi lebih sedikit parameter - WorldEntities
Memiliki semua pengetahuan tentang contoh yang ShaderProgram
ingin mereka buat
static ShaderManager
Kebutuhan baru ini untuk:
- pertahankan hitungan berapa kali a
ShaderProgram
digunakan dan sayaShaderProgram
tidak membuat salinan ATAU ShaderProgram
menghitung referensi mereka dan hanya memanggilglDeleteProgram
destruktor mereka ketika hitungan itu0
DANShaderManager
secara berkala memeriksa untukShaderProgram
dengan hitungan 1 dan membuangnya.
Kelemahan dari pendekatan ini yang saya lihat adalah:
Saya memiliki kelas statis global yang mungkin menjadi masalah. Konteks OpenGL perlu dibuat sebelum memanggil
glX
fungsi apa pun . Jadi berpotensi,WorldEntity
mungkin dibuat dan mencoba untuk membuatShaderProgram
sebelum pembuatan Konteks OpenGL, yang akan mengakibatkan crash.Satu-satunya cara untuk mengatasi hal ini adalah kembali menyerahkan semua yang ada sebagai pointer / referensi, atau memiliki kelas global GLContext yang dapat ditanyakan, atau memegang segala sesuatu di kelas yang menciptakan Konteks pada konstruksi. Atau mungkin hanya boolean global
IsContextCreated
yang dapat diperiksa. Tapi saya khawatir ini memberi saya kode jelek di mana-mana.Apa yang bisa saya lihat adalah pindah ke:
- Kelas besar
Engine
yang memiliki setiap kelas lain tersembunyi di dalamnya sehingga dapat mengontrol urutan konstruksi / dekonstruksi dengan tepat. Ini tampak seperti kekacauan besar kode antarmuka antara pengguna mesin dan mesin, seperti pembungkus di atas pembungkus - Seluruh kelas "Manajer" yang melacak instance dan menghapus hal-hal saat diperlukan. Ini mungkin kejahatan yang perlu?
- Kelas besar
DAN
- Kapan sebenarnya membersihkan
ShaderProgram
sstatic ShaderManager
? Setiap beberapa menit? Setiap Loop Game? Saya dengan anggun menangani penyusunan ulang shader dalam kasus ketika sebuahShaderProgram
dihapus tetapi kemudian baruWorldEntity
memintanya; tapi saya yakin ada cara yang lebih baik.
2. Metode yang lebih baik
Itulah yang saya minta di sini
WorldEntity
s; Bukankah itu menggeser beberapa masalah? Karena sekarang kelas WorldFactory harus lulus setiap WolrdEntity ShaderProgram yang benar.