View in English

  • Global Nav Open Menu Global Nav Close Menu
  • Apple Developer
Search
Cancel
  • Apple Developer
  • News
  • Discover
  • Design
  • Develop
  • Distribute
  • Support
  • Account
Only search within “”

Quick Links

5 Quick Links

Vidéos

Ouvrir le menu Fermer le menu
  • Collections
  • Sujets
  • Toutes les vidéos
  • À propos

Retour à WWDC25

  • À propos
  • Résumé
  • Transcription
  • Code
  • Explorez les jeux Metal 4

    Apprenez à optimiser votre moteur de jeu avec les dernières avancées de Metal 4. Nous verrons comment unifier l'encodage de commandes pour minimiser la surcharge du processeur, faire évoluer la gestion des ressources graphiques pour prendre en charge des scènes massives tout en maximisant votre budget mémoire, et charger rapidement de vastes bibliothèques d'états de pipeline. Pour tirer le meilleur parti de cette session, regardez d'abord « Découvrez Metal 4 ».

    Chapitres

    • 0:00 - Introduction
    • 1:33 - Encoder plus efficacement
    • 8:42 - Optimiser la gestion de vos ressources
    • 17:24 - Charger rapidement les pipelines
    • 31:25 - Étapes suivantes

    Ressources

    • Human Interface Guidelines: Designing for games
    • Metal binary archives
    • Reading and writing to sparse textures
    • Resource synchronization
    • Synchronizing resource accesses between multiple passes with a fence
    • Synchronizing resource accesses with earlier passes with a consumer-based queue barrier
    • Synchronizing resource accesses with subsequent passes with a producer-based queue barrier
    • Synchronizing resource accesses within a single pass with an intrapass barrier
    • Understanding the Metal 4 core API
    • Using a render pipeline to render primitives
    • Using the Metal 4 compilation API
      • Vidéo HD
      • Vidéo SD

    Vidéos connexes

    WWDC25

    • Allez plus loin avec les jeux Metal 4
    • Combinez l’apprentissage automatique et les graphismes de Metal 4
    • Découvrez Metal 4
    • Faites passer vos jeux au niveau supérieur
    • Nouveautés du rendu Metal pour les apps immersives

    Tech Talks

    • Tune CPU job scheduling for Apple silicon games
  • Rechercher dans cette vidéo…

    Bonjour, je m’appelle Jason. Et je m’appelle Yang. Nous sommes ingénieurs en pilotes GPU. Dans cette vidéo, nous allons vous montrer comment accélérer votre moteur de jeu avec Metal 4. C’est la deuxième présentation de notre série en quatre parties sur la prochaine version majeure de l’API Metal. Avant de découvrir les jeux Metal 4, regardez « Discover Metal 4 » pour mieux comprendre toutes les possibilités qu’offre cette technologie. Après cette présentation, regardez « Go further with Metal 4 games » pour en savoir plus sur les nouvelles API Metal FX et Metal Raytracing. Et regardez « Combine Metal 4 machine learning and graphics » pour une conférence dédiée à l’intégration de l’AA. Découvrons Metal 4.

    Metal 4 est conçu pour les moteurs de jeu modernes. Les jeux vidéo d’Ubisoft, notamment Assassin’s Creed Shadows, nous transportent dans des univers fantastiques grâce à leurs personnages finement ciselés et leurs décors époustouflants de réalisme. Ils diffusent des volumes impressionnants de données géométriques et de textures haute définition, le tout traité par des milliers de shaders pour exploiter au maximum les capacités de la puce Apple. Les jeux du futur seront encore plus exigeants et auront besoin d’une API graphique à la hauteur. C’est ce que propose Metal 4.

    Metal 4 apporte des améliorations significatives à Metal pour les jeux. Un encodage efficace des commandes et une gestion évolutive des ressources accélèrent votre Hot Path, tandis qu’un chargement plus rapide du pipeline permet aux joueurs de passer plus vite des écrans de chargement au jeu. Dans cette présentation, nous verrons comment optimiser ces fonctionnalités. Dans chaque image de votre jeu, les draw calls, les dispatches de kernel, les blits et les tâches de ray tracing sont tous sur le hot path de l’encodage. L’encodage Metal 4 est conçu pour relever ce défi en étant efficace et simultané.

    Metal 4 unifie les opérations les plus courantes en deux classes d’encodeurs afin que vous puissiez en faire plus avec chaque encodeur. Les allocateurs de commandes vous permettent de gérer explicitement les allocations de mémoire allouées lors de l’encodage. Les tampons de commande vous permettent d’encoder le travail sur plusieurs threads.

    La vidéo « Discover Metal 4 » a décrit comment deux classes d’encodeurs, render et compute, gèrent l’encodage de toutes les commandes. Pour les utiliser efficacement, vous devez synchroniser les dépendances de données entre les opérations de calcul et utiliser des mappages de pièces jointes pour remapper les sorties du shader de fragments. Toutes les opérations de calcul, les dispatches de kernel, les blits et les builds de structure d’accélération peuvent désormais être encodés dans un seul encodeur de calcul. Sans synchronisation supplémentaire, ces commandes s’exécutent simultanément. Cela permet aux charges de travail sans dépendances de mieux utiliser les ressources GPU. Si certaines commandes de synchronisation doivent s’exécuter en série en raison d’une dépendance des données, vous pouvez l’exprimer à l’aide d’un Pass Barrier. Cette barrière assure que le GPU attend la fin des blits précédents sur cet encodeur avant de commencer les calculs suivants. Voici un exemple de la synchronisation de l’accès d’un blit à un dispatch. Tout d’abord, le blit copyFromBuffer met à jour le buffer1 pour encoder un pass barrier. Vous pouvez ensuite encoder un dispatch qui utilise les données de buffer1. C’est ce qu’on appelle l’encodage de calcul unifié. Effectuez tous vos calculs dans un seul encodeur et utilisez des barrières pour exprimer les dépendances entre les données. Metal 4 a également mis à jour l’encodage du rendu. Avec le mappage d’attachments de couleur, vous pouvez désormais contrôler la correspondance entre les sorties de couleur d’un pipeline de rendu et les attachements de l’encodeur de rendu. Au lieu de lier les pipelines à une disposition de cible de rendu fixe, vous pouvez fournir un mappage d’attachements de couleur. Ensuite, lorsque vous définissez un nouveau pipeline, vous pouvez modifier le mappage au lieu de changer d’encodeur. Supposons que vous ayez un pipeline Metal avec une fonction fragment qui dessine dans trois attachments. Sans mappage des attachments de couleur, vous créez un encodeur de rendu avec trois attachments de couleur. La fonction de fragment renvoie trois sorties couleur et l’encodeur dirige ces sorties vers les attachements correspondants dans la mémoire de tuiles. Pour le prochain draw call, vous aurez peut-être besoin d’un pipeline qui écrit dans un autre ensemble de sorties. Les attachments étant différents, vous devrez créer un encodeur de rendu pour faire correspondre les sorties de couleur. Grâce au mappage des attachments de couleur, vous n’avez pas besoin d’un deuxième encodeur. Au lieu de cela, l’encodeur de rendu possède tous les attachments de couleur nécessaires aux deux pipelines. Ensuite, le mappage d’attachments de couleur transfère la sortie d’ombrage en attachments spécifiques. Pour implémenter le mappage d’attachements de couleur, commencez par configurer un descripteur de passe de rendu qui prend en charge le mappage d’attachements de couleur. Créez ensuite l’ensemble complet des attachments qui seront utilisés par l’encodeur. Pour configurer l’attachment sur lequel l’encodeur va dessiner, créez un mappage d’attachments de couleur, puis définissez ses entrées de remappage. Pour chaque entrée, définissez un index logique pour déterminer la sortie du shader, ainsi qu’un index physique pour l’index d’attachement. Créez ces objets de mappage avant l’encodage et réutilisez-les à chaque image. Lorsque vous définissez un pipeline de rendu, associez également un mappage d’attachments de couleur. Si un pipeline dessine sur différents attachments, vous pouvez passer à un autre mappage d’attachments de couleur. Le mappage d’attachments de couleur peut réduire considérablement le nombre d’encodeurs de rendu dans votre jeu. Vous réduirez ainsi la surcharge d’encodage et améliorerez l’efficacité du GPU en réduisant le nombre de passes de rendu.

    Metal 4 vous donne également plus de contrôle sur les allocations de mémoire.

    Les allocateurs de commandes vous permettent de réutiliser la mémoire tampon des commandes et d’éviter les allocations dynamiques lors de l’encodage. La mémoire de l’allocateur augmente à mesure que vous encodez des commandes. Réinitialisez les allocateurs une fois que les commandes GPU associées ont fini de s’exécuter. La réinitialisation rend leur mémoire de commandes disponible pour une réutilisation dans l’encodage ultérieur des commandes. Vous pouvez utiliser plusieurs allocateurs pour éviter de bloquer l’encodage pendant que le GPU effectue son travail. Lorsque vous encodez sur un nouvel allocateur de commandes, celui-ci alloue de la mémoire pour les commandes encodées. Cette mémoire de commandes correspond au travail exécuté sur le GPU. Attendez donc que le travail engagé soit terminé avant de réinitialiser. Une fois le travail sur le GPU terminé, réinitialisez l’allocateur de commandes. Cette opération marque immédiatement sa mémoire comme disponible pour être réutilisée. Pour continuer l’encodage pendant l’exécution du travail sur le GPU, utilisez un deuxième allocateur de commandes. Vous pourrez ainsi bloquer l’encodage à la fin du travail sur le GPU. Vous devez réinitialiser un allocateur de commandes une fois que son travail sur le GPU est terminé. La mémoire de l’allocateur augmentera pour prendre en charge l’encodage jusqu’à ce qu’elle soit réinitialisée. Si vous ne prévoyez pas d’encoder d’autres tâches sur un allocateur de commandes, vous pouvez le libérer afin de réduire votre empreinte mémoire. Les allocateurs de commandes ne sont pas thread-safe. Utilisez donc des allocateurs différents pour chaque thread. Ceci est important lorsque vous parallélisez votre encodage de scène. Les tampons de commande Metal 4 vous permettent de diviser votre encodage sur plusieurs threads. Dans l’encodage monothread, vous encodez une série de commandes dans un ou plusieurs tampons de commande en séquence. Tirez parti du puissant CPU multicœur de la puce Apple et lancez plusieurs tampons de commande sur plusieurs threads, chacun avec un allocateur de commande différent. Vous pouvez utiliser la flexibilité améliorée de l’encodeur de calcul de Metal 4 pour répartir uniformément l’encodage des blits, des dispatches et des structures d’accélération. Lorsque vous avez terminé d’encoder vos tampons de commande, vous pouvez les soumettre avec un seul appel de commit. Metal 4 vous permet également de valider plusieurs encodeurs de rendu en une seule passe sur le GPU. Supposons que vous ayez une passe de rendu qui prend beaucoup de temps à encoder. Par défaut, si vous répartissez votre encodage sur plusieurs encodeurs de rendu différents, le GPU les traite comme des passes de rendu distinctes. Chaque passe entraîne une surcharge pour le stockage et le chargement des résultats intermédiaires.

    Metal 4 offre l’option Suspendre/Reprendre pour fusionner plusieurs encodeurs de rendu. Il suffit de suspendre un encodeur de rendu dans un tampon de commande et de le reprendre dans un autre tampon de commande. Une fois que vous avez terminé d’encoder les tampons de commande, soumettez-les séquentiellement en un seul appel de commit. L’envoi des encodeurs de rendu dans un commit indique à Metal de fusionner les passes de rendu. Pour utiliser cette fonction, créez le premier encodeur avec des options de suspension. Metal fusionnera cet encodeur avec les futurs encodeurs. Utilisez un tampon de commande différent pour chaque encodeur. L’encodeur central possède à la fois des options de reprise et de suspension. Créez l’encodeur final en ne reprenant que les options. Validez les trois tampons de commande encodés ensemble. Et voilà ! Vos passes de rendu sont maintenant fusionnées. Vous pouvez utiliser Metal 4 pour améliorer l’efficacité de votre encodage en réduisant le nombre de vos encodeurs, en réutilisant votre mémoire de commande et en encodant sur plusieurs threads. Pour en savoir plus sur l’encodage des commandes Metal 4, consultez l’article sur le site Apple Developer. Maintenant que nous avons abordé l’encodage efficace, passons à la gestion efficace des ressources. Metal 4 propose de nouvelles fonctionnalités pour vous aider à gérer vos ressources à grande échelle. Les tables d’arguments et les jeux de résidence vous permettent d’étendre votre liaison de ressources à des milliers de ressources. Metal 4 vous permet de gérer vos ressources graphiques et vous offre un contrôle sur les dépendances. Les Queue Barriers permettent d’exprimer vos dépendances de ressources à grande échelle. Les pools de vues de texture et les heap sparse de placement vous aident à gérer la mémoire requise par les ressources volumineuses. L’augmentation de la complexité des shaders signifie souvent qu’un modèle sans liaison est approprié pour la quantité de ressources. Avec un seul tampon d’arguments, votre shader peut accéder à des milliers de ressources, notamment des tampons, des textures, des échantillonneurs, des états de pipeline, etc. Mais les points de liaison indexés sont toujours utilisés pour lier vos ressources de niveau racine.

    Utilisez des tables d’arguments pour lier les ressources par index. Pendant l’encodage, définissez quelle table d’arguments doit être utilisée par le prochain dessin ou dispatch. Ces ressources sont disponibles pour les shaders en tant qu’arguments de fonction indexés. Au moment du dessin et du dispatch, Metal rassemble les arguments. Vous pouvez donc créer une nouvelle ressource pour un index de liaison entre les différents appels de rendu. Une table d’arguments unique peut être définie sur plusieurs étapes d’encodage.

    La création de tables d’arguments avant l’encodage vous permet de déplacer la liaison des ressources en dehors de votre chemin critique. Vous pouvez attacher une table d’arguments à plusieurs encodeurs. Utilisez les tables d’arguments avec les tampons d’arguments pour adapter vos besoins en matière de liaison de ressources. L’étape suivante pour accéder à ces ressources dans vos shaders consiste à les rendre visibles par le GPU. Chaque fois que vous avez besoin d’une ressource sur le GPU, ajoutez-la à un ensemble de résidence. qui inclut les pipelines, les tampons, les textures et les drawables. Les ensembles de résidence vous permettent de regrouper des ressources tout en les rendant visibles. Attachez-les à un tampon de commande en cours de validation ou directement à la file d’attente de commandes. Si un ensemble de résidence ne change pas beaucoup au fil du temps, attachez-le à la file d’attente de commandes. Si un ensemble de résidence change fréquemment, attachez-le aux tampons de commande appropriés. La préparation de ressources importantes pour le GPU peut prendre du temps. Vous pouvez demander à Metal de rendre les ressources d’un ensemble résidentes à l’avance. Il vaut mieux opter pour un nombre limité de résidences, mais avec plus de ressources. Cela permet d’optimiser les performances en donnant à Metal la possibilité de gérer vos ressources de manière groupée. Pour en savoir plus sur les ensembles de résidence, consultez l’article sur le site Apple Developer, ainsi que la présentation de l’année dernière intitulée « Port advanced games to Apple platforms ». Avec Metal 4, votre contrôle sur la résidence des ressources s’applique également aux surfaces drawable de votre jeu. Pour envoyer le contenu rendu de votre jeu à l’écran, vous effectuez un rendu sur les surfaces drawable dans un CAMetalLayer. Chaque couche Metal gère un ensemble de résidence dynamique. Ajoutez-le à votre file d’attente de commandes pour que toutes les textures du calque soient résidentes. Il vous suffit d’ajouter la résidence une seule fois. CAMetalLayer le mettra à jour si nécessaire. Dans Metal 4, vous devez également synchroniser votre rendu avec l’élément drawable. Au cours de chaque image, après avoir obtenu le prochain élément drawable, encodez une attente dans la file d’attente des commandes avant de procéder au rendu de l’élément drawable. Ensuite, après avoir validé le travail de rendu, encodez un signal dans l’élément drawable de la file d’attente.

    Appelez la fonction present pour envoyer le contenu de votre image à l’écran une fois le rendu terminé. Pour réduire la charge liée au suivi, Metal 4 vous permet de synchroniser vos ressources. Plus tôt dans cette présentation, j’ai expliqué comment utiliser les Pass Barriers dans les encodeurs. Les Queue Barriers, quant à elles, expriment une dépendance de données entre les encodeurs d’une même file d’attente.

    Les barrières filtrent en fonction des étapes de Metal. Chaque commande d’un encodeur est associée à une ou plusieurs étapes d’exécution. Par exemple, les draw calls dans les encodeurs de rendu génèrent des opérations de fragment shading et de sommets. Les GPU de la puce Apple regroupent toutes les opérations des sommets, puis toutes les opérations de fragment shading. Les commandes de calcul de Metal 4 correspondent aux étapes de la structure de dispatch, de blit et d’accélération. Il est important de choisir des étapes appropriées pour éviter une sursynchronisation. Dans cet exemple, une passe de calcul effectue une simulation atmosphérique dans un dispatch de kernel. Il écrit les résultats dans une texture en mémoire. La passe de rendu génère la scène. Le fragment shading nécessite que les résultats de la simulation soient fusionnés avec l’éclairage, mais le travail sur les sommets doit pouvoir se chevaucher librement avec le travail de calcul. Pour synchroniser l’accès aux résultats de la simulation, encodez une barrière entre les étapes de dispatch de la file d’attente et l’étape de fragment de l’encodeur de rendu. Pour implémenter cet exemple, commencez par encoder le dispatch sur un encodeur de calcul. Ensuite, sur un encodeur de commande de rendu, ajoutez une barrière après le dispatch de l’étape de file d’attente et avant l’étape de fragment. Après la barrière, vous pouvez encoder les draw calls. Metal garantit que toute tâche de fragment, dans l’encodeur de rendu actuel et dans les encodeurs futurs, n’est pas exécutée tant que toutes les tâches de dispatch des encodeurs précédents ne sont pas terminées.

    Pour vous aider à trouver les meilleurs emplacements pour vos barrières, le débogueur Metal vous indiquera leur position, ainsi que les encodeurs et les étapes auxquels ils s’appliquent. Utilisez-le pour maximiser la simultanéité tout en conservant vos dépendances de données.

    Pour en savoir plus sur la synchronisation de vos ressources à l’aide de barrières Metal, consultez les articles complets sur le site Apple Developer.

    La diffusion en continu de textures et de tampons vous permet de gérer l’empreinte mémoire de milliers de ressources. Metal 4 vous permet de diffuser efficacement des tampons et des textures. Vous pouvez créer des vues de textures légères et gérer votre empreinte de ressources mémoire avec Placement Sparse. Les jeux modernes peuvent créer des centaines de vues de texture et de tampon de texture par image. TextureViewPools vous permet de préallouer la mémoire requise pour contenir toutes vos vues de texture. Vous pouvez ensuite créer des vues de texture légères à n’importe quel index du pool. Cela n’entraîne aucune allocation dynamique, vous pouvez donc les créer pendant l’encodage. Utilisez l’ID de ressource de la vue de texture pour la lier à un tampon ou à une table d’arguments. Voici comment vous pouvez mettre cela en œuvre. Créez un pool de vues de texture avant l’encodage. Dans ce cas, le pool de vues de texture créé a une mémoire allouée pour 500 vues de texture. Lors de l’encodage, définissez une vue de texture à l’index souhaité dans le pool de vues de texture. Utilisez le MTLResourceID renvoyé pour lier votre vue de texture à une table d’arguments. Parfois, les ressources que vous devez lier peuvent avoir une empreinte mémoire importante. Les ressources sparse sont idéales pour les ressources haute fidélité qui ne peuvent pas toutes tenir en mémoire en même temps. Elles dissocient la création des ressources de leur support mémoire. Grâce aux éléments sparse de placement, vous contrôlez la façon dont vos ressources sont mappées sur les pages de votre heap. Lors de la mise à jour des mappages mémoire pour vos ressources, les API de la file d’attente de commandes Metal 4 vous permettent de synchroniser ces mises à jour avec d’autres tâches GPU. La mémoire d’un heap de placement est organisée sous la forme d’une séquence de tuiles. Vous contrôlez l’affectation de ces tuiles à vos tampons et textures sparse. Fournissez de la mémoire aux ressources sparses en mappant des plages d’octets ou des zones de pixels à des tuiles sparse.

    Lors de la création d’un heap de placement, tenez compte des tailles de page sparse dont vos ressources auront besoin. Les pages de plus grande taille offrent certains avantages en termes de performances lors des opérations de mappage et de démappage, mais elles utilisent davantage de mémoire pour le padding et l’alignement. Le heap prend en charge toutes les tailles de page sparse jusqu’au maximum que vous spécifiez. Cet exemple choisit une taille de page maximale de 64 Ko. Une fois que vous avez créé un heap de placement, vous pouvez créer les ressources sparse. La création de tampons et de textures sparse se fait à partir d’un appareil Metal, comme pour les ressources non sparse. Pour les tampons, alignez la taille de tampon demandé sur les multiples d’une taille de tuile sparse. L’appareil fournit une requête pour effectuer cette conversion. Définissez la taille de page sparse lors de l’appel d’un nouveau tampon avec la longueur ou sur un descripteur de texture. Cette propriété informe l’appareil Metal qu’un heap de placement fournira le support mémoire. Lorsque vous créez pour la première fois un tampon sparse, il n’y a pas de mémoire qui le sauvegarde. Vous affectez des tuiles à des plages de tampons avec des opérations de mappage de mise à jour. Pour affecter des tuiles d’un heap de placement à un tampon, spécifiez d’abord une opération de mappage de mise à jour. Indiquez le décalage et la longueur de départ et le décalage de tuile dans le heap à affecter à cette plage de tampon. Soumettez ensuite l’opération de mappage sur une file d’attente de commandes Metal 4.

    Pour en savoir plus sur l’utilisation de ressources sparse, consultez l’article sur le site web Apple Developer. Un autre défi pour les jeux modernes est la gestion de grandes bibliothèques d’états de pipeline. Pour cela, je vais passer la parole à Yang. Merci, Jason. Les jeux modernes doivent créer des milliers de pipelines pour créer des visuels complexes et dynamiques. Le chargement rapide de nombreux pipelines est essentiel pour éliminer les saccades liées au traitement des shaders et réduire le temps de chargement de votre jeu. Pour charger rapidement des pipelines dans Metal 4, vous pouvez réutiliser vos compilations de pipeline de rendu. Vous pouvez aussi compiler des pipelines sur un appareil avec un nouveau niveau de parallélisme. Pour aller plus loin, la compilation des pipelines à l’avance vous permet de réduire le temps de chargement de votre pipeline à presque zéro. Je vais commencer par vous montrer comment réutiliser vos compilations de pipeline de rendu avec des états de pipeline de rendu flexibles. Imaginons que vous développiez un jeu de construction dans lequel les joueurs peuvent placer des maisons sur une carte. Lorsque le joueur choisit l’emplacement d’une maison, le jeu doit afficher la maison sous forme d’hologramme. Vous devez donc avoir un pipeline avec un état de fusion additive. Le joueur place la maison et la construction commence. Pour rendre la maison transparente et montrer l’avancement des travaux, vous devez utiliser un autre pipeline avec un état de fusion transparent. Enfin, lorsque la construction de la maison est terminée, celle-ci est rendue à l’aide d’un troisième pipeline avec un état de fusion opaque. Vous pouvez compiler ces trois pipelines avec un état complet en fournissant les configurations complètes du pipeline lors de leur création. Commencez par une fonction de sommet et la fonction de fragment, ainsi que les configurations d’attachements de couleur pour les maisons opaques, transparentes et holographiques. La configuration des attachements de couleur désigne ici la partie du descripteur qui affecte l’écriture des sorties du fragment shader vers les attachements de couleur. Cela inclut le format pixel des attachments, les masques appropriés et les états de fusion. Créez un descripteur de pipeline de rendu qui fait référence à la fonction de sommet, à la fonction de fragment et à la configuration opaque. Avec ce descripteur, vous créez le pipeline opaque contenant un binaire de sommet, un corps binaire de fragment et une partie de sortie de fragment. En modifiant la configuration des attachments de couleur dans le descripteur, vous pouvez créer de la même manière le pipeline transparent et le pipeline hologramme. La plupart des binaires de ces trois pipelines sont identiques, seule la partie de sortie de fragment est différente. À partir d’une vue chronologique du CPU, vous compilez l’intégralité du pipeline opaque, puis le pipeline transparent, suivi du pipeline holographique. Le CPU consacre beaucoup de temps à recompiler le même pipeline, à l’exception de la sortie des fragments. Avec Metal 4, vous pouvez réutiliser la majeure partie du traitement du pipeline en créant un pipeline non spécialisé. Utilisez ensuite différentes configurations d’attachments de couleur pour obtenir les pipelines spécialisés finaux nécessaires. Cela peut vous faire gagner beaucoup de temps dans le processus de rendu. Pour réaliser ces économies, commencez par créer un pipeline non spécialisé. Commencez par le même descripteur, mais au lieu de fournir la configuration réelle d’attachments de couleur, définissez tous les champs sur non spécialisé. Pour ce faire, il suffit de parcourir tous les descripteurs d’attachement de couleur et de définir pixelFormat, writeMask et blendingState sur leurs valeurs non spécialisées correspondantes. Le pipeline non spécialisé contient un binaire de sommet, le corps binaire du fragment et la partie de sortie par défaut du fragment. La sortie de fragment par défaut fonctionne dans certains cas, mais la plupart du temps, il sera nécessaire de la remplacer en spécialisant le pipeline. Pour créer le pipeline spécialisé, commencez par le pipeline non spécialisé et un nouveau descripteur de pipeline de rendu. Cette fois-ci, définissez la configuration d’attachement de couleur dans le descripteur sur les valeurs nécessaires. Le pipeline spécialisé contient une sortie de fragment correspondante qui remplace la sortie de fragment par défaut. Cette nouvelle sortie de fragment peut être générée très rapidement. Nul besoin donc de recommencer le processus complet de compilation des shaders. Je vais revenir à l’exemple de la spécialisation du pipeline transparent. Commencez par définir les propriétés non spécialisées dans le descripteur. Activez l’état de fusion et définissez des sous-états de fusion. Le code ici définit le pipeline pour effectuer un mélange alpha prémultiplié. Ensuite, instanciez le pipeline spécialisé à l’aide du nouveau descripteur avec le pipeline non spécialisé. Votre jeu peut créer des milliers de pipelines de rendu avec état. Pour réduire au maximum le temps de chargement, créez l’ensemble de votre pipeline de rendu de manière non spécialisée, puis spécialisez-le ultérieurement selon vos besoins. Après cela, vous remarquerez peut-être une légère baisse des performances du GPU. Une grande partie de la surcharge peut provenir d’un travail inutile du corps de fragment partagé. Par exemple, si un fragment shader écrit dans quatre canaux de couleur et que l’attachment de couleur ne comporte qu’un seul canal, le compilateur n’est plus en mesure d’optimiser les canaux inutilisés. Il y a aussi une légère surcharge due au passage du corps binaire du fragment à la partie de sortie du fragment. Cette surcharge est généralement faible, mais peut être importante dans certains shaders de fragment. Identifiez les shaders importants et compilez-les avec leur état en arrière-plan afin que votre joueur puisse bénéficier de temps de chargement courts et d’une excellente fréquence d’images. Vous pouvez utiliser les Instruments Metal System Trace pour classer les shaders de fragments spécialisés les plus gourmands. Pour récapituler, voici comment intégrer au mieux des états de pipeline de rendu flexibles dans votre jeu. Commencez par compiler tous les pipelines de rendu non spécialisés et spécialisez-les selon vos besoins. En cas de baisse notable des performances, utilisez l’Instrument Metal System Trace pour établir le profil de votre jeu et identifier les pipelines importants. Pour ces pipelines, compilez une version de base en arrière-plan et utilisez-la pour remplacer une version spécialisée lorsqu’elle est prête. Pour en savoir plus sur les états de pipeline de rendu flexibles, consultez cet article sur le site web Apple Developer.

    Après avoir réutilisé des compilations de pipeline avec des états de pipeline de rendu flexibles, raccourcissez encore le temps de chargement de votre pipeline en paralysant la compilation de votre propre appareil. Certains jeux peuvent utiliser un seul thread pour charger les pipelines pendant le jeu. Voici le thread de compilation unique qui crée les pipelines que le jeu est sur le point d’utiliser. Et voici un thread de rendu qui exécute le travail de rendu répété des images, tel que l’encodage. Si les pipelines requis ne sont pas prêts à temps, le jeu peut présenter des ralentissements. Vous pouvez accélérer le chargement du pipeline en ajoutant un autre thread de compilation. La compilation du pipeline se termine plus tôt. Cependant, si vous ne faites pas attention à la priorité des threads, le jeu peut toujours manquer ses intervalles actuels. Après avoir baissé la priorité du processus en arrière-plan par rapport à celle du processus d’affichage, le problème a été résolu. Le joueur peut désormais profiter d’un jeu plus fluide. Voici comment ajouter le traitement multithread à votre jeu. Utilisez les API de traitement Metal 4. Le compilateur est capable d’aller encore plus loin dans Metal 4. Vous pouvez utiliser Grand Central Dispatch ou créer votre propre pool de threads, selon ce qui correspond le mieux à l’architecture de votre jeu. Quel que soit la solution choisie, n’oubliez pas de définir une priorité appropriée. Metal respectera la priorité de vos tâches de compilation. Grand Central Dispatch est le moyen le plus simple de publier vos compilations multithreads. Si vous souhaitez que la compilation hérite de la priorité du thread appelant, vous pouvez utiliser des groupes de répartition avec les méthodes asynchrones fournies par le compilateur. Les méthodes asynchrones sont celles qui ont un gestionnaire de traitement. Metal exécutera automatiquement ces méthodes simultanément. Si vous souhaitez personnaliser la priorité de votre compilation, vous pouvez créer une file d’attente de répartition simultanée avec une qualité de service personnalisée, ou classe QoS. Pour le préchauffage et la diffusion en continu des pipelines, gardez les paramètres par défaut. Pour soumettre des tests de traitement à une file d’attente de dispatch, vous pouvez invoquer des méthodes de synchronisation dans un bloc et les envoyer à la file d’attente via dispatch_async. Les méthodes de synchronisation sont celles qui n’ont pas de gestionnaire de traitement.

    Vous pouvez également créer votre propre pool de threads si cela correspond mieux à l’architecture de votre jeu. Utilisez une propriété maximumConcurrentCompletionTestCount dans Metal Device comme nombre de threads pour un pool de threads. Définissez le nombre de threads par défaut sur 2, car il s’agit de la concurrence maximale dans les systèmes d’exploitation qui ne prennent pas en charge cette propriété. Il est également important de définir une classe de qualité de service appropriée, ou QoS, pour nos threads de compilation afin d’éviter d’affamer d’autres threads importants dans votre jeu. Définissez la classe QoS par défaut pour le préchauffage et la diffusion en continu du pipeline. C’est tout ! Vous pouvez maintenant commencer à envoyer des tâches de compilation à votre pool de threads. Pour en savoir plus sur les méthodes optimales de parallélisation et de priorisation lors de la compilation de votre pipeline, consultez les articles disponibles sur le site Apple Developer. La compilation multithread sur l’appareil peut réduire considérablement votre temps de compilation. Pour optimiser davantage les performances, la solution consiste à précompiler les pipelines lors du développement. Pour compiler les pipelines à l’avance, les jeux utilisent généralement un processus de bout en bout. Ce processus commence par la collecte des configurations de pipeline utilisées dans le jeu en exécutant ce dernier avec certains instruments. Les résultats récoltés sont introduits dans la chaîne d’outils GPU pour créer des binaires GPU. Enfin, au moment de l’exécution, le jeu recherche les fichiers binaires GPU précompilés pour créer rapidement des pipelines. Metal 4 facilite plus que jamais la collecte en ligne de votre configuration de pipeline et la recherche de binaires précompilés dans votre jeu commercialisé. Dans Metal 4, le moyen le plus simple de récolter une configuration de pipeline consiste à sérialiser un script de pipeline. Un script de pipeline est un fichier au format JSON. Il contient une représentation textuelle des descripteurs de pipeline que vous créez sur l’appareil. La sérialisation d’un script de pipeline est simplifiée grâce au sérialiseur de jeux de données de pipeline dans Metal 4. Une fois cet objet lié à votre compilateur, il enregistre automatiquement les descripteurs pour les pipelines créés. Ensuite, vous pouvez sérialiser ces descripteurs dans un script pipeline. Pour créer le sérialiseur du jeu de données du pipeline, commencez par un descripteur. Définissez la configuration sur CaptureDescriptors. Cela indique au sérialiseur de suivre uniquement les descripteurs de pipeline et réduit son empreinte mémoire. Utilisez un descripteur de sérialiseur pour créer le sérialiseur du jeu de données du pipeline. Attachez ensuite un sérialiseur au descripteur de compilateur que vous utilisez pour créer le compilateur. Après avoir créé le compilateur, vous pouvez l’utiliser pour créer des pipelines comme d’habitude. Le sérialiseur enregistre automatiquement les descripteurs de pipeline que vous utilisez. Une fois la collecte terminée, appelez serializeAsPipelinesScriptWithError pour sérialiser les pipelines enregistrés dans un script de pipeline. La valeur renvoyée est une NSData. Vous pouvez utiliser votre méthode préférée pour le renvoyer vers votre système de développement. Cet exemple l’écrit simplement dans un fichier sur le disque. Définissez le suffixe du fichier sur mtl4-json. C’est le suffixe attendu par la chaîne d’outils GPU. Une fois que vous avez récupéré les configurations du pipeline, l’étape suivante consiste à créer les fichiers binaires. Intégrez votre script de configuration de pipeline et les bibliothèques Metal IR à metal-tt. Il produira les binaires GPU compressés dans une archive Metal. Avant d’exécuter le script pipeline récupéré dans metal-tt, ouvrez le script et modifier le chemin d’accès aux bibliothèques Metal IR afin qu’il corresponde à celui de votre système de développement. Pour plus d’informations sur le format du script de configuration du pipeline, ouvrez la page du manuel à l’aide de cette commande. Ensuite, exécutez simplement la commande metal-tt à l’écran pour créer une archive pour iOS. Après avoir précompilé les fichiers binaires, votre jeu devra pouvoir les localiser pendant son exécution. Metal 4 simplifie encore plus la création de pipelines à partir d’archives contenant des binaires GPU. Il suffit d’utiliser le même descripteur que celui utilisé pour compiler sur l’appareil afin de récupérer les états du pipeline. Par exemple, créez un objet MTL4Archive en fournissant l’URL de votre archive. Ensuite, interrogez l’état du pipeline directement à partir de l’objet d’archive à l’aide d’un descripteur de pipeline.

    La recherche dans les archives peut échouer pour plusieurs raisons, par exemple l’absence de pipeline correspondant, un système d’exploitation incompatible ou une architecture GPU incompatible. Vous devez gérer vous-même ces erreurs dans Metal 4. Ici, le programme revient simplement à la compilation sur l’appareil, de sorte que le jeu ait toujours un état de pipeline requis pour continuer.

    Voici à nouveau la chronologie du CPU pour l’exemple de jeu avec un traitement multithread sur l’appareil. En adoptant le traitement anticipé, le temps de chargement du pipeline est réduit à presque rien. Pour en savoir plus sur le traitement anticipé, consultez ces articles sur le site web Apple Developer. Voici un récapitulatif. Metal 4 offre d’excellents moyens pour accélérer le chargement des états du pipeline. Vous pouvez réutiliser les résultats de la compilation à l’aide de la spécialisation de pipeline. Vous pouvez accélérer davantage la compilation grâce au multithreading. Pour réduire au maximum les temps de chargement du pipeline, adoptez la compilation anticipée avec un workflow simplifié de collecte et de recherche. Nous vous avons présenté de nombreuses façons d’utiliser les API Metal 4 pour créer la prochaine génération de jeux. Téléchargez Xcode pour optimiser l’encodage, la gestion des ressources et le chargement du pipeline de votre jeu. Nous avons inclus des exemples de projets et des articles détaillés pour vous aider. Pour découvrir d’autres fonctionnalités de Metal 4, consultez les autres présentations de cette série. Merci de votre attention.

    • 0:01 - Synchronize access to a buffer within an encoder

      // Synchronize access to a buffer within an encoder
      
      id<MTL4ComputeCommandEncoder> encoder = [commandBuffer computeCommandEncoder];
      
      [encoder copyFromBuffer:src sourceOffset:0 toBuffer:buffer1 destinationOffset:0 size:64];
      
      [encoder barrierAfterEncoderStages:MTLStageBlit 
                     beforeEncoderStages:MTLStageDispatch
                       visibilityOptions:MTL4VisibilityOptionDevice];
      
      [encoder setComputePipelineState:pso];
      
      [argTable setAddress:buffer1.gpuAddress atIndex:0];
      [encoder setArgumentTable:argTable];
      [encoder dispatchThreads:threadsPerGrid threadsPerThreadgroup:threadsPerThreadgroup];
      
      [encoder endEncoding];code snippet.
    • 4:29 - Configure superset of color attachments

      // Configure superset of color attachments
      
      MTL4RenderPassDescriptor *desc = [MTLRenderPassDescriptor renderPassDescriptor];
      
      desc.supportColorAttachmentMapping = YES;
      
      desc.colorAttachments[0].texture = colortex0;
      desc.colorAttachments[1].texture = colortex1;
      desc.colorAttachments[2].texture = colortex2;
      desc.colorAttachments[3].texture = colortex3;
      desc.colorAttachments[4].texture = colortex4;
    • 4:38 - Set color attachment map entries

      // Set color attachment map entries
      
      MTLLogicalToPhysicalColorAttachmentMap* myAttachmentRemap = [MTLLogicalToPhysicalColorAttachmentMap new];
      
      [myAttachmentRemap setPhysicalIndex:0 forLogicalIndex:0];
      [myAttachmentRemap setPhysicalIndex:3 forLogicalIndex:1];
      [myAttachmentRemap setPhysicalIndex:4 forLogicalIndex:2];
    • 4:57 - Set a color attachment map per pipeline

      // Set a color attachment map per pipeline
      
      [renderEncoder setRenderPipelineState:myPipeline];
      [renderEncoder setColorAttachmentMap:myAttachmentRemap];
      // Draw with myPipeline
      
      [renderEncoder setRenderPipelineState:myPipeline2];
      [renderEncoder setColorAttachmentMap:myAttachmentRemap2];
      // Draw with myPipeline2
    • 8:03 - Encode a single render pass with 3 render encoders

      // Encode a single render pass with 3 render encoders with suspend/resume options
      
      
      id<MTL4RenderCommandEncoder> enc0 = [cmdbuf0 renderCommandEncoderWithDescriptor:desc options:MTL4RenderEncoderOptionSuspending];
      
      id<MTL4RenderCommandEncoder> enc1 = [cmdbuf1 renderCommandEncoderWithDescriptor:desc options:MTL4RenderEncoderOptionResuming | MTL4RenderEncoderOptionSuspending];
      
      id<MTL4RenderCommandEncoder> enc2 = [cmdbuf2 renderCommandEncoderWithDescriptor:desc options:MTL4RenderEncoderOptionResuming];
      
      
      id<MTL4CommandBuffer> cmdbufs[] = { cmdbuf0, cmdbuf1, cmdbuf2 };
      [commandQueue commit:cmdbufs count:3]
    • 11:48 - Synchronize drawable contents

      // Synchronize drawable contents
      
      id<MTLDrawable> drawable = [metalLayer nextDrawable];
      [queue waitForDrawable:drawable];
      
      // ... encode render commands to commandBuffer ...
      [queue commit:&commandBuffer count:1];
      
      [queue signalDrawable:drawable];
      
      [drawable present];
    • 13:25 - Encode a queue barrier to synchronize data

      // Encode a queue barrier to synchronize data
      
      id<MTL4ComputeCommandEncoder> compute = [commandBuffer computeCommandEncoder];
      
      [compute dispatchThreadgroups:threadGrid threadsPerThreadgroup:threadsPerThreadgroup];
      
      [compute endEncoding];
      
      
      id<MTL4RenderCommandEncoder> render = [commandBuffer renderCommandEncoderWithDescriptor:des];
      
      [render barrierAfterQueueStages:MTLStageDispatch
                         beforeStages:MTLStageFragment
                    visibilityOptions:MTL4VisibilityOptionDevice];
      
      [renderCommandEncoder drawPrimitives:MTLPrimitiveTypeTriangle
                               vertexStart:vertexStart
                               vertexCount:vertexCount];
      
      [render endEncoding];
    • 14:57 - Create a texture view pool

      // Create a texture view pool
      
      MTLResourceViewPoolDescriptor *desc = [[MTLResourceViewPoolDescriptor alloc] init]; 
      desc.resourceCount = 500;
       
      id <MTLTextureViewPool> myTextureViewPool =  
          [myDevice newTextureViewPoolWithDescriptor:myTextureViewPoolDescriptor 
                                               error:nullptr];
    • 15:07 - Set a texture view

      // Set a texture view
      
      MTLResourceID myTextureView = [myTextureViewPool setTextureView:myTexture  
                                                           descriptor:myTextureViewDescriptor  
                                                              atIndex:5];
      
      [myArgumentTable setTexture:myTextureView 
                          atIndex:0];
    • 16:01 - Choose appropriate sparse page size

      MTLHeapDescriptor *desc = [MTLHeapDescriptor new];    
      desc.type = MTLHeapTypePlacement;
      desc.storageMode = MTLStorageModePrivate;
      desc.maxCompatiblePlacementSparsePageSize = MTLSparsePageSize64;
      desc.size = alignedHeapSize;
      
      id<MTLHeap> heap = [device newHeapWithDescriptor:desc];
    • 17:05 - Update buffer mappings

      // Update buffer mappings
      
      MTL4UpdateSparseBufferMappingOperation bufferOperation;
      
      bufferOperation.mode = MTLSparseTextureMappingModeMap;  
      bufferOperation.bufferRange.location = bufferOffsetInTiles;
      bufferOperation.bufferRange.length = length;
      bufferOperation.heapOffset = heapOffsetInTiles;
      
      [cmdQueue updateBufferMappings:myBuf heap:myHeap operations:&bufferOperation count:1];
    • 20:41 - Set unspecialized configuration

      // In MTL4RenderPipelineColorAttachmentDescriptor
      // Set unspecialized configuration
      
      pipelineDescriptor.colorAttachments[i].pixelFormat   = MTLPixelFormatUnspecialized;
      pipelineDescriptor.colorAttachments[i].writeMask     = MTLColorWriteMaskUnspecialized;
      pipelineDescriptor.colorAttachments[i].blendingState = MTL4BlendStateUnspecialized;
    • 21:40 - Create a specialized transparent pipeline

      // Create a specialized transparent pipeline
      
      // Set the previously unspecialized properties
      pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
      pipelineDescriptor.colorAttachments[0].writeMask =
          MTLColorWriteMaskRed | MTLColorWriteMaskGreen | MTLColorWriteMaskBlue;
      pipelineDescriptor.colorAttachments[0].blendingState = MTL4BlendStateEnabled;
      
      pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;
      pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = 
          MTLBlendFactorOneMinusSourceAlpha;
      pipelineDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
      
      id<MTLRenderPipelineState> transparentPipeline = 
          [compiler newRenderPipelineStateBySpecializationWithDescriptor:pipelineDescriptor
                                                                pipeline:unspecializedPipeline
                                                                   error:&error];
      
      // Similarly, create the specialized opaque and hologram pipelines
    • 26:22 - Determine thread count

      // Determine thread count
      NSInteger numThreads = 2;
      if (@available(macOS 13.3, iOS 19, visionOS 3, tvOS 19, *))
      {
          numThreads = [device maximumConcurrentCompilationTaskCount];
      }
    • 26:30 - Set a proper QoS class for your compilation threads

      // Create thread pool
      for (NSInteger i = 0; i < numThreads; ++i)
      {
          // Creating a thread with a QoS class DEFAULT
          pthread_attr_set_qos_class_np(&attr, QOS_CLASS_DEFAULT, 0) ;
          pthread_create(&threadIds[i], &attr, entryPoint, NULL);
          pthread_attr_destroy(&attr);
      }
    • 28:24 - Harvest pipeline configuration scripts

      // Harvest pipeline configuration scripts with the pipeline data set serializer
      
      // Create a pipeline data set serializer that only captures descriptors
      MTL4PipelineDataSetSerializerDescriptor *desc = [MTL4PipelineDataSetSerializerDescriptor new];
      desc.configuration = MTL4PipelineDataSetSerializerConfigurationCaptureDescriptors;
      id<MTL4PipelineDataSetSerializer> serializer =
          [device newPipelineDataSetSerializerWithDescriptor:desc];
      
      // Set the pipeline data set serializer when creating the compiler
      MTL4CompilerDescriptor *compilerDesc = [MTL4CompilerDescriptor new];
      [compilerDesc setPipelineDataSetSerializer:serializer];
      id<MTL4Compiler> compiler = [device newCompilerWithDescriptor:compilerDesc error:nil];
      
      // Create pipelines using the compiler as usual
      
      // Serialize the descriptors as a pipeline script
      NSData *data = [serializer serializeAsPipelinesScriptWithError:&err];
      
      // Write the pipeline script data to disk
      NSString *path = [NSString pathWithComponents:@[folder, @"pipelines.mtl4-json"]];
      BOOL success = [data writeToFile:path options:NSDataWritingAtomic error:&err];
    • 30:28 - Query pipeline state from MTLArchive

      // Query pipeline state from MTLArchive
      
      id<MTL4Archive> archive = [device newArchiveWithURL:archiveURL error:&error];
      
      id<MTLRenderPipelineState> pipeline = 
          [archive newRenderPipelineStateWithDescriptor:descriptor error:&error];
      
      if (pipeline == nil)
      {
          // handle lookup miss
      		pipeline = [compiler newRenderPipelineStateWithDescriptor:descriptor 
                                                compilerTaskOptions:nil 
      }
    • 0:00 - Introduction
    • Cette séance est la deuxième d’une série en quatre volets consacrée à Metal 4, la nouvelle API graphique d’Apple conçue pour les moteurs de jeu modernes. Metal 4 améliore l’encodage des commandes, la gestion des ressources et le chargement des pipelines. Metal 4 répond aux exigences des jeux actuels et futurs, qui diffusent des gigaoctets de géométrie détaillée et de textures, rendus à l’aide de milliers de shaders, afin de tirer pleinement parti de toute la puissance de calcul offerte par la puce Apple. Regardez également les autres volets de la série pour en savoir plus sur MetalFX, le ray tracing et l’intégration de l’apprentissage automatique.

    • 1:33 - Encoder plus efficacement
    • Metal 4 est conçu pour améliorer l’efficacité du GPU en optimisant l’encodage des commandes. Il introduit deux principales classes d’encodeurs, render et compute, qui peuvent désormais gérer la plupart des opérations courantes dans les jeux. Vous pouvez utiliser Metal 4 pour améliorer l’efficacité de votre encodage en réduisant le nombre de vos encodeurs, en réutilisant votre mémoire de commande et en encodant sur plusieurs threads.

    • 8:42 - Optimiser la gestion de vos ressources
    • Metal 4 propose de nouvelles fonctionnalités pour vous aider à gérer vos ressources à grande échelle. Les tables d’arguments et les jeux de résidence vous permettent d’étendre votre liaison de ressources à des milliers de ressources. Metal 4 vous permet de gérer vos ressources graphiques et vous offre un contrôle sur les dépendances. Les Queue Barriers permettent d’exprimer vos dépendances de ressources à grande échelle. Les pools de vues de texture et les tas clairsemés avec placement vous aident à gérer la mémoire nécessaire aux ressources volumineuses.

    • 17:24 - Charger rapidement les pipelines
    • Les jeux modernes doivent créer des milliers de pipelines pour créer des visuels complexes et dynamiques. Le chargement rapide de nombreux pipelines est essentiel pour éliminer les ralentissements liés au traitement des shaders et réduire le temps de chargement de votre jeu. Pour charger rapidement les pipelines dans Metal 4, réutilisez les compilations de vos pipelines de rendu, compilez les pipelines directement sur l’appareil avec un nouveau niveau de parallélisme, et effectuez des compilations anticipées afin que le temps de chargement des pipelines soit presque nul.

    • 31:25 - Étapes suivantes
    • Les API de Metal 4 sont conçues pour vous permettre de créer la prochaine génération de jeux hautes performances. Pour commencer, vous pouvez consulter la documentation sur le site web des développeurs, essayer les exemples de projets et télécharger la nouvelle version de Xcode.

Developer Footer

  • Vidéos
  • WWDC25
  • Explorez les jeux Metal 4
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • App Store
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Fonts
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Open Source
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Sample Code
    • Tutorials
    • Downloads
    • Forums
    • Videos
    Open Menu Close Menu
    • Support Articles
    • Contact Us
    • Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Get the Apple Developer app.
    Copyright © 2025 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines