Skip to content

Mafia: Definitive Edition: Add Luma mod - #225

Open
williamstosh wants to merge 1 commit into
Filoppi:mainfrom
williamstosh:feat/mafiade
Open

williamstosh wants to merge 1 commit into
Filoppi:mainfrom
williamstosh:feat/mafiade

Conversation

@williamstosh

Copy link
Copy Markdown

Mafia: Definitive Edition: Add Luma mod

Adds a Luma mod for Mafia: Definitive Edition (the 2020 remake), replacing the game's temporal
resolve with DLSS (DLAA at native resolution). Same shape as the existing Mafia II: Definitive
Edition and Mafia III mods.

What it does

  • Hooks the two TAA resolve variants (0x37B05605, 0x7B9B914F) and feeds DLSS SR with the game's
    own jittered colour (t0), history (t1) and motion vectors (t2).
  • Captures depth from the position fill pass (0x121A4A96), the only full resolution pass that both
    tests and writes depth.
  • Optional RCAS sharpening on the SR output (off by default), the same approach as the Mafia III mod.
  • Panel: SR on/off, RCAS strength, MV sign flip. Everything else stays behind #if DEVELOPMENT.

Facts worth recording (all measured - REPORT.md carries the raw evidence)

topic finding
Motion vectors This game has no standalone raw MV buffer. The TAA's own t2 is the MV (R16G16B16A16_FLOAT), stored in UV space, so it must be scaled by the render resolution before DLSS sees it.
Jitter TAA pass PS constant buffer slot 0, floats 32/33, pixel space, 8 phase Halton. No NDC conversion, no sign flip.
MVs already contain the jitter The engine bakes it into the projection matrices and the resolve does prevUV = curUV + MV with no jitter term at all (disassembly confirmed); the shader never reads the jitter floats we hand to DLSS. MVJittered is therefore set in the create flags, while the separate behaviour flag ships off - with DLSS 310.9.x, ON measures softer and noisier on this content (§6.2).
Two resolves per frame The engine resolves in layers; the second resolve covers character / skin / transparency. Cancelling it is what makes faces and hands show DLAA at all (cancel_second_taa, default on, left switchable so it can be A/B'd).
Do not cancel 0x747C6210 The post tonemap sharpen has mul r0, r0, v1 outside its if (strength > 0) block. Skipping the draw removes a per pixel multiply and turns the screen black. Tame it by zeroing cb0[0].z instead.
0x2B974E48 is not the colour AA All four of its FXAA taps read t0, a single channel R32_FLOAT auxiliary buffer. Feeding that to DLSS is exactly what produced the early "heavy noise" reports. Deliberately not hooked.
Tonemap 0x916B1D65 - the same hash RenoDX uses, so independently confirmed by two projects.

The one core change: SDR_OUTPUT_TRANSFORM

Not game specific, and the default keeps existing behaviour.

Luma's SDR display composition has always decoded the game's final image from sRGB to linear. That is
correct when the container encodes on write (_SRGB views, scRGB - which is what the scRGB swapchain
upgrade relies on), but on a plain UNORM container (R8G8B8A8_UNORM, R10G10B10A2_UNORM, ...)
the OS applies the sRGB transfer function itself, so the image ends up one decode too dark: over
contrasted and over saturated compared to vanilla.

  • SDR_OUTPUT_TRANSFORM = 0 - decode sRGB to linear (legacy, default, no existing game changes)
  • SDR_OUTPUT_TRANSFORM = 1 - pass through untouched (correct for a plain UNORM container)

Mafia: DE sets it to 1, which is where the need for it was found. Luma_DisplayComposition.hlsl
gets the matching branch (SDRGameOutputToContainer).

Verification

  • Builds as Development-Release and Publishing-Release, x64. The project ships v145 like the
    rest of the repo.
  • Played with SR on: DLAA engages, the panel reports the live DLSS state, depth and motion vectors are
    the game's own, RCAS checked A/B in game, DLSS render presets profiled (§6.1).
  • The AA quality and cutscene findings come from a per frame pixel shader set diagnostic that walked
    AA Low -> Medium -> High -> Low and then a full cutscene, in one session (§6.5 / §6.6).

Known limitations (REPORT.md §7)

  1. The game's own AA slider must be on Low. Medium and High run no temporal resolve at all
    (verified: every shader unique to those modes is a geometry pass), so there is no draw to replace.
    Nothing on this side can change that.
  2. Cutscenes, the main menu and the loading screens are pre-rendered video (6-8 shaders against
    100-190, no resolve, no tonemapper). There is nothing to anti-alias, and a video frame carries none
    of the guides DLSS SR / RR / NR or frame generation need.
  3. The DLSS render preset must be pinned to M for correct reflective / transparent content (§6.1).
  4. No native HDR output through this mod - an unsolved mechanism on this side, not a game limitation.
  5. It replaces the resolve only, it does not upscale, so there is no performance win: DLAA at native
    resolution costs more than the game's TAA.

Documentation

REPORT.md in the game folder is both the user facing document (§1-§3: requirements, installation,
settings) and the reverse engineering record (§4-§6: passes, measurements, raw evidence), with two SM5
disassemblies of the game's own shaders under docs/. It is written so that what was measured can be
told apart from what was assumed.

@Filoppi

Filoppi commented Oct 7, 2026

Copy link
Copy Markdown
Owner

I'm adding SDR support to Luma this well, given this overall, I'll wait to merge my changes. Thanks.

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.

2 participants