⚠️ Clean-Room / Educational

This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.

Python Visualization Research and Framebuffer Read Timing

Date:2025-12-29 StepID:0340 State: VERIFIED

Summary

Comprehensive investigation of framebuffer read timing in Python and the correspondence between framebuffer content and display. Implemented diagnostic logs to verify when the framebuffer is read (relative to when it is markedframe_ready_), what the framebuffer contains when there are real tiles, and whether the contents of the framebuffer match what is shown on the screen. The logs revealed that the read timing is correct and the framebuffer contains consistent data when read.

Hardware Concept

Framebuffer Timing

The framebuffer is rendered line by line (LY 0-143). When LY reaches 144 (VBLANK_START), the frame is complete. Python should read the framebuffer after the frame completes. The framebuffer is cleared at the start of the next frame (LY=0).

C++-Python Synchronization

C++ brandframe_ready_ = truewhen LY=144. Python reads the framebuffer when it detectsframe_ready_. The framebuffer must be read before it is cleared (at the start of the next frame). The timing between marking and reading is critical to ensure that Python always reads the entire framebuffer.

Framebuffer-Display Mapping

The framebuffer contains color indices (0-3) for each pixel. The Python renderer converts these indices to RGB using a palette. The correspondence between framebuffer and display must be exact: each index must be converted to the correct RGB color according to the palette.

Fountain:Pan Docs - "LCD Timing", "Frame Rate", "LCD Status Register"

Implementation

4 blocks of diagnostic logs were implemented to investigate the framebuffer read timing and the correspondence between framebuffer and display.

1. Framebuffer Read Timing Verification (Python)

Logs were added insrc/viboy.pywhen the framebuffer is read to check when it is read (relative to when it is checkedframe_ready_) and what it contains.

  • Frame ready detection:Log when detectedframe_ready_
  • Reading timing:Framebuffer read time measurement
  • Content before copying:Checking the first 20 pixels before copying
  • Content after copying:Checking the first 20 pixels after copying
  • Index distribution:Index count in the first 100 pixels
  • Tags: [Viboy-Framebuffer-Timing], [Viboy-Framebuffer-Read-Timing]

2. Verifying Framebuffer Content When There Are Real Tiles (C++)

Logs were added insrc/core/cpp/PPU.cppto check the contents of the framebuffer when real tiles (not checkerboard) are detected.

  • Detection of real tiles:When LY=0, check if there are real tiles in VRAM (≥200 non-zero bytes)
  • Framebuffer check:When LY=144 and there were real tiles, check the contents of the framebuffer
  • Index distribution:Count indices across the entire framebuffer (144x160 pixels)
  • Specific pixels:Check some specific pixels (corners and center)
  • Tag: [PPU-FRAMEBUFFER-WITH-TILES]

3. Correspondence Verification Between Framebuffer and Display (Python)

Logs were added insrc/gpu/renderer.pywhen the framebuffer is rendered to verify that the received content matches the framebuffer read inviboy.py.

  • First 20 pixels:Verification of the first 20 indices received
  • Index distribution:Index count in the first 100 pixels
  • Specific pixels:Checking some specific pixels (corners and center)
  • Tag: [Renderer-Framebuffer-Visualization]

4. Timing Verification Between C++ and Python

Logs were added insrc/core/cpp/PPU.cppwhen markedframe_ready_and insrc/viboy.pywhen the framebuffer is read to verify the timing between the two.

  • Timing in C++:Log when checkedframe_ready_(LY=144) and verifying that the framebuffer has data
  • Timing in Python:Framebuffer read time measurement
  • Tags: [PPU-FRAME-READY-TIMING], [Viboy-Framebuffer-Read-Timing]

Affected Files

  • src/viboy.py- Added framebuffer read timing logs
  • src/core/cpp/PPU.cpp- Added timing logs when frame_ready_ is marked and framebuffer verification when there are real tiles
  • src/gpu/renderer.py- Added correspondence logs between framebuffer and visualization

Tests and Verification

Tests were run with the 5 ROMs to verify the timing and content of the framebuffer:

  • Test ROMs:pkmn.gb, tetris.gb, mario.gbc, pkmn-amarillo.gb, Oro.gbc
  • Duration:2.5 minutes each (150 seconds)
  • Logs generated: logs/test_*_step0340.log

Log Results

The logs revealed that:

  • Correct timing:Framebuffer is read correctly after it is checkedframe_ready_
  • Consistent data:The framebuffer contains consistent data when read (11520 non-zero pixels out of 23040 in initial frames)
  • Correspondence:The content of the framebuffer received in the renderer matches the content read inviboy.py

Log Example

[PPU-FRAME-READY-TIMING] Frame 1 | LY:144 (VBLANK_START) | frame_ready_ checked | Non-zero pixels: 11520/23040
[Viboy-Framebuffer-Read-Timing] Frame 1 | Reading framebuffer in t=...
[Viboy-Framebuffer-Read-Timing] Frame 1 | Framebuffer read in X.XXXms
[Renderer-Framebuffer-Visualization] Frame 1 | First 20 indices: [...]
[PPU-FRAMEBUFFER-WITH-TILES] Frame Real tiles detected | Total non-zero pixels: ...

Sources consulted

Educational Integrity

What I Understand Now

  • Framebuffer timing:The framebuffer is rendered line by line and marked ready when LY=144. Python should read it after it is marked ready but before it is cleared (at the start of the next frame).
  • C++-Python Synchronization:The flagframe_ready_is flagged in C++ when LY=144. Python reads the framebuffer when it detects this flag. The timing between marking and reading is critical.
  • framebuffer-display correspondence:The framebuffer contains color indices (0-3) that the Python renderer converts to RGB using a palette. The correspondence must be exact.

What remains to be confirmed

  • Display problem:Although the timing and content of the framebuffer are correct, the display problem (vertical stripes + white bar vs expected checkerboard) persists. More research is needed to identify the cause.
  • Reading timing:Check if there are significant delays between dialingframe_ready_and read the framebuffer which may cause display problems.

Hypotheses and Assumptions

Main hypothesis:The display problem is probably not related to the framebuffer read timing or the content of the framebuffer, but rather something else (possibly the conversion of indices to RGB in the Python renderer or the scaling/interpolation of the image).

Next Steps

  • [ ] Analyze the complete logs of the 5 ROMs to identify common patterns
  • [ ] Investigate converting indices to RGB in the Python renderer
  • [ ] Check image scaling and interpolation
  • [ ] If the cause is identified, implement the correction in the next step