OK, so thinking a bit more about the I2C framerate issues, I just noticed that there's a simple fix.
So, there are basically only 2 ways to draw things in Chip-8:
- Clear the screen
- Draw a sprite (8x15 max) with a XOR
So, for performance reasons, most games avoid clearing the screen - as that would force you to redraw all the sprites. If you need to move a sprite, you XOR it at the current position and draw it again at the new position.
I could use the draw instructions to skip the frame buffer and draw directly to the screen, but with the time that each draw operation would take, this would cause some annoying artifacts (you would see the sprite disappearing and appearing instead of moving).
BUT this also has the side effect that the screen buffer generally doesn't change a lot between frames. So if I keep track of the previous frame buffer, I can compute the area of the screen that changed and render just that.
This has some obvious edge cases (if only the screen corners are updated, I need to redraw everything). But this seems to be mostly a non-issue in practice.