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

Less graphical glitches in SRWMXP #2297

Closed
wants to merge 4 commits into from

Conversation

aquanull
Copy link
Contributor

...by updating the right (or seemingly so) CLUT palette entries. Partially solves #1060.

This may seem weird as it reads bytes from a location different from where they were written to, but this works without breaking other games like SRWZ2 (which my alternative fix breaks some text in it).

… so) CLUTs.

Still some notable blending errors.
ConvertColors(clutBufConverted_, clutBufRaw_, getClutDestFormat(clutFormat), clutTotalBytes_ / sizeof(u16));
void *dstPtr = (u16 *)clutBufConverted_ + clutBase;
// This magically works!
int numPixels = clutTotalBytes_ / 2;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, I'm not sure that "magically works" is a good thing. I think the code is much clearer without hardcoding a 2 here. Why a 2? 2 colors? Magic numbers suck. sizeof(u16) has meaning.

So it seems like what you're doing here is running ConvertColors() offset by clutBase (plus a bunch of probably unnecessary changes), right?

If that's correct, it makes me think that LoadClut() should work differently too. If clutTotalBytes_ does not include clutBase, doesn't that mean it's not reading enough memory in? Otherwise, you're using old palette values or something?

-[Unknown]

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, this game reads from there.
It appears to me that the games update the register with how many bytes they will be using. My alternative fix map the read/write locations but that will break some text ("LEVEL-UP!") in SRWZ2.

I also changed what content to hash.

@unknownbrackets
Copy link
Collaborator

Fair warning: if you break things again and say "Just go ahead and fix it." about your own work, I'm gonna be pissed.

-[Unknown]

@aquanull
Copy link
Contributor Author

Sorry for last time's hassle.

@aquanull
Copy link
Contributor Author

This fixes the texures in SRWMXP:
example

void *dstPtr = (u16 *)clutBufConverted_ + clutBase;
// This magically works!
int numPixels = clutTotalBytes_ / sizeof(u16);
ConvertColors(dstPtr, srcPtr, getClutDestFormat(clutFormat), numPixels);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it work instead to do:

const u32 clutBase = (gstate.clutformat & 0x1f0000) >> 12; 
const u32 totalBytes = clutTotalBytes_ + clutBase;

And then just replace all the clutTotalBytes usage inside this function with totalBytes? I'm not really sure how that could work, but that sounds like what you're doing.

But, it would seem like LoadClut() doesn't load enough bytes in that case. I mean, think of it this way:

LoadClut has a contract that it populates clutBufRaw_[0 .. clutTotalBytes_] (looking at it in bytes.) That means anything > clutTotalBytes_ is out of bounds.

With your changes:

void *srcPtr = (u16 *)clutBufRaw_ + clutBase; 
int numPixels = clutTotalBytes_ / sizeof(u16); 
ConvertColors(dstPtr, srcPtr, getClutDestFormat(clutFormat), numPixels); 

You are reading from (again looking at it as bytes) clutBufRaw_[N .. N + clutTotalBytes_]. If N is greater than 0, you are reading uninitialized RAM, am I wrong? It's outside the bounds of what was read.

I think this means one of:

  • The solution here is that you're no longer running ConvertColors() on a portion of the palette that is being used. This means either it's being double-applied or applied incorrectly.
  • The uninitialized bytes happen to have useful data in them from a previous clut load, but we shouldn't be reading them.
  • ClutLoad() should be reading more bytes, or even a constant number of bytes to ensure these bytes are all initialized.

I don't think the right solution is to read uninitialized memory.

-[Unknown]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the scenes where there were the colour issues, this game first loads whole 0x400 bytes, then loads 0x20 (which is exactly how many it will use from the 0x400 bytes) "unused" bytes before setting the texture. The similar thing happens in SRWZ2 too.

EDIT: I'll try your suggested change.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It actually calls GE_CMD_LOADCLUT after setting the base with a value of 1?

That implies that GE_CMD_LOADCLUT respects the clutformat parameters after all (at least the base.) If yes, then we have to load into and read from the offset in LoadClut(), and we'll need a gstate_c.clutFormatChanged or something rather than just diff. Might even affect flushing...

But it's hard to tell without having the game to debug. I don't think I have any games that actually use that clutBase.

-[Unknown]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With your change it works, too.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Darn, that probably means loading the clut needs to respect the offset. If true, this means LoadClut() will have to care about clutBase, but hopefully flushing won't be an issue... I'm not sure. Changes to clut format should already flush....

-[Unknown]

@unknownbrackets
Copy link
Collaborator

In that case, does something like this work?

https://github.com/unknownbrackets/ppsspp/compare/clut-base

