Estoy escribiendo un juego en C ++ usando OpenGL.
Para aquellos que no saben, con la API de OpenGL haces muchas llamadas a cosas como glGenBuffersy glCreateShaderetc. Estos tipos de devolución GLuintson identificadores únicos de lo que acabas de crear. Lo que se está creando vive en la memoria de la GPU.
Teniendo en cuenta que la memoria GPU a veces es limitada, no desea crear dos cosas que sean iguales cuando sean utilizadas por múltiples objetos.
Por ejemplo, Shaders. Vincula un programa de sombreado y luego tiene un GLuint. Cuando hayas terminado con el Shader, deberías llamar glDeleteShader(o algo por el estilo).
Ahora, digamos que tengo una jerarquía de clase superficial como:
class WorldEntity
{
public:
/* ... */
protected:
ShaderProgram* shader;
/* ... */
};
class CarEntity : public WorldEntity
{
/* ... */
};
class PersonEntity: public WorldEntity
{
/* ... */
};
Cualquier código que haya visto requeriría que todos los Constructores hayan ShaderProgram*pasado para ser almacenados en el WorldEntity. ShaderProgrames mi clase que encapsula el enlace de GLuinta al estado actual del sombreador en el contexto de OpenGL, así como algunas otras cosas útiles que debe hacer con los sombreadores.
El problema que tengo con esto es:
- Hay muchos parámetros necesarios para construir un
WorldEntity(considere que puede haber una malla, un sombreador, un montón de texturas, etc., todos los cuales podrían compartirse, por lo que se pasan como punteros) - Lo que sea que esté creando las
WorldEntitynecesidades para saber loShaderProgramque necesita - Esto probablemente requiera algún tipo de clase de trago
EntityManagerque sepa qué instancia de quéShaderProgrampasar a diferentes entidades.
Así que ahora porque hay una Managernecesidad de las clases para registrarse EntityManagercon la ShaderPrograminstancia que necesitan, o necesito un gran imbécil switchen el administrador que necesito actualizar para cada nuevo WorldEntitytipo derivado.
Mi primer pensamiento fue crear una ShaderManagerclase (lo sé, los gerentes son malos) que paso por referencia o puntero a las WorldEntityclases para que puedan crear lo ShaderProgramque quieran, a través de ShaderManagery ShaderManagerpueden realizar un seguimiento de los ShaderPrograms ya existentes , para que pueda devuelva uno que ya existe o cree uno nuevo si es necesario.
(Podría almacenar el ShaderProgramcorreo electrónico a través del hash de los nombres de archivo del ShaderProgramcódigo fuente real)
Y ahora:
- Ahora estoy pasando punteros a en
ShaderManagerlugar deShaderProgram, por lo que todavía hay muchos parámetros - No necesito un
EntityManager, las entidades mismas sabrán qué instanciaShaderProgramcrear, yShaderManagermanejarán elShaderPrograms real . - Pero ahora no sé cuándo
ShaderManagerpuede eliminar de forma segura unoShaderProgramque contiene.
Así que ahora he agregado un recuento de referencias a mi ShaderProgramclase que elimina su GLuintvía interna glDeleteProgramy la elimino ShaderManager.
Y ahora:
- Un objeto puede crear lo
ShaderProgramque necesita - Pero ahora hay duplicados
ShaderProgramporque no hay un administrador externo que realice un seguimiento
Finalmente vengo a tomar una de dos decisiones:
1. Clase estática
A static classque se invoca para crear ShaderPrograms. Mantiene un seguimiento interno de ShaderPrograms basado en un hash de los nombres de archivo, esto significa que ya no necesito pasar punteros o referencias a ShaderPrograms o ShaderManagers, por lo que menos parámetros: WorldEntitiestienen todo el conocimiento sobre la instancia de ShaderProgramque quieren crear
Esta nueva static ShaderManagernecesita:
- mantengo un recuento de la cantidad de veces que
ShaderProgramse usa a y no hagoShaderProgramcopias O ShaderPrograms cuenta sus referencias y solo llamaglDeletePrograma su destructor cuando el recuento es0YShaderManagerperiódicamente buscaShaderProgram's con un recuento de 1 y los descarta.
Las desventajas de este enfoque que veo son:
Tengo una clase global estática que podría ser un problema. El contexto OpenGL debe crearse antes de invocar cualquier
glXfunción. Por lo tanto,WorldEntitypodría crearse un e intentar crear unoShaderProgramantes de la creación del contexto OpenGL, lo que provocará un bloqueo.La única forma de evitar esto es volver a pasar todo como punteros / referencias, o tener una clase global GLContext que se pueda consultar, o mantener todo en una clase que crea el Contexto en la construcción. O tal vez solo un booleano global
IsContextCreatedque se puede verificar. Pero me preocupa que esto me dé un código feo en todas partes.A lo que puedo ver la devolución es:
- La gran
Engineclase que tiene todas las demás clases ocultas dentro de ella para que pueda controlar el orden de construcción / deconstrucción adecuadamente. Esto parece un gran lío de código de interfaz entre el usuario del motor y el motor, como un contenedor sobre un contenedor - Toda una serie de clases "Manager" que realizan un seguimiento de las instancias y eliminan cosas cuando es necesario. Esto podría ser un mal necesario?
- La gran
Y
- ¿Cuándo borrar realmente
ShaderPrograms de lastatic ShaderManager? ¿Cada pocos minutos? Cada juego de bucle? Estoy manejando con gracia la recompilación de un sombreador en el caso en queShaderProgramse eliminó un pero luego un nuevo loWorldEntitysolicita; Pero estoy seguro de que hay una mejor manera.
2. Un mejor método
Eso es lo que pido aquí
WorldEntitys; ¿No es eso cambiar algo del problema? Porque ahora la clase WorldFactory necesita pasar a cada WolrdEntity el ShaderProgram correcto.