Penumbra icon indicating copy to clipboard operation
Penumbra copied to clipboard

Glamourer issues with Penumbra

Open lithiumfox opened this issue 8 months ago • 7 comments

Issue: Running Glamourer causes initially slow loading / Inconsistent crashes on load in Windows 11 & Wine-XIV 10.4.1 & hard CTD on Proton-XIV 9.26 everytime

Error:

Fatal error. 0xC0000005
   at Penumbra.Interop.Hooks.PostProcessing.ShaderReplacementFixer.PrepareColorTableDetour(FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.MaterialResourceHandle*, Byte, Byte)
   at Glamourer.Interop.Material.PrepareColorSet.Detour(FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.MaterialResourceHandle*, Penumbra.GameData.Structs.StainId, Penumbra.GameData.Structs.StainId)
   at Dalamud.Game.Framework.HandleFrameworkUpdate(FFXIVClientStructs.FFXIV.Client.System.Framework.Framework*)

Attempted Workaround: Removing Glamourer resolves crashing issues on Proton-XIV & obscenely long loading times on Wine-XIV. Note: Have yet to test on Windows proper but I was having issues with that as well. [I dual boot and am lazy/haven't fully moved over to linux yet]

Additional Information: Reinstalling Glamourer causes immediate CTD prior to Square Enix logo on Proton-XIV, and crashes upon reinstall/renable once in game

After uninstalling Glamourer, the game is loading consistently with no errors, so something seems wrong with Penumbra + Glamourer. (Have not tested with Penumbra disabled and Glamourer enabled though)

I will add additional logs if I can reproduce on Windows or Wine-XIV again, but I wanted to create this while I was working on my issues in Proton-XIV before I forgot and as I reproduce steps.

dalamud.boot.log

lithiumfox avatar Apr 12 '25 12:04 lithiumfox

Exact same issue present on NixOS, successful launch with Penumbra or Glamourer enabled feels like it happens only once in every 20 launches, though hard crashing instead of simply freezing before the Square Enix logo is a new failure case for me as of today. Dalamud in general has more issues than it used to prior to 7.2, but these two plugins are an extreme case.

simply-silver avatar Apr 21 '25 19:04 simply-silver

This issue is present under Fedora 41 & 42 from my view it seems to point to an issue with Penumbra and Galmourer but debugging wine on Linux appears to be a royal pain

xnuken avatar Apr 30 '25 12:04 xnuken

Issue also present on EndeavourOS using the default Wine-XIV included in xivlauncher-git. I'm getting the same error and CTD in PrepareColorTableDetour when attempting to enter the character select screen. Occasionally, I can get in without any problems, but it feels like for every time it doesn't crash, there are 5 times where it does. I've narrowed it down to most likely being specifically a Penumbra issue, as I was able to get in fairly consistently with just Glamourer, but beyond that I'm stumped.

Arisenby avatar May 01 '25 04:05 Arisenby

This is a really peculiar bug:

  • the AsmHook on one of the PapStackAccess patches (LoadAlwaysResidentMotionPacks) targets a 7-byte instruction (the 4C 8D 05 80 14 E0 01, in 2025.04.16.0000.0000)
  • this should be fine, since AsmHook will never generate a jump over 7 bytes!
  • except.. on Wine, Reloaded.Memory.Buffers returns a target buffer at e.g. 0xFFFF0A24 instead of 0x7FFF0000 on Windows
  • fasm will therefore generate a 0x67 32-bit addressing mode prefix to zero-extend the address
  • and the output sequence (67 FF 24 25 24 0A FF FF) is 8 bytes, while Reloaded.Hooks only saved a 7-byte sequence of the original lea r8, ..
  • so once this code is hit, it'll return to the last FF from the patch, and chaos ensues...

Image Image

I managed to locally fix this by replacing two instances of UInt32.MaxValue with Int32.MaxValue in goatcorp.Reloaded.Hooks, though I'm not sure if that's the right way to go to solve this, or if this should have better logic in e.g. the Reloaded.Memory.Buffers fork or even further down the line.

nta avatar May 04 '25 15:05 nta

Some hopefully-relevant context: The definition of FindOrCreateBufferInRange defaults to int.MaxValue:

public static MemoryBuffer FindOrCreateBufferInRange(int size, nuint minimumAddress = 1, nuint maximumAddress = int.MaxValue, int alignment = 4)

Two upstream Reloaded changes switched these callsites to passing uint.MaxValue nearly three years ago: https://github.com/goatcorp/goatcorp.Reloaded.Hooks/commit/57ce273c78e03f907c2e7ee7291096d017e45694 "Expand GetAbsoluteJumpMnemonics to 4GB Address Range" and https://github.com/goatcorp/goatcorp.Reloaded.Hooks/commit/f880d0eb056b678150e3f2d07c026ae61c33912a "Expand: GetAbsoluteCallMnemonics to 4GB Address Range"

keysmashes avatar May 04 '25 16:05 keysmashes

I presume these upstream changes were made to reduce failure rates in 32-bit applications, but sadly there doesn't seem to be any annotation as to the underlying reason in the commit message there.

Good call on the default value, though.

nta avatar May 04 '25 16:05 nta

Around 3 years ago, I was trying to migrate the library to be tolerant of the Large Address Aware flag in Windows, i.e. making it work in the upper 2GB of a 32-bit process. I'm guessing that based on the above, that caused a regression somewhere in a 64-bit code path. I was not aware of the sign extension problem.

My main concern at the time was people throwing massive texture packs at 32-bit games, and running out of address space; since in practice there's only ~1.6GB usable address space, once you factor in memory fragmentation, etc. Externally injected code further shrinks our available memory beyond that.

The way I tested at the time was by setting the Large Address Aware flag on the executable that provides the test runner (dotnet.exe is Large Address Aware, but the binaries used for tests, e.g. JetBrains' Test Runner might not be!!) and then running the LAA tests (disabled by default, opt in)

In any case, chances are the Rust rewrite Reloaded.Hooks-rs, doesn't suffer from this issue, as I wrote the JIT assembler there from scratch (for optimal performance & code size). [There's also advanced tests there, including wrapping around address space boundaries, etc.]

However, despite being ~80% done, that's been on the backburner for around 15 months now. I imagine I'll get back to it at the literal end of the year. I was originally hoping Q3, but with texture compression shenanigans and an archive format taking way, way longer to complete, that doesn't seem quite feasible anymore.

Sewer56 avatar May 04 '25 19:05 Sewer56