Had to kill the optimization of not rehashing except after initial clut load when format changed, but I think that was not common enough to optimize for anyway. Final Fantasy 2 and Final Fantasy Tactics (which care about things like that) still work.

Changed clutBufRaw_ to u8 to make offsetting clear and simple, probably follows clutTotalBytes_ better.

The above assumes that load and use are all offset by base.

-[Unknown]

@aquanull
Copy link
Contributor Author

@unknownbrackets: That also fixes MXP but breaks the title screen of Z2. I had tried a similar (not exactly the same) attempt which resulted in the same problem.

@unknownbrackets
Copy link
Collaborator

What does the frame dump look like for Z2 and for MXP?

Does it help to load and update the clutBase bytes too (that would be really strange, but I guess it could make sense)?

-[Unknown]

@aquanull
Copy link
Contributor Author

Frame dumps with your code:
Z2: https://docs.google.com/file/d/0B5hoahZqlUL3R2FUbzM1Y2pkQXc/edit?usp=sharing
MXP: https://docs.google.com/file/d/0B5hoahZqlUL3QUhPcXktMjEzQTA/edit?usp=sharing

There was also an old frame dump of MXP in #1060#issuecomment-18894167.

And here's the "alternative fix" I mentioned earlier. It was an experimental hack:
aquanull/ppsspp@placeholder...SRWMXPFixKludge
It didn't break the title screen in Z2, but instead added small glitches on some in-game text.

It roughly did the same thing (only concerning what to load) as yours, except delayed until UpdateCurrentClut().

EDIT: I don't get what you mean "to load and update the clutBase bytes" (or "clutBaseBytes"?). Do you mean to update the corresponding bits in gstate.clutformat?

@unknownbrackets
Copy link
Collaborator

Well, my change above makes it load the slice of the clut from base -> base + size. Another option is loading 0 -> base + size. That's what I'd meant.

MXP seems to:

  • Set clut to ABGR 1555 + no base
  • Load 512 bytes from 09651940 - 09651D40.
  • Set clut to ABGR 1555 + base 5 (theoretically that's 10 bytes in?)
  • Load 32 bytes from 09651940 - 09651960.

So based on the byte ranges, it doesn't sound like it should account for the offset after all, but who knows. It seems strange that the game would do this anyway...

So then, what about this?

https://github.com/unknownbrackets/ppsspp/compare/clut-base

That should be safe, although it may be slow if a game uses base in a way that doesn't just go outside the previously loaded bytes (if those bytes change, we'll cache a new texture each time...)

Or alternatively, could load the base + size....

May need to do tests on a PSP trying different cluts to be sure how this works...

-[Unknown]

@raven02
Copy link
Contributor

raven02 commented Jun 16, 2013

Unfornaturely i don't have MXP to test it but tested the above code towards SRWZ2 and seems to be fine and no broken .(BTW , compile succeed need clutBufRaw_ = new u32[4096 * 4]; // 16KB)

Looking forward the testing result from @aquanull on MXP

@unknownbrackets
Copy link
Collaborator

(BTW , compile succeed need clutBufRaw_ = new u32[4096 * 4]; // 16KB)

Did you change the .h file too? I changed it to a u8 * but I'll probably revert that if this works since it no longer makes the code clearer.

-[Unknown]

@raven02
Copy link
Contributor

raven02 commented Jun 16, 2013

Sorry .Just changed ,h as well and it is fine now ,

@aquanull
Copy link
Contributor Author

@unknownbrackets @raven02: Yes it works and looks safer. It is still weird that the actually used bytes are not what were loaded just now.

@unknownbrackets
Copy link
Collaborator

Yeah, but it's unfortunately still hashing unintialized bytes. It seems like the game is reading that part of the clut. I guess I just have to trust that not many games use this...

-[Unknown]

@aquanull
Copy link
Contributor Author

This still only try to do the minimal jobs, assuming that the games always send GE_CMD_LOADCLUT whenever they specify the actual palette entries to be used.

If that's not true, then neither this nor #2303 is safe because both rely on TextureCache::clutTotalBytes_ that is only updated when executing GE_CMD_LOADCLUT.

EDIT: I wonder if we could remove TextureCache::clutTotalBytes_.

@hrydgard
Copy link
Owner

Sorry I forgot about this one. Is it good to go?

@unknownbrackets
Copy link
Collaborator

I don't think this hashes properly. Wasn't the game fixed with the other change?

-[Unknown]

@hrydgard
Copy link
Owner

hrydgard commented Jul 2, 2013

Can this be fixed or should I close this pullrq?

@hrydgard
Copy link
Owner

hrydgard commented Aug 7, 2013

Ping? @aquanull

@hrydgard hrydgard closed this Aug 24, 2013
@aquanull aquanull deleted the SRWMXPFixMagic branch March 5, 2017 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants