Framework de sincronização

O framework de sincronização descreve explicitamente as dependências entre diferentes operações assíncronas no sistema gráfico Android. A estrutura fornece uma API que permite que os componentes indiquem quando os buffers são liberados. O framework também permite que os primitivos de sincronização sejam transmitidos entre drivers do kernel ao espaço do usuário e entre os próprios processos do espaço do usuário.

Por exemplo, um aplicativo pode enfileirar o trabalho a ser executado na GPU. A GPU começa a desenhar essa imagem. Embora a imagem não tenha sido desenhada na memória, o ponteiro do buffer é passado para a janela compositor junto com um limite que indica quando o trabalho da GPU será concluir. O compositor da janela começa o processamento antecipadamente e passa o trabalho para o controlador de tela. Da mesma forma, a CPU funciona é feito com antecedência. Quando a GPU terminar, o controlador de exibição exibe a imagem imediatamente.

O framework de sincronização também permite que implementadores aproveitem os recursos de sincronização nos próprios componentes de hardware. Por fim, o fornece visibilidade do pipeline de gráficos para ajudar na depuração.

Sincronização explícita

A sincronização explícita permite que produtores e consumidores de buffers gráficos para sinalizar quando terminam de usar um buffer. A sincronização explícita é implementado no espaço do kernel.

Os benefícios da sincronização explícita incluem:

  • Menos variação de comportamento entre os dispositivos
  • Suporte aprimorado para depuração
  • Métricas de teste aprimoradas

O framework de sincronização tem três tipos de objetos:

  • sync_timeline
  • sync_pt
  • sync_fence

sync_timeline

sync_timeline é uma linha do tempo monotonicamente crescente que os fornecedores devem implementar para cada instância de driver, como contexto de GL, ou controle de tela em 2D. sync_timeline de contagens jobs enviados ao kernel para um determinado hardware. sync_timeline fornece garantias sobre a ordem das operações e permite implementações específicas de hardware.

Siga estas diretrizes ao implementar sync_timeline:

  • Dê nomes úteis para todos os motoristas, cronogramas e limites para simplificar depuração.
  • Implementar timeline_value_str e pt_value_str em linhas do tempo para tornar a saída de depuração mais legível.
  • Implemente o preenchimento driver_data para fornecer bibliotecas de espaço do usuário. como a biblioteca GL, acesso a dados particulares da linha do tempo, se quiser. data_driver permite que os fornecedores transmitam informações sobre os dados imutáveis sync_fence e sync_pts para criar linhas de comando. com base neles.
  • Não permita que o espaço do usuário crie ou sinalize um limite explicitamente. explicitamente a criação de sinais/cercas resulta em um ataque de negação de serviço interrompe a funcionalidade do pipeline.
  • Não acessar sync_timeline, sync_pt ou sync_fence explicitamente. A API fornece todos os .

sinc_pt

sync_pt é um valor ou ponto único em uma sync_timeline. Um ponto tem três estados: ativo, sinalizado e erro. Os pontos começam no estado ativo e passar para os estados sinalizados ou de erro. Por exemplo, quando uma imagem consumidor não precisa mais de um buffer, um sync_pt é sinalizado para que os produtores de imagens saibam que não há problema em gravar no buffer novamente.

sincronia_de_sincronia

sync_fence é uma coleção de valores de sync_pt. que muitas vezes têm sync_timeline pais diferentes (como na tela e GPU). sync_fence, sync_pt e sync_timeline são os principais primitivos que os drivers e o espaço do usuário usam para comunicar suas dependências. Quando uma cerca fica sinalizada, comandos emitidos antes do limite têm a garantia de serem concluídos, porque O driver do kernel ou bloco de hardware executa comandos em ordem.

