Skip to content
Merged
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
4 changes: 3 additions & 1 deletion core/libs/tests/InterpolationReferenceRange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ int main() {
}
};
const auto attribute = Collect(5u, 1u, 0u, 2u);
expect(attribute.find("interpolation parameter reference is out of range (attribute 5 component 1 mode 0, 2 inputs)") != std::string::npos, "an attribute past the pixel inputs", attribute);
expect(attribute.find("interpolation parameter reference") == std::string::npos, "an attribute past the pixel inputs becomes a default", attribute);
const auto empty = Collect(0u, 0u, 0u, 0u);
expect(empty.find("interpolation parameter reference") == std::string::npos, "attribute 0 with no SPI inputs becomes a default", empty);
const auto component = Collect(0u, 4u, 2u, 1u);
expect(component.find("(attribute 0 component 4 mode 2, 1 inputs)") != std::string::npos, "a component past w", component);
const auto mode = Collect(1u, 3u, 3u, 3u);
Expand Down
32 changes: 32 additions & 0 deletions core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,35 @@ void ValidateOptions(const IrProgram& program, ShaderStageInputInfo inputInfo) {
}
}

void EnsurePixelInterpolatorCoverage(const IrProgram& program, ShaderPixelInputInfo& pixel) {
std::uint32_t needed = pixel.inputNum;
const auto consider = [&](std::uint32_t attribute) {
if (attribute < 32u && attribute + 1u > needed) needed = attribute + 1u;
};
for (const auto& block : program.Blocks()) {
for (const IrValue* inst : block->Instructions()) {
switch (inst->Opcode()) {
case IrOpcode::GetAttribute:
case IrOpcode::GetInterpolationParameter:
case IrOpcode::GetInterpolationParameterF16:
case IrOpcode::InterpolateHostP1:
case IrOpcode::InterpolateHostP2: {
const IrValue* attribute = inst->Argument(0)->Resolve();
if (attribute->HasImmediate() && attribute->Type() == IrType::U32) consider(attribute->ImmediateU32());
break;
}
default:
break;
}
}
}
if (needed <= pixel.inputNum) return;
for (std::uint32_t input = pixel.inputNum; input < needed; ++input) {
pixel.interpolatorSettings[input] = 0x20u;
}
pixel.inputNum = needed;
}

