Skip to main content

Introdução

Este artigo fornece uma visão geral das APIs do DirectStorage somente para consoles XBOX Series X|S. Consulte DirectStorage no Desktop para obter detalhes sobre o DirectStorage no Desktop. Os dispositivos de armazenamento NVMe mais recentes conectados por meio de um barramento PCIe podem alcançar níveis muito altos de taxa de transferência e IOPS (solicitações de E/S por segundo). A sobrecarga das APIs do Win32 significa que, embora a largura de banda de armazenamento disponível possa ser utilizada, aproveitá-la pode resultar em uma utilização da CPU inaceitavelmente alta. Isso é especialmente verdadeiro quando a carga de trabalho consiste em um grande número de solicitações pequenas. As APIs do DirectStorage foram projetadas para remover a maior parte da sobrecarga do sistema operacional, interagindo diretamente com o hardware NVMe subjacente. Isso permite alcançar uma largura de banda maior com menor uso da CPU. O objetivo é permitir o processamento de até 50.000 solicitações por segundo usando no máximo 10% de um único núcleo de CPU.

Problemas existentes

O conteúdo dos jogos está ficando cada vez maior à medida que a necessidade de ativos de maior resolução aumenta a cada geração de console. O hardware e o software existentes do XBOX One têm várias limitações que prejudicam a capacidade dos desenvolvedores de levar dados do disco rígido para a memória para esse conteúdo de próxima geração.
  • Alto uso da CPU
    • As APIs do Win32 existentes podem exigir um núcleo de CPU inteiro em sobrecarga.
    • Isso depende do número de solicitações do título.
  • Largura de banda máxima insuficiente do disco
  • Incapacidade de priorizar solicitações de disco
    • Sem a capacidade de priorizar as solicitações do título, pode ser difícil criar um sistema de streaming responsivo.
  • Incapacidade de cancelar solicitações de disco
    • Sem a capacidade de cancelar solicitações, pode ser difícil criar um sistema de leitura especulativa.
  • Nenhuma descompactação acelerada por hardware
    • Sem a descompactação acelerada por hardware, pode ser necessária uma grande quantidade de recursos de CPU para realizar a descompactação em software.
O conjunto de APIs do DirectStorage aborda diretamente cada um desses problemas. O efeito geral é um aumento drástico no desempenho do sistema de arquivos do XBOX.

Uso da CPU

O principal objetivo de design do DirectStorage é permitir que um título sustente 50 mil IOPS usando apenas entre 5% e 10% de um único núcleo de CPU. Isso permite que o título alcance a largura de banda máxima do subsistema de armazenamento NVMe e, ao mesmo tempo, permite que a CPU seja usada para outros requisitos do título. O DirectStorage também adiciona suporte para descompactação por hardware. Cada solicitação de leitura pode ser roteada diretamente da unidade NVMe para o bloco de descompactação por hardware interno. Isso elimina a necessidade de um título gastar recursos de CPU com descompactação.

Modelo de pipeline enfileirado

O DirectStorage usa um método em lote, no qual várias solicitações são adicionadas a uma fila. Em um momento posterior, a fila é liberada para o próximo estágio do pipeline. Isso reduz imediatamente o custo geral de CPU de uma transição entre estágios do pipeline. No conjunto de APIs do Win32 existente, há uma transição para cada solicitação. As filas do DirectStorage utilizam algoritmos sem bloqueio para minimizar a contenção. O título tem controle sobre quando cada fila é liberada. Em muitos casos com as APIs do Win32, os dados do disco podem precisar ser copiados para outro buffer. Em alguns casos, os dados podem precisar ser copiados mais de uma vez. O DirectStorage elimina esse problema mapeando o buffer de destino fornecido pelo título diretamente em cada camada do pipeline. O hardware grava diretamente no buffer fornecido pelo título. Essas alterações contribuem para reduções significativas da sobrecarga de CPU.

Descompactação

A capacidade do hardware de descompactar dados foi ampliada. Agora ele pode lidar com uma variedade maior de formatos e em uma velocidade maior do que o subsistema NVMe consegue fornecer dados. Além disso, o DirectStorage oferece suporte à descompactação no local, o que elimina a necessidade de gerenciar buffers separados para dados compactados e descompactados. O hardware oferece suporte a BCPACK e DEFLATE e fornece a capacidade de aplicar swizzle ao conteúdo final. Esses formatos não são mutuamente exclusivos. É possível ter os três aplicados aos dados. Isso dá ao título a capacidade de escolher qual método oferece a melhor taxa de compactação e o melhor desempenho. Ativos diferentes podem usar configurações diferentes de compactação e swizzle.

