Skip to main content

Introduction

Cet article fournit une vue d’ensemble des API DirectStorage pour les consoles XBOX Series X|S uniquement. Consultez DirectStorage sur ordinateur de bureau pour plus d’informations sur DirectStorage sur ordinateur de bureau. Les derniers périphériques de stockage NVMe connectés via un bus PCIe peuvent atteindre des niveaux très élevés de débit et d’IOPS (requêtes d’E/S par seconde). En raison de la surcharge des API Win32, même si la bande passante de stockage disponible peut être utilisée, en tirer parti peut entraîner une utilisation du processeur inacceptablement élevée. C’est particulièrement vrai lorsque la charge de travail se compose d’un grand nombre de petites requêtes. Les API DirectStorage sont conçues pour éliminer la majeure partie de la surcharge du système d’exploitation en interagissant étroitement avec le matériel NVMe sous-jacent. Cela permet d’obtenir une bande passante plus élevée avec une utilisation moindre du processeur. L’objectif est de permettre le traitement de jusqu’à 50 000 requêtes par seconde en utilisant au maximum 10 % d’un seul cœur de processeur.

Problèmes existants

Le contenu des jeux est de plus en plus volumineux, à mesure que le besoin de ressources en plus haute résolution augmente à chaque génération de consoles. Le matériel et les logiciels XBOX One existants présentent plusieurs limitations qui entravent la capacité des développeurs à transférer les données du disque dur vers la mémoire pour ce contenu de nouvelle génération.
  • Utilisation élevée du processeur
    • Les API Win32 existantes peuvent nécessiter un cœur de processeur entier en surcharge.
    • Cela dépend du nombre de requêtes du titre.
  • Bande passante maximale insuffisante du disque
  • Impossibilité de hiérarchiser les requêtes disque
    • Sans la possibilité de hiérarchiser les requêtes du titre, il peut être difficile de créer un système de streaming réactif.
  • Impossibilité d’annuler les requêtes disque
    • Sans la possibilité d’annuler des requêtes, il peut être difficile de créer un système de lecture spéculative.
  • Absence de décompression accélérée par le matériel
    • Sans décompression accélérée par le matériel, la décompression logicielle peut consommer beaucoup de ressources processeur.
L’ensemble d’API DirectStorage répond directement à chacun de ces problèmes. L’effet global est une augmentation considérable des performances du système de fichiers XBOX.

Utilisation du processeur

Le principal objectif de conception de DirectStorage est de permettre à un titre de soutenir 50 000 IOPS en n’utilisant qu’entre 5 % et 10 % d’un seul cœur de processeur. Cela permet au titre d’atteindre la bande passante maximale du sous-système de stockage NVMe, tout en permettant d’utiliser le processeur pour d’autres besoins du titre. DirectStorage ajoute également la prise en charge de la décompression matérielle. Chaque requête de lecture peut être acheminée directement du lecteur NVMe vers le bloc de décompression matérielle intégré. Le titre n’a ainsi plus besoin de consacrer des ressources processeur à la décompression.

Modèle de pipeline en file d’attente

DirectStorage utilise une méthode par lots, dans laquelle plusieurs requêtes sont ajoutées à une file d’attente. Plus tard, la file d’attente est vidée vers l’étape suivante du pipeline. Cela réduit immédiatement le coût processeur global d’une transition entre les étapes du pipeline. Avec l’ensemble d’API Win32 existant, il y a une transition pour chaque requête. Les files d’attente DirectStorage utilisent des algorithmes sans verrou pour réduire la contention. Le titre contrôle le moment où chaque file d’attente est vidée. Dans de nombreux cas avec les API Win32, les données du disque doivent être copiées dans une autre mémoire tampon. Dans certains cas, les données doivent être copiées plusieurs fois. DirectStorage élimine ce problème en mappant la mémoire tampon de destination fournie par le titre directement dans chaque couche du pipeline. Le matériel écrit directement dans la mémoire tampon fournie par le titre. Ces modifications contribuent à réduire considérablement la surcharge du processeur.

Décompression

La capacité de décompression des données par le matériel a été augmentée. Il peut désormais gérer une plus grande variété de formats, à une vitesse supérieure à celle à laquelle le sous-système NVMe peut fournir les données. De plus, DirectStorage prend en charge la décompression sur place, ce qui évite d’avoir à gérer des mémoires tampons distinctes pour les données compressées et décompressées. Le matériel prend en charge BCPACK et DEFLATE, et offre la possibilité de swizzler le contenu final. Ces formats ne s’excluent pas mutuellement. Il est possible d’appliquer les trois aux données. Le titre peut ainsi choisir la méthode qui lui offre le meilleur taux de compression et les meilleures performances. Différentes ressources peuvent utiliser différents paramètres de compression et de swizzle.

Profondeur de file d’attente

