Detalhes de renderização de BlinkNG

Stefan zager
Stefan Zager
Chris harrelson
Chris Harrelson

Blink refere-se à implementação do Chromium da plataforma da Web e engloba todas as fases de renderização anteriores à composição, culminando na confirmação do compositor. Saiba mais sobre a arquitetura de renderização intermitente em um artigo anterior desta série.

O Blink começou como uma bifurcação do WebKit, que é uma bifurcação do KHTML (link em inglês), que data de 1998. Ele contém alguns dos códigos mais antigos (e mais importantes) do Chromium e, em 2014, já estava mostrando sua idade. Nesse ano, embarcamos em um conjunto de projetos ambiciosos sob a bandeira do que estamos chamando de BlinkNG, com o objetivo de lidar com deficiências de longa data na organização e estrutura do código Blink. Este artigo aborda o BlinkNG e seus projetos constituintes: por que fizemos isso, o que eles realizaram, os princípios orientadores que moldaram o design e as oportunidades para melhorias futuras que eles oferecem.

O pipeline de renderização antes e depois do BlinkNG.

Renderização pré-NG

O pipeline de renderização do Blink sempre foi dividido conceitualmente em fases (style, layout, paint e assim por diante), mas as barreiras de abstração estavam vazando. Em termos gerais, os dados associados à renderização consistiam em objetos mutáveis e de longa duração. Esses objetos podiam ser (e foram) modificados a qualquer momento e eram frequentemente reciclados e reutilizados por sucessivas atualizações de renderização. Era impossível responder com confiabilidade a perguntas simples, como:

  • A saída de estilo, layout ou pintura precisa ser atualizada?
  • Quando esses dados terão seu valor "final"?
  • Quando é possível modificar esses dados?
  • Quando este objeto será excluído?

Há muitos exemplos disso, incluindo:

Style geraria ComputedStyles com base em folhas de estilo, mas ComputedStyle não era imutável. Em alguns casos, seria modificado por estágios posteriores do pipeline.

Style geraria uma árvore de LayoutObject, e layout anotaria esses objetos com informações de tamanho e posicionamento. Em alguns casos, o layout modifica até mesmo a estrutura da árvore. Não havia uma separação clara entre as entradas e saídas do layout.

O estilo geraria estruturas de dados complementares que determinaram o curso da composição, e essas estruturas de dados foram modificadas em cada fase após o estilo.

Em um nível inferior, os tipos de dados de renderização consistem basicamente em árvores especializadas (por exemplo, a árvore DOM, a árvore de estilo, a árvore de layout, a árvore de propriedades de pintura) e as fases de renderização são implementadas como caminhadas em árvore recursivas. O ideal é que uma caminhada em árvore seja contida: ao processar um determinado nó da árvore, não devemos acessar informações fora da subárvore com acesso root nesse nó. Isso nunca foi verdadeiro antes da renderizaçãoNG. A árvore percorre informações acessadas com frequência dos ancestrais do nó que está sendo processado. Isso deixava o sistema muito frágil e propenso a erros. Também era impossível começar a caminhar em qualquer lugar que não fosse a raiz da árvore.

Por fim, havia muitos desvios no pipeline de renderização espalhados por todo o código: layouts forçados acionados por JavaScript, atualizações parciais acionadas durante o carregamento de documentos, atualizações forçadas para preparação para segmentação de eventos, atualizações programadas solicitadas pelo sistema de exibição e APIs especializadas expostas apenas ao código de teste, para citar alguns. Havia até alguns caminhos recursivos e reentrantes no pipeline de renderização, ou seja, ir para o início de um estágio a partir do meio de outro. Cada um desses desviados de renderização tem um comportamento idiossincrático próprio e, em alguns casos, a saída da renderização depende da maneira como a atualização de renderização foi acionada.

O que mudamos

