This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Python Renderer Research and Fix
Summary
This step investigates why tiles appear fuzzy or half-finished in the Python renderer. Extensive diagnostic logs implemented to verify color index conversion to RGB, the application of the palette in NumPy and PixelArray, the scaling and interpolation, the format of the framebuffer, and the debug vs real palette. The logs revealed thatthe python renderer works correctly- Indices to RGB conversion is successful, palette is applied correctly in both NumPy and PixelArray, and scaling does not cause interpolation problems. The fuzzy tiles problem is probably not in the Python renderer, but somewhere else (possibly in the generation of the framebuffer in C++ or in how the tiles are displayed).
Hardware Concept
Conversion of Indices to RGB
The PPU framebuffer contains color indices (0-3), not RGB values directly. Each index
must be converted to an RGB color using the palette before drawing to the screen. The conversion
It's simple:rgb_color = palette[color_index], wherepaletteit's a list
of 4 RGB tuples (R, G, B) andcolor_indexis the pixel index (0-3).
Game Boy palette
The Game Boy palette has 4 possible colors (indexes 0-3):
- Color 0: White (lighter) - RGB(255, 255, 255)
- Color 1: Light gray - RGB(170, 170, 170)
- Color 2: Dark gray - RGB(85, 85, 85)
- Color 3: Black (darker) - RGB(8, 24, 32)
Each pixel in the framebuffer has an index (0-3) that is mapped to one of these colors using the palette.
Scaling and Interpolation
The original Game Boy screen is 160x144 pixels, but it scales to the window resolution (e.g. 480x432 with scale=3). Scaling can cause interpolation that mixes colors adjacent, which could cause the tiles to appear blurred. However, if the scaling is done correctly, the colors should remain true to the original.
Implementation
Extensive diagnostic logs were implemented in the Python renderer to verify each rendering process stage:
1. Framebuffer Format Check
Added logs to verify the type and size of the received framebuffer. The logs confirm that:
- The framebuffer is a
bytearraywith length 23040 (160×144) - The indices are in the correct range (0-3)
- The index distribution is correct (checkerboard: indexes 0 and 3)
2. Verification of Indices to RGB Conversion
Added logs to verify that indices are correctly converted to RGB using the palette. The logs show that:
- The indexes are converted correctly:
rgb_color = palette[color_index] - The indices are in the valid range (0-3)
- RGB values are valid 3-integer tuples (R, G, B)
3. Debug vs Real Palette Verification
Added logs to verify that the palette has the correct values. The logs confirm that:
- The palette has 4 valid colors
- Each color is a valid RGB tuple (R, G, B) with values in the range 0-255
- The palette is applied consistently in each frame
4. Verification of Palette Application in NumPy
Added logs to verify that index to RGB mapping works correctly in NumPy. The logs show that:
- The vectorized mapping works correctly:
rgb_array[mask] = rgb - Expected colors match current colors after mapping
- No problems with applying palette in NumPy
5. Verification of Palette Application in PixelArray (Fallback)
Added logs to verify that the palette is applied correctly in PixelArray (fallback when NumPy is not available). The logs confirm that:
- The colors are written correctly to the PixelArray:
px_array[x, y] = palette[color_index] - Expected colors match colors read from PixelArray
- No problems with applying the palette in PixelArray
6. Scaling Verification
Added logs to verify that scaling is working correctly and not causing problems. interpolation. The logs show that:
- Scaling is applied correctly:
pygame.transform.scale() - Colors remain true after scaling (no significant differences)
- No interpolation issues causing fuzzy tiles
Affected Files
src/gpu/renderer.py- Added diagnostic logs to verify index conversion to RGB, palette application, scaling, framebuffer format, and palette debuglogs/test_pkmn_step0337.log- Test logs with Pokémon Red
Tests and Verification
Tests were run with Pokémon Red to verify the Python renderer:
- Command executed:
timeout 150 python3 main.py roms/pkmn.gb 2>&1 | tee logs/test_pkmn_step0337.log - Result:The logs show that all renderer components are working correctly
- Validation:No problems were detected in the diagnostic logs
Log Findings
The logs show that:
- ✅ Framebuffer format:Correct - Type bytearray, length 23040 (160×144)
- ✅ Conversion of Indices to RGB:Success - Indices are correctly converted to RGB
- ✅ Debug palette:Correct - The palette has 4 valid colors
- ✅ Application of Palette in NumPy:Correct - Expected colors match current colors
- ✅ Scaling:Correct - Colors remain true after scaling
Log Example
[Renderer-Framebuffer-Format] Frame 1 | Type: <class 'bytearray'> | Length: 23040 | Expected: 23040 (160*144)
[Renderer-Palette-Debug] Frame 1 | Palette used: [(255, 255, 255), (170, 170, 170), (85, 85, 85), (8, 24, 32)]
[Renderer-Index-to-RGB] Frame 1 | Pixel (0, 0): index=3 -> RGB=(8, 24, 32) | Palette[3]=(8, 24, 32)
[Renderer-NumPy-Palette] Frame 1 | Pixel (0, 0): index=3 | Expected RGB=(8, 24, 32) | Current RGB=(8, 24, 32)
[Renderer-Scale] Frame 1 | Pixel (0, 0) -> (0, 0) | Original RGB=Color(8, 24, 32, 255) | Scaled RGB=Color(8, 24, 32, 255)
Sources consulted
- Bread Docs:Game Boy Pan Docs- General hardware reference
- General knowledge-based implementation of graphics rendering and index conversion to RGB
Educational Integrity
What I Understand Now
- Conversion of Indices to RGB:The framebuffer contains color indices (0-3) that must be converted to RGB using the palette before drawing. This conversion works correctly in the Python renderer.
- Palette Application:The palette is applied correctly in both NumPy (vectorized rendering) and PixelArray (fallback). There are no problems with applying the palette.
- Scaling:Scaling does not cause interpolation problems that make tiles appear fuzzy. Colors remain true after scaling.
What remains to be confirmed
- Origin of the Diffuse Tiles Problem:If the Python renderer works correctly, the problem is probably with the C++ framebuffer generation or how the tiles are displayed. Further investigation is needed.
- Verification with Real Tiles:The current logs show only the checkerboard (indexes 0 and 3). You need to verify with real tiles that contain indexes 1 and 2 to confirm that the conversion works correctly in all cases.
Hypotheses and Assumptions
Hypothesis:The problem with fuzzy tiles is not in the Python renderer, but somewhere else (possibly in the generation of the framebuffer in C++ or in how the tiles are displayed). Esta hipótesis se basa en que todos los logs de diagnóstico del renderizador Python muestran que funciona correctamente.
Next Steps
- [ ] Investigate the generation of the framebuffer in C++ to verify if there are problems in the decoding of tiles or palette application
- [ ] Verify with real tiles containing indexes 1 and 2 to confirm that the conversion works correctly in all cases
- [ ] If the problem persists, investigate other components of the rendering pipeline