libretro / mgba

mGBA Game Boy Advance Emulator
https://mgba.io/
Mozilla Public License 2.0
68 stars 73 forks source link

Add optional interframe blending #175

Closed jdgleaver closed 4 years ago

jdgleaver commented 4 years ago

This PR adds a new Interframe Blending core option. This simulates LCD ghosting by mixing the current and previous frames. This is required for correct rendering of games that 'abuse' the slow response time of the GB/GBC/GBA LCD panel to create transparency and/or antialiasing effects. Notable games affected by this are Wave Race on the GB and the F-Zero/Golden Sun/Boktai games on GBA.

Three settings are provided:

Simple and Smart are best used with games that rely on slow LCD response times for transparency effects (these effects are quite 'aggressive', and dumb 50:50 blending is often the only way to deal with them). LCD Ghosting is more of a 'prettification' effect, for general use.

Note that all these effects are generated in software, and are therefore somewhat slow. This is probably not much of an issue on any platform that can run mGBA full speed (but LCD Ghosting may be a bit heavy for e.g. some Android devices)

This closes #143

endrift commented 4 years ago

Why are you not using C's array subscripting where appropriate?

endrift commented 4 years ago

Also why are you using four buffers instead of just keeping a running buffer of the last blended version IIR-style?

jdgleaver commented 4 years ago

@endrift Apologies for the delayed reply - I had to leave right after making this PR, and have only just gotten online again.

Why are you not using C's array subscripting where appropriate?

I assure you that I'm not engaging in pointer manipulation just to be obtuse! The method I have used here is measurably faster on my dev machine. This may not be the case on all platforms, and with all compilers, but it certainly will never produce worse performance than conventional array indexing.

Moreover, in this particular case (where the width and stride are different), I genuinely believe that the pointer-based approach is both more readable and more conceptually elegant than the alternative.

Also why are you using four buffers instead of just keeping a running buffer of the last blended version IIR-style?

For the LCD Ghosting effect, my goal was to recreate the LCD response simulation from the famous Gameboy Shader. I did actually start with an IIR implementation, but I abandoned it for two reasons: (1) It just didn't look the same, and (2) on a conceptual level, an IIR approach seemed broken since it assumes incorrectly that an LCD panel has infinite persistence. Ultimately, I caved in and copied the shader algorithm verbatim. This produces results with which I am satisfied.

I don't want to create any friction, however, so let me propose that I go ahead and implement a 'fast' version of each blending method (including an IIR-based ghosting effect) - then we can all have the methods we prefer :)

endrift commented 4 years ago

I haven't gone over your PR in much depth so I can't say for all of them, but the majority of the places I noticed you're doing pointer arithmetic it's going to compile down to the exact same code one way or the other. I'm very curious how you were measuring this.

jdgleaver commented 4 years ago

I just used a combination of old-fashioned Callgrind and a measurement of maximum frame rates. I agree completely that a good compiler should reduce everything to identical assembly, but not all compilers are equal. Perhaps I am a fool for worrying about this stuff :)

But again, I don't want to create friction, nor do I have the energy to argue. If you absolutely insist that I remove these optimisations, then I will (albeit with great reluctance - as I said, even ignoring performance, they make the code vastly more readable. It would be a shame if everything had to degenerate into a mess of index counters)