Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/shaders.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:

naga-validate-macos:
name: "Validate: MSL"
runs-on: macos-15
runs-on: macos-26
steps:
- uses: actions/checkout@v6

Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ Bottom level categories:

### Added/New Features

#### General
- Add support for `per_vertex` in Metal and DX12, as well as some validation for `per_vertex`, and a new enable extension, `wgpu_per_vertex`. By @inner-daemons in [#9219](https://github.com/gfx-rs/wgpu/pull/9219).

#### Metal

- Unconditionally enable `Features::CLIP_DISTANCES`. By @ErichDonGubler in [#9270](https://github.com/gfx-rs/wgpu/pull/9270).
Expand Down
2 changes: 1 addition & 1 deletion naga/src/back/hlsl/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ impl crate::Interpolation {
Self::Perspective => None,
Self::Linear => Some("noperspective"),
Self::Flat => Some("nointerpolation"),
Self::PerVertex => unreachable!(),
Self::PerVertex => Some("nointerpolation"),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion naga/src/back/hlsl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ pub fn supported_capabilities() -> crate::valid::Capabilities {
| Caps::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING
| Caps::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
// No COOPERATIVE_MATRIX
// No PER_VERTEX
| Caps::PER_VERTEX
// No RAY_TRACING_PIPELINE
// No DRAW_INDEX
// No MEMORY_DECORATION_VOLATILE
Expand Down
17 changes: 17 additions & 0 deletions naga/src/back/hlsl/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,23 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
ep_input.local_invocation_index_name.as_ref().unwrap()
)?;
}
Some(crate::Binding::Location {
interpolation: Some(crate::Interpolation::PerVertex),
..
}) => {
if self.options.shader_model < ShaderModel::V6_1 {
return Err(Error::ShaderModelTooLow(
"per_vertex fragment inputs".to_string(),
ShaderModel::V6_1,
));
}
write!(
self.out,
"{{ GetAttributeAtVertex({0}.{1}, 0), GetAttributeAtVertex({0}.{1}, 1), GetAttributeAtVertex({0}.{1}, 2) }}",
ep_input.arg_name,
fake_member.name,
)?;
}
_ => {
write!(self.out, "{}.{}", ep_input.arg_name, fake_member.name)?;
}
Expand Down
7 changes: 6 additions & 1 deletion naga/src/back/msl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ enum ResolvedInterpolation {
SamplePerspective,
SampleNoPerspective,
Flat,
PerVertex,
}

// Note: some of these should be removed in favor of proper IR validation.
Expand Down Expand Up @@ -239,6 +240,8 @@ pub enum Error {
ResolveArraySizeError(#[from] crate::proc::ResolveArraySizeError),
#[error("entry point with stage {0:?} and name '{1}' not found")]
EntryPointNotFound(ir::ShaderStage, String),
#[error("Per vertex fragment inputs are not supported prior to MSL 4.0")]
PerVertexNotSupported,
}

#[derive(Clone, Debug, PartialEq, thiserror::Error)]
Expand Down Expand Up @@ -792,6 +795,7 @@ impl ResolvedInterpolation {
(I::Linear, S::Centroid) => Self::CentroidNoPerspective,
(I::Linear, S::Sample) => Self::SampleNoPerspective,
(I::Flat, _) => Self::Flat,
(I::PerVertex, S::Center) => Self::PerVertex,
_ => unreachable!(),
}
}
Expand All @@ -805,6 +809,7 @@ impl ResolvedInterpolation {
Self::SamplePerspective => "sample_perspective",
Self::SampleNoPerspective => "sample_no_perspective",
Self::Flat => "flat",
Self::PerVertex => unreachable!(),
};
out.write_str(identifier)?;
Ok(())
Expand Down Expand Up @@ -871,7 +876,7 @@ pub fn supported_capabilities() -> crate::valid::Capabilities {
| Caps::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING
| Caps::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
| Caps::COOPERATIVE_MATRIX
// No PER_VERTEX
| Caps::PER_VERTEX
// No RAY_TRACING_PIPELINE
// No DRAW_INDEX
// No MEMORY_DECORATION_VOLATILE
Expand Down
73 changes: 68 additions & 5 deletions naga/src/back/msl/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,16 @@ impl TypeContext<'_> {
_ => None,
}
}

fn unwrap_array(self) -> Self {
match self.gctx.types[self.handle].inner {
crate::TypeInner::Array { base, .. } => Self {
handle: base,
..self
},
_ => self,
}
}
}

