1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-03-03 16:29:15 +01:00

[dxbc] Bound-check dynamically indexed input registers

Halo MCC reads undefined PS inputs otherwise.
This commit is contained in:
Philip Rebohle 2025-01-11 11:19:51 +01:00 committed by Philip Rebohle
parent 7f4e25267c
commit f17317061c
2 changed files with 61 additions and 8 deletions

View File

@ -281,7 +281,7 @@ namespace dxvk {
return this->emitDclGlobalFlags(ins);
case DxbcOpcode::DclIndexRange:
return; // not needed for anything
return this->emitDclIndexRange(ins);
case DxbcOpcode::DclTemps:
return this->emitDclTemps(ins);
@ -375,6 +375,21 @@ namespace dxvk {
}
void DxbcCompiler::emitDclIndexRange(const DxbcShaderInstruction& ins) {
// dcl_index_range has one operand:
// (0) Range start, either an input or output register
// (1) Range end
uint32_t index = ins.dst[0].idxDim - 1u;
DxbcIndexRange range = { };
range.type = ins.dst[0].type;
range.start = ins.dst[0].idx[index].offset;
range.length = ins.imm[0].u32;
m_indexRanges.push_back(range);
}
void DxbcCompiler::emitDclTemps(const DxbcShaderInstruction& ins) {
// dcl_temps has one operand:
// (imm0) Number of temp registers
@ -5737,14 +5752,37 @@ namespace dxvk {
DxbcRegisterValue DxbcCompiler::emitRegisterLoadRaw(
const DxbcRegister& reg) {
if (reg.type == DxbcOperandType::IndexableTemp) {
bool doBoundsCheck = reg.idx[1].relReg != nullptr;
DxbcRegisterValue vectorId = emitIndexLoad(reg.idx[1]);
// Try to find index range for the given register
const DxbcIndexRange* indexRange = nullptr;
if (reg.idxDim && reg.idx[reg.idxDim - 1u].relReg) {
uint32_t offset = reg.idx[reg.idxDim - 1u].offset;
for (const auto& range : m_indexRanges) {
if (reg.type == range.type && offset >= range.start && offset < range.start + range.length)
indexRange = &range;
}
}
if (reg.type == DxbcOperandType::IndexableTemp || indexRange) {
bool doBoundsCheck = reg.idx[reg.idxDim - 1u].relReg != nullptr;
if (doBoundsCheck) {
uint32_t boundsCheck = m_module.opULessThan(
m_module.defBoolType(), vectorId.id,
m_module.constu32(m_xRegs.at(reg.idx[0].offset).alength));
DxbcRegisterValue indexId = emitIndexLoad(reg.idx[reg.idxDim - 1u]);
uint32_t boundsCheck = 0u;
if (reg.type == DxbcOperandType::IndexableTemp) {
boundsCheck = m_module.opULessThan(
m_module.defBoolType(), indexId.id,
m_module.constu32(m_xRegs.at(reg.idx[0].offset).alength));
} else {
uint32_t adjustedId = m_module.opISub(getVectorTypeId(indexId.type),
indexId.id, m_module.consti32(indexRange->start));
boundsCheck = m_module.opULessThan(
m_module.defBoolType(), adjustedId,
m_module.constu32(indexRange->length));
}
// Kind of ugly to have an empty else block here but there's no
// way for us to know the current block ID for the phi below

View File

@ -1,6 +1,7 @@
#pragma once
#include <array>
#include <utility>
#include <vector>
#include "../spirv/spirv_module.h"
@ -135,6 +136,13 @@ namespace dxvk {
uint32_t component = 0;
};
struct DxbcIndexRange {
DxbcOperandType type;
uint32_t start;
uint32_t length;
};
/**
* \brief Vertex shader-specific structure
@ -445,6 +453,10 @@ namespace dxvk {
// xfb output registers for geometry shaders
std::vector<DxbcXfbVar> m_xfbVars;
/////////////////////////////////////////////
// Dynamically indexed input and output regs
std::vector<DxbcIndexRange> m_indexRanges = { };
//////////////////////////////////////////////////////
// Shader resource variables. These provide access to
// constant buffers, samplers, textures, and UAVs.
@ -473,7 +485,7 @@ namespace dxvk {
uint32_t m_vArrayLengthId = 0;
uint32_t m_vArray = 0;
////////////////////////////////////////////////////
// Per-vertex input and output blocks. Depending on
// the shader stage, these may be declared as arrays.
@ -546,6 +558,9 @@ namespace dxvk {
void emitDclGlobalFlags(
const DxbcShaderInstruction& ins);
void emitDclIndexRange(
const DxbcShaderInstruction& ins);
void emitDclTemps(
const DxbcShaderInstruction& ins);