From 3b030d956931084690c624dba166eedc9851777a Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 30 Nov 2019 17:01:44 +0100 Subject: [PATCH] [dxbc] Implement workaround to replace NaN render target outputs by zero --- dxvk.conf | 8 ++++++++ src/d3d11/d3d11_options.cpp | 1 + src/d3d11/d3d11_options.h | 4 ++++ src/dxbc/dxbc_compiler.cpp | 19 +++++++++++++++---- src/dxbc/dxbc_options.cpp | 1 + src/dxbc/dxbc_options.h | 3 +++ 6 files changed, 32 insertions(+), 4 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 83f36a22d..f72a0c36d 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -126,6 +126,14 @@ # d3d11.strictDivision = False +# Replaces NaN outputs from fragment shaders with zeroes for floating +# point render target. Used in some games to prevent artifacting. +# +# Supported values: True, False + +# d3d11.enableRtOutputNanFixup = False + + # Clears workgroup memory in compute shaders to zero. Some games don't do # this and rely on undefined behaviour. Enabling may reduce performance. # diff --git a/src/d3d11/d3d11_options.cpp b/src/d3d11/d3d11_options.cpp index 2731b3cd7..86c2b5238 100644 --- a/src/d3d11/d3d11_options.cpp +++ b/src/d3d11/d3d11_options.cpp @@ -9,6 +9,7 @@ namespace dxvk { this->dcSingleUseMode = config.getOption("d3d11.dcSingleUseMode", true); this->strictDivision = config.getOption("d3d11.strictDivision", false); + this->enableRtOutputNanFixup = config.getOption("d3d11.enableRtOutputNanFixup", false); this->zeroInitWorkgroupMemory = config.getOption("d3d11.zeroInitWorkgroupMemory", false); this->relaxedBarriers = config.getOption("d3d11.relaxedBarriers", false); this->maxTessFactor = config.getOption("d3d11.maxTessFactor", 0); diff --git a/src/d3d11/d3d11_options.h b/src/d3d11/d3d11_options.h index b467a2eb6..e340f25ee 100644 --- a/src/d3d11/d3d11_options.h +++ b/src/d3d11/d3d11_options.h @@ -25,6 +25,10 @@ namespace dxvk { /// games may expect correct behaviour. bool strictDivision; + /// Enables workaround to replace NaN render target + /// outputs with zero + bool enableRtOutputNanFixup; + /// Enables out-of-bounds access check for constant /// buffers. Workaround for a few broken games that /// access random data inside their shaders. diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 68de83209..05ec96c3e 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -5834,10 +5834,21 @@ namespace dxvk { scalars[c] = m_module.opVectorExtractDynamic(compTypeId, vector.id, specId); } - vector.id = m_module.opCompositeConstruct( - getVectorTypeId(vector.type), - vector.type.ccount, - scalars.data()); + uint32_t typeId = getVectorTypeId(vector.type); + vector.id = m_module.opCompositeConstruct(typeId, vector.type.ccount, scalars.data()); + + // Replace NaN by zero if requested + if (m_moduleInfo.options.enableRtOutputNanFixup && vector.type.ctype == DxbcScalarType::Float32) { + uint32_t boolType = m_module.defBoolType(); + + if (vector.type.ccount > 1) + boolType = m_module.defVectorType(boolType, vector.type.ccount); + + uint32_t zero = emitBuildConstVecf32(0.0f, 0.0f, 0.0f, 0.0f, + DxbcRegMask((1u << vector.type.ccount) - 1)).id; + uint32_t isNan = m_module.opIsNan(boolType, vector.id); + vector.id = m_module.opSelect(typeId, isNan, zero, vector.id); + } emitValueStore(m_oRegs[i], vector, DxbcRegMask::firstN(vector.type.ccount)); diff --git a/src/dxbc/dxbc_options.cpp b/src/dxbc/dxbc_options.cpp index 827e507ac..0e187d710 100644 --- a/src/dxbc/dxbc_options.cpp +++ b/src/dxbc/dxbc_options.cpp @@ -37,6 +37,7 @@ namespace dxvk { case Tristate::False: minSsboAlignment = ~0u; break; } + enableRtOutputNanFixup = options.enableRtOutputNanFixup; strictDivision = options.strictDivision; zeroInitWorkgroupMemory = options.zeroInitWorkgroupMemory; dynamicIndexedConstantBufferAsSsbo = options.constantBufferRangeCheck; diff --git a/src/dxbc/dxbc_options.h b/src/dxbc/dxbc_options.h index afdb76c49..dac8d562c 100644 --- a/src/dxbc/dxbc_options.h +++ b/src/dxbc/dxbc_options.h @@ -32,6 +32,9 @@ namespace dxvk { /// dword offsets. Fixes RE2 and DMC5 on Nvidia drivers. bool useSdivForBufferIndex = false; + /// Enables NaN fixup for render target outputs + bool enableRtOutputNanFixup = false; + /// Enables sm4-compliant division-by-zero behaviour bool strictDivision = false;