O BlinkNG é composto por muitos subprojetos, grandes e pequenos, todos com o objetivo comum de eliminar os déficits arquitetônicos descritos anteriormente. Esses projetos compartilham alguns princípios orientadores projetados para tornar o pipeline de renderização mais um pipeline real:

  • Ponto de entrada uniforme: sempre devemos inserir o pipeline no início.
  • Estágios funcionais: cada estágio precisa ter entradas e saídas bem definidas, com comportamento funcional, ou seja, determinístico e repetível, e os resultados precisam depender apenas das entradas definidas.
  • Entradas constantes: as entradas de qualquer cenário precisam ser efetivamente constantes enquanto o cenário está em execução.
  • Saídas imutáveis: quando um estágio é concluído, as saídas dele precisam ser imutáveis pelo restante da atualização da renderização.
  • Consistência do ponto de verificação: ao final de cada etapa, os dados de renderização produzidos até agora precisam estar em um estado autoconsistente.
  • Eliminação de duplicação de trabalho: calcule cada item apenas uma vez.

Uma lista completa dos subprojetos do BlinkNG geraria uma leitura tediosa, mas a seguir estão algumas consequências específicas.

O ciclo de vida do documento

A classe DocumentLifecycle monitora o progresso no pipeline de renderização. Ele nos permite fazer verificações básicas que impõem as invariantes listadas anteriormente, como:

  • Se estamos modificando uma propriedade ComputedStyle, o ciclo de vida do documento precisa ser kInStyleRecalc.
  • Se o estado do DocumentLifecycle for kStyleClean ou posterior, NeedsStyleRecalc() precisará retornar false para qualquer nó anexado.
  • Ao entrar na fase do ciclo de vida de pintura, o estado dele precisa ser kPrePaintClean.

Ao implementar o BlinkNG, nós sistematicamente eliminamos caminhos de código que violavam essas invariantes e espalhamos muitas outras declarações em todo o código para garantir que não regressemos.

Se você já passou por tantos problemas examinando um código de renderização de baixo nível, pode se perguntar: "Como eu cheguei até aqui?" Como mencionado anteriormente, há vários pontos de entrada no pipeline de renderização. Antes, isso incluía caminhos de chamada recursivos e reentrantes e locais em que entramos no pipeline em uma fase intermediária, em vez de começar do início. Durante o BlinkNG, analisamos esses caminhos de chamadas e determinamos que todos eles eram redutíveis a dois cenários básicos:

  • Todos os dados de renderização precisam ser atualizados, por exemplo, ao gerar novos pixels para exibição ou ao fazer um teste de hit para a segmentação de eventos.
  • Precisamos de um valor atualizado para uma consulta específica que possa ser respondida sem atualizar todos os dados de renderização. Isso inclui a maioria das consultas JavaScript, por exemplo, node.offsetTop.

Agora há apenas dois pontos de entrada no pipeline de renderização, que correspondem a esses dois cenários. Os caminhos de código reentrantes foram removidos ou refatorados, e não é mais possível entrar no pipeline começando em uma fase intermediária. Isso eliminou muitos mistérios sobre exatamente quando e como as atualizações de renderização acontecem, facilitando muito o entendimento do comportamento do sistema.

Estilo, layout e pré-pintura do encanamento

Coletivamente, as fases de renderização antes da pintura são responsáveis pelo seguinte:

  • Executar o algoritmo style cascade para calcular as propriedades de estilo finais dos nós DOM.
  • Gerar a árvore de layout que representa a hierarquia de caixas do documento.
  • Determinar as informações de tamanho e posição de todas as caixas.
  • Arredondar ou ajustar a geometria de subpixels aos limites inteiros dos pixels para pintura.
  • Determinar as propriedades de camadas compostas (transformação afim, filtros, opacidade ou qualquer outra coisa que possa ser acelerada pela GPU).
  • Determinar qual conteúdo mudou desde a fase de pintura anterior e precisa ser pintado ou repintado (invalidação da pintura).