impl Display for TypeContext<'_> {
Expand Down Expand Up @@ -7084,6 +7094,7 @@ template <typename A>
let stage_in_name = self.namer.call(&format!("{fun_name}Input"));
let varyings_member_name = self.namer.call("varyings");
let mut has_varyings = false;

if !flattened_arguments.is_empty() {
if !do_vertex_pulling {
writeln!(self.out, "struct {stage_in_name} {{")?;
Expand Down Expand Up @@ -7125,8 +7136,25 @@ template <typename A>
);
} else {
has_varyings = true;
write!(self.out, "{}{} {}", back::INDENT, ty_name, name)?;
resolved.try_fmt(&mut self.out)?;
if let super::ResolvedBinding::User {
prefix,
index,
interpolation: Some(super::ResolvedInterpolation::PerVertex),
} = resolved
{
if options.lang_version < (4, 0) {
return Err(Error::PerVertexNotSupported);
}
write!(
self.out,
"{}{NAMESPACE}::vertex_value<{}> {name} [[user({prefix}{index})]]",
back::INDENT,
ty_name.unwrap_array()
)?;
} else {
write!(self.out, "{}{} {}", back::INDENT, ty_name, name)?;
resolved.try_fmt(&mut self.out)?;
}
writeln!(self.out, ";")?;
}
}
Expand Down Expand Up @@ -7852,16 +7880,51 @@ template <typename A>
{
write!(self.out, "{{}}, ")?;
}
if let Some(crate::Binding::Location { .. }) = member.binding {
if has_varyings {
write!(self.out, "{varyings_member_name}.")?;
match member.binding {
Some(crate::Binding::Location {
interpolation: Some(crate::Interpolation::PerVertex),
..
}) => {
writeln!(
self.out,
"{0}{{ {1}.{2}.get({NAMESPACE}::vertex_index::first), {1}.{2}.get({NAMESPACE}::vertex_index::second), {1}.{2}.get({NAMESPACE}::vertex_index::third) }}",
back::INDENT,
varyings_member_name,
arg_name,
)?;
continue;
}
Some(crate::Binding::Location { .. }) => {
if has_varyings {
write!(self.out, "{varyings_member_name}.")?;
}
}
_ => (),
}
write!(self.out, "{name}")?;
}
writeln!(self.out, " }};")?;
}
_ => match arg.binding {
Some(crate::Binding::Location {
interpolation: Some(crate::Interpolation::PerVertex),
..
}) => {
let ty_name = TypeContext {
handle: arg.ty,
gctx: module.to_ctx(),
names: &self.names,
access: crate::StorageAccess::empty(),
first_time: false,
};
writeln!(
self.out,
"{0}const {ty_name} {arg_name} = {{ {1}.{2}.get({NAMESPACE}::vertex_index::first), {1}.{2}.get({NAMESPACE}::vertex_index::second), {1}.{2}.get({NAMESPACE}::vertex_index::third) }};",
back::INDENT,
varyings_member_name,
arg_name,
)?;
}
Some(crate::Binding::Location { .. })
| Some(crate::Binding::BuiltIn(crate::BuiltIn::Barycentric { .. })) => {
if has_varyings {
Expand Down
11 changes: 11 additions & 0 deletions naga/src/back/wgsl/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ impl<W: Write> Writer<W> {
cooperative_matrix: bool,
draw_index: bool,
ray_tracing_pipeline: bool,
per_vertex: bool,
}
let mut needed = RequiredEnabled {
mesh_shaders: module.uses_mesh_shaders(),
Expand All @@ -321,6 +322,12 @@ impl<W: Write> Writer<W> {
} => {
needed.mesh_shaders = true;
}
crate::Binding::Location {
interpolation: Some(crate::Interpolation::PerVertex),
..
} => {
needed.per_vertex = true;
}
crate::Binding::BuiltIn(crate::BuiltIn::DrawIndex) => needed.draw_index = true,
crate::Binding::BuiltIn(
crate::BuiltIn::RayInvocationId
Expand Down Expand Up @@ -450,6 +457,10 @@ impl<W: Write> Writer<W> {
writeln!(self.out, "enable wgpu_ray_tracing_pipeline;")?;
any_written = true;
}
if needed.per_vertex {
writeln!(self.out, "enable wgpu_per_vertex;")?;
any_written = true;
}
if any_written {
// Empty line for readability
writeln!(self.out)?;
Expand Down
11 changes: 9 additions & 2 deletions naga/src/front/wgsl/parse/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,19 @@ pub fn map_built_in(
Ok(built_in)
}

pub fn map_interpolation(word: &str, span: Span) -> Result<'_, crate::Interpolation> {
pub fn map_interpolation(
enable_extensions: &EnableExtensions,
word: &str,
span: Span,
) -> Result<'static, crate::Interpolation> {
match word {
"linear" => Ok(crate::Interpolation::Linear),
"flat" => Ok(crate::Interpolation::Flat),
"perspective" => Ok(crate::Interpolation::Perspective),
"per_vertex" => Ok(crate::Interpolation::PerVertex),
"per_vertex" => {
enable_extensions.require(ImplementedEnableExtension::PerVertex, span)?;
Ok(crate::Interpolation::PerVertex)
}
_ => Err(Box::new(Error::UnknownAttribute(span))),
}
}
Expand Down
11 changes: 11 additions & 0 deletions naga/src/front/wgsl/parse/directive/enable_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub(crate) struct EnableExtensions {
wgpu_cooperative_matrix: bool,
draw_index: bool,
primitive_index: bool,
per_vertex: bool,
}

impl EnableExtensions {
Expand All @@ -36,6 +37,7 @@ impl EnableExtensions {
wgpu_cooperative_matrix: false,
draw_index: false,
primitive_index: false,
per_vertex: false,
}
}

Expand All @@ -56,6 +58,7 @@ impl EnableExtensions {
ImplementedEnableExtension::WgpuCooperativeMatrix => &mut self.wgpu_cooperative_matrix,
ImplementedEnableExtension::DrawIndex => &mut self.draw_index,
ImplementedEnableExtension::PrimitiveIndex => &mut self.primitive_index,
ImplementedEnableExtension::PerVertex => &mut self.per_vertex,
};
*field = true;
}
Expand All @@ -75,6 +78,7 @@ impl EnableExtensions {
ImplementedEnableExtension::WgpuCooperativeMatrix => self.wgpu_cooperative_matrix,
ImplementedEnableExtension::DrawIndex => self.draw_index,
ImplementedEnableExtension::PrimitiveIndex => self.primitive_index,
ImplementedEnableExtension::PerVertex => self.per_vertex,
}
}

Expand Down Expand Up @@ -127,6 +131,7 @@ impl EnableExtension {
const SUBGROUPS: &'static str = "subgroups";
const PRIMITIVE_INDEX: &'static str = "primitive_index";
const DRAW_INDEX: &'static str = "draw_index";
const PER_VERTEX: &'static str = "wgpu_per_vertex";

/// Convert from a sentinel word in WGSL into its associated [`EnableExtension`], if possible.
pub(crate) fn from_ident(word: &str, span: Span) -> Result<'_, Self> {
Expand All @@ -150,6 +155,7 @@ impl EnableExtension {
Self::SUBGROUPS => Self::Unimplemented(UnimplementedEnableExtension::Subgroups),
Self::DRAW_INDEX => Self::Implemented(ImplementedEnableExtension::DrawIndex),
Self::PRIMITIVE_INDEX => Self::Implemented(ImplementedEnableExtension::PrimitiveIndex),
Self::PER_VERTEX => Self::Implemented(ImplementedEnableExtension::PerVertex),
_ => return Err(Box::new(Error::UnknownEnableExtension(span, word))),
})
}
Expand All @@ -170,6 +176,7 @@ impl EnableExtension {
ImplementedEnableExtension::DrawIndex => Self::DRAW_INDEX,
ImplementedEnableExtension::PrimitiveIndex => Self::PRIMITIVE_INDEX,
ImplementedEnableExtension::WgpuRayTracingPipeline => Self::RAY_TRACING_PIPELINE,
ImplementedEnableExtension::PerVertex => Self::PER_VERTEX,
},
Self::Unimplemented(kind) => match kind {
UnimplementedEnableExtension::Subgroups => Self::SUBGROUPS,
Expand Down Expand Up @@ -218,6 +225,8 @@ pub enum ImplementedEnableExtension {
///
/// [`enable primitive-index;`]: https://www.w3.org/TR/WGSL/#extension-primitive_index
PrimitiveIndex,
/// Enables the `wgpu_per_vertex` extension, allows using `@interpolate(per_vertex)` attribute in WGSL, native only.
PerVertex,
}

impl ImplementedEnableExtension {
Expand All @@ -233,6 +242,7 @@ impl ImplementedEnableExtension {
Self::WgpuCooperativeMatrix,
Self::DrawIndex,
Self::PrimitiveIndex,
Self::PerVertex,
];

/// Returns slice of all variants of [`ImplementedEnableExtension`].
Expand All @@ -254,6 +264,7 @@ impl ImplementedEnableExtension {
Self::WgpuRayTracingPipeline => C::RAY_TRACING_PIPELINE,
Self::DrawIndex => C::DRAW_INDEX,
Self::PrimitiveIndex => C::PRIMITIVE_INDEX,
Self::PerVertex => C::PER_VERTEX,
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions naga/src/front/wgsl/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,10 @@ impl<'a> BindingParser<'a> {
"interpolate" => {
lexer.expect(Token::Paren('('))?;
let (raw, span) = lexer.next_ident_with_span()?;
self.interpolation
.set(conv::map_interpolation(raw, span)?, name_span)?;
self.interpolation.set(
conv::map_interpolation(&lexer.enable_extensions, raw, span)?,
name_span,
)?;
if lexer.next_if(Token::Separator(',')) {
let (raw, span) = lexer.next_ident_with_span()?;
self.sampling
Expand Down
5 changes: 5 additions & 0 deletions naga/src/valid/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ pub enum VaryingError {
MissingPerPrimitive,
#[error("Per vertex fragment inputs must be an array of length 3.")]
PerVertexNotArrayOfThree,
#[error("Per vertex can only have Center sampling or no sampling modifier")]
InvalidPerVertexSampling,
}

#[derive(Clone, Debug, thiserror::Error)]
Expand Down Expand Up @@ -693,6 +695,9 @@ impl VaryingContext<'_> {
Capabilities::PER_VERTEX,
));
}
if sampling.is_some_and(|e| e != crate::Sampling::Center) {
return Err(VaryingError::InvalidPerVertexSampling);
}
}
// If this is per-vertex, we change the type we validate to the inner type, otherwise we leave it be.
// This lets all validation be done on the inner type once we've ensured the per-vertex is array<T, 3>
Expand Down
1 change: 1 addition & 0 deletions naga/src/valid/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ impl Capabilities {
Self::RAY_HIT_VERTEX_POSITION => Some(Ext::WgpuRayQueryVertexReturn),
Self::COOPERATIVE_MATRIX => Some(Ext::WgpuCooperativeMatrix),
Self::RAY_TRACING_PIPELINE => Some(Ext::WgpuRayTracingPipeline),
Self::PER_VERTEX => Some(Ext::PerVertex),
_ => None,
}
}
Expand Down
8 changes: 7 additions & 1 deletion naga/tests/in/wgsl/per-vertex.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
capabilities = "PER_VERTEX"
targets = "WGSL | SPIRV"
targets = "WGSL | SPIRV | METAL | HLSL"

[msl]
lang_version = [4, 0]

[hlsl]
shader_model = "V6_1"
Loading
Loading