Tôi đang viết một trò chơi bằng C ++ bằng OpenGL.
Đối với những người không biết, với API OpenGL, bạn thực hiện rất nhiều cuộc gọi đến những thứ như glGenBuffersvà glCreateShadervv Các loại trả về GLuintnày là các định danh duy nhất cho những gì bạn vừa tạo. Thứ được tạo ra sống trên bộ nhớ GPU.
Xem xét rằng Bộ nhớ GPU đôi khi bị hạn chế, bạn không muốn tạo hai thứ giống nhau khi chúng được sử dụng bởi nhiều đối tượng.
Ví dụ, Shader. Bạn liên kết một Chương trình Shader và sau đó bạn có một GLuint. Khi bạn hoàn thành với Shader, bạn nên gọi glDeleteShader(hoặc một cái gì đó để ảnh hưởng).
Bây giờ, giả sử tôi có một hệ thống phân cấp lớp nông như:
class WorldEntity
{
public:
/* ... */
protected:
ShaderProgram* shader;
/* ... */
};
class CarEntity : public WorldEntity
{
/* ... */
};
class PersonEntity: public WorldEntity
{
/* ... */
};
Bất kỳ mã nào tôi từng thấy sẽ yêu cầu tất cả các Nhà xây dựng phải ShaderProgram*truyền cho nó để được lưu trữ trong WorldEntity. ShaderProgramlà lớp của tôi gói gọn sự ràng buộc của a GLuintvới trạng thái đổ bóng hiện tại trong bối cảnh OpenGL cũng như một vài điều hữu ích khác mà bạn cần làm với Shader.
Vấn đề tôi gặp phải là:
- Có rất nhiều tham số cần thiết để xây dựng một
WorldEntity(xem xét rằng có thể có một lưới, một shader, một loạt các kết cấu, v.v., tất cả chúng có thể được chia sẻ, vì vậy chúng được chuyển qua như con trỏ) - Bất cứ điều gì đang tạo ra
WorldEntitynhu cầu để biết những gìShaderProgramnó cần - Điều này có thể yêu cầu một số loại gulp
EntityManagerbiết được trường hợp nàoShaderProgramđể truyền cho các thực thể khác nhau.
Vì vậy, bây giờ bởi vì có Managercác lớp cần phải tự đăng ký EntityManagercùng với ShaderProgramthể hiện mà chúng cần hoặc tôi cần một switchngười quản lý lớn mà tôi cần cập nhật cho mọi WorldEntityloại dẫn xuất mới .
Suy nghĩ đầu tiên của tôi là tạo ra một ShaderManagerlớp (tôi biết, Người quản lý rất tệ) mà tôi chuyển bằng tham chiếu hoặc con trỏ đến các WorldEntitylớp để họ có thể tạo bất cứ thứ gì ShaderProgramhọ muốn, thông qua ShaderManagervà ShaderManagercó thể theo dõi các ShaderPrograms đã có , vì vậy nó có thể trả lại cái đã tồn tại hoặc tạo cái mới nếu cần.
(Tôi có thể lưu trữ ShaderPrograms thông qua hàm băm của tên tệp của ShaderProgrammã nguồn thực tế)
Vậy bây giờ:
- Bây giờ tôi chuyển con trỏ đến
ShaderManagerthay vìShaderProgramvậy, vẫn còn rất nhiều tham số - Tôi không cần
EntityManager, chính các thực thể sẽ biết thể hiện nàoShaderProgramđể tạo vàShaderManagersẽ xử lýShaderPrograms thực tế . - Nhưng bây giờ tôi không biết khi nào
ShaderManagercó thể xóa một cách an toànShaderProgram.
SO bây giờ tôi đã thêm tính tham khảo để tôi ShaderProgramlớp xóa nội bộ của mình GLuintqua glDeleteProgramvà tôi đi với ShaderManager.
Vậy bây giờ:
- Một đối tượng có thể tạo ra bất cứ thứ gì
ShaderProgramnó cần - Nhưng bây giờ có trùng lặp
ShaderProgramvì không có Trình quản lý bên ngoài theo dõi
Cuối cùng tôi đến để đưa ra một trong hai quyết định:
1. Lớp tĩnh
A static classđược viện dẫn để tạo ShaderPrograms. Nó giữ một dấu vết nội bộ ShaderProgramdựa trên hàm băm của tên tệp - điều này có nghĩa là tôi không còn cần phải truyền con trỏ hoặc tham chiếu đến ShaderPrograms hoặc ShaderManagers xung quanh, vì vậy ít tham số hơn - Có WorldEntitiestất cả kiến thức về thể hiện của ShaderProgramchúng muốn tạo
Điều này mới static ShaderManagercần:
- giữ số lần
ShaderProgramsử dụng a và tôiShaderProgramkhông thể sao chép HOẶC ShaderPrograms đếm các tham chiếu của chúng và chỉ gọi hàmglDeleteProgramhủy của chúng khi số đếm là0ANDShaderManagerkiểm tra định kỳShaderProgramcác số có 1 và loại bỏ chúng.
Nhược điểm của phương pháp này tôi thấy là:
Tôi có lớp tĩnh toàn cầu có thể là một vấn đề. Bối cảnh OpenGL cần được tạo trước khi gọi bất kỳ
glXchức năng nào . Vì vậy, có khả năng,WorldEntitycó thể được tạo và cố gắng tạoShaderProgramtrước khi tạo Bối cảnh OpenGL, điều này sẽ dẫn đến sự cố.Cách duy nhất để giải quyết vấn đề này là quay lại mọi thứ xung quanh dưới dạng con trỏ / tham chiếu hoặc có một lớp GLContext toàn cầu có thể được truy vấn hoặc giữ mọi thứ trong một lớp tạo ra Bối cảnh khi xây dựng. Hoặc có thể chỉ là một boolean toàn cầu
IsContextCreatedcó thể được kiểm tra. Nhưng tôi lo lắng rằng điều này mang lại cho tôi mã xấu xí ở khắp mọi nơi.Những gì tôi có thể thấy sự biến thành là:
- Lớp lớn
Enginecó tất cả các lớp khác ẩn bên trong nó để nó có thể kiểm soát trật tự xây dựng / giải cấu trúc một cách thích hợp. Điều này có vẻ như là một mớ hỗn độn lớn về mã giao diện giữa người sử dụng động cơ và động cơ, giống như một trình bao bọc trên một trình bao bọc - Toàn bộ các lớp "Trình quản lý" theo dõi các thể hiện và xóa mọi thứ khi cần thiết. Đây có thể là một điều ác cần thiết?
- Lớp lớn
VÀ
- Khi nào thực sự xóa
ShaderPrograms ra khỏistatic ShaderManager? Cứ vài phút? Mỗi vòng lặp trò chơi? Tôi duyên dáng xử lý việc biên dịch lại một shader trong trường hợp khi mộtShaderProgramđã bị xóa nhưng sau đó mộtWorldEntityyêu cầu mới ; nhưng tôi chắc chắn có một cách tốt hơn.
2. Một phương pháp tốt hơn
Đó là những gì tôi đang yêu cầu ở đây
WorldEntitys; không phải là thay đổi một số vấn đề? Bởi vì bây giờ lớp WorldFactory cần truyền cho mỗi WolrdEntity chương trình ShaderPro chính xác.