void ValidateValueReferences(const IrProgram& program, ShaderStageInputInfo inputInfo) {
for (const auto& block : program.Blocks()) {
for (const IrValue* inst : block->Instructions()) {
Expand Down Expand Up @@ -549,6 +578,9 @@ void ShaderInfoCollector::Collect(IrProgram& program, const ShaderStageInputInfo
if (!program.Resources().resourceTrackingComplete || program.Metadata().shaderInfoComplete) {
return Fail(!program.Resources().resourceTrackingComplete ? "shader resources were not tracked" : "shader info already collected");
}
if (program.Resources().stage == IrShaderStage::Pixel && inputInfo.pixel != nullptr) {
EnsurePixelInterpolatorCoverage(program, const_cast<ShaderPixelInputInfo&>(*inputInfo.pixel));
}
ValidateOptions(program, inputInfo);
ValidateValueReferences(program, inputInfo);
auto next = program.Info();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,15 @@ std::uint32_t EmitAttributeValue(SpirvEmitterState& state, std::uint32_t attr, s
}

std::uint32_t EmitInterpolationParameterValue(SpirvEmitterState& state, std::uint32_t attr, std::uint32_t chan, std::uint32_t mode) {
if (PixelParameterIsDefault(state, attr)) {
return mode == 2u ? EmitAttributeValue(state, attr, chan) : ConstantU32(state, 0u);
if (state.inputInfo.pixel == nullptr) {
throw std::runtime_error("interpolation parameter refers to an undefined attribute");
}
if (attr >= state.inputInfo.pixel->inputNum || PixelParameterIsDefault(state, attr)) {
return mode == 2u ? ConstantU32(state, state.inputInfo.pixel->InputDefaultBits(attr, chan & 3u)) : ConstantU32(state, 0u);
}
const auto* input = SpirvInputBindingForParameter(state, attr);
if (input == nullptr) {
throw std::runtime_error("interpolation parameter refers to an undefined attribute");
return mode == 2u ? ConstantU32(state, state.inputInfo.pixel->InputDefaultBits(attr, chan & 3u)) : ConstantU32(state, 0u);
}
if (!input->perVertex) {
return EmitAttributeValue(state, attr, chan);
Expand Down
2 changes: 1 addition & 1 deletion docs/dev/debt/shader.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@

### Functional

- [Pixel interpolators past `SPI_PS_IN_CONTROL.NUM_INTERP`](../../../core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp) (shader recompiler) - when the IR reads an attribute at or past the guest interpolator count, the missing `SPI_PS_INPUT_CNTL` slots are filled as DEFAULT (`OFFSET` 0x20, constant 0/1) before info collection, and SPIR-V emission of `GetInterpolationParameter` for a missing binding returns the same defaults instead of throwing, matching Mesa's treatment of a PS input with no VS output. Component and mode bounds still throw. A retail title prepares a pixel shader with `NUM_INTERP` 0 while it still issues `v_interp` on attribute 0 (`undefined attribute` after the collector fill alone). Console behaviour for that pairing is not measured.
- [Windows cache housekeeping](../../../core/shader/recompiler/ShaderDiskCache.cpp): eviction of old versions and temporary files is skipped for `\\?\` and `\\.\` cache roots because the supported WinLibs directory iterator can enumerate the process working directory for long prefixed paths. The active version's `last-used` stamp update still runs; it fails for long `\\.\` aliases with the supported toolchain. Ordinary Windows directory enumeration remains subject to MAX_PATH.

- [f64 compares](../../../core/shader/recompiler/Translation/src/CompareInstructions.cpp) flush denormal inputs to signed zero when bit 0 of the `FLOAT_MODE` f16/f64 denormal field is clear (modes 0 and 2); modes 1 and 3 and an unknown mode keep them. This follows the RDNA2 ISA section 6.4 and LLVM's sign-preserving AMDGPU denormal modes. The gfx1036 audit in [#981](https://github.com/boykopovar/AnyPS5/issues/981#issuecomment-6070046381) reports mode-sensitive `VopcCompareF64` results. Class compares still inspect the original bits. The full four-mode matrix has not been newly measured on AMD hardware or the PS5's gfx1013.

- [Unlinked calls](../../../core/shader/recompiler/ControlFlow/src/GraphBuilder.cpp) - the `s_swappc_b64` a table call's compare chain falls back to, when its target matches no linked copy, records the `UnlinkedCall` fault with the target and the program counter and stops the invocation, or continues after the call in programs whose faults do not stop invocations (see `bdaStopsInvocations`). The driver lists those calls in the request (`linkedCallFallbacks`), and it reports the fault like a BDA fault, naming the target: a dispatch that checks its faults when it completes throws, one whose check runs in a deferred write-back logs it and continues. The hardware would jump to the target, which is not modelled. Any other `s_swappc_b64` whose target is not resolved still throws when the shader is compiled.
- [Cached source IR](../../../core/shader/recompiler/Recompiler.cpp) releases lazily allocated SSA construction tables after resource preparation and reclaims values unreachable from instructions, metadata and resource plans when retaining a pending source program. Immediate shader preparation skips reclamation because compilation consumes the program. Reclamation must account for every new IR pointer owner; a remaining external use throws. Live IDs are retained. Source and variant entries remain cached without an eviction bound. A pending source program is released after its first variant is obtained; memory beyond the measured title checkpoint and Linux performance remain unmeasured.
- [Vertex user-data tail jumps](../../../core/shader/recompiler/ControlFlow/src/GraphBuilder.cpp) link a linear raw vertex prolog's terminal `s_setpc_b64` through an unchanged user-data pair to its continuation. The instruction semantics follow AMD RDNA2 ISA section 12.3; the prolog convention follows Mesa `aco_select_vs_prolog.cpp` at `0b2d6d1a61d9bbf89cf8beb88a696144f67c61f8`. Console hardware has not been measured. Branching prologs, computed low pointers and continuations reading `s_getpc_b64` remain unsupported
Expand Down
Loading