Profundidade da fila

A recomendação anterior era manter apenas de 12 a 16 solicitações assíncronas em andamento ao mesmo tempo em uma unidade rotacional. Ir além disso não trazia benefícios de desempenho, e ficar abaixo disso prejudicava significativamente o desempenho. Isso levava os títulos a realizar trabalho extra para equilibrar suas solicitações de leitura pendentes e permanecer dentro da meta recomendada. Com o objetivo do DirectStorage de permitir que um título alcance 50.000 IOPS, nossa recomendação mudou. O título não precisa mais tentar equilibrar o trabalho pendente e a profundidade da fila. O título deve enviar todas as suas solicitações pendentes. Não há benefício em reter algumas solicitações. Em muitos casos, reter solicitações pode prejudicar o desempenho, pois o hardware fica parado, aguardando novas solicitações. O sistema operacional ainda precisa dividir solicitações de leitura maiores em várias solicitações menores em alguns casos (por exemplo, ao lidar com a fragmentação do disco). No entanto, isso foi considerado na arquitetura do DirectStorage. O objetivo de design de 50.000 IOPS baseia-se na contagem de operações de E/S do título, e não nas solicitações finais enviadas ao hardware.

Notificação

Na arquitetura do Win32, uma quantidade significativa de sobrecarga é gasta com notificações de conclusão de leitura. O título pode sondar uma estrutura OVERLAPPED, aguardar um identificador de Event associado ou realizar uma leitura síncrona com bloqueio. No geral, isso aumenta a demanda de recursos de cada solicitação de leitura. O DirectStorage mantém os dois conceitos assíncronos de notificações e também adiciona um terceiro método. Não há suporte para leituras síncronas com bloqueio no DirectStorage. É possível que um título implemente seu próprio sistema. No entanto, isso não é recomendado. O primeiro método assíncrono é implementado por meio de um bloco de status que é definido quando as solicitações associadas são concluídas. O título pode sondar o bloco conforme necessário para determinar quando as leituras foram concluídas. Isso é semelhante ao método do Win32 de sondar uma estrutura OVERLAPPED para verificar a conclusão. O segundo método assíncrono usa um objeto Event do Windows para sinalizar a conclusão. Isso é semelhante a usar uma estrutura OVERLAPPED com um objeto Event correspondente. O título pode usar o método WaitForSingleObject para fazer com que o thread de chamada seja suspenso até que as operações de leitura sejam concluídas. O terceiro método assíncrono é implementado usando um ID3D12Fence. O título pode suspender aguardando o fence ou sondar o fence, se necessário. Há também o benefício de que a GPU pode usar o fence para notificação direta de solicitações concluídas. O sistema de notificação do DirectStorage não está vinculado a uma única solicitação de leitura. Ele é uma entrada colocada na fila, que é sinalizada quando todas as solicitações de leitura anteriores tiverem sido concluídas. Isso dá ao título controle sobre a granularidade necessária para a notificação. A notificação é sempre sinalizada na ordem da fila. A fila pode ser considerada uma fila FIFO (primeiro a entrar, primeiro a sair). O título só precisa consultar a última notificação relevante. Há garantia de que todas as solicitações enfileiradas anteriormente foram concluídas.

Descompactação de memória para memória

O DirectStorage fornece um tipo de fila para invocar o hardware de descompactação com a origem da descompactação sendo a memória em vez de um arquivo em disco. Isso permite que o hardware de descompactação seja utilizado se o ativo compactado não for proveniente de um arquivo ou tiver sido obtido anteriormente e mantido na memória como cache. Uma fila com origem em memória só aceita solicitações com origem em memória, e uma fila com origem em arquivo só aceita solicitações com origem em arquivo. Se nenhuma opção de descompactação for especificada em uma solicitação com origem em memória, o hardware de descompactação também pode atuar como um mecanismo de cópia DMA. Embora o DirectStorage garanta que as notificações de conclusão estejam em ordem, o DirectStorage não oferece garantia sobre quando as solicitações começam a ser processadas. Como resultado, não deve haver dependências de dados entre solicitações pendentes. Ou seja, o destino da solicitação A não pode ser usado como origem da solicitação B, a menos que a solicitação B seja enfileirada após a conclusão da solicitação A. As filas com origem em memória devem ser criadas com prioridade em tempo real. Além disso, as solicitações em tempo real com origem em memória são sempre processadas pelo hardware de descompactação antes das solicitações com origem em disco que exigem descompactação. Se as filas com origem em disco não tiverem nenhuma solicitação de descompactação, os dois tipos de fila são processados de forma totalmente paralela, sem afetar o outro tipo.

