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
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 Pythonsrc/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
PixelArrayen 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
- Optimizar renderizado (Step 0307):
- Cachear superficie escalada
- Optimizar bucle de renderizado
- Medir impacto con monitor de rendimiento
- Verificar sincronización (Step 0308):
- Confirmar que el snapshot se toma en el momento correcto
- Verificar que no hay condiciones de carrera
- 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