⚠️ 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 Rayas Verdes Recurrentes

Fecha: 2025-12-25 Step ID: 0301 Estado: VERIFIED

Resumen

Investigación de por qué las rayas verdes vuelven a aparecer después de unos minutos de ejecución, a pesar de la corrección implementada en el Step 0300. Se implementaron 3 monitores de diagnóstico para rastrear el uso de paletas y cambios en el modo de renderizado, y se corrigió self.COLORS que aún tenía valores verdes para el índice 0.

Objetivo: Identificar la causa raíz de las rayas verdes recurrentes y corregir cualquier código que use paletas con valores verdes en lugar de la paleta debug corregida.

Concepto de Hardware

Múltiples Fuentes de Paleta en el Renderer

El renderer puede usar múltiples fuentes de paleta durante la ejecución:

  • Paleta debug (local): Variable local palette definida en render_frame() con valores corregidos (blanco para índice 0).
  • Paleta de clase (self.palette): Atributo de instancia inicializado desde self.COLORS que puede ser modificado durante la ejecución.
  • Paleta del hardware (BGP): Registro 0xFF47 que mapea índices a colores según la configuración del juego.

Si el renderer cambia entre diferentes fuentes de paleta, los colores pueden cambiar dinámicamente. El problema identificado es que self.COLORS y self.palette aún tenían valores verdes para el índice 0, lo que podría causar que algún código los use en lugar de la paleta debug local.

Fuente: Pan Docs - "Background Palette (BGP)", "Palette"

Modo C++ vs Modo Python

El renderer puede operar en dos modos:

  • use_cpp_ppu=True: Usa el framebuffer de C++ directamente (más rápido). La paleta debug se aplica al mapear índices del framebuffer a colores RGB.
  • use_cpp_ppu=False: Calcula tiles desde VRAM en Python (más lento pero más flexible). Usa la paleta debug local para decodificar tiles.

Si el renderer cambia de modo durante la ejecución, puede usar diferentes rutas de código que tienen diferentes paletas. Los monitores implementados detectan estos cambios para identificar si hay una relación con la aparición de rayas verdes.

Fuente: Pan Docs - "LCD Control Register", "PPU Rendering"

Implementación

Se implementaron correcciones preventivas y 3 monitores de diagnóstico para rastrear el uso de paletas y cambios en el modo de renderizado.

Corrección de self.COLORS

Se corrigió self.COLORS en __init__() para que el índice 0 sea blanco en lugar de verde:

# Antes (Step 0300):
self.COLORS = [
    (224, 248, 208),  # 0: Blanco/Verde claro (VERDE)
    ...
]

# Después (Step 0301):
self.COLORS = [
    (255, 255, 255),  # 0: White (Color 0) - Corregido Step 0301
    ...
]

Esta corrección asegura que si algún código usa self.COLORS o self.palette (que se inicializa desde self.COLORS), los valores sean correctos.

Monitor [PALETTE-USE-TRACE]

Monitor que rastrea qué paleta se usa en cada frame. Se implementa en ambas rutas de renderizado (C++ y Python) y registra:

  • Número de frame
  • Estado de use_cpp_ppu
  • Color del índice 0 en la paleta debug local
  • Color del índice 0 en self.palette
# Monitor implementado en render_frame()
if self._palette_trace_count < 100 or (self._palette_trace_count % 1000 == 0):
    use_cpp = self.use_cpp_ppu
    palette_0_debug = palette[0]  # Paleta debug local
    palette_0_self = self._palette[0]  # self.palette
    print(f"[PALETTE-USE-TRACE] Frame {self._palette_trace_count} | use_cpp_ppu={use_cpp} | debug_palette[0]={palette_0_debug} | self.palette[0]={palette_0_self}")
self._palette_trace_count += 1

El monitor registra los primeros 100 frames y luego cada 1000 frames para no saturar los logs.

Monitor [PALETTE-SELF-CHANGE]

Monitor que detecta cambios en self.palette usando una propiedad con setter. Si algún código modifica self.palette, el monitor registra el cambio y un stack trace:

@property
def palette(self):
    """Getter para self.palette (usado por monitores Step 0301)."""
    return self._palette

@palette.setter
def palette(self, value):
    """Setter para self.palette con monitor de cambios (Step 0301)."""
    old_0 = self._palette[0] if self._palette else None
    self._palette = value
    new_0 = value[0] if value else None
    if old_0 != new_0:
        import traceback
        print(f"[PALETTE-SELF-CHANGE] self.palette[0] cambió: {old_0} -> {new_0}")
        print(f"[PALETTE-SELF-CHANGE] Stack trace:")
        traceback.print_stack(limit=5)

