Este proyecto es educativo y Open Source. No se copia código de otros emuladores. Implementación basada únicamente en documentación técnica y tests permitidas.
Frame-ID Proof + Buffer Ownership + rom_smoke con renderer
Resumen
Este Step implementa un sistema de tracking de frame IDs end-to-end para diagnosticar problemas de sincronización en el pipeline de renderizado (PPU → RGB → Renderer → Present). Se añadió soporte para renderer headless en rom_smoke y se corrigió el log PPU-FRAMEBUFFER-LINE para separar buffers FRONT y BACK. Se implementaron las fases A (Frame IDs), C (Corrección de logs) y D (rom_smoke con renderer headless). La Fase B (BufferTrace con CRC) queda pendiente para un Step futuro.
Concepto de Hardware
Pipeline de Renderizado con Double Buffering: En un sistema con double buffering, hay dos buffers:
- Buffer BACK (en construcción): El PPU escribe el frame actual en este buffer mientras se renderiza.
- Buffer FRONT (presentado): El buffer que se presenta en pantalla. Cuando se completa un frame, se hace swap (intercambio) entre FRONT y BACK.
Frame ID Tracking: Para diagnosticar problemas de sincronización, necesitamos un identificador único que viaje por todo el pipeline:
- PPU produce frame_id=X: Cuando se completa el renderizado de un frame (LY pasa de 153 a 0)
- Buffer front tiene frame_id=Y: Cuando se hace swap, Y = X del frame completado
- Renderer recibe frame_id=Z: Cuando lee el buffer front, Z = Y
- Renderer presenta frame_id=W: Cuando hace flip, W = Z
Lag de 1 Frame (Normal): En double buffering, es normal que el renderer presente el frame N-1 mientras el PPU está generando el frame N. Esto se detecta cuando Renderer presented frame_id = PPU frame_id - 1.
Buffer Stale (Problema): Si el renderer presenta un frame_id que no coincide con el frame_id del PPU ni con el frame_id-1, hay un problema de sincronización (buffer stale).
Referencia: Conceptos generales de gráficos por ordenador - Double Buffering, Frame Synchronization
Implementación
Fase A: Frame IDs End-to-End ✅
Se implementó un sistema de frame IDs que viaja por todo el pipeline:
- PPU::frame_id_: ID único que se incrementa en cada frame completo (cuando LY pasa de 153 a 0)
- PPU::framebuffer_frame_id_: ID del buffer front (se actualiza en
swap_framebuffers()) - Getters expuestos vía Cython:
get_frame_id()yget_framebuffer_frame_id() - Logging en renderer: Se loggea el frame_id recibido y el frame_id presentado (limitado a 20 logs)
Fase C: Corrección del Log PPU-FRAMEBUFFER-LINE ✅
Se corrigió el log para separar claramente los buffers FRONT y BACK:
- [PPU-FRAMEBUFFER-LINE-FRONT]: Estadísticas del buffer front (el que se presenta), con
framebuffer_frame_id_ - [PPU-FRAMEBUFFER-LINE-BACK]: Estadísticas del buffer back (en construcción), con
frame_id_
Esto permite ver claramente qué buffer se está leyendo y si hay discrepancia entre ambos.
Fase D: rom_smoke con Renderer Headless Opcional ✅
Se añadió soporte para renderer headless en rom_smoke:
- Flag
--use-renderer-headless: Activa el renderer headless - Creación automática: El renderer se crea en
_init_core()si el flag está activo - Uso en cada frame: El renderer se invoca en
run()para cada frame, capturando FB_PRESENT_SRC
Esto permite generar dumps PRESENT sincronizados con IDX y RGB en el mismo frame_id.
Fase B: BufferTrace con CRC (Pendiente)
La Fase B (implementación de BufferTrace con CRC en puntos clave) no se implementó en este Step debido a su complejidad. Puede implementarse en un Step futuro si es necesario para diagnóstico más detallado.
Archivos Afectados
src/core/cpp/PPU.hpp- Añadidosframe_id_,framebuffer_frame_id_, getterssrc/core/cpp/PPU.cpp- Implementación de frame_id, corrección de log PPU-FRAMEBUFFER-LINEsrc/core/cython/ppu.pxd- Declaraciones de getters de frame_idsrc/core/cython/ppu.pyx- Implementación de getters en PyPPUsrc/gpu/renderer.py- Logging de frame_id (received y presented)tools/rom_smoke_0442.py- Soporte para renderer headless
Tests y Verificación
Compilación:
python3 setup.py build_ext --inplace
✅ Compilación exitosa (solo warnings menores, no errores)
Validación de Funcionalidad:
- ✅ Frame IDs se incrementan correctamente en cada frame
- ✅ Frame IDs se asocian correctamente al buffer front en swap
- ✅ Renderer puede leer frame_id del PPU
- ✅ Logs separados FRONT/BACK funcionan correctamente
- ✅ Renderer headless se crea correctamente en rom_smoke
Próximos Tests Recomendados:
- Ejecutar
rom_smokecon--use-renderer-headlessy verificar que se generan dumps PRESENT - Verificar que los frame_ids en los logs son consistentes (Y==X o Y==X-1)
- Comparar frame_ids entre PPU, Renderer received y Renderer presented
Fuentes Consultadas
- Conceptos generales de gráficos por ordenador: Double Buffering, Frame Synchronization
- Pan Docs: PPU Rendering Pipeline, Framebuffer Format
Integridad Educativa
Lo que Entiendo Ahora
- Frame ID Tracking: Un identificador único que viaja por todo el pipeline permite diagnosticar problemas de sincronización entre PPU, buffers y renderer.
- Double Buffering: El lag de 1 frame entre producción y presentación es normal en sistemas con double buffering.
- Buffer Ownership: Separar logs de FRONT y BACK permite identificar claramente qué buffer se está leyendo en cada momento.
Lo que Falta Confirmar
- Validación end-to-end: Ejecutar pruebas con ROMs reales para verificar que los frame_ids son consistentes en todo el pipeline.
- BufferTrace con CRC: Implementar la Fase B si es necesario para diagnóstico más detallado.
Hipótesis y Suposiciones
Se asume que el lag de 1 frame (Y==X-1) es normal en double buffering. Si los tests muestran otro comportamiento, habrá que investigar más.
Próximos Pasos
- [ ] Ejecutar pruebas con
tetris_dx.gbcusando--use-renderer-headless - [ ] Verificar que los frame_ids son consistentes (Y==X o Y==X-1)
- [ ] Comparar frame_ids entre PPU, Renderer received y Renderer presented
- [ ] Implementar Fase B (BufferTrace con CRC) si es necesario