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 Pantallas Blancas Post-Doble Buffering
Resumen
A pesar de que el doble buffering eliminó completamente las condiciones de carrera (0 advertencias vs 7291 antes), todas las ROMs siguen mostrando pantallas completamente blancas. Se implementaron verificaciones detalladas en cada etapa del pipeline de renderizado (escritura al framebuffer back, intercambio de buffers, lectura en Python, renderizado) para identificar exactamente dónde se pierden los datos. Los logs confirman que el intercambio funciona correctamente y que Python lee datos del framebuffer, pero el renderizado normal no está escribiendo datos al framebuffer_back_ (todas las líneas están vacías). Los 11520 píxeles no-blancos que aparecen en el intercambio parecen venir del checkerboard, no del renderizado normal de tiles.
Concepto de Hardware
Pipeline de Renderizado Completo en Emuladores Híbridos
En un emulador híbrido C++/Python, el pipeline de renderizado tiene múltiples etapas que deben funcionar correctamente:
- Renderizado en C++ (PPU):
render_scanline()escribe píxeles alframebuffer_back_durante MODE_3_PIXEL_TRANSFER - Intercambio de buffers: Cuando se completa un frame (LY=144), se intercambian
framebuffer_front_yframebuffer_back_usandostd::swap() - Lectura en Python: Python lee el
framebuffer_front_a través de Cython (memoryview zero-copy) - Copia inmutable: Python crea un
bytearraysnapshot del framebuffer para protegerlo de cambios - Renderizado en Python: El renderizador convierte índices de color (0-3) a RGB usando la paleta BGP y dibuja en Pygame
- Actualización de pantalla: Pygame muestra la superficie en la ventana
Cualquier problema en cualquiera de estas etapas causará pantallas blancas o gráficos corruptos.
Verificación de Datos en Cada Etapa
Es crítico verificar que los datos se mantienen correctos en cada etapa:
- Si el framebuffer_back_ está vacío: El problema está en el renderizado C++ (no se escriben datos durante MODE_3)
- Si el framebuffer_front_ está vacío después del intercambio: El problema está en el intercambio (aunque
std::swap()es atómico) - Si Python lee ceros: El problema está en la lectura o el wrapper de Cython
- Si el renderizador recibe ceros: El problema está en la transferencia de datos (snapshot)
- Si la superficie está blanca: El problema está en el renderizado Python (conversión RGB o dibujo)
Implementación
Verificaciones Implementadas
Se implementaron verificaciones detalladas en cada etapa del pipeline:
- Verificación de escritura al framebuffer_back_: Al final de
render_scanline(), se verifica que se escribieron datos no-blancos en las líneas 0, 72 y 143 - Verificación de frame completo: Cuando LY=143 y mode=MODE_1_VBLANK, se verifica que el framebuffer_back_ tiene datos antes del intercambio
- Verificación de intercambio: En
swap_framebuffers(), se cuenta píxeles no-blancos antes y después del intercambio - Verificación de lectura en Python: En
viboy.py, se verifica el contenido del framebuffer antes y después de la copia - Verificación de renderizado: En
renderer.py, se verifica que el renderizador recibe datos y que la superficie tiene píxeles no-blancos
Correcciones Realizadas
- Movimiento de verificación: La verificación de escritura se movió al final de
render_scanline()(después de escribir) en lugar del principio - Corrección de buffer: Se corrigió código que verificaba
framebuffer_front_cuando debería verificarframebuffer_back_
Archivos Afectados
src/core/cpp/PPU.cpp- Agregadas verificaciones enrender_scanline(),swap_framebuffers()y verificación de frame completosrc/viboy.py- Agregada verificación de lectura del framebuffer antes y después de la copiasrc/gpu/renderer.py- Agregada verificación de datos recibidos y verificación de superficie después de dibujar
Tests y Verificación
Se ejecutaron pruebas con las 6 ROMs (TETRIS, Mario, Zelda DX, Oro, PKMN, PKMN-Amarillo) para analizar los logs de verificación:
Hallazgos de los Logs
- ✅ Intercambio funciona correctamente: Back buffer tiene 11520 píxeles no-blancos (50%), front los recibe correctamente
- ✅ Python lee correctamente: Lee 52 píxeles no-blancos en los primeros 100 (datos presentes)
- ❌ Renderizado normal no escribe datos: Todas las líneas renderizadas están vacías (0 píxeles no-blancos)
- ❌ Los 11520 píxeles no-blancos parecen venir del checkerboard: No del renderizado normal de tiles
Logs de Ejemplo
[PPU-SWAP-DETAILED] Frame 1 | Back before: 11520 non-zero | Front before: 0 non-zero | Front after: 11520 non-zero
[PPU-SWAP-DETAILED] Front first 20 pixels: 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 3 3 3 3
[Python-Read-Framebuffer] Frame 1 | First 20 indices: [3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3] | Non-zero in first 100: 52/100
[Python-Read-Framebuffer] ✅ Copia correcta: 52 non-zero pixels
Problema identificado: El renderizado normal no está escribiendo datos al framebuffer_back_ durante MODE_3_PIXEL_TRANSFER.
Todas las líneas están vacías, lo que sugiere que render_scanline() no está ejecutando la lógica de renderizado de tiles,
o que la VRAM está vacía cuando se intenta renderizar.
Fuentes Consultadas
- Pan Docs: LCD Controller (PPU)
- Implementación basada en conocimiento general de arquitectura LR35902 y sistemas de renderizado con doble buffering
Integridad Educativa
Lo que Entiendo Ahora
- Pipeline de renderizado: El pipeline completo desde C++ hasta Pygame tiene múltiples etapas que deben funcionar correctamente
- Verificación de datos: Es crítico verificar cada etapa para identificar dónde se pierden los datos
- Doble buffering funciona: El intercambio de buffers funciona correctamente, eliminando condiciones de carrera
- Problema en renderizado: El renderizado normal no está escribiendo datos al framebuffer_back_, todas las líneas están vacías
Lo que Falta Confirmar
- Por qué render_scanline() no escribe datos: Necesito investigar si la VRAM está vacía, si MODE_3 no se ejecuta, o si hay otro problema
- Origen de los 11520 píxeles no-blancos: Confirmar si vienen del checkerboard o de otro lugar
- Por qué las pantallas están blancas: Aunque Python lee datos, las pantallas están blancas, lo que sugiere un problema en el renderizado Python
Hipótesis y Suposiciones
Hipótesis principal: El renderizado normal no está escribiendo datos porque la VRAM está vacía cuando se intenta renderizar, o porque MODE_3_PIXEL_TRANSFER no se está ejecutando correctamente. Los 11520 píxeles no-blancos vienen del checkerboard, que se activa cuando VRAM está vacía, pero el checkerboard no se está mostrando en la pantalla (posible problema en el renderizado Python).
Próximos Pasos
- [ ] Investigar por qué
render_scanline()no escribe datos durante MODE_3_PIXEL_TRANSFER - [ ] Verificar si la VRAM está vacía cuando se intenta renderizar
- [ ] Verificar si MODE_3_PIXEL_TRANSFER se está ejecutando correctamente
- [ ] Investigar por qué las pantallas están blancas aunque Python lee datos del framebuffer
- [ ] Verificar si el problema está en la conversión RGB o en el dibujo en Pygame