La recommandation précédente était de ne maintenir que 12 à 16 requêtes asynchrones en cours à la fois sur un disque rotatif. Aller au-delà n’apportait aucun gain de performances, et rester en deçà nuisait considérablement aux performances. Les titres devaient donc effectuer un travail supplémentaire pour équilibrer leurs requêtes de lecture en attente afin de rester dans la cible recommandée. Avec l’objectif de DirectStorage de permettre à un titre d’atteindre 50 000 IOPS, notre recommandation a changé. Le titre n’a plus besoin d’essayer d’équilibrer le travail en attente et la profondeur de file d’attente. Le titre doit soumettre toutes ses requêtes en attente. Il n’y a aucun avantage à retenir certaines requêtes. Dans de nombreux cas, retenir des requêtes peut nuire aux performances, car le matériel est bloqué en attente de nouvelles requêtes. Le système d’exploitation doit toujours fractionner les grandes requêtes de lecture en plusieurs requêtes plus petites dans certains cas (par exemple, pour gérer la fragmentation du disque). Toutefois, cela a été pris en compte dans l’architecture de DirectStorage. L’objectif de conception de 50 000 IOPS est basé sur le nombre d’opérations d’E/S du titre et non sur les requêtes finales envoyées au matériel.

Notification

Dans l’architecture Win32, une surcharge importante est consacrée aux notifications d’achèvement de lecture. Le titre peut interroger une structure OVERLAPPED, attendre un handle d’événement associé ou effectuer une lecture bloquante synchrone. Globalement, cela augmente les besoins en ressources de chaque requête de lecture. DirectStorage conserve les deux concepts asynchrones de notification tout en ajoutant une troisième méthode. Les lectures bloquantes synchrones ne sont pas prises en charge dans DirectStorage ; un titre peut implémenter son propre système. Toutefois, cela n’est pas recommandé. La première méthode asynchrone est implémentée au moyen d’un bloc d’état défini lorsque les requêtes associées sont terminées. Le titre peut interroger le bloc si nécessaire pour déterminer quand les lectures sont terminées. Cela est similaire à la méthode Win32 consistant à interroger une structure OVERLAPPED pour détecter l’achèvement. La deuxième méthode asynchrone utilise un objet Event Windows pour signaler l’achèvement. Cela est similaire à l’utilisation d’une structure OVERLAPPED avec un objet Event correspondant. Le titre peut utiliser la méthode WaitForSingleObject pour suspendre le thread appelant jusqu’à ce que les opérations de lecture soient terminées. La troisième méthode asynchrone est implémentée à l’aide d’un ID3D12Fence. Le titre peut se suspendre en attendant la clôture ou interroger la clôture si nécessaire. Il y a également l’avantage que le GPU peut utiliser la clôture pour être directement notifié des requêtes terminées. Le système de notification de DirectStorage n’est pas lié à une seule requête de lecture. Il s’agit d’une entrée placée dans la file d’attente, qui est signalée lorsque toutes les requêtes de lecture précédentes sont terminées. Le titre contrôle ainsi la granularité dont il a besoin pour les notifications. Les notifications sont toujours signalées dans l’ordre de la file d’attente. La file d’attente peut être considérée comme une file FIFO (premier entré, premier sorti). Le titre n’a besoin d’interroger que la dernière notification pertinente. Toutes les requêtes précédemment mises en file d’attente sont assurément terminées.

Décompression de mémoire à mémoire

DirectStorage fournit un type de file d’attente permettant d’appeler le matériel de décompression avec la mémoire comme source de décompression au lieu d’un fichier sur disque. Cela permet d’utiliser le matériel de décompression si la ressource compressée ne provient pas d’un fichier ou si elle a été précédemment chargée et conservée en mémoire comme cache. Une file d’attente à source mémoire n’accepte que des requêtes à source mémoire, et une file d’attente à source fichier n’accepte que des requêtes à source fichier. Si aucune option de décompression n’est spécifiée dans une requête à source mémoire, le matériel de décompression peut également servir de moteur de copie DMA. Bien que DirectStorage garantisse que les notifications d’achèvement sont dans l’ordre, DirectStorage ne fournit aucune garantie quant au moment où le traitement des requêtes commence. Par conséquent, il ne doit y avoir aucune dépendance de données entre les requêtes en attente. Autrement dit, la destination de la requête A ne peut pas servir de source à la requête B, sauf si la requête B est mise en file d’attente après l’achèvement de la requête A. Les files d’attente à source mémoire doivent être créées avec une priorité en temps réel. En outre, les requêtes en temps réel à source mémoire sont toujours traitées par le matériel de décompression avant les requêtes à source disque qui nécessitent une décompression. Si les files d’attente à source disque ne contiennent aucune requête de décompression, les deux types de files d’attente sont traités entièrement en parallèle sans affecter l’autre type.

Priorité

