Compartir a través de


Copiar y acceder a datos de recursos (Direct3D 10)

Ya no es necesario pensar en los recursos que se crean en la memoria de vídeo o en la memoria del sistema. O si el tiempo de ejecución debe administrar la memoria. Gracias a la arquitectura del nuevo WDDM (Windows Display Driver Model), las aplicaciones ahora crean recursos de Direct3D 10 con marcas de uso diferentes para indicar cómo pretende la aplicación usar los datos de recursos. El nuevo modelo de controlador virtualiza la memoria usada por los recursos; Después, se convierte en responsabilidad del sistema operativo, controlador o administrador de memoria para colocar recursos en el área de memoria más eficaz posible según el uso esperado.

El caso predeterminado es que los recursos estén disponibles para la GPU. Por supuesto, después de decir eso, hay ocasiones en las que los datos de recursos deben estar disponibles para la CPU. Copiar datos de recursos para que el procesador adecuado pueda acceder a ellos sin afectar al rendimiento requiere conocimientos sobre cómo funcionan los métodos de API.

Copiar datos de recursos

Los recursos se crean en memoria cuando Direct3D ejecuta una llamada Create. Se pueden crear en memoria de vídeo, memoria del sistema o cualquier otro tipo de memoria. Dado que el modelo de controlador WDDM virtualiza esta memoria, las aplicaciones ya no necesitan realizar un seguimiento de qué tipo de recursos de memoria se crean.

Idealmente, todos los recursos se ubicarían en la memoria de vídeo para que la GPU pueda tener acceso inmediato a ellos. Sin embargo, a veces es necesario que la CPU lea los datos de recursos o que la GPU acceda a los datos de recursos en los que se ha escrito la CPU. Direct3D 10 controla estos diferentes escenarios solicitando que la aplicación especifique un uso y, a continuación, ofrece varios métodos para copiar datos de recursos cuando sea necesario.

Dependiendo de cómo se creó el recurso, no siempre es posible acceder directamente a los datos subyacentes. Esto puede significar que los datos del recurso deben copiarse del recurso de origen a otro recurso al que pueda acceder el procesador adecuado. En términos de Direct3D 10, la CPU puede acceder a los recursos predeterminados directamente mediante la GPU, los recursos dinámicos y de almacenamiento provisional pueden acceder directamente a ellos.

Una vez creado un recurso, no se puede cambiar su uso . En su lugar, copie el contenido de un recurso en otro recurso que se creó con un uso diferente. Direct3D 10 proporciona esta funcionalidad con tres métodos diferentes. Los dos primeros métodos( ID3D10Device::CopyResource y ID3D10Device::CopySubresourceRegion) están diseñados para copiar datos de recursos de un recurso a otro. El tercer método (ID3D10Device::UpdateSubresource) está diseñado para copiar datos de la memoria en un recurso.

Hay dos tipos principales de recursos: asignables y no asignables. Los recursos creados con usos dinámicos o provisionales se pueden asignar, mientras que los recursos creados con usos predeterminados o inmutables no se pueden asignar.

Copiar datos entre recursos no asignables es muy rápido porque este es el caso más común y se ha optimizado para funcionar bien. Dado que estos recursos no son accesibles directamente mediante la CPU, están optimizados para que la GPU pueda manipularlos rápidamente.

Copiar datos entre recursos asignables es más problemático porque el rendimiento dependerá del uso con el que se creó el recurso. Por ejemplo, la GPU puede leer un recurso dinámico bastante rápidamente, pero no puede escribir en ellos, y la GPU no puede leer o escribir en recursos de almacenamiento provisional directamente.

Las aplicaciones que desean copiar datos de un recurso con uso predeterminado en un recurso con uso provisional (para permitir que la CPU lea los datos; es decir, el problema de devolución de lectura de GPU) deben hacerlo con cuidado. Consulte Acceso a los datos de recursos para obtener más información sobre este último caso.

Acceso a datos de recursos

El acceso a un recurso requiere asignar el recurso; la asignación básicamente significa que la aplicación está intentando conceder acceso a la CPU a la memoria. El proceso de asignación de un recurso para que la CPU pueda acceder a la memoria subyacente puede provocar algunos cuellos de botella de rendimiento y, por este motivo, se debe tener cuidado sobre cómo y cuándo realizar esta tarea.

El rendimiento puede detenerse si la aplicación intenta asignar un recurso en un momento incorrecto. Si la aplicación intenta obtener acceso a los resultados de una operación antes de que finalice esa operación, se producirá una detenida de canalización.

La realización de una operación de mapa en un momento incorrecto podría provocar una caída grave en el rendimiento al forzar la GPU y la CPU a sincronizarse entre sí. Esta sincronización se producirá si la aplicación quiere acceder a un recurso antes de que la GPU termine de copiarla en un recurso que la CPU pueda asignar.

La CPU solo puede leer de los recursos creados con la marca D3D10_USAGE_STAGING. Dado que los recursos creados con esta marca no se pueden establecer como salidas de la canalización, si la CPU quiere leer los datos de un recurso generado por la GPU, los datos se deben copiar en un recurso creado con la marca de almacenamiento provisional. La aplicación puede hacerlo mediante los métodos ID3D10Device::CopyResource o ID3D10Device::CopySubresourceRegion para copiar el contenido de un recurso a otro. A continuación, la aplicación puede obtener acceso a este recurso llamando al método Map adecuado. Cuando ya no se necesita acceso al recurso, la aplicación debe llamar al método Unmap correspondiente. Por ejemplo, ID3D10Texture2D::Map e ID3D10Texture2D::Unmap. Los distintos métodos Map devuelven algunos valores específicos en función de las marcas de entrada. Consulte la sección Comentarios del mapa para obtener más información.

