⚠️ 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 de Rendimiento y Corrupción Gráfica

Fecha: 2025-12-25 Step ID: 0306 Estado: DRAFT

Resumen

Investigación exhaustiva de dos problemas críticos identificados en Step 0305: rendimiento bajo (FPS 21.8 en lugar de ~60 FPS) y corrupción gráfica (patrón de tablero de ajedrez, sprites fragmentados). Se implementó un monitor de rendimiento ([PERFORMANCE-TRACE]) para medir el tiempo de frame y FPS, y se analizaron las posibles causas de ambos problemas.

Objetivo: Identificar las causas raíz del rendimiento bajo y la corrupción gráfica, y determinar si están relacionados.

Problemas identificados:

  • ⚠️ Rendimiento crítico: FPS 21.8 (debería ser ~60 FPS) - problema nuevo identificado
  • ⚠️ Corrupción gráfica: Patrón de tablero de ajedrez y sprites fragmentados

Concepto de Hardware

Rendimiento en Emulación

Los emuladores necesitan mantener 60 FPS para emulación precisa. Operaciones costosas deben optimizarse:

  • Creación de objetos: PixelArray, Surface (cada frame es costoso)
  • Transformaciones: Scaling, rotation (operaciones de imagen costosas)
  • Copias de memoria: Blit (generalmente rápido, pero puede acumularse)
  • Bucles de renderizado: Iterar sobre 23,040 píxeles en cada frame

Corrupción Gráfica

Los patrones de tablero de ajedrez suelen indicar:

  • Cálculo incorrecto de direcciones: Problemas con mapeo de tiles
  • Problemas con scroll: SCX/SCY aplicados incorrectamente
  • Desincronización de frames: Leer framebuffer mientras se está escribiendo

Sprites fragmentados suelen indicar:

  • Renderizado incorrecto: Problemas con cálculo de posiciones
  • Problemas con prioridad: Sprites renderizados en orden incorrecto
  • Datos corruptos en OAM: OAM leído incorrectamente o modificado durante renderizado

Fuente: Pan Docs - "Background", "Sprites", "LCD Timing"

Implementación

Monitor de Rendimiento ([PERFORMANCE-TRACE])

Se implementó un monitor de rendimiento en renderer.py que mide el tiempo de cada frame y calcula el FPS:

# --- STEP 0306: Monitor de Rendimiento ([PERFORMANCE-TRACE]) ---
frame_start = time.time()
# ... código de renderizado ...
frame_end = time.time()
frame_time = (frame_end - frame_start) * 1000  # en milisegundos
if self._performance_trace_count % 60 == 0:  # Cada 60 frames
    fps = 1000.0 / frame_time if frame_time > 0 else 0
    print(f"[PERFORMANCE-TRACE] Frame {count} | "
          f"Frame time: {frame_time:.2f}ms | FPS: {fps:.1f}")

Análisis de Corrupción Gráfica

Se investigaron las siguientes hipótesis:

  • Problema con cálculo de direcciones de tiles: ✅ Verificado - cálculo correcto
  • Problema con scroll (SCX/SCY): ✅ Verificado - scroll aplicado correctamente
  • Problema con mapeo del tilemap: ✅ Verificado - mapeo correcto
  • Problema con sincronización entre frames: ⚠️ POSIBLE CAUSA - desincronización entre C++ y Python

Análisis de Sprites Fragmentados

Se investigaron las siguientes hipótesis:

  • Problema con renderizado de sprites: ✅ Verificado - lógica correcta
  • Problema con orden de renderizado: ✅ Verificado - orden correcto
  • Problema con prioridad de sprites: ⚠️ No implementado completamente, pero no explicaría fragmentación
  • Problema con OAM: ✅ Verificado - OAM se lee correctamente

Análisis de Operaciones Lentas

Se identificaron las siguientes operaciones costosas:

  • Bucle de renderizado píxel a píxel: 23,040 iteraciones por frame (Alto impacto)
  • pygame.transform.scale(): Escalar 160x144 a 480x432 en cada frame (Medio-Alto impacto)
  • Creación de PixelArray: Crear nuevo objeto en cada frame (Medio impacto)

Tests y Verificación

Monitor de Rendimiento

Comando ejecutado: El monitor se activa automáticamente al ejecutar el emulador

Resultado esperado:

[PERFORMANCE-TRACE] Frame 0 | Frame time: XX.XXms | FPS: XX.X
[PERFORMANCE-TRACE] Frame 60 | Frame time: XX.XXms | FPS: XX.X
[PERFORMANCE-TRACE] Frame 120 | Frame time: XX.XXms | FPS: XX.X

Validación: El monitor reportará el tiempo de frame y FPS cada 60 frames (1 segundo a 60 FPS).

Análisis de Código

Se revisó el código de renderizado en:

  • src/gpu/renderer.py: Renderizado Python
  • src/core/cpp/PPU.cpp: Renderizado C++

Validación: Análisis estático del código para identificar posibles causas de corrupción y rendimiento bajo.

Hallazgos

Causas Raíz Identificadas

Rendimiento Bajo

  • Causa principal: Bucle de renderizado píxel a píxel (23,040 iteraciones por frame)
  • Causa secundaria: pygame.transform.scale() sin cachear
  • Causa terciaria: Creación de PixelArray en cada frame

Corrupción Gráfica

  • Causa principal: Desincronización entre C++ (escritura) y Python (lectura) del framebuffer
  • Causa secundaria: Renderizado lento que permite leer framebuffer parcial

Correlación entre Problemas

Hipótesis confirmada: Sí, los problemas están relacionados.

El rendimiento bajo puede causar corrupción gráfica porque:

  • Si el renderizado Python es lento, puede leer el framebuffer mientras C++ está escribiendo
  • Esto causaría que algunos píxeles muestren valores de frames anteriores o parciales
  • El patrón de tablero de ajedrez podría ser el resultado de leer píxeles de diferentes frames mezclados

Próximos Pasos

  1. Optimizar renderizado (Step 0307):
    • Cachear superficie escalada
    • Optimizar bucle de renderizado
    • Medir impacto con monitor de rendimiento
  2. Verificar sincronización (Step 0308):
    • Confirmar que el snapshot se toma en el momento correcto
    • Verificar que no hay condiciones de carrera
  3. Probar correcciones:
    • Ejecutar emulador y verificar FPS mejora
    • Verificar que corrupción gráfica desaparece

Referencias

  • Pan Docs - "Background Tile Map"
  • Pan Docs - "Sprites"
  • Pan Docs - "LCD Timing"
  • Step 0219 - Snapshot Inmutable del Framebuffer
  • Step 0305 - Investigación de Renderizado Python
  • ANALISIS_STEP_0306_RENDIMIENTO_CORRUPCION.md - Análisis detallado