Guia de programação da cadeia de troca de composição
A API de cadeia de troca de composição é uma sucessora espiritual da cadeia de troca DXGI, que permite que os aplicativos renderizem e apresentem conteúdo na tela. Há vários benefícios em usar essa API na cadeia de troca DXGI. É dado ao seu aplicativo um controle mais detalhado em relação ao estado da cadeia de troca, além de mais liberdade quando se trata de como a cadeia de troca é usada. Além disso, a API fornece uma história melhor para o tempo presente preciso.
O que é apresentação?
Apresentação é o conceito de exibir os resultados das operações de desenho na tela. Uma apresentação é uma instância única de apresentação, uma solicitação para mostrar os resultados de uma operação de desenho em um único buffer, na tela. Uma apresentação pode conter atributos adicionais que descrevem como mostrar na tela. Nesta API, uma apresentação também pode ter um tempo alvo, que é um registro de data e hora relacionado ao sistema (um tempo de interrupção) que descreve o momento ideal em que a apresentação deve ser exibida. Seu aplicativo pode usar isso para controlar com mais precisão a taxa em que o conteúdo aparece na tela e para sincronizar apresentações com outros eventos no sistema, como uma faixa de áudio.
No centro da apresentação está a sincronização. Ou seja, as operações de desenho geralmente são realizadas por uma GPU, em oposição à CPU e, como tal, são executadas em uma linha do tempo assíncrona daquela da CPU que emitiu as operações inicialmente. A apresentação é uma operação enviada à GPU que garante que as operações de desenho emitidas anteriormente tenham sido concluídas antes que o buffer seja mostrado na tela.
Seu aplicativo geralmente emitirá muitas apresentações ao longo do tempo e terá várias texturas para selecionar ao emitir apresentações. Seu aplicativo deve usar os mecanismos de sincronização que essa API fornece para garantir que, após desenhar e apresentar um buffer, você não desenhe para esse buffer novamente até que essa apresentação tenha sido exibida e posteriormente substituída por um novo buffer de uma apresentação subsequente. Caso contrário, o conteúdo do buffer que seu aplicativo pretendia apresentar inicialmente pode ser substituído quando a apresentação for exibida na tela.
Modos de apresentação: composição, sobreposição de vários planos e inversão independente
Os buffers apresentados pelo seu aplicativo podem ser exibidos pelo sistema de algumas maneiras diferentes.
A maneira mais simples, que é a padrão, é que a apresentação será enviada ao DWM, e o DWM renderizará um quadro com base no buffer que foi apresentado. Ou seja, há uma cópia (ou mais precisamente, uma renderização 3D) do buffer de apresentação no backbuffer que o DWM envia para a exibição. Esse método de exibição de uma apresentação é chamado de Composição.
Um modo mais eficiente de exibir uma apresentação seria verificar o buffer de apresentação diretamente no hardware e eliminar a cópia que ocorre. Este método de exibir uma apresentação é chamado de exibição direta. Ao lidar com apresentações, o DWM pode decidir programar o hardware para exibir diretamente de um buffer de apresentação, atribuindo o buffer a um plano de sobreposição de vários planos (ou plano MPO, para abreviar) ou invertendo diretamente o buffer para o hardware (conhecido como inversão direta).
Uma maneira ainda mais eficiente de exibir uma apresentação seria fazer com que as apresentações fossem exibidas diretamente pelo kernel gráfico e ignorassem completamente o DWM. Este método de apresentação é conhecido como inversão independente (iflip). A sobreposição de vários planos e o iflip são descritos em Para obter o melhor desempenho, use o modelo de inversão DXGI.
A composição é a mais facilmente suportada, mas também a menos eficiente. A superfície precisa ser especialmente alocada para ser elegível para exibição direta ou iflip, e esse tipo de alocação especial tem requisitos de sistema mais rígidos do que a cadeia de troca de composição. Está disponível apenas em hardware WDDM 3.0 e superior. Como resultado, seu aplicativo pode consultar o suporte da API para apresentação somente de composição, bem como apresentação que se qualifica para exibição direta ou iflip.
Observação
Para que suas superfícies possam aproveitar esses modos de apresentação mais otimizados automaticamente, as superfícies precisam ser alocadas como sendo exibidas diretamente pela GPU. Para superfícies do Direct3D 11, você deve alocar suas superfícies como exibíveis. As superfícies que não são alocadas como exibíveis ainda podem ser compostas pelo compositor do sistema na tela, mas nunca obterão os benefícios do modo de inversão independente.
Fábrica de apresentações, capacidade de verificação e gerenciador de apresentações
O primeiro objeto que seu aplicativo usará fora da API de cadeia de troca de composição é a fábrica de apresentação . A fábrica de apresentação é criada por seu aplicativo e associada a um dispositivo Direct3D que seu aplicativo passa para a chamada para criar e, como tal, tem uma afinidade com o adaptador de vídeo associado a esse dispositivo.
A fábrica de apresentação expõe métodos para verificar se o sistema atual e o dispositivo gráfico são capazes de usar a API de cadeia de troca de composição. Você pode usar métodos de funcionalidade como IPresentationFactory::IsPresentationSupported para verificar o suporte do sistema. Se os métodos de funcionalidade indicarem suporte do sistema para a API, você poderá usar a fábrica de apresentações para criar um gerenciador de apresentações. Esse gerenciador de apresentações é o objeto que você usa para executar funções de apresentação e está associado ao mesmo dispositivo Direct3D e adaptador de vídeo que a fábrica de apresentações que foi usada para criá-lo.
No momento, os requisitos do sistema para usar a API de cadeia de troca de composição são drivers de GPU que oferecem suporte ao WDDM (Windows Device Driver Model) 2.0 e Windows 11 (Build 10.0.22000.194) ou superior. Para usar a API de cadeia de troca de composição da forma mais eficiente (exibição direta e inversão independente ou iflip), os sistemas precisarão de drivers de GPU que oferecem suporte ao WDDM 3.0.
Se o sistema não for capaz de usar a API de cadeia de troca de composição, seu aplicativo precisará ter um caminho de código separado para lidar com a apresentação usando métodos mais antigos, como uma cadeia de troca DXGI.
Registrar buffers de apresentação para apresentar
O gerenciador de apresentações rastreia os buffers que ele pode apresentar. Para apresentar uma textura Direct3D, seu aplicativo deve primeiro criar essa textura com Direct3D e, em seguida, registrá-la no gerenciador de apresentações. Quando uma textura é registrada no gerenciador de apresentação, ela é chamada de buffer de apresentação e, a partir desse ponto, pode ser apresentada à tela por esse gerenciador de apresentação. Seu aplicativo pode adicionar e remover buffers de apresentação conforme desejar, embora haja um número máximo de buffers de apresentação que podem ser adicionados a um único gerenciador de apresentações (atualmente 31). Esses buffers de apresentação também podem ser de tamanhos e formatos variados que entrarão em vigor quando um buffer de apresentação individual for apresentado.
Uma textura pode ser registrada com qualquer número de gerenciadores de apresentação. No entanto, isso não seria considerado uso normal na maioria dos casos e trará cenários de sincronização complicados que seu aplicativo seria responsável por gerenciar.
Definição do conteúdo a ser apresentado
Em geral, os buffers que apresentamos precisam ser associados ao conteúdo em uma árvore visual. Portanto, precisamos definir um tipo de vinculação para que, quando seu aplicativo apresentar problemas, fique claro para que lugar da árvore visual os buffers que estão sendo apresentados devem ir. Chamamos isso de Conteúdo de apresentação vinculativo.
O conteúdo apresentado pode assumir várias formas. Seu aplicativo pode querer apresentar um único buffer a ser exibido ou pode apresentar conteúdo estéreo com buffers para o olho esquerdo e direito e assim por diante. A versão inicial dessa API fornece suporte para apresentar um único buffer para a tela.
Definimos uma superfície de apresentação como uma forma de conteúdo de apresentação na qual um único buffer é apresentado por vez. Uma superfície de apresentação pode ser definida como conteúdo em uma árvore visual e pode mostrar um único buffer de apresentação na tela por vez. As apresentações do gerenciador de apresentações atualizarão o buffer que está sendo mostrado por uma ou mais superfícies de apresentação atomicamente.
O gerenciador de apresentações pode ser usado para criar uma ou mais superfícies de apresentação para um determinado identificador de superfície de composição. Cada identificador de superfície de composição pode ser vinculado a um ou mais elementos visuais em uma árvore visual (por estratégias descritas na documentação da API Windows.UI.Composition e DirectComposition) para definir a relação entre a superfície de apresentação associada e onde ela aparece em sua árvore visual. Seu aplicativo pode atualizar uma ou mais superfícies de apresentação, que são enviadas ao sistema e ocorrem na próxima operação atual.
Observe que o gerenciador de apresentações pode apresentar qualquer buffer de apresentação para qualquer número de superfícies de apresentação que quiser. Não há restrições. No entanto, cabe ao seu aplicativo controlar quais buffers você emitiu e onde, para garantir que você não tente emitir um novo desenho para esse buffer enquanto ele ainda estiver sendo exibido por uma superfície de apresentação.
Aplicar propriedades à superfície de apresentação
Além de especificar quais buffers exibir em uma superfície de apresentação, uma apresentação também pode especificar várias outras propriedades para essa superfície de apresentação. Isso inclui propriedades que definem como a textura de origem será amostrada, incluindo o modo alfa e o espaço de cores, como a textura de origem será transformada e disposta, e quaisquer restrições de exibição ou leitura para conteúdo protegido. Todos esses são expostos como métodos de definição de propriedades em uma superfície de apresentação que pode ser alterada pelo aplicativo e, assim como as atualizações de buffer, terão efeito quando a apresentação do seu aplicativo ocorrer.
Apresentar à superfície de apresentação
Depois que seu aplicativo cria superfícies de apresentação, registra buffers de apresentação e especifica atualizações a serem emitidas durante uma apresentação, você pode aplicar essas propriedades apresentando. Seu aplicativo emite uma apresentação por meio do gerenciador de apresentações. Quando essa apresentação é processada pelo sistema, todas as atualizações são aplicadas atomicamente. Além disso, o aplicativo também pode especificar outras propriedades da apresentação, como o horário ideal para que ela ocorra (o horário alvo da apresentação) e outras propriedades mais raras, como a taxa de conteúdo pretendida, que podem ser usadas para habilitar modos de atualização personalizados no sistema. Como as apresentações podem ser agendadas em um determinado horário, seu aplicativo pode emitir várias apresentações com antecedência. Essas apresentações serão processadas uma por uma quando o horário programado for atingido.
Sincronizar apresentação
Seu aplicativo deve ter certeza de que, ao renderizar para buffers e emitir apresentações, ele selecione um buffer para renderizar que não esteja sendo referenciado por nenhuma outra apresentação anterior pendente, pois isso pode sobrescrever o conteúdo do buffer pretendido por essas apresentações. Além disso, se o aplicativo apresentar problemas de renderização para um buffer que está sendo exibido por uma superfície de apresentação no hardware de exibição, a renderização poderá ser interrompida indefinidamente, porque o Direct3D não permite a renderização do buffer frontal.
A API de cadeia de troca de composição fornece alguns mecanismos diferentes para permitir que o aplicativo pratique a sincronização adequada dos buffers que ele apresentou.
Diz-se que um buffer está disponível se não houver apresentações pendentes que façam referência a ele e se ele não estiver sendo exibido pelo sistema no momento. Caso contrário, ele não estará disponível. A API fornece um evento para cada buffer de apresentação que indica se o buffer está disponível. Esse é o método mais simples de sincronização para seu aplicativo usar. Antes de desenhar em um buffer e apresentá-lo, seu aplicativo pode garantir que seu evento disponível seja sinalizado. O evento disponível de um buffer específico torna-se não sinalizado no momento em que é vinculado a uma superfície de apresentação na API e permanece não sinalizado até que a apresentação seja desativada.
Em segundo lugar, o gerenciador de apresentações rastreia um única limite de desativação de apresentações para comunicar ao aplicativo quais apresentações foram concluídas. O valor do limite corresponde ao identificador de apresentação da última apresentação que iniciou a fase de desativação do seu ciclo de vida, conforme discutido na seção de ciclo de vida abaixo. Quando uma apresentação entra nessa fase, é seguro supor que todos os buffers que foram substituídos por apresentações subsequentes podem ser reutilizados.
Esse método de sincronização é mais avançado, mas permite maior controle sobre a limitação do fluxo de trabalho e é mais informativo sobre o estado do sistema em relação à profundidade da fila de apresentação atual. Para obter uma visão geral do ciclo de vida de uma apresentação, consulte a seção abaixo.
Ciclo de vida de uma apresentação
As apresentações do gerenciador de apresentações são enfileiradas no sistema como parte de sua fila de apresentações. Os processos do sistema são apresentados em ordem de fila. Além disso, cada apresentação tem um identificador de apresentação associado exclusivo (para o gerenciador de apresentações), que é um valor crescente atribuído a uma apresentação, começando em 1 para a primeira apresentação e incrementando em 1 para cada apresentação subsequente. Esse identificador de apresentação é usado em várias partes da API, como primitivas de sincronização e estatísticas de apresentação, para se referir a essa apresentação específica.
Cada apresentação que seu aplicativo emite segue um ciclo de vida específico, conforme descrito aqui.
Depois que seu aplicativo configurar as alterações a serem feitas como parte de uma apresentação, ele usará o gerenciador de apresentações para realmente emitir a apresentação. Neste ponto, diz-se que a apresentação está pendente.
Uma vez pendente, uma apresentação ficará na fila de apresentações do gerente de apresentações, onde ficará até que uma das duas coisas aconteça.
- A apresentação é cancelada. O gerenciador de apresentações permite que seu aplicativo cancele apresentações emitidas anteriormente. Se isso acontecer, diz-se que a apresentação foi cancelada e, em seguida, torna-se imediatamente desativada. Nessa transição, os eventos de buffer disponíveis associados para a apresentação cancelada serão atualizados. No entanto, o limite de apresentações desativadas não será sinalizado, pois a apresentação exibida anteriormente (antes das apresentações que foram canceladas) permanecerá exibida. Por isso, o aplicativo não pode usar o limite de desativação de apresentações para determinar quais apresentações foram canceladas. Em vez disso, você deve aprender com a estatística de status de apresentação que retorna para cada apresentação cancelada. Sugerimos que seu aplicativo use eventos de buffer disponível para encontrar um buffer disponível para apresentar após um cancelamento. Assim que a apresentação for exibida, a apresentação anterior iniciará o processo de desativação e atualizará o limite de desativação de apresentações.
- Se não for cancelada, a apresentação eventualmente fica pronta para ser processada. Para estar pronta, duas condições principais devem ser atendidas.
- Todo o trabalho de desenho emitido para o contexto do Direct3D antes que a apresentação fosse chamada deve ser concluído. Isso garante que o buffer não seja exibido antes que o desenho do aplicativo seja concluído.
- Se um tempo alvo de apresentação foi especificado, então o tempo atual relacionado ao sistema que esperamos poder mostrar a apresentação atende ao tempo alvo solicitado que seu aplicativo aplicou à apresentação.
Quando o sistema decidir encontrar uma apresentação para mostrar na tela, ele escolherá a última apresentação que ficou pronta para ser exibida. Se houver várias apresentações prontas, todas, exceto a mais recente (ou seja, a apresentação com o maior identificador de apresentações), serão ignoradas e entrarão imediatamente no estado de desativadas, momento em que seus eventos de buffer disponíveis serão sinalizados, mas o limite de desativação de apresentações não será sinalizado, pois a apresentação ignorada não faz a transição do estado exibida.
Quando a apresentação pronta é escolhida para ser exibida, o sistema começa a fazer o trabalho para mostrá-la na tela. Isso pode significar renderizar o buffer como parte de um quadro DWM e, em seguida, solicitar que o hardware mostre esse quadro na tela ou pode significar enviar o buffer diretamente para o hardware de verificação no caso de iflip. Depois que isso ocorre, diz-se que a apresentação está na fila. Em um nível alto, isso significa que está a caminho de ser exibida.
Quando o hardware chega a exibir a apresentação, diz-se que essa apresentação é exibida. Ela ficará visível na tela até que uma apresentação subsequente chegue e a substitua.
Quando uma apresentação subsequente entra na fila, sabemos que o hardware acabará interrompendo a exibição da apresentação atual. Neste ponto, diz-se que a apresentação está desativada.
Quando essa apresentação subsequente é exibida, diz-se que a apresentação atual está desativada.
O gerenciador de apresentações expõe um limite de desativação de apresentações, que é sinalizado para o identificador de apresentações de cada apresentação quando ela entra no estado de desativação . Esse sinal indica ao aplicativo que é seguro emitir trabalho de renderização para os buffers associados a essa apresentação sem corromper uma apresentação anterior. Se o aplicativo apresentar problemas ao renderizar o trabalho durante o estado de desativação da apresentação, o trabalho de renderização será enfileirado até que a apresentação entre no estado de desativação, quando então será executado. Se o trabalho de renderização for emitido após a apresentação ser desativada, ele será executado imediatamente.
Veja a seguir um diagrama dessa alteração de estado.
Diagrama de buffers, superfícies e apresentações
Veja a seguir um diagrama que relaciona o gerenciador de apresentações, buffers de apresentação, superfícies de apresentação, apresentações e atualizações.
Este diagrama mostra um gerenciador de apresentações (com duas superfícies de apresentação e três buffers de apresentação) que teve duas apresentações emitidas até agora: a primeira apresentação exibiu o buffer 1 na superfície 1 e o buffer 2 na superfície 2. A segunda apresentação atualizou a superfície 2 para mostrar o buffer de apresentação 3 e não alterou a associação da superfície 1. Depois que a apresentação 2 for exibida, a superfície 1 mostrará o buffer 1 e a superfície 2 mostrará o buffer 3, que pode ser visto no estado atual dos objetos no gerenciador de apresentações. Cada apresentação na fila entrará em vigor quando for processada no sistema.
Observação
Como a apresentação 2 não alterou o buffer da superfície 1, a superfície 1 foi deixada vinculada ao buffer 1 da apresentação anterior. Nesse sentido, há uma referência "implícita" no buffer 1 na apresentação 2, uma vez que a superfície 1 permanecerá vinculada ao buffer 1 depois que a apresentação 2 for exibida.
Adicionar superfícies de apresentação à árvore visual
Superfícies de apresentação são conteúdo que existe como parte de uma árvore visual de composição. Cada superfície de apresentação é vinculada a um identificador de superfície de composição. No Windows.UI.Composition, um pincel de superfície pode ser criado para um identificador de superfície de composição preexistente e associado a um visual de sprite. No DirectComposition, uma superfície de composição pode ser criada a partir de um identificador de superfície de composição preexistente e associada como conteúdo a um visual. Consulte a respectiva documentação de cada API para obter mais informações.
APIs como o Windows Media Foundation, criadas para usar essa API, expõem identificadores de superfície de composição que serão pré-associados a uma superfície de apresentação. Um aplicativo também pode criar seu próprio identificador de superfície de composição para associar posteriormente a uma superfície de apresentação e adicionar a uma árvore visual chamando DCompositionCreateSurfaceHandle.
Ler estatísticas de apresentação
A API de cadeia de troca de composição expõe estatísticas de apresentação, que descrevem várias informações sobre como uma apresentação específica foi processada. As informações geralmente podem descrever como uma superfície de apresentação foi usada em um quadro DWM, o ponto no tempo em que ela apareceu, se foi mostrada e assim por diante.
Existem diferentes tipos de estatísticas de apresentação e elas são projetadas para serem expansíveis em versões futuras da API. Um aplicativo usa o gerenciador de apresentações para se registrar para receber tipos de estatísticas nos quais está interessado. Essas estatísticas são enviadas para a fila de estatísticas do gerenciador de apresentações. O gerenciador de apresentações expõe um evento de estatísticas disponíveis para aplicativos, que é um identificador de evento que indica quando a fila de estatísticas tem itens de estatísticas disponíveis para serem lidos. Quando isso acontecer, seu aplicativo poderá remover o primeiro item de estatísticas da fila, lê-lo e processá-lo. O gerenciador de apresentações redefinirá o evento de estatísticas disponíveis quando seu aplicativo tiver lido todas as estatísticas atualmente na fila. Um aplicativo lerá e processará estatísticas normalmente em um loop até que o evento de estatísticas disponíveis seja redefinido. Será comum que seu aplicativo processe essa fila de estatísticas no mesmo loop de trabalho que você usa para emitir apresentações. O padrão de uso sugerido é priorizar o processamento de estatísticas em vez de emitir novas apresentações, para garantir que a fila não estoure.
A fila tem um número máximo de estatísticas que ela rastreará, que será da ordem de 512-1024 estatísticas. A profundidade máxima da fila deve ser suficiente para armazenar ~5 segundos de estatísticas em casos normais. Se a fila de estatísticas ficar cheia e mais estatísticas forem relatadas, a política é que as estatísticas mais antigas sejam desativadas para liberar espaço.