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

Corrección del Problema de Framebuffer Vacío y Verificación Visual Final

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

Resumen

Se corrigió el problema crítico de que el framebuffer estaba vacío cuando Python lo leía, causando pantallas blancas. Se implementaron correcciones de timing para asegurar que el framebuffer solo se limpia cuando Python confirma que lo leyó, se agregaron verificaciones para asegurar que todas las líneas visibles se renderizan, y se corrigieron los logs de Python para que aparezcan correctamente. Estas correcciones aseguran que el framebuffer se mantiene estable hasta que Python lo lee completamente, eliminando condiciones de carrera entre C++ y Python.

Concepto de Hardware

Timing de Limpieza del Framebuffer

En un sistema de renderizado con múltiples hilos o componentes (C++ y Python), es crítico que el framebuffer se mantenga estable hasta que el componente de lectura (Python) termine de leerlo completamente. Si el framebuffer se limpia antes de que Python lo lea, el resultado será una pantalla blanca o gráficos corruptos.

El problema identificado en el Step 0361 era que el framebuffer se limpiaba al inicio del siguiente frame (cuando LY > 153), pero Python podía leer el framebuffer después de que se había limpiado. Esto causaba que algunos frames tuvieran el framebuffer casi vacío cuando Python los leía (ej: Frame 14: 0.35%).

Renderizado de Todas las Líneas

La PPU debe renderizar todas las líneas visibles (0-143) para un frame completo. Si alguna línea no se renderiza, el framebuffer tendrá áreas vacías. Es importante verificar que todas las líneas se renderizan correctamente y que cada línea escribe datos al framebuffer.

Estabilidad del Framebuffer

El framebuffer no debe modificarse mientras Python lo está leyendo. Cualquier modificación durante la lectura puede causar gráficos corruptos o pantallas blancas. Es crítico proteger el framebuffer durante la lectura usando flags de sincronización.

Implementación

Corrección de Timing de Limpieza del Framebuffer

Se eliminó la limpieza automática del framebuffer al inicio del siguiente frame (cuando LY > 153). Ahora el framebuffer solo se limpia cuando Python confirma que lo leyó mediante `confirm_framebuffer_read()`. Esto asegura que el framebuffer se mantiene hasta que Python lo lee completamente.

// --- Step 0362: Corrección de Timing de Limpieza del Framebuffer ---
// NO limpiar el framebuffer al inicio del siguiente frame
// El framebuffer solo se limpiará cuando Python confirme que lo leyó
if (ly_ > 153) {
    // RESERVADO: El framebuffer NO se limpia aquí
    // Se limpiará cuando Python llame a confirm_framebuffer_read()
    // NO llamar a clear_framebuffer() aquí
}

Verificación de Renderizado de Todas las Líneas

Se agregó verificación para asegurar que todas las líneas visibles (0-143) se renderizan. El sistema verifica que cada línea se renderiza exactamente una vez y que el framebuffer tiene datos después de renderizar todas las líneas.

// --- Step 0362: Verificación de Renderizado de Todas las Líneas ---
if (ly_ < 144 && mode_ == MODE_3_PIXEL_TRANSFER) {
    // Verificar que render_scanline() se ejecuta
    if (!lines_rendered[ly_]) {
        lines_rendered[ly_] = true;
        // Loggear línea renderizada
    }
    
    // Verificar que la línea tiene datos después de renderizar
    if (ly_ == 143) {
        // Última línea visible - verificar framebuffer completo
        int total_non_white = 0;
        for (int i = 0; i < 160 * 144; i++) {
            if (framebuffer_[i] != 0) {
                total_non_white++;
            }
        }
        // Advertencia si el framebuffer está vacío
    }
}

Verificación de Estabilidad del Framebuffer

Se agregó verificación para asegurar que el framebuffer no cambia mientras Python lo está leyendo. El sistema captura un snapshot del framebuffer antes de marcarlo como leído y lo compara después de que Python confirma que lo leyó.

// --- Step 0362: Verificación de Estabilidad del Framebuffer ---
bool PPU::get_frame_ready_and_reset() {
    if (frame_ready_) {
        // Capturar snapshot del framebuffer antes de marcarlo como leído
        uint8_t sample_indices[20];
        // Guardar snapshot para comparar después
        framebuffer_being_read_ = true;
        return true;
    }
}