Prioridade

O DirectStorage permite que um nível de prioridade seja atribuído a cada fila. Cada entrada da fila herda a prioridade da fila. Quatro níveis de prioridade diferentes são fornecidos: tempo real, alta, normal e baixa. As solicitações são processadas em um esquema round-robin ponderado. Por exemplo, processar X solicitações com prioridade alta antes de processar uma solicitação com prioridade normal. Processar Y solicitações com prioridade normal antes de processar uma solicitação com prioridade baixa. A ponderação de prioridade é calculada com base no tamanho de cada solicitação. A ponderação padrão entre cada prioridade é de aproximadamente 10x. Isso significa que, para cada 1 KB de solicitação de prioridade baixa, 10 KB de solicitação de prioridade média e 100 KB de solicitação de prioridade alta teriam sido processados. As solicitações de leitura existentes do Win32 são roteadas pelo mesmo sistema de prioridade. Todas as solicitações do Win32 são consideradas de prioridade normal. As filas com origem em memória devem ser criadas com prioridade em tempo real.

Cancelamento

Cada solicitação de leitura do DirectStorage tem uma máscara de 64 bits fornecida pelo título associada a ela. Isso serve para oferecer suporte ao cancelamento de solicitações de leitura pendentes. O título pode cancelar solicitações que correspondam a um conjunto específico de sinalizadores dentro da máscara. Mesmo com suporte para cancelamento, ainda é possível que uma solicitação de leitura seja processada pelo hardware. A solicitação de cancelamento do título é uma tentativa de melhor esforço. Se a solicitação já estiver sendo processada ativamente pelo hardware, ela não poderá ser cancelada. Como a solicitação de cancelamento é de melhor esforço, o título deve aguardar até ser notificado de que o processamento da solicitação de leitura foi concluído. O título não pode liberar nenhum recurso necessário até que uma notificação posterior na fila seja recebida. No entanto, durante esse período, novas solicitações que correspondam a sinalizadores usados em uma solicitação de cancelamento anterior podem ser enfileiradas e não serão canceladas. Quando uma solicitação cancelada é concluída, ela é considerada um sucesso, mesmo que tenha sido cancelada e não tenha produzido o resultado completo. Em outras palavras, se houver tentativa de cancelamento em uma solicitação, o título não poderá mais consumir o resultado da solicitação possivelmente cancelada após a conclusão.

Garantia

Os consoles XBOX One e XBOX One S tinham uma garantia mínima de 40 MB/s. O console XBOX One X aumentou a garantia mínima para 60 MB/s. Esses números estão bem abaixo dos limites reais do hardware, que ficam na faixa de 130 MB/s. Isso se devia inteiramente à sobrecarga causada pelo sistema operacional. O DirectStorage remove a maior parte da sobrecarga causada pelo sistema operacional. Isso permite uma garantia mínima mais próxima dos limites do hardware. A nova garantia mínima de desempenho é de 2,0 GB/s em uma janela de 250 ms para dados brutos. O uso de descompactação no conteúdo aumentará ainda mais a largura de banda final. Os futuros consoles XBOX oferecerão suporte à adição de unidades dinâmicas instaláveis pelo usuário que também são baseadas em NVMe. A mesma garantia mínima de desempenho fornecida para a unidade interna também é fornecida para a unidade instalável pelo usuário.

Visão geral da API

As interfaces do DirectStorage seguem o mesmo padrão das interfaces do Direct3D. Inicialmente, o título obtém uma fábrica singleton. A fábrica é usada para criar filas de solicitações e abrir arquivos; cada um desses objetos tem um mapeamento direto para o hardware. Em seguida, solicitações individuais são enfileiradas em uma fila para serem enviadas ao hardware.

IDStorageFactoryX

IDStorageFactoryX é a interface principal para criar filas, abrir arquivos e enviar solicitações pendentes. O objeto IDStorageFactoryX tem os seguintes métodos.
  • OpenFile
    • Cria um objeto IDStorageFileX , que representa um arquivo.
  • CreateQueue
    • Cria um objeto IDStorageQueueX. Usado para criar solicitações de leitura.
  • CreateStatusArray
    • Cria um objeto IDStorageStatusArray, que gerencia os sinalizadores de status de conclusão.
  • SetCPUAffinity
    • Restringe o trabalho do DirectStorage fora do thread de chamada a um conjunto de núcleos de CPU definido pelo título.
    • OBSERVAÇÃO O DirectStorage tenta fazer a maior parte do trabalho no thread de chamada. O trabalho fora do thread de chamada só ocorre quando não pode ser feito no thread de chamada. Veja alguns exemplos a seguir.
      • O pipeline de recursos subjacente está cheio durante IDStorageQueueX::Submit, e nem todas as solicitações da fila podem ser encaminhadas. As solicitações restantes serão processadas mais tarde, quando os recursos forem liberados, e isso é feito em um thread de trabalho do DirectStorage.
      • Processamento da conclusão de solicitações em ID3DFence ou IDStorageStatusArray.
  • SetDebugFlags
    • Controla se o DirectStorage fará validações adicionais no momento do enfileiramento da solicitação para auxiliar na depuração.
  • SetStagingBufferSize
    • Define o tamanho do buffer de preparo usado para armazenar temporariamente o conteúdo carregado do dispositivo de armazenamento antes de ser descriptografado/descompactado. Se forem usadas apenas filas com origem em memória, o buffer de preparo pode ter tamanho 0.

