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ư glGenBuffers
và glCreateShader
vv Các loại trả về GLuint
nà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
. ShaderProgram
là lớp của tôi gói gọn sự ràng buộc của a GLuint
vớ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
WorldEntity
nhu cầu để biết những gìShaderProgram
nó cần - Điều này có thể yêu cầu một số loại gulp
EntityManager
biế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ó Manager
các lớp cần phải tự đăng ký EntityManager
cùng với ShaderProgram
thể hiện mà chúng cần hoặc tôi cần một switch
người quản lý lớn mà tôi cần cập nhật cho mọi WorldEntity
loại dẫn xuất mới .
Suy nghĩ đầu tiên của tôi là tạo ra một ShaderManager
lớ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 WorldEntity
lớp để họ có thể tạo bất cứ thứ gì ShaderProgram
họ muốn, thông qua ShaderManager
và ShaderManager
có thể theo dõi các ShaderProgram
s đã 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ữ ShaderProgram
s thông qua hàm băm của tên tệp của ShaderProgram
mã nguồn thực tế)
Vậy bây giờ:
- Bây giờ tôi chuyển con trỏ đến
ShaderManager
thay vìShaderProgram
vậ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àShaderManager
sẽ xử lýShaderProgram
s thực tế . - Nhưng bây giờ tôi không biết khi nào
ShaderManager
có 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 ShaderProgram
lớp xóa nội bộ của mình GLuint
qua glDeleteProgram
và 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ì
ShaderProgram
nó cần - Nhưng bây giờ có trùng lặp
ShaderProgram
vì 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 ShaderProgram
s. Nó giữ một dấu vết nội bộ ShaderProgram
dự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 ShaderProgram
s hoặc ShaderManager
s xung quanh, vì vậy ít tham số hơn - Có WorldEntities
tất cả kiến thức về thể hiện của ShaderProgram
chúng muốn tạo
Điều này mới static ShaderManager
cần:
- giữ số lần
ShaderProgram
sử dụng a và tôiShaderProgram
không thể sao chép HOẶC ShaderProgram
s đếm các tham chiếu của chúng và chỉ gọi hàmglDeleteProgram
hủy của chúng khi số đếm là0
ANDShaderManager
kiểm tra định kỳShaderProgram
cá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ỳ
glX
chức năng nào . Vì vậy, có khả năng,WorldEntity
có thể được tạo và cố gắng tạoShaderProgram
trướ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
IsContextCreated
có 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
Engine
có 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
ShaderProgram
s 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ộtWorldEntity
yê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
WorldEntity
s; 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.