Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O impacto no desempenho de um driver de áudio no sistema pode ser significativamente reduzido seguindo estes princípios gerais:
Minimize o código executado durante a operação normal.
Execute o código somente quando necessário.
Considere o consumo total de recursos do sistema (não apenas o carregamento da CPU).
Otimize o código para velocidade e tamanho.
Além disso, os drivers de miniporto WavePci devem resolver vários problemas de desempenho específicos para dispositivos de áudio. A discussão a seguir lida principalmente com problemas de renderização de áudio, embora algumas das técnicas sugeridas também se apliquem à captura de áudio.
Mecanismos de manutenção de fluxo
Antes de discutir as otimizações de desempenho, algumas informações básicas são necessárias para entender os mecanismos WavePci para atender fluxos de dados.
Ao processar uma renderização de onda ou um fluxo de captura, um dispositivo de áudio requer manutenção em intervalos regulares pelo driver de miniporto. Quando novos mapeamentos estão disponíveis para um fluxo, o driver adiciona esses mapeamentos à fila de DMA do fluxo. O driver também remove da fila quaisquer mapeamentos que já tenham sido processados. Para obter informações sobre mapeamentos, consulte WavePci Latency.
Para executar a manutenção, o driver de miniporto fornece uma chamada de procedimento adiado (DPC) ou uma rotina de serviço de interrupção (ISR), dependendo se o intervalo é definido por um temporizador do sistema ou por interrupções controladas por DMA. No último caso, o hardware de DMA normalmente dispara uma interrupção sempre que termina de transferir alguma quantidade de dados de fluxo.
Cada vez que o DPC ou ISR é executado, ele determina quais fluxos exigem manutenção. O DPC ou ISR fornece um fluxo chamando o método IPortWavePci::Notify . Esse método usa como parâmetro de chamada o grupo de serviços do fluxo, que é um objeto do tipo IServiceGroup. O método Notify chama o método RequestService do grupo de serviços (consulte IServiceSink::RequestService).
Um objeto de grupo de serviços contém um grupo de coletores de serviço, que são objetos do tipo IServiceSink. IServiceGroup é derivado de IServiceSink e ambas as interfaces têm métodos RequestService . Quando o método Notify chama o método RequestService do grupo de serviços, o grupo de serviços responde chamando o método RequestService em cada coletor de serviço no grupo.
O grupo de serviços de um fluxo contém pelo menos um coletor de serviço, que o driver de porta adiciona ao grupo de serviços imediatamente após a criação do fluxo. O driver de porta chama o método IMiniportWavePci::NewStream do miniport driver para obter um ponteiro para o grupo de serviços. O método RequestService do coletor de serviço é a rotina de serviço específica do fluxo do driver de porta. Essa rotina faz o seguinte:
Chama o método IMiniportWavePciStream::Service do driver de miniport.
Dispara quaisquer posições ou eventos de relógio que estejam recém-pendentes no stream desde a última vez em que a rotina de serviço foi executada.
Conforme discutido em Eventos KS, os clientes podem se registrar para serem notificados quando um fluxo atinge uma posição específica ou quando um relógio atinge um carimbo de data/hora específico. O método NewStream tem a opção de não fornecer um grupo de serviços, nesse caso, o driver de porta configura seu próprio temporizador para marcar os intervalos entre chamadas à sua rotina de serviço.
Assim como o método NewStream, o método IMiniportWavePci::Init do driver miniport também gera um ponteiro para um grupo de serviços. Após a chamada Init, o driver de porta adiciona seu ponto de serviço ao grupo de serviços. Esse coletor de serviço específico contém a rotina de serviço do filtro como um todo. (O parágrafo anterior descreve o coletor de serviço para o fluxo associado a um pino no filtro.) Essa rotina de serviço chama o método IMiniportWavePci::Service do driver de miniport. A rotina de serviço é executada sempre que o DPC ou ISR chama Notify com o grupo de serviços para o filtro. O método Init tem a opção de não fornecer um grupo de serviços, caso em que o driver de porta nunca chama sua rotina de serviço de filtro.
Interrupções de hardware
Alguns drivers de miniporto geram muitas interrupções de hardware ou interrupções insuficientes. Em alguns dispositivos de renderização WavePci com aceleração de hardware do DirectSound, uma interrupção de hardware ocorre somente quando o fornecimento de mapeamentos está quase esgotado e o mecanismo de renderização corre o risco de passar fome. Em outros dispositivos WavePci acelerados por hardware, ocorre uma interrupção de hardware em cada conclusão de mapeamento individual ou em algum outro intervalo relativamente pequeno. Nesse caso, o ISR frequentemente descobre que tem pouco a fazer, mas cada interrupção ainda consome recursos do sistema com trocas de registro e recarregamentos de cache. A primeira etapa para melhorar o desempenho do driver é reduzir o número de interrupções o máximo possível sem correr o risco de passar fome. Depois de eliminar interrupções desnecessárias, ganhos de desempenho adicionais podem ser obtidos projetando o ISR para serem executados com mais eficiência.
Em alguns drivers, os ISRs perdem tempo chamando o método Notify de um fluxo sempre que ocorre uma interrupção de hardware, independentemente de o fluxo estar realmente em execução. Se nenhum fluxo estiver no estado RUN, o DMA estará inativo e qualquer tempo gasto tentando adquirir mapeamentos, liberar mapeamentos ou verificar novos eventos em qualquer fluxo será desperdiçado. Em um driver eficiente, o ISR verifica se um fluxo está em execução antes de chamar o método Notify do fluxo.
No entanto, um driver com esse tipo de ISR precisa garantir que todos os eventos pendentes em um fluxo sejam disparados quando o fluxo sair do estado RUN. Caso contrário, os eventos poderão ser atrasados ou perdidos. Esse problema surge somente durante transições RUN-to-PAUSE em sistemas operacionais mais antigos que o Microsoft Windows XP. No Windows XP e posterior, o driver de porta sinaliza automaticamente todos os eventos de posição pendentes imediatamente quando um fluxo altera o estado de RUN para PAUSE. Nos sistemas operacionais mais antigos, no entanto, o miniport driver é responsável por disparar quaisquer eventos pendentes chamando a função Notify imediatamente após a pausa do stream. Para obter mais informações, consulte PAUSE/ACQUIRE Optimizations abaixo.
Um controlador de miniporto WavePci típico gerencia uma única corrente de reprodução do driver KMixer do sistema. A implementação atual do KMixer usa um mínimo de três IRPs de mapeamento para armazenar em buffer um fluxo de reprodução. Cada IRP contém armazenamento de buffer suficiente para cerca de 10 milissegundos de áudio. Se o driver de miniporto disparar uma interrupção de hardware sempre que o controlador de DMA terminar com o mapeamento final em um IRP, as interrupções deverão ocorrer em intervalos regulares de 10 milissegundos, o que é frequente o suficiente para impedir que a fila de DMA passe fome.
DPCs de temporizador
Se um driver gerencia quaisquer fluxos DirectSound acelerados por hardware, ele deve usar um DPC de temporizador (consulte Objetos do Temporizador e DPCs) em vez de interrupções de hardware controladas por DMA. Equivalentemente, um dispositivo WavePci em um cartão PCI com um temporizador a bordo pode usar uma interrupção de hardware controlada por temporizador em vez de um DPC.
No caso de um buffer DirectSound, todo o buffer pode ser anexado a um único IRP. Se o buffer for grande e o miniport driver agendar uma interrupção de hardware somente quando chegar ao final do buffer, interrupções sucessivas poderão ocorrer tão distantes que a fila de DMA fica sem dados. Além disso, se o driver estiver gerenciando um grande número de fluxos DirectSound acelerados por hardware e cada fluxo gerar suas próprias interrupções, o impacto cumulativo de todas as interrupções poderá prejudicar o desempenho do sistema. Nessas circunstâncias, o driver de miniporto deve evitar o uso de interrupções de hardware para agendar o atendimento de fluxos individuais. Em vez disso, ele deve atender a todos os fluxos em um único DPC agendado para ser executado em intervalos gerados pelo temporizador regular.
Ao definir o intervalo de temporizador como 10 milissegundos, o intervalo entre execuções de DPC sucessivas é semelhante ao descrito anteriormente para a interrupção de hardware no caso de um único fluxo de reprodução do KMixer. Assim, o DPC pode lidar com o fluxo de reprodução do KMixer, além de fluxos DirectSound acelerados por hardware.
Quando o último fluxo sai do estado RUN, o driver de miniport deve desabilitar o DPC do temporizador para evitar o desperdício de ciclos de CPU do sistema. Imediatamente após desabilitar o DPC, o driver deve garantir que todos os eventos de relógio ou posição pendentes em fluxos em execução anteriormente sejam liberados. No Windows 98/Me e no Windows 2000, o driver deve chamar Notify para disparar quaisquer eventos pendentes nos fluxos que estão sendo pausados. No Windows XP e posterior, o sistema operacional dispara eventos pendentes automaticamente quando um fluxo sai do estado RUN, sem a necessidade de intervenção do driver de miniporto.
Otimizações PAUSE/ACQUIRE
No Windows 98/Me e no Windows 2000, a rotina de serviço de fluxo do driver de porta WavePci (o método RequestService ) sempre gera uma chamada para o método IMiniportWavePciStream::Service do driver de miniport, independentemente de o fluxo estiver no estado RUN. Nesses sistemas operacionais, o método Serviço deve verificar se o fluxo está em execução antes de passar um tempo fazendo o trabalho real. (No entanto, se o DPC ou ISR do driver de miniport já tiver sido otimizado para chamar Notificar somente para fluxos em execução, adicionar essa verificação ao método de serviço poderá ser redundante.)
No Windows XP e posterior, essa otimização é desnecessária porque o método Notify chama o método Service apenas em fluxos em execução.
Usando a interface IPreFetchOffset
Os usuários do DirectSound estão familiarizados com os conceitos duplos do cursor de reprodução e do cursor de gravação. O cursor de reprodução indica a posição no fluxo dos dados que estão sendo emitidos do dispositivo (a melhor estimativa do driver da amostra atualmente no DAC). A posição de gravação é a posição no fluxo do próximo local seguro onde o cliente pode gravar dados adicionais. Para WavePci, a suposição padrão é que o cursor de gravação esteja posicionado no final do último mapeamento solicitado. Se o driver de miniporto tiver adquirido um grande número de mapeamentos pendentes, o deslocamento entre o cursor de reprodução e o cursor de gravação poderá ser muito grande, grande o suficiente para falhar em determinados testes de posição de áudio WHQL. No Windows XP e posterior, a interface IPreFetchOffset resolve esses problemas.
O driver de miniporto usa IPreFetchOffset para especificar as características de pré-busca do hardware master do barramento, que são determinadas em grande parte pelo tamanho FIFO do hardware. O subsistema de áudio usa esses dados para definir um deslocamento constante entre o cursor de reprodução e o cursor de gravação. Esse deslocamento constante, que pode ser significativamente menor do que o deslocamento padrão, aproveita o fato de que os dados podem ser gravados em um mapeamento mesmo após o mapeamento ter sido entregue ao hardware, desde que o cursor de reprodução esteja longe o suficiente do local em que os dados são gravados. (Essa instrução pressupõe que o driver não copie ou manipule os dados em mapeamentos.) Um deslocamento típico pode estar na ordem de 64 amostras, dependendo do design do mecanismo. Com um deslocamento tão pequeno, um driver WavePci pode ser totalmente responsivo e funcional enquanto ainda solicita um grande número de mapeamentos.
Observe que o DirectSound atualmente adiciona uma folga no cursor de gravação de um pino acelerado por hardware de 10 milissegundos.
Para obter mais informações, consulte Deslocamentos de Pré-busca.
Processando dados em mapeamentos
Evite que o driver de hardware interaja com os dados nos mapeamentos, se possível. Qualquer processamento de software de dados contidos em mapeamentos deve ser dividido em um filtro de software separado do driver de hardware. Fazer com que um driver de hardware execute esse processamento reduz sua eficiência e cria problemas de latência.
Um driver de hardware deve se esforçar para ser transparente sobre seus recursos reais de hardware. O driver nunca deve alegar fornecer suporte de hardware para uma transformação de dados que seja realmente executada no software.
Primitivos de sincronização
É menos provável que um driver tenha problemas de deadlock ou desempenho agora e no futuro se seu código for projetado para evitar ser bloqueado sempre que possível. Especificamente, o thread de execução de um driver deve se esforçar para ser executado até a conclusão sem o risco de parar enquanto aguarda outro thread ou recurso. Por exemplo, os threads de driver podem usar as funções InterlockedXxx (por exemplo, ver InterlockedIncrement) para coordenar seus acessos a determinados recursos compartilhados sem risco de bloqueio.
Embora sejam técnicas poderosas, talvez você não consiga remover com segurança todos os spin locks, mutexes e outros primitivos de sincronização bloqueantes do caminho de execução. Use as funçõesXxx intertravadas de forma criteriosa, sabendo que uma espera indefinida pode causar escassez de dados.
Acima de tudo, não crie primitivos de sincronização personalizados. Os primitivos integrados do Windows (como mutexes, bloqueios de rotação, etc.) provavelmente serão modificados conforme necessário para dar suporte a novos recursos do agendador no futuro. É quase certo que um driver que utiliza constructos personalizados não funcionará no futuro.
IPinCount Interface
No Windows XP e posterior, a interface IPinCount fornece uma maneira para um driver de miniporto levar em conta com mais precisão os recursos de hardware que são consumidos alocando um pino. Ao chamar o método IPinCount::PinCount do driver de miniport, o driver de porta faz o seguinte:
Expõe as contagens atuais de pinos do filtro (como mantido pelo driver de portas) para o miniport driver.
Dá ao driver de porta-mínima a oportunidade de revisar as contagens de pinos para refletir dinamicamente a disponibilidade atual dos recursos de hardware.
Para alguns dispositivos de áudio, fluxos de onda com atributos diferentes (3D, estéreo/mono e assim por diante) também podem ter "pesos" diferentes em termos de quantos recursos de hardware eles consomem. Ao abrir ou fechar um fluxo "leve", o driver incrementa ou diminui a contagem de pinos disponíveis por um. Ao abrir um fluxo de dados "pesado", no entanto, o driver de miniporto pode precisar diminuir a contagem de pinos disponível em dois, em vez de um, para indicar com mais precisão o número de pinos que podem ser criados com os recursos restantes.
O processo é invertido quando um fluxo pesado é fechado. A contagem de pinos disponíveis pode aumentar em mais de um para refletir o fato de que dois ou mais fluxos leves podem ser criados com base nos recursos recém-liberados.