IDStorageFactoryX1

A interface IDStorageFactoryX1 estende a interface IDStorageFactoryX com um método GetStats.
  • GetStats
    • Obtém as estatísticas do DirectStorage. Essa função pode ser usada para integrar o DirectStorage a pipelines de diagnóstico e telemetria existentes. Ela faz um processamento mínimo e, portanto, pode ser chamada com frequência. As estatísticas não incluem operações de E/S de arquivo do Win32.

IDStorageFactoryX2

A interface IDStorageFactoryX2 estende a interface `IDStorageFactoryX1 com um método CreateQueue1.
  • CreateQueue1
    • Cria um objeto IDStorageQueueX2. Usa uma nova estrutura para permitir opções adicionais na criação da fila, incluindo a capacidade de substituir o recurso de envio automático de uma fila.

IDStorageFileX

Todos os arquivos precisam ser abertos inicialmente pelo DirectStorage por meio do objeto IDStorageFactoryX. Isso equivale a usar CreateFile nas interfaces de API do Win32. Os arquivos são abertos com a permissão FILE_SHARED_READ. Se necessário, os títulos podem abrir o arquivo simultaneamente usando as APIs do Win32, desde que as permissões adequadas sejam respeitadas. Durante o desenvolvimento, há suporte para implantações soltas e empacotadas. Os arquivos são fechados chamando explicitamente a função Close no objeto de arquivo ou quando a última referência ao objeto IDStorageFileX correspondente é liberada. No entanto, todas as operações de E/S pendentes devem ser concluídas antes que o arquivo possa ser fechado. Isso significa que ambos os métodos de fechar um arquivo ficarão bloqueados até que todas as operações de E/S pendentes nesse arquivo sejam concluídas. O jogo pode obter um identificador do Win32 para um arquivo representado por um objeto IDStorageFileX chamando a função GetHandle. O identificador é aberto com permissões GENERIC_READ e modo de compartilhamento FILE_SHARE_READ. Ele pode ser usado para consultar o tamanho do arquivo e assim por diante. O identificador deve ser fechado com CloseHandle() quando não for mais necessário.

IDStorageQueueX

As solicitações de leitura são enviadas ao NVMe por meio de um objeto IDStorageQueueX. No entanto, as solicitações não são enviadas ao dispositivo até que o título chame Submit na fila, ou até que um dos métodos Enqueue tenha preenchido mais da metade da capacidade da fila desde o último envio e dispare um envio automático. Os envios são tratados como uma única transição para o próximo estágio do pipeline. Isso permite que o título controle quando ocorre o custo de CPU da transição entre o título e o kernel. Os objetos IDStorageQueueX têm quatro propriedades.
  • SourceType
    • Especifica se a fila pode receber solicitações com origem em arquivo ou solicitações com origem em memória.
  • Priority
    • A prioridade de todas as solicitações enviadas à fila: tempo real, alta, normal ou baixa.
    • As filas com origem em memória devem ser criadas com prioridade em tempo real.
    • As solicitações são processadas em uma ordem round-robin ponderada com base na prioridade.
    • As solicitações do Win32 são processadas no nível de prioridade normal.
  • Capacity
    • O número máximo de solicitações pendentes que a fila pode conter.
    • Tentar enfileirar uma solicitação quando a fila está em sua capacidade máxima ficará bloqueado até que as entradas sejam concluídas pelo hardware.
    • A quantidade de memória necessária para uma fila é aproximadamente a capacidade da fila multiplicada pelo tamanho de um DSTORAGE_REQUEST.
  • Name
    • Serve apenas para auxiliar na depuração. O nome não é usado por nenhuma parte do código do DirectStorage, mas é visível nas ferramentas do desenvolvedor, como o PIX (tópico sob NDA).
O hardware processa as solicitações de forma assíncrona para alcançar a maior taxa de transferência. No entanto, ao contrário do Win32, o título é notificado da conclusão na ordem FIFO. Na notificação de conclusão, há garantia de que todas as solicitações anteriores para a mesma fila também foram concluídas.

IDStorageQueueX1

A interface IDStorageQueueX1 estende a interface IDStorageQueueX com o método EnqueueSetEvent.

IDStorageQueueX2

A interface IDStorageQueueX2 estende a interface `IDStorageQueueX1 com o método CreateQueue1. E uma propriedade adicional.
  • Options
    • Sinalizadores usados para controlar o comportamento da fila, incluindo a desativação do envio automático.

