Antecedentes:
Estoy diseñando un sistema de renderizado 3D simple para una arquitectura de tipo de sistema de componente de entidad usando C ++ y OpenGL. El sistema consta de un renderizador y un gráfico de escena. Cuando termine la primera iteración del renderizador, podría distribuir el gráfico de escena en la arquitectura ECS. Por ahora es marcador de posición de una forma u otra. Si es posible, los siguientes son mis objetivos para el procesador:
- Simplicidad . Esto es para un proyecto de investigación y quiero poder cambiar y expandir mis sistemas fácilmente (de ahí el enfoque ECS).
- Rendimiento . Mi escena puede tener muchos modelos pequeños y también grandes volúmenes con mucha geometría. No es aceptable adquirir objetos del contexto OGL y la geometría del búfer en cada cuadro de renderizado. Estoy apuntando a la localidad de datos para evitar errores de caché.
- Flexibilidad . Debe poder representar sprites, modelos y volúmenes (vóxeles).
- Desacoplado . El gráfico de escena se puede refactorizar en la arquitectura central de ECS después de escribir mi renderizador.
- Modular . Sería bueno poder intercambiar diferentes renderizadores sin cambiar el gráfico de mi escena.
- Transparencia referencial , lo que significa que en cualquier momento puedo darle una escena válida y siempre se mostrará la misma imagen para esa escena. Este objetivo en particular no es necesariamente obligatorio. Pensé que ayudaría a simplificar la serialización de escenas (necesitaré poder guardar y cargar escenas) y darme flexibilidad para intercambiar diferentes escenas durante el tiempo de ejecución con fines de prueba / experimentación.
Problema e ideas:
Se me ocurrieron algunos enfoques diferentes para probar, pero estoy luchando con la forma de almacenar en caché los recursos OGL (VAO, VBO, sombreadores, etc.) para cada nodo de representación. Los siguientes son los diferentes conceptos de almacenamiento en caché que he pensado hasta ahora:
- Caché centralizada. Cada nodo de escena tiene una ID y el renderizador tiene un caché que asigna ID para representar los nodos. Cada nodo de representación contiene los VAO y VBO asociados con la geometría. Un error de caché adquiere recursos y asigna la geometría a un nodo de representación en el caché. Cuando se cambia la geometría, se establece una bandera sucia. Si el renderizador ve una bandera de geometría sucia mientras itera por los nodos de la escena, rechaza los datos utilizando el nodo de renderizado. Cuando se elimina un nodo de escena, se difunde un evento y el procesador elimina el nodo de procesamiento asociado de la caché mientras libera recursos. Alternativamente, el nodo está marcado para su eliminación y el procesador es responsable de eliminarlo. Creo que este enfoque logra el objetivo 6 más cercano al tiempo que considera 4 y 5. 2 sufre la complejidad adicional y la pérdida de la localidad de datos con búsquedas de mapas en lugar de acceso a la matriz.
- Caché distribuido . Similar arriba excepto que cada nodo de escena tiene un nodo de renderizado. Esto evita la búsqueda en el mapa. Para abordar la localidad de datos, los nodos de renderizado podrían almacenarse en el renderizador. Luego, los nodos de la escena podrían tener punteros para representar los nodos y el renderizador establece el puntero en un error de caché. Creo que este tipo de imita un enfoque de componente de entidad, por lo que sería coherente con el resto de la arquitectura. El problema aquí es que ahora los nodos de escena contienen datos específicos de implementación de renderizador. Si cambio la forma en que se representan las cosas en el renderizador (como renderizar sprites frente a volúmenes) ahora necesito cambiar el nodo de renderizado o agregar más "componentes" al nodo de escena (lo que también significa cambiar el gráfico de escena). En el lado positivo, esta parece ser la forma más simple de poner en funcionamiento mi renderizador de primera iteración.
- Metadatos distribuidos . Un componente de metadatos de caché de renderizador se almacena en cada nodo de escena. Estos datos no son específicos de la implementación, sino que contienen un ID, tipo y cualquier otro dato relevante que necesita la memoria caché. Luego, la búsqueda de caché se puede hacer directamente en una matriz usando la ID, y el tipo puede indicar qué tipo de enfoque de representación usar (como sprites vs volúmenes).
- Visitante + mapeo distribuido . El renderizador es un visitante y los nodos de escena son elementos en el patrón de visitante. Cada nodo de escena contiene una clave de caché (como los metadatos pero solo una ID) que solo el renderizador manipula. La ID se puede usar para la matriz en lugar de la búsqueda generalizada de mapas. El renderizador puede permitir que el nodo de escena distribuya una función de representación diferente según el tipo de nodo de escena, y cualquier caché puede usar la ID. Una identificación predeterminada o fuera de rango indicaría una falta de caché.
Como resolverías este problema? ¿O tienes alguna sugerencia? ¡Gracias por leer mi muro de texto!