Uso de identificadores DDI de Context-Local
Esta sección solo se aplica a Windows 7 y versiones posteriores, y Windows Server 2008 R2 y versiones posteriores del sistema operativo Windows.
Cada objeto (por ejemplo, recurso, sombreador, etc.) tiene identificadores DDI locales de contexto.
Supongamos que se usa un objeto con tres contextos diferidos. En esta situación, cuatro identificadores hacen referencia al mismo objeto (un identificador para cada contexto diferido y otro identificador para el contexto inmediato). Dado que un subproceso puede manipular cada contexto simultáneamente, un identificador local de contexto garantiza que varios subprocesos de CPU no contengan memoria similar (ya sea intencionada o involuntariamente). Los identificadores locales de contexto también son intuitivos porque probablemente el controlador debe modificar gran parte de estos datos que están asociados lógicamente por contexto de todos modos (por ejemplo, el objeto podría estar enlazado por el contexto, etc.).
Todavía existe la distinción de un identificador de contexto inmediato frente a un identificador de contexto diferido. En concreto, se garantiza que el identificador de contexto inmediato es el primer identificador que se asigna y el último identificador que se destruye. El identificador de contexto inmediato correspondiente se proporciona durante la "apertura" de cada identificador de contexto diferido para vincularlos juntos. Actualmente no hay ningún concepto de objeto que tenga un identificador DDI por dispositivo (es decir, un identificador que se crea antes y se destruye después del identificador de contexto inmediato, y solo se hace referencia a él en orden por creación del identificador de contexto).
Algunos identificadores tienen relaciones de dependencia con otros identificadores (por ejemplo, las vistas tienen una dependencia de su recurso correspondiente). La garantía de creación y destrucción que existe para el contexto inmediato se extiende también a identificadores de contexto diferidos (es decir, el tiempo de ejecución crea un identificador de recursos local de contexto antes de que el tiempo de ejecución cree cualquier identificador de vista local de contexto para ese recurso y el tiempo de ejecución destruye un identificador de recursos local de contexto después de que el tiempo de ejecución destruya todos los identificadores de vista local de contexto para ese recurso). Cuando el tiempo de ejecución crea un identificador local de contexto, el tiempo de ejecución también proporciona los identificadores de dependencia locales de contexto correspondientes.
Organización de datos de controladores
Hay algunas preocupaciones sobre la organización de datos de controladores que necesitan atención. Al igual que direct3D versión 10, la localidad adecuada de los datos puede reducir los errores de caché entre la API y el controlador. La localidad adecuada de los datos también puede impedir la limitación de caché, que se produce cuando varios fragmentos de datos a los que se accede con frecuencia se resuelven en el mismo índice de caché y agotan la asociatividad de la caché. DDI se ha diseñado desde la versión 10 de Direct3D para ayudar a evitar que este tipo de problemas se manifesten mediante el controlador que informa a la API de la cantidad de memoria que requiere el controlador para satisfacer un identificador y la API que asigna el valor del identificador. Sin embargo, los nuevos problemas relacionados con los subprocesos afectan al diseño DDI en el período de tiempo de la versión 11 de Direct3D.
Naturalmente, los identificadores locales de contexto proporcionan una manera de asociar datos de objeto por contexto, lo que evita problemas de contención entre subprocesos. Sin embargo, dado que estos datos se replican para cada contexto diferido, el tamaño de estos datos es un problema importante. Esto proporciona la racionalización natural para compartir datos de solo lectura entre el identificador de contexto inmediato y los identificadores de contexto diferidos. Durante la creación de identificadores de contexto diferidos, se proporciona el identificador de contexto inmediato para establecer la conexión entre identificadores. Sin embargo, los datos que se encuentran fuera del contexto diferido controlan las ventajas de la localidad con los datos de API y el nivel adicional de direccionamiento indirecto a los datos de solo lectura impide que las ventajas de la localidad se extiendan a los datos de solo lectura. Algunos datos de solo lectura se pueden replicar en cada región de control de contexto si las ventajas de la localidad justifican la duplicación de datos. Sin embargo, la memoria que respalda cada identificador de contexto diferido debe considerarse como una prima que podría ser útil para reubicar los datos que no son aadjacentes del identificador si esos datos son relativamente grandes y no se accede con tanta frecuencia como otros datos. Idealmente, el tipo de datos que está asociado a cada identificador de contexto diferido sería todos los datos de alta frecuencia; por lo tanto, los datos no serían lo suficientemente grandes como para considerar la reubicación necesaria. Naturalmente, el conductor debe equilibrar estas motivaciones conflictivas.
Para que el diseño de datos del controlador sea eficazmente compatible con la versión 10 de Direct3D, pero no es divergente en la implementación, los datos de solo lectura deben ubicarse contiguos (pero aún separados de y después) el contexto inmediato controla los datos. Si el controlador usa este diseño, el controlador debe tener en cuenta que el relleno de línea de caché es necesario entre los datos del contexto inmediato y los datos de solo lectura. Dado que un subproceso podría manipular cada contexto controlar los datos con frecuencia (si no es simultáneamente), las penalizaciones de uso compartido falso se producen entre los datos del controlador de contexto inmediato y los datos de control de contexto diferidos si no se usa el relleno de línea de caché. El diseño del controlador debe ser consciente de las sanciones de uso compartido falso que se manifiestan si los punteros se establecen y atraviesan regularmente entre las regiones de memoria del controlador de contexto.
El entorno de ejecución de Direct3D usa la siguiente DDI de Direct3D 11 para los identificadores locales de contexto diferidos:
La función CheckDeferredContextHandleSizes comprueba los tamaños de los espacios de memoria privados del controlador que contienen los datos de identificadores de contexto diferidos.
La función CalcDeferredContextHandleSize determina el tamaño de la región de memoria para un contexto diferido.
Para que el entorno de ejecución de Direct3D recupere el tamaño del identificador de contexto diferido que requiere el controlador, se deben usar las funciones DDI anteriores. Inmediatamente después de crear un objeto para el contexto inmediato, el tiempo de ejecución llama a CalcDeferredContextHandleSize para consultar al controlador la cantidad de espacio de almacenamiento que el controlador necesita para satisfacer los identificadores de contexto diferidos para este objeto. Sin embargo, la API de Direct3D debe ajustar su asignador de memoria CLS mediante la determinación del número de tamaños de identificador únicos y sus valores a los que se accede; El tiempo de ejecución llama a la función CheckDeferredContextHandleSizes del controlador para obtener esta información. Por lo tanto, durante la creación de instancias del dispositivo, la API solicita una matriz de tamaños de identificadores de contexto diferidos mediante el sondeo doble. El primer sondeo consiste en solicitar cuántos tamaños se devuelven, mientras que el segundo sondeo pasa una matriz para recuperar el valor de cada tamaño. El controlador debe indicar la cantidad de memoria que requiere para satisfacer un identificador junto con el tipo de controlador. El controlador puede devolver varios tamaños asociados a un tipo de identificador determinado. Sin embargo, no está definido para que el controlador devuelva nunca un valor de CalcDeferredContextHandleSize que no se devolvió también correspondientemente en la matriz CheckDeferredContextHandleSizes .
En cuanto a la creación de los identificadores DDI, se usan los métodos create en el contexto diferido. Por ejemplo, examine las funciones CreateBlendState(D3D10_1) y DestroyBlendState . El HDEVICE apunta naturalmente al contexto diferido adecuado (frente al contexto inmediato); otros punteros de estructura CONST son NULL (suponiendo que el objeto no tiene dependencias); y, el identificador D3D10DDI_HRT* es un identificador D3D10DDI_H* para el objeto de contexto inmediato correspondiente.
En el caso de los objetos que tienen dependencias (por ejemplo, las vistas tienen una relación de dependencia en su recurso correspondiente), el puntero de estructura que proporciona el identificador de dependencia no es NULL. Sin embargo, el único miembro válido de la estructura es el identificador de dependencia; mientras que el resto de los miembros se rellenan con cero. Por ejemplo, el puntero D3D11DDIARG_CREATESHADERRESOURCEVIEW de una llamada a la función CreateShaderResourceView(D3D11) del controlador no será NULL cuando el tiempo de ejecución llame a esta función en un contexto diferido. En esta llamada a CreateShaderResourceView(D3D11), el runtime asigna el identificador local de contexto adecuado para el recurso al miembro hDrvResource de D3D11DDIARG_CREATESHADERRESOURCEVIEW. Sin embargo, el resto de los miembros de D3D11DDIARG_CREATESHADERRESOURCEVIEW están llenos de cero.
En el código de ejemplo siguiente se muestra cómo el tiempo de ejecución de Direct3D traduce la solicitud de creación de una aplicación y el primer uso del contexto diferido para llamar al controlador de pantalla en modo de usuario para crear contextos inmediatos frente a diferidos. La llamada de la aplicación a ID3D11Device::CreateTexture2D inicia el código en tiempo de ejecución en la sección "Creación de recursos". La llamada de la aplicación a ID3D11Device::CopyResource inicia el código en tiempo de ejecución en la siguiente sección "Uso diferido de recursos de contexto".
// Device Create
IC::pfnCheckDeferredContextHandleSizes( hIC, &u, NULL );
pArray = malloc( u * ... );
IC::pfnCheckDeferredContextHandleSizes( hIC, &u, pArray );
// Resource Create
s = IC::pfnCalcPrivateResourceSize( hIC, &Args );
pICRHandle = malloc( s );
IC::pfnCreateResource( hIC, &Args, pICRHandle, hRTResource );
s2 = IC::pfnCalcDeferredContextHandleSize( hIC, D3D10DDI_HT_RESOURCE, pICRHandle );
// Deferred Context Resource Usage
pDCRHandle = malloc( s2 );
DC::pfnCreateResource( hDC, NULL, pDCRHandle, pICRHandle );
Problemas con pfnSetErrorCb
Ninguna de las funciones de creación devuelve un código de error, que habría sido ideal para el modelo de subprocesos de direct3D versión 11. Todas las funciones de creación usan pfnSetErrorCb para recuperar códigos de error del controlador. Para maximizar la compatibilidad con el modelo de controlador direct3D versión 10, no se introdujeron nuevas funciones de creación de DDI que devuelven códigos de error. En su lugar, el controlador debe seguir usando el contexto unificado del dispositivo o inmediato D3D10DDI_HRTCORELAYER controlar con pfnSetErrorCb durante las funciones de creación. Cuando el controlador admite listas de comandos, el controlador debe usar el pfnSetErrorCb adecuado asociado al contexto correspondiente. Es decir, los errores de contexto diferidos deben ir a la llamada de contexto diferida determinada a pfnSetErrorCb con el identificador correspondiente, etc.
Los contextos diferidos pueden devolver E_OUTOFMEMORY a través de una llamada a pfnSetErrorCb desde las funciones DDI que anteriormente solo permitían D3DDDIERR_DEVICEREMOVED (como Draw, SetBlendState, etc.), ya que la memoria de contexto diferida exige crecer perpetuamente con cada llamada a una función DDI. La API de Direct3D desencadena una eliminación de contexto local para ayudar al controlador con un caso de error de este tipo, lo que reduce eficazmente la lista de comandos parcialmente compilada. La aplicación continúa determinando que está grabando una lista de comandos; Sin embargo, cuando la aplicación finalmente llama a la función FinishCommandList , FinishCommandList devuelve un código de error de E_OUTOFMEMORY.