EnqueueRequest

Essa interface é funcionalmente igual à interface ReadFile do Win32. Solicitações de leitura individuais são criadas e enviadas à fila. A principal diferença é que o DirectStorage permite que muitas solicitações sejam enfileiradas antes do envio, oferece suporte à descompactação por hardware e oferece suporte ao cancelamento. As solicitações têm várias propriedades principais. Origem da solicitação Dependendo da combinação de Options.SourceType e Options.SourceIsPhysicalPages, o DirectStorage usa um dos três grupos de propriedades a seguir para especificar onde os dados de origem estão.
  • File e FileOffset
    • Este grupo é usado quando Options.SourceType é DSTORAGE_REQUEST_SOURCE_FILE
    • File é aberto previamente com IDStorageFactoryX::OpenFile.
    • FileOffset precisa estar alinhado a 16 bytes se a descompactação for usada, ou não tem requisito de alinhamento se a descompactação não for usada.
      • Essa é uma grande mudança em relação ao Win32, que exigia alinhamento de 4 KiB dentro do arquivo para leituras assíncronas.
  • Source
    • Este grupo é usado quando Options.SourceType é DSTORAGE_REQUEST_SOURCE_MEMORY e Options.SourceIsPhysicalPages é FALSE.
    • O buffer de memória que contém os dados a serem descompactados.
  • SourcePageArray e SourcePageOffset
    • Este grupo é usado quando Options.SourceType é DSTORAGE_REQUEST_SOURCE_MEMORY e Options.SourceIsPhysicalPages é TRUE.
    • Semelhante a Source, mas fornece o buffer de memória de origem na forma de uma matriz de páginas físicas de 64 KB e o deslocamento em bytes na primeira página.
    • Páginas físicas de 64 KB podem ser alocadas por XMemAllocatePhysicalPages.
SourceSize
  • Tamanho, em bytes, dos dados de origem a serem lidos, seja de um buffer de memória ou de um arquivo.
IntermediateSize
  • Quando a descompactação zlib e BCPACKestão habilitadas nesta solicitação, IntermediateSize é usado para especificar o tamanho intermediário para o qual os dados de origem são descompactados com zlib (e a partir do qual são descompactados com BCPACK).
  • Caso contrário, deve ser definido como 0.
Destino da solicitação Dependendo de Options.DestinationIsPhysicalPages, o DirectStorage usa um dos dois grupos de propriedades a seguir para especificar onde o destino está.
  • Destination
    • Este grupo é usado quando Options.DestinationIsPhysicalPages é FALSE.
    • O buffer de destino para os dados carregados finais.
    • A descompactação ocorre usando buffers internos compartilhados e pode ser considerada no local.
  • DestinationPageArray e DestinationPageOffset
    • Este grupo é usado quando Options.DestinationIsPhysicalPages é TRUE.
    • Semelhante a Destination, mas fornece o buffer de memória de destino na forma de uma matriz de páginas físicas de 64 KB e o deslocamento em bytes na primeira página.
    • Páginas físicas de 64 KB podem ser alocadas por XMemAllocatePhysicalPages.
DestinationSize
  • Tamanho esperado, em bytes, do conteúdo carregado final. O destino deve conter espaço suficiente para acomodar a operação.
  • O tamanho deve ser igual a SourceSize quando a descompactação não for usada, ou maior que SourceSize quando a descompactação for usada.
CancellationTag
  • Uma marca arbitrária de 64 bits definida pelo título.
  • Essa marca é usada como máscara para solicitações de cancelamento.
Name
  • Cadeia de caracteres opcional para auxiliar na depuração. O Name pode aparecer nas ferramentas do desenvolvedor, como o PIX (tópico sob NDA), ou no registro de erro obtido de IDStorageQueueX::RetrieveErrorRecord. A cadeia de caracteres Name precisa estar acessível durante todo o tempo de vida da solicitação.
