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

Plus de vidéos

  • À propos
  • Résumé
  • Transcription
  • Code
  • Allez plus loin avec les jeux Metal 4

    Plongez au cœur des dernières avancées de Metal 4. Nous vous présenterons les nouvelles fonctionnalités de ray tracing qui vous aideront à gérer vos charges de travail les plus complexes et visuellement riches sur la puce Apple. Découvrez comment MetalFX peut vous aider à optimiser les charges de travail via l'amélioration des rendus, l'interpolation des images et le débruitage des scènes. Pour tirer le meilleur parti de cette session, nous vous recommandons de commencer par regarder « Découvrez Metal 4 » et « Explorez les jeux Metal 4 ».

    Chapitres

    • 0:00 - Introduction
    • 2:13 - Améliorer le rendu avec l’upscaling
    • 7:17 - Interpoler les images
    • 13:50 - Tracer des rayons avec Metal 4
    • 19:25 - Réduire le bruit lors de l’upscaling
    • 26:08 - Étapes suivantes

    Ressources

      • Vidéo HD
      • Vidéo SD

    Vidéos connexes

    WWDC25

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

    WWDC23

    • Your guide to Metal ray tracing

    WWDC22

    • Boost performance with MetalFX Upscaling
  • Rechercher dans cette vidéo…

    Bonjour, je m’appelle Matias Koskela. Aujourd’hui, je vais vous présenter les techniques et les pratiques qui permettent d’aller plus loin avec vos jeux avancés et vos apps pro sur les plates-formes Apple.

    Avant cela, vous pouvez regarder « Discover Metal 4 » pour un aperçu de Metal 4, et « Explore Metal 4 games » pour apprendre à utiliser la dernière itération de Metal. La présentation d’aujourd’hui est la deuxième de notre série sur les jeux Metal 4. Vous pourrez découvrir comment combiner l’apprentissage automatique et les graphismes avec Metal 4 dans une autre vidéo.

    Avec un rendu de haute qualité, les jeux, comme CyberPunk 2077 ici, sont de plus en plus réalistes. Chaque pixel implique des efforts coûteux, ce qui ne facilite pas les résolutions et les fréquences d’images élevées. Metal permet un rendu d’images de haute qualité sur toutes les plates-formes Apple, de l’iPhone au Mac. Pour des techniques telles que la rastérisation ou le ray tracing, Metal offre des API faciles à utiliser.

    MetalFX Upscaling permet d’adapter vos charges de travail à des résolutions et fréquences d’images encore plus élevées.

    Et pour aller encore plus loin, vous pouvez utiliser le nouvel interpolateur d’images MetalFX.

    Les derniers jeux, comme Cyberpunk 2077, offrent un traçage de chemin en temps réel très réaliste. Ces fonctionnalités étendues de rendu en temps réel sont possibles grâce aux nouveautés de Metal 4. Citons l’amélioration du ray tracing et le nouvel upscaler débruité MetalFX pour faciliter la mise à l’échelle en nécessitant moins de rayons dans le jeu.

    L’upscaler MetalFX contribue à une résolution et des fréquences d’images supérieures. Le nouvel interpolateur d’images MetalFX permet d’améliorer encore l’expérience de jeu. Les nouvelles fonctionnalités de ray tracing de Metal 4, que vous pouvez allier au nouvel upscaler débruité MetalFX, optimisent les performances.

    L’upscaling est une technique très utilisée, qui permet d’affiner les performances dans la plupart des scénarios. MetalFX a un upscaler basé sur l’apprentissage automatique, qui fait partie des plates-formes Apple depuis 2022 et s’améliore chaque année.

    MetalFX Upscaling offre de nouveaux outils et techniques qui permettent d’améliorer la qualité et les performances de votre jeu. Veillez d’abord à appliquer correctement l’upscaling temporel au jeu. Vous devez notamment bien régler le paramètre d’entrée d’exposition. Puis, vous pouvez affiner encore les performances avec la résolution dynamique, et accroître la qualité dans certains scénarios à l’aide d’indicateurs de réactivité.

    Imaginez un pipeline de rendu typique. D’abord, la rastérisation ou le ray tracing a lieu avant que le jeu effectue des effets de post-traitement comme le flou de mouvement. Puis, l’exposition et le mappage des tons sont appliqués, l’UI s’affiche, et le joueur voit enfin l’image.

    L’upscaling MetalFX est idéal après un rendu instable et avant les effets de post-traitement. Regardez « Boost performance with MetalFX Upscaling » pour plus de détails sur l’intégration de l’upscaler. Cette année, vous disposez d’encore plus d’outils et de fonctionnalités pour améliorer les performances de jeu.

    La définition d’une valeur d’exposition correcte sur l’upscaler est essentielle pour un résultat de haute qualité.

    Une valeur incorrecte pourra entraîner un scintillement et des images fantômes.

    Dans le pipeline de rendu, la couleur d’entrée et de sortie de l’upscaler se trouve dans l’espace colorimétrique linéaire. L’upscaler utilise un paramètre d’exposition, qui, lorsqu’il est multiplié par l’entrée de couleur, donne une luminosité correspondant approximativement à l’exposition utilisée dans le mappage des tons.

    L’upscaler peut ainsi identifier les caractéristiques visibles de l’image, lorsqu’elles sont présentées au joueur. Notez que cette valeur n’est qu’un indice pour l’upscaler et ne modifie pas la luminosité de la sortie. MetalFX inclut un nouvel outil pour mieux régler la valeur d’entrée d’exposition envoyée à l’upscaler.

    Il s’agit du débogueur d’exposition. Pour l’activer, définissez la variable d’environnement MTLFX_EXPOSURE_TOOL_ENABLED. Maintenant, l’upscaler affiche un damier gris au-dessus de l’image et lui applique la valeur d’exposition inverse.

    Vous pouvez ensuite examiner l’aspect du modèle à l’écran à la fin du pipeline.

    Si la valeur d’exposition transmise à l’upscaler ne concorde pas avec le mappeur de tons, le damier sera trop sombre ou trop lumineux.

    Un autre indicateur de décalage est lorsque la luminosité du damier change pendant que votre jeu est fonctionne.

    Lorsque la valeur d’exposition est correcte, le motif de grille est gris.

    Comme la complexité des jeux peut changer grandement d’une scène à l’autre, beaucoup utilisent un rendu à résolution dynamique.

    Lorsque l’image est plus complexe, la résolution d’entrée de l’upscaler est réduite. Plus elle devient complexe, plus le jeu réduit la résolution d’entrée de manière dynamique. Au lieu de nécessiter une entrée de la même taille à chaque image, l’upscaler temporel MetalFX permet désormais les entrées à taille dynamique. Pour optimiser la qualité de mise à l’échelle, le jeu ne doit pas régler l’échelle maximale à plus de 2x si ce n’est pas nécessaire.

    Une autre nouveauté de l’upscaler temporel MetalFX est une fonctionnalité facultative permettant de lui indiquer la réactivité des pixels.

    Lorsque le jeu affiche des effets transparents, ou des particules comme les feux d’artifice, il ne les restitue pas en textures en mouvement et en profondeur.

    À des rapports d’échelle élevés et à de faibles résolutions d’entrée, vous constaterez peut-être que ces particules se fondent dans l’arrière-plan ou qu’elles présentent des images fantômes. Cela est dû au fait que dans le rendu, elles peuvent apparaître comme des détails de texture ou des reflets spéculaires.

    Pour vous permettre de contrôler le traitement des particules, l’upscaler intègre une nouvelle entrée facultative appelée masque réactif. Ce masque vous permet de marquer les zones couvertes par ces effets.

    Pour l’utiliser, définissez une valeur de masque réactif dans le nuanceur, par exemple, en fonction du type de matériau dans le tampon G. Sur le code hôte, liez la texture à l’objet de l’upscaler temporel avant de l’encoder.

    N’utilisez le masque réactif que s’il n’est pas possible d’utiliser des résolutions d’entrée supérieures. N’utilisez pas non plus de masque réactif réglé pour un autre upscaler, car il pourrait masquer des zones qui ont déjà fière allure dans la sortie de l’upscaler MetalFX. L’utilisation de l’upscaler offre d’excellentes performances avec une grande qualité. Mais vous pouvez avoir besoin de taux de rafraîchissement encore plus élevés. Cette année, MetalFX inclut l’interpolation d’images sur toutes les plates-formes Apple.

    L’interpolation d’images MetalFX est très facile à intégrer dans un jeu. Configurez un objet interpolateur, générez l’UI dans les images interpolées, puis présentez et rythmez correctement l’image.

    Pour offrir une expérience de jeu fluide, l’interpolation d’images peut vous aider à utiliser des pixels que vous avez déjà affichés.

    Voici le même pipeline, cette fois sans rendu de l’UI.

    Interpolez vos images après l’étape de mappage des tons. Pour des résolutions et des fréquences d’images encore plus élevées, vous pouvez allier l’upscaling et l’interpolation dans le même pipeline.

    Pour utiliser l’interpolateur d’images MetalFX, votre app fournit deux rendus d’images, les vecteurs de mouvement et la profondeur. Si vous avez adopté l’upscaler, ces vecteurs et cette profondeur peuvent être utilisés. La texture de mouvement a de la couleur pour les objets, car ils se sont déplacés vers la droite. Avec ces entrées, MetalFX génère une image entre ces deux rendus d’images.

    Pour configurer l’interpolateur pour des performances combinées plus élevées, fournissez l’objet upscale au descripteur d’interpolateur. Lorsque vous créez l’interpolateur, définissez son échelle de mouvement et sa convention de profondeur. Ensuite, liez les cinq textures requises à l’interpolateur.

    Une fois que les images commencent à être interpolées, pensez au rendu de l’UI.

    Dans le pipeline de rendu typique, un jeu affiche généralement l’UI à la fin de chaque image à proximité de l’endroit où l’interpolation d’image doit avoir lieu.

    Le rendu alpha de l’UI mêle les éléments à l’image, peut contenir chaque image changeant de texte et ne modifie pas les textures de mouvement ou de profondeur.

    L’activation de l’interpolation d’images offre divers moyens de peaufiner l’aspect de l’UI.

    Trois techniques courantes permettent le rendu de l’UI avec interpolation d’images. L’UI composite, l’UI hors écran et l’UI à chaque image.

    Avec l’UI composite, l’interpolateur génère l’image précédente N - 1, l’image actuelle N sans UI et la même image N avec UI. L’UI composite est la plus facile à adopter. Dans ce mode, l’interpolateur d’images peut voir le delta entre la texture avec et sans UI. Il peut ainsi essayer de supprimer l’UI et de la placer au bon endroit dans l’image interpolée. Mais défusionner un pixel déjà fusionné ne se fait pas très bien. Dès lors, vous pouvez aider l’interpolateur en utilisant l’une des autres options.

    Comme l’UI hors écran, où le rendu se fait dans une texture d’UI distincte.

    L’interpolateur l’ajoute ensuite au-dessus de l’image interpolée. L’ajouter dans l’interpolateur vous épargne une charge et un stockage en plus. Parce que l’interpolateur peut écrire l’UI dans sa sortie.

    Enfin, dans l’UI à chaque image, la gestion de l’UI est laissée au code, ce qui peut nécessiter des modifications majeures de votre côté. Mais dans ce cas, vous pouvez mettre à jour l’UI de l’image interpolée, ce qui entraîne l’expérience la plus fluide pour le joueur.

    Vous avez désormais aussi une belle UI au-dessus de l’image interpolée. Voyons maintenant comment les images interpolées et les images avec rendu natif peuvent être présentées dans le bon ordre et avec les bons intervalles.

    En général, le rendu du jeu se compose du thread Render, du GPU et du thread Present. Le thread de rendu configure le travail nécessaire pour le GPU et la présentation. Lors du rendu d’une image, l’interpolateur peut générer une image avec un horodatage entre le rendu qui vient d’avoir lieu et l’image précédente. Et votre jeu peut ensuite présenter l’image interpolée. Après un certain intervalle, le jeu peut afficher l’image la plus récemment rendue.

    Il peut être difficile de déterminer la durée de cet intervalle de manière cohérente. Mais cela est nécessaire pour un rythme de jeu approprié.

    Le nouveau HUD Metal peut vous aider à identifier quand le rythme est déséquilibré. Regardez « Level up your games » pour voir comment l’activer et en découvrir les nouvelles fonctionnalités utiles.

    Examinez le graphique Intervalle d’image où l’axe horizontal est la durée, et l’axe vertical la longueur de l’intervalle d’image.

    Si son motif est irrégulier et que les pics indiquant des intervalles de mise à jour plus longs semblent aléatoires, le rythme est déséquilibré.

    Le rythme est également déséquilibré lorsque vous avez plus de deux compartiments d’histogramme d’intervalle d’image.

    Une fois le rythme corrigé, vous devriez voir une ligne plate si vous atteignez le taux de rafraîchissement cible de l’affichage, ou un motif régulier si vous êtes en dessous, avec au maximum deux compartiments d’histogramme.

    Voici un exemple d’utilisation d’une classe presentHelper pour parvenir à ce résultat. Pendant la boucle de dessin, tout est rendu en texture basse résolution et mis à l’échelle par l’upscaler MetalFX. L’UI s’affiche après avoir indiqué à l’assistant le début du rendu. Enfin, l’appel de l’interpolateur est géré par la classe presentHelper. Consultez l’exemple de code pour en savoir plus sur l’implémentation.

    En plus du rythme, il est important d’utiliser les paramètres de temps delta et de caméra corrects. Dans le cas contraire, la zone d’occlusion pourrait avoir des artefacts.

    Avec les bons paramètres, la zone d’occlusion s’aligne parfaitement.

    En effet, l’interpolateur peut maintenant ajuster les vecteurs de mouvement pour qu’ils correspondent à la longueur du mouvement réel de la simulation.

    Une fois que toutes les entrées et le rythme sont corrects, les images interpolées devraient avoir fière allure. Et l’entrée d’interpolation devrait avoir une fréquence d’images élevée. Essayez d’avoir au moins 30 images par seconde avant l’interpolation.

    L’upscaler et l’interpolateur d’images sont des techniques qui permettent de mettre à l’échelle presque tous les styles de rendu. En revanche, le ray tracing cible généralement les scénarios de rendu haut de gamme. Metal 4 inclut de nouvelles fonctionnalités de ray tracing pour les constructions de structures d’accélération et les fonctions d’intersection.

    De plus en plus de jeux utilisent le ray tracing Metal sur les plates-formes Apple.

    Dans cette démo, l’éclairage est réaliste et le drone est visible dans les reflets sur le sol. Les techniques et les complexités du ray tracing varient d’un jeu à l’autre.

    Il nécessite plus de flexibilité dans la gestion des fonctions d’intersection et plus d’options pour la construction de structures d’accélération.

    Metal 4 offre de nouvelles fonctionnalités pour rationaliser ces deux aspects.

    Pour apprendre les bases du ray tracing Metal, comme les constructions de structures d’accélération et les fonctions d’intersection, regardez « Your guide to Metal ray tracing ».

    Imaginons un jeu utilisant le ray tracing pour afficher une scène simple comme de l’herbe autour d’un arbre.

    Rien que dans cette scène simple, il y a plusieurs types de matériaux tels que le feuillage et le tronc opaque de l’arbre.

    C’est pourquoi de nombreuses fonctions d’intersection de ray tracing sont nécessaires. Séparément pour les rayons primaires et les rayons diffus.

    Un tampon de fonction d’intersection est un tampon d’argument contenant des descripteurs des fonctions d’intersection de la scène.

    Par exemple, l’herbe et les feuilles peuvent nécessiter une fonctionnalité similaire pour tracer les rayons primaires. Les tampons de fonction d’intersection permettent au jeu d’avoir facilement plusieurs entrées pointant vers la même fonction d’intersection.

    La configuration des indices tampons de fonction d’intersection nécessite de définir l’état au niveau de l’instance, où cet exemple de scène a deux instances. Et au niveau de la géométrie, où l’herbe n’a qu’une géométrie et l’arbre en a deux. L’intersecteur doit savoir quelle fonction d’intersection utiliser pour les rayons diffus qui touchent le tronc.

    Lorsque vous créez vos structures d’accélération d’instance, spécifiez intersectionFunctionTableOffset sur chaque descripteur d’instance.

    Lors de la création de la structure d’accélération primitive, définissez aussi intersectionFunctionTableOffset sur les descripteurs de géométrie.

    Lorsque vous configurez l’intersecteur dans le nuanceur, ajoutez intersection_function_buffer à ses balises.

    Ensuite, définissez le multiplicateur de géométrie de l’intersecteur. Il s’agit du nombre de types de rayons dans le tampon de fonction d’intersection.

    Notre exemple a deux types de rayons pour chaque géométrie. La valeur correcte est donc deux. Dans ces deux types de rayons, vous devez fournir l’indice de base du type de rayon que vous tracez. Dans cet exemple, l’indice de base du traçage des rayons primaire est 0.

    Pour le traçage des ombres, l’ID de base est 1.

    Lorsque l’instance et la contribution géométrique du tronc d’arbre, le multiplicateur de géométrie et l’ID de base du type de rayon diffus sont combinés, le pointeur se retrouve dans la fonction d’intersection souhaitée.

    Finalisez le code en transmettant les arguments du tampon de la fonction d’intersection à la méthode intersect.

    En spécifiant le tampon, sa taille et son pas. Ces arguments vous laissent plus de flexibilité que les arguments habituels d’autres API. En cas de portage à partir de DirectX, vous pouvez facilement transférer les tables de liaison de nuanceur vers les tampons de fonction d’intersection Metal.

    Dans DirectX, définissez l’adresse du tampon de fonction d’intersection et le pas sur l’hôte lors de la création du descripteur pour distribuer les rayons. Dans Metal, vous les définissez dans le nuanceur. Tous les threads du groupe SIMD doivent fixer la même valeur. Sinon, le comportement est indéfini.

    La gestion de l’indice du type de rayon et du multiplicateur de géométrie est la même dans DirectX et Metal. Votre app peut les définir dans le nuanceur. Dans DirectX et Metal, définissez l’indice de décalage par instance lors de la création de la structure d’accélération d’instance. Bien que l’indice de décalage de géométrie soit généré automatiquement dans DirectX, Metal vous permet de le définir vous-même.

    Les tampons de fonction d’intersection améliorent l’expérience de portage Metal de votre jeu en ray tracing. Une fois que vous êtes opérationnel, Metal 4 vous permet aussi d’optimiser la façon dont Metal crée vos structures d’accélération.

    Metal offre déjà beaucoup de contrôle sur les constructions de structures d’accélération. Outre le comportement par défaut, vous pouvez optimiser le réajustement, activer des scènes plus grandes ou créer la structure d’accélération plus rapidement. Cette année, vous avez encore plus de flexibilité et pouvez choisir l’intersection rapide pour réduire le temps de traçage des rayons.

    Ou vous pouvez choisir de minimiser l’utilisation de la mémoire de la structure d’accélération.

    Vous pouvez définir des indicateurs d’utilisation distincts par construction de structure d’accélération.

    Ces nouveaux indicateurs rendent la partie du pipeline de rendu dédiée au ray tracing encore plus adaptée à vos besoins. Si vous l’utilisez pour des effets stochastiques, vous aurez besoin d’un débruiteur. Le débruitage peut désormais faire partie de l’upscaler MetalFX.

    Qu’il s’agisse du ray tracing hybride ou du traçage de chemin complexe, le ray tracing en temps réel est de plus en plus utilisé. Dans cet exemple d’image, le ray tracing rend tout plus ancré et améliore considérablement les réflexions. Lors du ray tracing, l’utilisation du débruitage avec moins de rayons offre un juste milieu entre qualité et performances.

    Avec la nouvelle API MetalFX, combiner l’upscaling et le débruitage revient à ajouter quelques entrées supplémentaires. Pour aider davantage l’upscaler débruité et améliorer ainsi la qualité, ajoutez des entrées supplémentaires et veillez à l’exactitude des détails.

    Avant de pouvoir combiner l’upscaler et le débruiteur, passons en revue l’approche traditionnelle.

    Les pipelines de rendu ray tracing en temps réel et interactifs typiques tracent plusieurs effets séparément, les débruitent séparément et convertissent le résultat en texture instable sans bruit. Qui est améliorée par l’upscaler temporel MetalFX. Puis fait l’objet d’un post-traitement.

    Les débruiteurs traditionnels nécessitent un réglage séparé des paramètres artistiques pour chaque scène. Voici à quoi ressemblent certains débruiteurs sans réglage des paramètres artistiques. En revanche, il n’est pas nécessaire d’ajuster ces paramètres avec l’upscaler débruité MetalFX. Qui est appliqué après le rendu principal et juste avant le post-traitement. Les techniques de MetalFX basées sur l’apprentissage automatique offrent un débruitage et un upscaling robustes, performants et de qualité dans de nombreux scénarios. Et il est plus facile à intégrer. L’intégration de l’upscaler est un bon point de départ pour passer à l’upscaler débruité. Voici les entrées de l’upscaler. Couleur, mouvement et profondeur. La nouvelle API combinée est un ensemble supérieur de l’API upscaler.

    La nouvelle API nécessite des tampons auxiliaires supplémentaires sans bruit, présentés ici à gauche. La plupart sont peut-être déjà disponibles dans votre app. Examinons de plus près chacun d’entre eux.

    La première nouvelle entrée est « normales ». Pour de meilleurs résultats, elles devraient être dans l’espace monde.

    Ensuite, l’albédo diffus est la couleur de base de l’éclat diffus du matériau.

    Ensuite, la rugosité représente la douceur ou la rugosité de la surface, qui est une valeur linéaire. La dernière entrée est l’albédo spéculaire. Il doit s’agir d’une approximation sans bruit de l’éclat spéculaire du rendu. Elle doit inclure un composant fernel. Dans le code, l’ajout de ces nouvelles entrées est simple.

    La création d’un upscaler temporel typique ne prend qu’environ 10 lignes de code. Pour activer la version débruitée, vous devez modifier le type de scaler et ajouter les types des textures supplémentaires.

    De même, lors de l’encodage du scaler, l’upscaler s’en occupe. Là aussi, la seule différence est que vous devez lier les textures d’entrée supplémentaires.

    Une fois l’utilisation de base du débruiteur configurée, vous pouvez l’améliorer avec des entrées facultatives. Et en évitant certains pièges typiques de l’intégration.

    Certaines textures d’entrée facultatives contribuent à améliorer la qualité.

    La première est la distance d’impact spéculaire, indiquant la longueur du rayon entre le point de visibilité principal du pixel et le point de rebond secondaire. Vient ensuite le masque de résistance au bruit, qui permet de marquer les zones à ne pas débruiter. Enfin, l’incrustation de transparence est utilisée en fonction du canal alpha pour mêler une couleur qui est seulement améliorée et non débruitée.

    Le problème d’intégration le plus courant est une entrée avec trop de bruit. Pour pallier ce problème, utilisez les améliorations standard de l’échantillonnage de traçage de chemin, comme l’estimation de l’événement suivant, les techniques d’échantillonnage d’importance et, dans une grande scène avec de nombreuses sources lumineuses, échantillonnez celles qui ont un réel impact sur la scène.

    Les nombres aléatoires corrélés affectent aussi la qualité de l’échantillon de ray tracing. N’utilisez pas de générateurs de nombres aléatoires trop corrélés. La corrélation spatiale ou temporelle peut provoquer des artefacts.

    Un piège potentiel lié aux données auxiliaires est l’albédo diffus des matériaux métalliques. Dans cet exemple, les pièces d’échecs sont métalliques et ont donc de la couleur albédo spéculaire. Dans ce cas, l’albédo diffus des pièces d’échecs devrait être plus foncé.

    Enfin, il existe des pièges courants liés aux normales. Pour de meilleures décisions de débruitage, l’upscaler débruité de MetalFX s’attend à ce que les normales soient dans l’espace monde. Vous devez utiliser un type de données de texture avec un bit de signe. Sinon, la qualité peut être insuffisante selon l’orientation de la caméra.

    Une fois tous ces détails réglés, vous devriez avoir de belles images améliorées et débruitées.

    Voyons ce qui se passe lorsque vous mettez toutes ces fonctionnalités dans un seul moteur de rendu.

    Mes collègues ont créé une démo, qui utilise le pipeline de rendu dont j’ai parlé plus tôt. Elle utilise les nouvelles fonctionnalités de ray tracing de Metal 4 pour optimiser la partie du rendu spécifique au ray tracing. Le débruitage et l’upscaling ont lieu en même temps avec l’upscaler débruité MetalFX. Après l’exposition et le mappage des tons, les images sont interpolées par l’interpolateur d’images MetalFX.

    Cette démo utilise des effets avancés d’éclairage par ray tracing comme l’illumination globale, les reflets, les ombres et les occlusions ambiantes, pour donner vie à une scène montrant deux robots jouant aux échecs.

    Dans la vue en haut à droite, vous pouvez voir le rendu avant tout traitement MetalFX. Et d’autres entrées MetalFX dans les autres vues.

    Nous avons adopté l’upscaler débruité et l’interpolateur d’images MetalFX. Le débruiteur a grandement simplifié le rendu en éliminant le réglage manuel de l’aspect final.

    Si vous avez déjà intégré l’upscaler MetalFX, c’est l’occasion de passer à l’interpolation d’images. Si vous débutez avec MetalFX, jetez d’abord un coup d’œil à l’upscaler. Vérifiez ensuite que les effets de ray tracing utilisent les bonnes pratiques comme les tampons de fonction d’intersection. Réduisez le budget rayons du jeu avec l’upscaler débruité.

    J’ai hâte de voir les nouvelles fonctionnalités en action dans vos jeux. Et tout ce que vous allez créer avec Metal 4. Merci de votre attention !

    • 6:46 - Reactive Mask

      // Create reactive mask setup in shader
      out.reactivity = m_material_id == eRain ? (m_material_id == eSpark ? 1.0f : 0.0f) : 0.8f;
      
      // Set reactive mask before encoding upscaler on host
      temporalUpscaler.reactiveMask = reactiveMaskTexture;
    • 8:35 - MetalFX Frame Interpolator

      // Create and configure the interpolator descriptor
      MTLFXFrameInterpolatorDescriptor* desc = [MTLFXFrameInterpolatorDescriptor new];
      desc.scaler = temporalScaler;
      // ...
      
      // Create the effect and configure your effect
      id<MTLFXFrameInterpolator> interpolator = [desc newFrameInterpolatorWithDevice:device];
      interpolator.motionVectorScaleX = mvecScaleX;
      interpolator.motionVectorScaleY = mvecScaleY;
      interpolator.depthReversed = YES;
      
      // Set input textures
      interpolator.colorTexture = colorTexture;
      interpolator.prevColorTexture = prevColorTexture;
      interpolator.depthTexture = depthTexture;
      interpolator.motionTexture = motionTexture;
      interpolator.outputTexture = outputTexture;
    • 12:45 - Interpolator present helper class

      #include <thread>
      #include <mutex>
      #include <sys/event.h>
      #include <mach/mach_time.h>
      
      
      class PresentThread
      {
          int m_timerQueue;
          std::thread m_encodingThread, m_pacingThread;
          std::mutex m_mutex;
          std::condition_variable m_scheduleCV, m_threadCV, m_pacingCV;
          float m_minDuration;
          
          uint32_t m_width, m_height;
          MTLPixelFormat m_pixelFormat;
          
          const static uint32_t kNumBuffers = 3;
          uint32_t m_bufferIndex, m_inputIndex;
          bool m_renderingUI, m_presentsPending;
          
          CAMetalLayer *m_metalLayer;
          id<MTLCommandQueue> m_presentQueue;
      
          id<MTLEvent> m_event;
          id<MTLSharedEvent> m_paceEvent, m_paceEvent2;
          uint64_t m_eventValue;
          uint32_t m_paceCount;
          
          int32_t m_numQueued, m_framesInFlight;
          
          id<MTLTexture> m_backBuffers[kNumBuffers];
          id<MTLTexture> m_interpolationOutputs[kNumBuffers];
          id<MTLTexture> m_interpolationInputs[2];
          id<MTLRenderPipelineState> m_copyPipeline;
          
          std::function<void(id<MTLRenderCommandEncoder>)> m_uiCallback = nullptr;
          
          void PresentThreadFunction();
          void PacingThreadFunction();
          
          void CopyTexture(id<MTLCommandBuffer> commandBuffer, id<MTLTexture> dest, id<MTLTexture> src, NSString *label);
      
      public:
          
          PresentThread(float minDuration, CAMetalLayer *metalLayer);
          ~PresentThread()
          {
              std::unique_lock<std::mutex> lock(m_mutex);
              m_numQueued = -1;
              m_threadCV.notify_one();
              m_encodingThread.join();
          }
          void StartFrame(id<MTLCommandBuffer> commandBuffer)
          {
              [commandBuffer encodeWaitForEvent:m_event value:m_eventValue++];
          }
      
          void StartUI(id<MTLCommandBuffer> commandBuffer)
          {
              assert(m_uiCallback == nullptr);
              if(!m_renderingUI)
              {
                  CopyTexture(commandBuffer, m_interpolationInputs[m_inputIndex], m_backBuffers[m_bufferIndex], @"Copy HUDLESS");
                  m_renderingUI = true;
              }
          }
          
          void Present(id<MTLFXFrameInterpolator> frameInterpolator, id<MTLCommandQueue> queue);
          
          id<MTLTexture> GetBackBuffer()
          {
              return m_backBuffers[m_bufferIndex];
          }
      
          void Resize(uint32_t width, uint32_t height, MTLPixelFormat pixelFormat);
          
          void DrainPendingPresents()
          {
              std::unique_lock<std::mutex> lock(m_mutex);
              while(m_presentsPending)
                  m_scheduleCV.wait(lock);
          }
          
          bool UICallbackEnabled() const
          {
              return m_uiCallback != nullptr;
          }
          
          void SetUICallback(std::function<void(id<MTLRenderCommandEncoder>)> callback)
          {
              m_uiCallback = callback;
          }
          
      };
      
      PresentThread::PresentThread(float minDuration, CAMetalLayer *metalLayer)
          : m_encodingThread(&PresentThread::PresentThreadFunction, this)
          , m_pacingThread(&PresentThread::PacingThreadFunction, this)
          , m_minDuration(minDuration)
          , m_numQueued(0)
          , m_metalLayer(metalLayer)
          , m_inputIndex(0u)
          , m_bufferIndex(0u)
          , m_renderingUI(false)
          , m_presentsPending(false)
          , m_framesInFlight(0)
          , m_paceCount(0)
          , m_eventValue(0)
      {
          id<MTLDevice> device = metalLayer.device;
          m_presentQueue = [device newCommandQueue];
          m_presentQueue.label = @"presentQ";
          m_timerQueue = kqueue();
          
          metalLayer.maximumDrawableCount = 3;
          
          Resize(metalLayer.drawableSize.width, metalLayer.drawableSize.height, metalLayer.pixelFormat);
          
          m_event = [device newEvent];
          m_paceEvent = [device newSharedEvent];
      	m_paceEvent2 = [device newSharedEvent];
      }
      
      
      void PresentThread::Present(id<MTLFXFrameInterpolator> frameInterpolator, id<MTLCommandQueue> queue)
      {
          id<MTLCommandBuffer> commandBuffer = [queue commandBuffer];
          
          if(m_renderingUI)
          {
              frameInterpolator.colorTexture = m_interpolationInputs[m_inputIndex];
              frameInterpolator.prevColorTexture = m_interpolationInputs[m_inputIndex^1];
              frameInterpolator.uiTexture = m_backBuffers[m_bufferIndex];
          }
          else
          {
              frameInterpolator.colorTexture = m_backBuffers[m_bufferIndex];
              frameInterpolator.prevColorTexture = m_backBuffers[(m_bufferIndex + kNumBuffers - 1) % kNumBuffers];
              frameInterpolator.uiTexture = nullptr;
          }
          
          frameInterpolator.outputTexture = m_interpolationOutputs[m_bufferIndex];
      
          [frameInterpolator encodeToCommandBuffer:commandBuffer];
          [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
              std::unique_lock<std::mutex> lock(m_mutex);
              m_framesInFlight--;
              m_scheduleCV.notify_one();
              m_paceCount++;
              m_pacingCV.notify_one();
          }];
          [commandBuffer encodeSignalEvent:m_event value:m_eventValue++];
          [commandBuffer commit];
      
          std::unique_lock<std::mutex> lock(m_mutex);
          m_framesInFlight++;
          m_numQueued++;
          m_presentsPending = true;
          m_threadCV.notify_one();
          while((m_framesInFlight >= 2) || (m_numQueued >= 2))
              m_scheduleCV.wait(lock);
      
          m_bufferIndex = (m_bufferIndex + 1) % kNumBuffers;
          m_inputIndex = m_inputIndex^1u;
          m_renderingUI = false;
      }
      
      void PresentThread::CopyTexture(id<MTLCommandBuffer> commandBuffer, id<MTLTexture> dest, id<MTLTexture> src, NSString *label)
      {
          MTLRenderPassDescriptor *desc = [MTLRenderPassDescriptor new];
          desc.colorAttachments[0].texture = dest;
          desc.colorAttachments[0].loadAction = MTLLoadActionDontCare;
          desc.colorAttachments[0].storeAction = MTLStoreActionStore;
          id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:desc];
          [renderEncoder setFragmentTexture:src atIndex:0];
          [renderEncoder setRenderPipelineState:m_copyPipeline];
          [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
          if(m_uiCallback)
              m_uiCallback(renderEncoder);
          renderEncoder.label = label;
          [renderEncoder endEncoding];
      }
      
      
      void PresentThread::PacingThreadFunction()
      {
          NSThread *thread = [NSThread currentThread];
          [thread setName:@"PacingThread"];
          [thread setQualityOfService:NSQualityOfServiceUserInteractive];
          [thread setThreadPriority:1.f];
          
          mach_timebase_info_data_t info;
          mach_timebase_info(&info);
          
          // maximum delta (0.1ms) in machtime units
          const uint64_t maxDeltaInNanoSecs = 100000000;
          const uint64_t maxDelta = maxDeltaInNanoSecs * info.denom / info.numer;
          
          uint64_t time = mach_absolute_time();
          
          uint64_t paceEventValue = 0;
          
          for(;;)
          {
              std::unique_lock<std::mutex> lock(m_mutex);
              while(m_paceCount == 0)
                  m_pacingCV.wait(lock);
              m_paceCount--;
              lock.unlock();
              
              // we get signal...
              const uint64_t prevTime = time;
              time = mach_absolute_time();
      		m_paceEvent.signaledValue = ++paceEventValue;
      
              const uint64_t delta = std::min(time - prevTime, maxDelta);
              const uint64_t timeStamp = time + ((delta*31)>>6);
              
              struct kevent64_s timerEvent, eventOut;
              struct timespec timeout;
              timeout.tv_nsec = maxDeltaInNanoSecs;
              timeout.tv_sec = 0;
              EV_SET64(&timerEvent,
                       0,
                       EVFILT_TIMER,
                       EV_ADD | EV_ONESHOT | EV_ENABLE,
                       NOTE_CRITICAL | NOTE_LEEWAY | NOTE_MACHTIME | NOTE_ABSOLUTE,
                       timeStamp,
                       0,
                       0,
                       0);
              
              kevent64(m_timerQueue, &timerEvent, 1, &eventOut, 1, 0, &timeout);
              
              // main screen turn on...
              m_paceEvent2.signaledValue = ++paceEventValue;
          }
      }
      
      
      void PresentThread::PresentThreadFunction()
      {
          NSThread *thread = [NSThread currentThread];
          [thread setName:@"PresentThread"];
          [thread setQualityOfService:NSQualityOfServiceUserInteractive];
          [thread setThreadPriority:1.f];
          
      
          uint64_t eventValue = 0;
          uint32_t bufferIndex = 0;
      
          uint64_t paceEventValue = 0;
      
          for(;;)
          {
              std::unique_lock<std::mutex> lock(m_mutex);
              
              if(m_numQueued == 0)
              {
                  m_presentsPending = false;
                  m_scheduleCV.notify_one();
              }
              
              while(m_numQueued == 0)
                  m_threadCV.wait(lock);
              
              if(m_numQueued < 0)
                  break;
              lock.unlock();
      
              @autoreleasepool
              {
                  id<CAMetalDrawable> drawable = [m_metalLayer nextDrawable];
      
      			lock.lock();
      			m_numQueued--;
      			m_scheduleCV.notify_one();
      			lock.unlock();
      
                  id<MTLCommandBuffer> commandBuffer = [m_presentQueue commandBuffer];
                  [commandBuffer encodeWaitForEvent:m_event value:++eventValue];
                  CopyTexture(commandBuffer, drawable.texture, m_interpolationOutputs[bufferIndex], @"Copy Interpolated");
                  [commandBuffer encodeSignalEvent:m_event value:++eventValue];
      			[commandBuffer encodeWaitForEvent:m_paceEvent value:++paceEventValue];
      
                  if(m_minDuration > 0.f)
                      [commandBuffer presentDrawable:drawable afterMinimumDuration:m_minDuration];
                  else
                      [commandBuffer presentDrawable:drawable];
                  [commandBuffer commit];
              }
              
              @autoreleasepool
              {
                  id<MTLCommandBuffer> commandBuffer = [m_presentQueue commandBuffer];
                  id<CAMetalDrawable> drawable = [m_metalLayer nextDrawable];
                  CopyTexture(commandBuffer, drawable.texture, m_backBuffers[bufferIndex], @"Copy Rendered");
      			[commandBuffer encodeWaitForEvent:m_paceEvent2 value:++paceEventValue];
                  if(m_minDuration > 0.f)
                      [commandBuffer presentDrawable:drawable afterMinimumDuration:m_minDuration];
                  else
                      [commandBuffer presentDrawable:drawable];
                  [commandBuffer commit];
              }
              
              bufferIndex = (bufferIndex + 1) % kNumBuffers;
          }
      }
      
      void PresentThread::Resize(uint32_t width, uint32_t height, MTLPixelFormat pixelFormat)
      {
          if((m_width != width) || (m_height != height) || (m_pixelFormat != pixelFormat))
          {
              id<MTLDevice> device = m_metalLayer.device;
      
              if(m_pixelFormat != pixelFormat)
              {
                  id<MTLLibrary> lib = [device newDefaultLibrary];
                  MTLRenderPipelineDescriptor *pipelineDesc = [MTLRenderPipelineDescriptor new];
                  pipelineDesc.vertexFunction = [lib newFunctionWithName:@"FSQ_VS_V4T2"];
                  pipelineDesc.fragmentFunction = [lib newFunctionWithName:@"FSQ_simpleCopy"];
                  pipelineDesc.colorAttachments[0].pixelFormat = pixelFormat;
                  m_copyPipeline = [device newRenderPipelineStateWithDescriptor:pipelineDesc error:nil];
                  m_pixelFormat = pixelFormat;
              }
              
              DrainPendingPresents();
              
              m_width = width;
      		m_height = height;
              
              MTLTextureDescriptor *texDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixelFormat width:width height:height mipmapped:NO];
      		texDesc.storageMode = MTLStorageModePrivate;
              for(uint32_t i = 0; i < kNumBuffers; i++)
              {
                  texDesc.usage = MTLTextureUsageShaderRead|MTLTextureUsageShaderWrite|MTLTextureUsageRenderTarget;
                  m_backBuffers[i] = [device newTextureWithDescriptor:texDesc];
                  texDesc.usage = MTLTextureUsageShaderRead|MTLTextureUsageRenderTarget;
                  m_interpolationOutputs[i] = [device newTextureWithDescriptor:texDesc];
              }
              texDesc.usage = MTLTextureUsageShaderRead|MTLTextureUsageRenderTarget;
              m_interpolationInputs[0] = [device newTextureWithDescriptor:texDesc];
              m_interpolationInputs[1] = [device newTextureWithDescriptor:texDesc];
      
          }
      }
    • 13:00 - Set intersection function table offset

      // Set intersection function table offset on host-side geometry descriptors
      NSMutableArray<MTLAccelerationStructureGeometryDescriptor *> *geomDescs ...;
      for (auto g = 0; g < geomList.size(); ++g)
      {
          MTLAccelerationStructureGeometryDescriptor *descriptor = ...;
          descriptor.intersectionFunctionTableOffset = g;
          ...
          [geomDescs addObject:descriptor];
      }
    • 13:01 - Set up the intersector

      // Set up the intersector
      metal::raytracing::intersector<intersection_function_buffer, instancing, triangle> trace;
      trace.set_geometry_multiplier(2); // Number of ray types, defaults to 1
      trace.set_base_id(1);             // Set ray type index, defaults to 0
    • 13:02 - Ray trace intersection function buffers

      // Ray trace intersection function buffers
      
      // Set up intersection function buffer arguments
      intersection_function_buffer_arguments ifb_arguments;
      ifb_arguments.intersection_function_buffer = raytracingResources.ifbBuffer;
      ifb_arguments.intersection_function_buffer_size = raytracingResources.ifbBufferSize;
      ifb_arguments.intersection_function_stride = raytracingResources.ifbBufferStride;
      
      // Set up the ray and finish intersecting
      metal::raytracing::ray r = { origin, direction };
      auto result = trace.intersect(r, ads, ifb_arguments);
    • 13:02 - Change of temporal scaler setup to denoised temporal scaler setup

      // Change of temporal scaler setup to denoised temporal scaler setup
      
      MTLFXTemporalScalerDescriptor* desc = [MTLFXTemporalScalerDescriptor new];
      desc.colorTextureFormat = MTLPixelFormatBGRA8Unorm_sRGB;
      desc.outputTextureFormat = MTLPixelFormatBGRA8Unorm_sRGB;
      desc.depthTextureFormat = DepthStencilFormat;
      desc.motionTextureFormat = MotionVectorFormat;
      
      desc.diffuseAlbedoTextureFormat = DiffuseAlbedoFormat;
      desc.specularAlbedoTextureFormat = SpecularAlbedoFormat;
      desc.normalTextureFormat = NormalVectorFormat;
      desc.roughnessTextureFormat = RoughnessFormat;
      
      desc.inputWidth = _mainViewWidth;
      desc.inputHeight = _mainViewHeight;
      desc.outputWidth = _screenWidth;
      desc.outputHeight = _screenHeight;
      temporalScaler = [desc newTemporalDenoisedScalerWithDevice:_device];
    • 13:04 - Change temporal scaler encode to denoiser temporal scaler encode

      // Change temporal scaler encode to denoiser temporal scaler encode
      
      temporalScaler.colorTexture = _mainView;
      temporalScaler.motionTexture = _motionTexture;
      
      temporalScaler.diffuseAlbedoTexture = _diffuseAlbedoTexture;
      temporalScaler.specularAlbedoTexture = _specularAlbedoTexture;
      temporalScaler.normalTexture = _normalTexture;
      temporalScaler.roughnessTexture = _roughnessTexture;
      
      temporalScaler.depthTexture = _depthTexture;
      temporalScaler.jitterOffsetX = _pixelJitter.x;
      temporalScaler.jitterOffsetY = -_pixelJitter.y;
      temporalScaler.outputTexture = _upscaledColorTarget;
      temporalScaler.motionVectorScaleX = (float)_motionTexture.width;
      temporalScaler.motionVectorScaleY = (float)_motionTexture.height;
      [temporalScaler encodeToCommandBuffer:commandBuffer];
    • 16:04 - Creating instance descriptors for instance acceleration structure

      // Creating instance descriptors for instance acceleration structure
      MTLAccelerationStructureInstanceDescriptor *grassInstanceDesc, *treeInstanceDesc = . . .;
      grassInstanceDesc.intersectionFunctionTableOffset = 0;
      treeInstanceDesc.intersectionFunctionTableOffset  = 1;
      
      // Create buffer for instance descriptors of as many trees/grass instances the scene holds
      id <MTLBuffer> instanceDescs = . . .;
      for (auto i = 0; i < scene.instances.size(); ++i)
      . . .
    • 0:00 - Introduction
    • Découvrez la série Metal 4 pour le gaming, y compris les techniques avancées et les bonnes pratiques pour développer des jeux avancés et des apps professionnelles sur les plateformes Apple. Les API de Metal 4 vous permettent d’adapter vos charges de travail à des résolutions et à des fréquences d’images plus élevées sur les appareils Apple.

    • 2:13 - Améliorer le rendu avec l’upscaling
    • MetalFX dispose d’un upscaler basé sur l’apprentissage automatique, qui peut vous aider à obtenir une résolution plus élevée et des fréquences d’images plus rapides. Nouveauté de cette année : l’upscaler temporel MetalFX prend en charge les entrées dimensionnées dynamiquement, ce qui vous permet de réduire dynamiquement la résolution d’entrée pour les images particulièrement complexes. Vous pouvez éventuellement utiliser les indices de réactivité pour fournir à l’upscaler la réactivité des pixels afin d’obtenir des résultats plus nets pour les zones dotées de particules ou d’effets transparents. Et vous pouvez vérifier que la valeur d’exposition que vous transmettez à l’upscaler est correcte avec le nouvel outil de débogage d’exposition.

    • 7:17 - Interpoler les images
    • Nouveauté de cette année : MetalFX Frame Interpolation génère une image supplémentaire entre vos deux images rendues. Il existe plusieurs techniques de rendu de l’interface utilisateur avec Frame Interpolation. Vous devez également prendre en compte certains éléments lorsque vous rythmez et présentez à la fois des images interpolées et rendues nativement.

    • 13:50 - Tracer des rayons avec Metal 4
    • Metal 4 propose de nouvelles fonctionnalités de ray tracing pour les constructions de structures d’accélération et les fonctions d’intersection. En cas de portage à partir d’autres API, vous pouvez facilement transférer les tables de liaison de nuanceur vers les tampons de fonction d’intersection Metal. Une fois que vous êtes opérationnel, vous pouvez aussi optimiser la façon dont Metal 4 crée vos structures d’accélération.

    • 19:25 - Réduire le bruit lors de l’upscaling
    • Lors du ray tracing de scènes, l’utilisation du débruitage avec moins de rayons offre un juste équilibre entre qualité et performances. La nouvelle API MetalFX améliore les pipelines de rendu de ray tracing en temps réel en intégrant directement le débruitage dans le processus d’upscaling. Cela simplifie l’approche traditionnelle, qui implique des étapes distinctes de traçage, de débruitage et de composition. En spécifiant des tampons auxiliaires sans bruit tels que les normales, l’albédo diffus, la rugosité et l’albédo spéculaire, vous pouvez obtenir des résultats robustes, performants et de haute qualité sans réglage de paramètres distinct pour chaque scène.

    • 26:08 - Étapes suivantes
    • Si vous avez déjà intégré l’upscaler MetalFX, c’est l’occasion de passer à l’interpolation d’images. Si vous débutez avec MetalFX, jetez d’abord un coup d’œil à l’upscaler. Assurez-vous ensuite que vos effets de ray tracing utilisent les tampons de fonction d’intersection et l’upscaler débruité.

Developer Footer

  • Vidéos
  • WWDC25
  • Allez plus loin avec 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