Partager via


Objet d’application et DirectX

plateforme Windows universelle (UWP) avec les jeux DirectX n’utilisent pas beaucoup d’éléments et d’objets de l’interface utilisateur de l’interface utilisateur Windows. Au lieu de cela, étant donné qu’ils s’exécutent à un niveau inférieur dans la pile Windows Runtime, ils doivent interagir avec l’infrastructure d’interface utilisateur de manière plus fondamentale : en accédant et interopérant directement avec l’objet d’application. Découvrez quand et comment cette interopération se produit et comment vous, en tant que développeur DirectX, pouvez utiliser efficacement ce modèle dans le développement de votre application UWP.

Consultez le glossaire graphique Direct3D pour plus d’informations sur les termes ou concepts graphiques inconnus que vous rencontrez lors de la lecture.

Espaces de noms d’interface utilisateur principaux importants

Tout d’abord, notons les espaces de noms Windows Runtime que vous devez inclure (avec utilisation) dans votre application UWP. On entre dans les détails en un peu.

Remarque

Si vous ne développez pas d’application UWP, utilisez les composants d’interface utilisateur fournis dans les bibliothèques et espaces de noms spécifiques à JavaScript ou XAML au lieu des types fournis dans ces espaces de noms.

Objet d’application Windows Runtime

Dans votre application UWP, vous souhaitez obtenir une fenêtre et un fournisseur d’affichage à partir duquel vous pouvez obtenir une vue et auquel vous pouvez connecter votre chaîne d’échange (vos mémoires tampons d’affichage). Vous pouvez également raccorder cette vue aux événements spécifiques à la fenêtre pour votre application en cours d’exécution. Pour obtenir la fenêtre parente de l’objet d’application, définie par le type CoreWindow, créez un type qui implémente IFrameworkViewSource. Pour obtenir un exemple de code C++/WinRT montrant comment implémenter IFrameworkViewSource, consultez Composition native interopération avec DirectX et Direct2D.

Voici l’ensemble de étapes de base pour obtenir une fenêtre à l’aide de l’infrastructure d’interface utilisateur principale.

  1. Créez un type qui implémente IFrameworkView. C’est votre vue.

    Dans ce type, définissez :

    • Méthode Initialize qui prend une instance de CoreApplicationView en tant que paramètre. Vous pouvez obtenir une instance de ce type en appelant CoreApplication.CreateNewView. L’objet d’application l’appelle quand l’application est lancée.
    • Méthode SetWindow qui prend une instance de CoreWindow en tant que paramètre. Vous pouvez obtenir une instance de ce type en accédant à la propriété CoreWindow sur votre nouvelle instance CoreApplicationView.
    • Méthode Load qui prend une chaîne pour un point d’entrée comme paramètre unique. L’objet d’application fournit la chaîne de point d’entrée lorsque vous appelez cette méthode. C’est là que vous configurez des ressources. Vous créez vos ressources d’appareil ici. L’objet d’application l’appelle quand l’application est lancée.
    • Méthode Run qui active l’objet CoreWindow et démarre le répartiteur d’événements de fenêtre. L’objet d’application l’appelle au démarrage du processus de l’application.
    • Méthode Uninitialize qui nettoie les ressources configurées dans l’appel à Load. L’objet d’application appelle cette méthode lorsque l’application est fermée.
  2. Créez un type qui implémente IFrameworkViewSource. Il s’agit de votre fournisseur d’affichage.

    Dans ce type, définissez :

  3. Transmettez une instance du fournisseur de vues à CoreApplication.Run à partir de main.

Avec ces principes de base à l’esprit, examinons d’autres options que vous devez étendre cette approche.

Types d’interface utilisateur de base

Voici d’autres types d’interface utilisateur principaux dans Windows Runtime que vous pouvez trouver utiles :

Vous pouvez utiliser ces types pour accéder à la vue de votre application, en particulier les bits qui dessinent le contenu de la fenêtre parente de l’application, et gérer les événements déclenchés pour cette fenêtre. Le processus de la fenêtre d’application est un appartement à thread unique d’application (ASTA) isolé et qui gère tous les rappels.

