⚠️ 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.

Fix PPU: framebuffer blanco con TileData alta (render_bg/render_window/swap)

📋 Resumen Ejecutivo

⚠️ INTENTO REVERTIDO

Este Step intentó corregir el criterio de gating vram_has_tiles_ que era demasiado estricto para juegos CGB con tiledata alta pero baja diversidad de tile IDs únicos. Los cambios fueron revertidos porque no resolvieron completamente el problema del framebuffer blanco.

Problema Identificado:

  • Juegos como tetris_dx.gbc y zelda-dx.gbc reportaban tiledata_effective alta (56.6% y 79.0%) pero fb_nonzero=0/23040 (framebuffer completamente blanco).
  • El criterio de vram_has_tiles_ requería diversidad >= 5 tile IDs únicos, pero zelda-dx.gbc solo tenía 1 tile ID único.

Solución Intentada:

  • Relajar el criterio para permitir render cuando tiledata_effective >= 200 aunque unique_tile_ids sea bajo (>= 1) en modo CGB.
  • Override condicional: cgb_high_tiledata_override = (tiledata_effective >= 200) && (unique_tile_ids >= 1).

Resultado:

  • tetris_dx.gbc: Mejora significativa - vram_has_tiles_ se activa correctamente (Frame 676).
  • ⚠️ zelda-dx.gbc: vram_has_tiles_ se activa (Frame 13) pero framebuffer sigue blanco.
  • ❌ Los cambios fueron revertidos porque el problema persiste (requiere investigación más profunda).

🔧 Concepto de Hardware

Criterio de Detección de Tiles en VRAM

Fuente: Pan Docs - "Video RAM", "Tile Data", "Background Tile Map"

La PPU necesita determinar cuándo hay tiles válidos en VRAM para decidir si renderizar el fondo o mostrar un patrón de prueba (checkerboard). En juegos reales, especialmente durante inicialización o transiciones de pantalla, puede haber situaciones donde hay datos de tiles cargados pero con baja diversidad de tile IDs únicos en el tilemap.

Problema Original:

El criterio anterior requería tanto datos de tiles (tiledata_nonzero >= 200 o complete_tiles >= 10) Y diversidad en el tilemap (unique_tile_ids >= 5). Esto causaba falsos negativos en juegos CGB como zelda-dx.gbc que tienen tiledata alta pero solo 1 tile ID único.

Solución Intentada (Revertida):

Relajar el criterio para modo CGB: si tiledata_effective >= 200 y unique_tile_ids >= 1, permitir render aunque la diversidad sea baja.

// Criterio original (estricto)
vram_has_tiles_ = has_tiles_data && has_tilemap_diversity;

// Criterio intentado (relajado para CGB) - REVERTIDO
bool cgb_high_tiledata_override = (tiledata_effective >= 200) && (unique_tile_ids >= 1);
vram_has_tiles_ = has_tiles_data && (has_tilemap_diversity || cgb_high_tiledata_override);

Por qué fue Revertido:

Aunque el cambio mejoró la activación de vram_has_tiles_, el framebuffer seguía quedando blanco en zelda-dx.gbc. Esto sugiere que el problema no es solo el gating sino que hay otro problema en el pipeline de renderizado (posiblemente en el direccionamiento de tiles, swap de framebuffers, o limpieza incorrecta).

💻 Implementación

Archivos Modificados (Cambios Revertidos):

  • src/core/cpp/PPU.cpp - Líneas ~1559-1578 (función render_scanline())

Cambio Intentado:

// Cálculo de tiledata_effective para CGB
int tiledata_bank1 = count_vram_nonzero_bank1_tiledata();
int tiledata_effective = (tiledata_nonzero > tiledata_bank1) ? tiledata_nonzero : tiledata_bank1;

// Override para CGB con tiledata alta
bool cgb_high_tiledata_override = (tiledata_effective >= 200) && (unique_tile_ids >= 1);

