Saya sedang menulis game di C ++ menggunakan OpenGL.
Bagi mereka yang tidak tahu, dengan OpenGL API Anda melakukan banyak panggilan ke hal-hal seperti glGenBuffersdan glCreateShaderlain-lain. Jenis pengembalian ini GLuintadalah 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. ShaderProgramadalah kelas saya yang merangkum pengikatan GLuintke 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
WorldEntitykebutuhan untuk mengetahui apaShaderProgramyang dibutuhkan - Ini mungkin membutuhkan semacam kelas tegukan
EntityManageryang tahu contoh apa yangShaderProgramharus dilewatkan ke entitas yang berbeda.
Jadi sekarang karena ada Managerkelas-kelas yang perlu mendaftar sendiri EntityManagerbersama dengan ShaderPrograminstance yang mereka butuhkan, atau saya perlu keledai besar switchdi manajer yang perlu saya perbarui untuk setiap WorldEntityjenis turunan baru .
Pikiran pertama saya adalah membuat ShaderManagerkelas (saya tahu, Manajer buruk) yang saya lewati dengan referensi atau penunjuk ke WorldEntitykelas sehingga mereka dapat membuat apa pun yang ShaderProgrammereka inginkan, melalui ShaderManagerdan ShaderManagerdapat melacak ShaderPrograms yang sudah ada , sehingga dapat kembalikan yang sudah ada atau buat yang baru jika perlu.
(Saya bisa menyimpan ShaderPrograms melalui hash dari nama file ShaderProgramkode sumber aktual s)
Jadi sekarang:
- Saya sekarang meneruskan pointer ke
ShaderManagerbukanShaderProgram, jadi masih ada banyak parameter - Saya tidak memerlukan
EntityManager, entitas itu sendiri akan tahu apa yangShaderProgramharus dibuat, danShaderManagerakan menangani yang sebenarnyaShaderProgram. - Tapi sekarang saya tidak tahu kapan
ShaderManagerbisa dengan aman menghapusShaderProgramyang dimilikinya.
Jadi sekarang saya telah menambahkan penghitungan referensi ke ShaderProgramkelas saya yang menghapus internal GLuintmelalui glDeleteProgramdan saya tidak melakukannya ShaderManager.
Jadi sekarang:
- Objek dapat membuat apa pun
ShaderProgramyang dibutuhkannya - Tapi sekarang ada duplikat
ShaderProgramkarena tidak ada Manajer eksternal yang melacak
Akhirnya saya datang untuk membuat satu dari dua keputusan:
1. Kelas Statis
A static classyang dipanggil untuk membuat ShaderPrograms. Itu membuat trek internal ShaderProgramberdasarkan hash dari nama file - ini berarti saya tidak perlu lagi melewati pointer atau referensi ke ShaderProgramatau ShaderManagersekitar, jadi lebih sedikit parameter - WorldEntitiesMemiliki semua pengetahuan tentang contoh yang ShaderProgramingin mereka buat
static ShaderManagerKebutuhan baru ini untuk:
- pertahankan hitungan berapa kali a
ShaderProgramdigunakan dan sayaShaderProgramtidak membuat salinan ATAU ShaderProgrammenghitung referensi mereka dan hanya memanggilglDeleteProgramdestruktor mereka ketika hitungan itu0DANShaderManagersecara berkala memeriksa untukShaderProgramdengan 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
glXfungsi apa pun . Jadi berpotensi,WorldEntitymungkin dibuat dan mencoba untuk membuatShaderProgramsebelum 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
IsContextCreatedyang dapat diperiksa. Tapi saya khawatir ini memberi saya kode jelek di mana-mana.Apa yang bisa saya lihat adalah pindah ke:
- Kelas besar
Engineyang 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
ShaderProgramsstatic ShaderManager? Setiap beberapa menit? Setiap Loop Game? Saya dengan anggun menangani penyusunan ulang shader dalam kasus ketika sebuahShaderProgramdihapus tetapi kemudian baruWorldEntitymemintanya; tapi saya yakin ada cara yang lebih baik.
2. Metode yang lebih baik
Itulah yang saya minta di sini
WorldEntitys; Bukankah itu menggeser beberapa masalah? Karena sekarang kelas WorldFactory harus lulus setiap WolrdEntity ShaderProgram yang benar.