DirectStorage permet d’attribuer un niveau de priorité à chaque file d’attente. Chaque entrée de la file d’attente hérite de la priorité de la file. Quatre niveaux de priorité différents sont fournis : temps réel, haute, normale et basse. Les requêtes sont traitées selon un tourniquet pondéré. Par exemple, traiter X requêtes de priorité haute avant de traiter une requête de priorité normale. Traiter Y requêtes de priorité normale avant de traiter une requête de priorité basse. La pondération de priorité est calculée en fonction de la taille de chaque requête. La pondération par défaut entre chaque priorité est d’environ 10x. Cela signifie que pour chaque 1 Ko de requête de priorité basse, 10 Ko de requête de priorité moyenne et 100 Ko de requête de priorité haute auront été traités. Les requêtes de lecture Win32 existantes sont acheminées via le même système de priorité. Toutes les requêtes Win32 sont considérées comme de priorité normale. Les files d’attente à source mémoire doivent être créées avec une priorité en temps réel.

Annulation

Chaque requête de lecture DirectStorage est associée à un masque de 64 bits fourni par le titre. Cela permet de prendre en charge l’annulation des requêtes de lecture en attente. Le titre peut annuler les requêtes qui correspondent à un ensemble spécifique d’indicateurs dans le masque. Même avec la prise en charge de l’annulation, il est toujours possible qu’une requête de lecture soit traitée par le matériel. La demande d’annulation du titre est une tentative au mieux. Si la requête est déjà en cours de traitement actif par le matériel, elle ne peut pas être annulée. Comme la demande d’annulation est effectuée au mieux, le titre doit attendre d’être notifié que le traitement de la requête de lecture est terminé. Le titre ne peut libérer aucune des ressources nécessaires avant la réception d’une notification ultérieure dans la file d’attente. Toutefois, pendant ce temps, de nouvelles requêtes correspondant aux indicateurs utilisés dans une demande d’annulation précédente peuvent être mises en file d’attente et ne seront pas annulées. Lorsqu’une requête annulée se termine, elle est considérée comme une réussite, même si elle a été annulée et n’a pas produit le résultat complet. Autrement dit, si une annulation est tentée sur une requête, le titre ne peut plus consommer le résultat de la requête potentiellement annulée lors de son achèvement.

Garantie

Les consoles XBOX One et XBOX One S offraient une garantie minimale de 40 Mo/s. La console XBOX One X a augmenté la garantie minimale à 60 Mo/s. Ces chiffres sont bien inférieurs aux limites réelles du matériel, qui se situent autour de 130 Mo/s. Cela était entièrement dû à la surcharge causée par le système d’exploitation. DirectStorage élimine la majeure partie de la surcharge causée par le système d’exploitation. Cela permet une garantie minimale plus proche des limites du matériel. La nouvelle garantie minimale de performances est de 2,0 Go/s sur une fenêtre de 250 ms pour les données brutes. L’utilisation de la décompression sur le contenu augmente encore la bande passante finale. Les futures consoles XBOX prendront en charge l’ajout de disques dynamiques installables par l’utilisateur également basés sur NVMe. La même garantie minimale de performances que celle fournie pour le disque interne s’applique également au disque installable par l’utilisateur.

Vue d’ensemble de l’API

Les interfaces DirectStorage suivent le même modèle que les interfaces Direct3D. Le titre obtient d’abord une fabrique singleton. La fabrique sert à créer des files d’attente de requêtes et à ouvrir des fichiers ; chacun de ces objets est mappé directement au matériel. Les requêtes individuelles sont ensuite mises en file d’attente pour être soumises au matériel.

IDStorageFactoryX

IDStorageFactoryX est l’interface principale permettant de créer des files d’attente, d’ouvrir des fichiers et de soumettre les requêtes en attente. L’objet IDStorageFactoryX possède les méthodes suivantes.
  • OpenFile
    • Crée un objet IDStorageFileX , qui représente un fichier.
  • CreateQueue
    • Crée un objet IDStorageQueueX. Sert à créer des requêtes de lecture.
  • CreateStatusArray
    • Crée un objet IDStorageStatusArray, qui gère les indicateurs d’état d’achèvement.
  • SetCPUAffinity
    • Limite le travail de DirectStorage effectué hors du thread appelant à un ensemble de cœurs de processeur défini par le titre.
    • REMARQUE DirectStorage tente d’effectuer la majeure partie du travail dans le thread appelant. Le travail hors du thread appelant n’a lieu que lorsqu’il ne peut pas être effectué dans le thread appelant. Voici quelques exemples.
      • Le pipeline de ressources sous-jacent est plein pendant IDStorageQueueX::Submit, et toutes les requêtes de la file d’attente ne peuvent pas être transmises. Les requêtes restantes seront traitées ultérieurement, lorsque les ressources se libéreront, et ce traitement est effectué dans un thread de travail DirectStorage.
      • Traitement de l’achèvement des requêtes dans ID3DFence ou IDStorageStatusArray.
  • SetDebugFlags
    • Détermine si DirectStorage effectue des validations supplémentaires lors de la mise en file d’attente des requêtes afin de faciliter le débogage.
  • SetStagingBufferSize
    • Définit la taille de la mémoire tampon intermédiaire utilisée pour stocker temporairement le contenu chargé depuis le périphérique de stockage avant son déchiffrement/sa décompression. Si seules des files d’attente à source mémoire sont utilisées, la mémoire tampon intermédiaire peut avoir une taille de 0.

