⚠️ Clean-Room / Educativo

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

Fecha: 2025-12-29 Step ID: 0340 Estado: VERIFIED

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 framebuffer
  • src/core/cpp/PPU.cpp - Agregados logs de timing cuando se marca frame_ready_ y verificación del framebuffer cuando hay tiles reales
  • src/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

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