Soy un programador gráfico novato y me he estado preguntando recientemente: ¿cómo fluyen los datos del modelo (mallas y materiales) desde la aplicación (memoria de la CPU) a la tarjeta gráfica (memoria de la GPU)? Supongamos que tengo un modelo estático (p. Ej., Un edificio) que cargo y configuro una vez y no cambio a lo largo de la vida útil de la aplicación.
- ¿Sus datos se envían a la memoria de la GPU solo una vez y se quedan allí para siempre?
- Cuando el modelo se procesa realmente cada cuadro, ¿los procesadores de GPU tienen que recuperar sus datos cada vez de la memoria de GPU? Lo que quiero decir es que si tuviera 2 modelos renderizados varias veces cada uno, importaría si primero renderizo el primero varias veces y luego el segundo varias veces o si rendericé el primero solo una vez, el segundo solo una vez y siguió intercalando así? Podría llamar a esta pregunta "flujo de datos interno de la GPU" en este sentido.
- Obviamente, las tarjetas gráficas tienen RAM limitada; cuando no puede contener todos los datos del modelo necesarios para renderizar 1 fotograma, supongo que sigue obteniendo (algunos) de la RAM de la CPU cada fotograma, ¿es correcto?
Sé que hay muchos libros y cosas sobre esto en Internet, pero tal vez tenga algunas pautas generales rápidas sobre cómo administrar este flujo de datos (cuándo enviar qué y cuánto, cuándo y cómo procesar).
Editar: Olvidé hacer una distinción: hay que enviar los datos a la GPU y configurar / vincular los búferes como actuales . ¿Esto último causa algún flujo de datos?
Edit2: después de leer la publicación de Raxvan, me gustaría distinguir algunas acciones:
- creación de búfer con inicialización (como dijo, puedo almacenar los datos en CPU ram o GPU one)
- actualización de datos de búfer (que creo que es sencillo cuando los datos se guardan en la memoria RAM de la CPU y requiere ir de la GPU a la memoria RAM de la CPU (y luego de regreso) cuando se guarda en la memoria RAM de la GPU)
- enlazar el búfer como activo (¿es solo una forma de decirle a la API que quiero que este búfer se represente en la próxima llamada de sorteo y no hace nada por sí mismo ?)
- Llamada de sorteo de API (aquí me gustaría saber de usted lo que realmente sucede allí)
Respuestas:
Por lo general sí, pero el conductor es libre de hacer lo que es "óptima", los datos pueden ser almacenados en la VRAM o RAM o simplemente podría ser almacenado en caché aquí es un atricle que explica lo que realmente sucede con el flujo VBO .
Por ejemplo, si se marcó como un búfer dinámico de OpenGL (por ejemplo, VBO), es más probable que se almacene en la RAM. La GPU usa el acceso directo a memoria (DMA) para acceder al ram directamente sin la intervención de la CPU, esto es controlado por el controlador DMA en la tarjeta gráfica y el controlador de gráficos y se ejecuta en modo kernel.
Al igual que las CPU, las GPU pueden reordenar las instrucciones de la GPU y las operaciones de acceso a la memoria (léase: ejecución fuera de orden ), por lo que lo más probable es que la GPU maneje el escenario que mencionó accediendo a la memoria que está en su caché (generalmente accedido recientemente ), pero a veces no puede hacer esto.
No quieres que esto suceda. Pero independientemente de si eso sucede, la GPU comenzará a mover la memoria entre RAM y VRAM (el procesador de comandos en la GPU es responsable de esto), lo que hará que el procesamiento sea mucho más lento, lo que hará que la GPU se bloquee, ya que tendrá que esperar los datos para ser copiado de / a V / RAM.
Las GPU contienen un búfer de comandos , y todos los comandos API se envían a este búfer, tenga en cuenta que esto puede suceder simultáneamente con los datos que se copian en la GPU. El buffer del anillo de comandos es una cola de comunicación entre la CPU y la GPU , cualquier comando que deba ejecutarse debe enviarse a la cola para que la GPU pueda ejecutarlo. Al igual que cualquier operación que vincula nuevas memorias intermedias, debe enviarse a la GPU para que pueda acceder a alguna ubicación de memoria.
Esa es una de las razones por las que glBegin / glEnd fue desaprobado, el envío de nuevos comandos necesita sincronización de cola (usando barreras / barreras de memoria).
En cuanto a sus otros puntos:
Puede asignar un búfer sin inicialización y conservarlo para su uso posterior. O puede asignarle un búfer y copiar datos al mismo tiempo (hablando del nivel de API).
Puede usar glMapBuffer para actualizar la memoria en el lado de la GPU. si la memoria se copiará de / a RAM no es realmente el estándar y variará en gran medida según el proveedor, el tipo de GPU y el controlador.
Mi segundo punto en la pregunta principal cubre esto.
Piense en la vinculación como el uso del
this
puntero en cualquier lenguaje orientado a objetos, aunque no es estrictamente el mismo, cualquier llamada API resultante será relativa a ese búfer de vinculación .fuente
En general, el límite y la participación de la CPU frente a la GPU son específicos en la plataforma, pero la mayoría de ellos siguen este modelo: la CPU tiene algo de RAM, la GPU también y puede mover la memoria (en algunos casos, la RAM se comparte pero para el En aras de la simplicidad, sigamos con carneros separados).
primer punto : los datos que inicializa puede elegir mantenerlos en el ram de la CPU o en el ram de la GPU, y las ventajas son para ambos. Cuando renderiza algo, la GPU tiene que hacer el trabajo pesado, por lo que es obvio que los datos que ya están en la memoria de la GPU proporcionarán un mejor rendimiento. para la CPU, primero debe enviar los datos a la GPU (que puede optar por conservarlos por un tiempo) y luego realizar el renderizado.
segundo punto : Hay muchos trucos en el renderizado, pero la forma principal de hacerlo es con polígonos. En un marco, la GPU renderizará los objetos hechos de polígonos uno por uno y después de terminar, la GPU enviará la imagen a la pantalla. No existe un concepto como los objetos, solo hay polígonos y la forma en que los juntas creará una imagen. el trabajo de la GPU es proyectar esos polígonos de 3d a 2d y aplicar el efecto (si lo desea). Los polígonos van solo de manera directa CPU-> GPU-> SCREEN o GPU-> SCREEN directamente (si los polígonos ya están en el ram de GPU)
tercer punto : cuando renderiza animaciones, por ejemplo, es mejor mantener los datos cerca de la CPU porque allí hace el trabajo pesado, no sería óptimo mantener los datos en la GPU, moverlos a la CPU y retroceder cada fotograma. Hay muchos otros ejemplos como este para contar, pero en general todos los datos se mantendrán cerca de quien esté haciendo los cálculos. Por lo general, querrá mover la mayor cantidad de datos posible a la RAM de la GPU para obtener rendimiento.
El envío real de los datos a la GPU se realiza mediante la API que usa (directx / opengl u otro) y el concepto de enlace y cosas como estas son solo abstracciones para que la API entienda lo que quiere hacer.
Editar para la edición:
buffer creation with initialisation
: es como la diferencia entre inta = new int[10]
ya[0] = 0,a[1] = 1.... etc
cuando creas un buffer, dejas espacio para los datos y cuando inicias los datos pones las cosas que quieres allí.buffer data update
si está en el cpu ram, entonces tienesvertex * vertices
que puedes jugar con él, si no está allí, tendrías que moverlo de la GPUvertex * vertices = map(buffer_id);
(el mapa es una función mitológica que debería mover los datos de la GPU al CPU ram, también tiene su lado opuestobuffer_id = create_buffer(vertices)
;binding the buffer as active
es solo un concepto al que llamanbinding
renderización, es un proceso complejo y es como llamar a una función con 10000 parámetros. El enlace es solo un término que usaron para decir qué búfer va a dónde. No hay magia real detrás de este término, no convierte ni mueve ni reasigna los búferes, solo le dice al conductor que en la próxima llamada de sorteo use este búfer.API draw call
Después de todo el atascamiento y la fijación, este es el lugar donde el caucho se encuentra con la carretera. La llamada de extracción tomará todos los datos (o los identificadores que apuntan a los datos) que especificó, los envió a la GPU (si es necesario) y le indicará a la GPU que comience a procesar los números. Esto no es del todo cierto en todas las plataformas, hay muchas diferencias, pero para simplificar, el sorteo le dirá a la GPU que ... dibuje.fuente
La respuesta más correcta es, depende de cómo lo programes, pero es algo bueno de qué preocuparse. Si bien las GPU se han vuelto increíblemente rápidas, el ancho de banda hacia y desde la GPU RAM no lo es, y será su cuello de botella más frustrante.
Esperemos que si. Para la velocidad de renderizado, desea que se guarden tantos datos en la GPU como sea posible, en lugar de reenviarlos cada fotograma. Los VBO sirven para este propósito exacto. Hay VBO estáticos y dinámicos, el primero es el mejor para los modelos estáticos, y el segundo es el mejor para los modelos cuyos vértices cambiarán cada cuadro (por ejemplo, un sistema de partículas). Sin embargo, incluso cuando se trata de VBO dinámicos, no desea reenviar todos los vértices de cada cuadro; solo los que están cambiando.
En el caso de su edificio, los datos de vértice simplemente se quedarían allí, y lo único que cambia son sus matrices (modelo / mundo, proyección y vista).
En el caso de un sistema de partículas, hice un VBO dinámico lo suficientemente grande como para almacenar la cantidad máxima de partículas que existirán para ese sistema. Cada cuadro que envío los datos de las partículas emitidas ese cuadro, junto con un par de uniformes, y eso es todo. Cuando dibujo, puedo especificar un punto de inicio y fin en ese VBO, por lo que no tengo que eliminar datos de partículas. Solo puedo decir que no los dibujes.
El acto de enviar múltiples llamadas de extracción en lugar de solo una es un límite mucho mayor. Echa un vistazo a la representación instanciada; podría ayudarte mucho y hacer que la respuesta a esta pregunta sea inútil. Tuve algunos problemas con el controlador que aún no he resuelto, pero si puedes hacerlo funcionar, entonces el problema está resuelto.
No quieres quedarte sin GPU RAM. Si lo hace, cambie las cosas para que no lo haga. En el escenario muy hipotético que se acaba, probablemente se bloqueará de alguna manera, pero nunca lo he visto suceder, así que honestamente no lo sé.
No hay flujo de datos significativo, no. Tiene un costo, pero eso es cierto para cada línea de código que escriba. Descubrir cuánto te cuesta es, de nuevo, para qué sirve el perfil.
La respuesta de Raxvan suena bien, pero no es del todo precisa. En OpenGL, la creación del búfer no reserva espacio. Si desea reservar espacio sin pasar ningún dato, puede llamar a glBufferData y simplemente pasar nulo. (Vea la sección de notas aquí .)
Supongo que te refieres a glBufferData u otras funciones como esa, ¿verdad? Aquí es donde ocurre la transferencia de datos reales. (A menos que pase nulo, como acabo de decir en el último párrafo).
Sí, pero puede hacer un poco más que eso. Por ejemplo, si vincula un VAO (objeto de matriz de vértices), luego vincula un VBO, ese VBO queda vinculado al VAO. Más tarde, si vincula ese VAO nuevamente y llama a glDrawArrays, sabrá qué VBO dibujar.
Tenga en cuenta que si bien muchos tutoriales le permitirán crear un VAO para cada VBO, me han dicho que este no es su uso previsto. Supuestamente, debe crear un VAO y usarlo con cada VBO que tenga los mismos atributos. Todavía no lo he probado, así que no puedo decir con certeza si es mejor o peor.
Lo que sucede aquí es bastante sencillo (desde nuestra perspectiva). Digamos que vincula un VAO, luego llame a glDrawArrays. Usted especifica un punto de inicio y un conteo, y ejecuta su sombreador de vértices para cada vértice en ese rango, que a su vez pasa sus salidas por la línea. Sin embargo, todo ese proceso es otro ensayo propio.
fuente