From bee4898198bf0bfa41b130694e387e3c455c59b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Sat, 15 Jan 2022 15:16:59 +0000 Subject: [PATCH] hal/gl: Allow push constants trough emulation Uses freestanding uniforms for push constants --- Cargo.lock | 2 +- Cargo.toml | 1 + wgpu-core/Cargo.toml | 2 +- wgpu-hal/Cargo.toml | 4 +-- wgpu-hal/src/gles/adapter.rs | 5 +-- wgpu-hal/src/gles/command.rs | 43 ++++++++++++++++++++--- wgpu-hal/src/gles/conv.rs | 43 +++++++++++++++++++++++ wgpu-hal/src/gles/device.rs | 56 ++++++++++++++++++----------- wgpu-hal/src/gles/mod.rs | 15 +++++--- wgpu-hal/src/gles/queue.rs | 68 ++++++++++++++++++++++++++++++++++++ wgpu-hal/src/metal/device.rs | 2 +- wgpu/Cargo.toml | 6 ++-- 12 files changed, 206 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74884819c5e..6f93884aa8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1039,7 +1039,7 @@ dependencies = [ [[package]] name = "naga" version = "0.8.0" -source = "git+https://github.com/gfx-rs/naga?rev=c0b7ac7#c0b7ac7f542cc42ccac6f2ec3fc1fb01309cf4d7" +source = "git+https://github.com/JCapucho/naga?branch=glsl-out-push-constants-v2#c60d70eddf99d5858747d0a7823ab79c8cd0d019" dependencies = [ "bit-set", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index 0c7770ed44c..53c15013a73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ default-members = ["wgpu", "wgpu-hal", "wgpu-info"] [patch."https://github.com/gfx-rs/naga"] #naga = { path = "../naga" } +naga = { git = "https://github.com/JCapucho/naga", branch = "glsl-out-push-constants-v2" } [patch."https://github.com/zakarumych/gpu-descriptor"] #gpu-descriptor = { path = "../gpu-descriptor/gpu-descriptor" } diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 4460086e262..45977a517b5 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -38,7 +38,7 @@ thiserror = "1" [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "c0b7ac7" +rev = "a1840be" #version = "0.8" features = ["span", "validate", "wgsl-in"] diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 045aded7873..9f69af5098e 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -82,14 +82,14 @@ js-sys = { version = "0.3" } [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "c0b7ac7" +rev = "a1840be" #version = "0.8" # DEV dependencies [dev-dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "c0b7ac7" +rev = "a1840be" #version = "0.8" features = ["wgsl-in"] diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index e4bb5b60c94..e4a78da1d8b 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -286,7 +286,8 @@ impl super::Adapter { let mut features = wgt::Features::empty() | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES - | wgt::Features::CLEAR_TEXTURE; + | wgt::Features::CLEAR_TEXTURE + | wgt::Features::PUSH_CONSTANTS; features.set( wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER, extensions.contains("GL_EXT_texture_border_clamp"), @@ -399,7 +400,7 @@ impl super::Adapter { } else { !0 }, - max_push_constant_size: 0, + max_push_constant_size: super::MAX_PUSH_CONSTANTS as u32 * 4, min_uniform_buffer_offset_alignment, min_storage_buffer_offset_alignment, max_inter_stage_shader_components: gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) diff --git a/wgpu-hal/src/gles/command.rs b/wgpu-hal/src/gles/command.rs index a2e80e0f6c8..24b3cac8609 100644 --- a/wgpu-hal/src/gles/command.rs +++ b/wgpu-hal/src/gles/command.rs @@ -28,6 +28,7 @@ pub(super) struct State { has_pass_label: bool, instance_vbuf_mask: usize, dirty_vbuf_mask: usize, + push_offset_to_uniform: Vec, } impl super::CommandBuffer { @@ -43,6 +44,21 @@ impl super::CommandBuffer { self.data_bytes.extend(marker.as_bytes()); start..self.data_bytes.len() as u32 } + + fn add_push_constant_data(&mut self, data: &[u32]) -> Range { + let data_raw = unsafe { + std::slice::from_raw_parts( + data.as_ptr() as *const _, + data.len() * mem::size_of::(), + ) + }; + let start = self.data_bytes.len(); + assert!(start < u32::MAX as usize); + self.data_bytes.extend_from_slice(data_raw); + let end = self.data_bytes.len(); + assert!(end < u32::MAX as usize); + (start as u32)..(end as u32) + } } impl super::CommandEncoder { @@ -148,8 +164,10 @@ impl super::CommandEncoder { fn set_pipeline_inner(&mut self, inner: &super::PipelineInner) { self.cmd_buffer.commands.push(C::SetProgram(inner.program)); - //TODO: push constants - let _ = &inner.uniforms; + self.state.push_offset_to_uniform.clear(); + self.state + .push_offset_to_uniform + .extend_from_slice(&inner.uniforms); // rebind textures, if needed let mut dirty_textures = 0u32; @@ -603,10 +621,25 @@ impl crate::CommandEncoder for super::CommandEncoder { &mut self, _layout: &super::PipelineLayout, _stages: wgt::ShaderStages, - _offset: u32, - _data: &[u32], + start_offset: u32, + data: &[u32], ) { - unimplemented!() + let range = self.cmd_buffer.add_push_constant_data(data); + + let end = start_offset + data.len() as u32 * 4; + let mut offset = start_offset; + while offset < end { + let uniform = self.state.push_offset_to_uniform[offset as usize % 4].clone(); + let size = uniform.size; + if uniform.location.is_none() { + panic!("No uniform for push constant"); + } + self.cmd_buffer.commands.push(C::SetPushConstants { + uniform, + range: (range.start + offset)..range.end, + }); + offset += size; + } } unsafe fn insert_debug_marker(&mut self, label: &str) { diff --git a/wgpu-hal/src/gles/conv.rs b/wgpu-hal/src/gles/conv.rs index 09c269143ac..ffe60d39327 100644 --- a/wgpu-hal/src/gles/conv.rs +++ b/wgpu-hal/src/gles/conv.rs @@ -375,3 +375,46 @@ pub(super) fn map_storage_access(access: wgt::StorageTextureAccess) -> u32 { wgt::StorageTextureAccess::ReadWrite => glow::READ_WRITE, } } + +pub(super) fn is_sampler(glsl_uniform_type: u32) -> bool { + match glsl_uniform_type { + glow::INT_SAMPLER_1D + | glow::INT_SAMPLER_1D_ARRAY + | glow::INT_SAMPLER_2D + | glow::INT_SAMPLER_2D_ARRAY + | glow::INT_SAMPLER_2D_MULTISAMPLE + | glow::INT_SAMPLER_2D_MULTISAMPLE_ARRAY + | glow::INT_SAMPLER_2D_RECT + | glow::INT_SAMPLER_3D + | glow::INT_SAMPLER_CUBE + | glow::INT_SAMPLER_CUBE_MAP_ARRAY + | glow::UNSIGNED_INT_SAMPLER_1D + | glow::UNSIGNED_INT_SAMPLER_1D_ARRAY + | glow::UNSIGNED_INT_SAMPLER_2D + | glow::UNSIGNED_INT_SAMPLER_2D_ARRAY + | glow::UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE + | glow::UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY + | glow::UNSIGNED_INT_SAMPLER_2D_RECT + | glow::UNSIGNED_INT_SAMPLER_3D + | glow::UNSIGNED_INT_SAMPLER_CUBE + | glow::UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY + | glow::SAMPLER_1D + | glow::SAMPLER_1D_SHADOW + | glow::SAMPLER_1D_ARRAY + | glow::SAMPLER_1D_ARRAY_SHADOW + | glow::SAMPLER_2D + | glow::SAMPLER_2D_SHADOW + | glow::SAMPLER_2D_ARRAY + | glow::SAMPLER_2D_ARRAY_SHADOW + | glow::SAMPLER_2D_MULTISAMPLE + | glow::SAMPLER_2D_MULTISAMPLE_ARRAY + | glow::SAMPLER_2D_RECT + | glow::SAMPLER_2D_RECT_SHADOW + | glow::SAMPLER_3D + | glow::SAMPLER_CUBE + | glow::SAMPLER_CUBE_MAP_ARRAY + | glow::SAMPLER_CUBE_MAP_ARRAY_SHADOW + | glow::SAMPLER_CUBE_SHADOW => true, + _ => false, + } +} diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index a85301fa2c0..6dac2dfc0e8 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -272,30 +272,44 @@ impl super::Device { } } - let uniforms = { - let count = gl.get_active_uniforms(program); - let mut offset = 0; - let mut uniforms = Vec::new(); - - for uniform in 0..count { - let glow::ActiveUniform { size, utype, name } = - gl.get_active_uniform(program, uniform).unwrap(); - - if let Some(location) = gl.get_uniform_location(program, &name) { - // Sampler2D won't show up in UniformLocation and the only other uniforms - // should be push constants - uniforms.push(super::UniformDesc { - location, - offset, - utype, - }); + let mut uniforms: [super::UniformDesc; super::MAX_PUSH_CONSTANTS] = Default::default(); + let count = gl.get_active_uniforms(program); + let mut offset = 0; - offset += size as u32; - } + for uniform in 0..count { + let glow::ActiveUniform { utype, name, .. } = + gl.get_active_uniform(program, uniform).unwrap(); + + if conv::is_sampler(utype) { + continue; } - uniforms.into_boxed_slice() - }; + if let Some(location) = gl.get_uniform_location(program, &name) { + if uniforms[offset % 4].location.is_some() { + panic!("Offset already occupied") + } + + // `size` will always be 1 so we need to guess the real size from the type + let uniform_size = match utype { + glow::FLOAT | glow::INT => 4, + glow::FLOAT_VEC2 | glow::INT_VEC2 => 8, + glow::FLOAT_VEC3 | glow::INT_VEC3 => 12, + glow::FLOAT_VEC4 | glow::INT_VEC4 => 16, + glow::FLOAT_MAT2 => 16, + glow::FLOAT_MAT3 => 36, + glow::FLOAT_MAT4 => 64, + _ => panic!("Unsupported uniform datatype!"), + }; + + uniforms[offset % 4] = super::UniformDesc { + location: Some(location), + size: uniform_size, + utype, + }; + + offset += uniform_size as usize; + } + } Ok(super::PipelineInner { program, diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index d5e1980b212..be100b03e4c 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -88,6 +88,7 @@ const MAX_TEXTURE_SLOTS: usize = 16; const MAX_SAMPLERS: usize = 16; const MAX_VERTEX_ATTRIBUTES: usize = 16; const ZERO_BUFFER_SIZE: usize = 256 << 10; +const MAX_PUSH_CONSTANTS: usize = 16; impl crate::Api for Api { type Instance = Instance; @@ -392,11 +393,10 @@ struct VertexBufferDesc { stride: u32, } -#[allow(unused)] -#[derive(Clone)] +#[derive(Clone, Debug, Default)] struct UniformDesc { - location: glow::UniformLocation, - offset: u32, + location: Option, + size: u32, utype: u32, } @@ -407,7 +407,7 @@ type SamplerBindMap = [Option; MAX_TEXTURE_SLOTS]; struct PipelineInner { program: glow::Program, sampler_map: SamplerBindMap, - uniforms: Box<[UniformDesc]>, + uniforms: [UniformDesc; MAX_PUSH_CONSTANTS], } #[derive(Clone, Debug)] @@ -716,6 +716,11 @@ enum Command { InsertDebugMarker(Range), PushDebugGroup(Range), PopDebugGroup, + SetPushConstants { + uniform: UniformDesc, + /// Offset from the start of the `data_bytes` + range: Range, + }, } #[derive(Default)] diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index b3acddc9f85..4dbb442641e 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -1067,6 +1067,74 @@ impl super::Queue { #[cfg(not(target_arch = "wasm32"))] gl.pop_debug_group(); } + C::SetPushConstants { + ref uniform, + ref range, + } => { + let range = range.clone(); + fn get_data(data: &[u8], range: std::ops::Range) -> &[T] { + let range_size = range.end - range.start; + assert_eq!(range_size as usize % mem::size_of::(), 0); + assert!(data.len() >= range.end as usize); + let raw = &data[(range.start as usize)..(range.end as usize)]; + unsafe { + slice::from_raw_parts( + raw.as_ptr() as *const _, + raw.len() / mem::size_of::(), + ) + } + } + + let location = uniform.location.as_ref(); + + match uniform.utype { + glow::FLOAT => { + let data = get_data::(data_bytes, range)[0]; + gl.uniform_1_f32(location, data); + } + glow::FLOAT_VEC2 => { + let data = get_data::<[f32; 2]>(data_bytes, range)[0]; + gl.uniform_2_f32_slice(location, &data); + } + glow::FLOAT_VEC3 => { + let data = get_data::<[f32; 3]>(data_bytes, range)[0]; + gl.uniform_3_f32_slice(location, &data); + } + glow::FLOAT_VEC4 => { + let data = get_data::<[f32; 4]>(data_bytes, range)[0]; + gl.uniform_4_f32_slice(location, &data); + } + glow::INT => { + let data = get_data::(data_bytes, range)[0]; + gl.uniform_1_i32(location, data); + } + glow::INT_VEC2 => { + let data = get_data::<[i32; 2]>(data_bytes, range)[0]; + gl.uniform_2_i32_slice(location, &data); + } + glow::INT_VEC3 => { + let data = get_data::<[i32; 3]>(data_bytes, range)[0]; + gl.uniform_3_i32_slice(location, &data); + } + glow::INT_VEC4 => { + let data = get_data::<[i32; 4]>(data_bytes, range)[0]; + gl.uniform_4_i32_slice(location, &data); + } + glow::FLOAT_MAT2 => { + let data = get_data::<[f32; 4]>(data_bytes, range)[0]; + gl.uniform_matrix_2_f32_slice(location, false, &data); + } + glow::FLOAT_MAT3 => { + let data = get_data::<[f32; 9]>(data_bytes, range)[0]; + gl.uniform_matrix_3_f32_slice(location, false, &data); + } + glow::FLOAT_MAT4 => { + let data = get_data::<[f32; 16]>(data_bytes, range)[0]; + gl.uniform_matrix_4_f32_slice(location, false, &data); + } + _ => panic!("Unsupported uniform datatype!"), + } + } } } } diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index cc6dc6ab938..f2d09912757 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -117,7 +117,7 @@ impl super::Device { let mut immutable_buffer_mask = 0; for (var_handle, var) in module.global_variables.iter() { if var.class == naga::StorageClass::WorkGroup { - let size = module.types[var.ty].inner.span(&module.constants); + let size = module.types[var.ty].inner.size(&module.constants); wg_memory_sizes.push(size); } diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 5110322b833..dc6f4eb455a 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -136,20 +136,20 @@ env_logger = "0.8" [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "c0b7ac7" +rev = "a1840be" #version = "0.8" optional = true # used to test all the example shaders [dev-dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "c0b7ac7" +rev = "a1840be" #version = "0.8" features = ["wgsl-in"] [target.'cfg(target_arch = "wasm32")'.dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "c0b7ac7" +rev = "a1840be" #version = "0.8" features = ["wgsl-out"]