Skip to content

Texture Replacement

Connor McLaughlin edited this page Sep 27, 2024 · 3 revisions

Introduction

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.

For an explanation of why texture replacement on the PSX is so challenging, see https://github.com/stenzek/duckstation/pull/3244.

How to use it?

  1. Enable the texture cache in Graphics Settings -> Texture Replacements.
  2. Enable texture dumping.
  3. Run the game, load a level, move around.
  4. Shut down the virtual machine.
  5. Examine the texture dump folder (Tools -> Open Data Directory -> textures -> SERIAL -> dumps).
  6. If the textures are not dumping correctly, it is either not compatible, or you need to configure the options (explained below) specifically for the game.
  7. Copy the textures from the dump directory to the replacements directory, edit/resize/whatever as you see fit.
  8. Optionally, disable texture dumping, and check the Enable Texture Replacements option.
  9. Boot the game, and hopefully see your replacements.

Texture dump/replacement filename format

Replacement filenames must follow the following naming convention:

texupload-P4-AAAAAAAAAAAAAAAA-BBBBBBBBBBBBBBBB-64x256-0-192-64x64-P0-14 
  • 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, or webp formats.

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 its textures directory, with the following options:

DumpTexturePages

Instead of tracking VRAM writes and attempting to identify the "real" size of textures, create sub-rectangles from pages based on how they are drawn. 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.

DumpFullTexturePages

Dumps full texture pages instead of sub-rectangles. 256x256 pages will be dumped/replaced instead. Generally not useful.

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.

MaxVRAMWriteCoalesceWidth / MaxVRAMWriteCoalesceHeight

Determines the maximum size of an incoming VRAM write that will be merged with another write to the left/above of the incoming write. Needed for games that upload textures one line at a time. These games will log "tracking VRAM write of Nx1" repeatedly during loading. If the upload size is 1, then you can set the corresponding maximum coalesce dimension to 1 to merge these uploads, which should enable these textures to be dumped/replaced.

DumpTextureWidthThreshold / DumpTextureHeightThreshold

Determines the minimum size of a texture that will be dumped. Textures with a size smaller than this value will be ignored.

DumpVRAMWriteWidthThreshold / DumpVRAMWriteHeightThreshold

Determines the minimum size of a VRAM write that will be dumped, in background dumping mode. Uploads smaller than this size 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 filename which should be loaded as a replacement. Aliases can be used when duplicate textures are dumped and need replacing with a single image.

For example, without the newline, or keep the multi-line separator.

Aliases:
  # Alias-Texture-Name: Path-To-Texture
  texupload-P4-AAAAAAAAAAAAAAAA-BBBBBBBBBBBBBBBB-64x256-0-192-64x64-P0-14: |
    texupload-P4-BBBBBBBBBBBBBBBB-BBBBBBBBBBBBBBBB-64x256-0-64-64x64-P0-13.png
  texupload-P4-AAAAAAAAAAAAAAAA-BBBBBBBBBBBBBBBB-64x256-0-192-64x64-P0-14: mytexture.png