protonscr

Volumetric fog renders as opaque white/brown haze on NVIDIA Pascal under vkd3d-proton (The Finals, UE5)

vkd3dclosed
HansKristian-Work/vkd3d-proton#3167 · opened 2026-07-25 by okatodev · updated 2026-08-22 · 17 comments · github
Ookatodev 2026-07-25 github

Summary

On NVIDIA Pascal (GTX 10-series) GPUs on Linux, The Finals (Unreal Engine 5, DX12 via vkd3d-proton) renders volumetric fog as an opaque, near-fullscreen brownish-white haze that obscures the map, instead of the intended semi-transparent fog. The effect is inconsistent: it can fade/shrink over the first few seconds after loading a map, varies in intensity between maps, and is occasionally absent entirely. Forcing DX11 (-dx11) removes the artifact but makes the game unplayable due to severe CPU-bound stutter (expected, since UE5's renderer is primarily tuned for DX12).

Environment

  • Game: The Finals (Steam AppID 2073850), Unreal Engine 5, Shipping build (function names stripped)
  • GPU: NVIDIA Pascal (GTX 1060/1070/1080/1080 Ti class)
  • OS: Pop!_OS (Linux)
  • Compatibility layer: Steam Proton (tested on GE-Proton 11-1 and stock Proton builds; issue reproduces on all of them)
  • vkd3d-proton: bundled with the above Proton builds

Symptom (screenshot attached)

Uniform, low-contrast brownish-white fog fills the entire visible scene, with no discernible volumetric shape — looks consistent with a volumetric-fog integration/history buffer being filled with either extreme/garbage values or a wrong/uninitialized resource, rather than a rendering-logic bug (e.g. wrong density constant).

What we've ruled out so far

We dumped every shader compiled during a play session via VKD3D_SHADER_DUMP_PATH (~16k shaders total) and did static SPIR-V analysis + targeted patching via VKD3D_SHADER_OVERRIDE, gated by spirv-val so nothing invalid was ever loaded:

  1. Write-side check: Identified 52 compute shaders that perform a confirmed OpImageWrite into an image with Dim=3D (traced per-instruction through the SPIR-V type graph, not just "file contains both a 3D image type and an OpImageWrite somewhere" — we initially had that bug and it produced 2000+ false positives from bindless-heap boilerplate type declarations shared across nearly every compute shader). Patched 49/52 (3 had non-float/uint texel types we didn't attempt) to clamp any NaN, Inf, or finite value with |x| > 10000 to 0 immediately before the write. Reasoning: random garbage bits are ~100x more likely to decode as a huge finite float than as literal NaN/Inf (~46% vs ~0.4% in a random-bit-pattern simulation), so we wanted to cover both cases.

    • Result: no change in the fog artifact.
  2. Read-side check: Extended the same approach to any shader (not just compute) performing OpImageSample*/OpImageFetch/OpImageRead against a Dim=3D image, on the theory that the actual composite/"apply fog to scene" pass (likely a fragment shader sampling the finished froxel volume) might be where a NaN/huge value first gets introduced or amplified.

    • This matched 6367 of ~16000 shaders — clearly not fog-specific. We believe this reflects that UE5 has several independent systems that read 3D textures (Mesh Distance Fields, Lumen voxel data, volumetric clouds, translucency lighting volumes, in addition to fog itself), likely via a shared/inlined bindless-heap sampling pattern, so "reads a Dim=3D resource" is far too broad a signal to isolate fog specifically via static analysis alone.
    • Patched all matches anyway as a blunt test: no change in the fog artifact, but visible unrelated regressions appeared (e.g. character eye/visor shading visibly darker/different than the unpatched original — screenshot attached), confirming we were touching unrelated material shaders. We've reverted this.
  3. RenderDoc: Attempted to capture a frame via ENABLE_VULKAN_RENDERDOC_CAPTURE=1 to directly inspect the fog volume texture contents (NaN overlay). The game hangs for ~5-10s and then the process dies with no capture ever produced. We suspect Easy Anti-Cheat (which The Finals uses, with Linux/Proton support) is detecting the injected capture layer and killing the process, though we don't have a 100%-confirmed log line naming EAC specifically — happy to gather more targeted logs if useful. Given the risk/cost of forcing this, we haven't pursued it further.

  4. Confirmed that VKD3D_SHADER_QUIRK_*-style manual quirk hacks from an earlier (unguided) attempt were built against invented API names that don't exist in the real dxil_spv_shader_quirk enum, so that avenue was a dead end from the start (our mistake, not vkd3d-proton's).

What we suspect but can't confirm without more visibility into internals

  • Possibly uninitialized/garbage memory in a fog history or scattering-accumulation resource on first use (would explain the fade-in/first-frames convergence behavior and per-map variability), rather than a math bug in any single shader's instructions — which would explain why clamping values at every write and read site we could statically identify made no difference: if the corruption enters via something other than the arithmetic we're guarding (e.g. wrong resource bound, missing barrier/synchronization causing a stale/uninitialized read that our static per-instruction guards don't cover, or a resource that's read by fixed-function/copy operations we can't intercept with shader-level patches).
  • Given Pascal's known constraints on this driver stack (SPV_KHR_vulkan_memory_model / device-scope barriers, compute shader derivative fallback added in 2.12, FP16 fallback), we wonder if there's a related but distinct edge case in how coherent writes/reads to storage images are synchronized specifically for 3D image dimensionality on Pascal that isn't covered by the existing quirks.
Image

Attachments available on request

  • Full shader dump (~16k .spv files, hash-named)
  • List of the 52 write-candidate hashes with scores
  • PROTON_LOG output around a RenderDoc-attach attempt (shows a burst of INT3/access-violation exceptions across new threads shortly before the process dies)
  • Screenshots of the artifact and of the unrelated read-patch regression

Happy to run any specific diagnostic you'd suggest (env vars, targeted shader dumps, etc.) — we've built some tooling around VKD3D_SHADER_DUMP_PATH + VKD3D_SHADER_OVERRIDE for automated candidate patching that we can point at anything you want inspected.

Ddoitsujin maintainer 2026-07-25 github

Ok, at the risk of sounding unprofessional because the LLM reports are getting annoying:

Attachments available on request

No, not "on request". The issue template IS the request, goddammit. It literally asks for Proton logs, and for a reason. The log would contain things like the driver version which was also omitted here. Either read the damn thing or teach your godforsaken LLMs to ask for / provide the requested info.

This entire report is just random AI-generated word vomit that could literally be rephrased as follows:

Fog renders brown when it shouldn't, tried debugging it myself but couldn't find what the issue is.

There. 10 seconds of typing, didn't use several gigawatt-hours of electricity and is far easier to read than any of this nonsense. LLMs add words, not information.

Please, for the love of god, stop making the world a worse place than it already is.

That being said, keep in mind that Pascal isn't exactly a high-priority target and I'm not sure if we can do much here besides forwarding this to NV if it doesn't happen on any more modern GPU.

Ookatodev 2026-07-26 github

LLM report just a way to provide a information. If you're not literally to help here, just dont type something.
Actually, i get a rid of that, patching a spirv dump shaders, so i will provide more information, that can be more valuable, but this is will be still LLM written. Dont jude person that just using LLMs to make their life easier, for example, i dont know ENG well, and for me its not pleasure to write this report as fully helpful as can be.

And i dont following your template cause there's literally "Blank request" option provided. Driver version? Do you know a drivers for pascal doesnt update anymore, its obviuos its lastest.

Ookatodev 2026-07-26 github

I dont want to argue here, but there's so many yappers about ai, from both sides. So i'm just tired as you.
Dont do anything about this problem before i'm do so spirv things. And okay. can redo LLM shit next time to make it shorter

Ookatodev 2026-07-26 github

For a context, i made this fog dissapier, but i have 49 patched shaders on hand, and my final step is just determine one of them are OP.
Image

BBlisto91 2026-07-26 github

The issue is that these LLM outputs are usually overly worded and can drown out the important bits which make the issue harder to reason about at a glance. Often with details that are wrong so the analysis from them won't be taken too seriously.
In regards to language then I'd suggest just writing the issue in your original language and then do a simple old fashion machine translation to English before posting.

But we should probably mention it in the template yes and guide people towards it when making bug reports.

Ookatodev 2026-07-26 github

Okay, do wheather you want, this is your repo. Will give shaders/explanation a little bit later without LLM

Ookatodev 2026-07-27 github

So, I saw that you’ve started working on this, so let’s get right to it.
Here’s the situation: as previously mentioned, I tried various methods for patching the SPIR-V shader. The approach that worked was setting the shader's alpha channel to 1 (RGBA 0,0,0,1) right before writing to the 3D texture.
I can't say for sure what the shader contains initially or if only the alpha channel is causing the issue—it's possible the entire RGBA value is corrupted. I also couldn't pinpoint the exact shader; a precise bisect didn't yield results, and it's quite likely there isn't just one shader, but rather two or three.
If I set the RGBA value to (0,0,0,0), the frame turns completely black (see screenshot).

Image

Why don't I know exactly which shader or pair is at fault? Because I'm patching 25 shaders, and the fix works.
I’ve included all of them—both the originals and the patched versions—in the attached archive.
Screenshots showing the problem and the fix result can be found above.
I’m also not certain this is strictly a Pascal-related issue, as I only tested it on the latest version of the proprietary driver. There is no open-source driver available for my GPU—only Nouveau, which I haven't tried—so it's possible the problem lies with the driver type or version.

I don't know what else to add, but that's all for now. Please feel free to ask if you have any questions.

Patched 25 shaders:
vkd3d_override_MINIMAL.zip
Same but original 25 shaders:
original_shaders.zip

Mmomostein 2026-07-31 github

I have the same problem while playing The Finals on NixOS using my GTX 1080 ti. Is there any workaround available that doesn't involve using the -dx11 flag? Maybe using your patched shaders?

Ookatodev 2026-08-01 github

@momostein just download vkd3d_override_MINIMAL.zip from message above, exctract a folder (in archive may be a subfolder, be careful), and
VKD3D_SHADER_OVERRIDE=/path/to/vkd3d_override_MINIMAL %command%, its should be folder with spv files, nothing more, and that it, game updates may break this fix, so stay aware.

Mmomostein 2026-08-01 github

@momostein just download vkd3d_override_MINIMAL.zip from message above, exctract a folder (in archive may be a subfolder, be careful), and VKD3D_SHADER_OVERRIDE=/path/to/vkd3d_override_MINIMAL %command%, its should be folder with spv files, nothing more, and that it, game updates may break this fix, so stay aware.

This currently works for me. Thanks!

Mmomostein 2026-08-08 github

To give some more info. After configuring your shader overrides, I can play the game properly now. There's still one issue though. The skybox is now completely black on every map. I can't complain however. Now I can at least see the black skybox without the opaque fog blocking my view!

Ookatodev 2026-08-08 github

I cant do anything about it, Game too broken with this pipline, but fog was a blocker of gameplay, skybox is non.

Rregularpooria 2026-08-18 github

Hello! I put the changes you made into my system (The minimal shader archive) and now the fog is gone! Like momostein mentioned the skybox is black but I prefer that over not seeing the wall in front of me when playing

I am on Fedora 43 and here is my CPU and GPU.
CPU: Intel(R) Core(TM) i7-6700K (8) @ 4.20 GHz
GPU: NVIDIA GeForce GTX 1080 [Discrete] (Driver version 580.178.04)

Rregularpooria 2026-08-19 github

One thing I noticed is that with this fix, it will try to preload shaders everytime the game is restarted, annoying but still, better than the fog.

Rregularpooria 2026-08-21 github

With the latest update of The Finals, this issue seems to be solved, the foliage and concrete colours seem to be back to normal now and the fog problem is gone. All of this is without using custom shaders. @okatodev Can you double check on your end?

Ookatodev 2026-08-22 github

I cant check that rn.
But screenshots with mongo-hud or other resources will be helpful!

Ookatodev 2026-08-22 github

I can confirm, this problem is gone, for most artifacts and fog.
Closing the issue.
Image