Essa lista não mudou, mas, antes do BlinkNG, grande parte desse trabalho era feito de maneira ad hoc, distribuída em várias fases de renderização, com muitas funcionalidades duplicadas e ineficiências integradas. Por exemplo, a fase style sempre foi a principal responsável por calcular as propriedades de estilo finais para nós, mas houve alguns casos especiais em que não determinamos os valores finais da propriedade de estilo antes da conclusão da fase style. Não havia um ponto formal ou obrigatório no processo de renderização que pudéssemos dizer com certeza que as informações de estilo estavam completas e imutáveis.

Outro bom exemplo de problema pré-BlinkNG é a invalidação de pintura. Antes, a invalidação da pintura era espalhada em todas as fases da renderização antes da pintura. Ao modificar o código de estilo ou layout, era difícil saber quais alterações na lógica de invalidação de pintura eram necessárias, e era fácil cometer um erro que levava a bugs de invalidação ou excesso de erros. Para saber mais sobre as complexidades do antigo sistema de invalidação de pintura, leia o artigo desta série dedicado ao LayoutNG.

O ajuste da geometria do layout de subpixels aos limites inteiros de pixels para pintura é um exemplo de quando tínhamos várias implementações da mesma funcionalidade e fizemos muito trabalho redundante. Havia um caminho de código de ajuste de pixels usado pelo sistema de pintura, e outro caminho de código totalmente separado era usado sempre que precisávamos de um cálculo único e em tempo real de coordenadas ajustadas em pixels fora do código de pintura. Nem sempre todas as implementações tiveram os próprios bugs, e os resultados nem sempre foram iguais. Como não havia armazenamento em cache dessas informações, o sistema às vezes executava exatamente o mesmo cálculo repetidamente — outra limitação no desempenho.

Aqui estão alguns projetos significativos que eliminaram os déficits arquitetônicos das fases de renderização antes da pintura.

Project Squad: encadeamento da fase de estilo

Este projeto abordou dois déficits principais na fase de estilo que impediram o pipeline:

Há duas saídas principais da fase de estilo: ComputedStyle, que contém o resultado da execução do algoritmo CSS em cascata na árvore DOM, e uma árvore de LayoutObjects, que estabelece a ordem das operações para a fase de layout. Conceitualmente, a execução do algoritmo em cascata precisa acontecer estritamente antes da geração da árvore de layout. No entanto, anteriormente, essas duas operações eram intercaladas. O Esquadrão do Projeto conseguiu dividir as duas em fases sequenciais distintas.

Anteriormente, ComputedStyle nem sempre recebia o valor final durante o recálculo de estilo. Em algumas situações, o ComputedStyle era atualizado em uma fase posterior do pipeline. O Project Squad refatorou esses caminhos de código com sucesso para que o ComputedStyle nunca seja modificado após a fase de estilo.

LayoutNG: encadeamento da fase de layout

Esse projeto monumental, um dos pilares do RenderingNG, foi uma reescrita completa da fase de renderização do layout. Não faremos jus a todo o projeto aqui, mas há alguns aspectos notáveis para o projeto BlinkNG geral:

  • Anteriormente, a fase de layout recebia uma árvore de LayoutObject criada pela fase de estilo e anotava a árvore com informações de tamanho e posição. Portanto, não havia uma separação clara entre entradas e saídas. O LayoutNG introduziu a árvore de fragmentos, que é a saída principal somente leitura do layout e serve como a entrada principal para as fases de renderização subsequentes.
  • O LayoutNG trouxe a propriedade de contenção (link em inglês) para o layout: ao calcular o tamanho e a posição de um determinado LayoutObject, não vemos mais fora da subárvore com acesso root a esse objeto. Todas as informações necessárias para atualizar o layout de um determinado objeto são calculadas antecipadamente e fornecidas como uma entrada somente leitura ao algoritmo.
  • Antes, havia casos extremos em que o algoritmo de layout não era estritamente funcional: o resultado do algoritmo dependia da atualização de layout anterior mais recente. O LayoutNG eliminou esses casos.

