diff --git a/benches/criterion.rs b/benches/criterion.rs index 1175528458..ee8765b921 100644 --- a/benches/criterion.rs +++ b/benches/criterion.rs @@ -239,7 +239,7 @@ fn backends(c: &mut Criterion) { b.iter(|| { let mut string = String::new(); let options = naga::back::glsl::Options { - version: naga::back::glsl::Version::Embedded(320), + version: naga::back::glsl::Version::new_gles(320), writer_flags: naga::back::glsl::WriterFlags::empty(), binding_map: Default::default(), }; @@ -248,6 +248,7 @@ fn backends(c: &mut Criterion) { let pipeline_options = naga::back::glsl::PipelineOptions { shader_stage: ep.stage, entry_point: ep.name.clone(), + multiview: None, }; match naga::back::glsl::Writer::new( &mut string, diff --git a/cli/src/main.rs b/cli/src/main.rs index efaf5959bc..6b924e9951 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -133,7 +133,7 @@ impl FromStr for GlslProfileArg { Ok(Self(if s.starts_with("core") { Version::Desktop(s[4..].parse().unwrap_or(330)) } else if s.starts_with("es") { - Version::Embedded(s[2..].parse().unwrap_or(310)) + Version::new_gles(s[2..].parse().unwrap_or(310)) } else { return Err(format!("Unknown profile: {}", s)); })) @@ -454,6 +454,7 @@ fn run() -> Result<(), Box> { "comp" => naga::ShaderStage::Compute, _ => unreachable!(), }, + multiview: None, }; let mut buffer = String::new(); diff --git a/src/back/glsl/features.rs b/src/back/glsl/features.rs index 82718b040e..b898b1d2b3 100644 --- a/src/back/glsl/features.rs +++ b/src/back/glsl/features.rs @@ -82,7 +82,7 @@ impl FeaturesManager { // Used when both core and es support the feature ($feature:ident, $core:literal, $es:literal) => { if self.0.contains(Features::$feature) - && (version < Version::Desktop($core) || version < Version::Embedded($es)) + && (version < Version::Desktop($core) || version < Version::new_gles($es)) { missing |= Features::$feature; } @@ -106,7 +106,10 @@ impl FeaturesManager { check_feature!(CULL_DISTANCE, 450, 300); check_feature!(SAMPLE_VARIABLES, 400, 300); check_feature!(DYNAMIC_ARRAY_SIZE, 430, 310); - check_feature!(MULTI_VIEW, 140, 310); + match version { + Version::Embedded { is_webgl: true, .. } => check_feature!(MULTI_VIEW, 140, 300), + _ => check_feature!(MULTI_VIEW, 140, 310), + }; // Only available on glsl core, this means that opengl es can't query the number // of samples nor levels in a image and neither do bound checks on the sample nor // the level argument of texelFecth @@ -212,11 +215,16 @@ impl FeaturesManager { } if self.0.contains(Features::MULTI_VIEW) { - // https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_multiview.txt - writeln!(out, "#extension GL_EXT_multiview : require")?; + if let Version::Embedded { is_webgl: true, .. } = version { + // https://www.khronos.org/registry/OpenGL/extensions/OVR/OVR_multiview2.txt + writeln!(out, "#extension GL_OVR_multiview2 : require")?; + } else { + // https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_multiview.txt + writeln!(out, "#extension GL_EXT_multiview : require")?; + } } - if self.0.contains(Features::FMA) && version >= Version::Embedded(310) { + if self.0.contains(Features::FMA) && version >= Version::new_gles(310) { // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_gpu_shader5.txt writeln!(out, "#extension GL_EXT_gpu_shader5 : require")?; } @@ -269,6 +277,10 @@ impl<'a, W> Writer<'a, W> { self.features.request(Features::COMPUTE_SHADER) } + if self.multiview.is_some() { + self.features.request(Features::MULTI_VIEW); + } + for (ty_handle, ty) in self.module.types.iter() { match ty.inner { TypeInner::Scalar { kind, width } => self.scalar_required_features(kind, width), diff --git a/src/back/glsl/mod.rs b/src/back/glsl/mod.rs index a59a0b9d76..e714b64475 100644 --- a/src/back/glsl/mod.rs +++ b/src/back/glsl/mod.rs @@ -119,15 +119,31 @@ pub enum Version { /// `core` GLSL. Desktop(u16), /// `es` GLSL. - Embedded(u16), + Embedded { version: u16, is_webgl: bool }, } impl Version { + /// Create a new gles version + pub const fn new_gles(version: u16) -> Self { + Self::Embedded { + version, + is_webgl: false, + } + } + /// Returns true if self is `Version::Embedded` (i.e. is a es version) const fn is_es(&self) -> bool { match *self { Version::Desktop(_) => false, - Version::Embedded(_) => true, + Version::Embedded { .. } => true, + } + } + + /// Returns true if targetting WebGL + const fn is_webgl(&self) -> bool { + match *self { + Version::Desktop(_) => false, + Version::Embedded { is_webgl, .. } => is_webgl, } } @@ -140,7 +156,7 @@ impl Version { fn is_supported(&self) -> bool { match *self { Version::Desktop(v) => SUPPORTED_CORE_VERSIONS.contains(&v), - Version::Embedded(v) => SUPPORTED_ES_VERSIONS.contains(&v), + Version::Embedded { version: v, .. } => SUPPORTED_ES_VERSIONS.contains(&v), } } @@ -151,19 +167,19 @@ impl Version { /// Note: `location=` for vertex inputs and fragment outputs is supported /// unconditionally for GLES 300. fn supports_explicit_locations(&self) -> bool { - *self >= Version::Embedded(310) || *self >= Version::Desktop(410) + *self >= Version::Desktop(410) || *self >= Version::new_gles(310) } fn supports_early_depth_test(&self) -> bool { - *self >= Version::Desktop(130) || *self >= Version::Embedded(310) + *self >= Version::Desktop(130) || *self >= Version::new_gles(310) } fn supports_std430_layout(&self) -> bool { - *self >= Version::Desktop(430) || *self >= Version::Embedded(310) + *self >= Version::Desktop(430) || *self >= Version::new_gles(310) } fn supports_fma_function(&self) -> bool { - *self >= Version::Desktop(400) || *self >= Version::Embedded(310) + *self >= Version::Desktop(400) || *self >= Version::new_gles(310) } } @@ -171,7 +187,9 @@ impl PartialOrd for Version { fn partial_cmp(&self, other: &Self) -> Option { match (*self, *other) { (Version::Desktop(x), Version::Desktop(y)) => Some(x.cmp(&y)), - (Version::Embedded(x), Version::Embedded(y)) => Some(x.cmp(&y)), + (Version::Embedded { version: x, .. }, Version::Embedded { version: y, .. }) => { + Some(x.cmp(&y)) + } _ => None, } } @@ -181,7 +199,7 @@ impl fmt::Display for Version { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Version::Desktop(v) => write!(f, "{} core", v), - Version::Embedded(v) => write!(f, "{} es", v), + Version::Embedded { version: v, .. } => write!(f, "{} es", v), } } } @@ -215,7 +233,7 @@ pub struct Options { impl Default for Options { fn default() -> Self { Options { - version: Version::Embedded(310), + version: Version::new_gles(310), writer_flags: WriterFlags::ADJUST_COORDINATE_SPACE, binding_map: BindingMap::default(), } @@ -233,6 +251,8 @@ pub struct PipelineOptions { /// /// If no entry point that matches is found while creating a [`Writer`], a error will be thrown. pub entry_point: String, + /// How many views to render to, if doing multiview rendering. + pub multiview: Option, } /// Reflection info for texture mappings and uniforms. @@ -285,6 +305,7 @@ struct VaryingName<'a> { binding: &'a crate::Binding, stage: ShaderStage, output: bool, + targetting_webgl: bool, } impl fmt::Display for VaryingName<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -302,7 +323,11 @@ impl fmt::Display for VaryingName<'_> { write!(f, "_{}_location{}", prefix, location,) } crate::Binding::BuiltIn(built_in) => { - write!(f, "{}", glsl_built_in(built_in, self.output)) + write!( + f, + "{}", + glsl_built_in(built_in, self.output, self.targetting_webgl) + ) } } } @@ -400,6 +425,8 @@ pub struct Writer<'a, W> { named_expressions: crate::NamedExpressions, /// Set of expressions that need to be baked to avoid unnecessary repetition in output need_bake_expressions: back::NeedBakeExpressions, + /// How many views to render to, if doing multiview rendering. + multiview: Option, } impl<'a, W: Write> Writer<'a, W> { @@ -451,7 +478,7 @@ impl<'a, W: Write> Writer<'a, W> { reflection_names_globals: crate::FastHashMap::default(), entry_point: &module.entry_points[ep_idx], entry_point_idx: ep_idx as u16, - + multiview: pipeline_options.multiview, block_id: IdGenerator::default(), named_expressions: Default::default(), need_bake_expressions: Default::default(), @@ -540,6 +567,13 @@ impl<'a, W: Write> Writer<'a, W> { } } + if self.entry_point.stage == ShaderStage::Vertex && self.options.version.is_webgl() { + if let Some(multiview) = self.multiview.as_ref() { + writeln!(self.out, "layout(num_views = {}) in;", multiview)?; + writeln!(self.out)?; + } + } + let ep_info = self.info.get_entry_point(self.entry_point_idx as usize); // Write struct types. @@ -1180,7 +1214,11 @@ impl<'a, W: Write> Writer<'a, W> { } => (location, interpolation, sampling), crate::Binding::BuiltIn(built_in) => { if let crate::BuiltIn::Position { invariant: true } = built_in { - writeln!(self.out, "invariant {};", glsl_built_in(built_in, output))?; + writeln!( + self.out, + "invariant {};", + glsl_built_in(built_in, output, self.options.version.is_webgl()) + )?; } return Ok(()); } @@ -1238,6 +1276,7 @@ impl<'a, W: Write> Writer<'a, W> { }, stage: self.entry_point.stage, output, + targetting_webgl: self.options.version.is_webgl(), }; writeln!(self.out, " {};", vname)?; @@ -1378,6 +1417,7 @@ impl<'a, W: Write> Writer<'a, W> { binding: member.binding.as_ref().unwrap(), stage, output: false, + targetting_webgl: self.options.version.is_webgl(), }; if index != 0 { write!(self.out, ", ")?; @@ -1391,6 +1431,7 @@ impl<'a, W: Write> Writer<'a, W> { binding: arg.binding.as_ref().unwrap(), stage, output: false, + targetting_webgl: self.options.version.is_webgl(), }; writeln!(self.out, "{};", varying_name)?; } @@ -1897,6 +1938,7 @@ impl<'a, W: Write> Writer<'a, W> { binding: member.binding.as_ref().unwrap(), stage: ep.stage, output: true, + targetting_webgl: self.options.version.is_webgl(), }; write!(self.out, "{} = ", varying_name)?; @@ -1921,6 +1963,7 @@ impl<'a, W: Write> Writer<'a, W> { binding: result.binding.as_ref().unwrap(), stage: ep.stage, output: true, + targetting_webgl: self.options.version.is_webgl(), }; write!(self.out, "{} = ", name)?; self.write_expr(value, ctx)?; @@ -3629,7 +3672,11 @@ const fn glsl_scalar( } /// Helper function that returns the glsl variable name for a builtin -const fn glsl_built_in(built_in: crate::BuiltIn, output: bool) -> &'static str { +const fn glsl_built_in( + built_in: crate::BuiltIn, + output: bool, + targetting_webgl: bool, +) -> &'static str { use crate::BuiltIn as Bi; match built_in { @@ -3640,6 +3687,7 @@ const fn glsl_built_in(built_in: crate::BuiltIn, output: bool) -> &'static str { "gl_FragCoord" } } + Bi::ViewIndex if targetting_webgl => "int(gl_ViewID_OVR)", Bi::ViewIndex => "gl_ViewIndex", // vertex Bi::BaseInstance => "uint(gl_BaseInstance)", diff --git a/src/back/mod.rs b/src/back/mod.rs index 16b503cfb7..d8e016c008 100644 --- a/src/back/mod.rs +++ b/src/back/mod.rs @@ -59,7 +59,7 @@ struct FunctionCtx<'a> { named_expressions: &'a crate::NamedExpressions, } -impl<'a> FunctionCtx<'_> { +impl FunctionCtx<'_> { /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a local in the current function const fn name_key(&self, local: crate::Handle) -> crate::proc::NameKey { match self.ty { diff --git a/src/valid/analyzer.rs b/src/valid/analyzer.rs index 9a7130ff93..3f13aba323 100644 --- a/src/valid/analyzer.rs +++ b/src/valid/analyzer.rs @@ -484,7 +484,6 @@ impl FunctionInfo { return Err(ExpressionError::MissingCapabilities(needed_caps)); } - let _ = (); Uniformity { non_uniform_result: self .add_assignable_ref(base, &mut assignable_global) diff --git a/tests/in/functions-webgl.param.ron b/tests/in/functions-webgl.param.ron index bc3dd4a8dd..923da07299 100644 --- a/tests/in/functions-webgl.param.ron +++ b/tests/in/functions-webgl.param.ron @@ -1,6 +1,9 @@ ( glsl: ( - version: Embedded(300), + version: Embedded( + version: 300, + is_webgl: false + ), writer_flags: (bits: 0), binding_map: {}, ), diff --git a/tests/in/multiview.param.ron b/tests/in/multiview.param.ron new file mode 100644 index 0000000000..35dd990fd6 --- /dev/null +++ b/tests/in/multiview.param.ron @@ -0,0 +1,3 @@ +( + glsl_multiview: Some(2), +) diff --git a/tests/in/multiview.wgsl b/tests/in/multiview.wgsl new file mode 100644 index 0000000000..b4531c8a76 --- /dev/null +++ b/tests/in/multiview.wgsl @@ -0,0 +1,2 @@ +@vertex +fn main(@builtin(view_index) view_index: i32) {} diff --git a/tests/in/multiview_webgl.param.ron b/tests/in/multiview_webgl.param.ron new file mode 100644 index 0000000000..720b51fbbc --- /dev/null +++ b/tests/in/multiview_webgl.param.ron @@ -0,0 +1,11 @@ +( + glsl: ( + version: Embedded ( + version: 300, + is_webgl: true + ), + writer_flags: (bits: 0), + binding_map: {}, + ), + glsl_multiview: Some(2), +) diff --git a/tests/in/multiview_webgl.wgsl b/tests/in/multiview_webgl.wgsl new file mode 100644 index 0000000000..b4531c8a76 --- /dev/null +++ b/tests/in/multiview_webgl.wgsl @@ -0,0 +1,2 @@ +@vertex +fn main(@builtin(view_index) view_index: i32) {} diff --git a/tests/in/push-constants.param.ron b/tests/in/push-constants.param.ron index 0a35edeab8..3972328c9c 100644 --- a/tests/in/push-constants.param.ron +++ b/tests/in/push-constants.param.ron @@ -1,7 +1,10 @@ ( god_mode: true, glsl: ( - version: Embedded(320), + version: Embedded( + version: 320, + is_webgl: false + ), writer_flags: (bits: 0), binding_map: {}, ), diff --git a/tests/in/quad.param.ron b/tests/in/quad.param.ron index acde3aed42..568b961898 100644 --- a/tests/in/quad.param.ron +++ b/tests/in/quad.param.ron @@ -5,7 +5,10 @@ adjust_coordinate_space: true, ), glsl: ( - version: Embedded(300), + version: Embedded( + version: 300, + is_webgl: false + ), writer_flags: (bits: 0), binding_map: {}, ), diff --git a/tests/in/skybox.param.ron b/tests/in/skybox.param.ron index 9c9bd1f30a..b90f2e5a25 100644 --- a/tests/in/skybox.param.ron +++ b/tests/in/skybox.param.ron @@ -37,7 +37,10 @@ fake_missing_bindings: false, ), glsl: ( - version: Embedded(320), + version: Embedded( + version: 320, + is_webgl: false + ), writer_flags: (bits: 0), binding_map: { (group: 0, binding: 0): 0, diff --git a/tests/out/glsl/multiview.main.Vertex.glsl b/tests/out/glsl/multiview.main.Vertex.glsl new file mode 100644 index 0000000000..a466f7df9e --- /dev/null +++ b/tests/out/glsl/multiview.main.Vertex.glsl @@ -0,0 +1,13 @@ +#version 310 es +#extension GL_EXT_multiview : require + +precision highp float; +precision highp int; + + +void main() { + int view_index = gl_ViewIndex; + gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w); + return; +} + diff --git a/tests/out/glsl/multiview_webgl.main.Vertex.glsl b/tests/out/glsl/multiview_webgl.main.Vertex.glsl new file mode 100644 index 0000000000..bb8b6e687d --- /dev/null +++ b/tests/out/glsl/multiview_webgl.main.Vertex.glsl @@ -0,0 +1,14 @@ +#version 300 es +#extension GL_OVR_multiview2 : require + +precision highp float; +precision highp int; + +layout(num_views = 2) in; + + +void main() { + int view_index = int(gl_ViewID_OVR); + return; +} + diff --git a/tests/out/spv/multiview.spvasm b/tests/out/spv/multiview.spvasm new file mode 100644 index 0000000000..bb67d7eba8 --- /dev/null +++ b/tests/out/spv/multiview.spvasm @@ -0,0 +1,23 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 11 +OpCapability Shader +OpCapability MultiView +OpExtension "SPV_KHR_multiview" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %8 "main" %5 +OpDecorate %5 BuiltIn ViewIndex +%2 = OpTypeVoid +%3 = OpTypeInt 32 1 +%6 = OpTypePointer Input %3 +%5 = OpVariable %6 Input +%9 = OpTypeFunction %2 +%8 = OpFunction %2 None %9 +%4 = OpLabel +%7 = OpLoad %3 %5 +OpBranch %10 +%10 = OpLabel +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/tests/out/wgsl/multiview.wgsl b/tests/out/wgsl/multiview.wgsl new file mode 100644 index 0000000000..3ea9676b8c --- /dev/null +++ b/tests/out/wgsl/multiview.wgsl @@ -0,0 +1,4 @@ +@vertex +fn main(@builtin(view_index) view_index: i32) { + return; +} diff --git a/tests/snapshots.rs b/tests/snapshots.rs index a7f773490c..5859e2b1c5 100644 --- a/tests/snapshots.rs +++ b/tests/snapshots.rs @@ -79,6 +79,9 @@ struct Parameters { hlsl: naga::back::hlsl::Options, #[serde(default)] wgsl: WgslOutParameters, + #[cfg(all(feature = "deserialize", feature = "glsl-out"))] + #[serde(default)] + glsl_multiview: Option, } #[allow(unused_variables)] @@ -162,6 +165,7 @@ fn check_targets(module: &naga::Module, name: &str, targets: Targets) { &ep.name, ¶ms.glsl, params.bounds_check_policies, + params.glsl_multiview, ); } } @@ -283,6 +287,7 @@ fn write_output_glsl( ep_name: &str, options: &naga::back::glsl::Options, bounds_check_policies: naga::proc::BoundsCheckPolicies, + multiview: Option, ) { use naga::back::glsl; @@ -291,6 +296,7 @@ fn write_output_glsl( let pipeline_options = glsl::PipelineOptions { shader_stage: stage, entry_point: ep_name.to_string(), + multiview, }; let mut buffer = String::new(); @@ -531,6 +537,8 @@ fn convert_wgsl() { "binding-arrays", Targets::WGSL | Targets::HLSL | Targets::METAL | Targets::SPIRV, ), + ("multiview", Targets::SPIRV | Targets::GLSL | Targets::WGSL), + ("multiview_webgl", Targets::GLSL), ( "break-if", Targets::WGSL | Targets::GLSL | Targets::SPIRV | Targets::HLSL | Targets::METAL,