IDStorageFactoryX1

L’interface IDStorageFactoryX1 étend l’interface IDStorageFactoryX avec une méthode GetStats.
  • GetStats
    • Obtient les statistiques de DirectStorage. Cette fonction peut servir à intégrer DirectStorage aux pipelines de diagnostic et de télémétrie existants. Elle effectue un traitement minimal et peut donc être appelée fréquemment. Les statistiques n’incluent pas les opérations d’E/S de fichiers Win32.

IDStorageFactoryX2

L’interface IDStorageFactoryX2 étend l’interface `IDStorageFactoryX1 avec une méthode CreateQueue1.
  • CreateQueue1
    • Crée un objet IDStorageQueueX2. Utilise une nouvelle structure pour permettre des options supplémentaires lors de la création de la file d’attente, notamment la possibilité de remplacer la fonctionnalité de soumission automatique d’une file d’attente.

IDStorageFileX

Tous les fichiers doivent d’abord être ouverts par DirectStorage via l’objet IDStorageFactoryX. Cela équivaut à utiliser CreateFile dans les interfaces d’API Win32. Les fichiers sont ouverts avec l’autorisation FILE_SHARED_READ. Si nécessaire, les titres peuvent ouvrir simultanément le fichier à l’aide des API Win32, à condition de respecter les autorisations appropriées. Pendant le développement, les déploiements libres et empaquetés sont pris en charge. Les fichiers sont fermés soit en appelant explicitement la fonction Close sur l’objet fichier, soit lorsque la dernière référence à l’objet IDStorageFileX correspondant est libérée. Toutefois, toutes les opérations d’E/S en attente doivent être terminées avant que le fichier puisse être fermé. Cela signifie que les deux méthodes de fermeture d’un fichier bloquent jusqu’à ce que toutes les opérations d’E/S en attente sur ce fichier soient terminées. Le jeu peut obtenir un handle Win32 vers un fichier représenté par un objet IDStorageFileX en appelant la fonction GetHandle. Le handle est ouvert avec les autorisations GENERIC_READ et le mode de partage FILE_SHARE_READ. Il peut servir à interroger la taille du fichier, entre autres. Le handle doit être fermé avec CloseHandle() lorsqu’il n’est plus nécessaire.

IDStorageQueueX

Les requêtes de lecture sont soumises au NVMe via un objet IDStorageQueueX. Toutefois, les requêtes ne sont pas soumises au périphérique tant que le titre n’a pas appelé Submit sur la file d’attente, ou que l’une des méthodes Enqueue n’a pas rempli plus de la moitié de la capacité de la file d’attente depuis la dernière soumission, déclenchant ainsi une soumission automatique. Les soumissions sont traitées comme une seule transition vers l’étape suivante du pipeline. Le titre peut ainsi contrôler le moment où le coût processeur de la transition entre le titre et le noyau est engagé. Les objets IDStorageQueueX possèdent quatre propriétés.
  • SourceType
    • Indique si la file d’attente peut recevoir des requêtes à source fichier ou des requêtes à source mémoire.
  • Priority
    • Priorité de toutes les requêtes soumises à la file d’attente : temps réel, haute, normale ou basse.
    • Les files d’attente à source mémoire doivent être créées avec une priorité en temps réel.
    • Les requêtes sont traitées selon un tourniquet pondéré basé sur la priorité.
    • Les requêtes Win32 sont traitées au niveau de priorité normal.
  • Capacity
    • Nombre maximal de requêtes en attente que la file d’attente peut contenir.
    • Toute tentative de mise en file d’attente d’une requête lorsque la file d’attente est pleine bloque jusqu’à ce que des entrées soient terminées par le matériel.
    • La quantité de mémoire nécessaire pour une file d’attente correspond approximativement à la capacité de la file d’attente multipliée par la taille d’une DSTORAGE_REQUEST.
  • Name
    • Cette propriété sert uniquement à faciliter le débogage. Le nom n’est utilisé par aucune partie du code DirectStorage, mais il est visible dans les outils de développement, comme PIX (rubrique sous NDA).
Le matériel traite les requêtes de manière asynchrone pour obtenir le débit le plus élevé. Toutefois, contrairement à Win32, le titre est notifié de l’achèvement dans l’ordre FIFO. À la notification de l’achèvement, il est garanti que toutes les requêtes précédentes adressées à la même file d’attente sont également terminées.

IDStorageQueueX1

L’interface IDStorageQueueX1 étend l’interface IDStorageQueueX avec la méthode EnqueueSetEvent.

IDStorageQueueX2

L’interface IDStorageQueueX2 étend l’interface `IDStorageQueueX1 avec la méthode CreateQueue1. Ainsi qu’une propriété supplémentaire.
  • Options
    • Indicateurs utilisés pour contrôler le comportement de la file d’attente, notamment la désactivation de la soumission automatique.

