Spritework on the ZX Spectrum: Rendering Without Flickering

ibobev1 pts0 comments

Spritework on the ZX Spectrum: Rendering Without Flickering | Bumbershoot Software

Last week’s work got us a functional sprite renderer, but we quickly learned that we don’t have enough time to do all our rendering before the next screen is displayed. he targets would flicker or have pieces cut out of them:

This is a very interesting glitched frame: only the lower left corner of the target is visible. This doesn’t really match our description of the renderer last week—it drew to-to-bottom, left-to-right an 8-pixel column at a time. My best attempt at a reconstruction is this:

VBLANK starts and so does our frame processsing.

All targets are erased.

The first target is fully redrawn, and we get some distance into drawing the final column of the second target.

The raster starts actually drawing the targets. Our renderer is faster than the raster, so the second target is finished before it can display as glitched, but the third target hasn’t started at all yet so its top is completely blank.

At some point the renderer catches up to the raster partway down the first column of the last target. This is enough to completely draw that column before the raster gets there.

The renderer gets to work on the final column but it cannot catch up to the raster before the targets are completely passed, so the final column appears on the screen as completely blank.

Solving the problem here, happily, is much easier than reconstruction how the glitch happened in the first place; we just need to not erase everything all at once and structure our rendering so that we still have a mostly OK display when we’re interrupted. That will ultimately serve us very well:

The targets are our main problem here. The blaster truck is low enough on the screen that it we’ll always have time to deal with it normally, and the shots aren’t sprites at all—we’ll be updating them a few pixels at a time with bitmap operations.

Designing a New Render Order

Dealing with the targets is easier than it sounds. I took three shortcuts when designing my sprite system and all of them end up serendipitously paying off here.

We don’t actually composit the sprites into place: we just overwrite the 48 relevant bytes of screen memory. That means that any time the targets are redrawn shifted within the same 24×16 region, we don’t have to erase them at all because they’ll erase their old shape while laying themselves down.

We keep the rendered region 24×16 even when the rendered region fits in 16×16. A horizontally-aligned sprite draw just copies empty space into the third column. Our targets move left, and this means that they will generally be cleaning up after themselves as they go with no intervention from us.

I removed the explicit bounds-checking on the X dimension in my renderer, figuring that the partial-offscreen logic would cover what we need. It did, but that also changed the allowable bounds; valid X values now ran from -8 through 127 instead of -7. This in turn meant that the targets will clean up after themselves even as they scroll off the screen.

The only time we actually have to erase anything at all related to the targets is when we move them vertically! In all other cases they clean up after themselves in the process of being drawn, and if they’re interrupted we’ll just get minor tearing instead of flicker.

The score and the blaster are also non-issues; the blaster is very far down the screen so as long as we handle the blaster reasonably early each frame we can be confident we’ll never overrun, and the score is just printing out text and isn’t time-critical at all. At worst some frames will have partially-updated text.

That leaves the shots. Our sprite engine isn’t really up to displaying these; they need to be properly composited onto the screen, recreated if a passing target wipes them out, and ideally capable of managing collision detection on their own. They also sort of break our usual rule of everything being based on even-numbered pixels; the blaster is perfectly mirrored when it changes its facing, so its aperture is on an even or odd pixel depending on its facing. Overall, instead of shoehorning this into my sprite system I’m just going to draw vertical lines on the screen as needed. This lets me be a bit more casual about the timing as well because usually shots will get to mostly persist so I don’t have hard deadlines on drawing anything.

Putting it all together, I think this rendering order will end up working for me:

If the targets moved vertically, erase the two lines of graphics that would be left behind.

Erase out the bottom two pixels of every shot. At this point, everything that we might be "caught" erasing has already been erased, and nothing we draw later can have bits of it erased as collateral damage.

Draw the targets in their new positions.

Erase the blaster from its old location and facing and redraw it in its new location and facing.

Check the pixels that we’re about to extend our active shots to;...

targets screen time target column erase

Related Articles