DIRTY SPANS TECHNIQUE Please note: this is just an idea. I'll explain it in pseudo-code: /* in antic.c: */ int dirty_left[ATARI_HEIGHT]; int dirty_right[ATARI_HEIGHT]; static void draw_antic_*() { int x; for (x = left_margin; x < right_margin; x++) { UBYTE new_pixel = generate_pixel(x); if (new_pixel != scrn_ptr[x]) { dirty_left[ypos - 8] = x; x = right_margin; do { x--; new_pixel = generate_pixel(x); } while (new_pixel == scrn_ptr[x]); dirty_right[ypos - 8] = x + 1; for (x = dirty_left[ypos - 8]; x < dirty_right[ypos - 8]; x++) { scrn_ptr[x] = generate_pixel(x); } return; } } /* no pixels changed in this line */ dirty_left[ypos - 8] = dirty_right[ypos - 8] = 0; } /* Note: (ypos - 8) is because ypos ranges from 8 to 247 for the visible scanlines */ /* port-specific Atari_DisplayScreen(): */ void Atari_DisplayScreen(UBYTE *screen) { int y; for (y = 0; y < ATARI_HEIGHT; y++) { /* copy pixels dirty_left[y] <= x < dirty_right[y] from screen[] to the real screen */ blit_pixels(screen, dirty_left[y], dirty_right[y], y); } } Now some less formal explanation: The idea is to update only parts of scanlines that changed since last frame. We update one continuous sequence of pixels ("span") per scanline. dirty_left[y] and dirty_right[y] are the boundaries of the span in scanline y. In each scanline, we search for a first pixel that is different from the previous frame. If we don't find one, then the whole scanline is not dirty, which we mark with: dirty_left[y] = dirty_right[y] = 0; If we find left-most pixel that differs from the previous frame, we save its x coordinate in dirty_left[y]. Then we search starting from the right side for a pixel that changed since last frame. We know we'll find one. We save (x + 1) in dirty_right[y]. Now we normally generate pixels between dirty_left[y] and dirty_right[y]. The display routine doesn't need any additional comments, I think. Compared to the current approach (always display whole Atari screen), the DIRTY SPANS technique adds almost zero overhead in the worst case. In the best case we avoid screen updates completely. On average, we update just the areas that changed. There's one case when DIRTY SPANS perform worse than DIRTY RECT: if left and right sides of screen are changing while the center of screen remains static. This is however extremely rare (actually, I cannot recall any real Atari program that does such things). Compared to DIRTY RECT, DIRTY SPANS has nearly zero overhead in ANTIC code: while searching for boundaries there are comparisons but no writes to the screen; generating the changed part of the scanlines operates as fast as without DIRTY techniques. Displaying pixels is much faster: we immediately know which pixels to update, without stepping through 8K screen_dirty[]. It is also better for hardware architectures where continuous writes to the video memory are faster then random writes. It is possible to combine DIRTY SPANS with DIRTY RECT: in the first pass, find span boundaries using DIRTY SPANS. Then, in the display loop, update only the pixels that changed (compare current pixels to a backup copy of the screen). DIRTY SPANS should be straightforward to implement on most platforms: all we need is displaying 1-pixel-high bitmaps. The ANTIC code is what gets more complicated with DIRTY SPANS. Basically, we have to split each function that generates an Atari graphics mode into 3 parts: 1. Find dirty_left (similar to normal code, but compare instead of write pixels). 2. Find dirty_right (likewise, but *right-to-left*; it should be possible to implement it without a speed penalty). 3. Write pixels (same as the current code). This probably means more and more macros (antic.c is already full of them). As noted at the beginning of this document, these are all plans for the future. Piotr Fusik 2005-09-09