Oh, I got it -- it is an engine issue, coupled with the definition of a "frame" for emulators. A frame in Gens and BizHawk last, in general, from V-Int to V-Int (or when V-Int would have happened, in the cases where it is disabled).
Here is what I think is (at least part of) the problem: most of a frame happens during active display; the game updates its logic, decides where sprites are, where the camera is, and calls BuildSprites (or equivalent). Frame finishes rendering for display*, V-Int happens, a frame ends. During V-Int, the data buffered by BuildSprites is transferred to the VDP, as is horizontal scroll based on camera position. This stuff will be rendered during active display, while the game logic will update positions and so forth; at the end of this frame, the image generated the first frame I talked about will be shown. Reading data at this point will give the "wrong" values for sprite positions and camera location relative to the graphics because the graphics are from 2 frames ago -- you are using the new positions and camera location to render overlays on graphics two frames old. Sometimes, they will line up -- but that is by chance, or when you are moving at constant speed.
Now, the S3&K engine is a lot more optimized than S1 and S2, particularly when it comes to level drawing -- S3&K renders the level during active display to a RAM buffer and transfers the whole thing during vblank. S1 and S2 set flags during active display that say what needs to render, and it renders it all during vblank. And what is more, S1's 68k sound driver also probably gets in the way of it all, making it even worse.
I have no theory at the moment about the differences between Gens and BizHawk; could be due to accuracy differences or due to how they render the overlays.
Here is what I think is (at least part of) the problem: most of a frame happens during active display; the game updates its logic, decides where sprites are, where the camera is, and calls BuildSprites (or equivalent). Frame finishes rendering for display*, V-Int happens, a frame ends. During V-Int, the data buffered by BuildSprites is transferred to the VDP, as is horizontal scroll based on camera position. This stuff will be rendered during active display, while the game logic will update positions and so forth; at the end of this frame, the image generated the first frame I talked about will be shown. Reading data at this point will give the "wrong" values for sprite positions and camera location relative to the graphics because the graphics are from 2 frames ago -- you are using the new positions and camera location to render overlays on graphics two frames old. Sometimes, they will line up -- but that is by chance, or when you are moving at constant speed.
Now, the S3&K engine is a lot more optimized than S1 and S2, particularly when it comes to level drawing -- S3&K renders the level during active display to a RAM buffer and transfers the whole thing during vblank. S1 and S2 set flags during active display that say what needs to render, and it renders it all during vblank. And what is more, S1's 68k sound driver also probably gets in the way of it all, making it even worse.
I have no theory at the moment about the differences between Gens and BizHawk; could be due to accuracy differences or due to how they render the overlays.


31