[SpirvTranslator] Use a flat input for v_interp_mov P0 on MoltenVK (#910)
MoltenVK advertises VK_KHR_fragment_shader_barycentric, but SPIRV-Cross cannot translate PerVertexKHR inputs to MSL, so every pixel shader with v_interp_mov_f32 failed pipeline creation on macOS (Poppy Playtime). Reading only P0 is the provoking vertex value, which a flat input gives. Attributes that read P10 or P20 still use per-vertex inputs.
This commit is contained in:
parent
155add2c10
commit
9073974ba8
|
|
@ -38,6 +38,8 @@ internal interface IShaderPipelineHost
|
|||
|
||||
bool GraphicsSubgroupOperationsEnabled { get; }
|
||||
|
||||
bool PerVertexPixelInputsSupported => true;
|
||||
|
||||
RenderHostLimits Limits { get; }
|
||||
|
||||
// The sample counts a pipeline without attachments can rasterize at.
|
||||
|
|
|
|||
|
|
@ -560,6 +560,7 @@ internal sealed class ShaderProgramCache
|
|||
PixelOutputs = options.PixelOutputs,
|
||||
PixelInputEnable = options.PixelInputEnable,
|
||||
PixelCustomInterpolationMask = info.CustomInterpolationMask,
|
||||
SupportsPerVertexPixelInputs = _host.PerVertexPixelInputsSupported,
|
||||
PixelInputAddress = options.PixelInputAddress,
|
||||
PixelInputCntl = interpolators,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -645,6 +645,7 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
}
|
||||
|
||||
private bool _supportsFragmentShaderBarycentric;
|
||||
private bool _supportsPerVertexPixelInputs;
|
||||
private const string FragmentShaderBarycentricExtensionName = "VK_KHR_fragment_shader_barycentric";
|
||||
|
||||
private void CreateDevice()
|
||||
|
|
@ -742,6 +743,10 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
_supportsFragmentShaderBarycentric = barycentricFeatures.FragmentShaderBarycentric;
|
||||
}
|
||||
|
||||
// MoltenVK exposes the barycentric builtins, but SPIRV-Cross rejects PerVertexKHR inputs.
|
||||
_supportsPerVertexPixelInputs = _supportsFragmentShaderBarycentric &&
|
||||
!IsDeviceExtensionAvailable(PortabilitySubsetExtensionName);
|
||||
|
||||
var depthClipEnableFeatures = new PhysicalDeviceDepthClipEnableFeaturesEXT
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceDepthClipEnableFeaturesExt,
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
|
||||
bool IShaderPipelineHost.GraphicsSubgroupOperationsEnabled => GraphicsSubgroupOperationsEnabled;
|
||||
|
||||
bool IShaderPipelineHost.PerVertexPixelInputsSupported => _supportsPerVertexPixelInputs;
|
||||
|
||||
RenderHostLimits IShaderPipelineHost.Limits => _renderHostLimits;
|
||||
|
||||
SampleCountFlags IShaderPipelineHost.NoAttachmentSampleCounts => _noAttachmentSampleCounts;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ public static partial class Gen5SpirvTranslator
|
|||
private sealed partial class CompilationContext
|
||||
{
|
||||
private readonly HashSet<uint> _perVertexAttributes = [];
|
||||
private readonly HashSet<uint> _flatParameterAttributes = [];
|
||||
private readonly Dictionary<int, uint> _barycentricInputs = [];
|
||||
private uint _interpolationSampleId;
|
||||
private const uint InterpolateAtCentroid = 76;
|
||||
|
|
@ -27,6 +28,23 @@ public static partial class Gen5SpirvTranslator
|
|||
}
|
||||
}
|
||||
|
||||
if (!_request.SupportsPerVertexPixelInputs)
|
||||
{
|
||||
// Reading P0 alone is the provoking vertex value, which a flat input gives.
|
||||
// Other parameters still need the per-vertex values.
|
||||
foreach (var attribute in _perVertexAttributes.ToArray())
|
||||
{
|
||||
if (_request.Program.Instructions.All(instruction =>
|
||||
instruction.Control is not Gen5InterpolationControl control ||
|
||||
control.Attribute != attribute ||
|
||||
(instruction.Opcode == "VInterpMovF32" && (instruction.Words[0] & 0xFFu) == 2)))
|
||||
{
|
||||
_perVertexAttributes.Remove(attribute);
|
||||
_flatParameterAttributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_perVertexAttributes.Count == 0)
|
||||
{
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -873,7 +873,7 @@ public static partial class Gen5SpirvTranslator
|
|||
{
|
||||
_module.AddDecoration(variable, SpirvDecoration.PerVertexKhr);
|
||||
}
|
||||
else if ((cntl & 0x400u) != 0)
|
||||
else if ((cntl & 0x400u) != 0 || _flatParameterAttributes.Contains(attribute))
|
||||
{
|
||||
_module.AddDecoration(variable, SpirvDecoration.Flat);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,10 @@ public sealed class ShaderCompileRequest
|
|||
public IReadOnlyList<Gen5PixelOutputBinding> PixelOutputs { get; init; } = [];
|
||||
public uint PixelInputEnable { get; init; }
|
||||
public uint PixelCustomInterpolationMask { get; init; }
|
||||
|
||||
// False when the device cannot read one vertex's value of a pixel input (PerVertexKHR).
|
||||
// MoltenVK advertises fragment shader barycentrics but cannot translate PerVertexKHR to MSL.
|
||||
public bool SupportsPerVertexPixelInputs { get; init; } = true;
|
||||
public uint PixelInputAddress { get; init; }
|
||||
public IReadOnlyList<uint>? PixelInputCntl { get; init; }
|
||||
|
||||
|
|
|
|||
|
|
@ -79,8 +79,37 @@ public sealed class Gen5InterpolationParameterTests
|
|||
Assert.Contains("Pull-model interpolation", error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProvokingVertexMove_UsesAFlatInputWithoutPerVertexSupport()
|
||||
{
|
||||
var request = Request(2, false, inputCntl: 0x1, supportsPerVertex: false);
|
||||
Assert.True(Gen5SpirvTranslator.TryCompileProgram(request, out var shader, out var error), error);
|
||||
var instructions = Instructions(shader.Spirv);
|
||||
Assert.DoesNotContain(instructions, instruction => instruction.Opcode == SpirvOp.Capability &&
|
||||
instruction.Operands[0] == (uint)SpirvCapability.FragmentBarycentricKhr);
|
||||
Assert.DoesNotContain(instructions, instruction => instruction.Opcode == SpirvOp.Decorate &&
|
||||
instruction.Operands[1] == (uint)SpirvDecoration.PerVertexKhr);
|
||||
var input = Assert.Single(instructions, instruction => instruction.Opcode == SpirvOp.Decorate &&
|
||||
instruction.Operands[1] == (uint)SpirvDecoration.Location).Operands[0];
|
||||
Assert.Contains(instructions, instruction => instruction.Opcode == SpirvOp.Decorate &&
|
||||
instruction.Operands[0] == input && instruction.Operands[1] == (uint)SpirvDecoration.Flat);
|
||||
ValidateWhenAvailable(shader.Spirv);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0u)]
|
||||
[InlineData(1u)]
|
||||
public void VertexDifferenceMove_KeepsPerVertexInputWithoutPerVertexSupport(uint selector)
|
||||
{
|
||||
var request = Request(selector, false, inputCntl: 0x1, supportsPerVertex: false);
|
||||
Assert.True(Gen5SpirvTranslator.TryCompileProgram(request, out var shader, out var error), error);
|
||||
Assert.Contains(Instructions(shader.Spirv), instruction => instruction.Opcode == SpirvOp.Decorate &&
|
||||
instruction.Operands[1] == (uint)SpirvDecoration.PerVertexKhr);
|
||||
}
|
||||
|
||||
private static ShaderCompileRequest Request(
|
||||
uint selector, bool custom, uint inputs = 2, string opcode = "VInterpMovF32")
|
||||
uint selector, bool custom, uint inputs = 2, string opcode = "VInterpMovF32",
|
||||
uint inputCntl = 0x401, bool supportsPerVertex = true)
|
||||
{
|
||||
var interpolation = new Gen5ShaderInstruction(0, Gen5ShaderEncoding.Vintrp, opcode,
|
||||
[selector], [Gen5Operand.Vector(selector)], [Gen5Operand.Vector(4)], new Gen5InterpolationControl(1, 2));
|
||||
|
|
@ -90,8 +119,9 @@ public sealed class Gen5InterpolationParameterTests
|
|||
{
|
||||
PixelInputAddress = inputs,
|
||||
PixelInputEnable = inputs,
|
||||
PixelInputCntl = [0, 0x401],
|
||||
PixelInputCntl = [0, inputCntl],
|
||||
PixelCustomInterpolationMask = custom ? 2u : 0u,
|
||||
SupportsPerVertexPixelInputs = supportsPerVertex,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue