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 Renderizado de Líneas y Estado del Framebuffer
Resumen
Investigación exhaustiva del renderizado de líneas y el estado del framebuffer para identificar por qué algunas líneas del framebuffer están vacías cuando deberían tener datos. Se implementaron logs de diagnóstico para verificar si todas las líneas se renderizan (LY 0-143), si el framebuffer se limpia durante el renderizado, si hay líneas que se saltan, y el estado completo del framebuffer al final de cada frame. Los logs revelaron que todas las líneas se renderizan correctamente y el framebuffer tiene datos consistentes en todas las líneas.
Concepto de Hardware
Scanlines (Líneas de Escaneo)
La Game Boy tiene 144 líneas visibles (LY 0-143). Cada línea se renderiza durante H-Blank (Mode 0). Después de la línea 143, hay V-Blank (líneas 144-153). El registro LY (Line Y) se incrementa automáticamente después de cada línea y se resetea a 0 al inicio de cada frame.
Framebuffer
El framebuffer contiene índices de color (0-3) para cada píxel de la pantalla. Tamaño: 160×144 = 23,040 píxeles. Cada línea ocupa 160 píxeles en el framebuffer. El framebuffer se genera línea por línea durante el renderizado de cada scanline y se limpia al inicio del siguiente frame (cuando LY se resetea a 0).
Renderizado de Líneas
render_scanline() se llama para cada línea visible (LY 0-143). El renderizado debe completarse antes de que Python lea el framebuffer. El framebuffer se limpia al inicio del siguiente frame (LY=0), asegurando que Python siempre lee el framebuffer ANTES de que se limpie.
Fuente: Pan Docs - "LCD Status Register", "LY Register", "LCD Timing"
Implementación
Se implementaron 5 bloques de logs de diagnóstico en PPU.cpp para investigar el renderizado de líneas y el estado del framebuffer.
1. Verificación de Renderizado de Todas las Líneas
Se agregaron logs en PPU::render_scanline() al inicio para verificar que todas las líneas se renderizan (LY 0-143). Los logs marcan cada línea cuando se renderiza y verifican si hay líneas que no se renderizan.
- Array de líneas renderizadas: Array estático de 144 booleanos que marca cada línea cuando se renderiza
- Verificación periódica: Cada 10 frames, verifica si todas las líneas se han renderizado
- Tag:
[PPU-LINES-RENDER]
2. Verificación del Estado del Framebuffer Línea por Línea
Se agregaron logs en PPU::render_scanline() al final para verificar el estado del framebuffer después de renderizar cada línea. Los logs verifican líneas específicas (0, 72, 143) y algunas aleatorias.
- Distribución de índices: Cuenta cuántos píxeles tienen cada índice de color (0, 1, 2, 3) en cada línea
- Píxeles específicos: Verifica algunos píxeles específicos (x=0, 40, 80, 120, 159) para ver qué índices contienen
- Advertencias: Advierte si una línea está completamente vacía después de renderizar
- Tag:
[PPU-FRAMEBUFFER-LINE]
3. Verificación Mejorada de Limpieza del Framebuffer
Se mejoraron los logs existentes en PPU::clear_framebuffer() para verificar que el framebuffer solo se limpia al inicio del frame (LY=0) y no durante el renderizado (LY 1-143).
- Log de limpieza: Loggea cada vez que se limpia el framebuffer con el frame y LY actual
- Advertencia: Advierte si el framebuffer se limpia durante el renderizado (no debería pasar)
- Tag:
[PPU-CLEAR-FRAMEBUFFER]
4. Verificación de Secuencia de Líneas
Se agregaron logs en PPU::step() cuando LY cambia para verificar que LY incrementa correctamente (0, 1, 2, ..., 143, 144, ..., 153, 0) y no se saltan líneas.
- Verificación de secuencia: Verifica si LY incrementa correctamente o si se saltan líneas
- Log periódico: Loggea cada 10 líneas o líneas importantes (0, 72, 143, 144)
- Tag:
[PPU-LY-SEQUENCE]
5. Verificación del Estado Completo del Framebuffer al Final del Frame
Se agregaron logs en PPU::step() cuando LY llega a 144 (VBLANK_START) para verificar el estado completo del framebuffer al final del frame.
- Líneas con datos: Cuenta cuántas líneas tienen datos (no todas blancas)
- Píxeles no-cero: Cuenta cuántos píxeles tienen índices no-cero (1, 2, 3)
- Distribución global: Muestra la distribución de índices en todo el framebuffer
- Advertencias: Advierte si hay pocas líneas con datos o si el framebuffer está completamente vacío
- Tag:
[PPU-FRAMEBUFFER-COMPLETE]
Componentes creados/modificados
src/core/cpp/PPU.cpp: Agregados 5 bloques de logs de diagnóstico enrender_scanline(),clear_framebuffer()ystep()
Archivos Afectados
src/core/cpp/PPU.cpp- Agregados logs de diagnóstico del Step 0339
Tests y Verificación
Se ejecutaron pruebas con la ROM de Pokémon (pkmn.gb) durante 2.5 minutos. Los logs revelaron información valiosa sobre el renderizado de líneas y el estado del framebuffer.
Comando Ejecutado
timeout 150 python3 main.py roms/pkmn.gb 2>&1 | tee logs/test_pkmn_step0339.log
Resultado
Los logs muestran que:
- Todas las líneas se renderizan: 144/144 líneas tienen datos en cada frame
- El framebuffer tiene datos consistentes: 11520 píxeles no-cero de 23040 totales (50%), lo cual es correcto para el checkerboard
- La distribución es correcta: Solo índices 0 y 3, que es el patrón del checkerboard
- La secuencia de líneas es correcta: LY incrementa correctamente (0, 10, 20, 30, ..., 143, 144)
- El framebuffer solo se limpia al inicio del frame: No se detectaron limpiezas durante el renderizado
Ejemplo de Logs
[PPU-FRAMEBUFFER-COMPLETE] Frame 1 | LY: 144 (VBLANK_START) | Lines with data: 144/144 | Total non-zero pixels: 11520/23040 | Distribution: 0=11520 1=0 2=0 3=11520
[PPU-FRAMEBUFFER-LINE] Frame 1 | LY: 0 | Non-zero pixels: 80/160 | Distribution: 0=80 1=0 2=0 3=80
[PPU-LY-SEQUENCE] Frame 1 | LY: 0
[PPU-LY-SEQUENCE] Frame 1 | LY: 10
[PPU-LY-SEQUENCE] Frame 1 | LY: 20
...
[PPU-LY-SEQUENCE] Frame 1 | LY: 143
[PPU-LY-SEQUENCE] Frame 1 | LY: 144
Análisis de Logs
# Verificar si todas las líneas se renderizan
grep "\[PPU-LINES-RENDER\]" logs/test_*_step0339.log | head -n 30
# Verificar estado del framebuffer línea por línea
grep "\[PPU-FRAMEBUFFER-LINE\]" logs/test_*_step0339.log | head -n 50
# Verificar limpieza del framebuffer
grep "\[PPU-CLEAR-FRAMEBUFFER\]" logs/test_*_step0339.log | head -n 30
# Verificar secuencia de líneas
grep "\[PPU-LY-SEQUENCE\]" logs/test_*_step0339.log | head -n 50
# Verificar estado completo del framebuffer
grep "\[PPU-FRAMEBUFFER-COMPLETE\]" logs/test_*_step0339.log | head -n 30
Validación Nativa: Validación de módulo compilado C++
Hallazgos y Conclusiones
Hallazgos Principales
- ✅ Todas las líneas se renderizan correctamente: Los logs muestran que 144/144 líneas tienen datos en cada frame. No se detectaron líneas que no se rendericen.
- ✅ El framebuffer tiene datos consistentes: Cada línea tiene 80 píxeles no-cero de 160 totales (50%), lo cual es correcto para el checkerboard. La distribución de índices (solo 0 y 3) es correcta.
- ✅ La secuencia de líneas es correcta: LY incrementa correctamente de 0 a 144, sin saltos ni líneas duplicadas.
- ✅ El framebuffer solo se limpia al inicio del frame: No se detectaron limpiezas durante el renderizado (LY 1-143). El framebuffer se limpia correctamente solo cuando LY se resetea a 0.
- ✅ El estado completo del framebuffer es correcto: Al final de cada frame (LY=144), todas las 144 líneas tienen datos, con 11520 píxeles no-cero de 23040 totales.
Conclusiones
Los logs revelan que el renderizado de líneas funciona correctamente. Todas las líneas se renderizan, el framebuffer tiene datos consistentes en todas las líneas, y no hay problemas con la limpieza del framebuffer o la secuencia de líneas. El problema de "pantalla blanca con solo línea superior" mencionado en el Step 0338 probablemente no está relacionado con el renderizado de líneas, sino con otro aspecto (posiblemente la visualización en Python o el timing de lectura del framebuffer).
Fuentes Consultadas
Integridad Educativa
Lo que Entiendo Ahora
- Scanlines: La Game Boy tiene 144 líneas visibles (LY 0-143). Cada línea se renderiza durante H-Blank y el registro LY se incrementa automáticamente.
- Framebuffer: Contiene índices de color (0-3) para cada píxel. Se genera línea por línea durante el renderizado y se limpia al inicio del siguiente frame.
- Renderizado de líneas:
render_scanline()se llama para cada línea visible. El framebuffer se limpia al inicio del siguiente frame, asegurando que Python siempre lee el framebuffer ANTES de que se limpie.
Lo que Confirmé
- Todas las líneas se renderizan: Los logs confirman que 144/144 líneas tienen datos en cada frame.
- El framebuffer tiene datos consistentes: Cada línea tiene datos correctos (80 píxeles no-cero de 160 para el checkerboard).
- La secuencia de líneas es correcta: LY incrementa correctamente sin saltos ni duplicados.
- El framebuffer solo se limpia al inicio del frame: No se detectaron limpiezas durante el renderizado.
Próximos Pasos
Dado que el renderizado de líneas funciona correctamente, el problema de "pantalla blanca con solo línea superior" probablemente está en otro lugar. Los próximos pasos deberían investigar:
- El timing de lectura del framebuffer en Python
- La visualización del framebuffer en el renderizador Python
- La sincronización entre el renderizado C++ y la lectura Python
Próximos Pasos
- [ ] Investigar el timing de lectura del framebuffer en Python
- [ ] Verificar la visualización del framebuffer en el renderizador Python
- [ ] Investigar la sincronización entre el renderizado C++ y la lectura Python
- [ ] Si se identifica la causa: Implementar corrección en Step 0340