--- id: AGD-0027 ticket: frame-composer-public-syscall-surface title: Agenda - FrameComposer Public Syscall Surface status: accepted created: 2026-04-17 updated: 2026-04-17 tags: [gfx, runtime, syscall, abi, frame-composer, scene, camera, sprites] --- ## Contexto `DEC-0014` e os planos `PLN-0017` a `PLN-0021` fecharam a migração interna do pipeline de frame para `FrameComposer`: - `FrameComposer` virou o orquestrador canônico do frame; - `Hardware` passou a agregá-lo ao lado de `Gfx`; - scene, camera, cache, resolver e sprite emission migraram para ownership interno dele; - o frame loop do runtime passou a renderizar via `FrameComposer.render_frame()`. Isso resolveu a base operacional interna, mas não fechou a superfície pública equivalente para a VM. A ABI pública ainda expõe apenas o contrato legado de `gfx.set_sprite(...)`, enquanto `bind_scene(...)` e `set_camera(...)` existem apenas como APIs internas do driver. Na prática, hoje temos uma assimetria: - a base canônica do frame está em `FrameComposer`; - mas a ABI pública ainda não trata `FrameComposer` como serviço canônico para scene, camera e sprites. Essa lacuna impede a migração do restante da stack e também impede um stress cartridge que atravesse de verdade o pipeline novo por syscall pública. ## Problema Precisamos definir a nova superfície pública de syscall para o pipeline canônico de `FrameComposer` sem reabrir a decisão já aceita sobre ownership interno do frame. O problema concreto não é “adicionar 2 ou 3 syscalls”. Precisamos decidir: - quais operações de `FrameComposer` viram ABI pública agora; - se `gfx.set_sprite(...)` continua como shim legado ou perde status canônico; - qual é o contrato mínimo de scene/camera que a VM pode observar/controlar; - como nomear e versionar essa superfície pública sem criar um segundo modelo canônico concorrente; - qual é a estratégia de transição para cartridge, runtime tests e stress tests; - como propagar essa mudança para a spec canônica e, se necessário, para contratos de ABI e `ISA_CORE`. ## Pontos Criticos - `DEC-0014` já fechou `FrameComposer` como base canônica interna; esta agenda não deve reabrir isso. - A ABI pública atual ainda expõe `gfx.set_sprite(...)` com semântica herdada de índice/slot, mesmo que a implementação interna já use frame emission. - `bind_scene(scene_bank_id)` e `set_camera(x, y)` já existem no driver, mas ainda não existem como syscalls públicas. - Se a nova ABI expuser demais logo de início, vamos congelar cedo demais detalhes que ainda não provaram valor operacional. - Se a nova ABI expuser de menos, manteremos um modelo híbrido por tempo demais: - canônico internamente via `FrameComposer`; - legado externamente via `Gfx`/`set_sprite`. - Precisamos decidir se o namespace público continua em `gfx.*` por estabilidade do domínio, ou se devemos introduzir algo como `frame.*`. - A transição precisa preservar compatibilidade suficiente para não quebrar cartridges e testes existentes antes da migração do restante. - O contrato de sprite precisa deixar claro se o chamador ainda informa índice, se informa `layer`, e se `active` continua existindo na superfície pública. - A mudança não pode ficar só em código/runtime; a spec canônica precisa ser atualizada para refletir o novo serviço público. - Se o contrato público afetar superfícies documentadas de ABI ou o material de `ISA_CORE`, essa propagação precisa ser tratada como parte da mesma thread, não como follow-up solto. ## Opcoes ### Opcao 1 - Expor um núcleo mínimo canônico em `gfx.*` **Como seria:** Adicionar apenas a superfície mínima para a VM controlar o pipeline novo: - `gfx.bind_scene(bank_id)` - `gfx.unbind_scene()` - `gfx.set_camera(x, y)` - `gfx.emit_sprite(...)` `gfx.set_sprite(...)` permaneceria por um período como shim legado de compatibilidade. **Vantagens:** - fecha rapidamente a lacuna operacional; - habilita stress real do pipeline novo; - reduz o tempo de convivência entre modelo canônico e legado; - mantém o domínio público em `gfx`, evitando churn de namespace. **Desvantagens:** - introduz ABI nova que precisará de migração coordenada; - exige definir `emit_sprite(...)` com cuidado para não herdar sem querer o modelo de slot. ### Opcao 2 - Expor scene/camera agora e adiar o contrato novo de sprite **Como seria:** Publicar apenas: - `gfx.bind_scene(bank_id)` - `gfx.unbind_scene()` - `gfx.set_camera(x, y)` Sprites continuariam publicamente via `gfx.set_sprite(...)` até uma segunda fase. **Vantagens:** - menor mudança imediata de ABI; - desbloqueia o stress do world path e da câmera; - reduz o volume inicial da migração pública. **Desvantagens:** - mantém dois modelos públicos de sprite por mais tempo; - prolonga a semântica de compatibilidade do syscall legado; - adia exatamente uma das partes centrais da migração para `FrameComposer`. ### Opcao 3 - Criar um novo namespace público separado, como `composer.*` **Como seria:** O pipeline novo ganha syscalls em um domínio separado, por exemplo: - `composer.bind_scene` - `composer.unbind_scene` - `composer.set_camera` - `composer.emit_sprite` `gfx.*` ficaria como superfície legacy/low-level. **Vantagens:** - deixa explícita a mudança de serviço canônico; - evita sobrecarregar semanticamente `gfx`. **Desvantagens:** - adiciona churn conceitual e de nomenclatura; - fragmenta demais a superfície pública neste momento; - cria um custo de transição maior sem benefício operacional evidente. ## Sugestao / Recomendacao Seguir com a **Opcao 3**. Direção recomendada: - a superfície pública canônica deve migrar para o domínio `composer.*`; - `FrameComposer` vira a base canônica também na ABI pública, com namespace próprio em vez de continuar semanticamente preso a `gfx.*`; - o núcleo mínimo público deve ser: - `composer.bind_scene(bank_id) -> status` - `composer.unbind_scene()` - `composer.set_camera(x, y)` - `composer.emit_sprite(...) -> status` - `gfx.set_sprite(...)` deve morrer e ser removido completamente do contrato público. Para sprites, a recomendação provisória é: - a nova ABI pública não deve exigir índice explícito; - `composer.emit_sprite(...)` deve receber o payload completo necessário para o frame: - `glyph_id` - `palette_id` - `x` - `y` - `layer` - `bank_id` - `flip_x` - `flip_y` - `priority` - a ABI pode futuramente agrupar esse payload se isso melhorar ergonomia, mas o contrato mínimo deve nascer completo; - `active` não deve continuar no contrato canônico novo; - overflow continua sendo ignorado com status/telemetria adequada, sem trapar o runtime. Para scene/camera, a recomendação provisória é: - manter o contrato mínimo já aceito internamente; - `bind_scene` por bank id; - `unbind_scene` explícito; - `set_camera(x, y)` em pixel space com top-left viewport. - `bind_scene(...)`, `unbind_scene(...)` e `emit_sprite(...)` devem usar `ComposerOpStatus` como retorno operacional canônico. ## Perguntas em Aberto - Resolvido: - o nome público canônico de sprite será `composer.emit_sprite(...)`; - o syscall novo de sprite nasce completo com `glyph_id`, `palette_id`, `x`, `y`, `layer`, `bank_id`, `flip_x`, `flip_y`, `priority`; - `gfx.set_sprite(...)` deve morrer e ser removido completamente; - não haverá leitura de estado nesta primeira fase; - `bind_scene(...)`, `unbind_scene(...)` e `emit_sprite(...)` usarão `ComposerOpStatus`; - A ABI nova precisa expor refresh explícito, ou isso deve continuar totalmente interno ao `FrameComposer`? - Resolvido: - a ABI nova não deve expor refresh explícito; - o domínio público canônico será `composer.*`, não `gfx.*`. ## Criterio para Encerrar Esta agenda pode ser encerrada quando houver acordo explícito sobre: - a lista mínima de syscalls públicas canônicas do `FrameComposer`; - o nome canônico da operação pública de sprite; - a remoção completa de `gfx.set_sprite(...)` do contrato público; - o formato de retorno/status das novas operações; - a estratégia de transição necessária para decisão, plano e migração do restante da stack. ## Resolucao em Andamento Direção atualmente acordada nesta agenda: - o namespace público canônico será `composer.*`; - o núcleo mínimo inicial será: - `composer.bind_scene(bank_id) -> ComposerOpStatus` - `composer.unbind_scene() -> ComposerOpStatus` - `composer.set_camera(x, y)` - `composer.emit_sprite(glyph_id, palette_id, x, y, layer, bank_id, flip_x, flip_y, priority) -> ComposerOpStatus` - não haverá introspecção pública nesta primeira fase; - refresh/cache policy continua interno ao `FrameComposer`; - `gfx.set_sprite(...)` não terá caminho de compatibilidade e deve ser removido. ## Resolucao Esta agenda fica aceita com os seguintes pontos fechados: - o namespace público canônico do serviço será `composer.*`; - a superfície mínima inicial será: - `composer.bind_scene(bank_id) -> ComposerOpStatus` - `composer.unbind_scene() -> ComposerOpStatus` - `composer.set_camera(x, y)` - `composer.emit_sprite(glyph_id, palette_id, x, y, layer, bank_id, flip_x, flip_y, priority) -> ComposerOpStatus` - não haverá introspecção pública nesta primeira fase; - não haverá refresh/cache policy público; - `gfx.set_sprite(...)` deve ser removido completamente, sem shim de compatibilidade; - a transição deve introduzir `composer.*` e remover `gfx.set_sprite(...)` na mesma thread de migração, com atualização coordenada de bytecode, cartridges, tests e runtime; - a mesma thread deve atualizar a spec canônica do assunto e propagar a mudança para contratos de ABI e `ISA_CORE` quando essas superfícies forem impactadas pelo novo serviço público.