CPU - Flujo de datos de memoria GPU [cerrado]

16

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í)
NPS
fuente
No soy un experto de ninguna manera, pero si está utilizando OpenGL moderno (es decir, no inmediato) con VAO y VBO, los datos se envían a la GPU y se almacenan en VRAM cada vez que utiliza uno de los comandos de la familia glBuffer. Luego, cada vez que lo dibuja, los vértices relevantes se obtienen de VRAM y se procesan. Si se trata de un modelo que se mueve, tiende a almacenarlo estáticamente y usar matrices para moverse del espacio modelo al espacio mundo / cámara. En cuanto al último punto, no tengo idea de lo que sucede si te quedas sin RAM. Supongo que si te quedas sin VRAM, entonces los datos simplemente no se envían, posiblemente con un código de error.
Polar
@Polar: no exactamente. GL no especifica realmente en qué memoria se almacena un objeto de almacenamiento intermedio, e incluso es libre de moverlo en tiempo de ejecución en función del patrón de uso. GL4.4 aborda esto de alguna manera, pero señala que al final lo mejor que puede proporcionar es "una de esas cosas tontas"; ver opengl.org/registry/specs/ARB/buffer_storage.txt y especialmente los números 2 y 9.
Maximus Minimus
1
@JimmyShelter Ah, gracias, sería bueno si tuviéramos menos de "esas tonterías" y una especificación más concreta.
Polar
@Polar: lo molesto es que ARB_buffer_storage podría haber evitado incluir otra pista, pero los diseñadores perdieron la oportunidad. Oh, bueno, quizás 4.5 finalmente lo haga bien.
Maximus Minimus
2
No edite sus preguntas para "responder" a las respuestas. Publica una nueva pregunta en su lugar.

Respuestas:

12

¿Sus datos se envían a la memoria de la GPU solo una vez y se quedan allí para siempre?

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.

Cuando el modelo se procesa realmente cada cuadro, ¿los procesadores de GPU tienen que recuperar sus datos cada vez de la memoria de la GPU, incluso si un modelo procesa múltiples veces secuenciales?

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.

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?

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.

Se envían los datos a la GPU y se configuran / vinculan los búferes como actuales. ¿Esto último causa algún flujo de datos?

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).

ingrese la descripción de la imagen aquí

En cuanto a sus otros puntos:

Creación de búfer con inicialización

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).

actualización de datos de búfer

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.

Llamada de sorteo de API (aquí me gustaría saber de usted lo que realmente sucede allí).

Mi segundo punto en la pregunta principal cubre esto.

enlazar el búfer como activo (¿es solo una forma de decirle a la API que quiero que este búfer se procese en la próxima llamada de sorteo y no hace nada por sí mismo?)

Piense en la vinculación como el uso del thispuntero 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 .

concepto3d
fuente
3

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 int a = new int[10]y a[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 updatesi está en el cpu ram, entonces tienes vertex * verticesque puedes jugar con él, si no está allí, tendrías que moverlo de la GPU vertex * 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 opuesto buffer_id = create_buffer(vertices);

  • binding the buffer as activees solo un concepto al que llaman bindingrenderizació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 callDespué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.

Raxvan
fuente
2

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.

¿Sus datos se envían a la memoria de la GPU solo una vez y se quedan allí para siempre?

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.

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í?

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.

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?

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é.

Olvidé hacer una distinción: hay que enviar los datos a la GPU y hay que configurar / vincular los búferes como actuales. ¿Esto último causa algún flujo de datos?

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.

creación de búfer con inicialización

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í .)

actualización de datos de búfer

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).

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 extracción y no hace nada por sí mismo?)

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.

Llamada de sorteo API

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.

Desafío helado
fuente
"entonces problema resuelto" Sí, la creación de instancias ayudaría mucho, pero sin ella todavía tendría que hacer una llamada para cada objeto. Lo mismo en ambos casos. Entonces me pregunto si el orden importa.
NPS
@NPS - Importa un poco . Si se ordenan para que no tenga que seguir cambiando sus enlaces, sí, eso probablemente será una cantidad minúscula más rápido. Pero si tiene que hacer todo lo posible para clasificarlos, probablemente será mucho, mucho más costoso. Hay demasiadas variables que dependen de su implementación para decir mucho más que eso.
Icy Defiance