fix(gpu): one vertex attribute per guest stream view (#718)

The scalar evaluator gave every buffer_load_format instruction its own
attribute location. Two things multiply those: the CFG walk visits one
instruction on several paths, and an uber vertex shader fetches the same
stream from every material branch. UE's larger shaders reached 56 bindings
from 8 distinct views, and one reached 701 from 5.

Metal caps a vertex function at 31 attributes, so MoltenVK failed the MSL
compile with "'attribute' attribute parameter is out of bounds" and the
surrounding vkCreateGraphicsPipelines returned ErrorInitializationFailed.
Every draw using those pipelines was dropped, which is why Silent Hill:
The Short Message rendered a black scene. The vertex buffer count drove
Metal's buffer indices out of range too, giving the companion
"cannot reserve 'buffer' resource location at index 0" failures.

Key attributes by the guest stream view they read - absolute element
address, record stride and format - and alias every other fetch that
resolves to the same view onto that binding, so both translators map
those instruction PCs to one input variable. On PPSA10112 this takes the
worst shader from 56 attributes to 8 and pipeline failures from 840 to 0.
This commit is contained in:
kuba
2026-07-31 11:12:26 +02:00
committed by GitHub
parent e1695cf87f
commit eb0653eded
5 changed files with 205 additions and 9 deletions
+8 -1
View File
@@ -302,6 +302,12 @@ public sealed record Gen5GlobalMemoryBinding(
public bool WriteBackToGuest { get; set; } = true;
}
// One attribute per distinct guest stream view. AliasPcs carries the other
// fetch instructions that read the same view: uber-shaders fetch a stream from
// every material branch, and the scalar evaluator visits one instruction on
// several CFG paths. Both must resolve to this binding's single location,
// because Metal caps a vertex function at 31 attributes and one location per
// fetch instruction overruns that on UE's larger vertex shaders.
public sealed record Gen5VertexInputBinding(
uint Pc,
uint Location,
@@ -314,7 +320,8 @@ public sealed record Gen5VertexInputBinding(
byte[] Data,
int DataLength,
bool DataPooled,
bool PerInstance = false);
bool PerInstance = false,
IReadOnlyList<uint>? AliasPcs = null);
public sealed record Gen5ShaderEvaluation(
IReadOnlyList<uint> InitialScalarRegisters,