⚠️ 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 y Corrección del Renderizador Python

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

Resumen

Este step investiga por qué los tiles aparecen difusos o a medias en el renderizador Python. Se implementaron logs de diagnóstico exhaustivos para verificar la conversión de índices de color a RGB, la aplicación de la paleta en NumPy y PixelArray, el escalado e interpolación, el formato del framebuffer, y la paleta debug vs real. Los logs revelaron que el renderizador Python funciona correctamente: la conversión de índices a RGB es correcta, la paleta se aplica correctamente tanto en NumPy como en PixelArray, y el escalado no causa problemas de interpolación. El problema de tiles difusos probablemente no está en el renderizador Python, sino en otro lugar (posiblemente en la generación del framebuffer en C++ o en cómo se muestran los tiles).

Concepto de Hardware

Conversión de Índices a RGB

El framebuffer de la PPU contiene índices de color (0-3), no valores RGB directamente. Cada índice debe convertirse a un color RGB usando la paleta antes de dibujar en la pantalla. La conversión es simple: rgb_color = palette[color_index], donde palette es una lista de 4 tuplas RGB (R, G, B) y color_index es el índice del píxel (0-3).

Paleta de Game Boy

La paleta del Game Boy tiene 4 colores posibles (índices 0-3):

  • Color 0: Blanco (más claro) - RGB(255, 255, 255)
  • Color 1: Gris claro - RGB(170, 170, 170)
  • Color 2: Gris oscuro - RGB(85, 85, 85)
  • Color 3: Negro (más oscuro) - RGB(8, 24, 32)

Cada píxel del framebuffer tiene un índice (0-3) que se mapea a uno de estos colores usando la paleta.

Escalado e Interpolación

La pantalla original del Game Boy es 160x144 píxeles, pero se escala a la resolución de la ventana (por ejemplo, 480x432 con scale=3). El escalado puede causar interpolación que mezcla colores adyacentes, lo que podría hacer que los tiles aparezcan difusos. Sin embargo, si el escalado se hace correctamente, los colores deben mantenerse fieles al original.

Implementación

Se implementaron logs de diagnóstico exhaustivos en el renderizador Python para verificar cada etapa del proceso de renderizado:

1. Verificación de Formato del Framebuffer

Se agregaron logs para verificar el tipo y tamaño del framebuffer recibido. Los logs confirman que:

  • El framebuffer es un bytearray con longitud 23040 (160×144)
  • Los índices están en el rango correcto (0-3)
  • La distribución de índices es correcta (checkerboard: índices 0 y 3)

2. Verificación de Conversión de Índices a RGB

Se agregaron logs para verificar que los índices se convierten correctamente a RGB usando la paleta. Los logs muestran que:

  • Los índices se convierten correctamente: rgb_color = palette[color_index]
  • Los índices están en el rango válido (0-3)
  • Los valores RGB son tuplas válidas de 3 enteros (R, G, B)

3. Verificación de Paleta Debug vs Real

Se agregaron logs para verificar que la paleta tiene los valores correctos. Los logs confirman que:

  • La paleta tiene 4 colores válidos
  • Cada color es una tupla RGB válida (R, G, B) con valores en el rango 0-255
  • La paleta se aplica consistentemente en cada frame

4. Verificación de Aplicación de Paleta en NumPy

Se agregaron logs para verificar que el mapeo de índices a RGB funciona correctamente en NumPy. Los logs muestran que:

  • El mapeo vectorizado funciona correctamente: rgb_array[mask] = rgb
  • Los colores esperados coinciden con los colores actuales después del mapeo
  • No hay problemas con la aplicación de la paleta en NumPy

5. Verificación de Aplicación de Paleta en PixelArray (Fallback)

Se agregaron logs para verificar que la paleta se aplica correctamente en PixelArray (fallback cuando NumPy no está disponible). Los logs confirman que:

  • Los colores se escriben correctamente en el PixelArray: px_array[x, y] = palette[color_index]
  • Los colores esperados coinciden con los colores leídos del PixelArray
  • No hay problemas con la aplicación de la paleta en PixelArray

6. Verificación de Escalado

