Skip to content

Commit

Permalink
[spirv-out] Fix adding illegal decorators on fragment outputs. (#2286)
Browse files Browse the repository at this point in the history
* [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
  • Loading branch information
Wumpf authored Mar 20, 2023
1 parent 6db8da7 commit 67c081b
Show file tree
Hide file tree
Showing 11 changed files with 558 additions and 20 deletions.
44 changes: 24 additions & 20 deletions src/back/spv/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => (),
Expand All @@ -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, &[]);
}
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions tests/in/fragment-output.wgsl
Original file line number Diff line number Diff line change
@@ -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;
}
62 changes: 62 additions & 0 deletions tests/out/glsl/fragment-output.main.Fragment.glsl
Original file line number Diff line number Diff line change
@@ -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;
}

46 changes: 46 additions & 0 deletions tests/out/glsl/fragment-output.main_vec2scalar.Fragment.glsl
Original file line number Diff line number Diff line change
@@ -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;
}

46 changes: 46 additions & 0 deletions tests/out/glsl/fragment-output.main_vec4vec3.Fragment.glsl
Original file line number Diff line number Diff line change
@@ -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;
}

48 changes: 48 additions & 0 deletions tests/out/hlsl/fragment-output.hlsl
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 3 additions & 0 deletions tests/out/hlsl/fragment-output.hlsl.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
vertex=()
fragment=(main_vec4vec3_:ps_5_1 main_vec2scalar:ps_5_1 )
compute=()
67 changes: 67 additions & 0 deletions tests/out/msl/fragment-output.msl
Original file line number Diff line number Diff line change
@@ -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 };
}
Loading

0 comments on commit 67c081b

Please sign in to comment.