O framework de sincronização permite que vários consumidores ou produtores sinalizem terminar de usar um buffer, comunicando as informações de dependência com uma função . Os limites são respaldados por um descritor de arquivo e são passados de espaço do kernel para o espaço do usuário. Por exemplo, uma cerca pode ter dois Valores de sync_pt que significam quando dois consumidores de imagem diferentes terminam e a leitura de um buffer. Quando há sinalização, os produtores de imagens sabem que que os consumidores concluem o consumo.

Limites, como valores sync_pt, iniciam como ativos e mudam de estado com base em o estado de seus pontos. Se todos os valores sync_pt forem sinalizados, o sync_fence passa a ser sinalizado. Se um sync_pt cair em um estado de erro, todo o sync_fence terá um estado de erro.

A associação em um sync_fence é imutável depois que o limite é criados. Para conseguir mais de um ponto em uma cerca, uma mesclagem é conduzida em que os pontos de duas cercas distintas são adicionados a uma terceira cerca. Se um desses pontos tiver sido sinalizado na cerca de origem e o outro não, a terceira fronteira também não estará em um estado sinalizado.

Para implementar a sincronização explícita, forneça o seguinte:

  • Subsistema do espaço do kernel que implementa o framework de sincronização para um driver de hardware específico. Motoristas que precisam prestar atenção em cercas estão em geral, tudo que acessa ou se comunica com o Hardware Composer. Os principais arquivos incluem:
    • Implementação principal:
      • kernel/common/include/linux/sync.h
      • kernel/common/drivers/base/sync.c
    • Documentação em kernel/common/Documentation/sync.txt
    • para se comunicar com o espaço do kernel em platform/system/core/libsync
  • O fornecedor precisa fornecer o serviço de sincronização limites como parâmetros para validateDisplay() e presentDisplay() na HAL.
  • Duas extensões GL relacionadas a limites (EGL_ANDROID_native_fence_sync e EGL_ANDROID_wait_sync) e suporte de isolamento em gráficos motorista.

Estudo de caso: implemente um driver de exibição

Para usar a API que oferece suporte à função de sincronização, desenvolver um driver de exibição que tenha uma função de buffer de exibição. Antes do de sincronização existente, essa função receberia dma-buf objetos, coloque esses buffers na tela e faça o bloqueio enquanto o buffer estava visível. Por exemplo:

/*
 * assumes buffer is ready to be displayed.  returns when buffer is no longer on
 * screen.
 */
void display_buffer(struct dma_buf *buffer);

Com o framework de sincronização, a função display_buffer é mais complexo. Ao colocar um buffer em exibição, ele é associado com um limite que indica quando o buffer estará pronto. Você pode enfileirar e inicie o trabalho depois que a cerca for liberada.

Fazer isso não bloqueia nada. Você retorna imediatamente sua própria cerca, o que garante quando o buffer sairá da tela. Conforme você coloca os buffers em fila, o kernel lista dependências com o framework de sincronização:

/*
 * displays buffer when fence is signaled.  returns immediately with a fence
 * that signals when buffer is no longer displayed.
 */
struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence
*fence);

Integração da sincronização

Esta seção explica como integrar o framework de sincronização do espaço kernel ao do espaço do usuário do framework do Android e os motivadores que precisam se comunicar entre si. Os objetos do espaço do kernel são representados como descritores de arquivo em espaço do usuário.

Convenções de integração

Siga as convenções de interface da HAL do Android:

  • Se a API fornecer um descritor de arquivo que se refira a um sync_pt, o driver do fornecedor ou a HAL que usa a API fechar o descritor de arquivo.
  • Se o driver do fornecedor ou a HAL passar um descritor de arquivo que contenha um sync_pt a uma função da API, o driver do fornecedor ou a HAL não podem feche o descritor do arquivo.
  • Para continuar usando o descritor de arquivo de limite, o driver do fornecedor ou o A HAL precisa duplicar o descritor.

