This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Investigation and Correction of the White Screens and Performance Problem
Summary
A complete diagnostic system was implemented to investigate why screens appear white even though the logs indicate that the pipeline is working correctly. Added detailed checks at each stage of the rendering pipeline: checking the framebuffer before Python reads it, checking that `render_scanline()` is executed and writing to the framebuffer, checking that `render_frame()` is called in Python, investigating the performance issue (very low FPS), and checking the framebuffer clearing timing. The results confirm that the framebuffer has data when it is rendered, but an issue was identified where some frames have an almost empty framebuffer when Python reads it.
Hardware Concept
In a hybrid Python/C++ emulator, the rendering pipeline is critical for proper graphics display. The complete pipeline includes:
- Rendering in C++:The PPU renders each line (scanline) and writes color indices to the framebuffer
- Transfer to Python:Python reads framebuffer from C++ and converts it to RGB
- Rendering in Python:Python draws RGB pixels on screen using Pygame
It is essential to verify that each stage of the pipeline is working correctly. If a stage fails, the graphics will not display correctly. The white screens problem can be caused by:
- Framebuffer is empty when Python reads it
- The framebuffer is cleared before Python renders it
- Rendering does not run correctly
- Synchronization problems between C++ and Python
Emulator Performance:The emulator should maintain ~60 FPS for a smooth experience. Very low FPS (0.1, 10.8) indicates a serious problem that may be caused by infinite loops, crashes, or very expensive operations.
Implementation
Detailed checks were implemented at each stage of the rendering pipeline to identify where visual information is lost.
Components created/modified
- PPU.cpp - Checking the Framebuffer Before Reading:Added check in `get_frame_ready_and_reset()` that displays the contents of the framebuffer just before Python reads it, including index distribution and warning if it is empty
- PPU.cpp - Check render_scanline():Added check in `render_scanline()` that shows what indices are written to the framebuffer, the state of VRAM, and warning if VRAM has tiles but the line is empty
- PPU.cpp - Cleanup Timing Check:Added check in `confirm_framebuffer_read()` that displays the contents of the framebuffer before clearing it
- viboy.py - Verification of Call to render_frame():Added check that shows framebuffer contents when passed to `render_frame()` and warning if empty
- viboy.py - Performance Research:Added timing check in the main loop that shows the duration of each frame and warning if it is too slow
Design decisions
It was decided to add detailed logs with limits (20-50 logs) to avoid saturating the context. The logs are limited to the first frames to capture the initial behavior of the emulator. Fixed using `static` in Python (which doesn't exist) using instance variables instead.
Affected Files
src/core/cpp/PPU.cpp- Added framebuffer checks, render_scanline(), and cleanup timingsrc/viboy.py- Added render_frame() call and performance checks
Tests and Verification
Diagnostic tests were run with Oro.gbc for 30 seconds and the generated logs were analyzed:
- Command executed:
timeout 30 python3 main.py roms/Oro.gbc 2>&1 | tee logs/test_oro_step0361_diagnostic.log - Log analysis:Logs were analyzed using grep to extract specific diagnostic messages
Key Findings
- ✅ Framebuffer has data when rendering:The logs show that the framebuffer has 11520 non-white pixels (50%) in the first frames, which is correct for a checkerboard pattern
- ✅ render_scanline() runs correctly:The logs show that `render_scanline()` is executed and writes indices to the framebuffer (80 non-white pixels per line, 50%)
- ✅ Framebuffer is cleaned correctly:The logs show that the framebuffer is cleared after Python reads it, which is correct
- ⚠️ Problem detected:There is one frame (Frame 14) where the framebuffer is almost empty (80/23040 = 0.35%) when Python reads it, which could explain the white screens
- ❓ Python logs do not appear:The logs for `[Viboy-Render-Call]` and `[Viboy-Performance]` do not appear in the log, which requires further investigation
Log Evidence
[PPU-FRAMEBUFFER-BEFORE-READ] Frame 0 | Non-white pixels: 11520/23040 (50.00%) | Index distribution: 0=11520 1=0 2=0 3=11520
[PPU-RENDER-SCANLINE] LY=0 | Non-zero VRAM: 0/6144 | Line non-white: 80/160 (50.0%) | First 10 indices: 3 3 3 3 3 3 3 3 0 0
[PPU-CLEAR-TIMING] Frame 0 | Non-white pixels before clear: 11520/23040 (50.00%) | Clearing framebuffer now
[PPU-FRAMEBUFFER-BEFORE-READ] Frame 14 | Non-white pixels: 80/23040 (0.35%)
[PPU-FRAMEBUFFER-BEFORE-READ] ⚠️ WARNING: Framebuffer is empty when Python goes to read!
Sources consulted
- Pan Docs: Rendering Lines (Scanlines)
- Pan Docs: Framebuffer Synchronization
- General knowledge-based implementation of hybrid Python/C++ architecture
Educational Integrity
What I Understand Now
- Rendering Pipeline:The complete pipeline includes rendering in C++, porting to Python, and rendering in Python. Each stage must work correctly for the graphics to display
- Framebuffer verification:It is essential to verify that the framebuffer has data when it is rendered. The logs confirm that the framebuffer has data in most frames
- Empty Frames Problem:Some frames have an almost empty framebuffer when Python reads it, which could explain the white screens
What remains to be confirmed
- Python logs:The logs for `[Viboy-Render-Call]` and `[Viboy-Performance]` do not appear in the log. Needs additional research to determine why
- Cause of Empty Frames:Need to investigate why some frames have an almost empty framebuffer when Python reads it
- Performance:Need to investigate why the FPS is very low (0.1, 10.8) and if it is related to the white screen problem
Hypotheses and Assumptions
Main Hypothesis:The white screen problem could be caused by frames where the framebuffer is almost empty when Python reads it. This could be caused by a timing issue or the framebuffer being cleared too soon.
Next Steps
- [ ] Investigate why Python logs are not appearing
- [ ] Investigate why some frames have an almost empty framebuffer when Python reads it
- [ ] Investigate the performance issue (very low FPS)
- [ ] Implement correction based on findings
- [ ] Visually verify that graphs display correctly after correction