This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Python Rendering Research
Summary
Extensive investigation of the rendering code in Python to identify why green streaks appear when the PPU C++ framebuffer only contains 0 indices. Implemented 3 additional monitors to track the palette, the PixelArray and palette modifications during runtime.
Aim: Identify the root cause of the green streaks that appear after ~2 minutes of running, when the PPU C++ framebuffer only contains 0 indices. The problem is NOT in PPU C++, but in Python rendering.
Evaluated hypotheses:
- Hypothesis A: The palette is modified during execution
- Hypothesis B: There is other code that renders using the wrong palette
- Hipótesis C: PixelArray or scaling issue causing visual artifacts
- Hypothesis D: There is a palette that was not corrected in the previous steps
Hardware Concept
Rendering Flow
The rendering flow in the emulator follows these steps:
- PPU C++ generates framebuffer: The framebuffer contains color indices (0-3) in 1D format (23040 elements = 160x144)
- Python renderer get framebuffer: Zero-Copy via memoryview from PPU C++
- Renderer maps indices to RGB: Use a palette to convert indices (0-3) to RGB colors
- Renderer writes to PixelArray: Write RGB pixels to a Pygame surface (160x144)
- PixelArray is scaled and blit: The surface is scaled to the window (480x432) and blit to the screen
Possible Flow Problems
If the PPU C++ framebuffer only contains 0 (white) indices, but green streaks appear, the problem must be one of these points:
- Modified palette: Palette is changed during execution, causing 0 indices to be mapped to green
- Multiple pallets: There is another palette in use that was not corrected
- Problems with PixelArray: PixelArray or scaling causes visual artifacts
- Additional code: There is other code that renders using the wrong palette
Fountain: Pan Docs - "Framebuffer", "Palette", "Pixel Mapping"
Implementation
Comprehensive Pallet Search
An exhaustive search was performed for all palettes in the code:
- Search for green values: No green values found (224, 248, 208), (136, 192, 112), (52, 104, 86)
- Finding Palette Definitions: 40 matches found, all verified and corrected
Found pallets:
self.COLORS(line 191): Renderer base palette - ✅ Correcteddebug_palette_map(lines 502, 614, 990): Debug palette - ✅ Fixedpalette0andpalette1(lines 999, 1005): Sprite Palettes - ✅ Fixed
Rendering Code Search
All functions and rendering operations were searched:
- Rendering functions: 4 functions found (update_tile_cache, render_vram_debug, render_frame, render_sprites)
- Render operations: 17 operations found (blit, fill, set_at)
- Conclusion: There is no additional code that renders. The flow is clear and unique.
Implemented Monitors
Implemented 3 additional monitors to track rendering:
Monitor 1: [PALETTE-VERIFY]
Check the palette used in each frame:
- Location:
render_frame()after definingpalette - Frequency: Every 1000 frames or first 100 frames
- Functionality: Prints the RGB values of the palette (Palette[0], Palette[1], Palette[2], Palette[3])
if self._palette_verify_count % 1000 == 0 or self._palette_verify_count< 10:
if self._palette_verify_count < 100:
print(f"[PALETTE-VERIFY] Frame {self._palette_verify_count} | "
f"Palette[0]={palette[0]} Palette[1]={palette[1]} "
f"Palette[2]={palette[2]} Palette[3]={palette[3]}")
self._palette_verify_count += 1
Monitor 2: [PIXEL-VERIFY]
Check the center pixel before mapping to PixelArray:
- Location:
render_frame()before writing inPixelArray - Frequency: First 10 frames
- Functionality: Check the center pixel (line 72, column 80) before and after mapping
if self._pixel_verify_count< 10:
center_idx = (72 * 160 + 80) # Línea 72, columna 80
center_color_idx = frame_indices[center_idx] & 0x03
center_color_rgb = palette[center_color_idx]
print(f"[PIXEL-VERIFY] Frame {self._pixel_verify_count} | "
f"Center pixel: idx={center_idx} color_idx={center_color_idx} "
f"rgb={center_color_rgb}")
self._pixel_verify_count += 1
Monitor 3: [PALETTE-MODIFIED]
Detects if the palette is modified during execution:
- Location:
render_frame()after definingdebug_palette_map - Functionality: Compares the current palette with the last checked palette and shows stack trace if it changes
if self._last_palette_checked is not None:
if self._last_palette_checked != debug_palette_map:
print(f"[PALETTE-MODIFIED] Modified palette detected!")
print(f" Original: {self._original_debug_palette}")
print(f" Current: {debug_palette_map}")
import traceback
traceback.print_stack(limit=10)
self._last_palette_checked = dict(debug_palette_map)
Affected Files
src/gpu/renderer.py- Implementation of 3 additional monitors ([PALETTE-VERIFY], [PIXEL-VERIFY], [PALETTE-MODIFIED])ANALYSIS_STEP_0305_RENDERER.md- Analysis document with all findingsdebug_step_0305_renderer.log- Execution logs (in progress)
Tests and Verification
Running the Emulator: The emulator ran in the background for 2-3 minutes to capture logs.
Analysis Commands:
# Analyze [PALETTE-VERIFY]
Select-String -Path debug_step_0305_renderer.log -Pattern "\[PALETTE-VERIFY\]" | Select-Object -First 20 -Last 20
# Analyze [PIXEL-VERIFY]
Select-String -Path debug_step_0305_renderer.log -Pattern "\[PIXEL-VERIFY\]" | Select-Object -First 10
# Search for palette modifications
Select-String -Path debug_step_0305_renderer.log -Pattern "\[PALETTE-MODIFIED\]" | Select-Object -First 10
# Find patterns before green stripes
Select-String -Path debug_step_0305_renderer.log -Pattern "\[PALETTE-VERIFY\]" | Select-Object -Skip 50 -First 20
C++ Compiled Module Validation: Monitors run in Python and check the framebuffer obtained from PPU C++.
Findings
Palette Search
- ✅ No green values foundin the code
- ✅ All palettes are corrected: self.COLORS, debug_palette_map, palette0, palette1
- ✅ Hypothesis D rejected: There are no palettes pending correction
Rendering Code Search
- ✅ There is no additional code to render: There is only one main rendering stream
- ✅ Hypothesis B rejected: No other code using wrong palette
Implemented Monitors
- ✅ [PALETTE-VERIFY]: Deployed and active
- ✅ [PIXEL-VERIFY]: Deployed and active
- ✅ [PALETTE-MODIFIED]: Deployed and active
Execution Analysis and Screenshot
Observaciones de la captura de pantalla:
- ✅ Visible sprites: Pokémon sprites can be seen (although fragmented) and "RED" text
- ✅ Green stripe problem: No green streaks observedon capture (possibly resolved)
- ⚠️ Critical performance: FPS 21.8 (should be ~60 FPS) - new issue identified
- ⚠️ Graphic corruption: Checkerboard pattern in lower section, vertical lines in upper section, fragmented sprites
Conclusion: The original green streaking issue appears to be resolved, but two new critical issues have been identified:
- Performance: Extremely low FPS (21.8 vs 60 expected)
- Graphic corruption: Anomalous patterns and fragmented sprites
Sources consulted
- Pan Docs: "Framebuffer", "Palette", "Pixel Mapping"
- Step 0304: Extended Verification and Framebuffer Monitor
- Step 0303: Correction of Debug Palette Indexes 1 and 2
Educational Integrity
What I Understand Now
- Rendering flow: The PPU C++ framebuffer contains indices (0-3), which are mapped to RGB using a palette in Python, then written to the PixelArray and scaled.
- Comprehensive search: It is important to search all the palettes and rendering code to ensure that nothing is missed.
- Multiple monitors: Implementing multiple monitors allows you to capture different aspects of the problem (palette, pixels, modifications).
- Control de contexto: Logs should be redirected to files and analyzed with samples, not read in full to avoid cluttering the context.
What remains to be confirmed
- Log analysis: Pending complete analysis when logs are available.
- Root cause: If the monitors do not detect palette modifications, the problem may be with PixelArray, scaling, or how Pygame renders colors.
Hypotheses and Assumptions
Current hypothesis: If the PPU C++ framebuffer only contains 0 indices, but green streaks appear, the problem must be with:
- Mapping indices to RGB (wrong palette sometime)
- PixelArray or scaling causing artifacts
- Pygame rendering colors incorrectly
The monitors in place will help identify which of these hypotheses is correct.
Next Steps
High Priority
- [ ] Research Performance (FPS 21.8):
- Profile the render loop
- Check for crashes in Python code
- Optimize expensive operations (PixelArray, scaling)
- Check CPU-PPU synchronization
- [ ] Investigate Graphic Corruption:
- Verify framebuffer integrity
- Investigate checkerboard pattern (possible issue with tiles or VRAM)
- Check synchronization of tiles and sprites
- Review sprite rendering code
Medium Priority
- [ ] Check Green Streak Problem:
- Run extended session (10-15 minutes) to confirm that the green stripes do not appear
- If they appear, use the implemented monitors to diagnose
- [ ] Improve Monitors:
- Ensure logs are generated correctly
- Add performance monitors (FPS, frame time)
- Add graphical corruption monitors