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
- Os white papers Maximizando o desempenho de arquivos no XBOX Series X|S (tópico sob NDA) e Maximizando o desempenho de arquivos no XBOX One (tópico sob NDA) abordam isso em detalhes.
-
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.
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 aBCPACK 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 objetoEvent 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.
- Cria um objeto
-
CreateQueue
- Cria um objeto
IDStorageQueueX. Usado para criar solicitações de leitura.
- Cria um objeto
-
CreateStatusArray
- Cria um objeto
IDStorageStatusArray, que gerencia os sinalizadores de status de conclusão.
- Cria um objeto
-
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
ID3DFenceouIDStorageStatusArray.
- O pipeline de recursos subjacente está cheio durante
-
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 interfaceIDStorageFactoryX1 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 interfaceIDStorageFactoryX2 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.
- Cria um objeto
IDStorageFileX
Todos os arquivos precisam ser abertos inicialmente pelo DirectStorage por meio do objetoIDStorageFactoryX. 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 objetoIDStorageQueueX.
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).
IDStorageQueueX1
A interfaceIDStorageQueueX1 estende a interface IDStorageQueueX com o
método EnqueueSetEvent.
IDStorageQueueX2
A interfaceIDStorageQueueX2 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 à interfaceReadFile 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.
- Este grupo é usado quando
-
Source
- Este grupo é usado quando
Options.SourceTypeéDSTORAGE_REQUEST_SOURCE_MEMORYeOptions.SourceIsPhysicalPageséFALSE. - O buffer de memória que contém os dados a serem descompactados.
- Este grupo é usado quando
-
SourcePageArray e SourcePageOffset
- Este grupo é usado quando
Options.SourceTypeéDSTORAGE_REQUEST_SOURCE_MEMORYeOptions.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.
- Este grupo é usado quando
- Tamanho, em bytes, dos dados de origem a serem lidos, seja de um buffer de memória ou de um arquivo.
- Quando a descompactação
zlibeBCPACKestã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.
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.
- Este grupo é usado quando
-
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.
- Este grupo é usado quando
- 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.
- Uma marca arbitrária de 64 bits definida pelo título.
- Essa marca é usada como máscara para solicitações de cancelamento.
- 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 caracteresNameprecisa estar acessível durante todo o tempo de vida da solicitação.
- ZlibDecompress
- Indica que os dados precisam ser descompactados usando o padrão de descompactação RFC 1950.
- BcpackMode
- Indica qual modo de
BCPACKdeve ser usado para descompactar os dados. - None é uma opção válida e significa que os dados não estão compactados com
BCPACK.
- Indica qual modo de
- 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/SourcePageArrayeSourcePageOffset, ou ter origem em arquivo e, portanto, ter as propriedadesFile/FileOffset.
- Uma solicitação pode ter origem em memória e, portanto, ter as propriedades
- 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 objetoID3D12Fence, 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- Este é o padrão de compactação IETF RFC 1950.
-
BCPackBCPacké 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
Swizzlee de embaralhamento podem oferecer otimizações adicionais em pipelines de conteúdo.
- Os modos
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 objetoIDStorageQueueX 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 objetoEvent 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 umID3D12Fence 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.
- Cria mais contenção à medida que mais atualizações são realizadas.
- Considere evitar bloqueios o máximo possível.
- 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.
