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 0457: Aislar si el Bug es "Índices Mal" vs "Paleta/Conversión RGB Mal" con Evidencia
Resumen
Objetivo: Aislar si el bug del framebuffer RGB plano es causado por índices mal decodificados/renderizados (H1) o por conversión de paleta/RGB incorrecta (H2). Los tests ya están bien (0456), así que si siguen fallando, el bug es real en el core.
Hallazgo crítico: La instrumentación revela que el framebuffer de índices está completamente plano (todos los valores son 0), lo que confirma que el bug NO está en la conversión de paleta, sino en el decode/render de tiles. El problema está en cómo se decodifican los tiles desde VRAM o cómo se escriben los índices al framebuffer.
Evidencia numérica: Los tests muestran que los índices sampleados (8 píxeles) son todos 0: [0, 0, 0, 0, 0, 0, 0, 0], con un set único de {0} en lugar del esperado {0, 1, 2, 3}. Esto descarta H2 (conversión paleta/RGB) y confirma H1 (decode/render).
Concepto de Hardware
El proceso de renderizado en la Game Boy tiene dos fases principales:
- Decodificación y Renderizado: Los tiles se decodifican desde VRAM (formato 2bpp) y se escriben al framebuffer como índices de color (0-3). Esta fase incluye la lectura de tiles desde VRAM, la decodificación 2bpp, y la escritura de índices al framebuffer.
- Conversión de Paleta: Los índices se convierten a RGB usando las paletas BGP/OBP0/OBP1. Esta fase lee el registro de paleta, mapea el índice a un shade (0-3), y convierte el shade a RGB888.
Si el framebuffer RGB está plano (todo el mismo color), puede ser porque:
- H1 - Índices mal: El framebuffer de índices está plano (todos 0, todos 2, etc.) → el bug está en decode/render.
- H2 - Conversión mal: El framebuffer de índices tiene variedad (0, 1, 2, 3), pero la conversión paleta→RGB está rota y produce RGB plano → el bug está en conversión/escritura RGB.
Fuente: Pan Docs - Background, Window, Sprites, Color Palettes
Implementación
Se implementó instrumentación mínima para aislar el bug sin tocar los tests (que ya están bien según 0456).
Fase A: Exponer Framebuffer de Índices
Se añadió una API de debug para exponer el framebuffer de índices desde C++ a Python:
PPU::get_framebuffer_indices_ptr()enPPU.hpp- Devuelve puntero const al framebuffer_front_PyPPU::get_framebuffer_indices()enppu.pyx- Wrapper Cython que devuelve bytes de 23040 bytes- Declaración en
ppu.pxdpara que Cython pueda acceder al método
Esta API permite a los tests inspeccionar el framebuffer de índices antes de la conversión a RGB, permitiendo distinguir entre H1 y H2.
Fase B: Capturar Paleta Regs Usados
Se añadió captura de los registros de paleta usados en la conversión:
- Miembros
last_bgp_used_,last_obp0_used_,last_obp1_used_enPPU.hpp - Actualización en
convert_framebuffer_to_rgb()para capturar los valores leídos - Getters
get_last_bgp_used(),get_last_obp0_used(),get_last_obp1_used() - Wrapper
PyPPU::get_last_palette_regs_used()que devuelve un dict con los valores
Esto permite verificar que el registro de paleta usado en la conversión coincide con el escrito por el test, descartando bugs de lectura/reg caching.
Fase C: Revisión de Conversión
Se revisó el código de conversión y se confirmó que está correcto:
dmg_shade_to_rgb()- Tabla de conversión correcta: {255, 170, 85, 0}- Escritura al buffer RGB - Stride correcto:
rgb_idx = fb_index * 3 - Orden de conversión - Se llama después del swap, sobre el framebuffer_front_ correcto
La conversión no necesita corrección; el bug está en la fase anterior (decode/render).
Modificación de Tests
Se añadieron "sanity asserts" en los tests para verificar índices y paleta regs antes de mirar RGB:
test_palette_dmg_bgp_0454.py- Añadido sanity assert que verifica índices sample (8 píxeles) y paleta reg usadotest_palette_dmg_obj_0454.py- Añadido sanity assert similar para sprites
Estos asserts fallan inmediatamente si los índices están mal, permitiendo identificar el bug sin necesidad de mirar RGB.
Archivos Afectados
src/core/cpp/PPU.hpp- Añadido métodoget_framebuffer_indices_ptr()y miembros para paleta regs usadossrc/core/cpp/PPU.cpp- Implementación deget_framebuffer_indices_ptr()y captura de paleta regs enconvert_framebuffer_to_rgb()src/core/cython/ppu.pxd- Declaraciones de nuevos métodos para Cythonsrc/core/cython/ppu.pyx- Wrappers Cython:get_framebuffer_indices()yget_last_palette_regs_used()tests/test_palette_dmg_bgp_0454.py- Añadidos sanity asserts con índices y paleta regstests/test_palette_dmg_obj_0454.py- Añadidos sanity asserts con índices y paleta regs
Tests y Verificación
Comando ejecutado: pytest -v tests/test_palette_dmg_bgp_0454.py tests/test_palette_dmg_obj_0454.py tests/test_framebuffer_not_flat_0456.py
Resultado: ❌ 0/3 tests pasan (esperado: el bug es real en el core)
Evidencia numérica:
[TEST-BGP-SANITY] Índices sample (8 píxeles): [0, 0, 0, 0, 0, 0, 0, 0]
[TEST-BGP-SANITY] Índices únicos: {0}
AssertionError: Índices plano: solo {0} (esperado {0,1,2,3}).
Si esto falla → bug NO es paleta; es decode/render.
Interpretación: El framebuffer de índices está completamente plano (todos 0), lo que confirma que el bug está en decode/render, no en la conversión de paleta. La conversión paleta→RGB está correcta, pero no hay variedad de índices para convertir.
Validación de módulo compilado C++: ✅ Compilación exitosa, sin errores. Los métodos están disponibles en Python.
Fuentes Consultadas
- Pan Docs: Background, Window, Sprites, Color Palettes
- Plan Step 0457:
.cursor/plans/step_0457_-_aislar_si_bug_es_indices_mal_vs_paleta_conversion_rgb_mal_con_evidencia_y_aplicar_fix_mi_ce55b6c6.plan.md
Integridad Educativa
Lo que Entiendo Ahora
- Aislamiento de bugs: Cuando un síntoma puede tener múltiples causas, la instrumentación es esencial para aislar la causa raíz. En este caso, exponer el framebuffer de índices permitió distinguir entre "índices mal" y "conversión mal".
- Debug API: Añadir APIs de debug que exponen estado interno es una técnica válida para tests, siempre que no afecte el hot path del código de producción.
- Evidencia numérica: Los tests deben proporcionar evidencia numérica clara (índices sample, paleta regs usados) en lugar de solo asumir qué está mal.
Lo que Falta Confirmar
- Decode de tiles: Necesito investigar por qué el decode de tiles produce índices todos 0. ¿El problema está en
decode_tile_line(), en la lectura de VRAM, o en la escritura al framebuffer? - Renderizado de Background: ¿El problema está en
render_bg()orender_scanline()? ¿Se están leyendo los tiles correctamente desde VRAM?
Hipótesis y Suposiciones
Hipótesis principal: El bug está en el decode/render de tiles. Posibles causas:
- El decode 2bpp está mal implementado y siempre produce índice 0
- La lectura de VRAM no está funcionando correctamente
- La escritura al framebuffer_back_ está mal (se escribe siempre 0)
- El swap de framebuffers está mal y siempre se lee el buffer limpio
Próximos Pasos
- [ ] Investigar
decode_tile_line()- Verificar que el decode 2bpp produce los índices correctos - [ ] Investigar
render_bg()- Verificar que se leen los tiles correctamente desde VRAM - [ ] Investigar escritura al framebuffer - Verificar que los índices se escriben correctamente al framebuffer_back_
- [ ] Investigar swap de framebuffers - Verificar que el swap funciona correctamente
- [ ] Aplicar fix mínimo una vez identificada la causa raíz
- [ ] Re-ejecutar tests de paleta para validar la corrección