fengb / fundude

Gameboy emulator: Zig -> wasm
https://fengb.github.io/fundude/
MIT License
181 stars 8 forks source link

PPU: line-by-line #14

Closed fengb closed 4 years ago

fengb commented 5 years ago

Current process involves rendering out fake background and window and blitting onto the display, whereas the hardware is supposed to render each pixel separately.

Let's render each pixel when it's supposed to be ready (offset 80-291). Algorithm:

getSpritePixel(x, y) {
  s = getRelevantSprite(x, y)
  return getPixel(s, offset);
}

getBackgroundPixel(x, y) {
  return isWindow(x, y) ? window(x, y) : background(x, y);
}

merge(spritePixel, backgroundPixel) {
  if (isOnTop(spritePixel, backgroundPixel)) {
    return spritePixel;
  } else {
    return backgroundPixel;
  }
}
fengb commented 5 years ago

Gameboy Color uses a 15-bit palette, which means we can probably do something like this:

const Pixel = packed struct {
    value: u15,
    transparent: bool,
};

We might also want to consider 3 different types of pixels: patterns (from ROM), bg/window/spritesheet (15-bit with transparency), screen (without transparency so probably just u16)

fengb commented 4 years ago

New idea — use hblank as line draw

Drawbacks — technically out of spec

Benefits — pretty simple algorithm:

  1. render the background line — cacheable!
  2. render the window — cacheable!
  3. render the sprite line — cacheable!
  4. merge the lines — maybe cacheable?
fengb commented 4 years ago

Line-by-line implemented by https://github.com/fengb/fundude/commit/f14f2f8f33cde0c3c1121c8bc2bcbd683f788b14

There are still line level cache optimizations, and potentially weird bugs if VRAM is being used during mode 2, but this looks much better than before.

fengb commented 4 years ago

As a follow up to this, I actually experienced desyncing between core drawing and screen rendering so I added double buffering as a solution.