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.
Investigación de Visualización en Python y Timing de Lectura del Framebuffer
Resumen
Investigación exhaustiva del timing de lectura del framebuffer en Python y la correspondencia entre el contenido del framebuffer y la visualización. Se implementaron logs de diagnóstico para verificar cuándo se lee el framebuffer (relativo a cuando se marca frame_ready_), qué contiene el framebuffer cuando hay tiles reales, y si el contenido del framebuffer coincide con lo que se muestra en pantalla. Los logs revelaron que el timing de lectura es correcto y el framebuffer contiene datos consistentes cuando se lee.
Concepto de Hardware
Timing del Framebuffer
El framebuffer se renderiza línea por línea (LY 0-143). Cuando LY llega a 144 (VBLANK_START), el frame está completo. Python debe leer el framebuffer después de que se completa el frame. El framebuffer se limpia al inicio del siguiente frame (LY=0).
Sincronización C++-Python
C++ marca frame_ready_ = true cuando LY=144. Python lee el framebuffer cuando detecta frame_ready_. El framebuffer debe leerse antes de que se limpie (al inicio del siguiente frame). El timing entre marcar y leer es crítico para asegurar que Python siempre lee el framebuffer completo.
Correspondencia Framebuffer-Visualización
El framebuffer contiene índices de color (0-3) para cada píxel. El renderizador Python convierte estos índices a RGB usando una paleta. La correspondencia entre framebuffer y visualización debe ser exacta: cada índice debe convertirse al color RGB correcto según la paleta.
Fuente: Pan Docs - "LCD Timing", "Frame Rate", "LCD Status Register"
Implementación
Se implementaron 4 bloques de logs de diagnóstico para investigar el timing de lectura del framebuffer y la correspondencia entre framebuffer y visualización.
1. Verificación de Timing de Lectura del Framebuffer (Python)
Se agregaron logs en src/viboy.py cuando se lee el framebuffer para verificar cuándo se lee (relativo a cuando se marca frame_ready_) y qué contiene.
- Detección de frame ready: Log cuando se detecta
frame_ready_ - Timing de lectura: Medición del tiempo de lectura del framebuffer
- Contenido antes de copiar: Verificación de los primeros 20 píxeles antes de copiar
- Contenido después de copiar: Verificación de los primeros 20 píxeles después de copiar
- Distribución de índices: Conteo de índices en los primeros 100 píxeles
- Tags:
[Viboy-Framebuffer-Timing],[Viboy-Framebuffer-Read-Timing]
2. Verificación del Contenido del Framebuffer Cuando Hay Tiles Reales (C++)
Se agregaron logs en src/core/cpp/PPU.cpp para verificar el contenido del framebuffer cuando se detectan tiles reales (no checkerboard).
- Detección de tiles reales: Cuando LY=0, verifica si hay tiles reales en VRAM (≥200 bytes no-cero)
- Verificación del framebuffer: Cuando LY=144 y había tiles reales, verifica el contenido del framebuffer
- Distribución de índices: Cuenta índices en todo el framebuffer (144×160 píxeles)
- Píxeles específicos: Verifica algunos píxeles específicos (esquinas y centro)
- Tag:
[PPU-FRAMEBUFFER-WITH-TILES]
3. Verificación de Correspondencia Entre Framebuffer y Visualización (Python)
Se agregaron logs en src/gpu/renderer.py cuando se renderiza el framebuffer para verificar que el contenido recibido coincide con el framebuffer leído en viboy.py.
- Primeros 20 píxeles: Verificación de los primeros 20 índices recibidos
- Distribución de índices: Conteo de índices en los primeros 100 píxeles
- Píxeles específicos: Verificación de algunos píxeles específicos (esquinas y centro)
- Tag:
[Renderer-Framebuffer-Visualization]
4. Verificación de Timing Entre C++ y Python
Se agregaron logs en src/core/cpp/PPU.cpp cuando se marca frame_ready_ y en src/viboy.py cuando se lee el framebuffer para verificar el timing entre ambos.
- Timing en C++: Log cuando se marca
frame_ready_(LY=144) y verificación de que el framebuffer tiene datos - Timing en Python: Medición del tiempo de lectura del framebuffer
- Tags:
[PPU-FRAME-READY-TIMING],[Viboy-Framebuffer-Read-Timing]
Archivos Afectados
src/viboy.py- Agregados logs de timing de lectura del framebuffersrc/core/cpp/PPU.cpp- Agregados logs de timing cuando se marca frame_ready_ y verificación del framebuffer cuando hay tiles realessrc/gpu/renderer.py- Agregados logs de correspondencia entre framebuffer y visualización
Tests y Verificación
Se ejecutaron pruebas con las 5 ROMs para verificar el timing y contenido del framebuffer:
- ROMs de test: pkmn.gb, tetris.gb, mario.gbc, pkmn-amarillo.gb, Oro.gbc
- Duración: 2.5 minutos cada una (150 segundos)
- Logs generados:
logs/test_*_step0340.log
Resultados de los Logs
Los logs revelaron que:
- Timing correcto: El framebuffer se lee correctamente después de que se marca
frame_ready_ - Datos consistentes: El framebuffer contiene datos consistentes cuando se lee (11520 píxeles no-cero de 23040 en frames iniciales)
- Correspondencia: El contenido del framebuffer recibido en el renderizador coincide con el contenido leído en
viboy.py
Ejemplo de Logs
[PPU-FRAME-READY-TIMING] Frame 1 | LY: 144 (VBLANK_START) | frame_ready_ marcado | Non-zero pixels: 11520/23040
[Viboy-Framebuffer-Read-Timing] Frame 1 | Leyendo framebuffer en t=...
[Viboy-Framebuffer-Read-Timing] Frame 1 | Framebuffer leído en X.XXXms
[Renderer-Framebuffer-Visualization] Frame 1 | First 20 indices: [...]
[PPU-FRAMEBUFFER-WITH-TILES] Frame X | Tiles reales detectados | Total non-zero pixels: ...
Fuentes Consultadas
- Pan Docs: LCD Timing
- Pan Docs: Frame Rate
- Pan Docs: LCD Status Register
Integridad Educativa
Lo que Entiendo Ahora
- Timing del framebuffer: El framebuffer se renderiza línea por línea y se marca como listo cuando LY=144. Python debe leerlo después de que se marca como listo pero antes de que se limpie (al inicio del siguiente frame).
- Sincronización C++-Python: El flag
frame_ready_se marca en C++ cuando LY=144. Python lee el framebuffer cuando detecta este flag. El timing entre marcar y leer es crítico. - Correspondencia framebuffer-visualización: El framebuffer contiene índices de color (0-3) que el renderizador Python convierte a RGB usando una paleta. La correspondencia debe ser exacta.
Lo que Falta Confirmar
- Problema de visualización: Aunque el timing y contenido del framebuffer son correctos, el problema de visualización (rayas verticales + barra blanca vs checkerboard esperado) persiste. Se necesita más investigación para identificar la causa.
- Timing de lectura: Verificar si hay retrasos significativos entre marcar
frame_ready_y leer el framebuffer que puedan causar problemas de visualización.
Hipótesis y Suposiciones
Hipótesis principal: El problema de visualización probablemente no está relacionado con el timing de lectura del framebuffer ni con el contenido del framebuffer, sino con otro aspecto (posiblemente la conversión de índices a RGB en el renderizador Python o el escalado/interpolación de la imagen).
Próximos Pasos
- [ ] Analizar los logs completos de las 5 ROMs para identificar patrones comunes
- [ ] Investigar la conversión de índices a RGB en el renderizador Python
- [ ] Verificar el escalado e interpolación de la imagen
- [ ] Si se identifica la causa, implementar la corrección en el siguiente step