Options
  • ZlibDecompress
    • Indica que os dados precisam ser descompactados usando o padrão de descompactação RFC 1950.
  • BcpackMode
    • Indica qual modo de BCPACK deve ser usado para descompactar os dados.
    • None é uma opção válida e significa que os dados não estão compactados com BCPACK.
  • SwizzleMode
    • Indica como o swizzle deve ser aplicado aos dados finais na memória.
  • DestinationIsPhysicalPages
    • Indica que o buffer de destino é especificado usando DestinationPageArray e DestinationPageOffset em vez de Destination.
  • SourceType
    • Uma solicitação pode ter origem em memória e, portanto, ter as propriedades Source/ SourcePageArray e SourcePageOffset, ou ter origem em arquivo e, portanto, ter as propriedades File/FileOffset.
  • SourceIsPhysicalPages
    • Indica que o buffer de origem é especificado usando SourcePageArray e SourcePageOffset em vez de Source.

EnqueueStatus/EnqueueSignal/EnqueueSetEvent

As solicitações podem ser enfileiradas e tratadas como uma série de solicitações relacionadas. Isso é feito enfileirando uma notificação para quando o processamento atingir um determinado ponto da fila. As notificações são processadas somente quando todas as solicitações de leitura anteriores forem concluídas. Isso garante que os dados de todas as solicitações anteriores estejam imediatamente disponíveis. O título tem dois métodos de sondagem e um método de espera para notificação. O título pode inserir um objeto ID3D12Fence, um objeto IDStorageStatusArrayX ou uma operação de definição de evento. O ID3D12Fence se comporta como esperado para um objeto ID3D12Fence. Um thread do título pode aguardar um Event, a CPU pode sondar o fence e a GPU pode sondar o fence. O objeto IDStorageStatusArrayX permite a sondagem da conclusão pela CPU e o acesso a possíveis falhas de leitura. O método EnqueueSetEvent permite que um thread do título aguarde o evento especificado em vez de fazer sondagem. Isso difere de ID3D12Fence::SetEventOnCompletion, pois a implementação do XBOX de ID3D12Fence::SetEventOnCompletion fica em espera ativa no fence até que ele seja sinalizado, consumindo assim o thread de hardware da CPU até o sinal, enquanto EnqueueSetEvent permite que o thread do título use WaitForSingleObject/WaitForMultipleObjects para ceder a CPU a outros threads até que o evento seja sinalizado. Como mencionado anteriormente, todas as solicitações são concluídas em ordem, mesmo que o hardware subjacente decida reordená-las para obter desempenho. As notificações não são sinalizadas até que todas as solicitações enfileiradas anteriormente na fila tenham sido concluídas.

Descompactação

A descompactação é feita por hardware dedicado. Isso remove a sobrecarga de CPU dos algoritmos de descompactação tradicionais. O DirectStorage aloca um bloco fixo de memória durante a inicialização para usar como buffer de trabalho para a descompactação. Isso permite a descompactação no local e elimina a necessidade de manter os dados compactados e descompactados na memória ao mesmo tempo. O hardware de descompactação oferece suporte a três modos de operação. Os modos não são mutuamente exclusivos, portanto, qualquer combinação de modos pode ser especificada. Os modos de descompactação são aplicados na seguinte ordem: DEFLATE, BCPACK e Swizzle.
  • ZLibDecompress
  • BCPack
    • BCPack é um codificador de entropia personalizado projetado especificamente para dados BCn. Em geral, isso significa que os pontos de extremidade de cor são separados dos índices da paleta (ou seja, pesos) e compactados usando um algoritmo rANS.
  • Swizzle
    • Os modos Swizzle e de embaralhamento podem oferecer otimizações adicionais em pipelines de conteúdo.
Quando dados de alta entropia são compactados, é possível que a compactação na verdade aumente o tamanho. Por outro lado, a descompactação correspondente reduzirá o tamanho. O DirectStorage não permite descompactação com redução de tamanho, e cabe ao título detectar dados de alta entropia que não são compactáveis e evitar compactar esses ativos. Para obter mais detalhes, consulte o guia Otimizando conteúdo compactado usando DirectStorage e XBTC (tópico sob NDA).

Buffer de preparo