A fase de pré-pintura

Antes, não havia uma fase formal de renderização de pré-pintura, apenas um conjunto de operações pós-layout. A fase de pré-pintura surgiu do reconhecimento de que havia algumas funções relacionadas que poderiam ser melhor implementadas como uma travessia sistemática da árvore de layout após a conclusão do layout. E o mais importante:

  • Emitir invalidações de pintura: é muito difícil realizar a invalidação de pintura corretamente durante o curso do layout, quando temos informações incompletas. É muito mais fácil acertar e pode ser muito eficiente se for dividido em dois processos distintos: durante o estilo e o layout, o conteúdo pode ser marcado com uma simples sinalização booleana como "possivelmente precisa de invalidação de pintura". Durante a caminhada da árvore de pré-pintura, verificamos essas sinalizações e emitimos invalidações conforme necessário.
  • Geração de árvores de propriedades de pintura: um processo descrito em mais detalhes.
  • Computação e gravação de locais de pintura ajustados por pixels: os resultados gravados podem ser usados pela fase de pintura e também por qualquer código downstream que precise deles, sem computação redundante.

Árvores de propriedades: geometria consistente

As árvores de propriedade foram introduzidas no início do RenderingNG para lidar com a complexidade da rolagem, que na Web tem uma estrutura diferente de todos os outros tipos de efeitos visuais. Antes das árvores de propriedade, o compositor do Chromium usava uma única hierarquia de "camada" para representar a relação geométrica do conteúdo composto, mas isso logo se desfez à medida que a complexidade total de recursos, como position:fixo, se tornava aparente. A hierarquia de camadas cresceu ponteiros não locais extras indicando o "pai de rolagem" ou "pai de clipe" de uma camada e, em pouco tempo, foi muito difícil entender o código.

As árvores de propriedades corrigiram isso representando a rolagem flutuante e os aspectos de corte do conteúdo separadamente de todos os outros efeitos visuais. Isso tornou possível modelar corretamente a verdadeira estrutura visual e de rolagem dos sites. Em seguida, "tudo" que tivemos que fazer foi implementar algoritmos sobre as árvores de propriedades, como a transformação do espaço de tela das camadas compostas, ou determinar quais camadas rolaram e quais não o fizeram.

Na verdade, logo percebemos que havia muitos outros locais no código em que questões geométricas semelhantes foram levantadas. A postagem de estruturas de dados principais tem uma lista mais completa. Vários deles tinham implementações duplicadas da mesma coisa que o código do compositor estava fazendo; todos tinham um subconjunto diferente de bugs; e nenhum deles modelava corretamente a estrutura real do site. A solução ficou clara: centralizar todos os algoritmos de geometria em um só lugar e refatorar todo o código para usá-lo.

Esses algoritmos, por sua vez, dependem das árvores de propriedades, e é por isso que as árvores de propriedades são uma estrutura de dados chave, ou seja, usada em todo o pipeline da RenderingNG. Assim, para atingir esse objetivo de código de geometria centralizado, precisávamos introduzir o conceito de árvores de propriedades muito antes no pipeline (na pré-pintura) e alterar todas as APIs que agora dependiam delas para exigir a execução da pré-pintura antes que pudessem ser executadas.

Essa história é mais um aspecto do padrão de refatoração do BlinkNG: identifique os principais cálculos, faça a refatoração para evitar duplicá-los e crie estágios de pipeline bem definidos que criam as estruturas de dados que as alimentam. Computamos as árvores de propriedades exatamente no ponto em que todas as informações necessárias estão disponíveis, e garantimos que as árvores de propriedades não possam ser alteradas enquanto os estágios posteriores de renderização estiverem em execução.

Composição após a pintura: tinta e composição de tubulações

