-
Notifications
You must be signed in to change notification settings - Fork 838
Texture Replacement 2.0 #3244
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
Texture Replacement 2.0 #3244
Conversation
6a5345c
to
92d308d
Compare
Hi, just a heads up, Lara meshes that appear to be using OG textures are actually solid color meshes that cannot be retextured without additional game specific programming. |
Hi there guys. So i will be doing a lot testing here, so please, correct me with the format youd like me to use here if this is not it.. GAME: Tombraider III NTSC Dump status : Loaded in to the house, and made a B line to the front yard and stopped about 10 steps out the front door. Dumping with this config file,(I did tinker with all the "true/False" settings, and the one tht does the ram writes, also dumps some wierd alpha strip texturs, about 15 in this area) config - TombRaider III seems to have minimal garbadge imgs... ( I am guessing the largest files once you GROUP the textures by DIMENSIONS, are not really used? )I llooked at them and they just seem to trash to be anything in the game... but again, I am coming from Dreamcast/ps2 dumping, so maybe? Then there is the crazy amnt of different resolution imgs. Im used to having a few major resolutions, and then a couple misc size textures.... I am guess thou, that texturees ar cut from larger "texture pages" (even TR. LR for dreamcast used these pages, and i assuemed as the only other game I found this in so far to this exten, is Res evil 2... and i think that is because they WERE ps1 games... but were ported to the Dreamcast... but they look very different, in the sense they are much higheer resolution as shown here, but of course a 5th gen system wouldnt have as high res imgs as a 6th gen system... REPLACEMENTS: NOW THIS is why I am posting this game. When I upscaled the imgs, and put them right in the replacement folder, now every time it gets to a point where its loading in a texture from the replacements, I seem to get this error, |
Very interesting and promising. For nexus382:
Currently it is WIP. There is not the transparency yet, for example. |
Did Battle Tanx, and I have the same transparency issues as above.... but I honestly think that is most of the issues.... it seems theres soe trsh, but it was all Vram Dumps, so a simple Group files by Dimensions, and then ctrl shift click to slect them all but other then that were looking good here |
I tried a bit with Harvest Moon and was able to dump textures with the "Dump Texture Pages" setting. What surprised me was that it dumps the texture sprites of the baby or of the dog, but not of the girl. You would think that it's the same type of texture so it would dump either both or nothing. Also I was wondering if these black textures in the screenshot might be the missing sprites and are just covered by something? Oh and I was wondering if it is intended that some textures are dumped live and others only after closing the virtual machine. Thanks. |
14f8870
to
b4c9298
Compare
41eb9e3
to
bd6542c
Compare
Transparency should work now. You can identify these textures as they'll have a "STP4" or "STP8" texture mode. You'll need to be careful with the alpha in the replacement, basically an alpha of less than 255 means it's going to blended/semi-transparent. PSX uses alpha=1 as a "blend this texel" flag, and 0 means it's opaque, which is inverted compared to modern systems. When dumping, it inverts the alpha in the STPx mode, and inverts it back again when uploading the replacements.
I'll have to look into that one. The upload and CLUT hash look to be the same, so it should be merging those draws/records.
I'd have to trace what it's doing. It may be partially overwriting the data and need splits.
This is intentional. It defers dumping for as long as possible, until the upload gets invalidated. That way the size is maximized, since there's a greater chance of all texels being drawn at some point over time, versus the first time it is used. |
bd6542c
to
61f7896
Compare
3b1521a
to
fae6b7a
Compare
Ok, Just downloaded the new version, for the first time sense inital, and holy shit. The Tombraider three dump is SO much cleaner, Trying my first upscale test, see if things are not dumping now that i have way lesst imgs ( i m sure all that are missing were dups and trash, Im going to test with Tomb raider 3, Midevil, and Battle Tanx, to compare to the original times, and ill post updates later today :) stenzek, I want you to knowwee all appreciate you and yyou have no idea the amnt of joy your emulator brig all of us! We can do things on duck statiomn that we cant do ANY where else, adn all the QoL improvemnts, make the games less "acid trippy" with the straight lines, and non warping/wiggling settings... then thre is this Txt Rep 2.0..... I am so glad we met, adn im sure i just annoy thee fuck out of you, but you are deff a gifted human, and thru all the negitive, there are so many thousandsof people that wouldnt have what they do with out you:) |
ok so tombraider seems to be dumping 100% better now with the updates... BUT, I can not get ANY of the Twisted Metal 4 games to dump ANYthing, or even make a dump folder... going to tinker now |
Did you try the different texture dumping options one by one? And always played atleast for a few minutes? In Harvest Moon it took me a few attempts, because it needs the "dump textures pages" option and only creates the folder if you are past the menus. If you only went to the start screen the folder might not create before closing the virtual machine. This can be a bit confusing in the beginning. |
The dump pages option really is a last resort, due to the amount of duplication, and chances that the whole page won't match exactly (=> replacement does not apply). I do have a few further ideas that may improve compatibility, just lacking in time at the moment to implement them. |
Transparency works. We need to nullify RGB pixels if alpha is 0 otherwise it will appear black instead of transparent. I tried some games and here are some issues I have got: Some textures have either one or two pixels columns (on the right) or bottom rows. Resident Evil 2: |
I did to a pretty extensive degree, Did i hit every combonation with the settings? probs not, but i can try this further, BUT I think i found the issue.... It seems this game forces SW rendering....? I am not sure why but not even with a game specifc (in flycast, you can override the locked values for these type of situationsby making a gamee specific config,) was I able to get out of SW mode, and i have not furtherd tested SW, but that would be my guess as to why it didnt dump anything, i was ablle to get about 40 trash textures total after 30 minutes of tinkering with it so, I feel i was pretty through.
I didnt know it was that "useless" (i get some textures are better then none) and that it made that many dups, I will remember this in testing, thank you... and also as i noted above, why was I forced in to SW mode foor Twisted Metal 4? Would that have been why It was not dumping? Does SW utilise the shader cache?? |
Writing the size or the region into the filename sounds like such an interesting idea. I'm wondering if this concept could help to handle problematic textures in pcsx2 aswell. The problem with the changing hashes due to vram junk, you know. If you could just cut off those regions, the hashes wouldn't change anymore? |
So I found there are textures tht dont dump in Rouge trip. But, these are the "painting" like backgrounds... happns in ps2 games as well, like oyminushia 1 on the ps2... where the backgrounds are alomst like "paintings" and theres like a floor and walls with some type of design, but its all 2d flat.. Pcsx2 can not handle them, and hey dot dump, so i would assue that duck can not as well? Honestly, other then this, I have dumped parts of 12 games now, only issues here, and in 2 that run in SW mode |
Adjusting the size in the filename only affects what is replaced, unfortunately the area that is hashed remains the same. Because there is, for all intents and purposes, infinite ways you can cut up/subdivide a texture upload, and testing them all is infeasible. |
Ahh that's a shame. Still there are some cool approaches in this PR which could make pcsx2 better too like the idea with the config file with aliases
|
So for replacement textures, would it be necessary to change the size in the file name of the replacement texture? For example I have a menu screen that was dumped at 64x256. The current file name is "texupload-P4-546BDEF7D77604E0-CE18CDD2CDBF67B0-64x256-0-0-256x256-P0-1.png" Would changing the file name to the upscale size have any effect? |
I think the size in the file name always refer to the texture dump, not to the texture replacement. So if you just upscale your dump, you won't have to change anything in the file name. |
Backgrounds in RE games are decompressed with the MDEC, and written directly to VRAM. They're never drawn as textures. That's why the "VRAM write replacement" feature exists. |
8513c5c
to
8b0d94a
Compare
do you have to do something special.... because it seems i was only able to get thoes to dump using the old version of duckstation i had. ill double check, but im 95% sure that this version of ducky cant do it, ubt my "main stream" duckstation can. Do i have to turn off settings in the new way of dumping to get the old way to work? it is safe to say that this system will still be around and useable when tex rep 2.0 comes to main distro? will you incorperate it in to the new options page, so its all on the same place? and Will I be able to mix the 2 systems? Im getting the masks in 2.0 and char models for 3,.....2 seems not to be doing masks, but the old way gives me just the bgs... so to do the game.. or as much as i can, it would seem i need to utilize both methods at once. another reason i was asking if it will all kinda become "one" system |
VRAM write replacements are still an option. |
2ad47a3
to
9d51fec
Compare
I've re-done the texture page dumping option to actually be semi-useful now. It'll only dump to the draw/UV rect, and can replace any sub-rectangle. The lookup for this is very slow, since it needs to iterate through and test/hash all possible replacements.. but it's only on hash cache lookup, so hopefully it won't hurt too much. Net result is replacements in games like Crash Bandicoot work decently. Depending on different levels, the textures get uploaded in different sub-rectangles of the page, and will dupe. At least for Crash, the dupes are exactly the same size, so you can just define them all as aliases of the same texture in the config file. |
I'll probably merge this next week, since my free time is getting pretty limited at the moment, and getting "all known" issues fixed is unlikely to happen. Better to have something that works well in a decent number of games, than nothing at all. |
9d51fec
to
f950116
Compare
f950116
to
a291e5f
Compare
e539921
to
bd82899
Compare
a291e5f
to
1e93201
Compare
919a718
to
b078aed
Compare
d5ebebb
to
96f2059
Compare
96f2059
to
e06f1f1
Compare
After my last attempt about two years ago, I decided to have another crack at it a couple of months back, with a completely different approach. Please for the love of
$DEITY
do not shitpost here, or ask for texture packs for game X, I'll be deleting those as it's both off-topic, and a legal minefield.Why is this so hard?
The main challenge in doing texture replacement on the PSX is the lack of texture sizes. Each polygon specifies a 256x256 "texture page", and (usually) a palette. As one can imagine, not many textures in PSX games are 256x256. This means that within each 256x256 texture page, there are many smaller "sub-textures", which are the "actual" textures used by the game. An example of what this texture page looks like is shown below:
A texture page with multiple sub-textures.
Some of the sub-textures look fine, others look like the incorrect colours have been shown. That's because these other sub-textures use different palettes (aka CLUTs). So, we need a way to discover:
Then you have to consider that palette swapping is a thing. For example, different coloured flags for different teams/players, lighting effects, etc, all use the same texture data (aka indices), but different palettes. This was the main limitation of my previous dumping/replacement system, it assumed that each texture only had one unique palette. I wasn't going to make that mistake again.
There is another possible source for the texture sizes: the upload commands. The PSX GPU has no access to main RAM, therefore for a texture to be available, it has to be uploaded, usually by DMA, from main memory to VRAM. VRAM is addressed in a 16-bit 1024x512 coordinate space, so an upload could be something like
upload 32x32 pixels to location 512,256
. Therefore, can we use the upload details as a texture size?A view of texture data, reinterpreted to 16-bit VRAM.
No, of course not, above is what a typical transfer looks like. Games upload multiple textures in one VRAM transfer for efficiency, atlasing, and/or because that's just how they were designed. Womp womp. What do we have left? We need to track what the game draws.
Texture dumping - how does it work?
While my previous PR attempted to map each texel to a unique palette based on draw information, this new approach tracks rectangles/bounding boxes of palettes for each upload. The net result? Enabling dumping doesn't bring the frame rate down to single digits, and it supports palette swapped textures. Let's dissect what's going on here:
Each time the game uploads a texture, we track the bounding box of the upload in global VRAM space (1024x512), and associate it with a list of pages that it overlaps with. This will be very important later, but keep it in the back of your mind for now.
When the game draws a polygon, we look up the texture cache for a matching texture page and palette combination. If it's not in the cache, we create a new source for this combination, decode the texture (apply the palette), and upload it to the GPU. Before the polygon is sent to the GPU, we record the bounding box of texture coordinates (UVs) for the polygon, and associate it with the texture source. As this source is progressively used by more polygons, we take the union of the incoming UVs, and the existing bounding box, eventually ending up with the rectangle that the sub-texture occupies.
Eventually, the source will get invalidated, either by the virtual machine shutting down, or when the game uploads another texture or palette over the top of it. When this happens, we copy the list of palette rectangles back to the original upload metadata (remember the thing I told you to keep in the back of your mind?). This is basically our list of sub-textures, and we know which palette was used with them!
Perhaps at the same time, or some time in the future, this upload will eventually be invalidated too, by something overwriting it. We can simply go through all the sub-texture rectangles, and dump those out to files. We don't do this immediately on source invalidation, because it's possible that future draws may expand the sub-texture rectangle out further, and we don't want to cut the texture too small. See the figure below for what this looks like.
Extraction of a sub-texture.
Throwing a spanner in the works
Of course, it's not that easy. From the handful of games I've evaluated so far, there's been several curveballs:
Not using the full palette range
While we're on the topic of Metal Gear Solid, the "Reduce Palette Range" option was added for this game. On the PSX, you have the choice between 4-bit (16 colours), or 8-bit (256 colours) textures (ignore direct, it's hardly used). This means that you need to use a 1x256 (0.5KB) area of VRAM for each palette, as well as your texture data. Let's say you only need 32 colours, you can't use a 4-bit texture, but if you use 8-bit, then you're wasting ~0.4KB of VRAM for this palette. This might not seem like much, but over the course of tens of textures, it starts to add up, as the PSX only has 1MB of VRAM.
Mmm... CLUTs for breakfast.
Palettes are addressed in VRAM in units of 16x1 pixels, which means for our 32 colour example above, we can simply place two 32x1 palettes immediately next to one another, and save 0.8KB of VRAM. Nice! Except for our texture replacement system, what happens if those two textures are loaded in a different order? Let's say the left and right palettes are swapped around. If we use the whole 256x1 region as our unique identifier, the loading order will cause these IDs to be different, and duplicate both the dumps, and make replacement unreliable.
To work around this, instead of always using the 256x1 region as the ID, or more specifically, the hash of it, we scan through the texture indices (the "texture data") to identify the minimum/maximum index, which effectively determines the "real" size of the palette. Later when we hash it for dumping, we only hash the "used" part, therefore whatever is adjacent in VRAM is irrelevant, we still end up with the same hash.
VRAM write splitting
Our next contestant is Gran Turismo. Most of the textures in the game replaced without issue, however, the HUD was still being problematic. After loading the track, the original VRAM write had already been invalidated, so when the texture was actually drawn, there was no write to attach the palette region to, therefore we cannot dump or replace it. See the figure below for what this texture looks like in VRAM.
GT font texture.
The area in the bottom-left was not part of the original upload; the area in that upload was just all-zeros/black. The bottom-left area was later uploaded while the track was loading, and since we invalidate on any overlap between the incoming rectangle and original upload, that's what was causing the font (upper) area to be lost. Therefore, we need to split these VRAM writes when they're partially invalidated. We do this by cutting the texture up into 4 parts: an area above, to the left, to the right, and to the bottom of the incoming/invalidating area. For the case above, we only need to split it twice, once above, and to the right.
Split GT font texture.
These writes continue to be tracked separately, and can split recursively, depending on user configuration. Once the splits are eventually invalidated, they're merged back into the original write, and any textures from the area are dumped. Works well enough for the GT fonts :)
Texture animation
It's too late and I can't be bothered writing this up properly, but basically:
Texture replacement
With dumping out of the way, now we have to actually figure out how to replace these textures. This really is the easy part, as some of you who I've discussed the topic with would well know. Since I built this whole system on top of the texture cache, which already handles lookup/decoding/invalidation of 256x256 texture pages, we can just inject the replacements into here.
It is made slightly more complex due to the fact that we're dealing with sub-textures, not whole 256x256 pages, and the upscale factor of these replacement sub-textures might be different. Therefore, we just take the maximum upscale of all sub-textures, create a render target, stretch the original 256x256 data over it, and then draw the replacements over the top. An example can be seen in the figure below, where we have a mix of original and upscaled sub-textures.
Would you guess this is Tomb Raider?
Actually using that in the draw pipeline gives us this hall of horror:
I wouldn't want to be trapped here.
Memes aside, it's actually an easy way to determine where the replacement system is working and failing. Dump textures, overwrite all dumps with the chocobo, and see where the original textures leak through. As one can see, there's still a couple of gaps here, probably due to the textures not meeting my size threshold.
What's left?
I didn't want to PR this approach until I had a few games working fairly well, and the system mostly implemented. It is mostly implemented, the replacements/dumping will even survive save/load state. There's still some things left to do, like fixing up the copy-as-writes mode for FF8, fixing any regressions in the base renderer, and games broken in the texture cache in general.
Note that the texture replacement system is not going to be compatible with all games. In fact, I wouldn't be surprised if the number of games that are compatible is fairly small to begin. It's the sort of thing that we just have to work through the edge cases one by one, and hopefully come up with general solutions that are applicable to multiple games.
Regardless, I feel the system's in a state where we can begin experimenting with it, and identify where the shortcomings are. Hence the pull request, feel free to mess with the build.
How to use it?
textures
->SERIAL
->dumps
).Texture dump/replacement filename format
Replacement filenames must follow the following naming convention:
AAAAAAAAAAAAAAAA
represents the hash of the texture data (the "indices").BBBBBBBBBBBBBBBB
represents the hash of the palette.64x256
is the size of the original VRAM write/upload.0-192
is the offset into the original VRAM write/upload of this sub-texture.64x64
is the size of the sub-texture, in expanded form. Expanded in this case means that a P4 texture will be 256x256 in P4 format, but 64x64 in C16 format (4 4-bit texels in each 16-bit pixel).P0-14
is the range of the palette that should be hashed.Direct textures won't have any of the palette fields. Replacements can be in
png
,jpg
, orwebp
formats.This means that if the texture dumper finds a sub-texture rectangle that is too large, you can resize the texture and remove portions that are garbage by both editing the image and the filename. I plan to make a graphical slicing tool at some point, but it's not a high priority.
Options
Due to the edge cases mentioned above, and the fact that some of the dumping methods have negative impact for other games, you may find you need to alter the system configuration for some games. A
config.yaml
file is created when texture dumping is first activated for a game in itstextures
directory, with the following options:DumpTexturePages
Enables texture page dumping mode. Instead of tracking VRAM writes and attempting to identify the "real" size of textures, entire 256x256 pages will be dumped/replaced instead. In most games, this will lead to significant duplication in dumps, and reduce replacement reliability. However, some games are incompatible with write tracking, and must use page mode.
DumpC16Textures
Enables the dumping of direct textures (i.e. C16 format). Most games do not use direct textures, and when they do, it is usually for post-processing or FMVs. Ignoring C16 textures typically reduces garbage/false positive texture dumps, however, some games may require it.
ReducePaletteRange
Reduces the size of palettes (i.e. CLUTs) to only those indices that are used. This can help reduce duplication and improve replacement reliability in games that use 8-bit textures, but do not reserve or use the full 1x256 region in video memory for storage of the palette. When replacing textures dumped with this option enabled, CPU usage on the GPU thread does increase trivially, however, generally it is worthwhile for the reliability improvement. Games that require this option include Metal Gear Solid.
ConvertCopiesToWrites
Converts VRAM copies to VRAM writes, when a copy of performed into a previously tracked VRAM write. This is required for some games that construct animated textures by copying and replacing small portions of the texture with the parts that are animated. Generally this option will cause duplication when dumping, but it is required in some games, such as Final Fantasy VIII.
MaxVRAMWriteSplits
Determines the maximum number of times a VRAM write/upload can be split, before it is discarded and no longer tracked. This is required for games that partially overwrite texture data, such as Gran Turismo.
DumpTextureWidthThreshold / DumpTextureHeightThreshold
Determines the minimum size of a texture that will be dumped. Textures with a size smaller than this value will be ignored.
ReplacementScaleLinearFilter
Enables the use of a bilinear filter when scaling replacement textures. If more than one replacement texture in a 256x256 texture page has a different scaling over the native resolution, or the texture page is not covered, a bilinear filter will be used to resize/stretch the replacement texture, and/or the original native data.
Aliases
Use this section to define replacement aliases. One line per replacement texture, with the key set to the source ID, and the value set to the ID used in place of the source. For example:
NOTE: Aliases are currently not implemented.