L’affichage de votre application est généré par le fournisseur d’affichage pour votre fenêtre d’application et, dans la plupart des cas, sera implémenté par un package d’infrastructure spécifique ou le système lui-même. Vous n’avez donc pas besoin de l’implémenter vous-même. Pour DirectX, vous devez implémenter un fournisseur de vue dynamique, comme indiqué précédemment. Il existe une relation 1 à 1 spécifique entre les composants et les comportements suivants :

  • Vue d’une application, représentée par le type CoreApplicationView et qui définit la ou les méthodes de mise à jour de la fenêtre.
  • ASTA, dont l’attribution définit le comportement de threading de l’application. Vous ne pouvez pas créer d’instances de types attributs COM STA sur un ASTA.
  • Fournisseur d’affichage, que votre application obtient à partir du système ou que vous implémentez.
  • Fenêtre parente, représentée par le type CoreWindow.
  • Approvisionnement pour tous les événements d’activation. Les vues et les fenêtres ont des événements d’activation distincts.

En résumé, l’objet d’application fournit une fabrique de fournisseur d’affichage. Il crée un fournisseur d’affichage et instancie une fenêtre parente pour l’application. Le fournisseur d’affichage définit l’affichage de l’application pour la fenêtre parente de l’application. À présent, examinons les spécificités de la vue et de la fenêtre parente.

Comportements et propriétés CoreApplicationView

CoreApplicationView représente l’affichage actuel de l’application. L’application singleton crée l’affichage de l’application pendant l’initialisation, mais la vue reste dormante jusqu’à ce qu’elle soit activée. Vous pouvez obtenir coreWindow qui affiche l’affichage en accédant à la propriété CoreApplicationView.CoreWindow sur celle-ci, et vous pouvez gérer les événements d’activation et de désactivation pour la vue en inscrivant des délégués auprès de l’événement CoreApplicationView.Activated.

Comportements et propriétés CoreWindow

La fenêtre parente, qui est une instance CoreWindow , est créée et transmise au fournisseur d’affichage lorsque l’objet d’application initialise. Si l’application a une fenêtre à afficher, elle l’affiche ; sinon, il initialise simplement la vue.

CoreWindow fournit un certain nombre d’événements spécifiques aux comportements d’entrée et de fenêtre de base. Vous pouvez gérer ces événements en inscrivant vos propres délégués avec eux.

Vous pouvez également obtenir le répartiteur d’événements de fenêtre pour la fenêtre en accédant à la propriété CoreWindow.Dispatcher, qui fournit une instance de CoreDispatcher.

Comportements et propriétés CoreDispatcher

Vous pouvez déterminer le comportement de threading de l’envoi d’événements pour une fenêtre avec le type CoreDispatcher. Sur ce type, il existe une méthode particulièrement importante : la méthode CoreDispatcher.ProcessEvents, qui démarre le traitement des événements de fenêtre. L’appel de cette méthode avec une option incorrecte pour votre application peut entraîner toutes sortes de comportements inattendus de traitement des événements.

Option CoreProcessEventsOption Description
CoreProcessEventsOption.ProcessOneAndAllPending Distribuez tous les événements actuellement disponibles dans la file d’attente. Si aucun événement n’est en attente, attendez le nouvel événement suivant.
CoreProcessEventsOption.ProcessOneIfPresent Distribuez un événement s’il est en attente dans la file d’attente. Si aucun événement n’est en attente, n’attendez pas qu’un nouvel événement soit déclenché, mais retournez immédiatement.
CoreProcessEventsOption.ProcessUntilQuit Attendez les nouveaux événements et distribuez tous les événements disponibles. Poursuivez ce comportement jusqu’à ce que la fenêtre soit fermée ou que l’application appelle la méthode Close sur l’instance CoreWindow.
CoreProcessEventsOption.ProcessAllIfPresent Distribuez tous les événements actuellement disponibles dans la file d’attente. Si aucun événement n’est en attente, retournez immédiatement.

