Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Fixes the unknown opcode with Prince of Persia: Rival Swords (https://bugs.dolphin-emu.org/issues/9203).
This bug occurs because the game has some sort of caching layer to reduce the number of calls to GX functions. For instance, calls to their function to configure vertex descriptor (at
8010d558) compare with a cached value before callingGXSetVtxDesc(at80499164). I'm not sure whether this actually has any tangible benefit, as those functions don't directly send graphics commands but instead update other internal state, but still, it's something they do.They have a function that draws a black rectangle over the entire screen (at
80111b40via800ae644), which among other things configures the vertex descriptor to have a position and a color, and then draws the rectangle:However, when using Bink Video, Bink's drawing function (at
801e088c) does not use their caching system and instead directly callsGXSetVtxDesc. Thus, their cache doesn't match reality, so when it comes time to draw the black rectangle, the cache function thinks that the vertex descriptor is already configured correctly and thus never callsGXSetVtxDesc. The vertex descriptor that ends up being used has a position only, without any color data; the same data is thus interpreted this way instead:Things work out on later frames as the cache state and the actual GX state ends up in sync (though I'm not entirely sure what ends up making them consistent).
Using the hardware fifoplayer, I was able to confirm that the 3f was being read but ignored, by replacing it with 0x18 and observing that that produced a hang.
It does seem like the developers were aware that this kind of issue could happen, because the Bink drawing code is modified (the Bink changelog mentions, more or less, that the relevant code is in
wiitextures.cwhich is distributed in source form to developers, and I've seen other games that modify it too). But rather than changing the Bink code to use their caching functions, they instead did a weird halfhearted workaround:But, this doesn't work, as
GXClearVtxDescclears the vertex descriptor, soGXGetVtxDescjust returns the default values, and thus the ending state is different from the starting state.I attempted to make a game patch that moves the
GXClearVtxDesccall after theGXGetVtxDesccalls. This patch does solve the unknown opcode issue (at least in the situation I tested), but there are a lot of other issues because other things are also out of sync (later versions of Bink reset the state afterwards, but this version doesn't completely do it, resulting in a red flash on the frame where the unknown opcode would previously appear; here's a fifolog of the patched version). This patch isn't included in the PR.For comparison purposes, I also looked at other games: Raving Rabbids (at
803783c8), the Activision Demo Pack (80104d80for DemoLauncher.elf), and Shrek the Third (8021e1e8); none of them have this kind of call toGXSetVtxDesc, though the Activision Demo Pack does have the Bink code changed to use their own state-tracking cache code.I think the home-menu code also doesn't use their wrapper, so even changing Bink code for Rival Swords wouldn't fix the issue entirely, though I don't know for sure what would be broken.
I also did some refactoring to allow changing GATHER_PIPE_SIZE, in an attempt at making unknown opcode errors easier to debug; it's not perfect, but it helps. Those changes are only refactoring, and should not produce any different behaviors.