diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime.rs b/crates/console/prometeu-system/src/virtual_machine_runtime.rs index 37935e97..ea1a0bc7 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime.rs @@ -42,8 +42,8 @@ pub struct VirtualMachineRuntime { } impl VirtualMachineRuntime { - pub const CYCLES_PER_LOGICAL_FRAME: u64 = 100_000; - pub const SLICE_PER_TICK: u64 = 100_000; + pub const CYCLES_PER_LOGICAL_FRAME: u64 = 5_000_000; + pub const SLICE_PER_TICK: u64 = 5_000_000; pub const MAX_LOG_LEN: usize = 256; pub const MAX_LOGS_PER_FRAME: u32 = 10; } diff --git a/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md b/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md index baf123e2..42307ee1 100644 --- a/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md +++ b/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md @@ -17,6 +17,10 @@ O renderer `gfx` recompõe a cena inteira a cada frame logico, mesmo quando a mu 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. @@ -30,34 +34,362 @@ Hoje `render_all()` reconstrói buckets de sprites, escaneia os 512 sprites, red ## Alvo da Discussao -Definir ate onde o v1 precisa sair do modelo brute force para um pipeline com invalidacao e custo previsivel. +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. Granularidade de invalidacao. +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 region por layer; - - dirty list por sprite/HUD; + - dirty flags por subsistema (`world`, `hud`, `fade`, `sprites`); + - dirty regions restritas e derivadas de primitivas/sprites; - combinacao minima viavel. -2. Buckets e traversal de sprites. +3. Buckets e traversal de sprites. Decidir se os buckets continuam sendo reconstruidos por frame ou se viram estrutura incremental. -3. Fades. +4. Fades. Definir se fade neutro precisa bypass total e se fade parcial pode operar por regiao/camada. -4. Cache de composicao. +5. Cache de composicao. Delimitar se layers estaticas/HUD podem manter cache intermediario. -5. Meta de custo. +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. O fantasy console quer simular hardware de full redraw ou quer um renderer pragmatico para handheld barato? -2. Dirty regions quebram alguma expectativa de determinismo visivel? -3. O HUD deve continuar no mesmo pipeline da world scene? +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 @@ -68,7 +400,10 @@ Definir ate onde o v1 precisa sair do modelo brute force para um pipeline com in 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.