Um objeto de limite é renomeado sempre que passa pela BufferQueue. O suporte ao kernel de kernel permite que os limites tenham strings para nomes. Assim, a sincronização usa o nome da janela e o índice do buffer que estão na fila para nomear o limite, como SurfaceView:0. Isso é útil na depuração para identificar a origem de um impasse, pois os nomes aparecem na saída de /d/sync e relatórios de bugs.

Integração com ANativeWindow

A ANativeWindow reconhece cercas. dequeueBuffer, queueBuffer e cancelBuffer têm parâmetros de limite.

Integração com OpenGL ES

A integração da sincronização do OpenGL ES depende de duas extensões EGL:

  • O EGL_ANDROID_native_fence_sync oferece uma maneira de unem ou criam descritores de arquivo de limite nativo do Android objetos EGLSyncKHR.
  • EGL_ANDROID_wait_sync permite pausas na GPU em vez de no lado da CPU, fazendo com que a GPU aguarde por EGLSyncKHR. A A extensão EGL_ANDROID_wait_sync é igual à EGL_KHR_wait_sync.

Para usar essas extensões de forma independente, implemente a EGL_ANDROID_native_fence_sync com as extensões associadas suporte ao kernel. Em seguida, ative a EGL_ANDROID_wait_sync no driver. O EGL_ANDROID_native_fence_sync extensão consiste em um objeto EGLSyncKHR de limite nativo distinto não é válido. Como resultado, as extensões que se aplicam a EGLSyncKHR existentes tipos de objeto não se aplicam necessariamente a EGL_ANDROID_native_fence objetos, evitando interações indesejadas.

A extensão EGL_ANDROID_native_fence_sync emprega um objeto nativo correspondente atributo descritor do arquivo de limite que só pode ser definido no momento da criação e não podem ser consultados diretamente posteriormente a partir de um objeto de sincronização existente. Este atributo pode ser definido como um destes dois modos:

  • Um descritor de arquivo de limite válido encapsula um nativo existente Descritor de arquivo de limite do Android em um objeto EGLSyncKHR.
  • -1 cria um descritor de arquivo de limite nativo do Android a partir de uma objeto EGLSyncKHR.

Use a chamada de função DupNativeFenceFD() para extrair o Objeto EGLSyncKHR do descritor do arquivo de limite nativo do Android. Isso tem o mesmo resultado de uma consulta ao atributo set, mas segue a convenção de que o destinatário fecha a cerca (por isso a cópia operação). Por fim, destruir o objeto EGLSyncKHR fecha o atributo de limite interno.

Integração do Hardware Composer

O Hardware Composer processa três tipos de limites de sincronização:

  • Adquirir limites são transmitidos com buffers de entrada para as chamadas setLayerBuffer e setClientTarget. Eles representam uma gravação pendente no buffer e devem sinalizar antes que o O SurfaceFlinger ou o HWC tenta ler o buffer associado para e fazer a composição.
  • Os limites de liberação são recuperados após a chamada para presentDisplay usando a chamada de getReleaseFences. Elas representam uma leitura pendente do buffer anterior na mesma camada. Um liberar sinais de limite quando o HWC não estiver mais usando o buffer anterior porque o buffer atual substituiu o anterior na tela. Os limites de liberação são retornados ao app junto com os buffers anteriores que será substituída durante a composição atual. O app precisa aguardar liberar sinais de limite antes de gravar um novo conteúdo no buffer que foi devolvido a eles.
  • Limites atuais são retornados, um por frame, como parte da a chamada para presentDisplay. Cercas do presente representam quando composição deste frame foi concluída ou, alternativamente, quando a o resultado da composição do frame anterior não é mais necessário. Para serviços físicos é exibido, presentDisplay retorna limites presentes quando o o frame atual aparece na tela. Depois que os limites atuais são devolvidos, será seguro gravar novamente no buffer de destino SurfaceFlinger aplicável. Para exibições virtuais, os limites atuais são retornados quando seguro para leitura do buffer de saída.