⚠️ 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.

Investigation of Recurring Green Streaks

Date:2025-12-25 StepID:0301 State: VERIFIED

Summary

Investigation of why the green stripes appear again after a few minutes of running, despite the correction implemented in Step 0300. 3 diagnostic monitors were implemented to track palette usage and render mode changes, and fixedself.COLORSwhich still had green values ​​for index 0.

Aim: Identify the root cause of recurring green streaks and correct any code that uses palettes with green values instead of the fixed debug palette.

Hardware Concept

Multiple Palette Sources in the Renderer

The renderer can use multiple palette sources at runtime:

  • Debug palette (local): Local variablepalettedefined inrender_frame()with corrected values ​​(white for index 0).
  • Class palette (self.palette):Instance attribute initialized fromself.COLORSwhich can be modified during execution.
  • Hardware Palette (BGP): Register 0xFF47 that maps indices to colors based on game settings.

If the renderer switches between different palette sources, the colors may change dynamically. The problem identified is thatself.COLORSandself.palettethey still had values green for index 0, which could cause some code to use them instead of the local debug palette.

Fountain: Pan Docs - "Background Palette (BGP)", "Palette"

C++ Mode vs Python Mode

The renderer can operate in two modes:

  • use_cpp_ppu=True: Use the C++ framebuffer directly (faster). The debug palette is applied when mapping framebuffer indices to RGB colors.
  • use_cpp_ppu=False: Calculate tiles from VRAM in Python (slower but more flexible). Use the local debug palette to decode tiles.

If the renderer changes modes during execution, you can use different code paths that have different palettes. The monitors in place detect these changes to identify if there is a relationship with the appearance of green stripes.

Fountain: Pan Docs - "LCD Control Register", "PPU Rendering"

Implementation

Implemented preventive fixes and 3 diagnostic monitors to track pallet usage and changes in rendering mode.

Correction ofself.COLORS

It was correctedself.COLORSin__init__()so that index 0 is white instead of green:

# Before (Step 0300):
self.COLORS = [
    (224, 248, 208), #0: White/Light Green (GREEN)
    ...
]

# After (Step 0301):
self.COLORS = [
    (255, 255, 255), #0: White (Color 0) - Corrected Step 0301
    ...
]

This fix ensures that if any code usesself.COLORSeitherself.palette(which is initialized fromself.COLORS), the values ​​are correct.

[PALETTE-USE-TRACE] Monitor

Monitor that tracks which palette is used in each frame. Implemented in both render paths (C++ and Python) and record:

  • Frame number
  • State ofuse_cpp_ppu
  • Color of index 0 in the local debug palette
  • Index color 0 inself.palette
# Monitor implemented in render_frame()
if self._palette_trace_count< 100 or (self._palette_trace_count % 1000 == 0):
    use_cpp = self.use_cpp_ppu
    palette_0_debug = palette[0]  # Paleta debug local
    palette_0_self = self._palette[0]  # self.palette
    print(f"[PALETTE-USE-TRACE] Frame {self._palette_trace_count} | use_cpp_ppu={use_cpp} | debug_palette[0]={palette_0_debug} | self.palette[0]={palette_0_self}")
self._palette_trace_count += 1

The monitor records the first 100 frames and then every 1000 frames to avoid saturating the logs.

Monitor [PALETTE-SELF-CHANGE]

Monitor that detects changes inself.paletteusing a property with setter. If any code modifiesself.palette, the monitor records the change and a stack trace:

@property
def palette(self):
    """Getter for self.palette (used by Step 0301 monitors)."""
    return self._palette

@palette.setter
def palette(self, value):
    """Setter for self.palette with change monitor (Step 0301)."""
    old_0 = self._palette[0] if self._palette else None
    self._palette = value
    new_0 = value[0] if value else None
    if old_0 != new_0:
        import traceback
        print(f"[PALETTE-SELF-CHANGE] self.palette[0] changed: {old_0} -> {new_0}")
        print(f"[PALETTE-SELF-CHANGE] Stack trace:")
        traceback.print_stack(limit=5)

This monitor requires changingself.palettetoself._palettein__init__()to use the property.

Monitor [CPP-PPU-TOGGLE]

Monitor that detects changes inuse_cpp_ppuduring execution:

# Monitor implemented in render_frame()
if self._last_use_cpp_ppu is None:
    self._last_use_cpp_ppu = self.use_cpp_ppu
elif self._last_use_cpp_ppu != self.use_cpp_ppu:
    print(f"[CPP-PPU-TOGGLE] use_cpp_ppu changed: {self._last_use_cpp_ppu} -> {self.use_cpp_ppu}")
    self._last_use_cpp_ppu = self.use_cpp_ppu

This monitor helps identify if the renderer switches between C++ mode and Python mode, which could cause a different palette to be used.

Search for Code that Usesself.palette

An exhaustive search was carried out insrc/gpu/renderer.pyto identify all places where it is usedself.paletteeitherself.COLORS:

  • Line 198: self._palette = list(self.COLORS)- Initialization (fixed)
  • Lines 349-387: update_tile_cache(palette)- Use palette as parameter (local variable)
  • Lines 701, 705, 791, 867: Use ofpaletteas a local variable inrender_frame()

Conclusion: No code found that usesself.palettedirectly during rendering. All references topaletteThey are local variables defined inrender_frame()with the debug palette corrected. The correction ofself.COLORSis preemptive to ensure that if any future code usesself.palette, the values ​​are correct.

Affected Files

  • src/gpu/renderer.py- Correction ofself.COLORSand implementation of 3 diagnostic monitors

Tests and Verification

The implementation was verified by:

  • Static code analysis: Exhaustive search for uses ofself.paletteandself.COLORS
  • Linter: No syntax or type errors
  • Monitor validation: All 3 monitors are deployed and ready to capture logs during extended execution

Next step: Run the emulator for several minutes and analyze the logs generated by monitors to identify when and why the green streaks return.

Sources consulted

  • Pan Docs: "Background Palette (BGP)" - Palette management on Game Boy
  • Pan Docs: "LCD Control Register" - Render Modes
  • Pan Docs: "PPU Rendering" - Graphics Rendering

Educational Integrity

What I Understand Now

  • Multiple palette fonts: The renderer can use different palettes depending on the context. The local debug palette is the one used during rendering, butself.palettecould be used elsewhere.
  • Diagnostic Monitors: Monitors allow you to track dynamic changes in status of the renderer that could cause visual problems. They are essential tools for debugging.
  • Preventive fix: Although no code was found that usesself.paletteduring rendering, correctself.COLORSensures that any future code uses correct values.

What remains to be confirmed

  • Root Cause of Recurring Streaks: Monitors should run for several minutes to identify when and why the green stripes return. Possible causes:
    • Change inuse_cpp_ppuduring execution
    • Modification ofself.paletteby external code
    • Using a different palette in some unidentified code
  • Log analysis: The logs generated by the monitors must be analyzed to identify patterns that match the appearance of green stripes.

Hypotheses and Assumptions

Main hypothesis: Recurring green streaks could be due to some code modifiesself.paletteor change the rendering mode after a few minutes. The monitors implemented should capture these changes.

Assumption: If the correction ofself.COLORSdoes not solve the problem, the analysis Monitor logs should reveal the root cause.

Next Steps

  • [ ] Run the emulator for several minutes with the monitors active
  • [ ] Analyze logs generated by monitors to identify patterns
  • [ ] If root cause is identified, implement specific correction
  • [ ] If the streaks disappear, verify that they do not return with extended tests
  • [ ] If they persist, further investigate other possible places where green palette is used