Internamente, o DirectStorage usa um buffer para preparar todo o conteúdo lido do armazenamento NVMe bruto antes de realizar operações como descriptografia e descompactação. Esse buffer de preparo permite que a unidade NVMe e o silício de descriptografia/descompactação trabalhem em um pipeline em paralelo. Seu tamanho padrão é de 32 MiB e ele é alocado quando o primeiro ponteiro de fábrica do DirectStorage é recuperado. Se um título usar o DirectStorage apenas para operações de descompactação de memória para memória, o buffer de preparo não é necessário, e SetStagingBufferSize pode ser chamado para definir o tamanho do buffer de preparo como 0. A versão atual do DirectStorage oferece suporte a tamanhos de buffer de preparo de 0, 16, 20, 24, 28 e 32 MiB. Um tamanho menor que o padrão economizará memória para o título, mas pode afetar o desempenho geral de leitura. OBSERVAÇÃO: SetStagingBufferSize só pode ser chamado quando não existir nenhum objeto IDStorageQueueX nem IDStorageFileX; caso contrário, ele gerará um erro.

CancelRequestsWithTag

O DirectStorage oferece suporte ao cancelamento de solicitações. Cada solicitação tem uma marca de 64 bits definida pelo título associada a ela. A finalidade é servir como uma máscara de bits para indicar quais solicitações cancelar. O título fornece uma máscara e um valor para o cancelamento. A fila tentará cancelar todas as solicitações que correspondam ao critério: tag & mask == value. O cancelamento é uma operação de melhor esforço. Dependendo de onde a solicitação está no pipeline, talvez não seja possível cancelá-la. Por exemplo, a solicitação pode estar sendo descompactada pelo hardware, o que não pode ser cancelado. A API retorna imediatamente e não fica bloqueada aguardando o processamento de todas as solicitações canceladas. Os títulos devem aguardar até que uma notificação posterior na fila seja sinalizada antes de liberar os recursos associados às solicitações canceladas. É preciso ter cuidado para evitar adicionar solicitações a uma fila ao mesmo tempo em que CancelRequestsWithTag está sendo chamado. Nesse caso, o comportamento é indefinido. No entanto, as solicitações adicionadas à fila depois que a chamada para CancelRequestsWithTag tiver retornado não serão canceladas, mesmo que correspondam aos critérios. Somente as solicitações enfileiradas anteriormente serão canceladas.

GetErrorEvent/RetrieveErrorRecord

Se uma leitura causar um erro, essa leitura será marcada como concluída. As notificações futuras na fila não são impedidas de ser sinalizadas. A notificação de erros é tratada por meio de um objeto Event associado à fila, que pode ser recuperado com GetErrorEvent. O título pode usar WaitForSingleObject no Event retornado por GetErrorEvent. Se o evento for sinalizado, o título poderá obter o primeiro erro desde a última chamada à função RetrieveErrorRecord chamando a função RetrieveErrorRecord. O registro de erro retornado por RetrieveErrorRecord contém apenas dados da primeira solicitação com falha na fila desde a última chamada a RetrieveErrorRecord. Os dados no registro de erro são indefinidos se o Event de erro não estiver sinalizado ou se os dados já tiverem sido recuperados.

Query

Obtém informações sobre a fila. Isso inclui as estruturas DSTORAGE_QUEUE_DESC ou DSTORAGE_QUEUE_DESC1 usadas na criação da fila, bem como o número de slots vazios e o número de entradas que precisam ser enfileiradas para disparar o envio automático.

Práticas recomendadas

As mesmas práticas recomendadas indicadas para o Win32 também se aplicam ao DirectStorage. Os limites para o desempenho ideal mudaram radicalmente.

Tamanhos de leitura

A recomendação original para um disco rotacional era ler em blocos de pelo menos 128 KiB. O desempenho continua aumentando com tamanhos de bloco maiores. Blocos de 512 KiB alcançavam o melhor desempenho. A ausência de partes móveis no NVMe cria um limite muito menor. O desempenho de leitura dá um grande salto a partir de leituras de 32 KiB e começa a atingir o máximo com 64 KiB. Ler mais do que isso não aumenta o desempenho. Isso significa que é necessário menos esforço para mesclar dados em blocos maiores no pacote para obter o desempenho ideal. Acima de 512 KiB, se a descompactação for usada, prefira mais solicitações menores em paralelo a uma única solicitação enorme. Uma única solicitação enorme forçará a descompactação a ser serializada, enquanto várias solicitações simultâneas permitem que várias unidades de hardware de descompactação trabalhem em paralelo e alcancem a taxa de transferência máxima. Na versão de outubro de 2022 do Microsoft Game Development Kit (GDK), o tamanho máximo de uma única solicitação foi aumentado de 32 MiB como destino para 1 GiB de uso combinado de memória (origem mais destino). Isso é fornecido para facilitar a portabilidade de outras APIs de armazenamento que permitem tamanhos de leitura grandes. Mas a recomendação de tamanho anterior para alcançar a taxa de transferência máxima continua a mesma, porque solicitações grandes não permitem que várias unidades de hardware de descompactação trabalhem em paralelo.

