Sé que el título es un poco vago, pero es difícil describir lo que realmente estoy buscando, pero aquí va.
Cuando se trata de la representación de la CPU, el rendimiento es principalmente fácil de estimar y directo, pero cuando se trata de la GPU debido a mi falta de información técnica, no tengo ni idea. Estoy usando XNA, así que sería bueno si la teoría pudiera estar relacionada con eso.
Entonces, lo que realmente quiero saber es, ¿qué sucede cuándo y dónde (CPU / GPU) cuando haces acciones de dibujo específicas? ¿Qué es un lote? ¿Qué influencia tienen los efectos, proyecciones, etc.? ¿Los datos persisten en la tarjeta gráfica o se transfieren en cada paso? Cuando se habla de ancho de banda, ¿estás hablando del ancho de banda interno de una tarjeta gráfica o de la tubería de la CPU a la GPU?
Nota: en realidad no estoy buscando información sobre cómo ocurre el proceso de dibujo, ese es el negocio de la GPU, estoy interesado en todos los gastos generales que preceden a eso.
Me gustaría entender qué sucede cuando realizo la acción X, para adaptar mis arquitecturas y prácticas a eso.
Se agradece mucho cualquier artículo (posiblemente con ejemplos de código), información, enlaces, tutoriales que den más información sobre cómo escribir mejores juegos. Gracias :)
Respuestas:
Me gusta pensar en el rendimiento en términos de " límites ". Es una forma práctica de conceptualizar un sistema interconectado bastante complicado. Cuando tienes un problema de rendimiento, haces la pregunta: "¿Qué límites estoy alcanzando?" (O: "¿Estoy vinculado a la CPU / GPU?")
Puedes dividirlo en múltiples niveles. En el nivel más alto tienes la CPU y la GPU. Puede estar vinculado a la CPU (GPU inactivo esperando a la CPU), o vinculado a la GPU (la CPU está esperando en la GPU). Aquí hay una buena publicación de blog sobre el tema.
Puedes desglosarlo aún más. En el lado de la CPU , es posible que esté utilizando todos sus ciclos en los datos que ya están en el caché de la CPU. O puede que tenga memoria limitada , dejando la CPU inactiva esperando que los datos entren desde la memoria principal ( así que optimice su diseño de datos ). Podrías descomponerlo aún más.
(Si bien estoy haciendo una amplia descripción del rendimiento con respecto a XNA, señalaré que una asignación de un tipo de referencia (
class
nostruct
), aunque normalmente es barata, podría activar el recolector de basura, que quemará muchos ciclos, especialmente en Xbox 360 . Vea aquí para más detalles).En el lado de la GPU , comenzaré señalando esta excelente publicación de blog que tiene muchos detalles. Si desea un nivel de detalle loco en la tubería, lea esta serie de publicaciones de blog . ( Aquí hay uno más simple ).
En pocas palabras, algunos de los grandes son: " límite de relleno " (cuántos píxeles puede escribir en el backbuffer, a menudo, cuánto sobregiro puede tener), " límite de sombreado " (cuán complicados pueden ser sus sombreadores y cuántos datos puede pasar a través de ellos), " límite de ancho de banda de textura / captación de textura " (cuántos datos de textura puede acceder).
Y, ahora, llegamos al punto más grande, que es lo que realmente está preguntando, donde la CPU y la GPU tienen que interactuar (a través de las diversas API y controladores). En términos generales, existe el " límite de lote " y el " ancho de banda ". (Tenga en cuenta que la primera parte de la serie que mencioné anteriormente entra en detalles extensos ).
Pero, básicamente, un lote ( como ya sabe ) ocurre cada vez que llama a una de las
GraphicsDevice.Draw*
funciones (o parte de XNA, por ejemploSpriteBatch
, hace esto por usted). Como sin duda ya has leído, obtienes algunos miles * de estos por fotograma. Este es un límite de CPU, por lo que compite con su otro uso de CPU. Básicamente, es el controlador que empaqueta todo lo que le ha dicho que dibuje y lo envía a la GPU.Y luego está el ancho de banda de la GPU. Esta es la cantidad de datos sin procesar que puede transferir allí. Esto incluye toda la información de estado que acompaña a los lotes, desde establecer el estado de representación y las constantes / parámetros del sombreador (que incluye elementos como matrices de mundo / vista / proyecto), hasta vértices cuando se usan las
DrawUser*
funciones. También incluye todas las llamadas aSetData
yGetData
en texturas, tampones de vértices, etc.En este punto, debo decir que todo lo que pueda llamar
SetData
(texturas, búferes de vértices e índices, etc.), así comoEffect
s - permanece en la memoria de la GPU. No se reenvía constantemente a la GPU. Un comando de dibujo que hace referencia a esos datos simplemente se envía con un puntero a esos datos.(Además: solo puede enviar comandos de dibujo desde el hilo principal, pero puede
SetData
hacerlo en cualquier hilo).XNA complica las cosas un poco con sus clases de estado render (
BlendState
,DepthStencilState
, etc.). Estos datos de estado se envían por llamada de sorteo (en cada lote). No estoy 100% seguro, pero tengo la impresión de que se envía perezosamente (solo envía un estado que cambia). De cualquier manera, los cambios de estado son baratos hasta el punto de ser gratuitos, en relación con el costo de un lote.Finalmente, lo último que hay que mencionar es la canalización interna de la GPU . No desea forzarlo a que se vacíe escribiendo en datos que aún necesita leer, o leyendo datos que aún necesita escribir. Una descarga de tubería significa que espera a que finalicen las operaciones, de modo que todo esté en un estado coherente cuando se accede a los datos.
Los dos casos particulares a tener en cuenta son: Invocar
GetData
cualquier cosa dinámica, particularmente en una en laRenderTarget2D
que la GPU pueda estar escribiendo. Esto es extremadamente malo para el rendimiento, no lo hagas.El otro caso es llamar
SetData
a los búferes de vértices / índices. Si necesita hacer esto con frecuencia, use unDynamicVertexBuffer
(tambiénDynamicIndexBuffer
). Esto le permite a la GPU saber que cambiarán con frecuencia y hacer algo de magia interna para evitar el enjuague de la tubería.(Tenga en cuenta también que los búferes dinámicos son más rápidos que los
DrawUser*
métodos, pero tienen que asignarse previamente al tamaño máximo requerido).... Y eso es casi todo lo que sé sobre el rendimiento de XNA :)
fuente