EnqueueRequest

Cette interface est fonctionnellement identique à l’interface Win32 ReadFile. Des requêtes de lecture individuelles sont créées et soumises à la file d’attente. La principale différence est que DirectStorage permet de mettre en file d’attente de nombreuses requêtes avant la soumission, prend en charge la décompression matérielle et prend en charge l’annulation. Les requêtes possèdent plusieurs propriétés principales. Source de la requête Selon la combinaison de Options.SourceType et Options.SourceIsPhysicalPages, DirectStorage utilise l’un des trois groupes de propriétés suivants pour indiquer l’emplacement des données sources.
  • File et FileOffset
    • Ce groupe est utilisé lorsque Options.SourceType a la valeur DSTORAGE_REQUEST_SOURCE_FILE
    • File est préalablement ouvert avec IDStorageFactoryX::OpenFile.
    • FileOffset doit être aligné sur 16 octets si la décompression est utilisée, ou n’a aucune exigence d’alignement si la décompression n’est pas utilisée.
      • Il s’agit d’un changement majeur par rapport à Win32, qui exigeait un alignement de 4 Kio dans le fichier pour les lectures asynchrones.
  • Source
    • Ce groupe est utilisé lorsque Options.SourceType a la valeur DSTORAGE_REQUEST_SOURCE_MEMORY et Options.SourceIsPhysicalPages a la valeur FALSE.
    • Mémoire tampon contenant les données à décompresser.
  • SourcePageArray et SourcePageOffset
    • Ce groupe est utilisé lorsque Options.SourceType a la valeur DSTORAGE_REQUEST_SOURCE_MEMORY et Options.SourceIsPhysicalPages a la valeur TRUE.
    • Similaire à Source, mais fournit la mémoire tampon source sous la forme d’un tableau de pages physiques de 64 Ko et du décalage en octets dans la première page.
    • Les pages physiques de 64 Ko peuvent être allouées par XMemAllocatePhysicalPages.
SourceSize
  • Taille, en octets, des données sources à lire, à partir d’une mémoire tampon ou d’un fichier.
IntermediateSize
  • Lorsque les décompressions zlib et BCPACKsont toutes deux activées dans cette requête, IntermediateSize sert à indiquer la taille intermédiaire vers laquelle les données sources sont décompressées par zlib (et à partir de laquelle elles sont décompressées par BCPACK).
  • Sinon, cette propriété doit être définie sur 0.
Destination de la requête Selon Options.DestinationIsPhysicalPages, DirectStorage utilise l’un des deux groupes de propriétés suivants pour indiquer l’emplacement de la destination.
  • Destination
    • Ce groupe est utilisé lorsque Options.DestinationIsPhysicalPages a la valeur FALSE.
    • Mémoire tampon de destination des données chargées finales.
    • La décompression s’effectue à l’aide de mémoires tampons internes partagées et peut être considérée comme effectuée sur place.
  • DestinationPageArray et DestinationPageOffset
    • Ce groupe est utilisé lorsque Options.DestinationIsPhysicalPages a la valeur TRUE.
    • Similaire à Destination, mais fournit la mémoire tampon de destination sous la forme d’un tableau de pages physiques de 64 Ko et du décalage en octets dans la première page.
    • Les pages physiques de 64 Ko peuvent être allouées par XMemAllocatePhysicalPages.
DestinationSize
  • Taille attendue, en octets, du contenu chargé final. La destination doit disposer d’un espace suffisant pour l’opération.
  • La taille doit être égale à SourceSize lorsque la décompression n’est pas utilisée, ou supérieure à SourceSize lorsque la décompression est utilisée.
CancellationTag
  • Balise arbitraire de 64 bits définie par le titre.
  • Cette balise sert de masque pour les demandes d’annulation.
Name
  • Chaîne facultative destinée à faciliter le débogage. Le nom peut apparaître dans les outils de développement, comme PIX (rubrique sous NDA), ou dans l’enregistrement d’erreur obtenu à partir de IDStorageQueueX::RetrieveErrorRecord. La chaîne Name doit rester accessible pendant toute la durée de vie de la requête.
