Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IndexGenerator: Fix off-by-one in GetRemainingIndices #11402

Merged
merged 3 commits into from
Jan 5, 2023

Conversation

Pokechu22
Copy link
Contributor

@Pokechu22 Pokechu22 commented Jan 3, 2023

Fixes Pocoyo Racing (issue 13136), and also log spam about "Too little remaining index values. Use 32-bit or reset them on flush." on Super Mario Sunshine's grass special stage and some smash custom levels (issue 10312).

The problem with Pocoyo Racing is that it uses 65535 (0xffff) vertices in a single draw command, which caused GetRemainingIndices to overflow, acting as if there was space for more vertices even though there wasn't. Then the game draws 3681 more vertices, and the indices corresponding to those vertices are all messed up because of the overflow, resulting in vertex explosions. The index generator actually can handle 0xffff indices for vertices with one more value reserved for primitive restart, so the fix is just to let that work with GetRemainingIndices.

The logspam is misleading, as we already reset the index generator on flushes at the time that message was added (470c9ff); it's just that in 52feed0 the resetting was moved to after that log message.

There is still a situation where this could come up: if over 0x3fff vertices are used while drawing points or lines, and vertex shader point/line expansion is enabled, we will run out of indices. I don't know of any case where that happens in practice, though.

Pocoyo Racing still has some very weird behavior with fifologs, which is not addressed by this PR.

// subtract 1 from max_index to correctly reserve one index for primitive restart.
//
// Pocoyo Racing uses a draw command with 0xffff vertices, which previously caused issues; see
// https://bugs.dolphin-emu.org/issues/13136 for details.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any fifologs that test the primitive restart behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, both Mario Tennis fifologs; see #10414.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, though I guess I should clarify: the primitive restart referred to here is different from the one used on the GameCube/Wii. Here, primitive restart is used so that we can do a single draw call with a long triangle strip, even when the input is composed of multiple triangle strips (or even individual triangles). On the gamecube/wii, at least for mario tennis, it seems more like something where some vertices contain invalid data and need to be skipped (and there's less of a cost for doing multiple draw calls, I think?). On most platforms, dolphin uses primitive restart for everything, even converting lists of individual triangles into triangle strips composed of 3 vertices and then a primitive restart.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, just wanted to make sure the removal of the -1 was tested. I remember the primitive restart functionality, Stenzek and I had to debug my card...it turned out Vulkan+primitive restart didn't work for it :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that -1 functionality still exists (it's handled in the vertex loaders, not in IndexGenerator). IndexGenerator also still has its -1 (s_primitive_restart).

@PatrickFerry
Copy link
Member

Fixes the issue and the changes make sense to me

return max_index - m_base_index - 1;
if (!(m_base_index <= max_index)) [[unlikely]]
{
ASSERT_MSG(VIDEO, m_base_index <= max_index, "GetRemainingIndices would overflow: {} <= {}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be one of those game quirks (I think it is a stat?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this would be a useful game quirk; this one theoretically should never happen (it means we've already written too many indices). The ones in VertexManagerBase could possibly be a game quirk but I don't know how useful it would be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok. I was kinda going off of the comment in the OP:

There is still a situation where this could come up: if over 0x3fff vertices are used while drawing points or lines, and vertex shader point/line expansion is enabled, we will run out of indices. I don't know of any case where that happens in practice, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On further thought, I'm going to convert the ones in VertexManagerBase into assertions instead of just logs. I think a game quirk is overkill, but it's a situation that should be obvious to the user.

Fixes incorrect logspam when the buffer needed to be reset on flushes (which we already were doing, but 52feed0 moved it to after the check was made). This is https://bugs.dolphin-emu.org/issues/10312.

I also converted it to an assert, as if this does happen, things are going to render incorrectly, so we want to make it obvious.
Copy link
Member

@lioncash lioncash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good minus one nit


return max_index - m_base_index - 1;
if (!(m_base_index <= max_index)) [[unlikely]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this can be inverted a little so one doesn't need to mentally do it themselves when reading

Suggested change
if (!(m_base_index <= max_index)) [[unlikely]]
if (m_base_index > max_index) [[unlikely]]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was deliberate to match the condition in the assert, but it looks like compilers (at least MSVC) don't merge the two together, so there isn't a significant difference.

I prefer the <= version because it makes it more obvious that it would overflow, though. I could put a separate variable along the lines of const bool subtraction_would_overflow = (m_base_index <= max_index) and use it in both places, maybe?

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants