[spirv-out] Fix adding illegal decorators on fragment outputs. (#2286)

* [spirv-out] Fix adding illegal decorators on fragment outputs.

Furthermore, fix allowing to add `Centroid` and `Sample` decorator to vertex inputs.
Fixes #2270

* Add test for fragment outputs

* Fix fragment-output.wgsl test using more than 8 outputs in a single shader
Breaks HLSL & MSL validation

* formatting
This commit is contained in:
Andreas Reich 2023-03-20 13:43:13 +01:00 committed by GitHub
parent 6db8da72ca
commit 67c081bebe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 558 additions and 20 deletions

View File

@ -1407,12 +1407,17 @@ impl Writer {
} => {
self.decorate(id, Decoration::Location, &[location]);
// The Vulkan spec says: VUID-StandaloneSpirv-Flat-06202
//
// > The Flat, NoPerspective, Sample, and Centroid decorations
// > must not be used on variables with the Input storage class in
// > a vertex shader
if class != spirv::StorageClass::Input || stage != crate::ShaderStage::Vertex {
let no_decorations =
// VUID-StandaloneSpirv-Flat-06202
// > The Flat, NoPerspective, Sample, and Centroid decorations
// > must not be used on variables with the Input storage class in a vertex shader
(class == spirv::StorageClass::Input && stage == crate::ShaderStage::Vertex) ||
// VUID-StandaloneSpirv-Flat-06201
// > The Flat, NoPerspective, Sample, and Centroid decorations
// > must not be used on variables with the Output storage class in a fragment shader
(class == spirv::StorageClass::Output && stage == crate::ShaderStage::Fragment);
if !no_decorations {
match interpolation {
// Perspective-correct interpolation is the default in SPIR-V.
None | Some(crate::Interpolation::Perspective) => (),
@ -1423,20 +1428,19 @@ impl Writer {
self.decorate(id, Decoration::NoPerspective, &[]);
}
}
}
match sampling {
// Center sampling is the default in SPIR-V.
None | Some(crate::Sampling::Center) => (),
Some(crate::Sampling::Centroid) => {
self.decorate(id, Decoration::Centroid, &[]);
}
Some(crate::Sampling::Sample) => {
self.require_any(
"per-sample interpolation",
&[spirv::Capability::SampleRateShading],
)?;
self.decorate(id, Decoration::Sample, &[]);
match sampling {
// Center sampling is the default in SPIR-V.
None | Some(crate::Sampling::Center) => (),
Some(crate::Sampling::Centroid) => {
self.decorate(id, Decoration::Centroid, &[]);
}
Some(crate::Sampling::Sample) => {
self.require_any(
"per-sample interpolation",
&[spirv::Capability::SampleRateShading],
)?;
self.decorate(id, Decoration::Sample, &[]);
}
}
}
}

View File

@ -0,0 +1,41 @@
// Split up because some output languages limit number of locations to 8.
struct FragmentOutputVec4Vec3 {
@location(0) vec4f: vec4<f32>,
@location(1) vec4i: vec4<i32>,
@location(2) vec4u: vec4<u32>,
@location(3) vec3f: vec3<f32>,
@location(4) vec3i: vec3<i32>,
@location(5) vec3u: vec3<u32>,
}
@fragment
fn main_vec4vec3() -> FragmentOutputVec4Vec3 {
var output: FragmentOutputVec4Vec3;
output.vec4f = vec4<f32>(0.0);
output.vec4i = vec4<i32>(0);
output.vec4u = vec4<u32>(0u);
output.vec3f = vec3<f32>(0.0);
output.vec3i = vec3<i32>(0);
output.vec3u = vec3<u32>(0u);
return output;
}
struct FragmentOutputVec2Scalar {
@location(0) vec2f: vec2<f32>,
@location(1) vec2i: vec2<i32>,
@location(2) vec2u: vec2<u32>,
@location(3) scalarf: f32,
@location(4) scalari: i32,
@location(5) scalaru: u32,
}
@fragment
fn main_vec2scalar() -> FragmentOutputVec2Scalar {
var output: FragmentOutputVec2Scalar;
output.vec2f = vec2<f32>(0.0);
output.vec2i = vec2<i32>(0);
output.vec2u = vec2<u32>(0u);
output.scalarf = 0.0;
output.scalari = 0;
output.scalaru = 0u;
return output;
}

View File

@ -0,0 +1,62 @@
#version 310 es
precision highp float;
precision highp int;
struct FragmentOutput {
vec4 vec4f;
ivec4 vec4i;
uvec4 vec4u;
vec3 vec3f;
ivec3 vec3i;
uvec3 vec3u;
vec2 vec2f;
ivec2 vec2i;
uvec2 vec2u;
float scalarf;
int scalari;
uint scalaru;
};
layout(location = 0) out vec4 _fs2p_location0;
layout(location = 1) out ivec4 _fs2p_location1;
layout(location = 2) out uvec4 _fs2p_location2;
layout(location = 3) out vec3 _fs2p_location3;
layout(location = 4) out ivec3 _fs2p_location4;
layout(location = 5) out uvec3 _fs2p_location5;
layout(location = 6) out vec2 _fs2p_location6;
layout(location = 7) out ivec2 _fs2p_location7;
layout(location = 8) out uvec2 _fs2p_location8;
layout(location = 9) out float _fs2p_location9;
layout(location = 10) out int _fs2p_location10;
layout(location = 11) out uint _fs2p_location11;
void main() {
FragmentOutput output_ = FragmentOutput(vec4(0.0), ivec4(0), uvec4(0u), vec3(0.0), ivec3(0), uvec3(0u), vec2(0.0), ivec2(0), uvec2(0u), 0.0, 0, 0u);
output_.vec4f = vec4(0.0);
output_.vec4i = ivec4(0);
output_.vec4u = uvec4(0u);
output_.vec3f = vec3(0.0);
output_.vec3i = ivec3(0);
output_.vec3u = uvec3(0u);
output_.vec2f = vec2(0.0);
output_.vec2i = ivec2(0);
output_.vec2u = uvec2(0u);
output_.scalarf = 0.0;
output_.scalari = 0;
output_.scalaru = 0u;
FragmentOutput _e34 = output_;
_fs2p_location0 = _e34.vec4f;
_fs2p_location1 = _e34.vec4i;
_fs2p_location2 = _e34.vec4u;
_fs2p_location3 = _e34.vec3f;
_fs2p_location4 = _e34.vec3i;
_fs2p_location5 = _e34.vec3u;
_fs2p_location6 = _e34.vec2f;
_fs2p_location7 = _e34.vec2i;
_fs2p_location8 = _e34.vec2u;
_fs2p_location9 = _e34.scalarf;
_fs2p_location10 = _e34.scalari;
_fs2p_location11 = _e34.scalaru;
return;
}

View File

@ -0,0 +1,46 @@
#version 310 es
precision highp float;
precision highp int;
struct FragmentOutputVec4Vec3_ {
vec4 vec4f;
ivec4 vec4i;
uvec4 vec4u;
vec3 vec3f;
ivec3 vec3i;
uvec3 vec3u;
};
struct FragmentOutputVec2Scalar {
vec2 vec2f;
ivec2 vec2i;
uvec2 vec2u;
float scalarf;
int scalari;
uint scalaru;
};
layout(location = 0) out vec2 _fs2p_location0;
layout(location = 1) out ivec2 _fs2p_location1;
layout(location = 2) out uvec2 _fs2p_location2;
layout(location = 3) out float _fs2p_location3;
layout(location = 4) out int _fs2p_location4;
layout(location = 5) out uint _fs2p_location5;
void main() {
FragmentOutputVec2Scalar output_1 = FragmentOutputVec2Scalar(vec2(0.0), ivec2(0), uvec2(0u), 0.0, 0, 0u);
output_1.vec2f = vec2(0.0);
output_1.vec2i = ivec2(0);
output_1.vec2u = uvec2(0u);
output_1.scalarf = 0.0;
output_1.scalari = 0;
output_1.scalaru = 0u;
FragmentOutputVec2Scalar _e16 = output_1;
_fs2p_location0 = _e16.vec2f;
_fs2p_location1 = _e16.vec2i;
_fs2p_location2 = _e16.vec2u;
_fs2p_location3 = _e16.scalarf;
_fs2p_location4 = _e16.scalari;
_fs2p_location5 = _e16.scalaru;
return;
}

View File

@ -0,0 +1,46 @@
#version 310 es
precision highp float;
precision highp int;
struct FragmentOutputVec4Vec3_ {
vec4 vec4f;
ivec4 vec4i;
uvec4 vec4u;
vec3 vec3f;
ivec3 vec3i;
uvec3 vec3u;
};
struct FragmentOutputVec2Scalar {
vec2 vec2f;
ivec2 vec2i;
uvec2 vec2u;
float scalarf;
int scalari;
uint scalaru;
};
layout(location = 0) out vec4 _fs2p_location0;
layout(location = 1) out ivec4 _fs2p_location1;
layout(location = 2) out uvec4 _fs2p_location2;
layout(location = 3) out vec3 _fs2p_location3;
layout(location = 4) out ivec3 _fs2p_location4;
layout(location = 5) out uvec3 _fs2p_location5;
void main() {
FragmentOutputVec4Vec3_ output_ = FragmentOutputVec4Vec3_(vec4(0.0), ivec4(0), uvec4(0u), vec3(0.0), ivec3(0), uvec3(0u));
output_.vec4f = vec4(0.0);
output_.vec4i = ivec4(0);
output_.vec4u = uvec4(0u);
output_.vec3f = vec3(0.0);
output_.vec3i = ivec3(0);
output_.vec3u = uvec3(0u);
FragmentOutputVec4Vec3_ _e19 = output_;
_fs2p_location0 = _e19.vec4f;
_fs2p_location1 = _e19.vec4i;
_fs2p_location2 = _e19.vec4u;
_fs2p_location3 = _e19.vec3f;
_fs2p_location4 = _e19.vec3i;
_fs2p_location5 = _e19.vec3u;
return;
}

View File

@ -0,0 +1,48 @@
struct FragmentOutputVec4Vec3_ {
float4 vec4f : SV_Target0;
nointerpolation int4 vec4i : SV_Target1;
nointerpolation uint4 vec4u : SV_Target2;
float3 vec3f : SV_Target3;
nointerpolation int3 vec3i : SV_Target4;
nointerpolation uint3 vec3u : SV_Target5;
};
struct FragmentOutputVec2Scalar {
float2 vec2f : SV_Target0;
nointerpolation int2 vec2i : SV_Target1;
nointerpolation uint2 vec2u : SV_Target2;
float scalarf : SV_Target3;
nointerpolation int scalari : SV_Target4;
nointerpolation uint scalaru : SV_Target5;
};
FragmentOutputVec4Vec3_ main_vec4vec3_()
{
FragmentOutputVec4Vec3_ output = (FragmentOutputVec4Vec3_)0;
output.vec4f = (0.0).xxxx;
output.vec4i = (0).xxxx;
output.vec4u = (0u).xxxx;
output.vec3f = (0.0).xxx;
output.vec3i = (0).xxx;
output.vec3u = (0u).xxx;
FragmentOutputVec4Vec3_ _expr19 = output;
const FragmentOutputVec4Vec3_ fragmentoutputvec4vec3_ = _expr19;
return fragmentoutputvec4vec3_;
}
FragmentOutputVec2Scalar main_vec2scalar()
{
FragmentOutputVec2Scalar output_1 = (FragmentOutputVec2Scalar)0;
output_1.vec2f = (0.0).xx;
output_1.vec2i = (0).xx;
output_1.vec2u = (0u).xx;
output_1.scalarf = 0.0;
output_1.scalari = 0;
output_1.scalaru = 0u;
FragmentOutputVec2Scalar _expr16 = output_1;
const FragmentOutputVec2Scalar fragmentoutputvec2scalar = _expr16;
return fragmentoutputvec2scalar;
}

View File

@ -0,0 +1,3 @@
vertex=()
fragment=(main_vec4vec3_:ps_5_1 main_vec2scalar:ps_5_1 )
compute=()

View File

@ -0,0 +1,67 @@
// language: metal2.0
#include <metal_stdlib>
#include <simd/simd.h>
using metal::uint;
struct FragmentOutputVec4Vec3_ {
metal::float4 vec4f;
metal::int4 vec4i;
metal::uint4 vec4u;
metal::float3 vec3f;
metal::int3 vec3i;
metal::uint3 vec3u;
};
struct FragmentOutputVec2Scalar {
metal::float2 vec2f;
metal::int2 vec2i;
metal::uint2 vec2u;
float scalarf;
int scalari;
uint scalaru;
};
struct main_vec4vec3_Output {
metal::float4 vec4f [[color(0)]];
metal::int4 vec4i [[color(1)]];
metal::uint4 vec4u [[color(2)]];
metal::float3 vec3f [[color(3)]];
metal::int3 vec3i [[color(4)]];
metal::uint3 vec3u [[color(5)]];
};
fragment main_vec4vec3_Output main_vec4vec3_(
) {
FragmentOutputVec4Vec3_ output = {};
output.vec4f = metal::float4(0.0);
output.vec4i = metal::int4(0);
output.vec4u = metal::uint4(0u);
output.vec3f = metal::float3(0.0);
output.vec3i = metal::int3(0);
output.vec3u = metal::uint3(0u);
FragmentOutputVec4Vec3_ _e19 = output;
const auto _tmp = _e19;
return main_vec4vec3_Output { _tmp.vec4f, _tmp.vec4i, _tmp.vec4u, _tmp.vec3f, _tmp.vec3i, _tmp.vec3u };
}
struct main_vec2scalarOutput {
metal::float2 vec2f [[color(0)]];
metal::int2 vec2i [[color(1)]];
metal::uint2 vec2u [[color(2)]];
float scalarf [[color(3)]];
int scalari [[color(4)]];
uint scalaru [[color(5)]];
};
fragment main_vec2scalarOutput main_vec2scalar(
) {
FragmentOutputVec2Scalar output_1 = {};
output_1.vec2f = metal::float2(0.0);
output_1.vec2i = metal::int2(0);
output_1.vec2u = metal::uint2(0u);
output_1.scalarf = 0.0;
output_1.scalari = 0;
output_1.scalaru = 0u;
FragmentOutputVec2Scalar _e16 = output_1;
const auto _tmp = _e16;
return main_vec2scalarOutput { _tmp.vec2f, _tmp.vec2i, _tmp.vec2u, _tmp.scalarf, _tmp.scalari, _tmp.scalaru };
}

View File

@ -0,0 +1,172 @@
; SPIR-V
; Version: 1.1
; Generator: rspirv
; Bound: 109
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %36 "main_vec4vec3" %24 %26 %28 %30 %32 %34
OpEntryPoint Fragment %85 "main_vec2scalar" %73 %75 %77 %79 %81 %83
OpExecutionMode %36 OriginUpperLeft
OpExecutionMode %85 OriginUpperLeft
OpMemberDecorate %15 0 Offset 0
OpMemberDecorate %15 1 Offset 16
OpMemberDecorate %15 2 Offset 32
OpMemberDecorate %15 3 Offset 48
OpMemberDecorate %15 4 Offset 64
OpMemberDecorate %15 5 Offset 80
OpMemberDecorate %19 0 Offset 0
OpMemberDecorate %19 1 Offset 8
OpMemberDecorate %19 2 Offset 16
OpMemberDecorate %19 3 Offset 24
OpMemberDecorate %19 4 Offset 28
OpMemberDecorate %19 5 Offset 32
OpDecorate %24 Location 0
OpDecorate %26 Location 1
OpDecorate %28 Location 2
OpDecorate %30 Location 3
OpDecorate %32 Location 4
OpDecorate %34 Location 5
OpDecorate %73 Location 0
OpDecorate %75 Location 1
OpDecorate %77 Location 2
OpDecorate %79 Location 3
OpDecorate %81 Location 4
OpDecorate %83 Location 5
%2 = OpTypeVoid
%4 = OpTypeFloat 32
%3 = OpConstant %4 0.0
%6 = OpTypeInt 32 1
%5 = OpConstant %6 0
%8 = OpTypeInt 32 0
%7 = OpConstant %8 0
%9 = OpTypeVector %4 4
%10 = OpTypeVector %6 4
%11 = OpTypeVector %8 4
%12 = OpTypeVector %4 3
%13 = OpTypeVector %6 3
%14 = OpTypeVector %8 3
%15 = OpTypeStruct %9 %10 %11 %12 %13 %14
%16 = OpTypeVector %4 2
%17 = OpTypeVector %6 2
%18 = OpTypeVector %8 2
%19 = OpTypeStruct %16 %17 %18 %4 %6 %8
%21 = OpTypePointer Function %15
%22 = OpConstantNull %15
%25 = OpTypePointer Output %9
%24 = OpVariable %25 Output
%27 = OpTypePointer Output %10
%26 = OpVariable %27 Output
%29 = OpTypePointer Output %11
%28 = OpVariable %29 Output
%31 = OpTypePointer Output %12
%30 = OpVariable %31 Output
%33 = OpTypePointer Output %13
%32 = OpVariable %33 Output
%35 = OpTypePointer Output %14
%34 = OpVariable %35 Output
%37 = OpTypeFunction %2
%39 = OpTypePointer Function %9
%42 = OpTypePointer Function %10
%44 = OpConstant %8 1
%46 = OpTypePointer Function %11
%48 = OpConstant %8 2
%50 = OpTypePointer Function %12
%52 = OpConstant %8 3
%54 = OpTypePointer Function %13
%56 = OpConstant %8 4
%58 = OpTypePointer Function %14
%60 = OpConstant %8 5
%70 = OpTypePointer Function %19
%71 = OpConstantNull %19
%74 = OpTypePointer Output %16
%73 = OpVariable %74 Output
%76 = OpTypePointer Output %17
%75 = OpVariable %76 Output
%78 = OpTypePointer Output %18
%77 = OpVariable %78 Output
%80 = OpTypePointer Output %4
%79 = OpVariable %80 Output
%82 = OpTypePointer Output %6
%81 = OpVariable %82 Output
%84 = OpTypePointer Output %8
%83 = OpVariable %84 Output
%87 = OpTypePointer Function %16
%90 = OpTypePointer Function %17
%93 = OpTypePointer Function %18
%96 = OpTypePointer Function %4
%98 = OpTypePointer Function %6
%100 = OpTypePointer Function %8
%36 = OpFunction %2 None %37
%23 = OpLabel
%20 = OpVariable %21 Function %22
OpBranch %38
%38 = OpLabel
%40 = OpCompositeConstruct %9 %3 %3 %3 %3
%41 = OpAccessChain %39 %20 %7
OpStore %41 %40
%43 = OpCompositeConstruct %10 %5 %5 %5 %5
%45 = OpAccessChain %42 %20 %44
OpStore %45 %43
%47 = OpCompositeConstruct %11 %7 %7 %7 %7
%49 = OpAccessChain %46 %20 %48
OpStore %49 %47
%51 = OpCompositeConstruct %12 %3 %3 %3
%53 = OpAccessChain %50 %20 %52
OpStore %53 %51
%55 = OpCompositeConstruct %13 %5 %5 %5
%57 = OpAccessChain %54 %20 %56
OpStore %57 %55
%59 = OpCompositeConstruct %14 %7 %7 %7
%61 = OpAccessChain %58 %20 %60
OpStore %61 %59
%62 = OpLoad %15 %20
%63 = OpCompositeExtract %9 %62 0
OpStore %24 %63
%64 = OpCompositeExtract %10 %62 1
OpStore %26 %64
%65 = OpCompositeExtract %11 %62 2
OpStore %28 %65
%66 = OpCompositeExtract %12 %62 3
OpStore %30 %66
%67 = OpCompositeExtract %13 %62 4
OpStore %32 %67
%68 = OpCompositeExtract %14 %62 5
OpStore %34 %68
OpReturn
OpFunctionEnd
%85 = OpFunction %2 None %37
%72 = OpLabel
%69 = OpVariable %70 Function %71
OpBranch %86
%86 = OpLabel
%88 = OpCompositeConstruct %16 %3 %3
%89 = OpAccessChain %87 %69 %7
OpStore %89 %88
%91 = OpCompositeConstruct %17 %5 %5
%92 = OpAccessChain %90 %69 %44
OpStore %92 %91
%94 = OpCompositeConstruct %18 %7 %7
%95 = OpAccessChain %93 %69 %48
OpStore %95 %94
%97 = OpAccessChain %96 %69 %52
OpStore %97 %3
%99 = OpAccessChain %98 %69 %56
OpStore %99 %5
%101 = OpAccessChain %100 %69 %60
OpStore %101 %7
%102 = OpLoad %19 %69
%103 = OpCompositeExtract %16 %102 0
OpStore %73 %103
%104 = OpCompositeExtract %17 %102 1
OpStore %75 %104
%105 = OpCompositeExtract %18 %102 2
OpStore %77 %105
%106 = OpCompositeExtract %4 %102 3
OpStore %79 %106
%107 = OpCompositeExtract %6 %102 4
OpStore %81 %107
%108 = OpCompositeExtract %8 %102 5
OpStore %83 %108
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,45 @@
struct FragmentOutputVec4Vec3_ {
@location(0) vec4f: vec4<f32>,
@location(1) vec4i: vec4<i32>,
@location(2) vec4u: vec4<u32>,
@location(3) vec3f: vec3<f32>,
@location(4) vec3i: vec3<i32>,
@location(5) vec3u: vec3<u32>,
}
struct FragmentOutputVec2Scalar {
@location(0) vec2f: vec2<f32>,
@location(1) vec2i: vec2<i32>,
@location(2) vec2u: vec2<u32>,
@location(3) scalarf: f32,
@location(4) scalari: i32,
@location(5) scalaru: u32,
}
@fragment
fn main_vec4vec3_() -> FragmentOutputVec4Vec3_ {
var output: FragmentOutputVec4Vec3_;
output.vec4f = vec4<f32>(0.0);
output.vec4i = vec4<i32>(0);
output.vec4u = vec4<u32>(0u);
output.vec3f = vec3<f32>(0.0);
output.vec3i = vec3<i32>(0);
output.vec3u = vec3<u32>(0u);
let _e19 = output;
return _e19;
}
@fragment
fn main_vec2scalar() -> FragmentOutputVec2Scalar {
var output_1: FragmentOutputVec2Scalar;
output_1.vec2f = vec2<f32>(0.0);
output_1.vec2i = vec2<i32>(0);
output_1.vec2u = vec2<u32>(0u);
output_1.scalarf = 0.0;
output_1.scalari = 0;
output_1.scalaru = 0u;
let _e16 = output_1;
return _e16;
}

View File

@ -482,6 +482,10 @@ fn convert_wgsl() {
"functions",
Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL,
),
(
"fragment-output",
Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL,
),
("functions-webgl", Targets::GLSL),
(
"interpolate",