Options
  • ZlibDecompress
    • Indique que les données doivent être décompressées à l’aide de la norme de décompression RFC 1950.
  • BcpackMode
    • Indique le mode BCPACK à utiliser pour décompresser les données.
    • None est une option valide et signifie que les données ne sont pas compressées avec BCPACK.
  • SwizzleMode
    • Indique comment les données finales doivent être swizzlées en mémoire.
  • DestinationIsPhysicalPages
    • Indique que la mémoire tampon de destination est spécifiée à l’aide de DestinationPageArray et DestinationPageOffset au lieu de Destination.
  • SourceType
    • Une requête peut être à source mémoire, et possède alors les propriétés Source/ SourcePageArray et SourcePageOffset, ou à source fichier, et possède alors les propriétés File/FileOffset.
  • SourceIsPhysicalPages
    • Indique que la mémoire tampon source est spécifiée à l’aide de SourcePageArray et SourcePageOffset au lieu de Source.

EnqueueStatus/EnqueueSignal/EnqueueSetEvent

Les requêtes peuvent être mises en file d’attente et traitées comme une série de requêtes liées. Pour ce faire, on met en file d’attente une notification lorsque le traitement atteint un certain point de la file d’attente. Les notifications ne sont traitées que lorsque toutes les requêtes de lecture précédentes sont terminées. Cela garantit que les données de toutes les requêtes précédentes sont immédiatement disponibles. Le titre dispose de deux méthodes d’interrogation et d’une méthode d’attente pour les notifications. Le titre peut insérer un objet ID3D12Fence, un objet IDStorageStatusArrayX ou une opération de définition d’événement. L’objet ID3D12Fence se comporte comme prévu pour un objet ID3D12Fence. Un thread du titre peut attendre un Event, le processeur peut interroger la clôture et le GPU peut interroger la clôture. L’objet IDStorageStatusArrayX permet au processeur d’interroger l’achèvement et d’accéder aux éventuels échecs de lecture. La méthode EnqueueSetEvent permet à un thread du titre d’attendre l’événement spécifié au lieu d’effectuer une interrogation. Cela diffère de ID3D12Fence::SetEventOnCompletion, car l’implémentation XBOX de ID3D12Fence::SetEventOnCompletion tourne en boucle sur la clôture jusqu’à ce qu’elle soit signalée, consommant ainsi le thread matériel du processeur jusqu’au signal, tandis que EnqueueSetEvent permet au thread du titre d’utiliser WaitForSingleObject/WaitForMultipleObjects pour céder le processeur à d’autres threads jusqu’à ce que l’événement soit signalé. Comme indiqué précédemment, toutes les requêtes sont terminées dans l’ordre, même si le matériel sous-jacent décide de les réorganiser pour des raisons de performances. Les notifications ne sont pas signalées tant que toutes les requêtes précédemment mises dans la file d’attente ne sont pas terminées.

Décompression

La décompression est gérée par un matériel dédié. Cela élimine la surcharge processeur des algorithmes de décompression traditionnels. DirectStorage alloue un bloc fixe de mémoire lors de l’initialisation pour l’utiliser comme mémoire tampon de travail pour la décompression. Cela permet la décompression sur place et évite de devoir conserver en mémoire à la fois les données compressées et décompressées. Le matériel de décompression prend en charge trois modes de fonctionnement. Les modes ne s’excluent pas mutuellement ; toute combinaison de modes peut donc être spécifiée. Les modes de décompression sont appliqués dans l’ordre suivant : DEFLATE, BCPACK et Swizzle.
  • ZLibDecompress
  • BCPack
    • BCPack est un codeur entropique personnalisé conçu spécifiquement pour les données BCn. En général, cela signifie que les points de terminaison de couleur sont séparés des indices de palette (c’est-à-dire des poids) et compressés à l’aide d’un algorithme rANS.
  • Swizzle
    • Les modes Swizzle et shuffle peuvent apporter des optimisations supplémentaires dans les pipelines de contenu.
Lorsque des données à entropie élevée sont compressées, il est possible que la compression augmente en fait leur taille. À l’inverse, la décompression correspondante réduit la taille. DirectStorage n’autorise pas une décompression qui réduit la taille, et il appartient au titre de détecter les données à entropie élevée incompressibles et d’éviter de compresser ces ressources. Pour plus d’informations, consultez le guide Optimisation du contenu compressé à l’aide de DirectStorage et XBTC (rubrique sous NDA).

Mémoire tampon intermédiaire

DirectStorage utilise en interne une mémoire tampon pour placer tout le contenu lu à partir du stockage NVMe brut avant d’effectuer des opérations telles que le déchiffrement et la décompression. Cette mémoire tampon intermédiaire permet au lecteur NVMe et au circuit de déchiffrement/décompression de fonctionner en pipeline, en parallèle. Sa taille par défaut est de 32 Mio et elle est allouée lorsque le premier pointeur de fabrique DirectStorage est récupéré. Si un titre utilise DirectStorage uniquement pour des opérations de décompression de mémoire à mémoire, la mémoire tampon intermédiaire n’est pas nécessaire, et SetStagingBufferSize peut être appelée pour définir la taille de la mémoire tampon intermédiaire sur 0. La version actuelle de DirectStorage prend en charge des tailles de mémoire tampon intermédiaire de 0, 16, 20, 24, 28 et 32 Mio. Une taille inférieure à la valeur par défaut permet au titre d’économiser de la mémoire, mais peut affecter les performances de lecture globales. REMARQUE : SetStagingBufferSize ne peut être appelée que lorsqu’il n’existe aucun objet IDStorageQueueX ni aucun objet IDStorageFileX ; sinon, elle lève une erreur.

