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.
Step 0459: Instrumentación Pipeline Conversión Shade→RGB
Resumen
Objetivo: Instrumentar el pipeline de conversión idx→shade→rgb para identificar dónde colapsa el bug de "RGB solo 2 colores únicos en vez de ≥3". El Step 0458 confirmó que los índices se escriben correctamente (0, 1, 2, 3), por lo que el bug restante debe estar en la conversión de paleta o en la conversión shade→RGB.
Hallazgo crítico: ✅ El pipeline de conversión funciona correctamente. La instrumentación muestra que cuando hay 4 índices distintos (0, 1, 2, 3), el pipeline produce 4 colores únicos: (255,255,255), (170,170,170), (85,85,85), (0,0,0). El problema NO está en el pipeline de conversión.
Problema real identificado: El framebuffer de índices solo tiene variedad en los primeros píxeles (donde está el tile renderizado). El resto del framebuffer tiene solo índices 0 y 2, lo que produce solo 2 colores únicos. Esto es un problema de renderizado, no de conversión.
Resultado: ✅ Pipeline de conversión validado y funcionando correctamente. No se requiere fix en el pipeline. El problema está en el renderizado (fuera del alcance de este Step).
Concepto de Hardware
Pipeline de Conversión DMG: Índice → Shade → RGB
En modo DMG (Game Boy clásico), el pipeline de conversión de color funciona en 3 etapas:
- Índice de color (0-3): El framebuffer contiene índices crudos (0, 1, 2, 3) que representan qué píxel del tile se está renderizando.
- Shade (0-3): El registro BGP (0xFF47) mapea cada índice a un shade (tono de gris). Cada índice ocupa 2 bits en BGP:
- Bits 1-0: shade para índice 0
- Bits 3-2: shade para índice 1
- Bits 5-4: shade para índice 2
- Bits 7-6: shade para índice 3
- RGB (0-255): El shade se convierte a RGB888 usando una tabla fija:
- Shade 0 = White (255, 255, 255)
- Shade 1 = Light gray (170, 170, 170)
- Shade 2 = Dark gray (85, 85, 85)
- Shade 3 = Black (0, 0, 0)
Fuente: Pan Docs - "Color Palettes" (DMG)
Errores Típicos en el Pipeline
Los errores más comunes que causan colapso a 2 colores son:
- Shift de paleta incorrecto: Usar
<<en vez de*2, o usar el shade en vez del índice para calcular el shift. - Colapso en shade_to_rgb: La tabla de conversión mapea shades diferentes al mismo valor RGB.
- Stride incorrecto: La escritura RGB pisa bytes o usa un stride incorrecto.
- Buffer incorrecto: Se lee/escribe desde el buffer incorrecto (back en vez de front).
Implementación
Fase A: Instrumentación del Pipeline
Se añadió instrumentación de debug para capturar samples del pipeline idx→shade→rgb:
- Miembros de debug en PPU.hpp:
last_idx_samples_[32]- Primeros 32 índices procesadoslast_shade_samples_[32]- Primeros 32 shades resultanteslast_rgb_samples_[32][3]- Primeros 32 valores RGB (R, G, B)last_convert_sample_count_- Número de samples capturadoslast_bgp_used_debug_- BGP usado en la conversión
- Modificación en convert_framebuffer_to_rgb(): Captura samples durante la conversión (solo primeros N píxeles para no saturar contexto).
- Getters en PPU.hpp:
get_last_idx_samples(),get_last_shade_samples(),get_last_rgb_samples(), etc. - Exposición en Cython: Método
get_last_dmg_convert_samples()que devuelve un dict con los samples. - Modificación en tests: Verificación del pipeline después de render 1 frame.
Evidencia Numérica
La instrumentación muestra que el pipeline funciona correctamente:
[TEST-PIPELINE] BGP usado: 0xE4
[TEST-PIPELINE] Primeros 8 píxeles:
idx: [0, 1, 2, 3, 0, 1, 2, 3]
shade: [0, 1, 2, 3, 0, 1, 2, 3]
rgb: [(255, 255, 255), (170, 170, 170), (85, 85, 85), (0, 0, 0),
(255, 255, 255), (170, 170, 170), (85, 85, 85), (0, 0, 0)]
✅ Pipeline OK: idx=4 únicos, shade=4 únicos, rgb=4 únicos
Conclusión: El pipeline de conversión funciona correctamente. Cuando hay 4 índices distintos, produce 4 colores únicos.
Problema Real Identificado
El análisis del framebuffer completo muestra que:
- Primeros 100 píxeles: Distribución 0=25, 1=25, 2=25, 3=25 (4 índices distintos) ✅
- Muestreo completo: Distribución 0=1152, 1=0, 2=1152, 3=0 (solo índices 0 y 2) ❌
Esto significa que el tile solo se está renderizando en los primeros píxeles, y el resto del framebuffer tiene solo índices 0 y 2. Esto es un problema de renderizado, no de conversión.
Archivos Afectados
src/core/cpp/PPU.hpp- Añadidos miembros de debug para samples del pipeline (bajo#ifdef VIBOY_DEBUG_PPU)src/core/cpp/PPU.cpp- Modificadoconvert_framebuffer_to_rgb()para capturar samples, inicialización de miembros en constructorsrc/core/cython/ppu.pxd- Añadidas declaraciones de getters para samplessrc/core/cython/ppu.pyx- Añadido métodoget_last_dmg_convert_samples()para exponer samples a Pythontests/test_palette_dmg_bgp_0454.py- Añadida verificación del pipeline después de render 1 frame
Tests y Verificación
Comando ejecutado: pytest -v tests/test_palette_dmg_bgp_0454.py
Resultado: Test falla porque el framebuffer completo solo tiene 2 colores únicos, pero la instrumentación muestra que el pipeline funciona correctamente en los primeros píxeles.
Código del Test:
# --- Step 0459: Verificar pipeline idx→shade→rgb ---
samples = ppu.get_last_dmg_convert_samples()
if samples:
idx_samples = samples['idx'][:8] # Primeros 8
shade_samples = samples['shade'][:8]
rgb_samples = samples['rgb'][:8]
bgp_used = samples['bgp_used']
# Assert: idx_samples contiene 0,1,2,3
unique_idx = set(idx_samples)
assert unique_idx == {0, 1, 2, 3}
# Assert: shade_samples tiene ≥3 valores distintos
unique_shade = set(shade_samples)
assert len(unique_shade) >= 3
# Assert: rgb produce ≥3 valores distintos
unique_rgb = set(rgb_samples)
assert len(unique_rgb) >= 3
Validación Nativa: ✅ Compilación exitosa con -DVIBOY_DEBUG_PPU, sin errores. La instrumentación captura correctamente los samples del pipeline.
Evidencia: Los primeros 8 píxeles muestran 4 colores únicos: (255,255,255), (170,170,170), (85,85,85), (0,0,0). El pipeline funciona correctamente.
Fuentes Consultadas
- Pan Docs: Power Up Sequence
- Pan Docs: Color Palettes (DMG)
Integridad Educativa
Lo que Entiendo Ahora
- Pipeline de conversión DMG: El pipeline idx→shade→rgb funciona en 3 etapas: índice crudo (0-3) → shade mediante BGP (0-3) → RGB888 mediante tabla fija. Cada etapa es independiente y puede fallar por separado.
- Instrumentación de debug: Capturar samples del pipeline permite identificar exactamente dónde colapsa la conversión. Los samples muestran que el pipeline funciona correctamente cuando hay variedad de índices.
- Separación de responsabilidades: El problema de "RGB solo 2 colores únicos" puede estar en el renderizado (índices incorrectos) o en la conversión (paleta/RGB). La instrumentación permite aislar el problema.
Lo que Falta Confirmar
- Renderizado completo: El tile solo se renderiza en los primeros píxeles. Necesito verificar por qué el resto del framebuffer tiene solo índices 0 y 2. Esto es un problema de renderizado, no de conversión.
- Distribución de índices: Los logs muestran que hay 17280 píxeles no-cero con distribución 0=5760, 1=5760, 2=5760, 3=5760, pero el muestreo completo solo encuentra índices 0 y 2. Necesito investigar esta discrepancia.
Hipótesis y Suposiciones
Hipótesis confirmada: El pipeline de conversión funciona correctamente. Cuando hay 4 índices distintos, produce 4 colores únicos. No se requiere fix en el pipeline.
Hipótesis pendiente: El problema está en el renderizado. El tile no se está repitiendo correctamente en toda la pantalla, o hay un problema con cómo se están escribiendo los índices al framebuffer.
Próximos Pasos
- [ ] Investigar por qué el framebuffer de índices solo tiene variedad en los primeros píxeles
- [ ] Verificar que el tile se está renderizando correctamente en toda la pantalla
- [ ] Si el problema es de renderizado, corregirlo en un Step futuro