This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Enabling Real Rendering (Background C++)
Summary
The C++ → Python video link works correctly (confirmed with gray screen and red dot at 60 FPS). Removed the diagnostic code (gray screen and red pixel) from the C++ PPU builder and enabled the actual rendering of the Background. The framebuffer is now initialized to blank (0xFFFFFFFF) and the `render_scanline()` method renders the actual game tiles from VRAM when the PPU enters H-Blank (Mode 0).
Hardware Concept
Scanline rendering on Game Boy:The PPU renders the screen line by line (scanline rendering). Each visible line (0-143) goes through 4 modes:
- Mode 2 (OAM Search):0-79 cycles - PPU searches for sprites that intersect the current line
- Mode 3 (Pixel Transfer):80-251 cycles - PPU transfers tile data from VRAM
- Mode 0 (H-Blank):252-455 cycles - The PPU renders the entire line into the framebuffer
- Mode 1 (V-Blank):Lines 144-153 - Vertical delay period, not rendered
Background Rendering:During H-Blank (Mode 0), the PPU reads the tilemap from VRAM (0x9800-0x9BFF or 0x9C00-0x9FFF), decodes the tiles from Tile Data (0x8000-0x8FFF or 0x8800-0x97FF), applies the scroll (SCX/SCY) and the BGP palette, and writes the 160 pixels of the current line to the framebuffer.
Framebuffer initialization:The framebuffer must be initialized to a default color (white) before the PPU starts rendering. If left uninitialized or initialized to a diagnostic color (gray), Games will display incorrect colors or test screens instead of actual content.
Fountain:Pan Docs - LCD Timing, Background, Tile Data, 2bpp Format
Implementation
The following changes were made to `src/core/cpp/PPU.cpp`:
1. Clearing the Diagnostic Code
Removed the diagnostic code that painted the screen gray and one pixel red:
- Builder:Changed framebuffer initialization from gray (`0xFF808080`) to white (`0xFFFFFFFF`)
- Builder:Removed the line that painted the diagnostic pixel red (`framebuffer_[0] = 0xFFFF0000`)
2. Rendering Logic Verification
Verified that the rendering logic works correctly:
- `step()` method:Calls `render_scanline()` when the PPU enters Mode 0 (H-Blank) and the line is visible (LY< 144)
- `render_scanline()` method:Render Background, Window and Sprites in the correct order
- `render_bg()` method:Read VRAM, apply scroll (SCX/SCY), decode 2bpp tiles and apply BGP palette
Modified components
src/core/cpp/PPU.cpp- Builder: Initialization of framebuffer to blank, removal of diagnostic code
Design decisions
Initialization color:White (0xFFFFFFFF) was chosen as the default color because it is color 0 of the BGP palette standard (0xE4), which corresponds to "transparent" or "white" in most games. If a tile does not render correctly, It will appear white instead of gray, making it easier to debug.
No changes to rendering logic:The rendering logic was already implemented correctly in previous steps. Only the diagnostic code needed to be removed to trigger the actual rendering.
Affected Files
src/core/cpp/PPU.cpp- Clean diagnostic code, initialize framebuffer to blank
Tests and Verification
Rendering tests were run to ensure that cleaning the diagnostic code did not break functionality:
$ python -m pytest tests/test_core_ppu_rendering.py -v
============================= test session starts =============================
platform win32 -- Python 3.13.5, pytest-9.0.2, pluggy-1.6.0
collected 4 items
tests/test_core_ppu_rendering.py::TestCorePPURendering::test_bg_rendering_simple_tile PASSED [ 25%]
tests/test_core_ppu_rendering.py::TestCorePPURendering::test_bg_rendering_scroll PASSED [ 50%]
tests/test_core_ppu_rendering.py::TestCorePPURendering::test_window_rendering PASSED [ 75%]
tests/test_core_ppu_rendering.py::TestCorePPURendering::test_framebuffer_memoryview PASSED [100%]
============================== 4 passed in 0.12s ==============================
Compiled C++ module validation:All tests passed, confirming that:
- Background rendering works correctly
- Scroll (SCX/SCY) is applied correctly
- The Window is rendered on top of the Background
- Framebuffer is correctly exposed as memoryview (Zero-Copy)
Key test:`test_bg_rendering_simple_tile` verifies that a black tile renders correctly in the framebuffer when set to VRAM and tilemap. This test confirms that the actual rendering works.
Sources consulted
- Bread Docs:LCD Timing, Background, Tile Data
- Bread Docs:2bpp Format, Tile Decoding
Educational Integrity
What I Understand Now
- Scanline rendering:The PPU renders line by line during H-Blank (Mode 0), not frame by frame. This is critical for synchronization with the CPU.
- Framebuffer initialization:The framebuffer must be initialized to a default color (white) before the PPU begins rendering. Diagnostic colors (gray, red) must be removed before triggering actual rendering.
- Render Order:Background → Window → Sprites. This order determines which layer is drawn on top of another.
What remains to be confirmed
- Sprite Rendering:The sprites render but the priority logic may need adjustment. Verify with real games.
- Windows:The Window may have timing or positioning problems in some games. Check with Tetris and other games.
Hypotheses and Assumptions
Initialization color:White (0xFFFFFFFF) is assumed to be a safe color to initialize the framebuffer. If a game displays incorrect colors, it may be necessary to initialize to another color or check the BGP palette.
Next Steps
- [ ] Test with real ROMs (Tetris, Mario) to verify that the rendering works correctly
- [ ] Verify that sprites are rendered correctly (priority, transparency, flip)
- [ ] Optimize rendering if necessary (tile caching, loop optimization)
- [ ] Implement support for Game Boy Color (CGB palettes, VRAM Banks)