CancelRequestsWithTag

DirectStorage prend en charge l’annulation des requêtes. Chaque requête est associée à une balise de 64 bits définie par le titre. Elle sert de masque de bits pour déterminer les requêtes à annuler. Le titre fournit un masque et une valeur pour l’annulation. La file d’attente tente d’annuler toutes les requêtes qui correspondent au critère : tag & mask == value. L’annulation est une opération effectuée au mieux. Selon l’emplacement de la requête dans le pipeline, il peut être impossible de l’annuler. Par exemple, la requête peut être en cours de décompression par le matériel, ce qui ne peut pas être annulé. L’API retourne immédiatement et ne bloque pas en attendant que toutes les requêtes annulées soient traitées. Les titres doivent attendre qu’une notification ultérieure dans la file d’attente soit signalée avant de libérer les ressources associées aux requêtes annulées. Veillez à ne pas ajouter de requêtes à une file d’attente au moment même où CancelRequestsWithTag est appelée. Dans ce cas, le comportement n’est pas défini. Toutefois, les requêtes ajoutées à la file d’attente après le retour de l’appel à CancelRequestsWithTag ne seront pas annulées, même si elles correspondent aux critères. Seules les requêtes précédemment mises en file d’attente seront annulées.

GetErrorEvent/RetrieveErrorRecord

Si une lecture provoque une erreur, cette lecture est marquée comme terminée. Les notifications ultérieures de la file d’attente ne sont pas empêchées d’être signalées. La notification des erreurs est gérée par un objet Event associé à la file d’attente, qui peut être récupéré avec GetErrorEvent. Le titre peut utiliser WaitForSingleObject sur l’objet Event renvoyé par GetErrorEvent. Si l’événement est signalé, le titre peut obtenir la première erreur survenue depuis le dernier appel à la fonction RetrieveErrorRecord en appelant la fonction RetrieveErrorRecord. L’enregistrement d’erreur renvoyé par RetrieveErrorRecord contient uniquement les données de la première requête ayant échoué dans la file d’attente depuis le dernier appel à RetrieveErrorRecord. Les données de l’enregistrement d’erreur ne sont pas définies si l’objet Event d’erreur n’est pas signalé ou si les données ont déjà été récupérées.

Query

Obtient des informations sur la file d’attente. Cela inclut les structures DSTORAGE_QUEUE_DESC ou DSTORAGE_QUEUE_DESC1 utilisées pour la création de la file d’attente, ainsi que le nombre d’emplacements vides et le nombre d’entrées à mettre en file d’attente pour déclencher la soumission automatique.

Meilleures pratiques

Les mêmes conseils de meilleures pratiques donnés pour Win32 s’appliquent également à DirectStorage. Les seuils de performances optimales ont radicalement changé.

Tailles de lecture

Le conseil d’origine pour un disque rotatif était de lire par blocs d’au moins 128 Kio. Les performances continuent d’augmenter avec des tailles de bloc plus importantes. Les blocs de 512 Kio offraient les meilleures performances. L’absence de pièces mobiles sur le NVMe crée un seuil beaucoup plus bas. Les performances de lecture font un bond important à partir des lectures de 32 Kio et commencent à plafonner à 64 Kio. Lire au-delà n’augmente pas les performances. Cela signifie qu’il faut moins d’efforts pour fusionner les données en blocs plus importants dans le package afin d’obtenir des performances optimales. Au-delà de 512 Kio, si la décompression est utilisée, préférez des requêtes plus petites mais plus nombreuses en parallèle à une seule requête volumineuse. Une seule requête volumineuse oblige la décompression à être sérialisée, tandis que plusieurs requêtes simultanées permettent à plusieurs unités matérielles de décompression de fonctionner en parallèle et d’atteindre le débit maximal. Dans la version d’octobre 2022 du Microsoft Game Development Kit (GDK), la taille maximale d’une requête unique est passée de 32 Mio pour la destination à 1 Gio d’utilisation mémoire combinée (source plus destination). Cela permet de faciliter le portage depuis d’autres API de stockage qui autorisent de grandes tailles de lecture. Mais la recommandation de taille précédente pour atteindre le débit maximal reste la même, car les requêtes volumineuses ne permettent pas à plusieurs unités matérielles de décompression de fonctionner en parallèle.

Ordre