Este monitor requiere cambiar self.palette a self._palette en __init__() para usar la propiedad.

Monitor [CPP-PPU-TOGGLE]

Monitor que detecta cambios en use_cpp_ppu durante la ejecución:

# Monitor implementado en render_frame()
if self._last_use_cpp_ppu is None:
    self._last_use_cpp_ppu = self.use_cpp_ppu
elif self._last_use_cpp_ppu != self.use_cpp_ppu:
    print(f"[CPP-PPU-TOGGLE] use_cpp_ppu cambió: {self._last_use_cpp_ppu} -> {self.use_cpp_ppu}")
    self._last_use_cpp_ppu = self.use_cpp_ppu

Este monitor ayuda a identificar si el renderer cambia entre modo C++ y modo Python, lo que podría causar que se use una paleta diferente.

Búsqueda de Código que Use self.palette

Se realizó una búsqueda exhaustiva en src/gpu/renderer.py para identificar todos los lugares donde se usa self.palette o self.COLORS:

  • Línea 198: self._palette = list(self.COLORS) - Inicialización (corregida)
  • Líneas 349-387: update_tile_cache(palette) - Usa paleta como parámetro (variable local)
  • Líneas 701, 705, 791, 867: Uso de palette como variable local en render_frame()

Conclusión: No se encontró ningún código que use self.palette directamente durante el renderizado. Todas las referencias a palette son variables locales definidas en render_frame() con la paleta debug corregida. La corrección de self.COLORS es preventiva para asegurar que si algún código futuro usa self.palette, los valores sean correctos.

Archivos Afectados

  • src/gpu/renderer.py - Corrección de self.COLORS e implementación de 3 monitores de diagnóstico

Tests y Verificación

La implementación se verificó mediante:

  • Análisis estático de código: Búsqueda exhaustiva de usos de self.palette y self.COLORS
  • Linter: Sin errores de sintaxis o tipo
  • Validación de monitores: Los 3 monitores están implementados y listos para capturar logs durante ejecución extendida

Próximo paso: Ejecutar el emulador durante varios minutos y analizar los logs generados por los monitores para identificar cuándo y por qué vuelven las rayas verdes.

Fuentes Consultadas

  • Pan Docs: "Background Palette (BGP)" - Gestión de paletas en Game Boy
  • Pan Docs: "LCD Control Register" - Modos de renderizado
  • Pan Docs: "PPU Rendering" - Renderizado de gráficos

Integridad Educativa

Lo que Entiendo Ahora

  • Múltiples fuentes de paleta: El renderer puede usar diferentes paletas según el contexto. La paleta debug local es la que se usa durante el renderizado, pero self.palette podría ser usada en otros lugares.
  • Monitores de diagnóstico: Los monitores permiten rastrear cambios dinámicos en el estado del renderer que podrían causar problemas visuales. Son herramientas esenciales para debugging.
  • Corrección preventiva: Aunque no se encontró código que use self.palette durante el renderizado, corregir self.COLORS asegura que cualquier código futuro use valores correctos.

Lo que Falta Confirmar

  • Causa raíz de rayas recurrentes: Los monitores deben ejecutarse durante varios minutos para identificar cuándo y por qué vuelven las rayas verdes. Posibles causas:
    • Cambio en use_cpp_ppu durante la ejecución
    • Modificación de self.palette por código externo
    • Uso de una paleta diferente en algún código no identificado
  • Análisis de logs: Los logs generados por los monitores deben analizarse para identificar patrones que coincidan con la aparición de rayas verdes.

Hipótesis y Suposiciones

Hipótesis principal: Las rayas verdes recurrentes podrían deberse a que algún código modifica self.palette o cambia el modo de renderizado después de unos minutos. Los monitores implementados deberían capturar estos cambios.

Suposición: Si la corrección de self.COLORS no resuelve el problema, el análisis de logs de los monitores debería revelar la causa raíz.

Próximos Pasos

  • [ ] Ejecutar el emulador durante varios minutos con los monitores activos
  • [ ] Analizar logs generados por los monitores para identificar patrones
  • [ ] Si se identifica la causa raíz, implementar corrección específica
  • [ ] Si las rayas desaparecen, verificar que no vuelvan con pruebas extendidas
  • [ ] Si persisten, investigar más a fondo otros posibles lugares donde se use paleta verde