// Nuevo criterio
vram_has_tiles_ = has_tiles_data && (has_tilemap_diversity || cgb_high_tiledata_override);

🧪 Tests y Verificación

Compilación:

$ python3 setup.py build_ext --inplace
✅ Compilación exitosa (exit code: 0)

Suite Paralela (2 minutos, todas las ROMs):

$ export VBC_SUITE=1
$ # Ejecución paralela de 8 ROMs
✅ Suite completada (exit code: 0)

Evidencia de los Logs:

tetris_dx.gbc:

[VRAM-STATE-CHANGE] Frame 676 | has_tiles: 0 -> 1 | tiledata: 4032/4032 | tiles: 252/384 | unique_ids: 185
[VIDEO-SUMMARY] Frame 700 | tiledata=56.6% | tiles: 252/384 | unique_ids: 185 | fb_nonzero: 15360/23040 (66.7%)
[CGB-RGB-CHECK] Frame 700 | Non-white pixels: YES (66.7%)

Mejora significativa: vram_has_tiles_ se activa correctamente, framebuffer tiene píxeles no-blancos.

zelda-dx.gbc:

[VRAM-STATE-CHANGE] Frame 13 | has_tiles: 0 -> 1 | tiledata: 5632/5632 | tiles: 352/384 | unique_ids: 1
[VIDEO-SUMMARY] Frame 700 | tiledata=79.0% | tiles: 352/384 | unique_ids: 1 | fb_nonzero: 0/23040 (0.0%)
[CGB-RGB-CHECK] Frame 700 | Non-white pixels: NO (all white or black)

⚠️ Problema persiste: vram_has_tiles_ se activa pero framebuffer sigue completamente blanco (0%).

🔍 Análisis Post-Mortem

Hallazgos Clave:

  1. El gating no es el único problema: Aunque vram_has_tiles_ se activa correctamente en zelda-dx.gbc, el framebuffer sigue blanco.
  2. Diversidad baja puede ser válida: zelda-dx.gbc tiene 352 tiles completos pero solo 1 tile ID único (posiblemente carga el mismo tile en toda la pantalla durante inicialización).
  3. Problema más profundo: El renderizado inline no usa vram_has_tiles_ como gating (el bucle for en render_scanline() no verifica este flag), por lo que debe haber otro problema:
    • Direccionamiento de tiles incorrecto
    • Swap de framebuffers no funciona correctamente
    • Limpieza incorrecta del framebuffer
    • Problema en get_tile_color_for_bg()

Por qué se Revirtió:

  • El cambio mejora el comportamiento de tetris_dx.gbc pero no resuelve zelda-dx.gbc.
  • El criterio relajado podría causar falsos positivos en otros juegos.
  • Se necesita una investigación más profunda del pipeline de renderizado antes de modificar el gating.

Próximos Pasos Sugeridos (para Steps futuros):

  1. Instrumentar el bucle de renderizado inline en render_scanline() para verificar qué valores de color se escriben al framebuffer.
  2. Verificar que swap_framebuffers() intercambia correctamente los punteros front/back.
  3. Auditar get_tile_color_for_bg() con datos reales de zelda-dx.gbc.
  4. Considerar si el problema es específico de CGB (bancos de VRAM, paletas CGB).

🎯 Conclusión

Este Step documentó un intento de corrección del criterio de vram_has_tiles_ que fue revertido porque no resolvió completamente el problema del framebuffer blanco. Los cambios fueron beneficiosos para tetris_dx.gbc pero insuficientes para zelda-dx.gbc.

El análisis reveló que el problema del framebuffer blanco no es solo el gating de vram_has_tiles_ sino que hay un problema más profundo en el pipeline de renderizado que requiere investigación adicional.

Estado Final: DRAFT/REVERTIDO - Los cambios de código fueron revertidos pero la documentación se mantiene para trazabilidad.