Arquitetura
O Compust é um cliente X11 que possui a seleção de compositor de uma tela. O servidor X continua responsável pela entrada, pelas conexões dos aplicativos e pelo hardware de exibição. O gerenciador de janelas continua responsável por posicionamento, decorações, foco e áreas de trabalho.
Da janela ao quadro
Aplicativo desenha em uma janela redirecionada
|
Damage / eventos de janela
|
Cena: empilhamento e tempo de vida
|
XRender: fundo -> desfoque -> janelas
|
Cópia com Present ou XRender
|
Overlay do Composite (sem região de entrada)
session.rs negocia as capacidades e adquire _NET_WM_CM_Sn. Um bloqueio breve do servidor torna a verificação do proprietário e a aquisição do redirecionamento atômicas em relação aos outros clientes. O bloqueio também é liberado em caso de erro. Um timestamp real do servidor é obtido antes de adquirir a seleção e anunciar MANAGER.
surface.rs captura cada janela visível de nível superior em um pixmap nomeado. Damage é associado ao pixmap, que permanece válido até o término da animação de fechamento, mesmo que a janela original desapareça. picture.rs controla as imagens XRender e os pixmaps próprios; o encerramento da última conexão também libera recursos e redirecionamentos após falhas na inicialização.
scene.rs mantém as superfícies na ordem de empilhamento do servidor. Ele espelha os filhos da raiz a partir dos eventos de criação, destruição, mudança de parentesco, configuração e circulação, de modo que reempilhar não custa uma ida e volta de QueryTree. A resposta de uma consulta já reflete todo evento que o servidor gerou antes de tratá-la; por isso, eventos com número de sequência anterior não alteram o espelho, e um evento que cite uma janela ausente do espelho provoca nova consulta. Superfícies de janelas destruídas permanecem abaixo da antiga vizinha superior até o fim do fade. Remapear uma janela substitui seu conteúdo somente após uma captura bem-sucedida, preservando a opacidade corrente para a reabertura. events.rs atualiza geometria, tempo de vida, formato e opacidade. O servidor dá à janela um novo pixmap a cada redimensionamento e informa cada redimensionamento; por isso uma superfície é recapturada quando o tamanho informado em um ConfigureNotify difere do capturado. Isso inclui uma janela redimensionada e restaurada antes de os eventos serem tratados, cujo primeiro evento traz o tamanho intermediário; comparar só o tamanho atual a deixaria passar. Um movimento apenas atualiza a posição a partir do evento, sem ida e volta. surface/client.rs descobre o aplicativo por WM_STATE em até oito níveis de descendentes. Cada janela visitada recebe uma assinatura de eventos de propriedade e de janelas filhas antes da leitura de seu estado. A criação tardia ou remoção de WM_STATE, a criação de filhos, as mudanças de parentesco e a destruição atualizam a associação e a opacidade da moldura afetada. A propriedade de opacidade da moldura tem precedência quando existe.
Um descendente que desaparece durante a descoberta é ignorado somente no caso de BadWindow. Se o cliente selecionado desaparecer antes da leitura de opacidade, a moldura sobrevivente usa sua própria opacidade ou o padrão opaco; os eventos seguintes de ciclo de vida atualizam a associação. Perder um cliente não deve encerrar a superfície da moldura. Trata-se de suporte limitado às convenções dos gerenciadores de janelas, não de uma implementação completa de EWMH/ICCCM.
rules.rs guarda as regras por janela. Uma captura lê a identidade do cliente em uma única ida e volta: a classe de recurso de WM_CLASS, o primeiro tipo de _NET_WM_WINDOW_TYPE que o Compust conhece, se WM_TRANSIENT_FOR está definida, o título de _NET_WM_NAME ou, na falta dele, de WM_NAME, se _GTK_FRAME_EXTENTS declara margens, nas quais o cliente desenha a própria sombra e os próprios cantos, e se _NET_WM_STATE lista _NET_WM_STATE_FULLSCREEN. As regras também podem escolher pelo foco, que vem da raiz: _NET_ACTIVE_WINDOW indica o cliente ativo, uma mudança nela custa uma leitura e dá de novo suas opções às superfícies que perderam e ganharam o foco, e sem a propriedade toda janela conta como focada. Todas as respostas são lidas antes de tratar um erro, porque o erro de uma resposta não lida chegaria depois como evento e pararia o compositor. Uma janela sem tipo conhecido é dialog quando é transitória e não é override-redirect, e normal nos demais casos, como determina a EWMH. A superfície guarda as opções que as regras lhe dão, que a pintura lê a cada quadro. Um redimensionamento recaptura a superfície, mas mantém a identidade sem lê-la de novo. Uma mudança em uma dessas propriedades na janela de onde veio a identidade faz a leitura de novo, assim como um cliente encontrado depois, como quando ganha WM_STATE. Uma moldura cujo cliente sumiu mantém a identidade desse cliente, assim como uma janela que já sumiu quando sua mudança é lida, então uma janela em fechamento termina o fade com as próprias regras. Uma recarga resolve de novo as regras de todas as superfícies.
As leituras de propriedades validam a representação no protocolo antes de usar os dados dos aplicativos. WM_STATE deve ter tipo WM_STATE, formato 32 e exatamente dois valores. _NET_WM_WINDOW_OPACITY deve conter um único CARDINAL de 32 bits, sem dados excedentes na propriedade. WM_CLASS deve ser STRING de 8 bits; _NET_WM_NAME, UTF8_STRING de 8 bits; e WM_NAME, STRING de 8 bits, lida como Latin-1, ou UTF8_STRING; _NET_WM_WINDOW_TYPE e _NET_WM_STATE devem ser listas de no máximo 32 átomos. Valores inválidos são tratados como ausentes, inclusive permitindo usar a opacidade válida do cliente quando a da moldura está malformada, texto inválido na sua codificação e texto maior que o 1 KiB lido para uma classe ou os 4 KiB lidos para um título. Erros normais de requisições X continuam seguindo os tratamentos existentes.
Renderização e agendamento
O renderizador desenha o papel de parede em um buffer reutilizável com a profundidade da raiz, compõe as janelas de baixo para cima e aplica uma máscara A8 para a opacidade efetiva. O alfa por pixel permanece na imagem de origem. Os retângulos de Shape recortam tanto a janela quanto o desfoque. Eles são levados a coordenadas da raiz com aritmética de 32 bits e limitados à janela e à área a repintar antes de chegar ao servidor, então formatos extremos fora da tela não estouram coordenadas de 16 bits. O módulo de desfoque monta uma pirâmide em torno de cada janela translúcida que nenhuma regra mantém nítida: cada nível reduz o anterior pela metade com amostragem bilinear, e depois o nível mais grosso é ampliado de volta, nível por nível, em um buffer de fundo mostrado dentro do formato da janela. O fundo aparece com a força com que a janela cobre cada pixel, seu alfa vezes sua opacidade: a margem transparente e a sombra que janelas ARGB, como menus de navegador, desenham em volta de si recebem pouco desfoque, e o desfoque surge e some com a janela. O raio escolhe a profundidade, arredondada para 2, 4, 8 ou 16 pixels. A área da pirâmide recebe uma margem de duas vezes o passo mais grosso e se alinha a esse passo, para que pixels deixados por quadros anteriores não alcancem a janela e o resultado continue ancorado à tela quando as janelas se movem. Os buffers dos níveis só existem com o desfoque ativado. Drivers baseados em glamor aceleram essas transformações bilineares, mas processam na CPU os filtros de convolução usados por um desfoque de caixa anterior; o guia de validação de desktops compara os dois.
animation.rs usa Instant monotônico e interpolação smoothstep inteira. O fechamento e a reabertura partem da opacidade amostrada naquele momento, preservando a continuidade de animações interrompidas. O loop agenda um quadro final no ponto de chegada, inclusive quando a duração é zero.
Cada quadro repinta apenas uma área da raiz. O Damage de cada janela informa o retângulo envolvente das mudanças desde o aviso anterior, o que as localiza sem buscar a região. renderer/damage.rs guarda o que o último quadro mostrou de cada superfície: limites, formato, imagem capturada, opacidade efetiva, se o desfoque aparece através dela e posição no empilhamento. Cada mudança registra também a camada mais baixa da pilha que ela altera; o fundo é a camada 0. O conteúdo de uma superfície muda a partir da própria camada, assim como os limites antigos e novos de uma superfície que se moveu, foi redimensionada ou recapturada, mudou de formato, opacidade ou desfoque, surgiu ou saiu. Uma reordenação muda a cena abaixo de cada superfície que trocou de lugar, nos limites das duas, mas a saída só onde elas se sobrepõem. Uma exposição do overlay muda apenas a saída; um renderizador novo ou uma troca de papel de parede partem do fundo, e uma rejeição do Present repinta tudo. region.rs guarda a área a repintar como no máximo 16 retângulos, unindo o par cujo envoltório acrescenta menos, então ela pode cobrir mais do que mudou, nunca menos. A pintura é recortada pela área, e um quadro de área vazia nem chega a ser pintado.
Cada superfície desfocada guarda seu fundo desfocado em um buffer que cobre seus limites na tela. O desfoque lê em volta de cada pixel que escreve, então uma mudança abaixo de uma superfície em qualquer ponto da sua área de alcance a desfoca de novo, o que repinta toda a área de alcance e muda a superfície para as que estão acima. Uma superfície repintada onde o fundo guardado não corresponde mais aos seus limites também é desfocada de novo. Qualquer outra repintura, como a mudança do próprio conteúdo da superfície, o fade da sua opacidade ou a mudança de uma janela acima dela, copia o fundo guardado e não custa desfoque. Os buffers são arredondados para 64 pixels e só são substituídos quando pequenos demais ou mais de quatro vezes grandes demais; um buffer sai quando sua superfície deixa de ser desfocada.
renderer/cover.rs pula o que superfícies opacas escondem. Uma superfície sem canal alfa, exibida com opacidade total, esconde seu formato de todas as superfícies abaixo dela e do fundo, que só são pintados onde a área continua à vista. Uma superfície que precisa ser desfocada de novo lê a cena abaixo dela em toda a sua área de alcance, então nada ali fica escondido das superfícies abaixo dela. Uma superfície desfocada escondida por inteiro pelas de cima não é desfocada; em vez disso, descarta o fundo guardado, para ser desfocada de novo quando algo a descobrir. A área coberta é guardada como no máximo 64 retângulos; além disso, ela esconde menos do que poderia, nunca mais.
renderer/shadow.rs dá uma sombra a uma superfície enquanto shadow_radius for maior que zero e a superfície projetar uma: sua regra diz que sim ou, na falta dela, o tipo do cliente projeta sombra e o cliente não reserva margens para uma sombra própria; além disso, seu formato precisa ser o retângulo inteiro. A sombra é esse retângulo, deslocado pelo offset e desfocado. A sombra de uma superfície arredondada é, em vez disso, o desfoque do seu formato arredondado: o produto das tiras descritas abaixo, menos o desfoque de cada canto que o arredondamento corta, dado por uma correção calculada uma vez por par de raios. Perto de cada canto, um remendo guarda esses valores e também preenche o quadrado de canto da superfície na medida em que ela deixa cada pixel descoberto, para que nenhum fundo apareça entre o arco e a sombra; remendos que se sobreporiam em superfícies pequenas se juntam. Desfocar um retângulo se separa em um perfil ao longo de cada eixo; por isso cada pintor guarda duas tiras por superfície, do comprimento da largura e da altura da sombra, e as multiplica pela força da sombra em cada pixel: o XRender compõe uma tira através da outra, e o pintor de GPU amostra as duas em um único desenho. As tiras do XRender arredondam seu comprimento para cima, então um redimensionamento envia perfis novos para as mesmas imagens. Três filtros de caixa calculados com inteiros dão os perfis, de modo que os dois pintores desenham os mesmos valores. A sombra pertence à camada de sua superfície. Ela é pintada depois que o fundo da superfície foi desfocado, o que a deixa fora desse desfoque, e antes da superfície, em volta dos limites da superfície e nunca embaixo deles, onde a área de repintura alcança e nenhuma superfície opaca acima a esconde. O que um quadro mostrou de uma superfície inclui a sombra, então um movimento, um fade ou uma recarga repintam a extensão antiga e a nova das duas, enquanto o conteúdo da própria superfície não mexe na sombra. Uma superfície totalmente escondida ainda pinta a parte da sombra que aparece.
renderer/corner.rs dá a cada superfície o raio dos seus cantos arredondados: o corner_radius da sua regra ou, na falta dele, o global, quando a superfície projetaria sombra pelo seu tipo e pelas suas margens; nenhum enquanto o cliente estiver em tela cheia ou o formato não for o retângulo inteiro; e no máximo metade do lado menor, borda incluída. O que um quadro mostrou de uma superfície inclui esse raio, então uma mudança repinta a superfície. O módulo calcula o disco a partir do qual os dois pintores desenham os cantos, contando 16 × 16 pontos por pixel com inteiros. O pintor XRender envia um disco A8 por raio em uso e desenha uma superfície arredondada em duas partes: o interior pela máscara de opacidade, sob um recorte que deixa de fora os quatro quadrados de canto, e depois cada quadrado pelo seu quadrante do disco, ou de uma cópia temporária que uma composição pela máscara de opacidade multiplica pela opacidade. Um fundo desfocado abaixo dela aparece por pesos que o disco multiplica nos cantos, então o desfoque para nos arcos. Uma superfície opaca arredondada esconde apenas o seu interior das superfícies abaixo dela, e uma arredondada nunca conta como cobrindo a tela. O pintor de GPU desenha as mesmas partes a partir de uma textura de disco por raio: os quadrados de canto da janela por um desenho que mantém o próprio alfa da janela e o multiplica pelo do disco, os do fundo desfocado por um que multiplica o alfa da janela e o do disco, e os remendos de uma sombra arredondada pelo desenho de sombra, com um pixel opaco no lugar do segundo perfil.
compositor.rs processa lotes limitados de eventos para que uma tempestade de eventos não adie indefinidamente a pintura. Há redesenho somente após dano, mudanças relevantes ou durante animações, respeitando max_fps como teto. Um evento que não altera nenhuma superfície exibida nem a ordem delas não provoca redesenho: com um único buffer do Present, esse quadro atrasaria o seguinte em um vblank. Quando ocioso, o socket X é aguardado por no máximo um segundo para observar os sinais de encerramento e de recarga.
Com unredirect_fullscreen, o compositor suspende a composição enquanto uma superfície esconde todo o resto: a superfície do topo entre as exibidas, quando ainda está mapeada, é opaca com opacidade total, é inteiramente retangular e cobre a tela. Ele deixa de redirecionar os filhos da raiz, de modo que as janelas desenham na tela por conta própria, e desmapeia o overlay. As superfícies mantêm seus pixmaps nesse período, que não recebem mais desenho, e a cena continua acompanhando os eventos. Um evento cujo tratamento capturaria uma janela ou substituiria o renderizador retoma a composição antes: uma janela visível que é mapeada, uma janela que chega à raiz, uma janela mapeada que muda de tamanho ou uma mudança da tela. Depois de qualquer outro lote de eventos, a composição é retomada se a cena não tiver mais uma superfície assim. A retomada redireciona as janelas de novo, cada uma para um pixmap novo que começa com o que a janela mostra, mapeia o overlay e captura de novo todas as superfícies mapeadas, o que repinta a tela inteira, porque a superfície que a cobria mudou ou saiu. Uma superfície que fechou nesse período é descartada sem fade, porque seu pixmap mostra uma imagem anterior à suspensão.
config.rs lê o arquivo de --config ou, sem ele, o primeiro compust/compust.toml nos diretórios de configuração XDG. SIGUSR1 marca um indicador que o loop de eventos confere após cada lote de eventos. watch.rs também recarrega a configuração quando um arquivo muda: um descritor inotify, consultado junto da conexão X, observa o diretório de cada arquivo que a busca poderia ler e o do arquivo para o qual um link simbólico entre eles aponta, porque editores costumam salvar substituindo o arquivo, o que uma observação do próprio arquivo perderia. Uma mudança em um desses nomes, ou a perda de um diretório observado ou de eventos, torna a recarga devida 100 ms depois, e cada nova mudança a adia: um salvamento em várias etapas, como o do Vim, que move o arquivo antigo antes de gravar o novo, recarrega uma vez e nunca entre as etapas. Cada recarga volta a observar os arquivos. Onde o inotify não está disponível, resta o SIGUSR1. A recarga repete essa busca e, portanto, lê o que uma reinicialização leria; um arquivo ilegível ou inválido mantém a configuração em uso. A opacidade, inclusive a opacidade e o desfoque dados por regras, e o teto de quadros são lidos a cada quadro. Cada transição de fade usa a duração configurada no momento em que começa, então um fade_ms recarregado, global ou em uma regra, vale também para janelas abertas antes. O renderizador registra o raio de desfoque e a opção vsync com que foi criado, e uma recarga que altere qualquer um deles o substitui na pintura seguinte, depois que Present libera o buffer, como em qualquer quadro. Os tratadores de sinais são instalados antes da tomada da seleção do compositor: um cliente pode sinalizar o processo assim que vê a seleção, e SIGUSR1 o encerraria. SIGUSR1 segue o picom; SIGHUP é evitado porque um terminal ao ser fechado o envia.
Com Present, um único buffer é enviado no modo COPY com a área repintada como região de atualização, então o servidor copia apenas essa área; o buffer mantém o restante do quadro anterior, que ainda corresponde à tela. Ele só volta a ser usado após as notificações de conclusão e liberação. Uma mudança de tela via RandR é a exceção: o renderizador é substituído sem esperar, porque uma reconfiguração de CRTC em hardware modesetting/amdgpu descartou os dois eventos de um envio pendente. Um tempo limite de um segundo cobre perdas por qualquer outra causa: o renderizador é substituído e o quadro é repintado uma vez. Se o envio seguinte também exceder o tempo limite, os buffers são substituídos de novo, mas a pintura espera por novo dano, para que um servidor que parou de informar não cause uma repintura por segundo. Um ConfigureNotify da raiz pode solicitar essa substituição, mas não cancelá-la. Assinaturas anteriores são liberadas, mas o servidor também informa à nova assinatura o envio pendente de um renderizador substituído, e nada o ordena em relação ao primeiro quadro do novo renderizador: após um redimensionamento da raiz, o Xvfb o exibiu por último em cerca de uma a cada três vezes, deixando os pixels do buffer antigo na área de atualização dele. Por isso os números de série continuam de um renderizador para o seguinte, e uma conclusão que não seja a do próprio renderizador faz com que ele exiba de novo o buffer inteiro. É uma base simples, não um agendador de baixa latência com vários buffers. Sem Present, XRender copia a área diretamente para a janela de composição.
Um BadMatch na requisição PresentPixmap pendente exata pode acionar a cópia via XRender após consultas de geometria confirmarem que o pixmap original e a saída ainda existem na mesma tela, com profundidades iguais e as dimensões esperadas do buffer. A requisição rejeitada não retém o buffer, então a espera por conclusão e liberação é encerrada. A sessão passa então a considerar Present indisponível, e a alternativa se mantém quando um redimensionamento da raiz ou uma recarga de configuração recria o renderizador. Um aviso registra a requisição rejeitada. Recursos inválidos e erros não relacionados não fazem parte dessa recuperação.
Renderizador de GPU
O renderizador planeja cada quadro uma vez: a área a repintar, quais fundos são desfocados de novo, o que superfícies opacas escondem e o recorte de cada superfície. renderer/paint.rs entrega esse plano a um de dois pintores. O pintor XRender o executa como descrito acima. O pintor de GPU em renderer/gpu.rs, escolhido com backend = "gl", o executa com OpenGL ES na própria GPU do servidor X e guarda seus fundos e a pirâmide de desfoque como texturas de GPU. Um recorte é uma lista de retângulos que podem se sobrepor, que o XRender une; o pintor de GPU desenha um quad por retângulo, então primeiro divide o recorte em pedaços disjuntos, e um desenho translúcido se mistura uma única vez em cada pixel.
O DRI3 Open nomeia o dispositivo DRM do servidor, e o pintor de GPU abre o dispositivo EGL por trás do mesmo nó, então uma máquina com várias GPUs usa aquela com que o servidor renderiza. O buffer de fundo continua sendo o pixmap que o servidor alocou: o pintor o importa por DRI3 BuffersFromPixmap como destino de desenho, então o Present mostra seus quadros exatamente como mostra os do pintor XRender, e nenhum formato ou modificador de buffer precisa ser negociado. Os pixmaps das janelas e do papel de parede são importados da mesma forma, uma vez por captura, e lidos sem cópia. O kernel ordena as leituras e escritas da GPU em buffers compartilhados só depois que cada lado enviou seu trabalho. O pintor envia seus desenhos antes de o quadro ser apresentado. O glamor retém os próprios comandos até o servidor ficar ocioso ou uma fence ser acionada, então antes de cada quadro o pintor aciona uma fence do SYNC e espera a resposta: um servidor ocupado poderia, do contrário, deixar uma janela recém-pintada ser lida antes de seu conteúdo chegar.
O pintor precisa de DRI3 1.2. Sem ele, ou quando o pintor de GPU não abre, o Compust avisa e continua com o XRender. Um quadro que falhe, inclusive por um erro de GL informado no envio, também passa a sessão para o XRender, que repinta tudo; renderizadores posteriores não tentam a GPU de novo. O Xvfb não tem DRI3, e o Xwayland se recusa a compartilhar pixmaps, então nos dois o backend GL volta ao XRender.
Todo o código unsafe do compositor fica em crates/gl, o crate compust-gl: a libEGL carregada em tempo de execução, a escolha do dispositivo, um contexto OpenGL ES sem superfície, importações de dma-buf e um único desenho que lê uma origem em (pixel de destino + deslocamento) × escala, o que cobre a composição e as passadas da pirâmide. Cada bloco unsafe diz por que é correto. O EGL dá a mesma display a todos os usuários de um dispositivo, então o crate nunca a encerra, e cada chamada de GL torna antes o próprio contexto atual, para que um renderizador e o que o substitui mantenham seus objetos separados.
Mapa do código
| Área | Arquivos |
|---|---|
| Inicialização e configuração | main.rs, config.rs, rules.rs, watch.rs |
| Propriedade e capacidades X11 | session.rs, atoms.rs, capabilities.rs |
| Recursos capturados e tempo de vida | picture.rs, surface.rs |
| Associação entre cliente e moldura | surface/client.rs |
| Cena e eventos | scene.rs, events.rs, compositor.rs |
| Efeitos e apresentação | animation.rs, region.rs, renderer.rs, renderer/{paint,damage,cover,blur,shadow,corner,present,wallpaper}.rs |
| Renderizador de GPU | renderer/gpu.rs, e crates/gl/src/ para EGL, OpenGL ES e texturas dma-buf |
| Verificação com servidor real | tests/x11.rs, tests/cases/, tests/support/, e tests/gpu.rs com um servidor que tenha DRI3 |
Os caminhos de código são relativos a src/, exceto os de teste e crates/gl. O crate do compositor usa unsafe_code = "forbid"; essa restrição vale para esse crate, não para o compust-gl nem para os detalhes internos das outras dependências. Rust evita várias classes de erro de memória, mas corridas do protocolo, erros de renderização e vazamentos de recursos ainda exigem testes.
Verificação da captura
Os testes de corridas durante a captura encaminham apenas a conexão do compositor por tests/support/proxy.rs. O proxy delimita requisições X11 na ordem de bytes nativa usando x11rb, encaminha respostas e eventos sem alteração e aguarda sua thread terminar no encerramento. tests/support/capture.rs consulta os opcodes das extensões no Xvfb e conclui uma destruição de janela com confirmação imediatamente antes de encaminhar a requisição escolhida. O cliente de teste mantém uma conexão direta com o servidor para conferir pixels e recursos. Esse transporte pertence somente ao binário de testes; a execução normal do compositor não possui pontos de interceptação.
O mesmo transporte atende aos testes de apresentação substituindo um campo da requisição antes do envio; o Xvfb produz o erro real. Ele também registra a região de atualização de cada requisição PresentPixmap, para que os testes de regiões confiram quanto da tela cada quadro substituiu. Substituir a fence SYNC de espera por uma que nunca é acionada retém, em vez disso, os eventos de conclusão e liberação de um envio. tests/support/monitors.rs usa requisições RandR com confirmação no servidor isolado daquele teste. tests/support/resources.rs consulta XRes usando a janela proprietária da seleção do compositor como identificador do cliente. Ele compara contagens de recursos e os bytes totais reportados para os XIDs dos pixmaps próprios em estados renderizados equivalentes após aquecimento. QueryResourceBytes do XRes 1.2 fornece esses tamanhos sem dividi-los pela contagem de referências; a atribuição de QueryClientPixmapBytes pode variar enquanto Present mantém uma referência. O teste exige tamanhos para todos os pixmaps próprios e exclui referências cruzadas para evitar contagem duplicada. XRes é habilitado apenas para desenvolvimento; o compositor de produção não exige a extensão. As capturas dos testes usam as dimensões atuais do overlay.
Como estender
Um pintor executa o plano de um quadro; um novo, como Vulkan, deve manter o plano e os contratos de tempo de vida dos recursos e dizer como importa, sincroniza e apresenta. A sincronização explícita por DRI3 1.4 e buffers alocados pelo próprio pintor de GPU continuam em aberto.
A próxima otimização deve partir de medições. Cada repintura evitada acrescenta estado que uma mudança despercebida deixa desatualizado, então cada uma precisa de um teste que faça essa mudança e compare o quadro com o de um renderizador novo. Meça também o servidor X, pois o XRender delega trabalho a ele. O roteiro define critérios de aceitação para essas mudanças.
Melhore esta página no GitHub