A camada é o processo de descoberta de qual conteúdo do DOM vai para a própria camada composta (que, por sua vez, representa uma textura da GPU). Antes da renderizaçãoNG, a criação de camadas era executada antes da pintura, e não depois. Consulte o pipeline atual aqui. Observe a mudança de ordem. Primeiro, decidiríamos quais partes do DOM entravam em qual camada composta e, só então, desenharíamos listas de exibição para essas texturas. Naturalmente, as decisões dependiam de fatores como quais elementos DOM eram animados ou rolados ou quais tinham transformações 3D e quais elementos pintavam.

Isso causou grandes problemas, porque era mais ou menos necessário que houvesse dependências circulares no código, o que é um grande problema para um pipeline de renderização. Vamos conferir um exemplo. Suponha que precisamos invalidate a pintura, o que significa que precisamos reexibir a lista de exibição e fazer a varredura novamente. A necessidade de invalidação pode ser resultado de uma mudança no DOM ou de um estilo ou layout diferente. Mas, claro, queremos invalidar apenas as partes que realmente mudaram. Isso significou descobrir quais camadas compostas foram afetadas e, em seguida, invalidar parte ou todas as listas de exibição para essas camadas.

Isso significa que a invalidação dependia do DOM, do estilo, do layout e de decisões anteriores de criação de camadas (que antes significava o frame renderizado anterior). Mas a criação de camadas atual também depende de todos esses fatores. E como não tínhamos duas cópias de todos os dados de camadas, era difícil dizer a diferença entre as decisões passadas e futuras de camadas. Então, terminamos com muitos códigos que tinham raciocínio circular. Isso, às vezes, gerava códigos ilógicos ou incorretos, ou até mesmo falhas ou problemas de segurança, se não tivéssemos muito cuidado.

Para lidar com essa situação, já apresentamos o conceito do objeto DisableCompositingQueryAsserts. Na maioria das vezes, se o código tentasse consultar decisões anteriores de criação de camadas, isso causava uma falha na declaração e travava o navegador se estivesse no modo de depuração. Isso nos ajudou a evitar a introdução de novos bugs. E sempre que o código era legitimamente necessário para consultar decisões anteriores de camadas, colocamos um código para permitir isso, alocando um objeto DisableCompositingQueryAsserts.

Nosso plano era, com o tempo, eliminar todos os objetos DisableCompositingQueryAssert de sites de chamada e depois declarar o código como seguro e correto. Mas descobrimos que algumas chamadas eram essencialmente impossíveis de remover, desde que a criação de camadas acontecesse antes da pintura. Finalmente, conseguimos removê-lo apenas muito recentemente. Este foi o primeiro motivo descoberto para o projeto Composite After Paint. O que aprendemos é que, mesmo que você tenha uma fase de pipeline bem definida para uma operação, se ela estiver no lugar errado no pipeline, você vai travar em algum momento.

O segundo motivo para o projeto Composição após pintura foi o bug de composição fundamental. Uma maneira de informar esse bug é que os elementos DOM não são uma boa representação individual de um esquema de camadas eficiente ou completo para o conteúdo de uma página da Web. E como a composição era antes da pintura, ela dependia de elementos DOM e não de listas de exibição ou árvores de propriedades. Isso é muito semelhante ao motivo pelo qual introduzimos as árvores de propriedades. Assim como acontece com as árvores de propriedades, a solução sairá diretamente se você descobrir a fase certa do pipeline, executá-la no momento certo e fornecer as estruturas de dados-chave corretas. Assim como no caso das árvores de propriedades, essa foi uma boa oportunidade de garantir que, quando a fase de pintura fosse concluída, a saída seria imutável para todas as fases subsequentes do pipeline.

Vantagens

Como vimos, um pipeline de renderização bem definido produz enormes benefícios a longo prazo. Há ainda mais coisas do que você imagina:

  • Confiabilidade muito melhor: esta é bem direta. Um código mais limpo, com interfaces bem definidas e compreensíveis, é mais fácil de entender, escrever e testar. Isso o torna mais confiável. Isso também torna o código mais seguro e estável, com menos falhas e menos bugs de uso após a liberação.
  • Expansão da cobertura de testes: no BlinkNG, adicionamos vários novos testes ao nosso pacote. Isso inclui testes de unidade que fornecem verificação focada de aspectos internos, testes de regressão que nos impedem de reintroduzir bugs antigos que corrigimos (muitos!) e muitas adições ao Pacote de testes de plataforma da Web mantido coletivamente, que todos os navegadores usam para medir a conformidade com os padrões da Web.
  • Mais fácil de estender: se um sistema for dividido em componentes claros, não será necessário entender outros componentes em nenhum nível de detalhe para avançar no atual. Isso torna mais fácil para todos agregarem valor ao código de renderização sem precisar ser um especialista aprofundado, além de facilitar o raciocínio sobre o comportamento de todo o sistema.
  • Desempenho: otimizar algoritmos escritos em código espaguete é bastante difícil, mas é quase impossível conseguir coisas ainda maiores, como rolagem e animações de linha de execução universal ou processos e linhas de execução para isolamento de sites sem esse pipeline. O paralelismo pode nos ajudar a melhorar muito o desempenho, mas também é extremamente complicado.
  • Rendimento e contenção: vários novos recursos foram disponibilizados pelo BlinkNG para desenvolver o pipeline de maneiras novas e inéditas. Por exemplo, e se quiséssemos executar o pipeline de renderização apenas até um orçamento expirar? Ou pular a renderização de subárvores conhecidas por não serem relevantes para o usuário no momento? Isso é o que a propriedade CSS content-visibilidade permite. Que tal fazer com que o estilo de um componente dependa do layout? Essas são as consultas de contêiner.

Estudo de caso: Consultas de contêiner

As consultas de contêiner são um recurso da plataforma da Web muito esperado e que tem sido o recurso mais solicitado pelos desenvolvedores de CSS há anos. Se é tão bom, por que ainda não existe? Isso porque a implementação de consultas de contêiner exige uma compreensão e controle muito cuidadosos da relação entre o código de estilo e layout. Vamos analisar melhor.

Uma consulta de contêiner permite que os estilos que se aplicam a um elemento dependam do tamanho disposto de um ancestral. Como o tamanho dispostas é calculado durante o layout, isso significa que precisamos executar o recálculo de estilo após o layout, mas o recalculo de estilo é executado antes do layout. Esse paradoxo de frango e ovo é o motivo principal de não podermos implementar consultas de contêiner antes do BlinkNG.

Como podemos resolver isso? Não é uma dependência de pipeline com versões anteriores, ou seja, o mesmo problema que projetos como o Composite After Paint resolveu? Pior ainda, e se os novos estilos mudarem o tamanho do ancestral? Isso não vai causar um loop infinito?

Em princípio, a dependência circular pode ser resolvida usando a propriedade CSS "Conter", o que permite a renderização fora de um elemento não depender da renderização dentro da subárvore desse elemento. Isso significa que os novos estilos aplicados por um contêiner não afetam o tamanho dele, porque as consultas dele exigem contenção.

Mas, na verdade, isso não foi suficiente, e era necessário introduzir um tipo mais fraco de contenção do que apenas a contenção de tamanho. Isso ocorre porque é comum querer que um contêiner de consultas de contêiner possa ser redimensionado em apenas uma direção (geralmente em bloco) com base nas dimensões inline dele. Então, o conceito de contenção de tamanho inline foi adicionado. Mas, como você pode ver na observação longa nessa seção, não ficou claro por muito tempo se a contenção de tamanho inline era possível.

Uma coisa é descrever a contenção na linguagem de especificação abstrata e outra coisa é implementá-la corretamente. Lembre-se de que um dos objetivos do BlinkNG era levar o princípio de contenção para as caminhadas pela árvore, que constituem a lógica principal de renderização: ao percorrer uma subárvore, nenhuma informação precisa ser exigida de fora dela. Como acontece (bem, não foi exatamente um acidente), é muito mais limpo e mais fácil implementar a contenção de CSS se o código de renderização obedecer ao princípio de contenção.

Futuro: composição fora do tópico principal... e além!

O pipeline de renderização mostrado aqui está um pouco à frente da implementação atual do RenderingNG. Ela mostra a criação de camadas como fora da linha de execução principal, mas, no momento, ela ainda está na linha de execução principal. No entanto, é apenas uma questão de tempo até que isso seja feito, agora que a composição após a pintura foi enviada, e a criação de camadas está após a pintura.

Para entender por que isso é importante e aonde mais isso pode levar, precisamos considerar a arquitetura do mecanismo de renderização de um ponto de vista um pouco mais elevado. Um dos obstáculos mais duráveis para melhorar o desempenho do Chromium é o simples fato de que a linha de execução principal do renderizador lida com a lógica principal do aplicativo (ou seja, com o script em execução) e a maior parte da renderização. Consequentemente, a linha de execução principal costuma ficar saturada de trabalho, e o congestionamento da linha de execução principal costuma ser o gargalo em todo o navegador.

A boa notícia é que não precisa ser assim. Esse aspecto da arquitetura do Chromium remonta aos dias KHTML, quando a execução com um único thread era o modelo de programação dominante. Quando os processadores com vários núcleos se tornaram comuns nos dispositivos para consumidores, a suposição de thread único estava completamente incorporada ao Blink (anteriormente WebKit). Queríamos introduzir mais linhas de execução no mecanismo de renderização há muito tempo, mas isso era simplesmente impossível no sistema antigo. Um dos principais objetivos da renderização do NG era nos procurar nesse buraco e possibilitar a migração parcial ou total do trabalho de renderização para outra linha de execução.

Agora que o BlinkNG está se aproximando da conclusão, já começamos a explorar essa área. Non-Block Commit é o primeiro passo na mudança do modelo de linha de execução do renderizador. A confirmação do compositor (ou apenas confirmação) é uma etapa de sincronização entre a linha de execução principal e a do compositor. Durante a confirmação, fazemos cópias de dados de renderização produzidos na linha de execução principal para serem usados pelo código de composição downstream em execução na linha de execução do compositor. Enquanto essa sincronização está ocorrendo, a execução da linha de execução principal é interrompida enquanto o código de cópia é executado na linha de execução do compositor. Isso é feito para garantir que a linha de execução principal não modifique os dados de renderização enquanto a linha de execução do compositor os copia.

A confirmação sem bloqueio elimina a necessidade de a linha de execução principal parar e aguardar o término do estágio de confirmação. A linha de execução principal vai continuar trabalhando enquanto a confirmação é executada simultaneamente na linha de execução do compositor. O efeito líquido da confirmação sem bloqueio será uma redução no tempo dedicado à renderização do trabalho na linha de execução principal, o que diminui o congestionamento na linha de execução principal e melhora o desempenho. No momento em que este artigo foi escrito (março de 2022), temos um protótipo funcional de compromisso sem bloqueio e estamos nos preparando para fazer uma análise detalhada do impacto disso no desempenho.

A espera nas asas é a composição fora da linha de execução principal, com o objetivo de fazer com que o mecanismo de renderização corresponda à ilustração, movendo a camada da linha de execução principal para uma linha de execução de worker. Assim como a confirmação sem bloqueio, isso reduz o congestionamento na linha de execução principal, diminuindo a carga de trabalho de renderização. Um projeto como este nunca teria sido possível sem as melhorias arquitetônicas do Composite After Paint.

E há mais projetos em andamento (trocadilho intencional)! Finalmente temos uma base que possibilita testar a redistribuição do trabalho de renderização e estamos muito animados para ver o que é possível!