Summary
When HALF_WIDTH_BUFFERS=1 and FIELD_BUFFERS=0, Scene::prepareFrame's framebuffer clear loop only writes the top half of the buffer. The bottom half retains last-frame's pixels and the rasteriser then overdraws on top of ghost geometry.
Reproduction
JetConfig with HALF_WIDTH_BUFFERS 1, FIELD_BUFFERS 0, any rasterised content, full-screen render (rasterizeBand(0, screenHeight)). Visible result: a vertical-half ghost copy of the previous frame's geometry appears in the lower half of the display.
Root cause
src/Scene.cpp ~line 340 (non-interlaced, non-checkerboard clear branch):
#if HALF_WIDTH_BUFFERS
const int rowPixels = screenWidth / 2;
const int rowCount = screenHeight / 2; // <- halved unconditionally
#else
const int rowPixels = screenWidth;
const int rowCount = screenHeight;
#endif
However the rasteriser's bufferIndex math at src/Renderer.cpp:~1053 walks y over the full [0, screenHeight) range when FIELD_BUFFERS=0:
#if FIELD_BUFFERS
int32_t bufferIndex = (y >> 1) * (screenWidth / 2) + (xStart / 2);
#else
int32_t bufferIndex = y * (screenWidth / 2) + (xStart / 2); // full y range
#endif
So the buffer is half-width but FULL-height in this combination. The /2 on rowCount is only correct when FIELD_BUFFERS=1 also halves the buffer height. The default Config.example.hpp ships both flags on together (intended as a quarter-area mode) so this combination wasn't exercised upstream.
Suggested fix
Gate the height halve on FIELD_BUFFERS:
#if HALF_WIDTH_BUFFERS
const int rowPixels = screenWidth / 2;
#if FIELD_BUFFERS
const int rowCount = screenHeight / 2;
#else
const int rowCount = screenHeight;
#endif
#else
const int rowPixels = screenWidth;
const int rowCount = screenHeight;
#endif
Context
Found while porting Jet to a Raspberry Pi Zero 2 W on Zephyr RTOS at 912x492 RGB565. Happy to file a PR if useful — flagging the bug first since the CLA grant is broad. Tested locally with the above patch and the artifact is fully gone.
Summary
When
HALF_WIDTH_BUFFERS=1andFIELD_BUFFERS=0,Scene::prepareFrame's framebuffer clear loop only writes the top half of the buffer. The bottom half retains last-frame's pixels and the rasteriser then overdraws on top of ghost geometry.Reproduction
JetConfig with
HALF_WIDTH_BUFFERS 1,FIELD_BUFFERS 0, any rasterised content, full-screen render (rasterizeBand(0, screenHeight)). Visible result: a vertical-half ghost copy of the previous frame's geometry appears in the lower half of the display.Root cause
src/Scene.cpp~line 340 (non-interlaced, non-checkerboard clear branch):However the rasteriser's bufferIndex math at
src/Renderer.cpp:~1053walksyover the full[0, screenHeight)range whenFIELD_BUFFERS=0:So the buffer is half-width but FULL-height in this combination. The
/2onrowCountis only correct whenFIELD_BUFFERS=1also halves the buffer height. The defaultConfig.example.hppships both flags on together (intended as a quarter-area mode) so this combination wasn't exercised upstream.Suggested fix
Gate the height halve on
FIELD_BUFFERS:Context
Found while porting Jet to a Raspberry Pi Zero 2 W on Zephyr RTOS at 912x492 RGB565. Happy to file a PR if useful — flagging the bug first since the CLA grant is broad. Tested locally with the above patch and the artifact is fully gone.