Ordem

Anteriormente, com discos rotacionais, havia um esforço para ordenar o local das leituras no disco. A situação ideal era ler de locais sequenciais no disco. Isso gerava a quantidade mínima de movimento da cabeça do disco, o que eliminava o tempo de busca como fator. Fazer isso podia aumentar o desempenho em uma ordem de grandeza. Havia até benefício em enviar leituras aleatórias classificadas por local e, em alguns casos, elas eram 2x mais rápidas. Ainda é útil ordenar as solicitações de leitura para que sejam o mais sequenciais possível em uma unidade NVMe. A unidade NVMe lê em blocos alinhados a 64 KiB. Por causa disso, pode haver desperdício de largura de banda lendo seções não usadas de um bloco de 64 KiB. Se uma solicitação de leitura for de apenas 4 KiB, haverá 60 KiB de largura de banda desperdiçada. O NVMe reutilizará esses 60 KiB extras para atender a outras solicitações pendentes, se possível. Por exemplo, se você tiver duas leituras sequenciais, uma de 32 KiB seguida por outra de 8 KiB, ainda haverá apenas uma leitura de 64 KiB da unidade.

Gerenciamento de filas

A recomendação com discos rotacionais era ter um tamanho de fila entre 12 e 16. Não havia benefício com uma profundidade de fila maior, e a redução de desempenho decorrente do uso de profundidades de fila menores era significativa. A especificação NVMe diz que uma unidade NVMe deve oferecer suporte a várias filas com uma profundidade de até 65.536 entradas por fila. O DirectStorage oferece suporte a esse requisito, permitindo assim que o título envie milhares de solicitações de uma só vez. Anteriormente, com discos rotacionais, os títulos armazenavam em buffer as solicitações pendentes para manter uma profundidade de fila na faixa de 12 a 16. A recomendação para o DirectStorage é não armazenar solicitações em buffer, mas enfileirá-las assim que forem criadas. Todo o sistema é um pipeline, e o armazenamento em buffer pelo título criará bolhas no pipeline, o que pode prejudicar seriamente o desempenho. Outra recomendação é criar uma fila com capacidade de pelo menos quatro vezes o maior número de solicitações criadas por quadro. Isso deve permitir capacidade suficiente para que novas solicitações possam ser adicionadas sem paralisação, aguardando a conclusão das solicitações existentes.

Gerenciamento de notificações

Em geral, quanto menos solicitações de notificação forem adicionadas à fila, melhor. A recomendação é encontrar um equilíbrio entre as necessidades do título e manter as solicitações de notificação enfileiradas no mínimo. Enfileirar uma notificação após cada solicitação só prejudicará o desempenho geral devido ao aumento da sobrecarga de processamento dessas notificações. Um exemplo pode ser o agrupamento por conteúdo. Por exemplo, texturas SFS, necessidades de terreno (por exemplo, malha e textura) e necessidades de atores (por exemplo, malha, textura e animação). Isso permite que uma única notificação seja vinculada à disponibilidade de todos os ativos necessários para criar um objeto. A escolha entre usar um ID3D12Fence ou uma matriz de status depende das necessidades do título. Os dados precisam ser usados imediatamente pela GPU? É aceitável que o thread de verificação seja suspenso até que as leituras sejam concluídas? A sondagem periódica é suficiente porque os dados só podem ser processados em determinados pontos do quadro?

Considerações

Com a possibilidade de um número muito maior de solicitações em andamento, é preciso ter cuidado para evitar a criação de gargalos em outras partes do título. Os custos para o título gerenciar as solicitações podem rapidamente superar a economia obtida pelo DirectStorage. A recomendação é examinar todo o código de suporte por solicitação e determinar o que pode ser minimizado. Cada solicitação exige a alocação de um novo bloco de memória?
  • O sistema de memória tem sobrecarga para encontrar um novo bloco e atualizar listas internas.
  • Considere reutilizar blocos de memória o máximo possível.
É necessário um bloqueio para atualizar os gerenciadores?
  • Cria mais contenção à medida que mais atualizações são realizadas.
  • Considere evitar bloqueios o máximo possível.
O carregamento especulativo é usado?
  • Há suporte para cancelamento, o que pode levar à criação de mais solicitações.
  • No entanto, é preciso haver memória disponível para a especulação.
  • Considere limites rígidos para os limiares de especulação.

Confira também

DirectStorage Uso e detalhes internos do DirectStorage (tópico sob NDA) Otimizando conteúdo compactado usando DirectStorage e XBTC (tópico sob NDA) Analisando o desempenho do DirectStorage (tópico sob NDA)
Last modified on October 6, 2026