UWP utilisant DirectX doit utiliser l’option CoreProcessEventsOption.ProcessAllIfPresent pour empêcher les comportements bloquants susceptibles d’interrompre les mises à jour graphiques.

Considérations relatives à ASTA pour les développeurs DirectX

L’objet d’application qui définit la représentation au moment de l’exécution de votre applicationUWP et DirectX utilise un modèle de thread appelé Application Single-Threaded Apartment (ASTA) pour héberger les vues d’interface utilisateur de votre application. Si vous développez une application UWP et DirectX, vous connaissez les propriétés d’un ASTA, car tout thread que vous distribuez à partir de votre application UWP et DirectX doit utiliser les API Windows ::System ::Threading ou utiliser CoreWindow ::CoreDispatcher. (Vous pouvez obtenir le Objet CoreWindow pour l’ASTA en appelant CoreWindow ::GetForCurrentThread à partir de votre application.)

La chose la plus importante pour vous de connaître, en tant que développeur d’une application DirectX UWP, est que vous devez autoriser votre thread d’application à distribuer des threads MTA en définissant Platform ::MTAThread sur main().

[Platform::MTAThread]
int main(Platform::Array<Platform::String^>^)
{
    auto myDXAppSource = ref new MyDXAppSource(); // your view provider factory 
    CoreApplication::Run(myDXAppSource);
    return 0;
}

Lorsque l’objet d’application de votre application DirectX UWP s’active, il crée l’ASTA qui sera utilisé pour la vue d’interface utilisateur. Les nouveaux appels de thread ASTA dans votre fabrique de fournisseurs d’affichage, pour créer le fournisseur d’affichage pour votre objet d’application et, par conséquent, votre code de fournisseur d’affichage s’exécutera sur ce thread ASTA.

En outre, tout thread que vous spinez de l’ASTA doit être dans une MTA. N’oubliez pas que les threads MTA que vous supprimez peuvent toujours créer des problèmes de réentrance et entraîner un blocage.

Si vous transférez du code existant pour qu’il s’exécute sur le thread ASTA, gardez à l’esprit ces considérations :

  • Les primitives d’attente, telles que CoWaitForMultipleObjects, se comportent différemment dans un ASTA que dans un STA.

  • La boucle modale d’appel COM fonctionne différemment dans un ASTA. Vous ne pouvez plus recevoir d’appels non liés pendant qu’un appel sortant est en cours. Par exemple, le comportement suivant crée un interblocage à partir d’un ASTA (et bloque immédiatement l’application) :

    1. L’ASTA appelle un objet MTA et transmet un pointeur d’interface P1.
    2. Plus tard, l’ASTA appelle le même objet MTA. L’objet MTA appelle P1 avant de revenir à l’ASTA.
    3. P1 ne peut pas entrer l’ASTA, car il est bloqué lors d’un appel non lié. Toutefois, le thread MTA est bloqué, car il tente d’appeler P1.

    Vous pouvez résoudre ce problème en procédant comme suit :

    • Utilisation du modèle asynchrone défini dans la bibliothèque de modèles parallèles (PPLTasks.h)
    • Appel de CoreDispatcher ::P rocessEvents à partir de l’ASTA de votre application (le thread principal de votre application) dès que possible pour autoriser des appels arbitraires.

    Cela dit, vous ne pouvez pas compter sur la remise immédiate d’appels non liés à l’ASTA de votre application. Pour plus d’informations sur les appels asynchrones, lisez la programmation asynchrone en C++.

Dans l’ensemble, lors de la conception de votre application UWP, utilisez CoreDispatcher pour CoreWindow et CoreDispatcher ::P rocessEvents pour gérer tous les threads d’interface utilisateur plutôt que d’essayer de créer et de gérer vos threads MTA vous-même. Lorsque vous avez besoin d’un thread distinct que vous ne pouvez pas gérer avec CoreDispatcher, utilisez des modèles asynchrones et suivez les instructions mentionnées précédemment pour éviter les problèmes de réentrance.