Nota

Cuando la aplicación llama al método Map, recibe un puntero a los datos de recursos a los que se va a acceder. El tiempo de ejecución garantiza que el puntero tenga una alineación específica, en función del nivel de característica. Para D3D_FEATURE_LEVEL_10_0 y versiones posteriores, el puntero se alinea a 16 bytes. Para menor que D3D_FEATURE_LEVEL_10_0, el puntero se alinea a 4 bytes. La alineación de 16 bytes permite a la aplicación realizar operaciones optimizadas para SSE en los datos de forma nativa, sin realignación ni copia.

 

Consideraciones de rendimiento

Es mejor pensar en un equipo como una máquina que se ejecuta como una arquitectura paralela con dos tipos principales de procesadores: una o más CPU y una o varias GPU. Como en cualquier arquitectura paralela, el mejor rendimiento se consigue cuando cada procesador está programado con suficientes tareas para evitar que se quede inactiva y cuando el trabajo de un procesador no esté esperando el trabajo de otro.

El peor escenario para el paralelismo de GPU/CPU es la necesidad de forzar a un procesador a esperar los resultados del trabajo realizado por otro. Direct3D 10 intenta quitar este costo haciendo asincrónica los métodos ID3D10Device::CopyResource e ID3D10Device::CopySubresourceRegion ; la copia no se ha ejecutado necesariamente en el momento en que el método devuelve. La ventaja de esto es que la aplicación no paga el costo de rendimiento de copiar realmente los datos hasta que la CPU accede a los datos, que es cuando se llama a Map. Si se llama al método Map después de copiar realmente los datos, no se produce ninguna pérdida de rendimiento. Por otro lado, si se llama al método Map antes de copiar los datos, se producirá una detención de canalización.

Las llamadas asincrónicas en Direct3D 10 (que son la gran mayoría de los métodos y, especialmente, las llamadas de representación) se almacenan en lo que se denomina búfer de comandos. Este búfer es interno para el controlador de gráficos y se usa para realizar llamadas por lotes al hardware subyacente para que el costoso cambio del modo de usuario al modo kernel en Microsoft Windows se produzca lo más raramente posible.

El búfer de comandos se vacía, lo que provoca un modificador de modo de usuario o kernel, en una de las cuatro situaciones, que son las siguientes.

  1. Se llama a Present .
  2. Se llama a ID3D10Device::Flush.
  3. El búfer de comandos está lleno; su tamaño es dinámico y está controlado por el sistema operativo y el controlador de gráficos.
  4. La CPU requiere acceso a los resultados de un comando en espera de ejecutarse en el búfer de comandos.

De las cuatro situaciones anteriores, el número cuatro es el más crítico para el rendimiento. Si la aplicación emite una llamada ID3D10Device::CopyResource o ID3D10Device::CopySubresourceRegion , esta llamada se pone en cola en el búfer de comandos. Si la aplicación intenta asignar el recurso de almacenamiento provisional que era el destino de la llamada de copia antes de que se haya vaciado el búfer de comandos, se producirá una detención de canalización porque no solo es necesario ejecutar la llamada al método Copy, pero también deben ejecutarse todos los demás comandos almacenados en búfer de comandos. Esto hará que la GPU y la CPU se sincronicen porque la CPU esperará a acceder al recurso de almacenamiento provisional mientras la GPU vacía el búfer de comandos y, por último, rellena el recurso que necesita la CPU. Una vez que la GPU finalice la copia, la CPU comenzará a acceder al recurso de almacenamiento provisional, pero durante este tiempo, la GPU estará inactiva.

Esto con frecuencia en tiempo de ejecución degradará gravemente el rendimiento. Por ese motivo, la asignación de recursos creados con el uso predeterminado debe realizarse con cuidado. La aplicación debe esperar lo suficiente para vaciar el búfer de comandos y, por tanto, hacer que todos esos comandos terminen de ejecutarse antes de intentar asignar el recurso de almacenamiento provisional correspondiente. ¿Cuánto tiempo debe esperar la aplicación? Al menos dos fotogramas porque esto permitirá que el paralelismo entre las CPU y la GPU se aprovechen al máximo. La forma en que funciona la GPU es que mientras la aplicación procesa el marco N mediante el envío de llamadas al búfer de comandos, la GPU está ocupada ejecutando las llamadas del fotograma anterior, N-1.

Por lo tanto, si una aplicación quiere asignar un recurso que se origina en la memoria de vídeo y llama a ID3D10Device::CopyResource o ID3D10Device::CopySubresourceRegion en el marco N, esta llamada comenzará a ejecutarse en el marco N+1, cuando la aplicación envíe llamadas para el siguiente fotograma. La copia debe finalizarse cuando la aplicación está procesando el marco N+2.

Fotograma Estado de GPU/CPU
No
  • Los problemas de CPU representan llamadas para el marco actual.
N+1
  • GPU que ejecuta llamadas enviadas desde la CPU durante la trama N.
  • Los problemas de CPU representan llamadas para el marco actual.
N+2
  • La GPU terminó de ejecutar llamadas enviadas desde la CPU durante el fotograma N. Resultados listos.
  • GPU que ejecuta llamadas enviadas desde la CPU durante la trama N+1.
  • Los problemas de CPU representan llamadas para el marco actual.
N+3
  • La GPU finalizó la ejecución de llamadas enviadas desde la CPU durante el fotograma N+1. Resultados listos.
  • GPU que ejecuta llamadas enviadas desde la CPU durante la trama N+2.
  • Los problemas de CPU representan llamadas para el marco actual.
N+4 ...

 

Recursos (Direct3D 10)