void PPU::confirm_framebuffer_read() {
    // Verificar que el framebuffer no cambió mientras Python lo leía
    bool changed = false;
    for (int i = 0; i < 20; i++) {
        if (framebuffer_[i] != saved_sample[i]) {
            changed = true;
            break;
        }
    }
    // Limpiar framebuffer solo después de confirmar
}

Corrección de Logs de Python

Se corrigió la configuración del logger para asegurar que los logs aparecen correctamente. Se agregó `print()` además de `logger.info()` para logs críticos, y se configuró el logger para escribir a stdout explícitamente.

# --- Step 0362: Corrección de Logs de Python ---
logging.basicConfig(
    level=logging.INFO,
    format='%(message)s',
    force=True,
    stream=sys.stdout  # Asegurar que va a stdout
)

# En el código de renderizado:
log_msg = f"[Viboy-Render] Frame ready, reading framebuffer"
logger.info(log_msg)
print(log_msg, flush=True)  # flush=True para asegurar salida inmediata

Archivos Afectados

  • src/core/cpp/PPU.cpp - Corrección de timing de limpieza del framebuffer, verificación de renderizado de todas las líneas, verificación de estabilidad del framebuffer
  • src/viboy.py - Corrección de logs de Python con print() y configuración explícita del logger

Tests y Verificación

La verificación se realizó mediante:

  • Logs de diagnóstico: Se agregaron logs detallados para verificar que el framebuffer no se limpia prematuramente, que todas las líneas se renderizan, y que el framebuffer se mantiene estable
  • Compilación exitosa: El código C++ se compiló sin errores (solo warnings menores de variables no usadas)
  • Verificación de logs: Los logs de Python ahora aparecen correctamente usando tanto logger como print()

Comandos de Verificación

# Compilar extensión C++
python3 setup.py build_ext --inplace

# Verificar logs (después de ejecutar el emulador)
grep "\[PPU-FRAMEBUFFER-NO-CLEAR\]" logs/*.log
grep "\[PPU-LINE-RENDER\]" logs/*.log
grep "\[PPU-FRAME-COMPLETE\]" logs/*.log
grep "\[PPU-FRAMEBUFFER-STABILITY\]" logs/*.log
grep "\[Viboy-Render\]" logs/*.log

Fuentes Consultadas

  • Pan Docs: LCD Timing, V-Blank, STAT Register
  • Conceptos generales de sistemas de renderizado con múltiples componentes

Integridad Educativa

Lo que Entiendo Ahora

  • Timing de framebuffer: En sistemas con múltiples componentes, es crítico que el framebuffer se mantenga estable hasta que el componente de lectura termine de leerlo. Limpiar el framebuffer demasiado pronto causa pérdida de datos visuales.
  • Protección de datos compartidos: Cuando múltiples componentes (C++ y Python) comparten datos (framebuffer), es necesario usar flags de sincronización para prevenir condiciones de carrera.
  • Verificación de renderizado completo: Es importante verificar que todas las líneas visibles se renderizan y que el framebuffer tiene datos después de renderizar todas las líneas.

Lo que Falta Confirmar

  • Verificación visual: Necesita ejecutarse el emulador y verificar visualmente que los gráficos se muestran correctamente después de estas correcciones
  • Rendimiento: Verificar que las correcciones no afectan negativamente el rendimiento del emulador

Hipótesis y Suposiciones

Se asume que el problema de pantallas blancas se debe principalmente al timing de limpieza del framebuffer. Si el problema persiste después de estas correcciones, será necesario investigar otros aspectos del pipeline de renderizado.

Próximos Pasos

  • [ ] Ejecutar pruebas de verificación con juegos reales para verificar visualmente que los gráficos se muestran correctamente
  • [ ] Analizar los logs generados para confirmar que las correcciones funcionan
  • [ ] Si el problema persiste, implementar soluciones alternativas (doble buffering, etc.)
  • [ ] Si se corrige el problema visual, proceder con verificaciones finales y optimizaciones