diff --git a/src/back/glsl/features.rs b/src/back/glsl/features.rs index 0d78558991..3f300ddf67 100644 --- a/src/back/glsl/features.rs +++ b/src/back/glsl/features.rs @@ -343,6 +343,8 @@ impl<'a, W> Writer<'a, W> { } } + let mut push_constant_used = false; + for (handle, global) in self.module.global_variables.iter() { if ep_info[handle].is_empty() { continue; @@ -350,7 +352,12 @@ impl<'a, W> Writer<'a, W> { match global.class { StorageClass::WorkGroup => self.features.request(Features::COMPUTE_SHADER), StorageClass::Storage { .. } => self.features.request(Features::BUFFER_STORAGE), - StorageClass::PushConstant => return Err(Error::PushConstantNotSupported), + StorageClass::PushConstant => { + if push_constant_used { + return Err(Error::MultiplePushConstants); + } + push_constant_used = true; + } _ => {} } } diff --git a/src/back/glsl/mod.rs b/src/back/glsl/mod.rs index 239cb0a159..1e6f65b861 100644 --- a/src/back/glsl/mod.rs +++ b/src/back/glsl/mod.rs @@ -86,7 +86,9 @@ impl crate::AtomicFunction { impl crate::StorageClass { fn is_buffer(&self) -> bool { match *self { - crate::StorageClass::Uniform | crate::StorageClass::Storage { .. } => true, + crate::StorageClass::PushConstant + | crate::StorageClass::Uniform + | crate::StorageClass::Storage { .. } => true, _ => false, } } @@ -96,6 +98,7 @@ impl crate::StorageClass { match *self { crate::StorageClass::WorkGroup | crate::StorageClass::Uniform + | crate::StorageClass::PushConstant | crate::StorageClass::Storage { .. } => false, _ => true, } @@ -227,6 +230,8 @@ pub struct Options { pub writer_flags: WriterFlags, /// Map of resources association to binding locations. pub binding_map: BindingMap, + /// The binding to give the push constant buffer if it's present + pub push_constant_binding: u32, } impl Default for Options { @@ -235,6 +240,7 @@ impl Default for Options { version: Version::Embedded(310), writer_flags: WriterFlags::ADJUST_COORDINATE_SPACE, binding_map: BindingMap::default(), + push_constant_binding: 0, } } } @@ -257,6 +263,7 @@ pub struct PipelineOptions { pub struct ReflectionInfo { pub texture_mapping: crate::FastHashMap, pub uniforms: crate::FastHashMap, String>, + pub push_constant: Option, } /// Structure that connects a texture to a sampler or not @@ -348,10 +355,10 @@ pub enum Error { /// Contains the missing [`Features`](Features) #[error("The selected version doesn't support {0:?}")] MissingFeatures(Features), - /// [`StorageClass::PushConstant`](crate::StorageClass::PushConstant) was used and isn't - /// supported in the glsl backend - #[error("Push constants aren't supported")] - PushConstantNotSupported, + /// [`StorageClass::PushConstant`](crate::StorageClass::PushConstant) was used more than + /// once in the entry point which isn't supported + #[error("Multiple push constants aren't supported")] + MultiplePushConstants, /// The specified [`Version`](Version) isn't supported #[error("The specified version isn't supported")] VersionNotSupported, @@ -887,7 +894,13 @@ impl<'a, W: Write> Writer<'a, W> { global: &crate::GlobalVariable, ) -> BackendResult { if self.options.version.supports_explicit_locations() { - if let Some(ref br) = global.binding { + if let crate::StorageClass::PushConstant = global.class { + write!( + self.out, + "layout(std140, binding = {}) ", + self.options.push_constant_binding + )? + } else if let Some(ref br) = global.binding { match self.options.binding_map.get(br) { Some(binding) => { let layout = match global.class { @@ -898,7 +911,9 @@ impl<'a, W: Write> Writer<'a, W> { "std140, " } } - crate::StorageClass::Uniform => "std140, ", + crate::StorageClass::Uniform | crate::StorageClass::PushConstant => { + "std140, " + } _ => "", }; write!(self.out, "layout({}binding = {}) ", layout, binding)? @@ -2852,15 +2867,12 @@ impl<'a, W: Write> Writer<'a, W> { } /// Helper method used to produce the reflection info that's returned to the user - /// - /// It takes an iterator of [`Function`](crate::Function) references instead of - /// [`Handle`](crate::arena::Handle) because [`EntryPoint`](crate::EntryPoint) isn't in any - /// [`Arena`](crate::arena::Arena) and we need to traverse it fn collect_reflection_info(&self) -> Result { use std::collections::hash_map::Entry; let info = self.info.get_entry_point(self.entry_point_idx as usize); let mut texture_mapping = crate::FastHashMap::default(); let mut uniforms = crate::FastHashMap::default(); + let mut push_constant = None; for sampling in info.sampling_set.iter() { let tex_name = self.reflection_names_globals[&sampling.image].clone(); @@ -2891,6 +2903,10 @@ impl<'a, W: Write> Writer<'a, W> { let name = self.reflection_names_globals[&handle].clone(); uniforms.insert(handle, name); } + crate::StorageClass::PushConstant => { + let name = self.reflection_names_globals[&handle].clone(); + push_constant = Some(name) + } _ => (), }, crate::TypeInner::Image { .. } => { @@ -2914,6 +2930,7 @@ impl<'a, W: Write> Writer<'a, W> { Ok(ReflectionInfo { texture_mapping, uniforms, + push_constant, }) } } @@ -3021,7 +3038,7 @@ fn glsl_storage_class(class: crate::StorageClass) -> Option<&'static str> { Sc::Uniform => Some("uniform"), Sc::Handle => Some("uniform"), Sc::WorkGroup => Some("shared"), - Sc::PushConstant => None, + Sc::PushConstant => Some("uniform"), } } diff --git a/tests/in/functions-webgl.param.ron b/tests/in/functions-webgl.param.ron index bc3dd4a8dd..1d84e945ba 100644 --- a/tests/in/functions-webgl.param.ron +++ b/tests/in/functions-webgl.param.ron @@ -3,5 +3,6 @@ version: Embedded(300), writer_flags: (bits: 0), binding_map: {}, + push_constant_binding: 0, ), ) diff --git a/tests/in/interpolate.param.ron b/tests/in/interpolate.param.ron index cb08368c87..bfea94342e 100644 --- a/tests/in/interpolate.param.ron +++ b/tests/in/interpolate.param.ron @@ -10,5 +10,6 @@ version: Desktop(400), writer_flags: (bits: 0), binding_map: {}, + push_constant_binding: 0, ), ) diff --git a/tests/in/push-constants.param.ron b/tests/in/push-constants.param.ron new file mode 100644 index 0000000000..4e25193b0f --- /dev/null +++ b/tests/in/push-constants.param.ron @@ -0,0 +1,9 @@ +( + god_mode: true, + glsl: ( + version: Embedded(320), + writer_flags: (bits: 0), + binding_map: {}, + push_constant_binding: 4, + ), +) diff --git a/tests/in/push-constants.wgsl b/tests/in/push-constants.wgsl new file mode 100644 index 0000000000..bb5b0e732a --- /dev/null +++ b/tests/in/push-constants.wgsl @@ -0,0 +1,13 @@ +struct PushConstants { + multiplier: f32; +}; +var pc: PushConstants; + +struct FragmentIn { + [[location(0)]] color: vec4; +}; + +[[stage(fragment)]] +fn main(in: FragmentIn) -> [[location(0)]] vec4 { + return in.color * pc.multiplier; +} diff --git a/tests/in/quad.param.ron b/tests/in/quad.param.ron index acde3aed42..c969a84b2e 100644 --- a/tests/in/quad.param.ron +++ b/tests/in/quad.param.ron @@ -8,5 +8,6 @@ version: Embedded(300), writer_flags: (bits: 0), binding_map: {}, + push_constant_binding: 0, ), ) diff --git a/tests/in/skybox.param.ron b/tests/in/skybox.param.ron index 9c9bd1f30a..ea040e4fb6 100644 --- a/tests/in/skybox.param.ron +++ b/tests/in/skybox.param.ron @@ -43,6 +43,7 @@ (group: 0, binding: 0): 0, (group: 0, binding: 1): 0, }, + push_constant_binding: 0, ), hlsl: ( shader_model: V5_1, diff --git a/tests/out/glsl/push-constants.main.Fragment.glsl b/tests/out/glsl/push-constants.main.Fragment.glsl new file mode 100644 index 0000000000..3a9f896c3d --- /dev/null +++ b/tests/out/glsl/push-constants.main.Fragment.glsl @@ -0,0 +1,23 @@ +#version 320 es + +precision highp float; +precision highp int; + +struct PushConstants { + float multiplier; +}; +struct FragmentIn { + vec4 color; +}; +layout(std140, binding = 4) uniform PushConstants_block_0Fragment { PushConstants pc; }; + +layout(location = 0) smooth in vec4 _vs2fs_location0; +layout(location = 0) out vec4 _fs2p_location0; + +void main() { + FragmentIn in_ = FragmentIn(_vs2fs_location0); + float _e4 = pc.multiplier; + _fs2p_location0 = (in_.color * _e4); + return; +} + diff --git a/tests/snapshots.rs b/tests/snapshots.rs index dbc86b682d..dd3bd0ccc0 100644 --- a/tests/snapshots.rs +++ b/tests/snapshots.rs @@ -439,6 +439,7 @@ fn convert_wgsl() { Targets::SPIRV | Targets::METAL | Targets::HLSL | Targets::WGSL | Targets::GLSL, ), ("extra", Targets::SPIRV | Targets::METAL | Targets::WGSL), + ("push-constants", Targets::GLSL), ( "operators", Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL,