Vamos conferir as principais estruturas de dados, que são entradas e saídas para pipeline de renderização.
Essas estruturas de dados são:
- As árvores de frames são compostas por nós locais e remotos que representam quais documentos estão em qual processo de renderização e qual renderizador Blink estão disponíveis.
- A árvore de fragmentos imutável representa a saída e a entrada do algoritmo de restrição de layout.
- As árvores de propriedades representam as hierarquias de transformação, recorte, efeito e rolagem de um documento da Web. Elas são usadas em todo o pipeline.
- Listas de exibição e blocos de pintura são as entradas dos algoritmos de varredura e de camadas.
- Os frames do compositor encapsulam superfícies, superfícies de renderização e textura da GPU. blocos usados para desenhar usando a GPU.
Antes de analisar essas estruturas de dados, o exemplo a seguir baseia-se um da análise de arquitetura. Isso é usado ao longo deste documento com demonstrações de como os dados estruturas se aplicam a ele.
<!-- Example code -->
<html>
<div style="overflow: hidden; width: 100px; height: 100px;">
<iframe style="filter: blur(3px);
transform: rotateZ(1deg);
width: 100px; height: 300px"
id="one" src="foo.com/etc"></iframe>
</div>
<iframe style="top:200px;
transform: scale(1.1) translateX(200px)"
id="two" src="bar.com"></iframe>
</html>
Árvores emolduradas
Às vezes, o Chrome pode optar por renderizar um frame de origem cruzada em um processo de renderização diferente do frame pai.
No código de exemplo, há três frames no total:
Com o isolamento de sites, o Chromium usa dois processos de renderização para mostrar a página da Web. Cada processo de renderização tem sua própria representação da árvore de frames dessa página da Web:
Um frame renderizado em um processo diferente é representado como um frame remoto. Um frame remoto contém as informações mínimas necessárias para atuar como um marcador na renderização, como dimensões, por exemplo. Caso contrário, o frame remoto não terá as informações necessárias para renderizar o conteúdo real.
Por outro lado, um frame local representa um frame que passa pelo padrão pipeline de renderização. O frame local contém todas as informações necessárias para transformar os dados desse frame (como a árvore DOM e os dados de estilo) em algo que podem ser renderizados e exibidos.
O pipeline de renderização opera na granularidade de
fragmento de árvore de frames local.
Considere um exemplo mais complicado com foo.com
como frame principal:
<iframe src="bar.com"></iframe>
E o seguinte subframe bar.com
:
<iframe src="foo.com/etc"></iframe>
Embora ainda haja apenas dois renderizadores, agora há três frames locais
fragmentos de árvore, com dois no processo de renderização para foo.com
e um no
processo de renderização para bar.com
:
Para produzir um frame de compositor para a página da Web, O Viz solicita simultaneamente um frame do compositor a partir do frame raiz de cada um as três árvores de frames locais e, em seguida, os agrega. Consulte também a seção de frames do compositor.
O frame principal foo.com
e o subframe foo.com/other-page
fazem parte da mesma árvore de frames e renderizados no mesmo processo.
No entanto, os dois frames ainda têm
ciclos de vida de documentos
já que fazem parte de diferentes fragmentos da árvore de frames local.
Por esse motivo, é impossível gerar um frame do compositor para ambos em uma atualização.
O processo de renderização não tem informações suficientes
para compor o frame do compositor gerado para foo.com/other-page
diretamente no frame do compositor para o frame principal de foo.com
.
Por exemplo, o frame pai bar.com
fora do processo pode afetar a exibição do iframe foo.com/other-url
,
transformando o iframe com CSS ou ocultando partes do iframe com outros elementos no DOM.
A cascata de atualização da propriedade visual
Propriedades visuais, como o fator de escala do dispositivo e o tamanho da janela de visualização, afetam a saída renderizada. e precisam ser sincronizados entre os fragmentos da árvore de frames local. A raiz de cada fragmento de árvore de frames local tem um objeto de widget associado. As atualizações de propriedades visuais vão para o widget do frame principal antes da propagação. aos widgets restantes de cima para baixo.
Por exemplo, quando o tamanho da janela de visualização muda:
Esse processo não é instantâneo, de modo que as propriedades visuais replicadas também incluam um token de sincronização. O compositor do Viz usa esse token de sincronização para aguardar todos os fragmentos da árvore de frames local para enviar um frame do compositor com o token de sincronização atual. Esse processo evita misturar frames do compositor com diferentes propriedades visuais.
A árvore de fragmentos imutável
A árvore de fragmentos imutável é a saída do estágio de layout da renderização pipeline. Ela representa a posição e o tamanho de todos os elementos da página. (sem transformações aplicadas).
Cada fragmento representa uma parte de um elemento DOM. Normalmente, há apenas um fragmento por elemento, mas pode haver mais se for dividido em páginas diferentes durante a impressão, em um contexto de várias colunas.
Após o layout, cada fragmento se torna imutável e nunca mais é alterado. É importante ressaltar que também colocamos algumas restrições adicionais. O que não fazemos:
- Permitir qualquer "para cima" referências na árvore. Um filho não pode ter um ponteiro para o pai.
- "balão" dados na árvore (um filho somente lê informações dos filhos, não do pai).
Essas restrições nos permitem reutilizar um fragmento para um layout subsequente. Sem essas restrições, precisaríamos gerar novamente toda a árvore com frequência, o que é caro.
A maioria dos layouts costuma ser atualizações incrementais, por exemplo, um app da Web atualizando uma pequena parte da IU em resposta ao clique do usuário em um elemento. O ideal é que o layout funcione apenas de forma proporcional ao que realmente mudou na tela. Podemos fazer isso reutilizando o máximo possível de partes da árvore anterior. Isso significa (normalmente) que só precisamos reconstruir a coluna da árvore.
No futuro, esse design imutável pode nos permitir fazer coisas interessantes como passar a árvore de fragmentos imutável pelos limites das linhas de execução, se necessário (para executar fases subsequentes em uma linha de execução diferente), gerar várias árvores para uma animação de layout suave, ou executar layouts especulativos paralelos. Isso também nos oferece o potencial do layout com várias linhas de execução.
Itens de fragmento inline
O conteúdo inline (principalmente texto estilizado) usa uma representação um pouco diferente. Em vez de uma estrutura de árvore com caixas e ponteiros, representamos o conteúdo inline em uma lista simples que representa a árvore. O principal benefício é que a representação de lista simples para inline é rápida, úteis para inspecionar ou consultar estruturas de dados em linha, e eficiência de memória. Isso é muito importante para o desempenho da renderização na Web, porque a renderização de texto é muito complexa, e podem se tornar facilmente a parte mais lenta do pipeline, a menos que sejam altamente otimizados.
A lista simples é criada para cada Contexto de formatação inline na ordem de uma pesquisa em profundidade na subárvore de layout inline. Cada entrada na lista é uma tupla de (objeto, número de descendentes). Por exemplo, considere este DOM:
<div style="width: 0;">
<span style="color: blue; position: relative;">Hi</span> <b>there</b>.
</div>
A propriedade width
é definida como 0 para que a linha fique entre "Hi" e "lá".
Quando o contexto de formatação inline dessa situação é representado como uma árvore, ela tem esta aparência:
{
"Line box": {
"Box <span>": {
"Text": "Hi"
}
},
"Line box": {
"Box <b>": {
"Text": "There"
}
},
{
"Text": "."
}
}
A lista simples tem esta aparência:
- (Caixa de linha, 2)
- (Caixa <span>, 1)
- (Texto "Oi", 0)
- (Caixa de linha, 3)
- (Caixa <b>, 1)
- (Texto "there", 0)
- (Texto ".", 0)
Há muitos consumidores dessa estrutura de dados: APIs de acessibilidade,
e APIs de geometria, como
getClientRects
,
e contenteditable
.
Cada um tem requisitos diferentes.
Esses componentes acessam a estrutura de dados simples com um cursor de conveniência.
O cursor
tem APIs, como MoveToNext
, MoveToNextLine
e CursorForChildren
.
Essa representação de cursor é muito eficiente para conteúdo de texto por vários motivos:
- A iteração na ordem de pesquisa que prioriza a profundidade é muito rápida. Isso é usado com muita frequência porque é semelhante aos movimentos do acento circunflexo. Como é uma lista simples, a pesquisa em profundidade apenas incrementa o deslocamento da matriz, oferecendo iterações rápidas e localidade da memória.
- Ele oferece uma pesquisa abrangente, necessária quando, por exemplo, pintando o plano de fundo de caixas alinhadas e linhas.
- Saber o número de descendentes agiliza a mudança para o próximo irmão (basta incrementar o deslocamento da matriz por esse número).
Árvores de propriedade
O DOM é uma árvore de elementos (além dos nós de texto), e o CSS pode aplicar vários estilos a elementos.
Ela aparece de quatro maneiras:
- Layout:entradas para o algoritmo de restrição de layout.
- Paint:como pintar e fazer a varredura do elemento. (mas não descendentes).
- Visual: efeitos de varredura/desenho aplicados à subárvore do DOM. como transformações, filtros e recortes.
- Rolagem: alinhada ao eixo e com canto arredondado recorte e rolagem da subárvore contida.
As árvores de propriedades são estruturas de dados que explicam como os efeitos visuais e de rolagem se aplicam aos elementos DOM. Eles fornecem os meios para responder a perguntas como: onde, em relação à tela, é um elemento DOM específico, de acordo com o tamanho e a posição do layout? Qual sequência de operações da GPU deve ser usada para aplicar efeitos visuais e de rolagem?
Os efeitos visuais e de rolagem na Web são muito complicados em sua glória. Portanto, o mais importante que as árvores de propriedades fazem é traduzir essa complexidade em uma única estrutura de dados que representa precisamente a estrutura e o significado, sem deixar de lado a complexidade do DOM e do CSS. Isso nos permite implementar algoritmos para composição e rolagem com muito mais confiança. Especificamente, faça o seguinte:
- Geometria potencialmente propensa a erros e outros cálculos podem ser centralizados em um só lugar.
- A complexidade de construir e atualizar árvores de propriedades isolados em um estágio de pipeline de renderização.
- É muito mais fácil e rápido enviar árvores de propriedades para linhas de execução e processos diferentes do que o estado DOM completo, o que torna possível usá-los em muitos casos de uso.
- Quanto mais casos de uso houver, mais ganhos podemos ter com o armazenamento em cache de geometria baseado em cima, porque eles podem reutilizar as informações armazenamento em cache.
O RenderingNG usa árvores de propriedades para muitas finalidades, incluindo:
- Separação da composição da tinta e da composição da linha de execução principal.
- Determinar uma estratégia ideal de composição / desenho.
- Medição IntersectionObserver geometria.
- Evitar o trabalho para elementos fora da tela e blocos de textura da GPU.
- Invalidar a pintura e a varredura de maneira eficiente e precisa.
- Medição mudança de layout e Maior exibição de conteúdo nas Core Web Vitals.
Todo documento da Web tem quatro árvores de propriedades separadas: transformação, corte, efeito e rolagem.(*) A árvore de transformação representa as transformações e rolagem de CSS. (Uma transformação de rolagem é representada como uma matriz de transformação 2D.) A árvore de clipes representa clipes flutuantes. A árvore de efeitos representa todos os outros efeitos visuais: opacidade, filtros, máscaras, modos de combinação e outros tipos de clipes, como clipe de caminho. A árvore de rolagem representa informações sobre rolagem, por exemplo, a maneira como rola cadeia. ela é necessária para executar a rolagem na linha de execução do compositor. Cada nó em uma árvore de propriedades representa uma rolagem ou um efeito visual aplicado por um elemento DOM. Se tiver vários efeitos, pode haver mais de um nó da árvore de propriedades em cada árvore para o mesmo elemento.
A topologia de cada árvore é como uma representação esparsa do DOM. Por exemplo, se houver três elementos DOM com clipes excedentes, haverá três nós da árvore de corte, e a estrutura da árvore de clipes vai seguir a relação de bloco que contém entre os clipes excedentes. Também há ligações entre as árvores. Esses links indicam a hierarquia relativa do DOM, e, portanto, a ordem de aplicação dos nós. Por exemplo, se uma transformação em um elemento DOM estiver abaixo de outro elemento DOM com um filtro, é claro que a transformação é aplicada antes do filtro.
Cada elemento DOM tem um estado da árvore de propriedades, que são 4 tuplas (transformar, recortar, aplicar, rolar) que indica o clipe ancestral mais próximo, transformam e afetam os nós da árvore que entram em vigor nesse elemento. Isso é muito conveniente, porque com essa informação sabemos exatamente a lista de clipes, transformações e efeitos aplicados a esse elemento, e em que ordem. Isso nos diz onde ele está na tela e como desenhá-lo.
Exemplo
(fonte)
<html>
<div style="overflow: scroll; width: 100px; height: 100px;">
<iframe style="filter: blur(3px);
transform: rotateZ(1deg);
width: 100px; height: 300px"
id="one" srcdoc="iframe one"></iframe>
</div>
<iframe style="top:200px;
transform: scale(1.1) translateX(200px)" id=two
srcdoc="iframe two"></iframe>
</html>
No exemplo anterior (que é um pouco diferente do que está na introdução), aqui estão os elementos-chave das árvores de propriedades geradas:
Mostrar listas e blocos de pintura
Um item de exibição contém comandos de desenho de baixo nível (consulte aqui) que podem ser rasterizadas Skia. Os itens de exibição normalmente são simples, com apenas alguns comandos de desenho, como desenhar uma borda ou um plano de fundo. A caminhada pela árvore de pintura faz a iteração da árvore de layout e dos fragmentos associados seguindo a ordem de pintura CSS. para produzir uma lista de itens de exibição.
Exemplo:
<div id="green" style="background:green; width:80px;">
Hello world
</div>
<div id="blue" style="width:100px;
height:100px; background:blue;
position:absolute;
top:0; left:0; z-index:-1;">
</div>
Esse HTML e CSS produziria a seguinte lista de exibição: em que cada célula é um item de exibição:
Plano de fundo da visualização | #blue em segundo plano |
#green em segundo plano |
Texto inline #green |
---|---|---|---|
drawRect com tamanho 800 x 600 e cor branca. |
drawRect com tamanho 100 x 100 na posição 0,0 e cor azul. |
drawRect com tamanho 80 x 18 na posição 8,8 e cor verde. |
drawTextBlob com as posições 8,8 e o texto "Hello world". |
A lista de itens de exibição é ordenada de frente para trás. No exemplo acima, o div verde está antes do div azul na ordem do DOM, mas a ordem de pintura CSS exige que o div azul do Z-index negativo pinte antes (etapa 3) do div verde (etapa 4.1). Os itens de exibição correspondem aproximadamente às etapas atômicas da especificação da ordem de pintura do CSS. Um único elemento DOM pode resultar em vários itens de exibição, por exemplo, como #green tem um item de exibição para o plano de fundo e outro para o texto inline. Essa granularidade é importante para representar toda a complexidade da especificação da ordem de pintura do CSS, como a intercalação criada pela margem negativa:
<div id="green" style="background:green; width:80px;">
Hello world
</div>
<div id="gray" style="width:35px; height:20px;
background:gray;margin-top:-10px;"></div>
Isso produz a seguinte lista de exibição, em que cada célula é um item de exibição:
Plano de fundo da visualização | #green em segundo plano |
#gray em segundo plano |
Texto inline #green |
---|---|---|---|
drawRect com tamanho 800 x 600 e cor branca. |
drawRect com tamanho 80 x 18 na posição 8,8 e cor verde. |
drawRect com tamanho 35 x 20 na posição 8,16 e cor cinza. |
drawTextBlob com as posições 8,8 e o texto "Hello world". |
A lista de itens de exibição é armazenada e reutilizada em atualizações posteriores. Se um objeto de layout não tiver sido alterado durante a caminhada pela árvore, seus itens de exibição são copiados da lista anterior. Uma otimização adicional depende de uma propriedade da especificação do pedido de pintura do CSS: empilhamento de contextos pintam atomicamente. Se nenhum objeto de layout tiver sido alterado em um contexto de empilhamento, a caminhada pela árvore de pintura pula o contexto de empilhamento e copia toda a sequência de itens de exibição da lista anterior.
O estado atual da árvore de propriedade é mantido durante a caminhada pela árvore de pintura e a lista de itens de exibição é reunida em "blocos" de itens de exibição que compartilham o mesmo estado da árvore de propriedades. Isso é demonstrado neste exemplo:
<div id="scroll" style="background:pink; width:100px;
height:100px; overflow:scroll;
position:absolute; top:0; left:0;">
Hello world
<div id="orange" style="width:75px; height:200px;
background:orange; transform:rotateZ(25deg);">
I'm falling
</div>
</div>
Isso produz a seguinte lista de exibição, em que cada célula é um item de exibição:
Plano de fundo da visualização | #scroll em segundo plano |
Texto inline #scroll |
#orange em segundo plano |
Texto inline #orange |
---|---|---|---|---|
drawRect com tamanho 800 x 600 e cor branca. |
drawRect com tamanho 100 x 100 na posição 0,0 e cor rosa. |
drawTextBlob com a posição 0,0 e o texto "Hello world". |
drawRect com tamanho 75x200 na posição 0,0 e cor laranja. |
drawTextBlob com posição 0,0 e texto "I'm caindo". |
Então, a árvore de propriedades de transformação e os blocos de pintura seriam (simplificados para simplificar):
A lista ordenada de pedaços de tinta, que são grupos de itens de exibição e um estado de árvore de propriedades, são as entradas para a etapa de criação em camadas do pipeline de renderização. A lista inteira de pedaços de tinta poderia ser mesclada em uma única camada composta e rasterizada juntas, mas isso exigiria uma rasterização dispendiosa toda vez que o usuário rolasse a tela. Uma camada composta pode ser criada para cada bloco de tinta e rasterizadas individualmente para evitar toda a nova rasterização, mas isso pode esgotar rapidamente a memória da GPU. A etapa de criação de camadas precisa equilibrar a memória da GPU e reduzir os custos quando houver mudanças. Uma boa abordagem geral é mesclar blocos por padrão, e não mesclar blocos de pintura que têm estados de árvore de propriedades que devem ser alterados no thread do compositor, como com a rolagem compositor-thread ou com as animações de transformação do compositor-thread.
O ideal é que o exemplo anterior produza duas camadas compostas:
- Uma camada composta de 800 x 600 contendo os comandos de desenho:
drawRect
com tamanho 800 x 600 e em brancodrawRect
com tamanho 100 x 100 na posição 0,0 e cor rosa
- Uma camada composta de 144 x 224 contendo os comandos de desenho:
drawTextBlob
com a posição 0,0 e o texto "Hello world"- traduzir 0,18
rotateZ(25deg)
drawRect
com tamanho 75x200 na posição 0,0 e cor laranjadrawTextBlob
com a posição 0,0 e o texto "I'm caindo"
Se o usuário rolar a tela para #scroll
,
a segunda camada composta é movida, mas não é necessária nenhuma varredura.
No exemplo, da seção anterior sobre árvores de propriedades, há seis pedaços de tinta. Junto com os estados da árvore de propriedades (transformar, cortar, efeito, rolagem), eles são:
- Plano de fundo do documento: rolagem do documento, clipe do documento, raiz, rolagem do documento.
- Horizontal, vertical e canto de rolagem para div (três blocos de pintura separados):
rolagem do documento, clipe do documento,
#one
desfoque, rolagem do documento. - Iframe
#one
: rotação de#one
, clipe de rolagem flutuante,#one
desfoque, rolagem do div. - Iframe
#two
: escala de#two
, recorte de documento, raiz, rolagem do documento.
Frames de composição: superfícies, superfícies de renderização e blocos de textura da GPU
O navegador e os processos de renderização gerenciam a varredura do conteúdo, em seguida, envie frames do compositor para o processo Viz para apresentação na tela. Os frames de composição representam como agrupar o conteúdo rasterizado e desenhá-lo de forma eficiente usando a GPU.
Blocos
Na teoria, Um processo de renderização ou um compositor do processo do navegador pode rasterizar pixels em uma única textura do tamanho total da janela de visualização do renderizador e enviar essa textura ao Viz. Para exibi-lo, o compositor de exibição precisaria apenas copiar os pixels dessa textura para a posição adequada no buffer de frame (por exemplo, a tela). No entanto, se esse compositor quisesse atualizar um único pixel, seria necessário fazer uma nova varredura de toda a janela de visualização e enviar uma nova textura ao Viz.
Em vez disso, a janela de visualização é dividida em blocos. Um bloco de textura de GPU separado respalda cada bloco com os pixels rasterizados para parte da janela de visualização. O renderizador pode atualizar blocos individuais ou até mesmo basta alterar a posição dos blocos existentes na tela. Por exemplo, ao rolar um site, a posição dos blocos existentes mudaria para cima e apenas ocasionalmente um novo bloco precisaria passar por varredura para conteúdo mais abaixo na página.
Quadriciclos e superfícies
Os blocos de textura da GPU são um tipo especial de quad, que é apenas um nome chique para uma categoria de textura ou outra. Um quadrângulo identifica a textura de entrada e indica como transformar e aplicar efeitos visuais a ela. Por exemplo, blocos de conteúdo regulares têm uma transformação que indica a posição x, y deles na grade de blocos.
Esses blocos rasterizados são agrupados em uma passe de renderização, que é uma lista de quads. A passagem de renderização não contém informações de pixels. em vez disso, ele tem instruções sobre onde e como desenhar cada quadril para produzir a saída de pixel desejada. Há um quadrado de desenho para cada bloco de textura da GPU. O compositor da tela só precisa iterar pela lista de quads, desenhando cada um com os efeitos visuais especificados, para produzir a saída de pixels desejada para a passagem de renderização. A composição de quads de desenho para uma passagem de renderização pode ser feita de maneira eficiente na GPU. porque os efeitos visuais permitidos são cuidadosamente escolhidos para serem associados diretamente aos recursos da GPU.
Existem outros tipos de quadrângulos de desenho além dos blocos rasterizados. Por exemplo, há quadrados de desenho de cores sólidas que não têm suporte de uma textura, ou quadrados de desenho de textura para texturas que não sejam de blocos, como vídeo ou tela.
Também é possível que um frame do compositor incorpore outro. Por exemplo, o compositor do navegador produz um frame do compositor com a interface do usuário do navegador, e um retângulo vazio em que o conteúdo do compositor de renderização será incorporado. Outro exemplo são iframes isolados de site. Esse embedding é realizado por meio de superfícies.
Quando um compositor envia um frame do compositor, ele é acompanhado por um identificador, chamado de ID de superfície, permitindo que outros frames do compositor o incorporem por referência. O frame do compositor mais recente enviado com um ID de superfície específico é armazenado pelo Viz. Outro frame do compositor pode se referir a ele mais tarde por meio de um quadrado de desenho da superfície, e, portanto, o Viz sabe o que desenhar. Os quadrângulos de desenho da superfície contêm apenas IDs de superfície, e não texturas.
Transmissões de renderização intermediárias
Alguns efeitos visuais, como muitos filtros ou modos de combinação avançados, exigem que dois ou mais quadrados sejam desenhados em uma textura intermediária. Em seguida, a textura intermediária é desenhada em um buffer de destino na GPU (ou possivelmente em outra textura intermediária). aplicando o efeito visual ao mesmo tempo. Para permitir isso, um frame do compositor contém uma lista de passagens de renderização. Sempre há uma passagem de renderização raiz, que é desenhado por último e cujo destino corresponde ao buffer do frame; e podem haver mais.
A possibilidade de várias passagens de renderização explica o nome "passar de renderização". Cada passagem precisa ser executada sequencialmente na GPU, em várias "passagens", enquanto uma única passagem pode ser concluída em uma única computação de GPU massivamente paralela.
Agregação
Vários frames do compositor são enviados ao Viz, e precisam ser desenhados juntos na tela. Isso é feito por uma fase de agregação que os converte em um único frame compositor agregado. A agregação substitui os quads de desenho da superfície pelos frames do compositor especificados. Também é uma oportunidade de otimizar o conteúdo que está fora da tela ou texturas intermediárias desnecessárias. Por exemplo, em muitos casos, o frame do compositor para um iframe isolado de site não precisa de uma textura intermediária própria, e podem ser desenhados diretamente no buffer do frame por meio de quadrângulos de desenho apropriados. A fase de agregação identifica essas otimizações e as aplica com base em conhecimento global não acessível a compositores de renderização individuais.
Exemplo
Aqui estão os frames do compositor que representam o exemplo do início do nesta postagem.
- Plataforma
foo.com/index.html
: id=0- Passagem de renderização 0: desenha na saída.
- Quadrado de desenho da passagem de renderização: desenhe com desfoque de 3 px e corte na passagem de renderização 0.
- Passagem de renderização 1:
- Desenhe quadrângulos para o conteúdo do bloco do iframe
#one
, com as posições x e y para cada um.
- Desenhe quadrângulos para o conteúdo do bloco do iframe
- Passagem de renderização 1:
- Quadriciclo de desenho da superfície: com ID 2, desenhado com transformação de escala e translação.
- Quadrado de desenho da passagem de renderização: desenhe com desfoque de 3 px e corte na passagem de renderização 0.
- Passagem de renderização 0: desenha na saída.
- Superfície da interface do navegador: ID=1
- Passagem de renderização 0: desenha na saída.
- Desenhe quadrângulos para a interface do navegador (lado a lado também)
- Passagem de renderização 0: desenha na saída.
- Plataforma
bar.com/index.html
: ID=2- Passagem de renderização 0: desenha na saída.
- Desenhe quadrângulos para o conteúdo do iframe
#two
, com as posições x e y para cada um.
- Desenhe quadrângulos para o conteúdo do iframe
- Passagem de renderização 0: desenha na saída.
Ilustrações de Una Kravets.