410 lines
16 KiB
Markdown
410 lines
16 KiB
Markdown
---
|
|
id: AGD-0010
|
|
ticket: perf-gfx-render-pipeline-and-dirty-regions
|
|
title: Agenda - [PERF] GFX Render Pipeline and Dirty Regions
|
|
status: open
|
|
created: 2026-03-27
|
|
resolved:
|
|
decision:
|
|
tags: []
|
|
---
|
|
|
|
# Agenda - [PERF] GFX Render Pipeline and Dirty Regions
|
|
|
|
## Problema
|
|
|
|
O renderer `gfx` recompõe a cena inteira a cada frame logico, mesmo quando a mudanca visual e pequena.
|
|
|
|
Hoje `render_all()` reconstrói buckets de sprites, escaneia os 512 sprites, redesenha layers e aplica dois passes fullscreen de fade sem politica de invalidacao.
|
|
|
|
Ao mesmo tempo, a arquitetura atual ja opera como um framebuffer destrutivo em memoria: draws escrevem diretamente no buffer de trabalho e operacoes posteriores sobrescrevem o que estiver por baixo. A discussao nao e migrar para um modelo tipo GPU, scene graph ou retained rendering.
|
|
|
|
Existe ainda um driver de produto importante: o objetivo continua sendo viabilizar hardware handheld proprio com limitacoes reais de orcamento e com economia de memoria o mais agressiva possivel. Isso significa que ganhos de CPU nao podem ser avaliados isoladamente; custo de RAM adicional, buffers extras e estruturas auxiliares entram diretamente no criterio de aceitacao.
|
|
|
|
## Dor
|
|
|
|
- custo visual basico cresce demais para hardware simples.
|
|
- pequenos updates pagam preco de full redraw.
|
|
- fade, HUD e world composition ficam sempre no caminho critico.
|
|
|
|
## Hotspots Atuais
|
|
|
|
- [gfx.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/gfx.rs#L563)
|
|
- [gfx.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/gfx.rs#L671)
|
|
|
|
## Alvo da Discussao
|
|
|
|
Definir ate onde o v1 pode sofisticar o modelo de desenho destrutivo em memoria para reduzir custo e manter previsibilidade, sem migrar para um renderer de estilo GPU.
|
|
|
|
## O Que Precisa Ser Definido
|
|
|
|
1. Perfil real de custo.
|
|
Medir separadamente:
|
|
- rebuild de buckets;
|
|
- rasterizacao de layers;
|
|
- rasterizacao de sprites;
|
|
- fades fullscreen;
|
|
- conversao/copia no host;
|
|
- apresentacao no host.
|
|
|
|
2. Granularidade minima de invalidacao compativel com framebuffer destrutivo.
|
|
Escolher entre:
|
|
- dirty flag por frame inteiro;
|
|
- dirty flags por subsistema (`world`, `hud`, `fade`, `sprites`);
|
|
- dirty regions restritas e derivadas de primitivas/sprites;
|
|
- combinacao minima viavel.
|
|
|
|
3. Buckets e traversal de sprites.
|
|
Decidir se os buckets continuam sendo reconstruidos por frame ou se viram estrutura incremental.
|
|
|
|
4. Fades.
|
|
Definir se fade neutro precisa bypass total e se fade parcial pode operar por regiao/camada.
|
|
|
|
5. Cache de composicao.
|
|
Delimitar se layers estaticas/HUD podem manter cache intermediario.
|
|
|
|
6. Primitivas e contrato operacional.
|
|
Definir quais draws devem continuar sendo vistos como escrita destrutiva direta no framebuffer e onde valem aceleracoes internas sem mudar a semantica observavel.
|
|
|
|
7. Meta de custo.
|
|
Fechar qual teto de pixels/trabalho por frame e aceitavel no baseline.
|
|
|
|
8. Orcamento de memoria.
|
|
Definir quais otimizacoes aceitam memoria adicional e quais precisam caber no modelo mais economico possivel para handheld barato.
|
|
|
|
## Open Questions de Arquitetura
|
|
|
|
1. Quais partes do custo atual estao no `render_all()` e quais partes estao na conversao/apresentacao do host?
|
|
2. Qual o maior ganho de curto prazo dentro do modelo atual: buckets, fades, HUD, primitivas ou host copy/convert?
|
|
3. Dirty regions restritas quebram alguma expectativa de determinismo visivel ou sao apenas um detalhe interno de implementacao?
|
|
4. O HUD deve continuar no mesmo pipeline da world scene?
|
|
5. Quais primitivas merecem tratamento especial para reduzir overdraw sem mudar a semantica de sobrescrita?
|
|
6. Qual o teto aceitavel de memoria extra para caches, buffers separados ou estruturas auxiliares no handheld alvo?
|
|
|
|
## Problemas de Medio e Grande Porte Identificados
|
|
|
|
### 1. Composicao completa do framebuffer a cada frame logico
|
|
|
|
`render_all()` recompõe o `back` inteiro por software, mesmo quando a mudanca visual e pequena.
|
|
|
|
Impacto:
|
|
|
|
- custo cresce com a quantidade de pixels tocados, nao apenas com a quantidade de estado alterado;
|
|
- layers, sprites e fades disputam o mesmo budget de CPU;
|
|
- o modelo atual favorece previsibilidade sem ainda ter atalhos internos suficientes.
|
|
|
|
### 2. Rasterizacao pixel a pixel de tilemaps
|
|
|
|
Cada layer visivel percorre tiles e, para cada tile nao vazio, resolve indices e escreve pixel a pixel no framebuffer.
|
|
|
|
Impacto:
|
|
|
|
- o custo real esta mais na escrita de pixels do que na manutencao do `TileMap`;
|
|
- scroll e barato como estado, mas caro na hora de compor a imagem se tudo for repintado;
|
|
- layers estaticas continuam pagando custo de rasterizacao em toda recomposicao.
|
|
|
|
### 3. Rasterizacao pixel a pixel de sprites e escolha por bucket full scan
|
|
|
|
Os 512 sprites sao varridos para reconstruir buckets e os sprites ativos sao desenhados pixel a pixel.
|
|
|
|
Impacto:
|
|
|
|
- quando poucos sprites mudam, ainda existe custo fixo de rebuild dos buckets;
|
|
- overdraw entre sprites e layers pode explodir o numero de writes no `back`;
|
|
- a escolha de sprites ativos/prioridade ainda e simples demais para cenarios com alta ocupacao.
|
|
|
|
### 4. Fades fullscreen com custo proporcional ao frame inteiro
|
|
|
|
`scene fade` e `hud fade` percorrem o framebuffer inteiro quando ativos.
|
|
|
|
Impacto:
|
|
|
|
- custo grande e previsivel, mas potencialmente desproporcional;
|
|
- fade neutro ja pode ser bypass, mas fade parcial ainda custa uma passada completa;
|
|
- dois passes fullscreen no mesmo frame podem virar gargalo antes de layers ou sprites.
|
|
|
|
### 5. HUD no mesmo caminho critico da composicao principal
|
|
|
|
O HUD e redesenhado como tilemap depois da world scene e antes do `hud fade`.
|
|
|
|
Impacto:
|
|
|
|
- HUD estatico continua gerando trabalho por frame;
|
|
- mistura responsabilidades de world/UI no mesmo budget de composicao;
|
|
- dificulta isolar ganho de cache ou dirtying so para interface.
|
|
|
|
### 6. Conversao obrigatoria de RGB565 para RGBA8 no host
|
|
|
|
No `RedrawRequested`, o host percorre todo o `front_buffer` e converte para o frame do `pixels`.
|
|
|
|
Impacto:
|
|
|
|
- ha custo de leitura do framebuffer inteiro e escrita de um segundo buffer inteiro;
|
|
- esse custo existe mesmo quando o host esta apenas apresentando o mesmo quadro logico;
|
|
- pode competir com o custo do renderer sem estar visivel na discussao do `gfx`.
|
|
|
|
### 7. Pipeline serializado no host desktop
|
|
|
|
`tick`, `render_all()`, conversao de formato e apresentacao ocorrem no mesmo fluxo operacional.
|
|
|
|
Impacto:
|
|
|
|
- o frame time final acumula custo de runtime, composicao, copy/convert e present;
|
|
- falta desacoplamento para esconder latencia entre producao e apresentacao;
|
|
- dificulta atribuir gargalo sem instrumentacao por etapa.
|
|
|
|
## Estudo Inicial de Possiveis Optimizacoes
|
|
|
|
### A. Dirty flags por subsistema em vez de dirty rect generico
|
|
|
|
Ideia:
|
|
|
|
- flags independentes para `world`, `sprites`, `hud`, `scene_fade`, `hud_fade`, `host_present`.
|
|
|
|
Vantagens:
|
|
|
|
- preserva a semantica de framebuffer destrutivo;
|
|
- reduz recomposicao desnecessaria quando so um subsistema mudou;
|
|
- e muito mais simples de validar do que dirty regions arbitrarias.
|
|
|
|
Riscos:
|
|
|
|
- exige definir dependencias claras entre world, sprites, HUD e fades;
|
|
- pode induzir falsos positivos conservadores, o que e aceitavel no v1.
|
|
|
|
### B. Rebuild incremental ou condicional dos buckets de sprite
|
|
|
|
Ideia:
|
|
|
|
- so reconstruir buckets quando algum `GfxSetSprite` mudar atividade, prioridade, banco ou tile;
|
|
- opcionalmente manter contadores/sinais de mutacao da OAM.
|
|
|
|
Vantagens:
|
|
|
|
- elimina custo fixo por frame quando OAM permanece estavel;
|
|
- combina com a ideia de sprites como estado, nao como draw list efemera.
|
|
|
|
Riscos:
|
|
|
|
- precisa invalidacao correta para evitar bucket desatualizado;
|
|
- ganho pode ser pequeno se o custo dominante estiver no draw dos sprites, nao no rebuild.
|
|
|
|
### C. Separar world e HUD em buffers logicos distintos
|
|
|
|
Ideia:
|
|
|
|
- manter um buffer da world scene e um buffer do HUD, compondo no final apenas quando necessario.
|
|
|
|
Vantagens:
|
|
|
|
- HUD estatico deixa de participar da recomposicao da world;
|
|
- permite cache e fade especificos para cada dominio;
|
|
- se alinha bem com a separacao conceitual entre cena e interface.
|
|
|
|
Riscos:
|
|
|
|
- aumenta uso de memoria e pontos de sincronizacao;
|
|
- precisa manter semantica clara de sobrescrita entre mundo e HUD.
|
|
- pode ser inviavel no perfil de handheld barato se exigir buffers adicionais permanentes.
|
|
|
|
### D. Cache de layer estatica ou composicao parcial da world
|
|
|
|
Ideia:
|
|
|
|
- layers que nao mudam podem manter imagem intermediaria pronta;
|
|
- scroll, tile updates ou troca de bank invalidam o cache correspondente.
|
|
|
|
Vantagens:
|
|
|
|
- reduz rasterizacao repetida de cenario estavel;
|
|
- aproxima o ganho de hardware tile-based sem abandonar software raster.
|
|
|
|
Riscos:
|
|
|
|
- cache amplo demais vira complexidade estrutural;
|
|
- se scroll muda constantemente, o ganho cai bastante.
|
|
- pode consumir memoria demais para um hardware com orcamento agressivo.
|
|
|
|
### E. Otimizacao de fades
|
|
|
|
Ideia:
|
|
|
|
- bypass total para fade neutro;
|
|
- opcionalmente aplicar fade apenas sobre buffers relevantes;
|
|
- estudar LUTs ou blend mais barato por pixel.
|
|
|
|
Vantagens:
|
|
|
|
- ataca um custo fullscreen claramente delimitado;
|
|
- baixo risco conceitual.
|
|
|
|
Riscos:
|
|
|
|
- fade parcial por regiao pode complicar demais o contrato;
|
|
- LUT ajuda aritmetica, mas nao elimina custo de varrer memoria.
|
|
|
|
### F. Melhorias em primitivas e spans de memoria
|
|
|
|
Ideia:
|
|
|
|
- acelerar `fill_rect`, linhas horizontais/verticais, clears e possiveis blits contiguos;
|
|
- explorar caminhos de row spans contiguos em vez de loops mais genericos.
|
|
|
|
Vantagens:
|
|
|
|
- melhora direta do modelo de desenho destrutivo;
|
|
- baixo risco arquitetural;
|
|
- cria fundacao para outros atalhos internos.
|
|
|
|
Riscos:
|
|
|
|
- ganho localizado se o workload dominante vier de tile/sprite compositing;
|
|
- precisa medir por primitive class.
|
|
|
|
### G. Reduzir custo de copy/convert no host
|
|
|
|
Ideia:
|
|
|
|
- medir separadamente `draw_rgb565_to_rgba8` e `pixels.render()`;
|
|
- estudar formato mais proximo do host ou conversao mais barata;
|
|
- evitar redraw host quando nao houver novo front relevante.
|
|
|
|
Vantagens:
|
|
|
|
- ataca custo fora do `gfx` que ainda entra no frame time total;
|
|
- pode render ganho imediato no desktop host.
|
|
|
|
Riscos:
|
|
|
|
- parte do custo depende da stack `pixels/wgpu`, nao apenas do runtime;
|
|
- alguns ganhos podem ser especificos do host desktop e nao do contrato do console.
|
|
|
|
## Restricao de Plataforma
|
|
|
|
Qualquer recomendacao desta agenda deve ser filtrada por um criterio adicional:
|
|
|
|
- priorizar solucoes que melhorem custo sem multiplicar buffers;
|
|
- tratar memoria extra como recurso escasso de primeira classe;
|
|
- preferir flags, metadados pequenos e estruturas incrementais a caches grandes;
|
|
- evitar solucoes cuja performance dependa de assumir um host desktop mais forte do que o handheld alvo.
|
|
|
|
Direcao atual da discussao:
|
|
|
|
- nesta etapa, buffers extras devem ficar fora;
|
|
- o foco deve ser otimizar ao maximo o pipeline existente;
|
|
- cache intermediario ou novos buffers so entram em estudo depois que as otimizacoes de baixo custo de memoria forem medidas e esgotadas.
|
|
|
|
## Sugestao / Recomendacao Atual
|
|
|
|
Priorizar o estudo em camadas, nesta ordem:
|
|
|
|
1. instrumentar o pipeline por etapa;
|
|
2. validar dirty flags por subsistema como mecanismo minimo de invalidacao;
|
|
3. testar rebuild condicional de buckets e bypass/isolamento de fades;
|
|
4. otimizar primitivas e caminhos contiguos de escrita no framebuffer atual;
|
|
5. estudar separacao world/HUD e cache de layer apenas se os dados justificarem e o teto de memoria permitir;
|
|
5. tratar copy/convert do host como frente paralela de otimizacao, nao como substituto da analise do `gfx`.
|
|
|
|
## Achados Consolidados Ate Aqui
|
|
|
|
1. O contrato base continua sendo framebuffer destrutivo em memoria.
|
|
Draws escrevem diretamente no buffer de trabalho e operacoes posteriores sobrescrevem o que estiver por baixo. A agenda nao aponta para migracao a um renderer tipo GPU, retained mode ou compositor sofisticado.
|
|
|
|
2. O `present()` atual nao parece ser o problema principal.
|
|
No `gfx`, `present()` faz swap de buffers, nao copia completa de pixels.
|
|
|
|
3. O custo suspeito esta na recomposicao e na apresentacao, nao na manutencao do estado logico.
|
|
Escritas de `TileMap`, scroll e `GfxSetSprite` sao pequenas e pontuais; o custo potencialmente dominante esta em rasterizar pixels, aplicar fades fullscreen e converter o framebuffer para o host.
|
|
|
|
4. Os maiores suspeitos atuais de custo sao:
|
|
- rasterizacao de layers;
|
|
- rasterizacao de sprites;
|
|
- fades fullscreen;
|
|
- conversao `RGB565 -> RGBA8` no host;
|
|
- `pixels.render()` / apresentacao no host.
|
|
|
|
5. O pipeline atual deve ser otimizado antes de considerar buffers extras.
|
|
A direcao aceita da discussao e esgotar primeiro flags pequenas, bypasses, melhorias de primitive paths e ajustes incrementais no pipeline existente.
|
|
|
|
6. Restricao de plataforma pesa tanto quanto CPU.
|
|
Como o alvo e um handheld proprio com limitacoes de orcamento e memoria, solucoes que consumam RAM adicional significativa devem ficar fora desta fase.
|
|
|
|
7. A instrumentacao minima desejada para retomar o estudo ficou definida.
|
|
Quando houver workload representativo, queremos medir ao menos:
|
|
- `render_all_total_us`
|
|
- `bucket_rebuild_us`
|
|
- `layer_raster_us`
|
|
- `sprite_raster_us`
|
|
- `scene_fade_us`
|
|
- `hud_raster_us`
|
|
- `hud_fade_us`
|
|
- `host_convert_us`
|
|
- `host_present_us`
|
|
|
|
8. A retencao de metricas deve ser barata e orientada a analise posterior.
|
|
A preferencia atual e por acumuladores e ring buffer pequeno de snapshots, sem logging pesado por frame no hot path.
|
|
|
|
9. Ring buffer no `TileMap` nao e prioridade nesta fase.
|
|
O custo suspeito nao esta na manutencao da grade logica do mapa, e sim na composicao da janela visivel no framebuffer. Mudar a estrutura do mapa so faria sentido se o gargalo principal estivesse em streaming/atualizacao estrutural do cenario, o que nao e a hipotese atual.
|
|
|
|
10. A direcao de otimizacao mais promissora e padronizar copias massivas para o framebuffer atual.
|
|
Em vez de atacar apenas tilemaps com loops pixel a pixel, a discussao passa a favorecer um padrao geral de blit/copia massiva com:
|
|
- calculo previo de offsets e spans;
|
|
- fast paths para casos contiguos;
|
|
- trabalho por linha/chunk em vez de trabalho atomizado por pixel quando possivel;
|
|
- reaproveitamento dessa infraestrutura para tilemaps, sprites e outras operacoes de desenho destrutivo.
|
|
|
|
11. Migrar o `back` para `RGBA8888` nao e direcao aceita neste momento.
|
|
Isso aumentaria custo de memoria e largura de banda no alvo handheld, alem de deslocar a otimizacao para o host desktop. O contrato interno continua preferencialmente em `RGB565`, mesmo considerando fades e uma futura pipeline de lights.
|
|
|
|
## Nova Direcao Tecnica Em Estudo
|
|
|
|
Quando esta agenda for reaberta, a linha principal de investigacao deve considerar:
|
|
|
|
- manter o `TileMap` simples como estado logico;
|
|
- assumir que a janela visivel provavelmente continuara sendo recomposta na maior parte dos frames;
|
|
- concentrar a otimizacao em como essa recomposicao escreve no `back`;
|
|
- desenhar uma infraestrutura compartilhada de copias massivas/blits para o framebuffer atual;
|
|
- usar essa infraestrutura nao apenas para tilemaps, mas como base comum para raster de sprites e outros caminhos de desenho.
|
|
|
|
Pergunta orientadora da reabertura:
|
|
|
|
- como transformar o renderer de um pipeline de writes atomizados por pixel em um pipeline com fast paths de spans/chunks, sem perder a semantica de framebuffer destrutivo e sem pagar memoria extra relevante?
|
|
|
|
## Status de Standby
|
|
|
|
Esta agenda deve ficar em espera ate existir um game ou workload real que exercite de forma representativa:
|
|
|
|
- tilemaps com scroll e composicao de world;
|
|
- sprites em quantidade suficiente para testar buckets e overdraw;
|
|
- HUD ativo;
|
|
- fades;
|
|
- apresentacao completa no host.
|
|
|
|
Antes disso, qualquer conclusao sobre gargalo ou prioridade de otimizacao tende a ser prematura.
|
|
|
|
## Proximo Gatilho Para Reabrir
|
|
|
|
Reabrir esta agenda quando houver:
|
|
|
|
- um game jogavel ou cena de teste que percorra o pipeline completo;
|
|
- dados de workload mais proximos do uso real;
|
|
- necessidade concreta de justificar uma rodada de instrumentacao e profiling.
|
|
|
|
## Dependencias
|
|
|
|
- `../specs/04-gfx-peripheral.md`
|
|
- `../specs/11-portability-and-cross-platform-execution.md`
|
|
|
|
## Criterio de Saida Desta Agenda
|
|
|
|
Pode virar PR quando houver decisao escrita sobre:
|
|
|
|
- filosofia explicita de framebuffer destrutivo como contrato base;
|
|
- plano de instrumentacao para localizar o custo dominante do pipeline;
|
|
- nivel minimo de invalidacao no v1;
|
|
- politica de rebuild de buckets de sprites;
|
|
- bypass/cache de fade e HUD;
|
|
- politica para otimizar primitivas sem mudar a semantica observavel;
|
|
- meta de custo para o render pipeline.
|