From 9b99c55a2e74864c550448dba47e3a95d5916170 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 5 Apr 2019 20:19:33 +0200 Subject: [PATCH] [dxbc] Implement optional constant buffer range check --- src/dxbc/dxbc_compiler.cpp | 84 +++++++++++++++++++++++--------------- src/dxbc/dxbc_options.h | 4 ++ 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index c8dd616e9..c80cbed14 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -4587,35 +4587,6 @@ namespace dxvk { } - DxbcRegisterPointer DxbcCompiler::emitGetConstBufPtr( - const DxbcRegister& operand) { - // Constant buffers take a two-dimensional index: - // (0) register index (immediate) - // (1) constant offset (relative) - DxbcRegisterInfo info; - info.type.ctype = DxbcScalarType::Float32; - info.type.ccount = 4; - info.type.alength = 0; - info.sclass = spv::StorageClassUniform; - - const uint32_t regId = operand.idx[0].offset; - const DxbcRegisterValue constId = emitIndexLoad(operand.idx[1]); - - const uint32_t ptrTypeId = getPointerTypeId(info); - - const std::array indices = - {{ m_module.consti32(0), constId.id }}; - - DxbcRegisterPointer result; - result.type.ctype = info.type.ctype; - result.type.ccount = info.type.ccount; - result.id = m_module.opAccessChain(ptrTypeId, - m_constantBuffers.at(regId).varId, - indices.size(), indices.data()); - return result; - } - - DxbcRegisterPointer DxbcCompiler::emitGetImmConstBufPtr( const DxbcRegister& operand) { const DxbcRegisterValue constId @@ -4677,9 +4648,6 @@ namespace dxvk { case DxbcOperandType::Output: return emitGetOutputPtr(operand); - case DxbcOperandType::ConstantBuffer: - return emitGetConstBufPtr(operand); - case DxbcOperandType::ImmediateConstantBuffer: return emitGetImmConstBufPtr(operand); @@ -5241,8 +5209,31 @@ namespace dxvk { DxbcRegisterValue DxbcCompiler::emitConstantBufferLoad( const DxbcRegister& reg, DxbcRegMask writeMask) { - DxbcRegisterPointer ptr = emitGetOperandPtr(reg); + // Constant buffers take a two-dimensional index: + // (0) register index (immediate) + // (1) constant offset (relative) + DxbcRegisterInfo info; + info.type.ctype = DxbcScalarType::Float32; + info.type.ccount = 4; + info.type.alength = 0; + info.sclass = spv::StorageClassUniform; + + uint32_t regId = reg.idx[0].offset; + DxbcRegisterValue constId = emitIndexLoad(reg.idx[1]); + + uint32_t ptrTypeId = getPointerTypeId(info); + + const std::array indices = + {{ m_module.consti32(0), constId.id }}; + + DxbcRegisterPointer ptr; + ptr.type.ctype = info.type.ctype; + ptr.type.ccount = info.type.ccount; + ptr.id = m_module.opAccessChain(ptrTypeId, + m_constantBuffers.at(regId).varId, + indices.size(), indices.data()); + // Load individual components from buffer std::array ccomps = { 0, 0, 0, 0 }; std::array scomps = { 0, 0, 0, 0 }; uint32_t scount = 0; @@ -5283,6 +5274,33 @@ namespace dxvk { scount, scomps.data()); } + // HACK: If requested, use the constant buffer size to perform a range + // check. This does NOT match the API behaviour, but is needed for some + // games since out-of-bounds access is undefined behaviour. + if (m_moduleInfo.options.constantBufferRangeCheck && reg.idx[1].relReg) { + uint32_t zero = m_module.constf32(0.0f); + uint32_t cond = m_module.opULessThan( + m_module.defBoolType(), constId.id, + m_module.consti32(m_constantBuffers[regId].size)); + + if (scount > 1) { + std::array zeroes = {{ zero, zero, zero, zero }}; + std::array conds = {{ cond, cond, cond, cond }}; + + zero = m_module.opCompositeConstruct( + getVectorTypeId(result.type), + scount, zeroes.data()); + cond = m_module.opCompositeConstruct( + m_module.defVectorType(m_module.defBoolType(), scount), + scount, conds.data()); + } + + result.id = m_module.opSelect( + getVectorTypeId(result.type), cond, + result.id, zero); + } + + // Apply any post-processing that might be necessary result = emitRegisterBitcast(result, reg.dataType); result = emitSrcOperandModifiers(result, reg.modifiers); return result; diff --git a/src/dxbc/dxbc_options.h b/src/dxbc/dxbc_options.h index 9fdf554c6..c109ed928 100644 --- a/src/dxbc/dxbc_options.h +++ b/src/dxbc/dxbc_options.h @@ -32,6 +32,10 @@ namespace dxvk { /// Enables sm4-compliant division-by-zero behaviour bool strictDivision = false; + // Enables range checking for constant buffers. + // Fixes issues in some games, breaks others. + bool constantBufferRangeCheck = false; + /// Clear thread-group shared memory to zero bool zeroInitWorkgroupMemory = false; };