Rendering Architecture
Motivation
The rendering system on the Amiga was designed with the restrictions of the Amiga hardware in mind. There is a limit on how many blits can be performed per frame and it is not very high. Since a game needs to also update its game world, we took measurements of how much effect dirty rectangle restoration and blit queue would take.
On a barebones A500 system, there is surprisingy little time available for blitting if the game also needs to update other data structures and game logic, so it was decided to forego explicit queuing of blits and just directly blit objects as they are updated.
Central Concepts
On the display level, we only deal with Amiga display primitives in a very direct manner, meaning
Copper lists
Playfields
Sprites
Blitter operations
Copper lists
Copper lists are the central element of RATR0’s display design. Each stage can define its own copper list which defines how the playfields, sprites and color palettes are arranged. Because of this, the programmer will have a great amount of control over the visual aspects of the game.
Playfields
RATR0 fully supports single and dual playfield modes. RATR0 expands the base dual playfield by allowing the developer to define more than 2 playfields for each stage and use the copper list to assign them to different positions of the screen. With this, we can partition the screen into vertical portions and implement advanced split screen and parallax effects.
Double buffering
The render component will strictly use double buffering. This is to ensure smooth transitions at the cost of moderate additional space consumption
Render list
On each frame we need to determine
the dirty background rectangles
the changed objects to redraw
the collided objects
We need therefore to have data structures for the following:
background tiles
BOBs
sprites
collision boxes
Unfortunately, trees seem to be significantly slower on Amiga than hashing, so we use hash sets for BOB sets and a hash grids to process collisions
Background restoration algorithm (dirty rectangles)
Idea
As the screen buffers get modified by game objects, obstructed portions need to be restored. The restoration method is taking care of that.
BOB changes
A change for a BOB happened when
the object moved
the object frame changed
We need to
calculate the restore tiles on the old object
add these tiles to the display buffer’s dirty list
add the BOB to the draw list of the both buffers
Dirty rectangle sets
We want to make sure we capture dirty regions, so they can be blitted all in one go. Also, since multiple objects can obstruct the same space, we want to ensure those rectangles only get restored once, so some kind of set data structure is needed.
We are splitting the play area into 16x16 pixel tiles, which is a good tradeoff between size, efficiency and flexibility.
After initial prototyping it became clear that tree or list based data structures are a bit costly in terms of space and CPU time, O(log(n)) insertion turned out costlier than expected for something that has to be run repeatedly for every frame. So the next thing that came to mind was a hash table. It turns out that due to the regular matrix structure map, we can efficiently map dirty tiles into a bit set, implemented as arrays of 32 bit integers. Insertion is O(1) and we can relatively efficiently query the set, since each 32 bit number that is a zero means that we can skip it.
The algorithm
We obviously need to determine every object that has some kind of change before we render them.
We then add the dirty rectangles to the set of every affected buffer, which means both the back buffer and possibly the front buffer, since that would be next.
In a first approach, we just add the dirty to both buffers
Graphics effects
Palette interpolation
In a palette based system like the Amiga, palette manipulation is an effective tool to achieve effects that don’t require a lot of computational resources. We can easily implement fade-in/fade-out effects by interpolating a palette into another.