Commit Graph
1 Commits
Author SHA1 Message Date
avan 9c46452388 Add raw copy dependencies for incompatible textures
Trails in the Sky 1st uses texture descriptors with different formats and dimensions that map to the same region of guest GPU memory. For example, the game interprets the same underlying data as both an R32Uint 2x1 texture and an R32G32Float 1x1 texture. Both textures have an 8-byte logical payload, but their texel formats and dimensions are different.

Ryubing cannot represent these two descriptors as normally compatible texture views, so it creates separate host texture objects for them. Since these host textures represent the same guest memory, a modification made to one of them should become visible to the other before the other texture is read. However, the existing synchronization mechanism does not fully handle this situation.

The game uses this pattern for its exposure or brightness history. A compute shader first writes the updated exposure data to the R32G32Float 1x1 host texture. A later operation then reads the raw bits of the same data through the R32Uint 2x1 host texture. The original Ryubing implementation did not synchronize the contents of these two separate host textures. As a result, the reading texture received stale zero values, which caused the shader to calculate an incorrectly low exposure value and made the rendered image appear too dark.

TextureGroup already has a guest-memory synchronization mechanism for incompatible overlaps. When _flushIncompatibleOverlaps is enabled, the most recent texture contents are written back to guest memory, after which the other texture reloads the data. However, this mechanism was primarily enabled through IsFormatHostIncompatible, which checks whether an individual texture format can be correctly supported by the host GPU.

In this case, both R32Uint and R32G32Float are individually supported by the host GPU. Therefore, IsFormatHostIncompatible does not enable this synchronization path. The issue is not that either format is unsupported. The issue is that two individually supported aliases, represented by separate host textures, do not maintain coherent contents.

In theory, the latest contents could be flushed to guest memory immediately after every GPU write to a host texture, allowing other textures that map to the same guest memory to reload the updated data. However, doing so could frequently trigger expensive operations such as GPU waits, data readbacks, guest texture layout conversions, and memory-tracking updates. This would be particularly costly for textures that are updated every frame or during every compute dispatch.

For this reason, the fix does not require an immediate flush after every write. Instead, it extends Ryubing's existing TextureDependency and TextureGroupHandle mechanisms. When two fully incompatible textures map to exactly the same guest memory and satisfy a strict set of raw-copy requirements, the program creates a raw-copy dependency between them.

When the source texture is modified, the target texture is only marked as requiring a raw copy. The latest logical raw bytes are copied from the source only when the target texture is actually about to be used. This keeps the contents of the separate host texture aliases coherent while avoiding unnecessary round trips through guest memory.
2026-08-04 23:17:36 -05:00