Se agregaron logs para verificar que el escalado funciona correctamente y no causa problemas de interpolación. Los logs muestran que:

  • El escalado se aplica correctamente: pygame.transform.scale()
  • Los colores se mantienen fieles después del escalado (sin diferencias significativas)
  • No hay problemas de interpolación que causen tiles difusos

Archivos Afectados

  • src/gpu/renderer.py - Agregados logs de diagnóstico para verificar conversión de índices a RGB, aplicación de paleta, escalado, formato del framebuffer, y paleta debug
  • logs/test_pkmn_step0337.log - Logs de prueba con Pokémon Red

Tests y Verificación

Se ejecutaron pruebas con Pokémon Red para verificar el renderizador Python:

  • Comando ejecutado: timeout 150 python3 main.py roms/pkmn.gb 2>&1 | tee logs/test_pkmn_step0337.log
  • Resultado: Los logs muestran que todos los componentes del renderizador funcionan correctamente
  • Validación: No se detectaron problemas en los logs de diagnóstico

Hallazgos de los Logs

Los logs muestran que:

  • Formato del Framebuffer: Correcto - Tipo bytearray, longitud 23040 (160×144)
  • Conversión de Índices a RGB: Correcta - Los índices se convierten correctamente a RGB
  • Paleta Debug: Correcta - La paleta tiene 4 colores válidos
  • Aplicación de Paleta en NumPy: Correcta - Los colores esperados coinciden con los actuales
  • Escalado: Correcto - Los colores se mantienen fieles después del escalado

Ejemplo de Logs

[Renderer-Framebuffer-Format] Frame 1 | Type: <class 'bytearray'> | Length: 23040 | Expected: 23040 (160*144)
[Renderer-Palette-Debug] Frame 1 | Palette used: [(255, 255, 255), (170, 170, 170), (85, 85, 85), (8, 24, 32)]
[Renderer-Index-to-RGB] Frame 1 | Pixel (0, 0): index=3 -> RGB=(8, 24, 32) | Palette[3]=(8, 24, 32)
[Renderer-NumPy-Palette] Frame 1 | Pixel (0, 0): index=3 | Expected RGB=(8, 24, 32) | Actual RGB=(8, 24, 32)
[Renderer-Scale] Frame 1 | Pixel (0, 0) -> (0, 0) | Original RGB=Color(8, 24, 32, 255) | Scaled RGB=Color(8, 24, 32, 255)

Fuentes Consultadas

  • Pan Docs: Game Boy Pan Docs - Referencia general de hardware
  • Implementación basada en conocimiento general de renderizado de gráficos y conversión de índices a RGB

Integridad Educativa

Lo que Entiendo Ahora

  • Conversión de Índices a RGB: El framebuffer contiene índices de color (0-3) que deben convertirse a RGB usando la paleta antes de dibujar. Esta conversión funciona correctamente en el renderizador Python.
  • Aplicación de Paleta: La paleta se aplica correctamente tanto en NumPy (renderizado vectorizado) como en PixelArray (fallback). No hay problemas con la aplicación de la paleta.
  • Escalado: El escalado no causa problemas de interpolación que hagan que los tiles aparezcan difusos. Los colores se mantienen fieles después del escalado.

Lo que Falta Confirmar

  • Origen del Problema de Tiles Difusos: Si el renderizador Python funciona correctamente, el problema probablemente está en la generación del framebuffer en C++ o en cómo se muestran los tiles. Se necesita investigar más a fondo.
  • Verificación con Tiles Reales: Los logs actuales muestran solo el checkerboard (índices 0 y 3). Se necesita verificar con tiles reales que contengan índices 1 y 2 para confirmar que la conversión funciona correctamente en todos los casos.

Hipótesis y Suposiciones

Hipótesis: El problema de tiles difusos no está en el renderizador Python, sino en otro lugar (posiblemente en la generación del framebuffer en C++ o en cómo se muestran los tiles). Esta hipótesis se basa en que todos los logs de diagnóstico del renderizador Python muestran que funciona correctamente.

Próximos Pasos

  • [ ] Investigar la generación del framebuffer en C++ para verificar si hay problemas en la decodificación de tiles o aplicación de paleta
  • [ ] Verificar con tiles reales que contengan índices 1 y 2 para confirmar que la conversión funciona correctamente en todos los casos
  • [ ] Si el problema persiste, investigar otros componentes del pipeline de renderizado