Objeto de aplicación y DirectX
Plataforma universal de Windows (UWP) con juegos DirectX no usan muchos de los elementos y objetos de la interfaz de usuario de la interfaz de usuario de Windows. En su lugar, dado que se ejecutan en un nivel inferior en la pila de Windows Runtime, deben interoperar con el marco de la interfaz de usuario de una manera más fundamental: al acceder e interoperar directamente con el objeto de aplicación. Aprende cuándo y cómo se produce esta interoperación y cómo, como desarrollador de DirectX, puedes usar eficazmente este modelo en el desarrollo de tu aplicación para UWP.
Consulte el glosario de gráficos de Direct3D para obtener información sobre los términos o conceptos de gráficos desconocidos que encuentre al leer.
Los espacios de nombres principales de la interfaz de usuario principales
En primer lugar, tomemos nota de los espacios de nombres de Windows Runtime que debes incluir (con el uso) en tu aplicación para UWP. Entramos en los detalles un poco.
- Windows.ApplicationModel.Core
- Windows.ApplicationModel.Activation
- Windows.UI.Core
- Windows.System
- Windows.Foundation
Nota:
Si no estás desarrollando una aplicación para UWP, usa los componentes de la interfaz de usuario proporcionados en las bibliotecas y espacios de nombres específicos de JavaScript o XAML en lugar de los tipos proporcionados en estos espacios de nombres.
El objeto de aplicación de Windows Runtime
En tu aplicación para UWP, quieres obtener una ventana y un proveedor de vistas desde el que puedes obtener una vista y a la que puedes conectar la cadena de intercambio (tus búferes de pantalla). También puede enlazar esta vista a los eventos específicos de la ventana para la aplicación en ejecución. Para obtener la ventana primaria del objeto de aplicación, definida por el tipo CoreWindow, cree un tipo que implemente IFrameworkViewSource. Para ver un ejemplo de código de C++/WinRT en el que se muestra cómo implementar IFrameworkViewSource, consulte La interoperación nativa de composición con DirectX y Direct2D.
Este es el conjunto básico de pasos para obtener una ventana mediante el marco de interfaz de usuario principal.
Cree un tipo que implemente IFrameworkView. Esta es su vista.
En este tipo, defina:
- Método Initialize que toma una instancia de CoreApplicationView como parámetro. Para obtener una instancia de este tipo, llame a CoreApplication.CreateNewView. El objeto de aplicación lo llama cuando se inicia la aplicación.
- Método SetWindow que toma una instancia de CoreWindow como parámetro. Para obtener una instancia de este tipo, acceda a la propiedad CoreWindow en la nueva instancia de CoreApplicationView.
- Método Load que toma una cadena para un punto de entrada como único parámetro. El objeto de aplicación proporciona la cadena de punto de entrada al llamar a este método. Aquí es donde se configuran los recursos. Aquí se crean los recursos del dispositivo. El objeto de aplicación lo llama cuando se inicia la aplicación.
- Método Run que activa el objeto CoreWindow e inicia el distribuidor de eventos de ventana. El objeto de aplicación lo llama cuando se inicia el proceso de la aplicación.
- Un método Uninitialize que limpia los recursos configurados en la llamada a Load. El objeto de aplicación llama a este método cuando se cierra la aplicación.
Cree un tipo que implemente IFrameworkViewSource. Este es el proveedor de vistas.
En este tipo, defina:
- Método denominado CreateView que devuelve una instancia de la implementación de IFrameworkView, como se creó en el paso 1.
Pase una instancia del proveedor de vistas a CoreApplication.Run desde main.
Teniendo en cuenta estos aspectos básicos, echemos un vistazo a más opciones que tiene que ampliar este enfoque.
Tipos de interfaz de usuario principales
Estos son otros tipos de interfaz de usuario principales en Windows Runtime que puede resultar útil:
- Windows.ApplicationModel.Core.CoreApplicationView
- Windows.UI.Core.CoreWindow
- Windows.UI.Core.CoreDispatcher
Puedes usar estos tipos para acceder a la vista de la aplicación, en concreto, los bits que dibujan el contenido de la ventana primaria de la aplicación y controlan los eventos desencadenados para esa ventana. El proceso de la ventana de la aplicación es un apartamento de un solo subproceso de aplicación (ASTA) aislado y que controla todas las devoluciones de llamada.
La vista de la aplicación la genera el proveedor de vistas para la ventana de la aplicación y, en la mayoría de los casos, se implementará mediante un paquete de marco específico o el propio sistema, por lo que no es necesario implementarla usted mismo. Para DirectX, debe implementar un proveedor de vistas delgadas, como se explicó anteriormente. Hay una relación específica de 1 a 1 entre los siguientes componentes y comportamientos:
- Vista de una aplicación, representada por el tipo CoreApplicationView , que define los métodos para actualizar la ventana.
- ASTA, la atribución de la que define el comportamiento de subproceso de la aplicación. No se pueden crear instancias de tipos con atributos STA COM en un ASTA.
- Proveedor de vistas, que la aplicación obtiene del sistema o que implementa.
- Una ventana primaria, representada por el tipo CoreWindow.
- Aprovisionamiento de todos los eventos de activación. Ambas vistas y ventanas tienen eventos de activación independientes.
En resumen, el objeto de aplicación proporciona un generador de proveedores de vistas. Crea un proveedor de vistas y crea una instancia de una ventana primaria para la aplicación. El proveedor de vistas define la vista de la aplicación para la ventana primaria de la aplicación. Ahora, vamos a analizar los detalles de la vista y la ventana primaria.
Propiedades y comportamientos de CoreApplicationView
CoreApplicationView representa la vista de aplicación actual. El singleton de la aplicación crea la vista de la aplicación durante la inicialización, pero la vista permanece inactiva hasta que se activa. Puede obtener coreWindow que muestra la vista si accede a la propiedad CoreApplicationView.CoreWindow en ella y puede controlar los eventos de activación y desactivación de la vista registrando delegados con el evento CoreApplicationView.Activated.
Comportamientos y propiedades de CoreWindow
La ventana primaria, que es una instancia de CoreWindow , se crea y se pasa al proveedor de vistas cuando se inicializa el objeto de aplicación. Si la aplicación tiene una ventana que se va a mostrar, la muestra; de lo contrario, simplemente inicializa la vista.
CoreWindow proporciona una serie de eventos específicos de los comportamientos de la ventana básica y de entrada. Puede controlar estos eventos registrando sus propios delegados con ellos.
También puede obtener el distribuidor de eventos de ventana para la ventana accediendo a la propiedad CoreWindow.Dispatcher, que proporciona una instancia de CoreDispatcher.
Comportamientos y propiedades de CoreDispatcher
Puede determinar el comportamiento de subprocesos del envío de eventos para una ventana con el tipo CoreDispatcher. En este tipo, hay un método especialmente importante: el método CoreDispatcher.ProcessEvents , que inicia el procesamiento de eventos de ventana. Llamar a este método con la opción incorrecta para la aplicación puede provocar todo tipo de comportamientos de procesamiento de eventos inesperados.
Opción CoreProcessEventsOption | Descripción |
---|---|
CoreProcessEventsOption.ProcessOneAndAllPending | Envíe todos los eventos disponibles actualmente en la cola. Si no hay ningún evento pendiente, espere al siguiente evento nuevo. |
CoreProcessEventsOption.ProcessOneIfPresent | Envíe un evento si está pendiente en la cola. Si no hay ningún evento pendiente, no espere a que se genere un nuevo evento, sino que vuelva inmediatamente. |
CoreProcessEventsOption.ProcessUntilQuit | Espere a nuevos eventos y envíe todos los eventos disponibles. Continúe con este comportamiento hasta que se cierre la ventana o la aplicación llame al método Close en la instancia de CoreWindow. |
CoreProcessEventsOption.ProcessAllIfPresent | Envíe todos los eventos disponibles actualmente en la cola. Si no hay ningún evento pendiente, vuelva inmediatamente. |
UWP que usa DirectX debe usar la opción CoreProcessEventsOption.ProcessAllIfPresent para evitar comportamientos de bloqueo que podrían interrumpir las actualizaciones de gráficos.
Consideraciones de ASTA para desarrolladores de DirectX
El objeto de aplicación que define la representación en tiempo de ejecución de la aplicaciónUWP y DirectX usa un modelo de subprocesos denominado Application Single-Threaded Apartment (ASTA) para hospedar las vistas de interfaz de usuario de la aplicación. Si estás desarrollando una aplicación para UWP y DirectX, estás familiarizado con las propiedades de un ASTA, ya que cualquier subproceso que envíes desde tu aplicación para UWP y DirectX debe usar las API windows::System::Threading o usar CoreWindow::CoreDispatcher. (Puede obtener el Objeto CoreWindow para ASTA llamando a CoreWindow::GetForCurrentThread desde la aplicación).
Lo más importante que debes tener en cuenta, como desarrollador de una aplicación DirectX para UWP, es que debes habilitar el subproceso de la aplicación para enviar subprocesos de MTA estableciendo Platform::MTAThread en main()..
[Platform::MTAThread]
int main(Platform::Array<Platform::String^>^)
{
auto myDXAppSource = ref new MyDXAppSource(); // your view provider factory
CoreApplication::Run(myDXAppSource);
return 0;
}
Cuando se activa el objeto de aplicación para la aplicación DirectX para UWP, crea el ASTA que se usará para la vista de la interfaz de usuario. El nuevo subproceso de ASTA llama al generador del proveedor de vistas para crear el proveedor de vistas para el objeto de aplicación y, como resultado, el código del proveedor de vistas se ejecutará en ese subproceso de ASTA.
Además, cualquier subproceso que se desactive del ASTA debe estar en un MTA. Tenga en cuenta que todos los subprocesos de MTA que se desactiven todavía pueden crear problemas de reentrada y generar un interbloqueo.
Si va a migrar código existente para que se ejecute en el subproceso de ASTA, tenga en cuenta estas consideraciones:
Los primitivos de espera, como CoWaitForMultipleObjects, se comportan de forma diferente en un ASTA que en un STA.
El bucle modal de llamada COM funciona de forma diferente en un ASTA. Ya no puede recibir llamadas no relacionadas mientras una llamada saliente está en curso. Por ejemplo, el siguiente comportamiento creará un interbloqueo desde un ASTA (y bloqueará inmediatamente la aplicación):
- El ASTA llama a un objeto MTA y pasa un puntero de interfaz P1.
- Más adelante, el ASTA llama al mismo objeto MTA. El objeto MTA llama a P1 antes de volver al ASTA.
- P1 no puede escribir el ASTA, ya que está bloqueado realizando una llamada no relacionada. Sin embargo, el subproceso de MTA está bloqueado, ya que intenta realizar la llamada a P1.
Puede resolverlo mediante :
- Uso del patrón asincrónico definido en la biblioteca de patrones paralelos (PPLTasks.h)
- Llamar a CoreDispatcher::P rocessEvents desde el ASTA de la aplicación (el subproceso principal de la aplicación) lo antes posible para permitir llamadas arbitrarias .
Dicho esto, no puedes confiar en la entrega inmediata de llamadas no relacionadas con el ASTA de la aplicación. Para obtener más información sobre las llamadas asincrónicas, lea Programación asincrónica en C++.
En general, al diseñar la aplicación para UWP, usa CoreDispatcher para coreWindow y CoreDispatcher::P rocessEvents para controlar todos los subprocesos de interfaz de usuario en lugar de intentar crear y administrar tus subprocesos de MTA tú mismo. Cuando necesite un subproceso independiente que no pueda controlar con CoreDispatcher, use patrones asincrónicos y siga las instrucciones mencionadas anteriormente para evitar problemas de reentrada.