Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package/Shaders/Common/SharedData.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace SharedData
bool InMapMenu; // If the world/local map is open (note that the renderer is still deferred here)
bool HideSky; // HideSky flag in WorldSpace, e.g. Blackreach
float MipBias; // Offset to mip level for TAA sharpness#
uint IsNotNativeD3D11; // Linux/Proton/DXVK detected - use spatial shadow denoising
};

struct GrassLightingSettings
Expand Down
20 changes: 18 additions & 2 deletions package/Shaders/Utility.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,15 @@ float GetPoissonDiskFilteredShadowVisibility(uint3 seed, Texture2DArray<float4>
float alphaTestOffset = -AlphaTestRef.y;
float sampleRadius = ShadowSampleParam.z * 2048.0;
float seedNormalized = seed.x * (1.0 / 4294967295.0); // Use only x component for efficiency
uint frameOffset = SharedData::FrameCount * sampleCount;

// On Linux/Proton/DXVK, use position-based offset instead of frame-based to avoid temporal jitter
uint frameOffset;
if (SharedData::IsNotNativeD3D11) {
int3 quantizedPos = int3(floor(positionMS.xyz * 8.0));
frameOffset = Random::murmur3(uint3(quantizedPos));
} else {
frameOffset = SharedData::FrameCount * sampleCount;
}

// Pre-compute constants
const float3 positionOffsetUp = float3(0, 0, 1);
Expand Down Expand Up @@ -616,7 +624,15 @@ PS_OUTPUT main(PS_INPUT input)
float fadeFactor = input.Alpha.x;
# endif

float noise = Random::InterleavedGradientNoise(input.PositionCS.xy, SharedData::FrameCount);
// On Linux/Proton/DXVK, use spatial noise based on world position instead of frame-based temporal noise to stop shimmering
float noise;
if (SharedData::IsNotNativeD3D11) {
int3 quantizedPos = int3(floor(positionMS.xyz * 8.0));
uint posHash = Random::murmur3(uint3(quantizedPos));
noise = float(posHash) / 4294967295.0;
} else {
noise = Random::InterleavedGradientNoise(input.PositionCS.xy, SharedData::FrameCount);
}

float2 rotation;
sincos(Math::TAU * noise, rotation.y, rotation.x);
Expand Down
12 changes: 12 additions & 0 deletions src/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,16 @@ void State::SetAdapterDescription(const std::wstring& description)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
adapterDescription = converter.to_bytes(description);

// Detect Linux/Proton/DXVK by checking adapter description
std::string lowerDesc = adapterDescription;
std::transform(lowerDesc.begin(), lowerDesc.end(), lowerDesc.begin(), ::tolower);
isRunningOnDXVK = (lowerDesc.find("dxvk") != std::string::npos) ||
(lowerDesc.find("wine") != std::string::npos) ||
(lowerDesc.find("llvmpipe") != std::string::npos);
if (isRunningOnDXVK) {
logger::info("Detected Linux/Proton/DXVK environment - using spatial shadow denoising");
}
Comment on lines +710 to +719

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

DXVK adapter description string D3D11 GetAdapterDesc

💡 Result:

Short answer

  • DXVK fills DXGI_ADAPTER_DESC.Description with the Vulkan physical device name (VkPhysicalDeviceProperties::deviceName, converted to wide chars). VendorId/DeviceId come from VkPhysicalDeviceProperties.vendorID/deviceID and memory fields from the device memory info; DXGI structs otherwise follow DXGI_ADAPTER_DESC semantics. Sources: DXVK uses Vulkan physical-device properties when enumerating adapters, Vulkan exposes deviceName/vendorID/deviceID, and DXGI_ADAPTER_DESC defines the Description field. [1][2][3]

Sources

  • DXVK project (repository / adapter handling / docs showing DXVK maps Vulkan physical devices to DXGI adapters). [1]
  • Vulkan reference: VkPhysicalDeviceProperties (deviceName, vendorID, deviceID). [2]
  • Microsoft: DXGI_ADAPTER_DESC / IDXGIAdapter::GetDesc (Description field semantics). [3]

🌐 Web query:

Wine DXVK llvmpipe adapter name identification

💡 Result:

Short answer — identify the llvmpipe adapter by checking Vulkan device names (vulkaninfo) or DXVK logs, then use that string with DXVK_FILTER_DEVICE_NAME to exclude/choose it.

How to do it

  • List Vulkan devices: run
    vulkaninfo | grep -i deviceName
    The output shows lines like "deviceName = llvmpipe (LLVM 15.0.7, 256 bits)" for software (llvmpipe) and your real GPU(s). Use the exact substring you want to match. [1][2]

  • Or read DXVK startup logs: DXVK prints adapter names when enumerating devices, e.g. "Skipping Vulkan 1.0 adapter: llvmpipe (LLVM ...)" — inspect your DXVK stdout/log to see the same name. [2]

  • Force/select a device in DXVK:
    export DXVK_FILTER_DEVICE_NAME="Your GPU device substring"
    (DXVK matches substrings from the Vulkan device name; if the filter matches the wrong device or none, DXVK will fail to create a device — use the exact substring from vulkaninfo/DXVK logs). [3]

Notes

  • If Vulkan lists only llvmpipe, the system is using Mesa software rendering (install or fix your GPU Vulkan ICD or set VK_ICD_FILENAMES to the GPU ICD JSON). [2]
  • You can also select by UUID with DXVK_FILTER_DEVICE_UUID (vulkaninfo shows UUIDs). [3]

Sources

  • Example of llvmpipe in Vulkan output / Mesa issues. [1]
  • DXVK logging and examples showing llvmpipe in adapter list. [2]
  • DXVK device-filter environment variables (DXVK_FILTER_DEVICE_NAME / UUID). [3]

References
[1] Mesa / llvmpipe example (vulkan/glxinfo output).
[2] DXVK/GitHub issues and community posts showing "llvmpipe" in DXVK logs.
[3] DXVK documentation / README (DXVK_FILTER_DEVICE_NAME and DXVK_FILTER_DEVICE_UUID).


🌐 Web query:

How to detect DXVK Wine Proton in Direct3D adapter description

💡 Result:

Short answer: you generally can't reliably detect DXVK/Proton/Wine just from the Direct3D adapter description alone. Instead check for DXVK-specific signs at runtime (logs, HUD, DLLs, env vars) or for mismatches between the reported adapter string and the underlying Vulkan driver. Sources: DXVK docs and project pages. [1][2][3][4]

Practical checks (ordered from most reliable to least):

  1. Look for DXVK logs or output
  • DXVK prints logs to stderr when used under Wine/Proton (or to files when DXVK_LOG_PATH set). If you can capture process stderr or the game folder, presence of d3d9/d3d11/dxgi log messages proves DXVK. [2][3]
  1. Check for DXVK binaries / DLL overrides in the prefix/process
  • If the process loads DXVK DLLs (d3d11.dll, dxgi.dll, d3d9.dll from dxvk) that’s definitive. Inspect loaded modules (Process Explorer / lsof / /proc//maps) or the Wine prefix to see installed DLLs. [2][3]
  1. Use DXVK_HUD or environment variables
  • DXVK has a HUD (DXVK_HUD) that can show "version", "devinfo" and device strings. If enabling DXVK_HUD produces the overlay, DXVK is active. Also check for DXVK_/VKD3D_ env vars in the process environment. [1][4]
  1. Detect vendor/driver mismatch (heuristic only)
  • Under DXVK the adapter description will often reflect the host Vulkan driver (Mesa, RADV, NVIDIA) rather than a Windows native driver. If the adapter string contains obvious Linux/Vulkan driver names (e.g., "RADV", "Mesa", "vulkan", "Intel Vulkan"), or mentions "Proton"/"Wine" in other outputs, that’s a hint but not definitive. This is fragile and can be spoofed. [1][4]
  1. For D3D12: check for vkd3d(-proton)
  • D3D12 translation on Proton uses vkd3d(-proton) rather than DXVK; look for vkd3d logs, DLLs or HUD differences. [1][3]

Example quick commands (Linux/Wine host)

  • Check loaded modules: lsof -p or grep dxgi /proc//maps (shows dxgi/d3d11 DLL path). (See point 2.) [2][3]
  • Run with DXVK_HUD=devinfo,fps to force the overlay and verify. (See point 3.) [1][4]
  • Inspect stderr or set DXVK_LOG_PATH to force log files and inspect them for "info: " lines. [2][3]

References

  • DXVK official site / FAQ (HUD, usage). [1]
  • DXVK README / setup and logging behavior (how it emits logs under Wine/Proton and DLLs). [2][3]
  • HOWTO / Linux pages describing DXVK_HUD and devinfo behavior. [4]

If you want, tell me whether you need a small script (Windows/Wine) to perform these checks automatically and I’ll give one.


Detection keywords are unlikely to match adapter descriptions.

The DXVK adapter description is populated from the Vulkan physical device name (e.g., "llvmpipe (LLVM ...)", "RADV", "Intel Vulkan"), not from DXVK or Wine identifiers. Checking for "dxvk" or "wine" strings in the adapter description will not reliably detect DXVK/Proton environments—these translation layer names do not appear in the reported device names. The "llvmpipe" check is valid and will match software rendering, but it doesn't specifically identify DXVK or Wine.

For reliable detection of DXVK/Wine at runtime, consider checking for DXVK-specific signs: presence of DXVK logs, DLL overrides, environment variables (DXVK_*), or the DXVK HUD. Adapter string matching alone is insufficient for this purpose.

}

void State::UpdateSharedData([[maybe_unused]] bool a_inWorld, [[maybe_unused]] bool a_prepass)
Expand Down Expand Up @@ -778,6 +788,8 @@ void State::UpdateSharedData([[maybe_unused]] bool a_inWorld, [[maybe_unused]] b
data.MipBias = 0;
}

data.IsNotNativeD3D11 = isRunningOnDXVK ? 1 : 0;

sharedDataCB->Update(data);
}

Expand Down
3 changes: 2 additions & 1 deletion src/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class State
bool settingCustomShader = false;
RE::BSShader* currentShader = nullptr;
std::string adapterDescription = "";
bool isRunningOnDXVK = false; // Detected via adapter description

uint32_t currentVertexDescriptor = 0;
uint32_t currentPixelDescriptor = 0;
Expand Down Expand Up @@ -203,7 +204,7 @@ class State
uint InMapMenu;
uint HideSky;
float MipBias;
float pad0;
uint IsNotNativeD3D11; // True on DXVK/Wine - use spatial denoising instead of temporal
};
STATIC_ASSERT_ALIGNAS_16(SharedDataCB);

Expand Down