Auparavant, avec les disques rotatifs, des efforts étaient consacrés à l’ordonnancement de l’emplacement des lectures sur le disque. La situation idéale consistait à lire à partir d’emplacements séquentiels sur le disque. Cela réduisait au minimum les mouvements de la tête du disque, ce qui éliminait le temps de recherche comme facteur. Cela pouvait augmenter les performances d’un ordre de grandeur. Il était même avantageux de soumettre des lectures aléatoires triées par emplacement, ce qui était dans certains cas 2 fois plus rapide. Il reste utile d’ordonner les requêtes de lecture de manière aussi séquentielle que possible sur un lecteur NVMe. Le lecteur NVMe lit par blocs alignés sur 64 Kio. De ce fait, de la bande passante peut être gaspillée à lire des sections inutilisées d’un bloc de 64 Kio. Si une requête de lecture ne porte que sur 4 Kio, 60 Kio de bande passante seront gaspillés. Le NVMe réutilise ces 60 Kio supplémentaires pour satisfaire d’autres requêtes en attente si possible. Par exemple, si vous avez deux lectures séquentielles, une de 32 Kio suivie d’une de 8 Kio, il n’y a toujours qu’une seule lecture de 64 Kio depuis le lecteur.

Gestion des files d’attente

La recommandation pour les disques rotatifs était d’avoir une taille de file d’attente comprise entre 12 et 16. Une profondeur de file d’attente plus importante n’apportait aucun avantage, et la baisse de performances liée à l’utilisation de profondeurs plus faibles était importante. La spécification NVMe indique qu’un lecteur NVMe doit prendre en charge plusieurs files d’attente d’une profondeur allant jusqu’à 65 536 entrées par file. DirectStorage prend en charge cette exigence, ce qui permet au titre de soumettre des milliers de requêtes à la fois. Auparavant, avec les disques rotatifs, les titres mettaient en mémoire tampon les requêtes en attente pour maintenir une profondeur de file d’attente comprise entre 12 et 16. La recommandation pour DirectStorage est de ne pas mettre en mémoire tampon les requêtes, mais de les mettre en file d’attente dès leur création. L’ensemble du système est un pipeline, et la mise en mémoire tampon par le titre crée des bulles dans le pipeline, ce qui peut nuire gravement aux performances. Une autre recommandation consiste à créer une file d’attente d’une capacité au moins quatre fois supérieure au plus grand nombre de requêtes créées par image. Cela devrait offrir une capacité suffisante pour que de nouvelles requêtes puissent être ajoutées sans blocage en attendant que les requêtes existantes se terminent.

Gestion des notifications

En général, moins il y a de requêtes de notification ajoutées à la file d’attente, mieux c’est. La recommandation est de trouver un équilibre entre les besoins du titre et la réduction au minimum des requêtes de notification mises en file d’attente. Mettre en file d’attente une notification après chaque requête ne fait que nuire aux performances globales en raison de la surcharge accrue liée au traitement de ces notifications. Un exemple consisterait à regrouper par contenu. Par exemple, les textures SFS, les besoins du terrain (par exemple, maillage et texture) et les besoins des acteurs (par exemple, maillage, texture et animation). Cela permet de lier une seule notification à la disponibilité de toutes les ressources nécessaires pour créer un objet. Le choix entre l’utilisation d’un ID3D12Fence et celle d’un tableau d’état dépend des besoins du titre. Les données doivent-elles être utilisées immédiatement par le GPU ? Est-il acceptable que le thread de vérification se suspende jusqu’à la fin des lectures ? Une interrogation périodique suffit-elle parce que les données ne peuvent être traitées qu’à certains moments de l’image ?

Considérations

Avec la possibilité d’avoir un nombre beaucoup plus élevé de requêtes en cours, il faut veiller à ne pas créer de goulots d’étranglement dans d’autres parties du titre. Les coûts de gestion des requêtes par le titre pourraient rapidement dépasser les gains obtenus grâce à DirectStorage. La recommandation est d’examiner tout le code de prise en charge par requête et de déterminer ce qui peut être réduit. Chaque requête nécessite-t-elle l’allocation d’un nouveau bloc de mémoire ?
  • Le système de mémoire engendre une surcharge pour trouver un nouveau bloc et mettre à jour les listes internes.
  • Envisagez de réutiliser les blocs de mémoire autant que possible.
Un verrou est-il nécessaire pour mettre à jour les gestionnaires ?
  • Cela crée davantage de contention à mesure que les mises à jour se multiplient.
  • Envisagez d’adopter une approche sans verrou autant que possible.
Le chargement spéculatif est-il utilisé ?
  • L’annulation est prise en charge, ce qui pourrait conduire à créer davantage de requêtes.
  • Toutefois, de la mémoire doit être disponible pour la spéculation.
  • Envisagez des limites strictes pour les seuils de spéculation.

Voir aussi

DirectStorage Utilisation et détails internes de DirectStorage (rubrique sous NDA) Optimisation du contenu compressé à l’aide de DirectStorage et XBTC (rubrique sous NDA) Analyse des performances de DirectStorage (rubrique sous NDA)
Last modified on October 6, 2026