From f3cbd6ce54690544871c8008eb9b2f1b4238e0a8 Mon Sep 17 00:00:00 2001 From: Adam Winiarczuk Date: Wed, 11 Sep 2024 00:28:36 +0200 Subject: [PATCH 01/59] fix: Set mip_level_count in TextureInitTracker for externally created textures (#6249) Co-authored-by: Adam Winiarczuk --- wgpu-core/src/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index dbf2bac76..5df285da5 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -1057,7 +1057,7 @@ impl Texture { if init { TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count()) } else { - TextureInitTracker::new(0, 0) + TextureInitTracker::new(desc.mip_level_count, 0) }, ), full_range: TextureSelector { From c2e0ad293fc635c8695e8b2358610a5918f77695 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:03:04 +0100 Subject: [PATCH 02/59] Vulkan: Avoid undefined behaviour with adversarial debug label (#6257) --- CHANGELOG.md | 4 ++++ wgpu-hal/src/vulkan/device.rs | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96b823a2f..c58f5894d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,6 +111,10 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Fix GL debug message callbacks not being properly cleaned up (causing UB). By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114) +#### Vulkan + +- Vulkan debug labels assumed no interior nul byte. By @DJMcNab in [#6257](https://github.com/gfx-rs/wgpu/pull/6257) + ### Changes - `wgpu_hal::gles::Adapter::new_external` now requires the context to be current when dropping the adapter and related objects. By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114). diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 54905b4ba..181da3d88 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -15,6 +15,13 @@ use std::{ }; impl super::DeviceShared { + /// Set the name of `object` to `name`. + /// + /// If `name` contains an interior null byte, then the name set will be truncated to that byte. + /// + /// # Safety + /// + /// It must be valid to set `object`'s debug name pub(super) unsafe fn set_object_name(&self, object: impl vk::Handle, name: &str) { let Some(extension) = self.extension_fns.debug_utils.as_ref() else { return; @@ -44,7 +51,7 @@ impl super::DeviceShared { &buffer_vec }; - let name = unsafe { CStr::from_bytes_with_nul_unchecked(name_bytes) }; + let name = CStr::from_bytes_until_nul(name_bytes).expect("We have added a null byte"); let _result = unsafe { extension.set_debug_utils_object_name( From ff52b8633155d8f3aab6924f78b812cc77c40310 Mon Sep 17 00:00:00 2001 From: Teodor Tanasoaia <28601907+teoxoy@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:56:26 +0200 Subject: [PATCH 03/59] [d3d12] handle absence of Windows SDK (#6262) --- wgpu-hal/src/auxil/dxgi/factory.rs | 2 +- wgpu-hal/src/dx12/instance.rs | 2 +- wgpu-hal/src/dx12/mod.rs | 34 +++++++++++++++++++++--------- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/wgpu-hal/src/auxil/dxgi/factory.rs b/wgpu-hal/src/auxil/dxgi/factory.rs index bf806ab32..4b71abda3 100644 --- a/wgpu-hal/src/auxil/dxgi/factory.rs +++ b/wgpu-hal/src/auxil/dxgi/factory.rs @@ -138,7 +138,7 @@ pub fn create_factory( // The `DXGI_CREATE_FACTORY_DEBUG` flag is only allowed to be passed to // `CreateDXGIFactory2` if the debug interface is actually available. So // we check for whether it exists first. - if lib_dxgi.debug_interface1().is_ok() { + if let Ok(Some(_)) = lib_dxgi.debug_interface1() { factory_flags |= Dxgi::DXGI_CREATE_FACTORY_DEBUG; } diff --git a/wgpu-hal/src/dx12/instance.rs b/wgpu-hal/src/dx12/instance.rs index 7b6acc623..31d0511d3 100644 --- a/wgpu-hal/src/dx12/instance.rs +++ b/wgpu-hal/src/dx12/instance.rs @@ -34,7 +34,7 @@ impl crate::Instance for super::Instance { .intersects(wgt::InstanceFlags::VALIDATION | wgt::InstanceFlags::GPU_BASED_VALIDATION) { // Enable debug layer - if let Ok(debug_controller) = lib_main.debug_interface() { + if let Ok(Some(debug_controller)) = lib_main.debug_interface() { if desc.flags.intersects(wgt::InstanceFlags::VALIDATION) { unsafe { debug_controller.EnableDebugLayer() } } diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index defeab1ad..0efb41813 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -190,7 +190,7 @@ impl D3D12Lib { blob.ok_or(crate::DeviceError::Unexpected) } - fn debug_interface(&self) -> Result { + fn debug_interface(&self) -> Result, crate::DeviceError> { // Calls windows::Win32::Graphics::Direct3D12::D3D12GetDebugInterface on d3d12.dll type Fun = extern "system" fn( riid: *const windows_core::GUID, @@ -200,11 +200,18 @@ impl D3D12Lib { let mut result__ = None; - (func)(&Direct3D12::ID3D12Debug::IID, <*mut _>::cast(&mut result__)) - .ok() - .into_device_result("GetDebugInterface")?; + let res = (func)(&Direct3D12::ID3D12Debug::IID, <*mut _>::cast(&mut result__)).ok(); - result__.ok_or(crate::DeviceError::Unexpected) + if let Err(ref err) = res { + match err.code() { + Dxgi::DXGI_ERROR_SDK_COMPONENT_MISSING => return Ok(None), + _ => {} + } + } + + res.into_device_result("GetDebugInterface")?; + + result__.ok_or(crate::DeviceError::Unexpected).map(Some) } } @@ -219,7 +226,7 @@ impl DxgiLib { } /// Will error with crate::DeviceError::Unexpected if DXGI 1.3 is not available. - pub fn debug_interface1(&self) -> Result { + pub fn debug_interface1(&self) -> Result, crate::DeviceError> { // Calls windows::Win32::Graphics::Dxgi::DXGIGetDebugInterface1 on dxgi.dll type Fun = extern "system" fn( flags: u32, @@ -230,11 +237,18 @@ impl DxgiLib { let mut result__ = None; - (func)(0, &Dxgi::IDXGIInfoQueue::IID, <*mut _>::cast(&mut result__)) - .ok() - .into_device_result("debug_interface1")?; + let res = (func)(0, &Dxgi::IDXGIInfoQueue::IID, <*mut _>::cast(&mut result__)).ok(); - result__.ok_or(crate::DeviceError::Unexpected) + if let Err(ref err) = res { + match err.code() { + Dxgi::DXGI_ERROR_SDK_COMPONENT_MISSING => return Ok(None), + _ => {} + } + } + + res.into_device_result("debug_interface1")?; + + result__.ok_or(crate::DeviceError::Unexpected).map(Some) } /// Will error with crate::DeviceError::Unexpected if DXGI 1.4 is not available. From c8beade1877251c494036fc3661b04ec6aad63a9 Mon Sep 17 00:00:00 2001 From: Teodor Tanasoaia <28601907+teoxoy@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:30:38 +0200 Subject: [PATCH 04/59] remove backend from ID (#6263) --- deno_webgpu/lib.rs | 5 +- player/src/bin/play.rs | 9 +- player/src/lib.rs | 6 +- player/tests/data/bind-group.ron | 26 +- player/tests/data/buffer-copy.ron | 4 +- player/tests/data/clear-buffer-texture.ron | 18 +- .../tests/data/pipeline-statistics-query.ron | 22 +- player/tests/data/quad.ron | 28 +- player/tests/data/zero-init-buffer.ron | 34 +- .../tests/data/zero-init-texture-binding.ron | 50 +-- .../data/zero-init-texture-copytobuffer.ron | 8 +- .../data/zero-init-texture-rendertarget.ron | 14 +- player/tests/test.rs | 14 +- wgpu-core/src/device/global.rs | 82 ++--- wgpu-core/src/device/mod.rs | 8 +- wgpu-core/src/device/queue.rs | 2 +- wgpu-core/src/id.rs | 152 +++------ wgpu-core/src/identity.rs | 22 +- wgpu-core/src/instance.rs | 292 +++++------------- wgpu-core/src/present.rs | 2 +- wgpu-core/src/registry.rs | 10 +- wgpu-core/src/storage.rs | 12 +- wgpu/src/backend/wgpu_core.rs | 6 +- 23 files changed, 282 insertions(+), 544 deletions(-) diff --git a/deno_webgpu/lib.rs b/deno_webgpu/lib.rs index 6f708014f..e31812e25 100644 --- a/deno_webgpu/lib.rs +++ b/deno_webgpu/lib.rs @@ -401,10 +401,7 @@ pub fn op_webgpu_request_adapter( force_fallback_adapter, compatible_surface: None, // windowless }; - let res = instance.request_adapter( - &descriptor, - wgpu_core::instance::AdapterInputs::Mask(backends, |_| None), - ); + let res = instance.request_adapter(&descriptor, backends, None); let adapter = match res { Ok(adapter) => adapter, diff --git a/player/src/bin/play.rs b/player/src/bin/play.rs index eb26ce6ba..558eb194b 100644 --- a/player/src/bin/play.rs +++ b/player/src/bin/play.rs @@ -56,7 +56,7 @@ fn main() { global.instance_create_surface( window.display_handle().unwrap().into(), window.window_handle().unwrap().into(), - Some(wgc::id::Id::zip(0, 1, wgt::Backend::Empty)), + Some(wgc::id::Id::zip(0, 1)), ) } .unwrap(); @@ -74,14 +74,15 @@ fn main() { #[cfg(not(feature = "winit"))] compatible_surface: None, }, - wgc::instance::AdapterInputs::IdSet(&[wgc::id::AdapterId::zip(0, 0, backend)]), + wgt::Backends::from(backend), + Some(wgc::id::AdapterId::zip(0, 1)), ) .expect("Unable to find an adapter for selected backend"); let info = global.adapter_get_info(adapter); log::info!("Picked '{}'", info.name); - let device_id = wgc::id::Id::zip(1, 0, backend); - let queue_id = wgc::id::Id::zip(1, 0, backend); + let device_id = wgc::id::Id::zip(0, 1); + let queue_id = wgc::id::Id::zip(0, 1); let res = global.adapter_request_device( adapter, &desc, diff --git a/player/src/lib.rs b/player/src/lib.rs index 8ea4e775b..241c19096 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -352,11 +352,7 @@ impl GlobalPlay for wgc::global::Global { let (encoder, error) = self.device_create_command_encoder( device, &wgt::CommandEncoderDescriptor { label: None }, - Some( - comb_manager - .process(device.backend()) - .into_command_encoder_id(), - ), + Some(comb_manager.process().into_command_encoder_id()), ); if let Some(e) = error { panic!("{e}"); diff --git a/player/tests/data/bind-group.ron b/player/tests/data/bind-group.ron index 87b3a1c72..80a5d18ba 100644 --- a/player/tests/data/bind-group.ron +++ b/player/tests/data/bind-group.ron @@ -2,13 +2,13 @@ features: [], expectations: [], //not crash! actions: [ - CreateBuffer(Id(0, 1, Empty), ( + CreateBuffer(Id(0, 1), ( label: None, size: 16, usage: 64, mapped_at_creation: false, )), - CreateBindGroupLayout(Id(0, 1, Empty), ( + CreateBindGroupLayout(Id(0, 1), ( label: None, entries: [ ( @@ -20,29 +20,29 @@ ), ], )), - CreateBindGroup(Id(0, 1, Empty), ( + CreateBindGroup(Id(0, 1), ( label: None, - layout: Id(0, 1, Empty), + layout: Id(0, 1), entries: [ ( binding: 0, resource: Buffer(( - buffer_id: Id(0, 1, Empty), + buffer_id: Id(0, 1), offset: 0, size: None, )), ) ], )), - CreatePipelineLayout(Id(0, 1, Empty), ( + CreatePipelineLayout(Id(0, 1), ( label: Some("empty"), bind_group_layouts: [ - Id(0, 1, Empty), + Id(0, 1), ], push_constant_ranges: [], )), CreateShaderModule( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, flags: (bits: 3), @@ -50,12 +50,12 @@ data: "empty.wgsl", ), CreateComputePipeline( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, - layout: Some(Id(0, 1, Empty)), + layout: Some(Id(0, 1)), stage: ( - module: Id(0, 1, Empty), + module: Id(0, 1), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -70,9 +70,9 @@ SetBindGroup( index: 0, num_dynamic_offsets: 0, - bind_group_id: Some(Id(0, 1, Empty)), + bind_group_id: Some(Id(0, 1)), ), - SetPipeline(Id(0, 1, Empty)), + SetPipeline(Id(0, 1)), ], dynamic_offsets: [], string_data: [], diff --git a/player/tests/data/buffer-copy.ron b/player/tests/data/buffer-copy.ron index 5c66f2c01..0cce3b1f6 100644 --- a/player/tests/data/buffer-copy.ron +++ b/player/tests/data/buffer-copy.ron @@ -10,7 +10,7 @@ ], actions: [ CreateBuffer( - Id(0, 1, Empty), + Id(0, 1), ( label: Some("dummy"), size: 16, @@ -19,7 +19,7 @@ ), ), WriteBuffer( - id: Id(0, 1, Empty), + id: Id(0, 1), data: "data1.bin", range: ( start: 0, diff --git a/player/tests/data/clear-buffer-texture.ron b/player/tests/data/clear-buffer-texture.ron index 7b25fa42c..4b7548d2f 100644 --- a/player/tests/data/clear-buffer-texture.ron +++ b/player/tests/data/clear-buffer-texture.ron @@ -20,7 +20,7 @@ ) ], actions: [ - CreateTexture(Id(0, 1, Empty), ( + CreateTexture(Id(0, 1), ( label: Some("Output Texture"), size: ( width: 64, @@ -36,7 +36,7 @@ // First fill the texture to ensure it wasn't just zero initialized or "happened" to be zero. WriteTexture( to: ( - texture: Id(0, 1, Empty), + texture: Id(0, 1), mip_level: 0, array_layer: 0, ), @@ -52,7 +52,7 @@ ), ), CreateBuffer( - Id(0, 1, Empty), + Id(0, 1), ( label: Some("Output Buffer"), size: 16384, @@ -62,7 +62,7 @@ ), CreateBuffer( - Id(1, 1, Empty), + Id(1, 1), ( label: Some("Buffer to be cleared"), size: 16, @@ -72,7 +72,7 @@ ), // Make sure there is something in the buffer, otherwise it might be just zero init! WriteBuffer( - id: Id(1, 1, Empty), + id: Id(1, 1), data: "data1.bin", range: ( start: 0, @@ -82,7 +82,7 @@ ), Submit(1, [ ClearTexture( - dst: Id(0, 1, Empty), + dst: Id(0, 1), subresource_range: ImageSubresourceRange( aspect: all, baseMipLevel: 0, @@ -93,12 +93,12 @@ ), CopyTextureToBuffer( src: ( - texture: Id(0, 1, Empty), + texture: Id(0, 1), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1, Empty), + buffer: Id(0, 1), layout: ( offset: 0, bytes_per_row: Some(256), @@ -112,7 +112,7 @@ ), // Partial clear to prove ClearBuffer( - dst: Id(1, 1, Empty), + dst: Id(1, 1), offset: 4, size: Some(8), ) diff --git a/player/tests/data/pipeline-statistics-query.ron b/player/tests/data/pipeline-statistics-query.ron index 8a6e4239b..17ef08b1b 100644 --- a/player/tests/data/pipeline-statistics-query.ron +++ b/player/tests/data/pipeline-statistics-query.ron @@ -9,13 +9,13 @@ ), ], actions: [ - CreatePipelineLayout(Id(0, 1, Empty), ( + CreatePipelineLayout(Id(0, 1), ( label: Some("empty"), bind_group_layouts: [], push_constant_ranges: [], )), CreateShaderModule( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, flags: (bits: 3), @@ -23,12 +23,12 @@ data: "empty.wgsl", ), CreateComputePipeline( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, - layout: Some(Id(0, 1, Empty)), + layout: Some(Id(0, 1)), stage: ( - module: Id(0, 1, Empty), + module: Id(0, 1), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -37,7 +37,7 @@ ), ), CreateQuerySet( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: Some("Compute Invocation QuerySet"), count: 2, @@ -45,7 +45,7 @@ ), ), CreateBuffer( - Id(0, 1, Empty), + Id(0, 1), ( label: Some("Compute Invocation Result Buffer"), size: 16, @@ -57,9 +57,9 @@ RunComputePass( base: ( commands: [ - SetPipeline(Id(0, 1, Empty)), + SetPipeline(Id(0, 1)), BeginPipelineStatisticsQuery( - query_set_id: Id(0, 1, Empty), + query_set_id: Id(0, 1), query_index: 0, ), Dispatch((2, 3, 7,)), @@ -71,10 +71,10 @@ ), ), ResolveQuerySet( - query_set_id: Id(0, 1, Empty), + query_set_id: Id(0, 1), start_query: 0, query_count: 1, - destination: Id(0, 1, Empty), + destination: Id(0, 1), destination_offset: 0, ) ]), diff --git a/player/tests/data/quad.ron b/player/tests/data/quad.ron index aad576c42..a954cb597 100644 --- a/player/tests/data/quad.ron +++ b/player/tests/data/quad.ron @@ -10,14 +10,14 @@ ], actions: [ CreateShaderModule( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, flags: (bits: 3), ), data: "quad.wgsl", ), - CreateTexture(Id(0, 1, Empty), ( + CreateTexture(Id(0, 1), ( label: Some("Output Texture"), size: ( width: 64, @@ -31,12 +31,12 @@ view_formats: [], )), CreateTextureView( - id: Id(0, 1, Empty), - parent_id: Id(0, 1, Empty), + id: Id(0, 1), + parent_id: Id(0, 1), desc: (), ), CreateBuffer( - Id(0, 1, Empty), + Id(0, 1), ( label: Some("Output Buffer"), size: 16384, @@ -44,19 +44,19 @@ mapped_at_creation: false, ), ), - CreatePipelineLayout(Id(0, 1, Empty), ( + CreatePipelineLayout(Id(0, 1), ( label: None, bind_group_layouts: [], push_constant_ranges: [], )), CreateRenderPipeline( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, - layout: Some(Id(0, 1, Empty)), + layout: Some(Id(0, 1)), vertex: ( stage: ( - module: Id(0, 1, Empty), + module: Id(0, 1), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -66,7 +66,7 @@ ), fragment: Some(( stage: ( - module: Id(0, 1, Empty), + module: Id(0, 1), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -84,7 +84,7 @@ RunRenderPass( base: ( commands: [ - SetPipeline(Id(0, 1, Empty)), + SetPipeline(Id(0, 1)), Draw( vertex_count: 3, instance_count: 1, @@ -98,7 +98,7 @@ ), target_colors: [ Some(( - view: Id(0, 1, Empty), + view: Id(0, 1), resolve_target: None, channel: ( load_op: clear, @@ -117,12 +117,12 @@ ), CopyTextureToBuffer( src: ( - texture: Id(0, 1, Empty), + texture: Id(0, 1), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1, Empty), + buffer: Id(0, 1), layout: ( offset: 0, bytes_per_row: Some(256), diff --git a/player/tests/data/zero-init-buffer.ron b/player/tests/data/zero-init-buffer.ron index 2b3f4a890..c4cf25f65 100644 --- a/player/tests/data/zero-init-buffer.ron +++ b/player/tests/data/zero-init-buffer.ron @@ -39,7 +39,7 @@ ], actions: [ CreateBuffer( - Id(0, 1, Empty), + Id(0, 1), ( label: Some("mapped_at_creation: false, with MAP_WRITE"), size: 16, @@ -48,7 +48,7 @@ ), ), CreateBuffer( - Id(1, 1, Empty), + Id(1, 1), ( label: Some("mapped_at_creation: false, without MAP_WRITE"), size: 16, @@ -57,7 +57,7 @@ ), ), CreateBuffer( - Id(2, 1, Empty), + Id(2, 1), ( label: Some("partially written"), size: 24, @@ -66,7 +66,7 @@ ), ), WriteBuffer( - id: Id(2, 1, Empty), + id: Id(2, 1), data: "data1.bin", range: ( start: 4, @@ -75,20 +75,20 @@ queued: true, ), CreateShaderModule( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, flags: (bits: 3), ), data: "zero-init-buffer-for-binding.wgsl", ), - CreateBuffer(Id(3, 1, Empty), ( + CreateBuffer(Id(3, 1), ( label: Some("used in binding"), size: 16, usage: 129, // STORAGE + MAP_READ mapped_at_creation: false, )), - CreateBindGroupLayout(Id(0, 1, Empty), ( + CreateBindGroupLayout(Id(0, 1), ( label: None, entries: [ ( @@ -105,34 +105,34 @@ ), ], )), - CreateBindGroup(Id(0, 1, Empty), ( + CreateBindGroup(Id(0, 1), ( label: None, - layout: Id(0, 1, Empty), + layout: Id(0, 1), entries: [ ( binding: 0, resource: Buffer(( - buffer_id: Id(3, 1, Empty), + buffer_id: Id(3, 1), offset: 0, size: Some(16), )), ), ], )), - CreatePipelineLayout(Id(0, 1, Empty), ( + CreatePipelineLayout(Id(0, 1), ( label: None, bind_group_layouts: [ - Id(0, 1, Empty), + Id(0, 1), ], push_constant_ranges: [], )), CreateComputePipeline( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, - layout: Some(Id(0, 1, Empty)), + layout: Some(Id(0, 1)), stage: ( - module: Id(0, 1, Empty), + module: Id(0, 1), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -145,11 +145,11 @@ base: ( label: None, commands: [ - SetPipeline(Id(0, 1, Empty)), + SetPipeline(Id(0, 1)), SetBindGroup( index: 0, num_dynamic_offsets: 0, - bind_group_id: Some(Id(0, 1, Empty)), + bind_group_id: Some(Id(0, 1)), ), Dispatch((4, 1, 1)), ], diff --git a/player/tests/data/zero-init-texture-binding.ron b/player/tests/data/zero-init-texture-binding.ron index e828b6c13..48415f43c 100644 --- a/player/tests/data/zero-init-texture-binding.ron +++ b/player/tests/data/zero-init-texture-binding.ron @@ -17,7 +17,7 @@ // MISSING: Partial views ], actions: [ - CreateTexture(Id(0, 1, Empty), ( + CreateTexture(Id(0, 1), ( label: Some("Sampled Texture"), size: ( width: 64, @@ -31,12 +31,12 @@ view_formats: [], )), CreateTextureView( - id: Id(0, 1, Empty), - parent_id: Id(0, 1, Empty), + id: Id(0, 1), + parent_id: Id(0, 1), desc: (), ), CreateBuffer( - Id(0, 1, Empty), + Id(0, 1), ( label: Some("Sampled Texture Buffer"), size: 16384, @@ -44,7 +44,7 @@ mapped_at_creation: false, ), ), - CreateTexture(Id(1, 1, Empty), ( + CreateTexture(Id(1, 1), ( label: Some("Storage Texture"), size: ( width: 64, @@ -58,12 +58,12 @@ view_formats: [], )), CreateTextureView( - id: Id(1, 1, Empty), - parent_id: Id(1, 1, Empty), + id: Id(1, 1), + parent_id: Id(1, 1), desc: (), ), CreateBuffer( - Id(1, 1, Empty), + Id(1, 1), ( label: Some("Storage Texture Buffer"), size: 16384, @@ -73,7 +73,7 @@ ), - CreateBindGroupLayout(Id(0, 1, Empty), ( + CreateBindGroupLayout(Id(0, 1), ( label: None, entries: [ ( @@ -98,29 +98,29 @@ ), ], )), - CreateBindGroup(Id(0, 1, Empty), ( + CreateBindGroup(Id(0, 1), ( label: None, - layout: Id(0, 1, Empty), + layout: Id(0, 1), entries: [ ( binding: 0, - resource: TextureView(Id(0, 1, Empty)), + resource: TextureView(Id(0, 1)), ), ( binding: 1, - resource: TextureView(Id(1, 1, Empty)), + resource: TextureView(Id(1, 1)), ), ], )), - CreatePipelineLayout(Id(0, 1, Empty), ( + CreatePipelineLayout(Id(0, 1), ( label: None, bind_group_layouts: [ - Id(0, 1, Empty), + Id(0, 1), ], push_constant_ranges: [], )), CreateShaderModule( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, flags: (bits: 3), @@ -128,12 +128,12 @@ data: "zero-init-texture-binding.wgsl", ), CreateComputePipeline( - id: Id(0, 1, Empty), + id: Id(0, 1), desc: ( label: None, - layout: Some(Id(0, 1, Empty)), + layout: Some(Id(0, 1)), stage: ( - module: Id(0, 1, Empty), + module: Id(0, 1), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -146,11 +146,11 @@ RunComputePass( base: ( commands: [ - SetPipeline(Id(0, 1, Empty)), + SetPipeline(Id(0, 1)), SetBindGroup( index: 0, num_dynamic_offsets: 0, - bind_group_id: Some(Id(0, 1, Empty)), + bind_group_id: Some(Id(0, 1)), ), Dispatch((4, 1, 1)), ], @@ -161,12 +161,12 @@ ), CopyTextureToBuffer( src: ( - texture: Id(0, 1, Empty), + texture: Id(0, 1), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1, Empty), + buffer: Id(0, 1), layout: ( offset: 0, bytes_per_row: Some(256), @@ -180,12 +180,12 @@ ), CopyTextureToBuffer( src: ( - texture: Id(1, 1, Empty), + texture: Id(1, 1), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(1, 1, Empty), + buffer: Id(1, 1), layout: ( offset: 0, bytes_per_row: Some(256), diff --git a/player/tests/data/zero-init-texture-copytobuffer.ron b/player/tests/data/zero-init-texture-copytobuffer.ron index 599ddbd67..eae95aaae 100644 --- a/player/tests/data/zero-init-texture-copytobuffer.ron +++ b/player/tests/data/zero-init-texture-copytobuffer.ron @@ -10,7 +10,7 @@ // MISSING: Partial copies ], actions: [ - CreateTexture(Id(0, 1, Empty), ( + CreateTexture(Id(0, 1), ( label: Some("Copy To Buffer Texture"), size: ( width: 64, @@ -24,7 +24,7 @@ view_formats: [], )), CreateBuffer( - Id(0, 1, Empty), + Id(0, 1), ( label: Some("Copy to Buffer Buffer"), size: 16384, @@ -35,12 +35,12 @@ Submit(1, [ CopyTextureToBuffer( src: ( - texture: Id(0, 1, Empty), + texture: Id(0, 1), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1, Empty), + buffer: Id(0, 1), layout: ( offset: 0, bytes_per_row: Some(256), diff --git a/player/tests/data/zero-init-texture-rendertarget.ron b/player/tests/data/zero-init-texture-rendertarget.ron index ec844fe07..adbb86962 100644 --- a/player/tests/data/zero-init-texture-rendertarget.ron +++ b/player/tests/data/zero-init-texture-rendertarget.ron @@ -10,7 +10,7 @@ // MISSING: Partial view. ], actions: [ - CreateTexture(Id(0, 1, Empty), ( + CreateTexture(Id(0, 1), ( label: Some("Render Target Texture"), size: ( width: 64, @@ -24,12 +24,12 @@ view_formats: [], )), CreateTextureView( - id: Id(0, 1, Empty), - parent_id: Id(0, 1, Empty), + id: Id(0, 1), + parent_id: Id(0, 1), desc: (), ), CreateBuffer( - Id(0, 1, Empty), + Id(0, 1), ( label: Some("Render Target Buffer"), size: 16384, @@ -48,7 +48,7 @@ ), target_colors: [ Some(( - view: Id(0, 1, Empty), + view: Id(0, 1), resolve_target: None, channel: ( load_op: load, @@ -64,12 +64,12 @@ ), CopyTextureToBuffer( src: ( - texture: Id(0, 1, Empty), + texture: Id(0, 1), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1, Empty), + buffer: Id(0, 1), layout: ( offset: 0, bytes_per_row: Some(256), diff --git a/player/tests/test.rs b/player/tests/test.rs index 3b26a202d..ec96f5446 100644 --- a/player/tests/test.rs +++ b/player/tests/test.rs @@ -105,9 +105,8 @@ impl Test<'_> { adapter: wgc::id::AdapterId, test_num: u32, ) { - let backend = adapter.backend(); - let device_id = wgc::id::Id::zip(test_num, 0, backend); - let queue_id = wgc::id::Id::zip(test_num, 0, backend); + let device_id = wgc::id::Id::zip(test_num, 1); + let queue_id = wgc::id::Id::zip(test_num, 1); let res = global.adapter_request_device( adapter, &wgt::DeviceDescriptor { @@ -137,7 +136,7 @@ impl Test<'_> { } println!("\t\t\tMapping..."); for expect in &self.expectations { - let buffer = wgc::id::Id::zip(expect.buffer.index, expect.buffer.epoch, backend); + let buffer = wgc::id::Id::zip(expect.buffer.index, expect.buffer.epoch); global .buffer_map_async( buffer, @@ -160,7 +159,7 @@ impl Test<'_> { for expect in self.expectations { println!("\t\t\tChecking {}", expect.name); - let buffer = wgc::id::Id::zip(expect.buffer.index, expect.buffer.epoch, backend); + let buffer = wgc::id::Id::zip(expect.buffer.index, expect.buffer.epoch); let (ptr, size) = global .buffer_get_mapped_range( buffer, @@ -237,7 +236,8 @@ impl Corpus { force_fallback_adapter: false, compatible_surface: None, }, - wgc::instance::AdapterInputs::IdSet(&[wgc::id::Id::zip(0, 0, backend)]), + wgt::Backends::from(backend), + Some(wgc::id::Id::zip(0, 1)), ) { Ok(adapter) => adapter, Err(_) => continue, @@ -247,7 +247,7 @@ impl Corpus { let supported_features = global.adapter_features(adapter); let downlevel_caps = global.adapter_downlevel_capabilities(adapter); - let test = Test::load(dir.join(test_path), adapter.backend()); + let test = Test::load(dir.join(test_path), backend); if !supported_features.contains(test.features) { println!( "\t\tSkipped due to missing features {:?}", diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 4b7143e55..10b82a73a 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -104,7 +104,7 @@ impl Global { profiling::scope!("Device::create_buffer"); let hub = &self.hub; - let fid = hub.buffers.prepare(device_id.backend(), id_in); + let fid = hub.buffers.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -175,21 +175,19 @@ impl Global { /// [`wgpu_types::BufferUsages`]: wgt::BufferUsages pub fn create_buffer_error( &self, - backend: wgt::Backend, id_in: Option, desc: &resource::BufferDescriptor, ) { - let fid = self.hub.buffers.prepare(backend, id_in); + let fid = self.hub.buffers.prepare(id_in); fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); } pub fn create_render_bundle_error( &self, - backend: wgt::Backend, id_in: Option, desc: &command::RenderBundleDescriptor, ) { - let fid = self.hub.render_bundles.prepare(backend, id_in); + let fid = self.hub.render_bundles.prepare(id_in); fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); } @@ -198,11 +196,10 @@ impl Global { /// See `create_buffer_error` for more context and explanation. pub fn create_texture_error( &self, - backend: wgt::Backend, id_in: Option, desc: &resource::TextureDescriptor, ) { - let fid = self.hub.textures.prepare(backend, id_in); + let fid = self.hub.textures.prepare(id_in); fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); } @@ -311,7 +308,7 @@ impl Global { let hub = &self.hub; - let fid = hub.textures.prepare(device_id.backend(), id_in); + let fid = hub.textures.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -354,7 +351,7 @@ impl Global { let hub = &self.hub; - let fid = hub.textures.prepare(device_id.backend(), id_in); + let fid = hub.textures.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -398,7 +395,7 @@ impl Global { profiling::scope!("Device::create_buffer"); let hub = &self.hub; - let fid = hub.buffers.prepare(A::VARIANT, id_in); + let fid = hub.buffers.prepare(id_in); let device = self.hub.devices.get(device_id); @@ -458,7 +455,7 @@ impl Global { let hub = &self.hub; - let fid = hub.texture_views.prepare(texture_id.backend(), id_in); + let fid = hub.texture_views.prepare(id_in); let error = 'error: { let texture = match hub.textures.get(texture_id).get() { @@ -522,7 +519,7 @@ impl Global { profiling::scope!("Device::create_sampler"); let hub = &self.hub; - let fid = hub.samplers.prepare(device_id.backend(), id_in); + let fid = hub.samplers.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -575,7 +572,7 @@ impl Global { profiling::scope!("Device::create_bind_group_layout"); let hub = &self.hub; - let fid = hub.bind_group_layouts.prepare(device_id.backend(), id_in); + let fid = hub.bind_group_layouts.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -647,7 +644,7 @@ impl Global { profiling::scope!("Device::create_pipeline_layout"); let hub = &self.hub; - let fid = hub.pipeline_layouts.prepare(device_id.backend(), id_in); + let fid = hub.pipeline_layouts.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -715,7 +712,7 @@ impl Global { profiling::scope!("Device::create_bind_group"); let hub = &self.hub; - let fid = hub.bind_groups.prepare(device_id.backend(), id_in); + let fid = hub.bind_groups.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -877,7 +874,7 @@ impl Global { profiling::scope!("Device::create_shader_module"); let hub = &self.hub; - let fid = hub.shader_modules.prepare(device_id.backend(), id_in); + let fid = hub.shader_modules.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -949,7 +946,7 @@ impl Global { profiling::scope!("Device::create_shader_module"); let hub = &self.hub; - let fid = hub.shader_modules.prepare(device_id.backend(), id_in); + let fid = hub.shader_modules.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -1006,10 +1003,9 @@ impl Global { profiling::scope!("Device::create_command_encoder"); let hub = &self.hub; - let fid = hub.command_buffers.prepare( - device_id.backend(), - id_in.map(|id| id.into_command_buffer_id()), - ); + let fid = hub + .command_buffers + .prepare(id_in.map(|id| id.into_command_buffer_id())); let device = self.hub.devices.get(device_id); @@ -1072,9 +1068,7 @@ impl Global { let hub = &self.hub; - let fid = hub - .render_bundles - .prepare(bundle_encoder.parent().backend(), id_in); + let fid = hub.render_bundles.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(bundle_encoder.parent()); @@ -1133,7 +1127,7 @@ impl Global { profiling::scope!("Device::create_query_set"); let hub = &self.hub; - let fid = hub.query_sets.prepare(device_id.backend(), id_in); + let fid = hub.query_sets.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); @@ -1194,7 +1188,7 @@ impl Global { let missing_implicit_pipeline_ids = desc.layout.is_none() && id_in.is_some() && implicit_pipeline_ids.is_none(); - let fid = hub.render_pipelines.prepare(device_id.backend(), id_in); + let fid = hub.render_pipelines.prepare(id_in); let implicit_context = implicit_pipeline_ids.map(|ipi| ipi.prepare(hub)); let error = 'error: { @@ -1378,7 +1372,7 @@ impl Global { ) { let hub = &self.hub; - let fid = hub.bind_group_layouts.prepare(pipeline_id.backend(), id_in); + let fid = hub.bind_group_layouts.prepare(id_in); let error = 'error: { let pipeline = match hub.render_pipelines.get(pipeline_id).get() { @@ -1431,7 +1425,7 @@ impl Global { let missing_implicit_pipeline_ids = desc.layout.is_none() && id_in.is_some() && implicit_pipeline_ids.is_none(); - let fid = hub.compute_pipelines.prepare(device_id.backend(), id_in); + let fid = hub.compute_pipelines.prepare(id_in); let implicit_context = implicit_pipeline_ids.map(|ipi| ipi.prepare(hub)); let error = 'error: { @@ -1562,7 +1556,7 @@ impl Global { ) { let hub = &self.hub; - let fid = hub.bind_group_layouts.prepare(pipeline_id.backend(), id_in); + let fid = hub.bind_group_layouts.prepare(id_in); let error = 'error: { let pipeline = match hub.compute_pipelines.get(pipeline_id).get() { @@ -1616,7 +1610,7 @@ impl Global { let hub = &self.hub; - let fid = hub.pipeline_caches.prepare(device_id.backend(), id_in); + let fid = hub.pipeline_caches.prepare(id_in); let error: pipeline::CreatePipelineCacheError = 'error: { let device = self.hub.devices.get(device_id); @@ -1881,7 +1875,7 @@ impl Global { // // https://github.com/gfx-rs/wgpu/issues/4105 - let surface_raw = surface.raw(device_id.backend()).unwrap(); + let surface_raw = surface.raw(device.backend()).unwrap(); match unsafe { surface_raw.configure(device.raw(), &hal_config) } { Ok(()) => (), Err(error) => { @@ -1963,7 +1957,6 @@ impl Global { /// submissions still in flight. fn poll_all_devices_of_api( &self, - backend: wgt::Backend, force_wait: bool, closures: &mut UserClosures, ) -> Result { @@ -1974,7 +1967,7 @@ impl Global { { let device_guard = hub.devices.read(); - for (_id, device) in device_guard.iter(backend) { + for (_id, device) in device_guard.iter() { let maintain = if force_wait { wgt::Maintain::Wait } else { @@ -2004,28 +1997,7 @@ impl Global { pub fn poll_all_devices(&self, force_wait: bool) -> Result { api_log!("poll_all_devices"); let mut closures = UserClosures::default(); - let mut all_queue_empty = true; - - #[cfg(vulkan)] - { - all_queue_empty &= - self.poll_all_devices_of_api(wgt::Backend::Vulkan, force_wait, &mut closures)?; - } - #[cfg(metal)] - { - all_queue_empty &= - self.poll_all_devices_of_api(wgt::Backend::Metal, force_wait, &mut closures)?; - } - #[cfg(dx12)] - { - all_queue_empty &= - self.poll_all_devices_of_api(wgt::Backend::Dx12, force_wait, &mut closures)?; - } - #[cfg(gles)] - { - all_queue_empty &= - self.poll_all_devices_of_api(wgt::Backend::Gl, force_wait, &mut closures)?; - } + let all_queue_empty = self.poll_all_devices_of_api(force_wait, &mut closures)?; closures.fire(); diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 97ce639fe..959f3cada 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -457,16 +457,12 @@ pub struct ImplicitPipelineIds<'a> { impl ImplicitPipelineIds<'_> { fn prepare(self, hub: &Hub) -> ImplicitPipelineContext { - let backend = self.root_id.backend(); ImplicitPipelineContext { - root_id: hub - .pipeline_layouts - .prepare(backend, Some(self.root_id)) - .id(), + root_id: hub.pipeline_layouts.prepare(Some(self.root_id)).id(), group_ids: self .group_ids .iter() - .map(|id_in| hub.bind_group_layouts.prepare(backend, Some(*id_in)).id()) + .map(|id_in| hub.bind_group_layouts.prepare(Some(*id_in)).id()) .collect(), } } diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index 1710c0591..f576b2412 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -444,7 +444,7 @@ impl Global { let staging_buffer = StagingBuffer::new(device, buffer_size)?; let ptr = unsafe { staging_buffer.ptr() }; - let fid = hub.staging_buffers.prepare(queue_id.backend(), id_in); + let fid = hub.staging_buffers.prepare(id_in); let id = fid.assign(staging_buffer); resource_log!("Queue::create_staging_buffer {id:?}"); diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index 19baa2e6f..76ea8e333 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -4,18 +4,25 @@ use std::{ fmt::{self, Debug}, hash::Hash, marker::PhantomData, + num::NonZeroU64, }; -use wgt::{Backend, WasmNotSendSync}; +use wgt::WasmNotSendSync; -type IdType = u64; -type ZippedIndex = Index; -type NonZeroId = std::num::NonZeroU64; - -const INDEX_BITS: usize = ZippedIndex::BITS as usize; -const EPOCH_BITS: usize = INDEX_BITS - BACKEND_BITS; -const BACKEND_BITS: usize = 3; -const BACKEND_SHIFT: usize = INDEX_BITS * 2 - BACKEND_BITS; -pub const EPOCH_MASK: u32 = (1 << (EPOCH_BITS)) - 1; +const _: () = { + if std::mem::size_of::() != 4 { + panic!() + } +}; +const _: () = { + if std::mem::size_of::() != 4 { + panic!() + } +}; +const _: () = { + if std::mem::size_of::() != 8 { + panic!() + } +}; /// The raw underlying representation of an identifier. #[repr(transparent)] @@ -30,50 +37,18 @@ pub const EPOCH_MASK: u32 = (1 << (EPOCH_BITS)) - 1; serde(from = "SerialId") )] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct RawId(NonZeroId); +pub struct RawId(NonZeroU64); impl RawId { - #[doc(hidden)] - #[inline] - pub fn from_non_zero(non_zero: NonZeroId) -> Self { - Self(non_zero) - } - - #[doc(hidden)] - #[inline] - pub fn into_non_zero(self) -> NonZeroId { - self.0 - } - /// Zip together an identifier and return its raw underlying representation. - pub fn zip(index: Index, epoch: Epoch, backend: Backend) -> RawId { - assert_eq!(0, epoch >> EPOCH_BITS); - assert_eq!(0, (index as IdType) >> INDEX_BITS); - let v = index as IdType - | ((epoch as IdType) << INDEX_BITS) - | ((backend as IdType) << BACKEND_SHIFT); - Self(NonZeroId::new(v).unwrap()) + pub fn zip(index: Index, epoch: Epoch) -> RawId { + let v = (index as u64) | ((epoch as u64) << 32); + Self(NonZeroU64::new(v).unwrap()) } /// Unzip a raw identifier into its components. - #[allow(trivial_numeric_casts)] - pub fn unzip(self) -> (Index, Epoch, Backend) { - ( - (self.0.get() as ZippedIndex) as Index, - (((self.0.get() >> INDEX_BITS) as ZippedIndex) & (EPOCH_MASK as ZippedIndex)) as Index, - self.backend(), - ) - } - - pub fn backend(self) -> Backend { - match self.0.get() >> (BACKEND_SHIFT) as u8 { - 0 => Backend::Empty, - 1 => Backend::Vulkan, - 2 => Backend::Metal, - 3 => Backend::Dx12, - 4 => Backend::Gl, - _ => unreachable!(), - } + pub fn unzip(self) -> (Index, Epoch) { + (self.0.get() as Index, (self.0.get() >> 32) as Epoch) } } @@ -116,20 +91,20 @@ pub struct Id(RawId, PhantomData); #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] enum SerialId { // The only variant forces RON to not ignore "Id" - Id(Index, Epoch, Backend), + Id(Index, Epoch), } impl From for SerialId { fn from(id: RawId) -> Self { - let (index, epoch, backend) = id.unzip(); - Self::Id(index, epoch, backend) + let (index, epoch) = id.unzip(); + Self::Id(index, epoch) } } impl From for RawId { fn from(id: SerialId) -> Self { match id { - SerialId::Id(index, epoch, backend) => RawId::zip(index, epoch, backend), + SerialId::Id(index, epoch) => RawId::zip(index, epoch), } } } @@ -150,29 +125,13 @@ where self.0 } - #[allow(dead_code)] - pub(crate) fn dummy(index: u32) -> Self { - Id::zip(index, 1, Backend::Empty) - } - - #[allow(dead_code)] - pub(crate) fn is_valid(&self) -> bool { - self.backend() != Backend::Empty - } - - /// Get the backend this identifier corresponds to. #[inline] - pub fn backend(self) -> Backend { - self.0.backend() + pub fn zip(index: Index, epoch: Epoch) -> Self { + Id(RawId::zip(index, epoch), PhantomData) } #[inline] - pub fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self { - Id(RawId::zip(index, epoch, backend), PhantomData) - } - - #[inline] - pub fn unzip(self) -> (Index, Epoch, Backend) { + pub fn unzip(self) -> (Index, Epoch) { self.0.unzip() } } @@ -194,16 +153,8 @@ where T: Marker, { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let (index, epoch, backend) = self.unzip(); - let backend = match backend { - Backend::Empty => "_", - Backend::Vulkan => "vk", - Backend::Metal => "mtl", - Backend::Dx12 => "d3d12", - Backend::Gl => "gl", - Backend::BrowserWebGpu => "webgpu", - }; - write!(formatter, "Id({index},{epoch},{backend})")?; + let (index, epoch) = self.unzip(); + write!(formatter, "Id({index},{epoch})")?; Ok(()) } } @@ -326,43 +277,16 @@ impl CommandBufferId { } } -#[test] -fn test_id_backend() { - for &b in &[ - Backend::Empty, - Backend::Vulkan, - Backend::Metal, - Backend::Dx12, - Backend::Gl, - ] { - let id = Id::<()>::zip(1, 0, b); - let (_id, _epoch, backend) = id.unzip(); - assert_eq!(id.backend(), b); - assert_eq!(backend, b); - } -} - #[test] fn test_id() { - let last_index = ((1u64 << INDEX_BITS) - 1) as Index; - let indexes = [1, last_index / 2 - 1, last_index / 2 + 1, last_index]; - let epochs = [1, EPOCH_MASK / 2 - 1, EPOCH_MASK / 2 + 1, EPOCH_MASK]; - let backends = [ - Backend::Empty, - Backend::Vulkan, - Backend::Metal, - Backend::Dx12, - Backend::Gl, - ]; + let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX]; + let epochs = [1, Epoch::MAX / 2 - 1, Epoch::MAX / 2 + 1, Epoch::MAX]; for &i in &indexes { for &e in &epochs { - for &b in &backends { - let id = Id::<()>::zip(i, e, b); - let (index, epoch, backend) = id.unzip(); - assert_eq!(index, i); - assert_eq!(epoch, e); - assert_eq!(backend, b); - } + let id = Id::<()>::zip(i, e); + let (index, epoch) = id.unzip(); + assert_eq!(index, i); + assert_eq!(epoch, e); } } } diff --git a/wgpu-core/src/identity.rs b/wgpu-core/src/identity.rs index c89731f7a..0493b9d2c 100644 --- a/wgpu-core/src/identity.rs +++ b/wgpu-core/src/identity.rs @@ -1,5 +1,3 @@ -use wgt::Backend; - use crate::{ id::{Id, Marker}, lock::{rank, Mutex}, @@ -52,7 +50,7 @@ impl IdentityValues { /// /// The backend is incorporated into the id, so that ids allocated with /// different `backend` values are always distinct. - pub fn alloc(&mut self, backend: Backend) -> Id { + pub fn alloc(&mut self) -> Id { assert!( self.id_source != IdSource::External, "Mix of internally allocated and externally provided IDs" @@ -61,12 +59,12 @@ impl IdentityValues { self.count += 1; match self.free.pop() { - Some((index, epoch)) => Id::zip(index, epoch + 1, backend), + Some((index, epoch)) => Id::zip(index, epoch + 1), None => { let index = self.next_index; self.next_index += 1; let epoch = 1; - Id::zip(index, epoch, backend) + Id::zip(index, epoch) } } } @@ -85,7 +83,7 @@ impl IdentityValues { /// Free `id`. It will never be returned from `alloc` again. pub fn release(&mut self, id: Id) { if let IdSource::Allocated = self.id_source { - let (index, epoch, _backend) = id.unzip(); + let (index, epoch) = id.unzip(); self.free.push((index, epoch)); } self.count -= 1; @@ -103,8 +101,8 @@ pub struct IdentityManager { } impl IdentityManager { - pub fn process(&self, backend: Backend) -> Id { - self.values.lock().alloc(backend) + pub fn process(&self) -> Id { + self.values.lock().alloc() } pub fn mark_as_used(&self, id: Id) -> Id { self.values.lock().mark_as_used(id) @@ -135,10 +133,10 @@ impl IdentityManager { fn test_epoch_end_of_life() { use crate::id; let man = IdentityManager::::new(); - let id1 = man.process(Backend::Empty); - assert_eq!(id1.unzip(), (0, 1, Backend::Empty)); + let id1 = man.process(); + assert_eq!(id1.unzip(), (0, 1)); man.free(id1); - let id2 = man.process(Backend::Empty); + let id2 = man.process(); // confirm that the epoch 1 is no longer re-used - assert_eq!(id2.unzip(), (0, 2, Backend::Empty)); + assert_eq!(id2.unzip(), (0, 2)); } diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 014fbc7bc..581c5ce0d 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -1,13 +1,12 @@ use std::sync::Arc; use std::{borrow::Cow, collections::HashMap}; -use crate::hub::Hub; use crate::{ api_log, device::{queue::Queue, resource::Device, DeviceDescriptor, DeviceError}, global::Global, hal_api::HalApi, - id::{markers, AdapterId, DeviceId, Id, Marker, QueueId, SurfaceId}, + id::{markers, AdapterId, DeviceId, QueueId, SurfaceId}, lock::{rank, Mutex}, present::Presentation, resource::ResourceType, @@ -368,26 +367,6 @@ pub enum RequestDeviceError { UnsupportedFeature(wgt::Features), } -pub enum AdapterInputs<'a, M: Marker> { - IdSet(&'a [Id]), - Mask(Backends, fn(Backend) -> Option>), -} - -impl AdapterInputs<'_, M> { - fn find(&self, b: Backend) -> Option>> { - match *self { - Self::IdSet(ids) => Some(Some(ids.iter().find(|id| id.backend() == b).copied()?)), - Self::Mask(bits, ref fun) => { - if bits.contains(b.into()) { - Some(fun(b)) - } else { - None - } - } - } - } -} - #[derive(Clone, Debug, Error)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[non_exhaustive] @@ -467,7 +446,7 @@ impl Global { let id = self .surfaces - .prepare(Backend::Empty, id_in) // No specific backend for Surface, since it's not specific. + .prepare(id_in) // No specific backend for Surface, since it's not specific. .assign(Arc::new(surface)); Ok(id) } @@ -511,10 +490,7 @@ impl Global { surface_per_backend: std::iter::once((Backend::Metal, raw_surface)).collect(), }; - let id = self - .surfaces - .prepare(Backend::Metal, id_in) - .assign(Arc::new(surface)); + let id = self.surfaces.prepare(id_in).assign(Arc::new(surface)); Ok(id) } @@ -536,10 +512,7 @@ impl Global { surface_per_backend: std::iter::once((Backend::Dx12, surface)).collect(), }; - let id = self - .surfaces - .prepare(Backend::Dx12, id_in) - .assign(Arc::new(surface)); + let id = self.surfaces.prepare(id_in).assign(Arc::new(surface)); Ok(id) } @@ -607,178 +580,81 @@ impl Global { drop(surface) } - pub fn enumerate_adapters(&self, inputs: AdapterInputs) -> Vec { + pub fn enumerate_adapters(&self, backends: Backends) -> Vec { profiling::scope!("Instance::enumerate_adapters"); api_log!("Instance::enumerate_adapters"); - fn enumerate( - hub: &Hub, - backend: Backend, - instance: &dyn hal::DynInstance, - inputs: &AdapterInputs, - list: &mut Vec, - ) { - let Some(id_backend) = inputs.find(backend) else { - return; - }; - + let mut adapters = Vec::new(); + for (_, instance) in self + .instance + .instance_per_backend + .iter() + .filter(|(backend, _)| backends.contains(Backends::from(*backend))) + { profiling::scope!("enumerating", &*format!("{:?}", backend)); let hal_adapters = unsafe { instance.enumerate_adapters(None) }; for raw in hal_adapters { let adapter = Adapter::new(raw); log::info!("Adapter {:?}", adapter.raw.info); - let id = hub - .adapters - .prepare(backend, id_backend) - .assign(Arc::new(adapter)); - list.push(id); + let id = self.hub.adapters.prepare(None).assign(Arc::new(adapter)); + adapters.push(id); } } - - let mut adapters = Vec::new(); - for (backend, instance) in &self.instance.instance_per_backend { - enumerate( - &self.hub, - *backend, - instance.as_ref(), - &inputs, - &mut adapters, - ); - } adapters } - fn select( - &self, - backend: Backend, - selected: &mut usize, - new_id: Option, - mut list: Vec, - ) -> Option { - match selected.checked_sub(list.len()) { - Some(left) => { - *selected = left; - None - } - None => { - let adapter = Adapter::new(list.swap_remove(*selected)); - log::info!("Adapter {:?}", adapter.raw.info); - let id = self - .hub - .adapters - .prepare(backend, new_id) - .assign(Arc::new(adapter)); - Some(id) - } - } - } - pub fn request_adapter( &self, desc: &RequestAdapterOptions, - inputs: AdapterInputs, + backends: Backends, + id_in: Option, ) -> Result { profiling::scope!("Instance::request_adapter"); api_log!("Instance::request_adapter"); - fn gather( - backend: Backend, - instance: &Instance, - inputs: &AdapterInputs, - compatible_surface: Option<&Surface>, - force_software: bool, - device_types: &mut Vec, - ) -> (Option>, Vec) { - let id = inputs.find(backend); - match (id, instance.raw(backend)) { - (Some(id), Some(inst)) => { - let compatible_hal_surface = - compatible_surface.and_then(|surface| surface.raw(backend)); - let mut adapters = unsafe { inst.enumerate_adapters(compatible_hal_surface) }; - if force_software { - adapters.retain(|exposed| exposed.info.device_type == wgt::DeviceType::Cpu); - } - if let Some(surface) = compatible_surface { - adapters - .retain(|exposed| surface.get_capabilities_with_raw(exposed).is_ok()); - } - device_types.extend(adapters.iter().map(|ad| ad.info.device_type)); - (id, adapters) - } - _ => (None, Vec::new()), - } - } - let compatible_surface = desc.compatible_surface.map(|id| self.surfaces.get(id)); let compatible_surface = compatible_surface.as_ref().map(|surface| surface.as_ref()); - let mut device_types = Vec::new(); + let mut adapters = Vec::new(); - #[cfg(vulkan)] - let (id_vulkan, adapters_vk) = gather( - Backend::Vulkan, - &self.instance, - &inputs, - compatible_surface, - desc.force_fallback_adapter, - &mut device_types, - ); - #[cfg(metal)] - let (id_metal, adapters_metal) = gather( - Backend::Metal, - &self.instance, - &inputs, - compatible_surface, - desc.force_fallback_adapter, - &mut device_types, - ); - #[cfg(dx12)] - let (id_dx12, adapters_dx12) = gather( - Backend::Dx12, - &self.instance, - &inputs, - compatible_surface, - desc.force_fallback_adapter, - &mut device_types, - ); - #[cfg(gles)] - let (id_gl, adapters_gl) = gather( - Backend::Gl, - &self.instance, - &inputs, - compatible_surface, - desc.force_fallback_adapter, - &mut device_types, - ); - - if device_types.is_empty() { - return Err(RequestAdapterError::NotFound); - } - - let (mut integrated, mut discrete, mut virt, mut cpu, mut other) = - (None, None, None, None, None); - - for (i, ty) in device_types.into_iter().enumerate() { - match ty { - wgt::DeviceType::IntegratedGpu => { - integrated = integrated.or(Some(i)); - } - wgt::DeviceType::DiscreteGpu => { - discrete = discrete.or(Some(i)); - } - wgt::DeviceType::VirtualGpu => { - virt = virt.or(Some(i)); - } - wgt::DeviceType::Cpu => { - cpu = cpu.or(Some(i)); - } - wgt::DeviceType::Other => { - other = other.or(Some(i)); - } + for (backend, instance) in self + .instance + .instance_per_backend + .iter() + .filter(|(backend, _)| backends.contains(Backends::from(*backend))) + { + let compatible_hal_surface = + compatible_surface.and_then(|surface| surface.raw(*backend)); + let mut backend_adapters = + unsafe { instance.enumerate_adapters(compatible_hal_surface) }; + if desc.force_fallback_adapter { + backend_adapters.retain(|exposed| exposed.info.device_type == wgt::DeviceType::Cpu); } + if let Some(surface) = compatible_surface { + backend_adapters + .retain(|exposed| surface.get_capabilities_with_raw(exposed).is_ok()); + } + adapters.extend(backend_adapters); } - let preferred_gpu = match desc.power_preference { + match desc.power_preference { + PowerPreference::LowPower => { + sort(&mut adapters, true); + } + PowerPreference::HighPerformance => { + sort(&mut adapters, false); + } + PowerPreference::None => {} + }; + + fn sort(adapters: &mut [hal::DynExposedAdapter], prefer_integrated_gpu: bool) { + adapters.sort_by(|a, b| { + get_order(a.info.device_type, prefer_integrated_gpu) + .cmp(&get_order(b.info.device_type, prefer_integrated_gpu)) + }); + } + + fn get_order(device_type: wgt::DeviceType, prefer_integrated_gpu: bool) -> u8 { // Since devices of type "Other" might really be "Unknown" and come // from APIs like OpenGL that don't specify device type, Prefer more // Specific types over Other. @@ -786,42 +662,28 @@ impl Global { // This means that backends which do provide accurate device types // will be preferred if their device type indicates an actual // hardware GPU (integrated or discrete). - PowerPreference::LowPower => integrated.or(discrete).or(other).or(virt).or(cpu), - PowerPreference::HighPerformance => discrete.or(integrated).or(other).or(virt).or(cpu), - PowerPreference::None => { - let option_min = |a: Option, b: Option| { - if let (Some(a), Some(b)) = (a, b) { - Some(a.min(b)) - } else { - a.or(b) - } - }; - // Pick the lowest id of these types - option_min(option_min(discrete, integrated), other) + match device_type { + wgt::DeviceType::DiscreteGpu if prefer_integrated_gpu => 2, + wgt::DeviceType::IntegratedGpu if prefer_integrated_gpu => 1, + wgt::DeviceType::DiscreteGpu => 1, + wgt::DeviceType::IntegratedGpu => 2, + wgt::DeviceType::Other => 3, + wgt::DeviceType::VirtualGpu => 4, + wgt::DeviceType::Cpu => 5, } - }; + } - let mut selected = preferred_gpu.unwrap_or(0); - #[cfg(vulkan)] - if let Some(id) = self.select(Backend::Vulkan, &mut selected, id_vulkan, adapters_vk) { - return Ok(id); + if let Some(adapter) = adapters.into_iter().next() { + log::info!("Adapter {:?}", adapter.info); + let id = self + .hub + .adapters + .prepare(id_in) + .assign(Arc::new(Adapter::new(adapter))); + Ok(id) + } else { + Err(RequestAdapterError::NotFound) } - #[cfg(metal)] - if let Some(id) = self.select(Backend::Metal, &mut selected, id_metal, adapters_metal) { - return Ok(id); - } - #[cfg(dx12)] - if let Some(id) = self.select(Backend::Dx12, &mut selected, id_dx12, adapters_dx12) { - return Ok(id); - } - #[cfg(gles)] - if let Some(id) = self.select(Backend::Gl, &mut selected, id_gl, adapters_gl) { - return Ok(id); - } - let _ = selected; - - log::warn!("Some adapters are present, but enumerating them failed!"); - Err(RequestAdapterError::NotFound) } /// # Safety @@ -834,7 +696,7 @@ impl Global { ) -> AdapterId { profiling::scope!("Instance::create_adapter_from_hal"); - let fid = self.hub.adapters.prepare(hal_adapter.backend(), input); + let fid = self.hub.adapters.prepare(input); let id = fid.assign(Arc::new(Adapter::new(hal_adapter))); resource_log!("Created Adapter {:?}", id); @@ -901,9 +763,8 @@ impl Global { profiling::scope!("Adapter::request_device"); api_log!("Adapter::request_device"); - let backend = adapter_id.backend(); - let device_fid = self.hub.devices.prepare(backend, device_id_in); - let queue_fid = self.hub.queues.prepare(backend, queue_id_in); + let device_fid = self.hub.devices.prepare(device_id_in); + let queue_fid = self.hub.queues.prepare(queue_id_in); let adapter = self.hub.adapters.get(adapter_id); let (device, queue) = @@ -933,9 +794,8 @@ impl Global { ) -> Result<(DeviceId, QueueId), RequestDeviceError> { profiling::scope!("Global::create_device_from_hal"); - let backend = adapter_id.backend(); - let devices_fid = self.hub.devices.prepare(backend, device_id_in); - let queues_fid = self.hub.queues.prepare(backend, queue_id_in); + let devices_fid = self.hub.devices.prepare(device_id_in); + let queues_fid = self.hub.queues.prepare(queue_id_in); let adapter = self.hub.adapters.get(adapter_id); let (device, queue) = adapter.create_device_and_queue_from_hal( diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index 93297bf3f..c9d0124bf 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -131,7 +131,7 @@ impl Global { return Err(SurfaceError::NotConfigured); }; - let fid = hub.textures.prepare(device.backend(), texture_id_in); + let fid = hub.textures.prepare(texture_id_in); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { diff --git a/wgpu-core/src/registry.rs b/wgpu-core/src/registry.rs index 6524a4df9..b3349235e 100644 --- a/wgpu-core/src/registry.rs +++ b/wgpu-core/src/registry.rs @@ -70,18 +70,14 @@ impl FutureId<'_, T> { } impl Registry { - pub(crate) fn prepare( - &self, - backend: wgt::Backend, - id_in: Option>, - ) -> FutureId { + pub(crate) fn prepare(&self, id_in: Option>) -> FutureId { FutureId { id: match id_in { Some(id_in) => { self.identity.mark_as_used(id_in); id_in } - None => self.identity.process(backend), + None => self.identity.process(), }, data: &self.storage, } @@ -154,7 +150,7 @@ mod tests { s.spawn(|| { for _ in 0..1000 { let value = Arc::new(TestData); - let new_id = registry.prepare(wgt::Backend::Empty, None); + let new_id = registry.prepare(None); let id = new_id.assign(value); registry.remove(id); } diff --git a/wgpu-core/src/storage.rs b/wgpu-core/src/storage.rs index 5a57782bf..593074875 100644 --- a/wgpu-core/src/storage.rs +++ b/wgpu-core/src/storage.rs @@ -1,7 +1,5 @@ use std::sync::Arc; -use wgt::Backend; - use crate::id::{Id, Marker}; use crate::resource::ResourceType; use crate::{Epoch, Index}; @@ -75,7 +73,7 @@ where T: StorageItem, { pub(crate) fn insert(&mut self, id: Id, value: T) { - let (index, epoch, _) = id.unzip(); + let (index, epoch) = id.unzip(); let index = index as usize; if index >= self.map.len() { self.map.resize_with(index + 1, || Element::Vacant); @@ -94,7 +92,7 @@ where } pub(crate) fn remove(&mut self, id: Id) -> T { - let (index, epoch, _) = id.unzip(); + let (index, epoch) = id.unzip(); match std::mem::replace(&mut self.map[index as usize], Element::Vacant) { Element::Occupied(value, storage_epoch) => { assert_eq!(epoch, storage_epoch); @@ -104,13 +102,13 @@ where } } - pub(crate) fn iter(&self, backend: Backend) -> impl Iterator, &T)> { + pub(crate) fn iter(&self) -> impl Iterator, &T)> { self.map .iter() .enumerate() .filter_map(move |(index, x)| match *x { Element::Occupied(ref value, storage_epoch) => { - Some((Id::zip(index as Index, storage_epoch, backend), value)) + Some((Id::zip(index as Index, storage_epoch), value)) } _ => None, }) @@ -128,7 +126,7 @@ where /// Get an owned reference to an item. /// Panics if there is an epoch mismatch, the entry is empty or in error. pub(crate) fn get(&self, id: Id) -> T { - let (index, epoch, _) = id.unzip(); + let (index, epoch) = id.unzip(); let (result, storage_epoch) = match self.map.get(index as usize) { Some(&Element::Occupied(ref v, epoch)) => (v.clone(), epoch), None | Some(&Element::Vacant) => panic!("{}[{:?}] does not exist", self.kind, id), diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index c5ba18cf6..37850c770 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -61,8 +61,7 @@ impl ContextWgpuCore { #[cfg(native)] pub fn enumerate_adapters(&self, backends: wgt::Backends) -> Vec { - self.0 - .enumerate_adapters(wgc::instance::AdapterInputs::Mask(backends, |_| None)) + self.0.enumerate_adapters(backends) } pub unsafe fn create_adapter_from_hal( @@ -589,7 +588,8 @@ impl crate::Context for ContextWgpuCore { surface.id }), }, - wgc::instance::AdapterInputs::Mask(wgt::Backends::all(), |_| None), + wgt::Backends::all(), + None, ); ready(id.ok()) } From eb18854b46a4d6c973a1981be6739ec19282f2f3 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 11 Sep 2024 23:32:20 -0700 Subject: [PATCH 05/59] spv-out: configure source language in debug info --- CHANGELOG.md | 1 + naga-cli/src/bin/naga.rs | 44 ++++++++++++++++++++++++----------- naga/src/back/spv/mod.rs | 3 ++- naga/src/back/spv/writer.rs | 2 +- naga/tests/snapshots.rs | 4 ++++ wgpu-hal/src/vulkan/device.rs | 2 ++ 6 files changed, 40 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c58f5894d..2a463c89b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). #### Naga - Accept only `vec3` (not `vecN`) for the `cross` built-in. By @ErichDonGubler in [#6171](https://github.com/gfx-rs/wgpu/pull/6171). +- Configure `SourceLanguage` when enabling debug info in SPV-out. By @kvark in [#6256](https://github.com/gfx-rs/wgpu/pull/6256) #### General diff --git a/naga-cli/src/bin/naga.rs b/naga-cli/src/bin/naga.rs index d97d96de7..e28519cb0 100644 --- a/naga-cli/src/bin/naga.rs +++ b/naga-cli/src/bin/naga.rs @@ -465,6 +465,7 @@ fn run() -> anyhow::Result<()> { let Parsed { mut module, input_text, + language, } = parse_input(input_path, input, ¶ms)?; // Include debugging information if requested. @@ -477,6 +478,7 @@ fn run() -> anyhow::Result<()> { params.spv_out.debug_info = Some(naga::back::spv::DebugInfo { source_code: input_text, file_name: input_path, + language, }) } else { eprintln!( @@ -579,6 +581,7 @@ fn run() -> anyhow::Result<()> { struct Parsed { module: naga::Module, input_text: Option, + language: naga::back::spv::SourceLanguage, } fn parse_input(input_path: &Path, input: Vec, params: &Parameters) -> anyhow::Result { @@ -593,16 +596,26 @@ fn parse_input(input_path: &Path, input: Vec, params: &Parameters) -> anyhow .context("Unable to determine --input-kind from filename")?, }; - let (module, input_text) = match input_kind { - InputKind::Bincode => (bincode::deserialize(&input)?, None), - InputKind::SpirV => { - naga::front::spv::parse_u8_slice(&input, ¶ms.spv_in).map(|m| (m, None))? - } + Ok(match input_kind { + InputKind::Bincode => Parsed { + module: bincode::deserialize(&input)?, + input_text: None, + language: naga::back::spv::SourceLanguage::Unknown, + }, + InputKind::SpirV => Parsed { + module: naga::front::spv::parse_u8_slice(&input, ¶ms.spv_in)?, + input_text: None, + language: naga::back::spv::SourceLanguage::Unknown, + }, InputKind::Wgsl => { let input = String::from_utf8(input)?; let result = naga::front::wgsl::parse_str(&input); match result { - Ok(v) => (v, Some(input)), + Ok(v) => Parsed { + module: v, + input_text: Some(input), + language: naga::back::spv::SourceLanguage::WGSL, + }, Err(ref e) => { let message = anyhow!( "Could not parse WGSL:\n{}", @@ -631,8 +644,8 @@ fn parse_input(input_path: &Path, input: Vec, params: &Parameters) -> anyhow }; let input = String::from_utf8(input)?; let mut parser = naga::front::glsl::Frontend::default(); - ( - parser + Parsed { + module: parser .parse( &naga::front::glsl::Options { stage: shader_stage.0, @@ -649,12 +662,11 @@ fn parse_input(input_path: &Path, input: Vec, params: &Parameters) -> anyhow error.emit_to_writer_with_path(&mut writer, &input, filename); std::process::exit(1); }), - Some(input), - ) + input_text: Some(input), + language: naga::back::spv::SourceLanguage::GLSL, + } } - }; - - Ok(Parsed { module, input_text }) + }) } fn write_output( @@ -833,7 +845,11 @@ fn bulk_validate(args: Args, params: &Parameters) -> anyhow::Result<()> { let path = Path::new(&input_path); let input = fs::read(path)?; - let Parsed { module, input_text } = match parse_input(path, input, params) { + let Parsed { + module, + input_text, + language: _, + } = match parse_input(path, input, params) { Ok(parsed) => parsed, Err(error) => { invalid.push(input_path.clone()); diff --git a/naga/src/back/spv/mod.rs b/naga/src/back/spv/mod.rs index 91407561a..32bd1fcec 100644 --- a/naga/src/back/spv/mod.rs +++ b/naga/src/back/spv/mod.rs @@ -16,7 +16,7 @@ mod selection; mod subgroup; mod writer; -pub use spirv::Capability; +pub use spirv::{Capability, SourceLanguage}; use crate::arena::{Handle, HandleVec}; use crate::proc::{BoundsCheckPolicies, TypeResolution}; @@ -89,6 +89,7 @@ impl IdGenerator { pub struct DebugInfo<'a> { pub source_code: &'a str, pub file_name: &'a std::path::Path, + pub language: SourceLanguage, } /// A SPIR-V block to which we are still adding instructions. diff --git a/naga/src/back/spv/writer.rs b/naga/src/back/spv/writer.rs index 488371558..678dcb424 100644 --- a/naga/src/back/spv/writer.rs +++ b/naga/src/back/spv/writer.rs @@ -1967,7 +1967,7 @@ impl Writer { source_file_id, }); self.debugs.append(&mut Instruction::source_auto_continued( - spirv::SourceLanguage::Unknown, + debug_info.language, 0, &debug_info_inner, )); diff --git a/naga/tests/snapshots.rs b/naga/tests/snapshots.rs index 0e285e7b0..936203986 100644 --- a/naga/tests/snapshots.rs +++ b/naga/tests/snapshots.rs @@ -358,6 +358,10 @@ fn check_targets( let debug_info = source_code.map(|code| naga::back::spv::DebugInfo { source_code: code, file_name: name.as_ref(), + // wgpu#6266: we technically know all the information here to + // produce the valid language but it's not too important for + // validation purposes + language: naga::back::spv::SourceLanguage::Unknown, }); if targets.contains(Targets::SPIRV) { diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 181da3d88..6c3bfc5ed 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -769,6 +769,7 @@ impl super::Device { temp_options.debug_info = Some(naga::back::spv::DebugInfo { source_code: &debug.source_code, file_name: debug.file_name.as_ref().as_ref(), + language: naga::back::spv::SourceLanguage::WGSL, }) } if !stage.zero_initialize_workgroup_memory { @@ -1742,6 +1743,7 @@ impl crate::Device for super::Device { .map(|d| naga::back::spv::DebugInfo { source_code: d.source_code.as_ref(), file_name: d.file_name.as_ref().as_ref(), + language: naga::back::spv::SourceLanguage::WGSL, }); if !desc.runtime_checks { naga_options.bounds_check_policies = naga::proc::BoundsCheckPolicies { From a21c0e26969ad7f0e0f7fbbd7e05176a4888dabb Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Sat, 14 Sep 2024 23:44:17 -0500 Subject: [PATCH 06/59] Clarify cfg(send_sync) gating in this backend/webgpu comment. (#6274) Addresses the confusion in #6267. --- wgpu/src/backend/webgpu.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index a98c0ca32..7646085f7 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -26,9 +26,10 @@ use crate::{ // implement `Send` and `Sync` to match native. // // SAFETY: All webgpu handle types in wasm32 are internally a `JsValue`, and `JsValue` is neither -// Send nor Sync. Currently, wasm32 has no threading support so implementing `Send` or `Sync` for a -// type is (for now) harmless. Eventually wasm32 will support threading, and depending on how this -// is integrated (or not integrated) with values like those in webgpu, this may become unsound. +// Send nor Sync. Currently, wasm32 has no threading support by default, so implementing `Send` or +// `Sync` for a type is harmless. However, nightly Rust supports compiling wasm with experimental +// threading support via `--target-features`. If `wgpu` is being compiled with those features, we do +// not implement `Send` and `Sync` on the webgpu handle types. #[derive(Clone, Debug)] pub(crate) struct Sendable(T); From d79ebc4db3e41dbde60fd0745142f00d0d325389 Mon Sep 17 00:00:00 2001 From: Johann Muszynski Date: Sun, 15 Sep 2024 10:07:39 +0300 Subject: [PATCH 07/59] Update some docs containing OpenGL or GLSL references (#6271) --- CHANGELOG.md | 4 ++++ wgpu-types/src/lib.rs | 37 +++++++++++++++++---------------- wgpu/src/api/pipeline_layout.rs | 4 ++-- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a463c89b..35355b178 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,6 +126,10 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Change the inconsistent `DropGuard` based API on Vulkan and GLES to a consistent, callback-based one. By @jerzywilczek in [#6164](https://github.com/gfx-rs/wgpu/pull/6164) +### Documentation + +- Removed some OpenGL and Vulkan references from `wgpu-types` documentation. Fixed Storage texel types in examples. By @Nelarius in [#6271](https://github.com/gfx-rs/wgpu/pull/6271) + ### Dependency Updates #### GLES diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index e3cab5605..c7167f826 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -568,7 +568,7 @@ bitflags::bitflags! { /// may also create uniform arrays of storage textures. /// /// ex. - /// - `var textures: array, 10>` (WGSL) + /// - `var textures: array, 10>` (WGSL) /// - `uniform image2D textures[10]` (GLSL) /// /// This capability allows them to exist and to be indexed by dynamically uniform @@ -1959,12 +1959,13 @@ impl TextureViewDimension { /// Alpha blend factor. /// -/// Alpha blending is very complicated: see the OpenGL or Vulkan spec for more information. -/// /// Corresponds to [WebGPU `GPUBlendFactor`]( -/// https://gpuweb.github.io/gpuweb/#enumdef-gpublendfactor). -/// Values using S1 requires [`Features::DUAL_SOURCE_BLENDING`] and can only be -/// used with the first render target. +/// https://gpuweb.github.io/gpuweb/#enumdef-gpublendfactor). Values using `Src1` +/// require [`Features::DUAL_SOURCE_BLENDING`] and can only be used with the first +/// render target. +/// +/// For further details on how the blend factors are applied, see the analogous +/// functionality in OpenGL: . #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -2024,10 +2025,11 @@ impl BlendFactor { /// Alpha blend operation. /// -/// Alpha blending is very complicated: see the OpenGL or Vulkan spec for more information. -/// /// Corresponds to [WebGPU `GPUBlendOperation`]( /// https://gpuweb.github.io/gpuweb/#enumdef-gpublendoperation). +/// +/// For further details on how the blend operations are applied, see +/// the analogous functionality in OpenGL: . #[repr(C)] #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -2102,8 +2104,6 @@ impl Default for BlendComponent { /// Describe the blend state of a render pipeline, /// within [`ColorTargetState`]. /// -/// See the OpenGL or Vulkan spec for more information. -/// /// Corresponds to [WebGPU `GPUBlendState`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpublendstate). #[repr(C)] @@ -6543,7 +6543,7 @@ pub enum StorageTextureAccess { /// Example WGSL syntax: /// ```rust,ignore /// @group(0) @binding(0) - /// var my_storage_image: texture_storage_2d; + /// var my_storage_image: texture_storage_2d; /// ``` /// /// Example GLSL syntax: @@ -6560,7 +6560,7 @@ pub enum StorageTextureAccess { /// Example WGSL syntax: /// ```rust,ignore /// @group(0) @binding(0) - /// var my_storage_image: texture_storage_2d; + /// var my_storage_image: texture_storage_2d; /// ``` /// /// Example GLSL syntax: @@ -6577,7 +6577,7 @@ pub enum StorageTextureAccess { /// Example WGSL syntax: /// ```rust,ignore /// @group(0) @binding(0) - /// var my_storage_image: texture_storage_2d; + /// var my_storage_image: texture_storage_2d; /// ``` /// /// Example GLSL syntax: @@ -6701,8 +6701,8 @@ pub enum BindingType { /// Dimension of the texture view that is going to be sampled. view_dimension: TextureViewDimension, /// True if the texture has a sample count greater than 1. If this is true, - /// the texture must be read from shaders with `texture1DMS`, `texture2DMS`, or `texture3DMS`, - /// depending on `dimension`. + /// the texture must be declared as `texture_multisampled_2d` or + /// `texture_depth_multisampled_2d` in the shader, and read using `textureLoad`. multisampled: bool, }, /// A storage texture. @@ -6710,15 +6710,16 @@ pub enum BindingType { /// Example WGSL syntax: /// ```rust,ignore /// @group(0) @binding(0) - /// var my_storage_image: texture_storage_2d; + /// var my_storage_image: texture_storage_2d; /// ``` /// /// Example GLSL syntax: /// ```cpp,ignore /// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage; /// ``` - /// Note that the texture format must be specified in the shader as well. - /// A list of valid formats can be found in the specification here: + /// Note that the texture format must be specified in the shader, along with the + /// access mode. For WGSL, the format must be one of the enumerants in the list + /// of [storage texel formats](https://gpuweb.github.io/gpuweb/wgsl/#storage-texel-formats). /// /// Corresponds to [WebGPU `GPUStorageTextureBindingLayout`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpustoragetexturebindinglayout). diff --git a/wgpu/src/api/pipeline_layout.rs b/wgpu/src/api/pipeline_layout.rs index 2b89d2b7a..20538dd9e 100644 --- a/wgpu/src/api/pipeline_layout.rs +++ b/wgpu/src/api/pipeline_layout.rs @@ -40,8 +40,8 @@ pub struct PipelineLayoutDescriptor<'a> { /// "set = 0", second entry will provide all the bindings for "set = 1" etc. pub bind_group_layouts: &'a [&'a BindGroupLayout], /// Set of push constant ranges this pipeline uses. Each shader stage that uses push constants - /// must define the range in push constant memory that corresponds to its single `layout(push_constant)` - /// uniform block. + /// must define the range in push constant memory that corresponds to its single `var` + /// buffer. /// /// If this array is non-empty, the [`Features::PUSH_CONSTANTS`] must be enabled. pub push_constant_ranges: &'a [PushConstantRange], From 2fac5e983e0f0d33f127bde04c2c3b0621faa685 Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Sun, 15 Sep 2024 03:07:40 -0500 Subject: [PATCH 08/59] Properly handle the case where Navigator.gpu is undefined and WebGPU is the only compiled backend (#6197) * Properly handle the case where `Navigator.gpu` is undefined and WebGPU is the only compiled backend. Previously, `Instance::request_adapter` would invoke a wasm binding with an undefined arg0, thus crashing the program. Now it will cleanly return `None` instead. Fixes #6196. * Fix typo in `Instance::new` doc comment. * Add note to CHANGELOG.md * Introduce `DefinedNonNullJsValue` type. * Assert definedness of self.gpu in surface_get_capabilities. * Use DefinedNonNullJsValue in signature of get_browser_gpu_property(). * Clarify meaning of gpu field with a comment. --- CHANGELOG.md | 1 + wgpu/src/api/instance.rs | 7 +- wgpu/src/backend/webgpu.rs | 96 +++++++++++++++---- .../webgpu/defined_non_null_js_value.rs | 46 +++++++++ 4 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 wgpu/src/backend/webgpu/defined_non_null_js_value.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 35355b178..a647a0662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). ### Bug Fixes - Fix incorrect hlsl image output type conversion. By @atlv24 in [#6123](https://github.com/gfx-rs/wgpu/pull/6123) +- Fix JS `TypeError` exception in `Instance::request_adapter` when browser doesn't support WebGPU but `wgpu` not compiled with `webgl` support. By @bgr360 in [#6197](https://github.com/gfx-rs/wgpu/pull/6197). #### Naga diff --git a/wgpu/src/api/instance.rs b/wgpu/src/api/instance.rs index 33a7b0f64..af6775b86 100644 --- a/wgpu/src/api/instance.rs +++ b/wgpu/src/api/instance.rs @@ -95,7 +95,7 @@ impl Instance { /// [`Backends::BROWSER_WEBGPU`] takes a special role: /// If it is set and WebGPU support is detected, this instance will *only* be able to create /// WebGPU adapters. If you instead want to force use of WebGL, either - /// disable the `webgpu` compile-time feature or do add the [`Backends::BROWSER_WEBGPU`] + /// disable the `webgpu` compile-time feature or don't add the [`Backends::BROWSER_WEBGPU`] /// flag to the the `instance_desc`'s `backends` field. /// If it is set and WebGPU support is *not* detected, the instance will use wgpu-core /// to create adapters. Meaning that if the `webgl` feature is enabled, it is able to create @@ -118,8 +118,9 @@ impl Instance { { let is_only_available_backend = !cfg!(wgpu_core); let requested_webgpu = _instance_desc.backends.contains(Backends::BROWSER_WEBGPU); - let support_webgpu = - crate::backend::get_browser_gpu_property().map_or(false, |gpu| !gpu.is_undefined()); + let support_webgpu = crate::backend::get_browser_gpu_property() + .map(|maybe_gpu| maybe_gpu.is_some()) + .unwrap_or(false); if is_only_available_backend || (requested_webgpu && support_webgpu) { return Self { diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 7646085f7..e982300e7 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -1,5 +1,6 @@ #![allow(clippy::type_complexity)] +mod defined_non_null_js_value; mod ext_bindings; mod webgpu_sys; @@ -22,6 +23,8 @@ use crate::{ CompilationInfo, SurfaceTargetUnsafe, UncapturedErrorHandler, }; +use defined_non_null_js_value::DefinedNonNullJsValue; + // We need to make a wrapper for some of the handle types returned by the web backend to make them // implement `Send` and `Sync` to match native. // @@ -38,7 +41,10 @@ unsafe impl Send for Sendable {} #[cfg(send_sync)] unsafe impl Sync for Sendable {} -pub(crate) struct ContextWebGpu(webgpu_sys::Gpu); +pub(crate) struct ContextWebGpu { + /// `None` if browser does not advertise support for WebGPU. + gpu: Option>, +} #[cfg(send_sync)] unsafe impl Send for ContextWebGpu {} #[cfg(send_sync)] @@ -189,6 +195,36 @@ impl MakeSendFuture { #[cfg(send_sync)] unsafe impl Send for MakeSendFuture {} +/// Wraps a future that returns `Option` and adds the ability to immediately +/// return None. +pub(crate) struct OptionFuture(Option); + +impl>, T> Future for OptionFuture { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { + // This is safe because we have no Drop implementation to violate the Pin requirements and + // do not provide any means of moving the inner future. + unsafe { + let this = self.get_unchecked_mut(); + match &mut this.0 { + Some(future) => Pin::new_unchecked(future).poll(cx), + None => task::Poll::Ready(None), + } + } + } +} + +impl OptionFuture { + fn some(future: F) -> Self { + Self(Some(future)) + } + + fn none() -> Self { + Self(None) + } +} + fn map_texture_format(texture_format: wgt::TextureFormat) -> webgpu_sys::GpuTextureFormat { use webgpu_sys::GpuTextureFormat as tf; use wgt::TextureFormat; @@ -1046,27 +1082,34 @@ pub enum Canvas { Offscreen(web_sys::OffscreenCanvas), } -/// Returns the browsers gpu object or `None` if the current context is neither the main thread nor a dedicated worker. +#[derive(Debug, Clone, Copy)] +pub struct BrowserGpuPropertyInaccessible; + +/// Returns the browser's gpu object or `Err(BrowserGpuPropertyInaccessible)` if +/// the current context is neither the main thread nor a dedicated worker. /// -/// If WebGPU is not supported, the Gpu property is `undefined` (but *not* necessarily `None`). +/// If WebGPU is not supported, the Gpu property is `undefined`, and so this +/// function will return `Ok(None)`. /// /// See: /// * /// * -pub fn get_browser_gpu_property() -> Option { +pub fn get_browser_gpu_property( +) -> Result>, BrowserGpuPropertyInaccessible> { let global: Global = js_sys::global().unchecked_into(); - if !global.window().is_undefined() { + let maybe_undefined_gpu: webgpu_sys::Gpu = if !global.window().is_undefined() { let navigator = global.unchecked_into::().navigator(); - Some(ext_bindings::NavigatorGpu::gpu(&navigator)) + ext_bindings::NavigatorGpu::gpu(&navigator) } else if !global.worker().is_undefined() { let navigator = global .unchecked_into::() .navigator(); - Some(ext_bindings::NavigatorGpu::gpu(&navigator)) + ext_bindings::NavigatorGpu::gpu(&navigator) } else { - None - } + return Err(BrowserGpuPropertyInaccessible); + }; + Ok(DefinedNonNullJsValue::new(maybe_undefined_gpu)) } impl crate::context::Context for ContextWebGpu { @@ -1096,9 +1139,11 @@ impl crate::context::Context for ContextWebGpu { type SubmissionIndexData = (); type PipelineCacheData = (); - type RequestAdapterFuture = MakeSendFuture< - wasm_bindgen_futures::JsFuture, - fn(JsFutureResult) -> Option, + type RequestAdapterFuture = OptionFuture< + MakeSendFuture< + wasm_bindgen_futures::JsFuture, + fn(JsFutureResult) -> Option, + >, >; type RequestDeviceFuture = MakeSendFuture< wasm_bindgen_futures::JsFuture, @@ -1115,12 +1160,13 @@ impl crate::context::Context for ContextWebGpu { >; fn init(_instance_desc: wgt::InstanceDescriptor) -> Self { - let Some(gpu) = get_browser_gpu_property() else { + let Ok(gpu) = get_browser_gpu_property() else { panic!( "Accessing the GPU is only supported on the main thread or from a dedicated worker" ); }; - ContextWebGpu(gpu) + + ContextWebGpu { gpu } } unsafe fn instance_create_surface( @@ -1190,12 +1236,16 @@ impl crate::context::Context for ContextWebGpu { if let Some(mapped_pref) = mapped_power_preference { mapped_options.power_preference(mapped_pref); } - let adapter_promise = self.0.request_adapter_with_options(&mapped_options); - - MakeSendFuture::new( - wasm_bindgen_futures::JsFuture::from(adapter_promise), - future_request_adapter, - ) + if let Some(gpu) = &self.gpu { + let adapter_promise = gpu.request_adapter_with_options(&mapped_options); + OptionFuture::some(MakeSendFuture::new( + wasm_bindgen_futures::JsFuture::from(adapter_promise), + future_request_adapter, + )) + } else { + // Gpu is undefined; WebGPU is not supported in this browser. + OptionFuture::none() + } } fn adapter_request_device( @@ -1316,7 +1366,11 @@ impl crate::context::Context for ContextWebGpu { let mut mapped_formats = formats.iter().map(|format| map_texture_format(*format)); // Preferred canvas format will only be either "rgba8unorm" or "bgra8unorm". // https://www.w3.org/TR/webgpu/#dom-gpu-getpreferredcanvasformat - let preferred_format = self.0.get_preferred_canvas_format(); + let gpu = self + .gpu + .as_ref() + .expect("Caller could not have created an adapter if gpu is undefined."); + let preferred_format = gpu.get_preferred_canvas_format(); if let Some(index) = mapped_formats.position(|format| format == preferred_format) { formats.swap(0, index); } diff --git a/wgpu/src/backend/webgpu/defined_non_null_js_value.rs b/wgpu/src/backend/webgpu/defined_non_null_js_value.rs new file mode 100644 index 000000000..fc5a8737e --- /dev/null +++ b/wgpu/src/backend/webgpu/defined_non_null_js_value.rs @@ -0,0 +1,46 @@ +use std::ops::{Deref, DerefMut}; + +use wasm_bindgen::JsValue; + +/// Derefs to a [`JsValue`] that's known not to be `undefined` or `null`. +#[derive(Debug)] +pub struct DefinedNonNullJsValue(T); + +impl DefinedNonNullJsValue +where + T: AsRef, +{ + pub fn new(value: T) -> Option { + if value.as_ref().is_undefined() || value.as_ref().is_null() { + None + } else { + Some(Self(value)) + } + } +} + +impl Deref for DefinedNonNullJsValue { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for DefinedNonNullJsValue { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsRef for DefinedNonNullJsValue { + fn as_ref(&self) -> &T { + &self.0 + } +} + +impl AsMut for DefinedNonNullJsValue { + fn as_mut(&mut self) -> &mut T { + &mut self.0 + } +} From 434f1974101daf26dc3fd15c1a356738d6720bff Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Sun, 15 Sep 2024 16:08:13 -0500 Subject: [PATCH 09/59] Document default pipeline layout behavior. (#6275) * Document default pipeline layout behavior. Fixes #6254. * Fix typo * Apply suggestions from code review Co-authored-by: Andreas Reich * Remove text about panics in get_bind_group_layout. We shouldn't advertise the behavior until it is consistent between webgpu and wgpu-core. --------- Co-authored-by: Andreas Reich --- wgpu/src/api/compute_pipeline.rs | 23 +++++++++++++++++++++++ wgpu/src/api/render_pipeline.rs | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/wgpu/src/api/compute_pipeline.rs b/wgpu/src/api/compute_pipeline.rs index 50f17122e..18d4e904e 100644 --- a/wgpu/src/api/compute_pipeline.rs +++ b/wgpu/src/api/compute_pipeline.rs @@ -20,6 +20,10 @@ super::impl_partialeq_eq_hash!(ComputePipeline); impl ComputePipeline { /// Get an object representing the bind group layout at a given index. + /// + /// If this pipeline was created with a [default layout][ComputePipelineDescriptor::layout], + /// then bind groups created with the returned `BindGroupLayout` can only be used with this + /// pipeline. pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout { let context = Arc::clone(&self.context); let data = self @@ -48,6 +52,25 @@ pub struct ComputePipelineDescriptor<'a> { /// Debug label of the pipeline. This will show up in graphics debuggers for easy identification. pub label: Label<'a>, /// The layout of bind groups for this pipeline. + /// + /// If this is set, then [`Device::create_compute_pipeline`] will raise a validation error if + /// the layout doesn't match what the shader module(s) expect. + /// + /// Using the same [`PipelineLayout`] for many [`RenderPipeline`] or [`ComputePipeline`] + /// pipelines guarantees that you don't have to rebind any resources when switching between + /// those pipelines. + /// + /// ## Default pipeline layout + /// + /// If `layout` is `None`, then the pipeline has a [default layout] created and used instead. + /// The default layout is deduced from the shader modules. + /// + /// You can use [`ComputePipeline::get_bind_group_layout`] to create bind groups for use with + /// the default layout. However, these bind groups cannot be used with any other pipelines. This + /// is convenient for simple pipelines, but using an explicit layout is recommended in most + /// cases. + /// + /// [default layout]: https://www.w3.org/TR/webgpu/#default-pipeline-layout pub layout: Option<&'a PipelineLayout>, /// The compiled shader module for this stage. pub module: &'a ShaderModule, diff --git a/wgpu/src/api/render_pipeline.rs b/wgpu/src/api/render_pipeline.rs index 1893f7c7b..3009cde1d 100644 --- a/wgpu/src/api/render_pipeline.rs +++ b/wgpu/src/api/render_pipeline.rs @@ -28,6 +28,9 @@ impl Drop for RenderPipeline { impl RenderPipeline { /// Get an object representing the bind group layout at a given index. + /// + /// If this pipeline was created with a [default layout][RenderPipelineDescriptor::layout], then + /// bind groups created with the returned `BindGroupLayout` can only be used with this pipeline. pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout { let context = Arc::clone(&self.context); let data = self @@ -121,6 +124,24 @@ pub struct RenderPipelineDescriptor<'a> { /// Debug label of the pipeline. This will show up in graphics debuggers for easy identification. pub label: Label<'a>, /// The layout of bind groups for this pipeline. + /// + /// If this is set, then [`Device::create_render_pipeline`] will raise a validation error if + /// the layout doesn't match what the shader module(s) expect. + /// + /// Using the same [`PipelineLayout`] for many [`RenderPipeline`] or [`ComputePipeline`] + /// pipelines guarantees that you don't have to rebind any resources when switching between + /// those pipelines. + /// + /// ## Default pipeline layout + /// + /// If `layout` is `None`, then the pipeline has a [default layout] created and used instead. + /// The default layout is deduced from the shader modules. + /// + /// You can use [`RenderPipeline::get_bind_group_layout`] to create bind groups for use with the + /// default layout. However, these bind groups cannot be used with any other pipelines. This is + /// convenient for simple pipelines, but using an explicit layout is recommended in most cases. + /// + /// [default layout]: https://www.w3.org/TR/webgpu/#default-pipeline-layout pub layout: Option<&'a PipelineLayout>, /// The compiled vertex stage, its entry point, and the input buffers layout. pub vertex: VertexState<'a>, From fc85e4f970a74e69916550a24769065914be8b29 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Wed, 18 Sep 2024 17:39:36 +1200 Subject: [PATCH 10/59] spv-in parse more atomic ops (#5824) * add parsing for spirv::Op::AtomicLoad and spirv::Op::AtomicStore * spv-in parse AtomicExchange and AtomicCompareExchange * add atomic i decrement * bookend atomic store statement with emmitter.finish/emitter.start to suppress a double load expression bookend atomic result expressions with emitter.finish/start to prevent double defs * add atomic iadd, isub, smin, umin, smax, umax, and, or, xor * parse atomic flag test and set, parse atomic flag clear * remove atomic compare exchange work * changelog * moved spirv tests into front/spv/mod.rs * feature gate atomic spv tests because they require wgsl-[in,out] * BlockContext::get_contained_global_variable returns Result * Generate spans covering the entire instruction. Granted, there is pre-existing code in the SPIR-V front end that gets this wrong, but: It doesn't make sense to read `self.data_offset`, and then immediately pass that to `self.span_from_with_op`. The point of that function is to make the span cover the entire instruction, operands included. * Move `From` implementation into spv front end * doc comments, minor cleanups * remove parsing of OpAtomicFlagClear and OpAtomicFlagTestAndSet * sync atomic spvasm files --------- Co-authored-by: Jim Blandy --- CHANGELOG.md | 1 + naga/src/front/atomic_upgrade.rs | 8 +- naga/src/front/spv/error.rs | 6 + naga/src/front/spv/mod.rs | 329 +++++++++++++++--- naga/tests/in/spv/atomic_exchange.spv | Bin 0 -> 1332 bytes naga/tests/in/spv/atomic_exchange.spvasm | 88 +++++ naga/tests/in/spv/atomic_i_add_sub.spv | Bin 0 -> 716 bytes naga/tests/in/spv/atomic_i_add_sub.spvasm | 51 +++ naga/tests/in/spv/atomic_i_decrement.spv | Bin 0 -> 868 bytes naga/tests/in/spv/atomic_i_decrement.spvasm | 64 ++++ naga/tests/in/spv/atomic_i_increment.spvasm | 1 - naga/tests/in/spv/atomic_load_and_store.spv | Bin 0 -> 1280 bytes .../tests/in/spv/atomic_load_and_store.spvasm | 86 +++++ .../out/ir/atomic_i_increment.compact.ron | 4 - naga/tests/out/ir/atomic_i_increment.ron | 4 - 15 files changed, 585 insertions(+), 57 deletions(-) create mode 100644 naga/tests/in/spv/atomic_exchange.spv create mode 100644 naga/tests/in/spv/atomic_exchange.spvasm create mode 100644 naga/tests/in/spv/atomic_i_add_sub.spv create mode 100644 naga/tests/in/spv/atomic_i_add_sub.spvasm create mode 100644 naga/tests/in/spv/atomic_i_decrement.spv create mode 100644 naga/tests/in/spv/atomic_i_decrement.spvasm create mode 100644 naga/tests/in/spv/atomic_load_and_store.spv create mode 100644 naga/tests/in/spv/atomic_load_and_store.spvasm diff --git a/CHANGELOG.md b/CHANGELOG.md index a647a0662..d42d72589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Support constant evaluation for `firstLeadingBit` and `firstTrailingBit` numeric built-ins in WGSL. Front-ends that translate to these built-ins also benefit from constant evaluation. By @ErichDonGubler in [#5101](https://github.com/gfx-rs/wgpu/pull/5101). - Add `first` and `either` sampling types for `@interpolate(flat, …)` in WGSL. By @ErichDonGubler in [#6181](https://github.com/gfx-rs/wgpu/pull/6181). +- Support for more atomic ops in the SPIR-V frontend. By @schell in [#5824](https://github.com/gfx-rs/wgpu/pull/5824). #### Vulkan diff --git a/naga/src/front/atomic_upgrade.rs b/naga/src/front/atomic_upgrade.rs index c59969ace..d2fbc1289 100644 --- a/naga/src/front/atomic_upgrade.rs +++ b/naga/src/front/atomic_upgrade.rs @@ -44,12 +44,8 @@ pub enum Error { MultiMemberStruct, #[error("encountered unsupported global initializer in an atomic variable")] GlobalInitUnsupported, -} - -impl From for crate::front::spv::Error { - fn from(source: Error) -> Self { - crate::front::spv::Error::AtomicUpgradeError(source) - } + #[error("expected to find a global variable")] + GlobalVariableMissing, } #[derive(Clone, Default)] diff --git a/naga/src/front/spv/error.rs b/naga/src/front/spv/error.rs index 42df9d807..bcbb47dde 100644 --- a/naga/src/front/spv/error.rs +++ b/naga/src/front/spv/error.rs @@ -159,3 +159,9 @@ impl Error { String::from_utf8(writer.into_inner()).unwrap() } } + +impl From for Error { + fn from(source: atomic_upgrade::Error) -> Self { + crate::front::spv::Error::AtomicUpgradeError(source) + } +} diff --git a/naga/src/front/spv/mod.rs b/naga/src/front/spv/mod.rs index 7dfb4ae29..5ad063a6b 100644 --- a/naga/src/front/spv/mod.rs +++ b/naga/src/front/spv/mod.rs @@ -565,11 +565,15 @@ impl<'a> BlockContext<'a> { /// Descend into the expression with the given handle, locating a contained /// global variable. /// + /// If the expression doesn't actually refer to something in a global + /// variable, we can't upgrade its type in a way that Naga validation would + /// pass, so reject the input instead. + /// /// This is used to track atomic upgrades. fn get_contained_global_variable( &self, mut handle: Handle, - ) -> Option> { + ) -> Result, Error> { log::debug!("\t\tlocating global variable in {handle:?}"); loop { match self.expressions[handle] { @@ -583,14 +587,16 @@ impl<'a> BlockContext<'a> { } crate::Expression::GlobalVariable(h) => { log::debug!("\t\t found {h:?}"); - return Some(h); + return Ok(h); } _ => { break; } } } - None + Err(Error::AtomicUpgradeError( + crate::front::atomic_upgrade::Error::GlobalVariableMissing, + )) } } @@ -1323,6 +1329,109 @@ impl> Frontend { )) } + /// Return the Naga [`Expression`] for `pointer_id`, and its referent [`Type`]. + /// + /// Return a [`Handle`] for a Naga [`Expression`] that holds the value of + /// the SPIR-V instruction `pointer_id`, along with the [`Type`] to which it + /// is a pointer. + /// + /// This may entail spilling `pointer_id`'s value to a temporary: + /// see [`get_expr_handle`]'s documentation. + /// + /// [`Expression`]: crate::Expression + /// [`Type`]: crate::Type + /// [`Handle`]: crate::Handle + /// [`get_expr_handle`]: Frontend::get_expr_handle + fn get_exp_and_base_ty_handles( + &self, + pointer_id: spirv::Word, + ctx: &mut BlockContext, + emitter: &mut crate::proc::Emitter, + block: &mut crate::Block, + body_idx: usize, + ) -> Result<(Handle, Handle), Error> { + log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id); + let p_lexp_handle; + let p_lexp_ty_id; + { + let lexp = self.lookup_expression.lookup(pointer_id)?; + p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx); + p_lexp_ty_id = lexp.type_id; + }; + + log::trace!("\t\t\tlooking up pointer type {pointer_id:?}"); + let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?; + let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?; + + log::trace!("\t\t\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}"); + let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?; + + Ok((p_lexp_handle, p_base_ty.handle)) + } + + #[allow(clippy::too_many_arguments)] + fn parse_atomic_expr_with_value( + &mut self, + inst: Instruction, + emitter: &mut crate::proc::Emitter, + ctx: &mut BlockContext, + block: &mut crate::Block, + block_id: spirv::Word, + body_idx: usize, + atomic_function: crate::AtomicFunction, + ) -> Result<(), Error> { + inst.expect(7)?; + let start = self.data_offset; + let result_type_id = self.next()?; + let result_id = self.next()?; + let pointer_id = self.next()?; + let _scope_id = self.next()?; + let _memory_semantics_id = self.next()?; + let value_id = self.next()?; + let span = self.span_from_with_op(start); + + let (p_lexp_handle, p_base_ty_handle) = + self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?; + + log::trace!("\t\t\tlooking up value expr {value_id:?}"); + let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle; + + block.extend(emitter.finish(ctx.expressions)); + // Create an expression for our result + let r_lexp_handle = { + let expr = crate::Expression::AtomicResult { + ty: p_base_ty_handle, + comparison: false, + }; + let handle = ctx.expressions.append(expr, span); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle, + type_id: result_type_id, + block_id, + }, + ); + handle + }; + emitter.start(ctx.expressions); + + // Create a statement for the op itself + let stmt = crate::Statement::Atomic { + pointer: p_lexp_handle, + fun: atomic_function, + value: v_lexp_handle, + result: Some(r_lexp_handle), + }; + block.push(stmt, span); + + // Store any associated global variables so we can upgrade their types later + self.upgrade_atomics + .insert(ctx.get_contained_global_variable(p_lexp_handle)?); + + Ok(()) + } + /// Add the next SPIR-V block's contents to `block_ctx`. /// /// Except for the function's entry block, `block_id` should be the label of @@ -3985,35 +4094,91 @@ impl> Frontend { ); emitter.start(ctx.expressions); } - Op::AtomicIIncrement => { + Op::AtomicLoad => { inst.expect(6)?; let start = self.data_offset; - let span = self.span_from_with_op(start); let result_type_id = self.next()?; let result_id = self.next()?; let pointer_id = self.next()?; let _scope_id = self.next()?; let _memory_semantics_id = self.next()?; + let span = self.span_from_with_op(start); log::trace!("\t\t\tlooking up expr {:?}", pointer_id); - let (p_lexp_handle, p_lexp_ty_id) = { - let lexp = self.lookup_expression.lookup(pointer_id)?; - let handle = get_expr_handle!(pointer_id, &lexp); - (handle, lexp.type_id) + let p_lexp_handle = + get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?); + + // Create an expression for our result + let expr = crate::Expression::Load { + pointer: p_lexp_handle, }; + let handle = ctx.expressions.append(expr, span); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle, + type_id: result_type_id, + block_id, + }, + ); - log::trace!("\t\t\tlooking up type {pointer_id:?}"); - let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?; - let p_ty_base_id = - p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?; + // Store any associated global variables so we can upgrade their types later + self.upgrade_atomics + .insert(ctx.get_contained_global_variable(p_lexp_handle)?); + } + Op::AtomicStore => { + inst.expect(5)?; + let start = self.data_offset; + let pointer_id = self.next()?; + let _scope_id = self.next()?; + let _memory_semantics_id = self.next()?; + let value_id = self.next()?; + let span = self.span_from_with_op(start); - log::trace!("\t\t\tlooking up base type {p_ty_base_id:?} of {p_ty:?}"); - let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?; + log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id); + let p_lexp_handle = + get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?); + log::trace!("\t\t\tlooking up value expr {:?}", pointer_id); + let v_lexp_handle = + get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?); + + block.extend(emitter.finish(ctx.expressions)); + // Create a statement for the op itself + let stmt = crate::Statement::Store { + pointer: p_lexp_handle, + value: v_lexp_handle, + }; + block.push(stmt, span); + emitter.start(ctx.expressions); + + // Store any associated global variables so we can upgrade their types later + self.upgrade_atomics + .insert(ctx.get_contained_global_variable(p_lexp_handle)?); + } + Op::AtomicIIncrement | Op::AtomicIDecrement => { + inst.expect(6)?; + let start = self.data_offset; + let result_type_id = self.next()?; + let result_id = self.next()?; + let pointer_id = self.next()?; + let _scope_id = self.next()?; + let _memory_semantics_id = self.next()?; + let span = self.span_from_with_op(start); + + let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles( + pointer_id, + ctx, + &mut emitter, + &mut block, + body_idx, + )?; + + block.extend(emitter.finish(ctx.expressions)); // Create an expression for our result let r_lexp_handle = { let expr = crate::Expression::AtomicResult { - ty: p_base_ty.handle, + ty: p_base_ty_h, comparison: false, }; let handle = ctx.expressions.append(expr, span); @@ -4027,22 +4192,26 @@ impl> Frontend { ); handle }; + emitter.start(ctx.expressions); - // Create a literal "1" since WGSL lacks an increment operation + // Create a literal "1" to use as our value let one_lexp_handle = make_index_literal( ctx, 1, &mut block, &mut emitter, - p_base_ty.handle, - p_lexp_ty_id, + p_base_ty_h, + result_type_id, span, )?; // Create a statement for the op itself let stmt = crate::Statement::Atomic { - pointer: p_lexp_handle, - fun: crate::AtomicFunction::Add, + pointer: p_exp_h, + fun: match inst.op { + Op::AtomicIIncrement => crate::AtomicFunction::Add, + _ => crate::AtomicFunction::Subtract, + }, value: one_lexp_handle, result: Some(r_lexp_handle), }; @@ -4050,8 +4219,38 @@ impl> Frontend { // Store any associated global variables so we can upgrade their types later self.upgrade_atomics - .extend(ctx.get_contained_global_variable(p_lexp_handle)); + .insert(ctx.get_contained_global_variable(p_exp_h)?); } + Op::AtomicExchange + | Op::AtomicIAdd + | Op::AtomicISub + | Op::AtomicSMin + | Op::AtomicUMin + | Op::AtomicSMax + | Op::AtomicUMax + | Op::AtomicAnd + | Op::AtomicOr + | Op::AtomicXor => self.parse_atomic_expr_with_value( + inst, + &mut emitter, + ctx, + &mut block, + block_id, + body_idx, + match inst.op { + Op::AtomicExchange => crate::AtomicFunction::Exchange { compare: None }, + Op::AtomicIAdd => crate::AtomicFunction::Add, + Op::AtomicISub => crate::AtomicFunction::Subtract, + Op::AtomicSMin => crate::AtomicFunction::Min, + Op::AtomicUMin => crate::AtomicFunction::Min, + Op::AtomicSMax => crate::AtomicFunction::Max, + Op::AtomicUMax => crate::AtomicFunction::Max, + Op::AtomicAnd => crate::AtomicFunction::And, + Op::AtomicOr => crate::AtomicFunction::InclusiveOr, + _ => crate::AtomicFunction::ExclusiveOr, + }, + )?, + _ => { return Err(Error::UnsupportedInstruction(self.state, inst.op)); } @@ -5709,33 +5908,48 @@ mod test { ]; let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap(); } +} - #[cfg(all(feature = "wgsl-in", wgsl_out))] - #[test] - fn atomic_i_inc() { +#[cfg(all(test, feature = "wgsl-in", wgsl_out))] +mod test_atomic { + fn atomic_test(bytes: &[u8]) { let _ = env_logger::builder().is_test(true).try_init(); - let bytes = include_bytes!("../../../tests/in/spv/atomic_i_increment.spv"); - let m = super::parse_u8_slice(bytes, &Default::default()).unwrap(); - let mut validator = crate::valid::Validator::new( + let m = crate::front::spv::parse_u8_slice(bytes, &Default::default()).unwrap(); + + let mut wgsl = String::new(); + let mut should_panic = false; + + for vflags in [ + crate::valid::ValidationFlags::all(), crate::valid::ValidationFlags::empty(), - Default::default(), - ); - let info = match validator.validate(&m) { - Err(e) => { - log::error!("{}", e.emit_to_string("")); - return; - } - Ok(i) => i, - }; - let wgsl = - crate::back::wgsl::write_string(&m, &info, crate::back::wgsl::WriterFlags::empty()) - .unwrap(); - log::info!("atomic_i_increment:\n{wgsl}"); + ] { + let mut validator = crate::valid::Validator::new(vflags, Default::default()); + match validator.validate(&m) { + Err(e) => { + log::error!("SPIR-V validation {}", e.emit_to_string("")); + should_panic = true; + } + Ok(i) => { + wgsl = crate::back::wgsl::write_string( + &m, + &i, + crate::back::wgsl::WriterFlags::empty(), + ) + .unwrap(); + log::info!("wgsl-out:\n{wgsl}"); + break; + } + }; + } + + if should_panic { + panic!("validation error"); + } let m = match crate::front::wgsl::parse_str(&wgsl) { Ok(m) => m, Err(e) => { - log::error!("{}", e.emit_to_string(&wgsl)); + log::error!("round trip WGSL validation {}", e.emit_to_string(&wgsl)); panic!("invalid module"); } }; @@ -5746,4 +5960,35 @@ mod test { panic!("invalid generated wgsl"); } } + + #[test] + fn atomic_i_inc() { + atomic_test(include_bytes!( + "../../../tests/in/spv/atomic_i_increment.spv" + )); + } + + #[test] + fn atomic_load_and_store() { + atomic_test(include_bytes!( + "../../../tests/in/spv/atomic_load_and_store.spv" + )); + } + + #[test] + fn atomic_exchange() { + atomic_test(include_bytes!("../../../tests/in/spv/atomic_exchange.spv")); + } + + #[test] + fn atomic_i_decrement() { + atomic_test(include_bytes!( + "../../../tests/in/spv/atomic_i_decrement.spv" + )); + } + + #[test] + fn atomic_i_add_and_sub() { + atomic_test(include_bytes!("../../../tests/in/spv/atomic_i_add_sub.spv")); + } } diff --git a/naga/tests/in/spv/atomic_exchange.spv b/naga/tests/in/spv/atomic_exchange.spv new file mode 100644 index 0000000000000000000000000000000000000000..cc64ce9aa8138334fc76425b7b235aa06ba0484d GIT binary patch literal 1332 zcmYk5OHY$g5Qe7|Bu1drf(mN+D&Dvl;|0Vb8dkVO{{U%7gD%uXyKv#cpX6V1;ZH!k z#Kh-0J%{up)49F#&Y3x1>l3TfF;$C*M!e$J(~OBQqW-=RF&Fg|_qP!F)9u>>{@JXp?sr=9C(>`d1L|bQN78V%IXS^R%(u$LP+J zv4pL!>{6%iUB-Rq*SCjt%g8o+bvF4u&iaeW>*#%Q-u(t5m-Cj`O>Fs$?a@!ZHe&oR zzwZjRdE>ufsL*n&Ph&dG*})cPKi|T)H7DO~#Cmgg5OY82bvN6n#Cs1dulfzWt-X)P z?J2P}Y-2U#8#v!+9T98pjEOeToxO2Z5cP2(H#vK*REVkdyG9pGT#{OdL?RVuYYTwXzN H>>&RER=-in literal 0 HcmV?d00001 diff --git a/naga/tests/in/spv/atomic_exchange.spvasm b/naga/tests/in/spv/atomic_exchange.spvasm new file mode 100644 index 000000000..09258f058 --- /dev/null +++ b/naga/tests/in/spv/atomic_exchange.spvasm @@ -0,0 +1,88 @@ +; SPIR-V +; Version: 1.5 +; Generator: Google rspirv; 0 +; Bound: 63 +; Schema: 0 + OpCapability Shader + OpCapability VulkanMemoryModel + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %1 "stage::test_atomic_exchange" %2 %3 + OpExecutionMode %1 LocalSize 32 1 1 + OpMemberDecorate %_struct_11 0 Offset 0 + OpMemberDecorate %_struct_11 1 Offset 4 + OpDecorate %_struct_12 Block + OpMemberDecorate %_struct_12 0 Offset 0 + OpDecorate %2 Binding 0 + OpDecorate %2 DescriptorSet 0 + OpDecorate %3 NonWritable + OpDecorate %3 Binding 1 + OpDecorate %3 DescriptorSet 0 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %15 = OpTypeFunction %void + %bool = OpTypeBool + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %false = OpConstantFalse %bool +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %uint_1 = OpConstant %uint 1 + %_struct_11 = OpTypeStruct %uint %uint + %22 = OpUndef %_struct_11 + %int = OpTypeInt 32 1 + %true = OpConstantTrue %bool + %_struct_12 = OpTypeStruct %uint +%_ptr_StorageBuffer__struct_12 = OpTypePointer StorageBuffer %_struct_12 + %2 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer + %3 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer + %26 = OpUndef %uint + %1 = OpFunction %void None %15 + %27 = OpLabel + %28 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0 + %29 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 + %30 = OpLoad %uint %29 + %31 = OpCompositeConstruct %_struct_11 %uint_0 %30 + OpBranch %32 + %32 = OpLabel + %33 = OpPhi %_struct_11 %31 %27 %34 %35 + %36 = OpPhi %uint %uint_0 %27 %37 %35 + OpLoopMerge %38 %35 None + OpBranch %39 + %39 = OpLabel + %40 = OpCompositeExtract %uint %33 0 + %41 = OpCompositeExtract %uint %33 1 + %42 = OpULessThan %bool %40 %41 + OpSelectionMerge %43 None + OpBranchConditional %42 %44 %45 + %44 = OpLabel + %47 = OpIAdd %uint %40 %uint_1 + %49 = OpCompositeInsert %_struct_11 %47 %33 0 + %50 = OpCompositeConstruct %_struct_11 %uint_1 %40 + OpBranch %43 + %45 = OpLabel + %51 = OpCompositeInsert %_struct_11 %uint_0 %22 0 + OpBranch %43 + %43 = OpLabel + %52 = OpPhi %_struct_11 %49 %44 %33 %45 + %53 = OpPhi %_struct_11 %50 %44 %51 %45 + %54 = OpCompositeExtract %uint %53 0 + %55 = OpBitcast %int %54 + OpSelectionMerge %56 None + OpSwitch %55 %57 0 %58 1 %59 + %57 = OpLabel + OpBranch %56 + %58 = OpLabel + OpBranch %56 + %59 = OpLabel + %60 = OpAtomicExchange %uint %28 %uint_2 %uint_0 %36 + %61 = OpIAdd %uint %36 %60 + OpBranch %56 + %56 = OpLabel + %62 = OpPhi %bool %false %57 %false %58 %true %59 + %34 = OpPhi %_struct_11 %22 %57 %22 %58 %52 %59 + %37 = OpPhi %uint %26 %57 %26 %58 %61 %59 + OpBranch %35 + %35 = OpLabel + OpBranchConditional %62 %32 %38 + %38 = OpLabel + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/atomic_i_add_sub.spv b/naga/tests/in/spv/atomic_i_add_sub.spv new file mode 100644 index 0000000000000000000000000000000000000000..8c268504002cd9317f7a4dc728dd6ad185e81093 GIT binary patch literal 716 zcmYk3+e$)V5QQh*W+&6q&MBIg-A5P%LQrq9v6S#4x^VYiy;8UJ039Ic`?hX!V4PVq zYi8EJEtmS$sH{XpJv#hqJ5dTN#T;_KW-CqQ8fpb#NEgnrz!3_PD!AG;dMcBDSfiU4DmLt>P{_YD78K&KEoD z1%T=dbY`^qjg9N}VjezNtLsQsYT-U)t+tIK@IS5sFT aSA(~lzXnK=0c1YdJ##%m46X6Dv?_)kE<~O%DZHWzNa$+`l~UCU)J?KfAv~k zYmd^ozP-EuV((zRoNrv#SJ`KZIKe#2s7-@y=FWinE97RuCOLPvzlHCv#t9m8pgrae z3e5(-r;Hcy-EAMPy^EOKCD2&iZc$stcZT;J?N?)uQJ=XJc*g3TeBORzSkp8gqn?&-gBC$;3C5}DI~DzrOAY|kS+=XEjjzO(P?zRpzN kIsaqxZ~Q%Zdzjn=lUw$BZ@Hp7+vmIZCg#N-Ui=9A1>XZQ#sB~S literal 0 HcmV?d00001 diff --git a/naga/tests/in/spv/atomic_i_decrement.spvasm b/naga/tests/in/spv/atomic_i_decrement.spvasm new file mode 100644 index 000000000..cc125beec --- /dev/null +++ b/naga/tests/in/spv/atomic_i_decrement.spvasm @@ -0,0 +1,64 @@ +; SPIR-V +; Version: 1.5 +; Generator: Google rspirv; 0 +; Bound: 42 +; Schema: 0 + OpCapability Shader + OpCapability VulkanMemoryModel + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %1 "stage::test_atomic_i_decrement" %2 %3 + OpExecutionMode %1 LocalSize 32 1 1 + OpDecorate %_runtimearr_uint ArrayStride 4 + OpDecorate %_struct_7 Block + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_8 Block + OpMemberDecorate %_struct_8 0 Offset 0 + OpDecorate %2 Binding 0 + OpDecorate %2 DescriptorSet 0 + OpDecorate %3 Binding 1 + OpDecorate %3 DescriptorSet 0 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %bool = OpTypeBool +%_runtimearr_uint = OpTypeRuntimeArray %uint + %_struct_7 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_7 = OpTypePointer StorageBuffer %_struct_7 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %false = OpConstantFalse %bool +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %true = OpConstantTrue %bool + %_struct_8 = OpTypeStruct %uint +%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8 + %2 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer + %3 = OpVariable %_ptr_StorageBuffer__struct_7 StorageBuffer + %1 = OpFunction %void None %11 + %21 = OpLabel + %22 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0 + %24 = OpArrayLength %uint %3 0 + OpBranch %25 + %25 = OpLabel + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %29 = OpAtomicIDecrement %uint %22 %uint_2 %uint_0 + %30 = OpULessThan %bool %29 %24 + OpSelectionMerge %31 None + OpBranchConditional %30 %32 %33 + %32 = OpLabel + %34 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 %29 + OpStore %34 %29 + %35 = OpIEqual %bool %29 %uint_0 + %41 = OpSelect %bool %35 %false %true + OpBranch %31 + %33 = OpLabel + OpBranch %31 + %31 = OpLabel + %40 = OpPhi %bool %41 %32 %false %33 + OpBranch %27 + %27 = OpLabel + OpBranchConditional %40 %25 %26 + %26 = OpLabel + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/atomic_i_increment.spvasm b/naga/tests/in/spv/atomic_i_increment.spvasm index 4586102d2..c072ca298 100644 --- a/naga/tests/in/spv/atomic_i_increment.spvasm +++ b/naga/tests/in/spv/atomic_i_increment.spvasm @@ -59,4 +59,3 @@ %26 = OpLabel OpReturn OpFunctionEnd - diff --git a/naga/tests/in/spv/atomic_load_and_store.spv b/naga/tests/in/spv/atomic_load_and_store.spv new file mode 100644 index 0000000000000000000000000000000000000000..e2e9ddfd0b8d20e693764d2a2ff9b9f8179a0788 GIT binary patch literal 1280 zcmYk5*-leY6owZqNQ_7s1*f_zIDp0h6|v}rS9mGDK+{NLOw=a!TzcUn`AP;}0VhJ@ z_nn?i&Px8SdEBSX#@cjDO+-X1Uh*r=MmL>U4})&E7>tTu zzZic0_`dgP*gxp?zZ~>N#qjHZm@u=*wDEfjePV)K^-I8+R^SO3yHVA3pEk1h7|%Qz zmyy-QE_v$CQrF#IeVbdlk2NLt`$O~<_-lO5dli`Pj@fn%*&5nj(Zk-?f%do6o3@el zYyV4y8*ppOk7K&rEg>6czYg#Xeo@bNRhGif;qcj3}M`smvLo51?Y->drfkDH5wiF2 zJ*9e_@%bi?k?rFw30Nchwvg?m&wBdu?oW{QOW)Yd@8v1b?%vPf(h0ZMzFle&&*9!{ z2RO5KXER=1e_c23kbC$BFM!{N-@Ciz+jWukX+Pqg^%#BidiFVv^W|IZBKu!)2D!cc m#_eg0Z_okW&=|Qf-pm;5`rSCU+&8wS-<;o+cKInA?|}a@eo%V= literal 0 HcmV?d00001 diff --git a/naga/tests/in/spv/atomic_load_and_store.spvasm b/naga/tests/in/spv/atomic_load_and_store.spvasm new file mode 100644 index 000000000..f65600c43 --- /dev/null +++ b/naga/tests/in/spv/atomic_load_and_store.spvasm @@ -0,0 +1,86 @@ +; SPIR-V +; Version: 1.5 +; Generator: Google rspirv; 0 +; Bound: 60 +; Schema: 0 + OpCapability Shader + OpCapability VulkanMemoryModel + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %1 "stage::test_atomic_load_and_store" %2 %3 + OpExecutionMode %1 LocalSize 32 1 1 + OpMemberDecorate %_struct_11 0 Offset 0 + OpMemberDecorate %_struct_11 1 Offset 4 + OpDecorate %_struct_12 Block + OpMemberDecorate %_struct_12 0 Offset 0 + OpDecorate %2 Binding 0 + OpDecorate %2 DescriptorSet 0 + OpDecorate %3 NonWritable + OpDecorate %3 Binding 1 + OpDecorate %3 DescriptorSet 0 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %15 = OpTypeFunction %void + %bool = OpTypeBool + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %false = OpConstantFalse %bool +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %uint_1 = OpConstant %uint 1 + %_struct_11 = OpTypeStruct %uint %uint + %22 = OpUndef %_struct_11 + %int = OpTypeInt 32 1 + %true = OpConstantTrue %bool + %_struct_12 = OpTypeStruct %uint +%_ptr_StorageBuffer__struct_12 = OpTypePointer StorageBuffer %_struct_12 + %2 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer + %3 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer + %1 = OpFunction %void None %15 + %26 = OpLabel + %27 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0 + %28 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 + %29 = OpLoad %uint %28 + %30 = OpCompositeConstruct %_struct_11 %uint_0 %29 + OpBranch %31 + %31 = OpLabel + %32 = OpPhi %_struct_11 %30 %26 %33 %34 + OpLoopMerge %35 %34 None + OpBranch %36 + %36 = OpLabel + %37 = OpCompositeExtract %uint %32 0 + %38 = OpCompositeExtract %uint %32 1 + %39 = OpULessThan %bool %37 %38 + OpSelectionMerge %40 None + OpBranchConditional %39 %41 %42 + %41 = OpLabel + %44 = OpIAdd %uint %37 %uint_1 + %46 = OpCompositeInsert %_struct_11 %44 %32 0 + %47 = OpCompositeConstruct %_struct_11 %uint_1 %37 + OpBranch %40 + %42 = OpLabel + %48 = OpCompositeInsert %_struct_11 %uint_0 %22 0 + OpBranch %40 + %40 = OpLabel + %49 = OpPhi %_struct_11 %46 %41 %32 %42 + %50 = OpPhi %_struct_11 %47 %41 %48 %42 + %51 = OpCompositeExtract %uint %50 0 + %52 = OpBitcast %int %51 + OpSelectionMerge %53 None + OpSwitch %52 %54 0 %55 1 %56 + %54 = OpLabel + OpBranch %53 + %55 = OpLabel + OpBranch %53 + %56 = OpLabel + %57 = OpAtomicLoad %uint %27 %uint_2 %uint_0 + %58 = OpIAdd %uint %57 %uint_2 + OpAtomicStore %27 %uint_2 %uint_0 %58 + OpBranch %53 + %53 = OpLabel + %59 = OpPhi %bool %false %54 %false %55 %true %56 + %33 = OpPhi %_struct_11 %22 %54 %22 %55 %49 %56 + OpBranch %34 + %34 = OpLabel + OpBranchConditional %59 %31 %35 + %35 = OpLabel + OpReturn + OpFunctionEnd diff --git a/naga/tests/out/ir/atomic_i_increment.compact.ron b/naga/tests/out/ir/atomic_i_increment.compact.ron index 58b01f587..12a4692a3 100644 --- a/naga/tests/out/ir/atomic_i_increment.compact.ron +++ b/naga/tests/out/ir/atomic_i_increment.compact.ron @@ -216,10 +216,6 @@ ), ], reject: [ - Emit(( - start: 13, - end: 14, - )), Atomic( pointer: 7, fun: Add, diff --git a/naga/tests/out/ir/atomic_i_increment.ron b/naga/tests/out/ir/atomic_i_increment.ron index 2c5528921..82fa97502 100644 --- a/naga/tests/out/ir/atomic_i_increment.ron +++ b/naga/tests/out/ir/atomic_i_increment.ron @@ -241,10 +241,6 @@ ), ], reject: [ - Emit(( - start: 14, - end: 15, - )), Atomic( pointer: 8, fun: Add, From ab9a78c64b9fcbaff7409bac80b0b19ba0d152cc Mon Sep 17 00:00:00 2001 From: mahkoh Date: Wed, 18 Sep 2024 15:53:50 +0200 Subject: [PATCH 11/59] Update parking_lot dependency (#6287) wgpu-hal does not compile with parking_lot versions that don't contain const Mutex::new. --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d42d72589..34bc30131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -144,6 +144,10 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Replace `winapi` code to use the `windows` crate. By @MarijnS95 in [#5956](https://github.com/gfx-rs/wgpu/pull/5956) and [#6173](https://github.com/gfx-rs/wgpu/pull/6173) +#### HAL + +- Update `parking_lot` to `0.12`. By @mahkoh in [#6287](https://github.com/gfx-rs/wgpu/pull/6287) + ## 22.0.0 (2024-07-17) ### Overview diff --git a/Cargo.toml b/Cargo.toml index 08bfffe81..8e2fa5cb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,7 +105,7 @@ noise = { version = "0.8", git = "https://github.com/Razaekel/noise-rs.git", rev nv-flip = "0.1" obj = "0.10" once_cell = "1.19.0" -parking_lot = ">=0.11, <0.13" # parking_lot 0.12 switches from `winapi` to `windows`; permit either +parking_lot = "0.12.1" pico-args = { version = "0.5.0", features = [ "eq-separator", "short-space-opt", From c3ab12aa29cfd1685dd84d378af0aa02db0b7e75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:34:12 -0400 Subject: [PATCH 12/59] build(deps): bump the patch-updates group across 1 directory with 11 updates (#6293) Bumps the patch-updates group with 11 updates in the / directory: | Package | From | To | | --- | --- | --- | | [anyhow](https://github.com/dtolnay/anyhow) | `1.0.87` | `1.0.89` | | [glow](https://github.com/grovesNL/glow) | `0.14.0` | `0.14.1` | | [arrayref](https://github.com/droundy/arrayref) | `0.3.8` | `0.3.9` | | [bytes](https://github.com/tokio-rs/bytes) | `1.7.1` | `1.7.2` | | [cc](https://github.com/rust-lang/cc-rs) | `1.1.18` | `1.1.21` | | [memmap2](https://github.com/RazrFalcon/memmap2-rs) | `0.9.4` | `0.9.5` | | [rustix](https://github.com/bytecodealliance/rustix) | `0.38.36` | `0.38.37` | | [toml_edit](https://github.com/toml-rs/toml) | `0.22.20` | `0.22.21` | | [unicode-ident](https://github.com/dtolnay/unicode-ident) | `1.0.12` | `1.0.13` | | [unicode-normalization](https://github.com/unicode-rs/unicode-normalization) | `0.1.23` | `0.1.24` | | [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) | `1.11.0` | `1.12.0` | Updates `anyhow` from 1.0.87 to 1.0.89 - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.87...1.0.89) Updates `glow` from 0.14.0 to 0.14.1 - [Commits](https://github.com/grovesNL/glow/commits) Updates `arrayref` from 0.3.8 to 0.3.9 - [Commits](https://github.com/droundy/arrayref/commits) Updates `bytes` from 1.7.1 to 1.7.2 - [Release notes](https://github.com/tokio-rs/bytes/releases) - [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/bytes/compare/v1.7.1...v1.7.2) Updates `cc` from 1.1.18 to 1.1.21 - [Release notes](https://github.com/rust-lang/cc-rs/releases) - [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.18...cc-v1.1.21) Updates `memmap2` from 0.9.4 to 0.9.5 - [Changelog](https://github.com/RazrFalcon/memmap2-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/RazrFalcon/memmap2-rs/compare/v0.9.4...v0.9.5) Updates `rustix` from 0.38.36 to 0.38.37 - [Release notes](https://github.com/bytecodealliance/rustix/releases) - [Changelog](https://github.com/bytecodealliance/rustix/blob/main/CHANGELOG.md) - [Commits](https://github.com/bytecodealliance/rustix/compare/v0.38.36...v0.38.37) Updates `toml_edit` from 0.22.20 to 0.22.21 - [Commits](https://github.com/toml-rs/toml/compare/v0.22.20...v0.22.21) Updates `unicode-ident` from 1.0.12 to 1.0.13 - [Release notes](https://github.com/dtolnay/unicode-ident/releases) - [Commits](https://github.com/dtolnay/unicode-ident/compare/1.0.12...1.0.13) Updates `unicode-normalization` from 0.1.23 to 0.1.24 - [Commits](https://github.com/unicode-rs/unicode-normalization/compare/v0.1.23...v0.1.24) Updates `unicode-segmentation` from 1.11.0 to 1.12.0 - [Commits](https://github.com/unicode-rs/unicode-segmentation/compare/v1.11.0...v1.12.0) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: glow dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: arrayref dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: bytes dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: cc dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: memmap2 dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: rustix dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: toml_edit dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: unicode-ident dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: unicode-normalization dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: unicode-segmentation dependency-type: indirect update-type: version-update:semver-minor dependency-group: patch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 46 +++++++++++++++++++++++----------------------- Cargo.toml | 4 ++-- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60a21c223..86d027a4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "arbitrary" @@ -206,9 +206,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -396,9 +396,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "calloop" @@ -434,9 +434,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.18" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", @@ -1308,9 +1308,9 @@ checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" [[package]] name = "glow" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f865cbd94bd355b89611211e49508da98a1fce0ad755c1e8448fb96711b24528" +checksum = "2f4a888dbe8181a7535853469c21c67ca9a1cea9460b16808fc018ea9e55d248" dependencies = [ "js-sys", "slotmap", @@ -1694,7 +1694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -1795,9 +1795,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] @@ -2610,9 +2610,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.36" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -3082,9 +3082,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", "toml_datetime", @@ -3221,24 +3221,24 @@ checksum = "bc3882f69607a2ac8cc4de3ee7993d8f68bb06f2974271195065b3bd07f2edea" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" diff --git a/Cargo.toml b/Cargo.toml index 8e2fa5cb8..da5ecbd37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ path = "./naga" version = "22.0.0" [workspace.dependencies] -anyhow = "1.0.87" +anyhow = "1.0.89" argh = "0.1.5" arrayvec = "0.7" bincode = "1" @@ -153,7 +153,7 @@ windows-core = { version = "0.58", default-features = false } # Gles dependencies khronos-egl = "6" -glow = "0.14.0" +glow = "0.14.1" glutin = { version = "0.31", default-features = false } glutin-winit = { version = "0.4", default-features = false } glutin_wgl_sys = "0.6" From 3fda684eb9e69c78b16312a3e927e3ea82e853d1 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Mon, 16 Sep 2024 12:56:58 -0700 Subject: [PATCH 13/59] [naga msl-out] Defeat the MSL compiler's infinite loop analysis. See the comments in the code for details. This patch emits the definition of the macro only when the first loop is encountered. This does make that first loop's code look a bit odd: it would be more natural to define the macro at the top of the file. (See the modified files in `naga/tests/out/msl`.) Rejected alternatives: - We could emit the macro definition unconditionally at the top of the file. But this changes every MSL snapshot output file, whereas only eight of them actually contain loops. - We could have the validator flag modules that contain loops. But the changes end up being not small, and spread across the validator, so this seems disproportionate. If we had other consumers of this information, it might make sense. - We could change the MSL backend to allow text to be generated out of order, so that we can decide whether to define the macro after we've generated all the function bodies. But at the moment this seems like unnecessary complexity, although it might be worth doing in the future if we had additional uses for it - say, to conditionally emit helper function definitions. Fixes #4972. --- naga/src/back/msl/writer.rs | 139 ++++++++++++++++++++- naga/tests/out/msl/boids.msl | 3 +- naga/tests/out/msl/break-if.msl | 9 +- naga/tests/out/msl/collatz.msl | 3 +- naga/tests/out/msl/control-flow.msl | 13 +- naga/tests/out/msl/do-while.msl | 3 +- naga/tests/out/msl/overrides-ray-query.msl | 3 +- naga/tests/out/msl/ray-query.msl | 3 +- naga/tests/out/msl/shadow.msl | 5 +- 9 files changed, 162 insertions(+), 19 deletions(-) diff --git a/naga/src/back/msl/writer.rs b/naga/src/back/msl/writer.rs index e0b3d31e8..a7374915c 100644 --- a/naga/src/back/msl/writer.rs +++ b/naga/src/back/msl/writer.rs @@ -376,6 +376,11 @@ pub struct Writer { /// Set of (struct type, struct field index) denoting which fields require /// padding inserted **before** them (i.e. between fields at index - 1 and index) struct_member_pads: FastHashSet<(Handle, u32)>, + + /// Name of the loop reachability macro. + /// + /// See `emit_loop_reachable_macro` for details. + loop_reachable_macro_name: String, } impl crate::Scalar { @@ -665,6 +670,7 @@ impl Writer { #[cfg(test)] put_block_stack_pointers: Default::default(), struct_member_pads: FastHashSet::default(), + loop_reachable_macro_name: String::default(), } } @@ -675,6 +681,125 @@ impl Writer { self.out } + /// Define a macro to invoke before loops, to defeat MSL infinite loop + /// reasoning. + /// + /// If we haven't done so already, emit the definition of a preprocessor + /// macro to be invoked before each loop in the generated MSL, to ensure + /// that the MSL compiler's optimizations do not remove bounds checks. + /// + /// Only the first call to this function for a given module actually causes + /// the macro definition to be written. Subsequent loops can simply use the + /// prior macro definition, since macros aren't block-scoped. + /// + /// # What is this trying to solve? + /// + /// In Metal Shading Language, an infinite loop has undefined behavior. + /// (This rule is inherited from C++14.) This means that, if the MSL + /// compiler determines that a given loop will never exit, it may assume + /// that it is never reached. It may thus assume that any conditions + /// sufficient to cause the loop to be reached must be false. Like many + /// optimizing compilers, MSL uses this kind of analysis to establish limits + /// on the range of values variables involved in those conditions might + /// hold. + /// + /// For example, suppose the MSL compiler sees the code: + /// + /// ```ignore + /// if (i >= 10) { + /// while (true) { } + /// } + /// ``` + /// + /// It will recognize that the `while` loop will never terminate, conclude + /// that it must be unreachable, and thus infer that, if this code is + /// reached, then `i < 10` at that point. + /// + /// Now suppose that, at some point where `i` has the same value as above, + /// the compiler sees the code: + /// + /// ```ignore + /// if (i < 10) { + /// a[i] = 1; + /// } + /// ``` + /// + /// Because the compiler is confident that `i < 10`, it will make the + /// assignment to `a[i]` unconditional, rewriting this code as, simply: + /// + /// ```ignore + /// a[i] = 1; + /// ``` + /// + /// If that `if` condition was injected by Naga to implement a bounds check, + /// the MSL compiler's optimizations could allow out-of-bounds array + /// accesses to occur. + /// + /// Naga cannot feasibly anticipate whether the MSL compiler will determine + /// that a loop is infinite, so an attacker could craft a Naga module + /// containing an infinite loop protected by conditions that cause the Metal + /// compiler to remove bounds checks that Naga injected elsewhere in the + /// function. + /// + /// This rewrite could occur even if the conditional assignment appears + /// *before* the `while` loop, as long as `i < 10` by the time the loop is + /// reached. This would allow the attacker to save the results of + /// unauthorized reads somewhere accessible before entering the infinite + /// loop. But even worse, the MSL compiler has been observed to simply + /// delete the infinite loop entirely, so that even code dominated by the + /// loop becomes reachable. This would make the attack even more flexible, + /// since shaders that would appear to never terminate would actually exit + /// nicely, after having stolen data from elsewhere in the GPU address + /// space. + /// + /// Ideally, Naga would prevent UB entirely via some means that persuades + /// the MSL compiler that no loop Naga generates is infinite. One approach + /// would be to add inline assembly to each loop that is annotated as + /// potentially branching out of the loop, but which in fact generates no + /// instructions. Unfortunately, inline assembly is not handled correctly by + /// some Metal device drivers. Further experimentation hasn't produced a + /// satisfactory approach. + /// + /// Instead, we accept that the MSL compiler may determine that some loops + /// are infinite, and focus instead on preventing the range analysis from + /// being affected. We transform *every* loop into something like this: + /// + /// ```ignore + /// if (volatile bool unpredictable = true; unpredictable) + /// while (true) { } + /// ``` + /// + /// Since the `volatile` qualifier prevents the compiler from assuming that + /// the `if` condition is true, it cannot be sure the infinite loop is + /// reached, and thus it cannot assume the entire structure is unreachable. + /// This prevents the range analysis impact described above. + /// + /// Unfortunately, what makes this a kludge, not a hack, is that this + /// solution leaves the GPU executing a pointless conditional branch, at + /// runtime, before each loop. There's no part of the system that has a + /// global enough view to be sure that `unpredictable` is true, and remove + /// it from the code. + /// + /// To make our output a bit more legible, we pull the condition out into a + /// preprocessor macro defined at the top of the module. + fn emit_loop_reachable_macro(&mut self) -> BackendResult { + if !self.loop_reachable_macro_name.is_empty() { + return Ok(()); + } + + self.loop_reachable_macro_name = self.namer.call("LOOP_IS_REACHABLE"); + let loop_reachable_volatile_name = self.namer.call("unpredictable_jump_over_loop"); + writeln!( + self.out, + "#define {} if (volatile bool {} = true; {})", + self.loop_reachable_macro_name, + loop_reachable_volatile_name, + loop_reachable_volatile_name, + )?; + + Ok(()) + } + fn put_call_parameters( &mut self, parameters: impl Iterator>, @@ -2924,10 +3049,15 @@ impl Writer { ref continuing, break_if, } => { + self.emit_loop_reachable_macro()?; if !continuing.is_empty() || break_if.is_some() { let gate_name = self.namer.call("loop_init"); writeln!(self.out, "{level}bool {gate_name} = true;")?; - writeln!(self.out, "{level}while(true) {{")?; + writeln!( + self.out, + "{level}{} while(true) {{", + self.loop_reachable_macro_name, + )?; let lif = level.next(); let lcontinuing = lif.next(); writeln!(self.out, "{lif}if (!{gate_name}) {{")?; @@ -2942,7 +3072,11 @@ impl Writer { writeln!(self.out, "{lif}}}")?; writeln!(self.out, "{lif}{gate_name} = false;")?; } else { - writeln!(self.out, "{level}while(true) {{")?; + writeln!( + self.out, + "{level}{} while(true) {{", + self.loop_reachable_macro_name, + )?; } self.put_block(level.next(), body, context)?; writeln!(self.out, "{level}}}")?; @@ -3379,6 +3513,7 @@ impl Writer { &[CLAMPED_LOD_LOAD_PREFIX], &mut self.names, ); + self.loop_reachable_macro_name.clear(); self.struct_member_pads.clear(); writeln!( diff --git a/naga/tests/out/msl/boids.msl b/naga/tests/out/msl/boids.msl index ce1ccc7cc..0dd520ac7 100644 --- a/naga/tests/out/msl/boids.msl +++ b/naga/tests/out/msl/boids.msl @@ -55,8 +55,9 @@ kernel void main_( vPos = _e8; metal::float2 _e14 = particlesSrc.particles[index].vel; vVel = _e14; +#define LOOP_IS_REACHABLE if (volatile bool unpredictable_jump_over_loop = true; unpredictable_jump_over_loop) bool loop_init = true; - while(true) { + LOOP_IS_REACHABLE while(true) { if (!loop_init) { uint _e91 = i; i = _e91 + 1u; diff --git a/naga/tests/out/msl/break-if.msl b/naga/tests/out/msl/break-if.msl index 8c0d9343b..3684f7222 100644 --- a/naga/tests/out/msl/break-if.msl +++ b/naga/tests/out/msl/break-if.msl @@ -7,8 +7,9 @@ using metal::uint; void breakIfEmpty( ) { +#define LOOP_IS_REACHABLE if (volatile bool unpredictable_jump_over_loop = true; unpredictable_jump_over_loop) bool loop_init = true; - while(true) { + LOOP_IS_REACHABLE while(true) { if (!loop_init) { if (true) { break; @@ -25,7 +26,7 @@ void breakIfEmptyBody( bool b = {}; bool c = {}; bool loop_init_1 = true; - while(true) { + LOOP_IS_REACHABLE while(true) { if (!loop_init_1) { b = a; bool _e2 = b; @@ -46,7 +47,7 @@ void breakIf( bool d = {}; bool e = {}; bool loop_init_2 = true; - while(true) { + LOOP_IS_REACHABLE while(true) { if (!loop_init_2) { bool _e5 = e; if (a_1 == e) { @@ -65,7 +66,7 @@ void breakIfSeparateVariable( ) { uint counter = 0u; bool loop_init_3 = true; - while(true) { + LOOP_IS_REACHABLE while(true) { if (!loop_init_3) { uint _e5 = counter; if (counter == 5u) { diff --git a/naga/tests/out/msl/collatz.msl b/naga/tests/out/msl/collatz.msl index e28374145..1ae910de6 100644 --- a/naga/tests/out/msl/collatz.msl +++ b/naga/tests/out/msl/collatz.msl @@ -19,7 +19,8 @@ uint collatz_iterations( uint n = {}; uint i = 0u; n = n_base; - while(true) { +#define LOOP_IS_REACHABLE if (volatile bool unpredictable_jump_over_loop = true; unpredictable_jump_over_loop) + LOOP_IS_REACHABLE while(true) { uint _e4 = n; if (_e4 > 1u) { } else { diff --git a/naga/tests/out/msl/control-flow.msl b/naga/tests/out/msl/control-flow.msl index 11771693a..dbf75163a 100644 --- a/naga/tests/out/msl/control-flow.msl +++ b/naga/tests/out/msl/control-flow.msl @@ -31,7 +31,8 @@ void switch_case_break( void loop_switch_continue( int x ) { - while(true) { +#define LOOP_IS_REACHABLE if (volatile bool unpredictable_jump_over_loop = true; unpredictable_jump_over_loop) + LOOP_IS_REACHABLE while(true) { switch(x) { case 1: { continue; @@ -49,7 +50,7 @@ void loop_switch_continue_nesting( int y, int z ) { - while(true) { + LOOP_IS_REACHABLE while(true) { switch(x_1) { case 1: { continue; @@ -60,7 +61,7 @@ void loop_switch_continue_nesting( continue; } default: { - while(true) { + LOOP_IS_REACHABLE while(true) { switch(z) { case 1: { continue; @@ -85,7 +86,7 @@ void loop_switch_continue_nesting( } } } - while(true) { + LOOP_IS_REACHABLE while(true) { switch(y) { case 1: default: { @@ -108,7 +109,7 @@ void loop_switch_omit_continue_variable_checks( int w ) { int pos_1 = 0; - while(true) { + LOOP_IS_REACHABLE while(true) { switch(x_2) { case 1: { pos_1 = 1; @@ -119,7 +120,7 @@ void loop_switch_omit_continue_variable_checks( } } } - while(true) { + LOOP_IS_REACHABLE while(true) { switch(x_2) { case 1: { break; diff --git a/naga/tests/out/msl/do-while.msl b/naga/tests/out/msl/do-while.msl index c1b4d08b0..b093da1dc 100644 --- a/naga/tests/out/msl/do-while.msl +++ b/naga/tests/out/msl/do-while.msl @@ -8,8 +8,9 @@ using metal::uint; void fb1_( thread bool& cond ) { +#define LOOP_IS_REACHABLE if (volatile bool unpredictable_jump_over_loop = true; unpredictable_jump_over_loop) bool loop_init = true; - while(true) { + LOOP_IS_REACHABLE while(true) { if (!loop_init) { bool _e1 = cond; if (!(cond)) { diff --git a/naga/tests/out/msl/overrides-ray-query.msl b/naga/tests/out/msl/overrides-ray-query.msl index 3a508b6f6..f2ad45c98 100644 --- a/naga/tests/out/msl/overrides-ray-query.msl +++ b/naga/tests/out/msl/overrides-ray-query.msl @@ -33,7 +33,8 @@ kernel void main_( rq.intersector.force_opacity((desc.flags & 1) != 0 ? metal::raytracing::forced_opacity::opaque : (desc.flags & 2) != 0 ? metal::raytracing::forced_opacity::non_opaque : metal::raytracing::forced_opacity::none); rq.intersector.accept_any_intersection((desc.flags & 4) != 0); rq.intersection = rq.intersector.intersect(metal::raytracing::ray(desc.origin, desc.dir, desc.tmin, desc.tmax), acc_struct, desc.cull_mask); rq.ready = true; - while(true) { +#define LOOP_IS_REACHABLE if (volatile bool unpredictable_jump_over_loop = true; unpredictable_jump_over_loop) + LOOP_IS_REACHABLE while(true) { bool _e31 = rq.ready; rq.ready = false; if (_e31) { diff --git a/naga/tests/out/msl/ray-query.msl b/naga/tests/out/msl/ray-query.msl index fbdaef548..129ad108a 100644 --- a/naga/tests/out/msl/ray-query.msl +++ b/naga/tests/out/msl/ray-query.msl @@ -53,7 +53,8 @@ RayIntersection query_loop( rq.intersector.force_opacity((_e8.flags & 1) != 0 ? metal::raytracing::forced_opacity::opaque : (_e8.flags & 2) != 0 ? metal::raytracing::forced_opacity::non_opaque : metal::raytracing::forced_opacity::none); rq.intersector.accept_any_intersection((_e8.flags & 4) != 0); rq.intersection = rq.intersector.intersect(metal::raytracing::ray(_e8.origin, _e8.dir, _e8.tmin, _e8.tmax), acs, _e8.cull_mask); rq.ready = true; - while(true) { +#define LOOP_IS_REACHABLE if (volatile bool unpredictable_jump_over_loop = true; unpredictable_jump_over_loop) + LOOP_IS_REACHABLE while(true) { bool _e9 = rq.ready; rq.ready = false; if (_e9) { diff --git a/naga/tests/out/msl/shadow.msl b/naga/tests/out/msl/shadow.msl index 3aefc21ee..f8aeef9d4 100644 --- a/naga/tests/out/msl/shadow.msl +++ b/naga/tests/out/msl/shadow.msl @@ -100,8 +100,9 @@ fragment fs_mainOutput fs_main( metal::float3 color = c_ambient; uint i = 0u; metal::float3 normal_1 = metal::normalize(in.world_normal); +#define LOOP_IS_REACHABLE if (volatile bool unpredictable_jump_over_loop = true; unpredictable_jump_over_loop) bool loop_init = true; - while(true) { + LOOP_IS_REACHABLE while(true) { if (!loop_init) { uint _e40 = i; i = _e40 + 1u; @@ -151,7 +152,7 @@ fragment fs_main_without_storageOutput fs_main_without_storage( uint i_1 = 0u; metal::float3 normal_2 = metal::normalize(in_1.world_normal); bool loop_init_1 = true; - while(true) { + LOOP_IS_REACHABLE while(true) { if (!loop_init_1) { uint _e40 = i_1; i_1 = _e40 + 1u; From f942ceef9bb04606cc02747de7386297de1edb7d Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Wed, 18 Sep 2024 10:54:45 -0400 Subject: [PATCH 14/59] docs(msl-out): note that Dawn also avoids loop UB like we do --- naga/src/back/msl/writer.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/naga/src/back/msl/writer.rs b/naga/src/back/msl/writer.rs index a7374915c..e09ca557a 100644 --- a/naga/src/back/msl/writer.rs +++ b/naga/src/back/msl/writer.rs @@ -782,6 +782,9 @@ impl Writer { /// /// To make our output a bit more legible, we pull the condition out into a /// preprocessor macro defined at the top of the module. + /// + /// This approach is also used by Chromium WebGPU's Dawn shader compiler, as of + /// . fn emit_loop_reachable_macro(&mut self) -> BackendResult { if !self.loop_reachable_macro_name.is_empty() { return Ok(()); From 0d339fc9f14c977e6ed9f516a111dc8205bc1958 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Wed, 18 Sep 2024 15:48:04 -0400 Subject: [PATCH 15/59] chore: suppress `unused_qualifications` for `wgpu_core::id` const. assertions (#6295) --- wgpu-core/src/id.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index 76ea8e333..4e4897c83 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -4,22 +4,23 @@ use std::{ fmt::{self, Debug}, hash::Hash, marker::PhantomData, + mem::size_of, num::NonZeroU64, }; use wgt::WasmNotSendSync; const _: () = { - if std::mem::size_of::() != 4 { + if size_of::() != 4 { panic!() } }; const _: () = { - if std::mem::size_of::() != 4 { + if size_of::() != 4 { panic!() } }; const _: () = { - if std::mem::size_of::() != 8 { + if size_of::() != 8 { panic!() } }; From dfc384a7fd4ab7250a75d59c6f831d9ffb220f7e Mon Sep 17 00:00:00 2001 From: Vecvec <130132884+Vecvec@users.noreply.github.com> Date: Thu, 19 Sep 2024 09:02:25 +1200 Subject: [PATCH 16/59] Stop Vulkan generating validation error in build acceleration structures (#6282) --- CHANGELOG.md | 1 + wgpu-hal/examples/ray-traced-triangle/main.rs | 80 ++++++++++++------- wgpu-hal/src/vulkan/command.rs | 3 + 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34bc30131..aafc73b31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -117,6 +117,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). #### Vulkan - Vulkan debug labels assumed no interior nul byte. By @DJMcNab in [#6257](https://github.com/gfx-rs/wgpu/pull/6257) +- Add `.index_type(vk::IndexType::NONE_KHR)` when creating `AccelerationStructureGeometryTrianglesDataKHR` in the raytraced triangle example to prevent a validation error. By @Vecvec in [#6282](https://github.com/gfx-rs/wgpu/pull/6282) ### Changes diff --git a/wgpu-hal/examples/ray-traced-triangle/main.rs b/wgpu-hal/examples/ray-traced-triangle/main.rs index 816827b5a..dd9184373 100644 --- a/wgpu-hal/examples/ray-traced-triangle/main.rs +++ b/wgpu-hal/examples/ray-traced-triangle/main.rs @@ -205,7 +205,7 @@ struct Example { uniform_buffer: A::Buffer, pipeline_layout: A::PipelineLayout, vertices_buffer: A::Buffer, - indices_buffer: A::Buffer, + indices_buffer: Option, texture: A::Texture, instances: [AccelerationStructureInstance; 3], instances_buffer: A::Buffer, @@ -217,6 +217,18 @@ struct Example { impl Example { fn init(window: &winit::window::Window) -> Result> { + let mut index_buffer = false; + + for arg in std::env::args() { + if arg == "index_buffer" { + index_buffer = true; + } + } + + if index_buffer { + log::info!("using index buffer") + } + let instance_desc = hal::InstanceDescriptor { name: "example", flags: wgt::InstanceFlags::default(), @@ -420,29 +432,34 @@ impl Example { vertices_buffer }; - let indices_buffer = unsafe { - let indices_buffer = device - .create_buffer(&hal::BufferDescriptor { - label: Some("indices buffer"), - size: indices_size_in_bytes as u64, - usage: hal::BufferUses::MAP_WRITE - | hal::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, - memory_flags: hal::MemoryFlags::TRANSIENT | hal::MemoryFlags::PREFER_COHERENT, - }) - .unwrap(); + let indices_buffer = if index_buffer { + unsafe { + let indices_buffer = device + .create_buffer(&hal::BufferDescriptor { + label: Some("indices buffer"), + size: indices_size_in_bytes as u64, + usage: hal::BufferUses::MAP_WRITE + | hal::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, + memory_flags: hal::MemoryFlags::TRANSIENT + | hal::MemoryFlags::PREFER_COHERENT, + }) + .unwrap(); - let mapping = device - .map_buffer(&indices_buffer, 0..indices_size_in_bytes as u64) - .unwrap(); - ptr::copy_nonoverlapping( - indices.as_ptr() as *const u8, - mapping.ptr.as_ptr(), - indices_size_in_bytes, - ); - device.unmap_buffer(&indices_buffer); - assert!(mapping.is_coherent); + let mapping = device + .map_buffer(&indices_buffer, 0..indices_size_in_bytes as u64) + .unwrap(); + ptr::copy_nonoverlapping( + indices.as_ptr() as *const u8, + mapping.ptr.as_ptr(), + indices_size_in_bytes, + ); + device.unmap_buffer(&indices_buffer); + assert!(mapping.is_coherent); - indices_buffer + Some((indices_buffer, indices.len())) + } + } else { + None }; let blas_triangles = vec![hal::AccelerationStructureTriangles { @@ -451,12 +468,15 @@ impl Example { vertex_format: wgt::VertexFormat::Float32x3, vertex_count: vertices.len() as u32, vertex_stride: 3 * 4, - indices: Some(hal::AccelerationStructureTriangleIndices { - buffer: Some(&indices_buffer), - format: wgt::IndexFormat::Uint32, - offset: 0, - count: indices.len() as u32, + indices: indices_buffer.as_ref().map(|(buf, len)| { + hal::AccelerationStructureTriangleIndices { + buffer: Some(buf), + format: wgt::IndexFormat::Uint32, + offset: 0, + count: *len as u32, + } }), + transform: None, flags: hal::AccelerationStructureGeometryFlags::OPAQUE, }]; @@ -800,7 +820,7 @@ impl Example { tlas, scratch_buffer, time: 0.0, - indices_buffer, + indices_buffer: indices_buffer.map(|(buf, _)| buf), vertices_buffer, uniform_buffer, texture_view, @@ -1026,7 +1046,9 @@ impl Example { self.device.destroy_bind_group(self.bind_group); self.device.destroy_buffer(self.scratch_buffer); self.device.destroy_buffer(self.instances_buffer); - self.device.destroy_buffer(self.indices_buffer); + if let Some(buffer) = self.indices_buffer { + self.device.destroy_buffer(buffer); + } self.device.destroy_buffer(self.vertices_buffer); self.device.destroy_buffer(self.uniform_buffer); self.device.destroy_acceleration_structure(self.tlas); diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index 8bd749cd3..6b02e35f4 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -503,6 +503,9 @@ impl crate::CommandEncoder for super::CommandEncoder { for triangles in in_geometries { let mut triangle_data = vk::AccelerationStructureGeometryTrianglesDataKHR::default() + // IndexType::NONE_KHR is not set by default (due to being provided by VK_KHR_acceleration_structure) but unless there is an + // index buffer we need to have IndexType::NONE_KHR as our index type. + .index_type(vk::IndexType::NONE_KHR) .vertex_data(vk::DeviceOrHostAddressConstKHR { device_address: get_device_address(triangles.vertex_buffer), }) From b54fb72d4a54e1bdf00b3bc423aa652ae77928a1 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Fri, 20 Sep 2024 03:29:05 -0400 Subject: [PATCH 17/59] chore: un-regress `unused_qualifications` lint in `naga::front::spv` (#6297) Regressed in #5824. --- naga/src/front/spv/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/naga/src/front/spv/error.rs b/naga/src/front/spv/error.rs index bcbb47dde..219048e10 100644 --- a/naga/src/front/spv/error.rs +++ b/naga/src/front/spv/error.rs @@ -162,6 +162,6 @@ impl Error { impl From for Error { fn from(source: atomic_upgrade::Error) -> Self { - crate::front::spv::Error::AtomicUpgradeError(source) + Error::AtomicUpgradeError(source) } } From 9f85f8aeea6c43c1a412bafc8fbcfb43aad0dd20 Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Fri, 20 Sep 2024 02:32:01 -0500 Subject: [PATCH 18/59] fix(wgpu): Raise validation error instead of panicking in get_bind_group_layout. (#6280) --- CHANGELOG.md | 14 ++- tests/tests/pipeline.rs | 205 +++++++++++++++++++++++++------ wgpu/src/api/compute_pipeline.rs | 2 + wgpu/src/api/render_pipeline.rs | 2 + wgpu/src/backend/wgpu_core.rs | 56 ++++++--- 5 files changed, 222 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aafc73b31..81dee037a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,11 +27,12 @@ Top level categories: Bottom level categories: +- Naga - General - DX12 - Vulkan - Metal -- GLES +- GLES / OpenGL - WebGPU - Emscripten - Hal @@ -82,6 +83,10 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Add `first` and `either` sampling types for `@interpolate(flat, …)` in WGSL. By @ErichDonGubler in [#6181](https://github.com/gfx-rs/wgpu/pull/6181). - Support for more atomic ops in the SPIR-V frontend. By @schell in [#5824](https://github.com/gfx-rs/wgpu/pull/5824). +#### General + +- Add `VideoFrame` to `ExternalImageSource` enum. By @jprochazk in [#6170](https://github.com/gfx-rs/wgpu/pull/6170) + #### Vulkan - Allow using [VK_GOOGLE_display_timing](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html) unsafely with the `VULKAN_GOOGLE_DISPLAY_TIMING` feature. By @DJMcNab in [#6149](https://github.com/gfx-rs/wgpu/pull/6149) @@ -89,7 +94,6 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). ### Bug Fixes - Fix incorrect hlsl image output type conversion. By @atlv24 in [#6123](https://github.com/gfx-rs/wgpu/pull/6123) -- Fix JS `TypeError` exception in `Instance::request_adapter` when browser doesn't support WebGPU but `wgpu` not compiled with `webgl` support. By @bgr360 in [#6197](https://github.com/gfx-rs/wgpu/pull/6197). #### Naga @@ -107,13 +111,17 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Deduplicate bind group layouts that are created from pipelines with "auto" layouts. By @teoxoy [#6049](https://github.com/gfx-rs/wgpu/pull/6049) - Fix crash when dropping the surface after the device. By @wumpf in [#6052](https://github.com/gfx-rs/wgpu/pull/6052) - Fix error message that is thrown in create_render_pass to no longer say `compute_pass`. By @matthew-wong1 [#6041](https://github.com/gfx-rs/wgpu/pull/6041) -- Add `VideoFrame` to `ExternalImageSource` enum. By @jprochazk in [#6170](https://github.com/gfx-rs/wgpu/pull/6170) - Document `wgpu_hal` bounds-checking promises, and adapt `wgpu_core`'s lazy initialization logic to the slightly weaker-than-expected guarantees. By @jimblandy in [#6201](https://github.com/gfx-rs/wgpu/pull/6201) +- Raise validation error instead of panicking in `{Render,Compute}Pipeline::get_bind_group_layout` on native / WebGL. By @bgr360 in [#6280](https://github.com/gfx-rs/wgpu/pull/6280). #### GLES / OpenGL - Fix GL debug message callbacks not being properly cleaned up (causing UB). By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114) +#### WebGPU + +- Fix JS `TypeError` exception in `Instance::request_adapter` when browser doesn't support WebGPU but `wgpu` not compiled with `webgl` support. By @bgr360 in [#6197](https://github.com/gfx-rs/wgpu/pull/6197). + #### Vulkan - Vulkan debug labels assumed no interior nul byte. By @DJMcNab in [#6257](https://github.com/gfx-rs/wgpu/pull/6257) diff --git a/tests/tests/pipeline.rs b/tests/tests/pipeline.rs index f7d8d1ec7..8e9c91e52 100644 --- a/tests/tests/pipeline.rs +++ b/tests/tests/pipeline.rs @@ -1,44 +1,16 @@ -use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; +use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters}; -// Create an invalid shader and a compute pipeline that uses it -// with a default bindgroup layout, and then ask for that layout. -// Validation should fail, but wgpu should not panic. -#[gpu_test] -static PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters( - TestParameters::default() - // https://github.com/gfx-rs/wgpu/issues/4167 - .expect_fail(FailureCase::always().panic("Error reflecting bind group")), - ) - .run_sync(|ctx| { - ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); +const INVALID_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor { + label: Some("invalid shader"), + source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed("not valid wgsl")), +}; - fail( - &ctx.device, - || { - let module = ctx - .device - .create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl("not valid wgsl".into()), - }); - - let pipeline = - ctx.device - .create_compute_pipeline(&wgpu::ComputePipelineDescriptor { - label: Some("mandelbrot compute pipeline"), - layout: None, - module: &module, - entry_point: Some("doesn't exist"), - compilation_options: Default::default(), - cache: None, - }); - - pipeline.get_bind_group_layout(0); - }, - None, - ); - }); +const TRIVIAL_COMPUTE_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor { + label: Some("trivial compute shader"), + source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( + "@compute @workgroup_size(1) fn main() {}", + )), +}; const TRIVIAL_VERTEX_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor { label: Some("trivial vertex shader"), @@ -47,6 +19,161 @@ const TRIVIAL_VERTEX_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderMod )), }; +const TRIVIAL_FRAGMENT_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor { + label: Some("trivial fragment shader"), + source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( + "@fragment fn main() -> @location(0) vec4 { return vec4(0); }", + )), +}; + +// Create an invalid shader and a compute pipeline that uses it +// with a default bindgroup layout, and then ask for that layout. +// Validation should fail, but wgpu should not panic. +#[gpu_test] +static COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = + GpuTestConfiguration::new() + .parameters(TestParameters::default()) + .run_sync(|ctx| { + ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); + + fail( + &ctx.device, + || { + let module = ctx.device.create_shader_module(INVALID_SHADER_DESC); + + let pipeline = + ctx.device + .create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("compute pipeline"), + layout: None, + module: &module, + entry_point: Some("doesn't exist"), + compilation_options: Default::default(), + cache: None, + }); + + // https://github.com/gfx-rs/wgpu/issues/4167 this used to panic + pipeline.get_bind_group_layout(0); + }, + Some("Shader 'invalid shader' parsing error"), + ); + }); + +#[gpu_test] +static COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration = + GpuTestConfiguration::new() + .parameters(TestParameters::default().test_features_limits()) + .run_sync(|ctx| { + ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); + + fail( + &ctx.device, + || { + let module = ctx.device.create_shader_module(TRIVIAL_COMPUTE_SHADER_DESC); + + let pipeline = + ctx.device + .create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("compute pipeline"), + layout: None, + module: &module, + entry_point: Some("main"), + compilation_options: Default::default(), + cache: None, + }); + + pipeline.get_bind_group_layout(0); + }, + Some("Invalid group index 0"), + ); + }); + +#[gpu_test] +static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = + GpuTestConfiguration::new() + .parameters(TestParameters::default()) + .run_sync(|ctx| { + ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); + + fail( + &ctx.device, + || { + let module = ctx.device.create_shader_module(INVALID_SHADER_DESC); + + let pipeline = + ctx.device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("render pipeline"), + layout: None, + vertex: wgpu::VertexState { + module: &module, + entry_point: Some("doesn't exist"), + compilation_options: Default::default(), + buffers: &[], + }, + primitive: Default::default(), + depth_stencil: None, + multisample: Default::default(), + fragment: None, + multiview: None, + cache: None, + }); + + pipeline.get_bind_group_layout(0); + }, + Some("Shader 'invalid shader' parsing error"), + ); + }); + +#[gpu_test] +static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration = + GpuTestConfiguration::new() + .parameters(TestParameters::default().test_features_limits()) + .run_sync(|ctx| { + ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); + + fail( + &ctx.device, + || { + let vs_module = ctx.device.create_shader_module(TRIVIAL_VERTEX_SHADER_DESC); + let fs_module = ctx + .device + .create_shader_module(TRIVIAL_FRAGMENT_SHADER_DESC); + + let pipeline = + ctx.device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("render pipeline"), + layout: None, + vertex: wgpu::VertexState { + module: &vs_module, + entry_point: Some("main"), + compilation_options: Default::default(), + buffers: &[], + }, + primitive: Default::default(), + depth_stencil: None, + multisample: Default::default(), + fragment: Some(wgpu::FragmentState { + module: &fs_module, + entry_point: Some("main"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format: wgpu::TextureFormat::Rgba8Unorm, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview: None, + cache: None, + }); + + pipeline.get_bind_group_layout(0); + }, + Some("Invalid group index 0"), + ); + }); + #[gpu_test] static NO_TARGETLESS_RENDER: GpuTestConfiguration = GpuTestConfiguration::new() .parameters(TestParameters::default()) diff --git a/wgpu/src/api/compute_pipeline.rs b/wgpu/src/api/compute_pipeline.rs index 18d4e904e..16885ac96 100644 --- a/wgpu/src/api/compute_pipeline.rs +++ b/wgpu/src/api/compute_pipeline.rs @@ -24,6 +24,8 @@ impl ComputePipeline { /// If this pipeline was created with a [default layout][ComputePipelineDescriptor::layout], /// then bind groups created with the returned `BindGroupLayout` can only be used with this /// pipeline. + /// + /// This method will raise a validation error if there is no bind group layout at `index`. pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout { let context = Arc::clone(&self.context); let data = self diff --git a/wgpu/src/api/render_pipeline.rs b/wgpu/src/api/render_pipeline.rs index 3009cde1d..dd1c1cefe 100644 --- a/wgpu/src/api/render_pipeline.rs +++ b/wgpu/src/api/render_pipeline.rs @@ -31,6 +31,8 @@ impl RenderPipeline { /// /// If this pipeline was created with a [default layout][RenderPipelineDescriptor::layout], then /// bind groups created with the returned `BindGroupLayout` can only be used with this pipeline. + /// + /// This method will raise a validation error if there is no bind group layout at `index`. pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout { let context = Arc::clone(&self.context); let data = self diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index 37850c770..3aac20e21 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -473,6 +473,18 @@ pub struct Queue { error_sink: ErrorSink, } +#[derive(Debug)] +pub struct ComputePipeline { + id: wgc::id::ComputePipelineId, + error_sink: ErrorSink, +} + +#[derive(Debug)] +pub struct RenderPipeline { + id: wgc::id::RenderPipelineId, + error_sink: ErrorSink, +} + #[derive(Debug)] pub struct ComputePass { pass: wgc::command::ComputePass, @@ -505,8 +517,8 @@ impl crate::Context for ContextWgpuCore { type TextureData = Texture; type QuerySetData = wgc::id::QuerySetId; type PipelineLayoutData = wgc::id::PipelineLayoutId; - type RenderPipelineData = wgc::id::RenderPipelineId; - type ComputePipelineData = wgc::id::ComputePipelineId; + type RenderPipelineData = RenderPipeline; + type ComputePipelineData = ComputePipeline; type PipelineCacheData = wgc::id::PipelineCacheId; type CommandEncoderData = CommandEncoder; type ComputePassData = ComputePass; @@ -1097,7 +1109,10 @@ impl crate::Context for ContextWgpuCore { "Device::create_render_pipeline", ); } - id + RenderPipeline { + id, + error_sink: Arc::clone(&device_data.error_sink), + } } fn device_create_compute_pipeline( &self, @@ -1139,7 +1154,10 @@ impl crate::Context for ContextWgpuCore { "Device::create_compute_pipeline", ); } - id + ComputePipeline { + id, + error_sink: Arc::clone(&device_data.error_sink), + } } unsafe fn device_create_pipeline_cache( @@ -1531,11 +1549,11 @@ impl crate::Context for ContextWgpuCore { } fn compute_pipeline_drop(&self, pipeline_data: &Self::ComputePipelineData) { - self.0.compute_pipeline_drop(*pipeline_data) + self.0.compute_pipeline_drop(pipeline_data.id) } fn render_pipeline_drop(&self, pipeline_data: &Self::RenderPipelineData) { - self.0.render_pipeline_drop(*pipeline_data) + self.0.render_pipeline_drop(pipeline_data.id) } fn pipeline_cache_drop(&self, cache_data: &Self::PipelineCacheData) { @@ -1549,9 +1567,13 @@ impl crate::Context for ContextWgpuCore { ) -> Self::BindGroupLayoutData { let (id, error) = self.0 - .compute_pipeline_get_bind_group_layout(*pipeline_data, index, None); + .compute_pipeline_get_bind_group_layout(pipeline_data.id, index, None); if let Some(err) = error { - panic!("Error reflecting bind group {index}: {err}"); + self.handle_error_nolabel( + &pipeline_data.error_sink, + err, + "ComputePipeline::get_bind_group_layout", + ) } id } @@ -1561,11 +1583,15 @@ impl crate::Context for ContextWgpuCore { pipeline_data: &Self::RenderPipelineData, index: u32, ) -> Self::BindGroupLayoutData { - let (id, error) = self - .0 - .render_pipeline_get_bind_group_layout(*pipeline_data, index, None); + let (id, error) = + self.0 + .render_pipeline_get_bind_group_layout(pipeline_data.id, index, None); if let Some(err) = error { - panic!("Error reflecting bind group {index}: {err}"); + self.handle_error_nolabel( + &pipeline_data.error_sink, + err, + "RenderPipeline::get_bind_group_layout", + ) } id } @@ -2108,7 +2134,7 @@ impl crate::Context for ContextWgpuCore { ) { if let Err(cause) = self .0 - .compute_pass_set_pipeline(&mut pass_data.pass, *pipeline_data) + .compute_pass_set_pipeline(&mut pass_data.pass, pipeline_data.id) { self.handle_error( &pass_data.error_sink, @@ -2311,7 +2337,7 @@ impl crate::Context for ContextWgpuCore { encoder_data: &mut Self::RenderBundleEncoderData, pipeline_data: &Self::RenderPipelineData, ) { - wgpu_render_bundle_set_pipeline(encoder_data, *pipeline_data) + wgpu_render_bundle_set_pipeline(encoder_data, pipeline_data.id) } fn render_bundle_encoder_set_bind_group( @@ -2434,7 +2460,7 @@ impl crate::Context for ContextWgpuCore { ) { if let Err(cause) = self .0 - .render_pass_set_pipeline(&mut pass_data.pass, *pipeline_data) + .render_pass_set_pipeline(&mut pass_data.pass, pipeline_data.id) { self.handle_error( &pass_data.error_sink, From 14abdd47547e0cd364e8431dec46c3d57be3e160 Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Fri, 20 Sep 2024 12:43:10 -0700 Subject: [PATCH 19/59] fix: usage of `a deprecated Node.js version` in CI (#6301) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 802c8bd75..598138af2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -554,7 +554,7 @@ jobs: cargo llvm-cov report --lcov --output-path lcov.info - name: upload coverage report to codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 if: steps.coverage.outcome == 'success' with: files: lcov.info From 9977edc51f1d2efa25ec4f2eb13a6bb4b3331898 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Sat, 21 Sep 2024 12:33:13 -0400 Subject: [PATCH 20/59] =?UTF-8?q?chore!:=20remove=20`#[no=5Fmangle]=20?= =?UTF-8?q?=E2=80=A6=20extern=20"C"=20=E2=80=A6`=20from=20`fn`s=20in=20`bu?= =?UTF-8?q?ndle=5Fffi`=20(#6272)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + wgpu-core/src/command/bundle.rs | 36 +++++++++++---------------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81dee037a..d4d021ddf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Fix error message that is thrown in create_render_pass to no longer say `compute_pass`. By @matthew-wong1 [#6041](https://github.com/gfx-rs/wgpu/pull/6041) - Document `wgpu_hal` bounds-checking promises, and adapt `wgpu_core`'s lazy initialization logic to the slightly weaker-than-expected guarantees. By @jimblandy in [#6201](https://github.com/gfx-rs/wgpu/pull/6201) - Raise validation error instead of panicking in `{Render,Compute}Pipeline::get_bind_group_layout` on native / WebGL. By @bgr360 in [#6280](https://github.com/gfx-rs/wgpu/pull/6280). +- **BREAKING**: Remove the last exposed C symbols in project, located in `wgpu_core::render::bundle::bundle_ffi`, to allow multiple versions of WGPU to compile together. By @ErichDonGubler in [#6272](https://github.com/gfx-rs/wgpu/pull/6272). #### GLES / OpenGL diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 394858f9f..b0d90976d 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -1584,8 +1584,7 @@ pub mod bundle_ffi { /// /// This function is unsafe as there is no guarantee that the given pointer is /// valid for `offset_length` elements. - #[no_mangle] - pub unsafe extern "C" fn wgpu_render_bundle_set_bind_group( + pub unsafe fn wgpu_render_bundle_set_bind_group( bundle: &mut RenderBundleEncoder, index: u32, bind_group_id: Option, @@ -1612,8 +1611,7 @@ pub mod bundle_ffi { }); } - #[no_mangle] - pub extern "C" fn wgpu_render_bundle_set_pipeline( + pub fn wgpu_render_bundle_set_pipeline( bundle: &mut RenderBundleEncoder, pipeline_id: id::RenderPipelineId, ) { @@ -1627,8 +1625,7 @@ pub mod bundle_ffi { .push(RenderCommand::SetPipeline(pipeline_id)); } - #[no_mangle] - pub extern "C" fn wgpu_render_bundle_set_vertex_buffer( + pub fn wgpu_render_bundle_set_vertex_buffer( bundle: &mut RenderBundleEncoder, slot: u32, buffer_id: id::BufferId, @@ -1643,8 +1640,7 @@ pub mod bundle_ffi { }); } - #[no_mangle] - pub extern "C" fn wgpu_render_bundle_set_index_buffer( + pub fn wgpu_render_bundle_set_index_buffer( encoder: &mut RenderBundleEncoder, buffer: id::BufferId, index_format: IndexFormat, @@ -1658,8 +1654,7 @@ pub mod bundle_ffi { /// /// This function is unsafe as there is no guarantee that the given pointer is /// valid for `data` elements. - #[no_mangle] - pub unsafe extern "C" fn wgpu_render_bundle_set_push_constants( + pub unsafe fn wgpu_render_bundle_set_push_constants( pass: &mut RenderBundleEncoder, stages: wgt::ShaderStages, offset: u32, @@ -1695,8 +1690,7 @@ pub mod bundle_ffi { }); } - #[no_mangle] - pub extern "C" fn wgpu_render_bundle_draw( + pub fn wgpu_render_bundle_draw( bundle: &mut RenderBundleEncoder, vertex_count: u32, instance_count: u32, @@ -1711,8 +1705,7 @@ pub mod bundle_ffi { }); } - #[no_mangle] - pub extern "C" fn wgpu_render_bundle_draw_indexed( + pub fn wgpu_render_bundle_draw_indexed( bundle: &mut RenderBundleEncoder, index_count: u32, instance_count: u32, @@ -1729,8 +1722,7 @@ pub mod bundle_ffi { }); } - #[no_mangle] - pub extern "C" fn wgpu_render_bundle_draw_indirect( + pub fn wgpu_render_bundle_draw_indirect( bundle: &mut RenderBundleEncoder, buffer_id: id::BufferId, offset: BufferAddress, @@ -1743,8 +1735,7 @@ pub mod bundle_ffi { }); } - #[no_mangle] - pub extern "C" fn wgpu_render_bundle_draw_indexed_indirect( + pub fn wgpu_render_bundle_draw_indexed_indirect( bundle: &mut RenderBundleEncoder, buffer_id: id::BufferId, offset: BufferAddress, @@ -1761,16 +1752,14 @@ pub mod bundle_ffi { /// /// This function is unsafe as there is no guarantee that the given `label` /// is a valid null-terminated string. - #[no_mangle] - pub unsafe extern "C" fn wgpu_render_bundle_push_debug_group( + pub unsafe fn wgpu_render_bundle_push_debug_group( _bundle: &mut RenderBundleEncoder, _label: RawString, ) { //TODO } - #[no_mangle] - pub extern "C" fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) { + pub fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) { //TODO } @@ -1778,8 +1767,7 @@ pub mod bundle_ffi { /// /// This function is unsafe as there is no guarantee that the given `label` /// is a valid null-terminated string. - #[no_mangle] - pub unsafe extern "C" fn wgpu_render_bundle_insert_debug_marker( + pub unsafe fn wgpu_render_bundle_insert_debug_marker( _bundle: &mut RenderBundleEncoder, _label: RawString, ) { From 841e7c885713983a354a814e5635bf43c1e37f56 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Sat, 21 Sep 2024 12:47:35 -0400 Subject: [PATCH 21/59] fix: only log `Device::maintain` waits if they happen (#6303) --- wgpu-core/src/device/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 1cd8ef0fe..03b183e08 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -457,13 +457,13 @@ impl Device { // If necessary, wait for that submission to complete. if maintain.is_wait() { + log::trace!("Device::maintain: waiting for submission index {submission_index}"); unsafe { self.raw() .wait(fence.as_ref(), submission_index, CLEANUP_WAIT_MS) } .map_err(|e| self.handle_hal_error(e))?; } - log::trace!("Device::maintain: waiting for submission index {submission_index}"); let mut life_tracker = self.lock_life(); let submission_closures = From 2f81ae156c420356f09ab162fb5c70875706f4cd Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Sat, 21 Sep 2024 20:27:32 -0400 Subject: [PATCH 22/59] =?UTF-8?q?style:=20use=20`concat!(=E2=80=A6)`=20in?= =?UTF-8?q?=20`DOWNLEVEL=5F*=5FMESSAGE`=20(#6305)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wgpu-core/src/lib.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index ccbe64d52..521238a7d 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -128,16 +128,26 @@ pub fn hal_label(opt: Option<&str>, flags: wgt::InstanceFlags) -> Option<&str> { opt } -const DOWNLEVEL_WARNING_MESSAGE: &str = "The underlying API or device in use does not \ -support enough features to be a fully compliant implementation of WebGPU. A subset of the features can still be used. \ -If you are running this program on native and not in a browser and wish to limit the features you use to the supported subset, \ -call Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \ -platform supports."; -const DOWNLEVEL_ERROR_MESSAGE: &str = "This is not an invalid use of WebGPU: the underlying API or device does not \ -support enough features to be a fully compliant implementation. A subset of the features can still be used. \ -If you are running this program on native and not in a browser and wish to work around this issue, call \ -Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \ -platform supports."; +const DOWNLEVEL_WARNING_MESSAGE: &str = concat!( + "The underlying API or device in use does not ", + "support enough features to be a fully compliant implementation of WebGPU. ", + "A subset of the features can still be used. ", + "If you are running this program on native and not in a browser and wish to limit ", + "the features you use to the supported subset, ", + "call Adapter::downlevel_properties or Device::downlevel_properties to get ", + "a listing of the features the current ", + "platform supports." +); + +const DOWNLEVEL_ERROR_MESSAGE: &str = concat!( + "This is not an invalid use of WebGPU: the underlying API or device does not ", + "support enough features to be a fully compliant implementation. ", + "A subset of the features can still be used. ", + "If you are running this program on native and not in a browser ", + "and wish to work around this issue, call ", + "Adapter::downlevel_properties or Device::downlevel_properties ", + "to get a listing of the features the current platform supports." +); #[cfg(feature = "api_log_info")] macro_rules! api_log { From 39cfeefd8bc3b1ae11df846c5d2bcbea6fcc203b Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sun, 22 Sep 2024 00:35:26 -0400 Subject: [PATCH 23/59] Update CI Dependencies (#6306) --- .github/workflows/ci.yml | 17 +++++++++-------- .github/workflows/shaders.yml | 10 +++++++--- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 598138af2..6463415eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,21 +13,23 @@ env: # # Sourced from https://vulkan.lunarg.com/sdk/home#linux - VULKAN_SDK_VERSION: "1.3.268" + # + # We don't include the 4th version number, as it's not used in any URL. + VULKAN_SDK_VERSION: "1.3.290" # Sourced from https://www.nuget.org/packages/Microsoft.Direct3D.WARP - WARP_VERSION: "1.0.8" + WARP_VERSION: "1.0.13" # Sourced from https://github.com/microsoft/DirectXShaderCompiler/releases # # Must also be changed in shaders.yaml - DXC_RELEASE: "v1.7.2308" - DXC_FILENAME: "dxc_2023_08_14.zip" + DXC_RELEASE: "v1.8.2407" + DXC_FILENAME: "dxc_2024_07_31_clang_cl.zip" # Sourced from https://archive.mesa3d.org/. Bumping this requires # updating the mesa build in https://github.com/gfx-rs/ci-build and creating a new release. - MESA_VERSION: "23.3.1" + MESA_VERSION: "24.2.3" # Corresponds to https://github.com/gfx-rs/ci-build/releases - CI_BINARY_BUILD: "build18" + CI_BINARY_BUILD: "build19" # We sometimes need nightly to use special things in CI. # @@ -311,7 +313,6 @@ jobs: rustup override set ${{ env.CORE_MSRV }} cargo -V - # Use special toolchain for rustdoc, see https://github.com/gfx-rs/wgpu/issues/4905 - name: Install Nightly Toolchain run: | rustup toolchain install ${{ env.NIGHTLY_VERSION }} --no-self-update --profile=minimal --component clippy @@ -442,7 +443,7 @@ jobs: dxc --version curl.exe -L --retry 5 https://www.nuget.org/api/v2/package/Microsoft.Direct3D.WARP/$WARP_VERSION -o warp.zip - 7z.exe e warp.zip -owarp build/native/amd64/d3d10warp.dll + 7z.exe e warp.zip -owarp build/native/bin/x64/d3d10warp.dll mkdir -p target/llvm-cov-target/debug/deps diff --git a/.github/workflows/shaders.yml b/.github/workflows/shaders.yml index 86ec5d0b2..c326942a0 100644 --- a/.github/workflows/shaders.yml +++ b/.github/workflows/shaders.yml @@ -9,13 +9,17 @@ on: env: # Sourced from https://vulkan.lunarg.com/sdk/home#linux - VULKAN_SDK_VERSION: "1.3.268" + # + # We don't include the 4th version number, as it's not used in any URL. + # + # Held back from 1.3.290 by https://github.com/gfx-rs/wgpu/issues/6307 + VULKAN_SDK_VERSION: "1.3.283" # Sourced from https://github.com/microsoft/DirectXShaderCompiler/releases # # Must also be changed in ci.yaml - DXC_RELEASE: "v1.7.2308" - DXC_FILENAME: "dxc_2023_08_14.zip" + DXC_RELEASE: "v1.8.2407" + DXC_FILENAME: "dxc_2024_07_31_clang_cl.zip" jobs: naga-validate-windows: From 390a4169fb9c9c538f57ec221e01221f2e8a099b Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sat, 14 Sep 2024 20:34:20 -0700 Subject: [PATCH 24/59] naga: Don't consider per-polygon inputs to be subgroup uniform Implementations can absolutely pack multiple triangles per subgroup. Fixes #6270. --- CHANGELOG.md | 1 + naga/src/valid/analyzer.rs | 15 ++++----------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d021ddf..894da6ddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Accept only `vec3` (not `vecN`) for the `cross` built-in. By @ErichDonGubler in [#6171](https://github.com/gfx-rs/wgpu/pull/6171). - Configure `SourceLanguage` when enabling debug info in SPV-out. By @kvark in [#6256](https://github.com/gfx-rs/wgpu/pull/6256) +- Per-polygon and flat inputs should not be considered subgroup uniform. By @magcius in [#6276](https://github.com/gfx-rs/wgpu/pull/6276). #### General diff --git a/naga/src/valid/analyzer.rs b/naga/src/valid/analyzer.rs index 89b3da6a4..af95fd098 100644 --- a/naga/src/valid/analyzer.rs +++ b/naga/src/valid/analyzer.rs @@ -589,23 +589,16 @@ impl FunctionInfo { requirements: UniformityRequirements::empty(), } } - // depends on the builtin or interpolation + // depends on the builtin E::FunctionArgument(index) => { let arg = &resolve_context.arguments[index as usize]; let uniform = match arg.binding { Some(crate::Binding::BuiltIn( - // per-polygon built-ins are uniform - crate::BuiltIn::FrontFacing // per-work-group built-ins are uniform - | crate::BuiltIn::WorkGroupId + crate::BuiltIn::WorkGroupId | crate::BuiltIn::WorkGroupSize - | crate::BuiltIn::NumWorkGroups) - ) => true, - // only flat inputs are uniform - Some(crate::Binding::Location { - interpolation: Some(crate::Interpolation::Flat), - .. - }) => true, + | crate::BuiltIn::NumWorkGroups, + )) => true, _ => false, }; Uniformity { From 3bdc867a46ebcce1facda8f43bc603652f2c01ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 23:21:50 -0400 Subject: [PATCH 25/59] build(deps): bump crate-ci/typos from 1.24.5 to 1.24.6 (#6311) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6463415eb..9570d13b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -620,7 +620,7 @@ jobs: cargo fmt --manifest-path xtask/Cargo.toml -- --check - name: Check for typos - uses: crate-ci/typos@v1.24.5 + uses: crate-ci/typos@v1.24.6 check-cts-runner: # runtime is normally 2 minutes From e7c139b1d4c6b92987d833fddff7b1a30df6b126 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 23:24:12 -0400 Subject: [PATCH 26/59] build(deps): bump the patch-updates group with 9 updates (#6312) Bumps the patch-updates group with 9 updates: | Package | From | To | | --- | --- | --- | | [thiserror](https://github.com/dtolnay/thiserror) | `1.0.63` | `1.0.64` | | [unicode-xid](https://github.com/unicode-rs/unicode-xid) | `0.2.5` | `0.2.6` | | [clap](https://github.com/clap-rs/clap) | `4.5.17` | `4.5.18` | | [clap_builder](https://github.com/clap-rs/clap) | `4.5.17` | `4.5.18` | | [clap_derive](https://github.com/clap-rs/clap) | `4.5.13` | `4.5.18` | | [quick-xml](https://github.com/tafia/quick-xml) | `0.36.1` | `0.36.2` | | [thiserror-impl](https://github.com/dtolnay/thiserror) | `1.0.63` | `1.0.64` | | [unicode-id-start](https://github.com/Boshen/unicode-id-start) | `1.2.0` | `1.3.0` | | [unicode-width](https://github.com/unicode-rs/unicode-width) | `0.1.13` | `0.1.14` | Updates `thiserror` from 1.0.63 to 1.0.64 - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.63...1.0.64) Updates `unicode-xid` from 0.2.5 to 0.2.6 - [Changelog](https://github.com/unicode-rs/unicode-xid/blob/master/CHANGELOG.md) - [Commits](https://github.com/unicode-rs/unicode-xid/compare/v0.2.5...v0.2.6) Updates `clap` from 4.5.17 to 4.5.18 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.17...clap_complete-v4.5.18) Updates `clap_builder` from 4.5.17 to 4.5.18 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.5.17...v4.5.18) Updates `clap_derive` from 4.5.13 to 4.5.18 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.5.13...v4.5.18) Updates `quick-xml` from 0.36.1 to 0.36.2 - [Release notes](https://github.com/tafia/quick-xml/releases) - [Changelog](https://github.com/tafia/quick-xml/blob/master/Changelog.md) - [Commits](https://github.com/tafia/quick-xml/compare/v0.36.1...v0.36.2) Updates `thiserror-impl` from 1.0.63 to 1.0.64 - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.63...1.0.64) Updates `unicode-id-start` from 1.2.0 to 1.3.0 - [Commits](https://github.com/Boshen/unicode-id-start/commits) Updates `unicode-width` from 0.1.13 to 0.1.14 - [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.13...v0.1.14) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: unicode-xid dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: clap dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: clap_builder dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: clap_derive dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: quick-xml dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: thiserror-impl dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: unicode-id-start dependency-type: indirect update-type: version-update:semver-minor dependency-group: patch-updates - dependency-name: unicode-width dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 36 ++++++++++++++++++------------------ Cargo.toml | 2 +- naga/Cargo.toml | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86d027a4f..f7a159109 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -509,9 +509,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstream", "anstyle", @@ -521,9 +521,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2402,9 +2402,9 @@ checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" [[package]] name = "quick-xml" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ "memchr", ] @@ -2948,18 +2948,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -3215,9 +3215,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-id-start" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc3882f69607a2ac8cc4de3ee7993d8f68bb06f2974271195065b3bd07f2edea" +checksum = "97e2a3c5fc9de285c0e805d98eba666adb4b2d9e1049ce44821ff7707cc34e91" [[package]] name = "unicode-ident" @@ -3242,15 +3242,15 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" diff --git a/Cargo.toml b/Cargo.toml index da5ecbd37..73f17b755 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,7 +125,7 @@ smallvec = "1" static_assertions = "1.1.0" strum = { version = "0.25.0", features = ["derive"] } tracy-client = "0.17" -thiserror = "1.0.63" +thiserror = "1.0.64" wgpu = { version = "22.0.0", path = "./wgpu", default-features = false } wgpu-core = { version = "22.0.0", path = "./wgpu-core" } wgpu-macros = { version = "22.0.0", path = "./wgpu-macros" } diff --git a/naga/Cargo.toml b/naga/Cargo.toml index ef66e1c5d..19912a36a 100644 --- a/naga/Cargo.toml +++ b/naga/Cargo.toml @@ -81,7 +81,7 @@ serde = { version = "1.0.210", features = ["derive"], optional = true } petgraph = { version = "0.6", optional = true } pp-rs = { version = "0.2.1", optional = true } hexf-parse = { version = "0.2.1", optional = true } -unicode-xid = { version = "0.2.5", optional = true } +unicode-xid = { version = "0.2.6", optional = true } [build-dependencies] cfg_aliases.workspace = true From 23fa0ae6d048043d40308a3ddebb176c4a686b63 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Mon, 23 Sep 2024 09:47:58 -0400 Subject: [PATCH 27/59] Allow wgpu Team to Update Naga Cargo.toml (#6314) * Allow wgpu Team to Update Naga Cargo.toml * Update deno team to crowlKats --- .github/CODEOWNERS | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 009cd3056..1cae09787 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,10 +1,17 @@ * @gfx-rs/wgpu -/cts_runner/ @gfx-rs/deno @gfx-rs/wgpu -/deno_webgpu/ @gfx-rs/deno @gfx-rs/wgpu +/cts_runner/ @crowlkats @gfx-rs/wgpu +/deno_webgpu/ @crowlkats @gfx-rs/wgpu /naga/ @gfx-rs/naga /naga-cli/ @gfx-rs/naga +# Both wgpu and naga teams are owners of naga infrastructure so +# either team can review changes to deps and docs. +naga/Cargo.toml @gfx-rs/wgpu @gfx-rs/naga +naga/README.md @gfx-rs/wgpu @gfx-rs/naga +naga/CHANGELOG.md @gfx-rs/wgpu @gfx-rs/naga +naga-cli/Cargo.toml @gfx-rs/wgpu @gfx-rs/naga + # We leave the codeowners empty for the changelog, so naga changes # don't trigger wgpu reviews and vise versa. /CHANGELOG.md From 859dd8817e7484b51823d443d7cac93c6e9a7ef2 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Mon, 23 Sep 2024 10:21:11 -0400 Subject: [PATCH 28/59] Only Initialize a Single Backend in Tests (#6315) * Only Initialize a Single Backend in Tests * Update tests/tests/create_surface_error.rs --- tests/src/init.rs | 39 +++++++++++++++++++++-------- tests/src/native.rs | 31 +++++++++++++++-------- tests/src/report.rs | 4 +-- tests/src/run.rs | 6 ++--- tests/tests/create_surface_error.rs | 2 +- tests/tests/device.rs | 2 +- wgpu-macros/src/lib.rs | 2 +- 7 files changed, 57 insertions(+), 29 deletions(-) diff --git a/tests/src/init.rs b/tests/src/init.rs index 140bb202f..3644655be 100644 --- a/tests/src/init.rs +++ b/tests/src/init.rs @@ -1,6 +1,8 @@ use wgpu::{Adapter, Device, Instance, Queue}; use wgt::{Backends, Features, Limits}; +use crate::report::AdapterReport; + /// Initialize the logger for the test runner. pub fn init_logger() { // We don't actually care if it fails @@ -11,7 +13,7 @@ pub fn init_logger() { } /// Initialize a wgpu instance with the options from the environment. -pub fn initialize_instance(force_fxc: bool) -> Instance { +pub fn initialize_instance(backends: wgpu::Backends, force_fxc: bool) -> Instance { // We ignore `WGPU_BACKEND` for now, merely using test filtering to only run a single backend's tests. // // We can potentially work support back into the test runner in the future, but as the adapters are matched up @@ -23,9 +25,9 @@ pub fn initialize_instance(force_fxc: bool) -> Instance { // To "disable" webgpu regardless, we do this by removing the webgpu backend whenever we see // the webgl feature. let backends = if cfg!(feature = "webgl") { - Backends::all() - Backends::BROWSER_WEBGPU + backends - wgpu::Backends::BROWSER_WEBGPU } else { - Backends::all() + backends }; // Some tests need to be able to force demote to FXC, to specifically test workarounds for FXC // behavior. @@ -43,12 +45,16 @@ pub fn initialize_instance(force_fxc: bool) -> Instance { }) } -/// Initialize a wgpu adapter, taking the `n`th adapter from the instance. +/// Initialize a wgpu adapter, using the given adapter report to match the adapter. pub async fn initialize_adapter( - adapter_index: usize, + adapter_report: Option<&AdapterReport>, force_fxc: bool, ) -> (Instance, Adapter, Option) { - let instance = initialize_instance(force_fxc); + let backends = adapter_report + .map(|report| Backends::from(report.info.backend)) + .unwrap_or_default(); + + let instance = initialize_instance(backends, force_fxc); #[allow(unused_variables)] let surface: Option; let surface_guard: Option; @@ -82,13 +88,24 @@ pub async fn initialize_adapter( cfg_if::cfg_if! { if #[cfg(not(target_arch = "wasm32"))] { - let adapter_iter = instance.enumerate_adapters(wgpu::Backends::all()); - let adapter_count = adapter_iter.len(); + let adapter_iter = instance.enumerate_adapters(backends); let adapter = adapter_iter.into_iter() - .nth(adapter_index) - .unwrap_or_else(|| panic!("Tried to get index {adapter_index} adapter, but adapter list was only {adapter_count} long. Is .gpuconfig out of date?")); + // If we have a report, we only want to match the adapter with the same info. + // + // If we don't have a report, we just take the first adapter. + .find(|adapter| if let Some(adapter_report) = adapter_report { + adapter.get_info() == adapter_report.info + } else { + true + }); + let Some(adapter) = adapter else { + panic!( + "Could not find adapter with info {:#?} in {:#?}", + adapter_report.map(|r| &r.info), + instance.enumerate_adapters(backends).into_iter().map(|a| a.get_info()).collect::>(), + ); + }; } else { - assert_eq!(adapter_index, 0); let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { compatible_surface: surface.as_ref(), ..Default::default() diff --git a/tests/src/native.rs b/tests/src/native.rs index 3d328b4ff..afad3d46d 100644 --- a/tests/src/native.rs +++ b/tests/src/native.rs @@ -19,15 +19,16 @@ struct NativeTest { } impl NativeTest { + /// Adapter index is only used for naming the test, the adapters are matched based on the adapter info. fn from_configuration( config: GpuTestConfiguration, - adapter: &AdapterReport, + adapter_report: AdapterReport, adapter_index: usize, ) -> Self { - let backend = adapter.info.backend; - let device_name = &adapter.info.name; + let backend = adapter_report.info.backend; + let device_name = &adapter_report.info.name; - let test_info = TestInfo::from_configuration(&config, adapter); + let test_info = TestInfo::from_configuration(&config, &adapter_report); let full_name = format!( "[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}", @@ -50,10 +51,12 @@ impl NativeTest { let env_value = if metal_validation { "1" } else { "0" }; std::env::set_var("MTL_DEBUG_LAYER", env_value); - // Metal Shader Validation is entirely broken in the paravirtualized CI environment. - // std::env::set_var("MTL_SHADER_VALIDATION", env_value); + if std::env::var("GITHUB_ACTIONS").as_deref() != Ok("true") { + // Metal Shader Validation is entirely broken in the paravirtualized CI environment. + std::env::set_var("MTL_SHADER_VALIDATION", env_value); + } - execute_test(config, Some(test_info), adapter_index).await; + execute_test(Some(&adapter_report), config, Some(test_info)).await; }), } } @@ -83,16 +86,24 @@ pub fn main() -> MainResult { &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) .context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")? }; - let report = GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?; + let mut report = + GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?; + + // Filter out the adapters that are not part of WGPU_BACKEND. + let wgpu_backends = wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::all()); + report + .devices + .retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend))); let mut test_guard = TEST_LIST.lock(); + // Iterate through all the tests. Creating a test per adapter. execute_native(test_guard.drain(..).flat_map(|test| { report .devices .iter() .enumerate() - .map(move |(adapter_index, adapter)| { - NativeTest::from_configuration(test.clone(), adapter, adapter_index) + .map(move |(adapter_index, adapter_report)| { + NativeTest::from_configuration(test.clone(), adapter_report.clone(), adapter_index) }) })); diff --git a/tests/src/report.rs b/tests/src/report.rs index 42633e72a..b26bdbfaf 100644 --- a/tests/src/report.rs +++ b/tests/src/report.rs @@ -25,8 +25,8 @@ impl GpuReport { /// A single report of the capabilities of an Adapter. /// /// Must be synchronized with the definition on wgpu-info/src/report.rs. -#[derive(Deserialize)] -pub(crate) struct AdapterReport { +#[derive(Deserialize, Clone)] +pub struct AdapterReport { pub info: AdapterInfo, pub features: Features, pub limits: Limits, diff --git a/tests/src/run.rs b/tests/src/run.rs index 303c4c24a..5fb15c4c3 100644 --- a/tests/src/run.rs +++ b/tests/src/run.rs @@ -24,14 +24,14 @@ pub struct TestingContext { pub queue: Queue, } -/// Execute the given test configuration with the given adapter index. +/// Execute the given test configuration with the given adapter report. /// /// If test_info is specified, will use the information whether to skip the test. /// If it is not, we'll create the test info from the adapter itself. pub async fn execute_test( + adapter_report: Option<&AdapterReport>, config: GpuTestConfiguration, test_info: Option, - adapter_index: usize, ) { // If we get information externally, skip based on that information before we do anything. if let Some(TestInfo { skip: true, .. }) = test_info { @@ -43,7 +43,7 @@ pub async fn execute_test( let _test_guard = isolation::OneTestPerProcessGuard::new(); let (instance, adapter, _surface_guard) = - initialize_adapter(adapter_index, config.params.force_fxc).await; + initialize_adapter(adapter_report, config.params.force_fxc).await; let adapter_info = adapter.get_info(); let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities(); diff --git a/tests/tests/create_surface_error.rs b/tests/tests/create_surface_error.rs index e3b48cb75..75c8b9020 100644 --- a/tests/tests/create_surface_error.rs +++ b/tests/tests/create_surface_error.rs @@ -6,7 +6,7 @@ #[wasm_bindgen_test::wasm_bindgen_test] fn canvas_get_context_returned_null() { // Not using the normal testing infrastructure because that goes straight to creating the canvas for us. - let instance = wgpu_test::initialize_instance(false); + let instance = wgpu_test::initialize_instance(wgpu::Backends::all(), false); // Create canvas let canvas = wgpu_test::initialize_html_canvas(); diff --git a/tests/tests/device.rs b/tests/tests/device.rs index c832af06b..2774bfd52 100644 --- a/tests/tests/device.rs +++ b/tests/tests/device.rs @@ -87,7 +87,7 @@ static REQUEST_DEVICE_ERROR_MESSAGE_NATIVE: GpuTestConfiguration = async fn request_device_error_message() { // Not using initialize_test() because that doesn't let us catch the error // nor .await anything - let (_instance, adapter, _surface_guard) = wgpu_test::initialize_adapter(0, false).await; + let (_instance, adapter, _surface_guard) = wgpu_test::initialize_adapter(None, false).await; let device_error = adapter .request_device( diff --git a/wgpu-macros/src/lib.rs b/wgpu-macros/src/lib.rs index 0b0812507..19eea678a 100644 --- a/wgpu-macros/src/lib.rs +++ b/wgpu-macros/src/lib.rs @@ -37,7 +37,7 @@ pub fn gpu_test(_attr: TokenStream, item: TokenStream) -> TokenStream { // Allow any type that can be converted to a GpuTestConfiguration let test_config = ::wgpu_test::GpuTestConfiguration::from(#expr).name_from_init_function_typename::(#ident_lower); - ::wgpu_test::execute_test(test_config, None, 0).await; + ::wgpu_test::execute_test(None, test_config, None).await; } } .into() From fc2fd95a988eaba59d561939971d7f516a4d5b7f Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 24 Sep 2024 22:52:25 -0400 Subject: [PATCH 29/59] fix: handle `Queue::submit` non-fatally (#6318) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change the signature of `wgpu_core::Global::queue_submit` to return a `(SubmissionIndex, …)` in addition to its current error type. * Change the control flow of errors in `Queue::submit` to break to the end of a block. This is similar to what we already do in many APIs in `wgpu_core`. * Hoist the scope of the local `submit_index` binding so it can be used at the point where we need to convert current error paths to also return the submission index. Later, we will likely want to avoid actually retrieving a new submission index so we can minimize the critical section of code. We'll need to figure out a strategy for returning a valid (but not necessarily unique) index in the case of failures that prevent successful submission. --- deno_webgpu/queue.rs | 2 +- tests/tests/regression/issue_6317.rs | 58 ++++++++++++++++++++++ tests/tests/root.rs | 1 + wgpu-core/src/device/queue.rs | 73 +++++++++++++++++++--------- wgpu/src/backend/wgpu_core.rs | 5 +- 5 files changed, 115 insertions(+), 24 deletions(-) create mode 100644 tests/tests/regression/issue_6317.rs diff --git a/deno_webgpu/queue.rs b/deno_webgpu/queue.rs index fdbf993f8..5915b68f2 100644 --- a/deno_webgpu/queue.rs +++ b/deno_webgpu/queue.rs @@ -44,7 +44,7 @@ pub fn op_webgpu_queue_submit( }) .collect::, AnyError>>()?; - let maybe_err = instance.queue_submit(queue, &ids).err(); + let maybe_err = instance.queue_submit(queue, &ids).err().map(|(_idx, e)| e); for rid in command_buffers { let resource = state.resource_table.take::(rid)?; diff --git a/tests/tests/regression/issue_6317.rs b/tests/tests/regression/issue_6317.rs new file mode 100644 index 000000000..20945006f --- /dev/null +++ b/tests/tests/regression/issue_6317.rs @@ -0,0 +1,58 @@ +use wgpu::{DownlevelFlags, Limits}; +use wgpu_macros::gpu_test; +use wgpu_test::{fail, GpuTestConfiguration, TestParameters}; + +#[gpu_test] +static NON_FATAL_ERRORS_IN_QUEUE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) + .limits(Limits::downlevel_defaults()), + ) + .run_sync(|ctx| { + let shader_with_trivial_bind_group = concat!( + "@group(0) @binding(0) var stuff: u32;\n", + "\n", + "@compute @workgroup_size(1) fn main() { stuff = 2u; }\n" + ); + + let module = ctx + .device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(shader_with_trivial_bind_group.into()), + }); + + let compute_pipeline = + ctx.device + .create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: None, + layout: None, + module: &module, + entry_point: None, + compilation_options: Default::default(), + cache: Default::default(), + }); + + fail( + &ctx.device, + || { + let mut command_encoder = ctx.device.create_command_encoder(&Default::default()); + { + let mut render_pass = command_encoder.begin_compute_pass(&Default::default()); + render_pass.set_pipeline(&compute_pipeline); + + // NOTE: We deliberately don't set a bind group here, to provoke a validation + // error. + + render_pass.dispatch_workgroups(1, 1, 1); + } + + let _idx = ctx.queue.submit([command_encoder.finish()]); + }, + Some(concat!( + "The current set ComputePipeline with '' label ", + "expects a BindGroup to be set at index 0" + )), + ) + }); diff --git a/tests/tests/root.rs b/tests/tests/root.rs index df0dce5fe..3bb8e14a9 100644 --- a/tests/tests/root.rs +++ b/tests/tests/root.rs @@ -6,6 +6,7 @@ mod regression { mod issue_4485; mod issue_4514; mod issue_5553; + mod issue_6317; } mod bgra8unorm_storage; diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index f576b2412..bd6d99f1c 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -1027,11 +1027,13 @@ impl Global { &self, queue_id: QueueId, command_buffer_ids: &[id::CommandBufferId], - ) -> Result { + ) -> Result { profiling::scope!("Queue::submit"); api_log!("Queue::submit {queue_id:?}"); - let (submit_index, callbacks) = { + let submit_index; + + let res = 'error: { let hub = &self.hub; let queue = hub.queues.get(queue_id); @@ -1042,7 +1044,7 @@ impl Global { // Fence lock must be acquired after the snatch lock everywhere to avoid deadlocks. let mut fence = device.fence.write(); - let submit_index = device + submit_index = device .active_submission_index .fetch_add(1, Ordering::SeqCst) + 1; @@ -1119,18 +1121,29 @@ impl Global { } // execute resource transitions - unsafe { + if let Err(e) = unsafe { baked.encoder.begin_encoding(hal_label( Some("(wgpu internal) Transit"), device.instance_flags, )) } - .map_err(|e| device.handle_hal_error(e))?; + .map_err(|e| device.handle_hal_error(e)) + { + break 'error Err(e.into()); + } //Note: locking the trackers has to be done after the storages let mut trackers = device.trackers.lock(); - baked.initialize_buffer_memory(&mut trackers, &snatch_guard)?; - baked.initialize_texture_memory(&mut trackers, device, &snatch_guard)?; + if let Err(e) = baked.initialize_buffer_memory(&mut trackers, &snatch_guard) + { + break 'error Err(e.into()); + } + if let Err(e) = + baked.initialize_texture_memory(&mut trackers, device, &snatch_guard) + { + break 'error Err(e.into()); + } + //Note: stateless trackers are not merged: // device already knows these resources exist. CommandBuffer::insert_barriers_from_device_tracker( @@ -1147,13 +1160,16 @@ impl Global { // Note: we could technically do it after all of the command buffers, // but here we have a command encoder by hand, so it's easier to use it. if !used_surface_textures.is_empty() { - unsafe { + if let Err(e) = unsafe { baked.encoder.begin_encoding(hal_label( Some("(wgpu internal) Present"), device.instance_flags, )) } - .map_err(|e| device.handle_hal_error(e))?; + .map_err(|e| device.handle_hal_error(e)) + { + break 'error Err(e.into()); + } let texture_barriers = trackers .textures .set_from_usage_scope_and_drain_transitions( @@ -1180,7 +1196,7 @@ impl Global { } if let Some(first_error) = first_error { - return Err(first_error); + break 'error Err(first_error); } } } @@ -1190,9 +1206,9 @@ impl Global { { used_surface_textures.set_size(hub.textures.read().len()); for texture in pending_writes.dst_textures.values() { - match texture.try_inner(&snatch_guard)? { - TextureInner::Native { .. } => {} - TextureInner::Surface { .. } => { + match texture.try_inner(&snatch_guard) { + Ok(TextureInner::Native { .. }) => {} + Ok(TextureInner::Surface { .. }) => { // Compare the Arcs by pointer as Textures don't implement Eq submit_surface_textures_owned .insert(Arc::as_ptr(texture), texture.clone()); @@ -1203,6 +1219,7 @@ impl Global { .unwrap() }; } + Err(e) => break 'error Err(e.into()), } } @@ -1224,10 +1241,12 @@ impl Global { } } - if let Some(pending_execution) = - pending_writes.pre_submit(&device.command_allocator, device, &queue)? - { - active_executions.insert(0, pending_execution); + match pending_writes.pre_submit(&device.command_allocator, device, &queue) { + Ok(Some(pending_execution)) => { + active_executions.insert(0, pending_execution); + } + Ok(None) => {} + Err(e) => break 'error Err(e.into()), } let hal_command_buffers = active_executions @@ -1249,14 +1268,17 @@ impl Global { submit_surface_textures.push(raw); } - unsafe { + if let Err(e) = unsafe { queue.raw().submit( &hal_command_buffers, &submit_surface_textures, (fence.as_mut(), submit_index), ) } - .map_err(|e| device.handle_hal_error(e))?; + .map_err(|e| device.handle_hal_error(e)) + { + break 'error Err(e.into()); + } // Advance the successful submission index. device @@ -1280,12 +1302,19 @@ impl Global { let (closures, _) = match device.maintain(fence_guard, wgt::Maintain::Poll, snatch_guard) { Ok(closures) => closures, - Err(WaitIdleError::Device(err)) => return Err(QueueSubmitError::Queue(err)), - Err(WaitIdleError::StuckGpu) => return Err(QueueSubmitError::StuckGpu), + Err(WaitIdleError::Device(err)) => { + break 'error Err(QueueSubmitError::Queue(err)) + } + Err(WaitIdleError::StuckGpu) => break 'error Err(QueueSubmitError::StuckGpu), Err(WaitIdleError::WrongSubmissionIndex(..)) => unreachable!(), }; - (submit_index, closures) + Ok(closures) + }; + + let callbacks = match res { + Ok(ok) => ok, + Err(e) => return Err((submit_index, e)), }; // the closures should execute with nothing locked! diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index 3aac20e21..1d1ffda20 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -2074,7 +2074,10 @@ impl crate::Context for ContextWgpuCore { let index = match self.0.queue_submit(queue_data.id, &temp_command_buffers) { Ok(index) => index, - Err(err) => self.handle_error_fatal(err, "Queue::submit"), + Err((index, err)) => { + self.handle_error_nolabel(&queue_data.error_sink, err, "Queue::submit"); + index + } }; for cmdbuf in &temp_command_buffers { From 8e787eb70a98c260f44b129128037d9a28d2c9ae Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Tue, 24 Sep 2024 20:40:53 -0700 Subject: [PATCH 30/59] style: simplify string formatting for readability (#6316) --- benches/benches/root.rs | 2 +- benches/benches/shader.rs | 2 +- examples/src/main.rs | 2 +- examples/src/timestamp_queries/mod.rs | 2 +- naga-cli/src/bin/naga.rs | 4 ++-- naga/src/back/dot/mod.rs | 2 +- naga/src/back/glsl/mod.rs | 12 ++++++------ naga/src/back/hlsl/help.rs | 3 +-- naga/src/back/hlsl/storage.rs | 2 +- naga/src/back/hlsl/writer.rs | 12 ++++++------ naga/src/back/msl/mod.rs | 3 +-- naga/src/back/msl/writer.rs | 7 +++---- naga/src/back/wgsl/writer.rs | 18 +++++++++--------- naga/src/front/wgsl/error.rs | 4 ++-- player/src/bin/play.rs | 2 +- player/src/lib.rs | 2 +- player/tests/test.rs | 15 +++++++-------- tests/src/image.rs | 7 +++---- tests/src/params.rs | 2 +- tests/tests/subgroup_operations/mod.rs | 4 ++-- tests/tests/vertex_formats/mod.rs | 5 +---- tests/tests/vertex_indices/mod.rs | 5 +---- wgpu-hal/src/vulkan/device.rs | 3 +-- wgpu-info/src/human.rs | 4 ++-- wgpu-macros/src/lib.rs | 4 ++-- wgpu/src/backend/wgpu_core.rs | 2 +- 26 files changed, 59 insertions(+), 71 deletions(-) diff --git a/benches/benches/root.rs b/benches/benches/root.rs index 064617783..630ea6027 100644 --- a/benches/benches/root.rs +++ b/benches/benches/root.rs @@ -39,7 +39,7 @@ impl DeviceState { let adapter_info = adapter.get_info(); - eprintln!("{:?}", adapter_info); + eprintln!("{adapter_info:?}"); let (device, queue) = block_on(adapter.request_device( &wgpu::DeviceDescriptor { diff --git a/benches/benches/shader.rs b/benches/benches/shader.rs index c6aa631d9..f1a949412 100644 --- a/benches/benches/shader.rs +++ b/benches/benches/shader.rs @@ -40,7 +40,7 @@ impl Inputs { _ => continue, }, Err(e) => { - eprintln!("Skipping file: {:?}", e); + eprintln!("Skipping file: {e:?}"); continue; } } diff --git a/examples/src/main.rs b/examples/src/main.rs index 8b149d5a2..5d29d484b 100644 --- a/examples/src/main.rs +++ b/examples/src/main.rs @@ -201,7 +201,7 @@ fn print_unknown_example(_result: Option) {} #[cfg(not(target_arch = "wasm32"))] fn print_unknown_example(result: Option) { if let Some(example) = result { - println!("Unknown example: {}", example); + println!("Unknown example: {example}"); } else { println!("Please specify an example as the first argument!"); } diff --git a/examples/src/timestamp_queries/mod.rs b/examples/src/timestamp_queries/mod.rs index 2921ae4c8..a253938a3 100644 --- a/examples/src/timestamp_queries/mod.rs +++ b/examples/src/timestamp_queries/mod.rs @@ -227,7 +227,7 @@ async fn run() { let queries = submit_render_and_compute_pass_with_queries(&device, &queue); let raw_results = queries.wait_for_results(&device); - println!("Raw timestamp buffer contents: {:?}", raw_results); + println!("Raw timestamp buffer contents: {raw_results:?}"); QueryResults::from_raw_results(raw_results, timestamps_inside_passes).print(&queue); } diff --git a/naga-cli/src/bin/naga.rs b/naga-cli/src/bin/naga.rs index e28519cb0..8cec1801c 100644 --- a/naga-cli/src/bin/naga.rs +++ b/naga-cli/src/bin/naga.rs @@ -853,7 +853,7 @@ fn bulk_validate(args: Args, params: &Parameters) -> anyhow::Result<()> { Ok(parsed) => parsed, Err(error) => { invalid.push(input_path.clone()); - eprintln!("Error validating {}:", input_path); + eprintln!("Error validating {input_path}:"); eprintln!("{error}"); continue; } @@ -866,7 +866,7 @@ fn bulk_validate(args: Args, params: &Parameters) -> anyhow::Result<()> { if let Err(error) = validator.validate(&module) { invalid.push(input_path.clone()); - eprintln!("Error validating {}:", input_path); + eprintln!("Error validating {input_path}:"); if let Some(input) = &input_text { let filename = path.file_name().and_then(std::ffi::OsStr::to_str); emit_annotated_error(&error, filename.unwrap_or("input"), input); diff --git a/naga/src/back/dot/mod.rs b/naga/src/back/dot/mod.rs index 4f29ab776..278087965 100644 --- a/naga/src/back/dot/mod.rs +++ b/naga/src/back/dot/mod.rs @@ -698,7 +698,7 @@ fn write_function_expressions( E::RayQueryGetIntersection { query, committed } => { edges.insert("", query); let ty = if committed { "Committed" } else { "Candidate" }; - (format!("rayQueryGet{}Intersection", ty).into(), 4) + (format!("rayQueryGet{ty}Intersection").into(), 4) } E::SubgroupBallotResult => ("SubgroupBallotResult".into(), 4), E::SubgroupOperationResult { .. } => ("SubgroupOperationResult".into(), 4), diff --git a/naga/src/back/glsl/mod.rs b/naga/src/back/glsl/mod.rs index 159d9cdcf..2ce9f22f2 100644 --- a/naga/src/back/glsl/mod.rs +++ b/naga/src/back/glsl/mod.rs @@ -2645,15 +2645,15 @@ impl<'a, W: Write> Writer<'a, W> { match literal { // Floats are written using `Debug` instead of `Display` because it always appends the // decimal part even it's zero which is needed for a valid glsl float constant - crate::Literal::F64(value) => write!(self.out, "{:?}LF", value)?, - crate::Literal::F32(value) => write!(self.out, "{:?}", value)?, + crate::Literal::F64(value) => write!(self.out, "{value:?}LF")?, + crate::Literal::F32(value) => write!(self.out, "{value:?}")?, // Unsigned integers need a `u` at the end // // While `core` doesn't necessarily need it, it's allowed and since `es` needs it we // always write it as the extra branch wouldn't have any benefit in readability - crate::Literal::U32(value) => write!(self.out, "{}u", value)?, - crate::Literal::I32(value) => write!(self.out, "{}", value)?, - crate::Literal::Bool(value) => write!(self.out, "{}", value)?, + crate::Literal::U32(value) => write!(self.out, "{value}u")?, + crate::Literal::I32(value) => write!(self.out, "{value}")?, + crate::Literal::Bool(value) => write!(self.out, "{value}")?, crate::Literal::I64(_) => { return Err(Error::Custom("GLSL has no 64-bit integer type".into())); } @@ -4614,7 +4614,7 @@ impl<'a, W: Write> Writer<'a, W> { for i in 0..count.get() { // Add the array accessor and recurse. - segments.push(format!("[{}]", i)); + segments.push(format!("[{i}]")); self.collect_push_constant_items(base, segments, layouter, offset, items); segments.pop(); } diff --git a/naga/src/back/hlsl/help.rs b/naga/src/back/hlsl/help.rs index 80f385d01..9032d22c8 100644 --- a/naga/src/back/hlsl/help.rs +++ b/naga/src/back/hlsl/help.rs @@ -1046,8 +1046,7 @@ impl<'a, W: Write> super::Writer<'a, W> { } ref other => { return Err(super::Error::Unimplemented(format!( - "Array length of base {:?}", - other + "Array length of base {other:?}" ))) } }; diff --git a/naga/src/back/hlsl/storage.rs b/naga/src/back/hlsl/storage.rs index 4d3a6af56..9fbdf6769 100644 --- a/naga/src/back/hlsl/storage.rs +++ b/naga/src/back/hlsl/storage.rs @@ -350,7 +350,7 @@ impl super::Writer<'_, W> { self.write_store_value(module, &value, func_ctx)?; writeln!(self.out, "));")?; } else { - write!(self.out, "{}{}.Store(", level, var_name)?; + write!(self.out, "{level}{var_name}.Store(")?; self.write_storage_address(module, &chain, func_ctx)?; write!(self.out, ", ")?; self.write_store_value(module, &value, func_ctx)?; diff --git a/naga/src/back/hlsl/writer.rs b/naga/src/back/hlsl/writer.rs index e33fc79f2..0eb18f0e1 100644 --- a/naga/src/back/hlsl/writer.rs +++ b/naga/src/back/hlsl/writer.rs @@ -965,7 +965,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { let constant = &module.constants[handle]; self.write_type(module, constant.ty)?; let name = &self.names[&NameKey::Constant(handle)]; - write!(self.out, " {}", name)?; + write!(self.out, " {name}")?; // Write size for array type if let TypeInner::Array { base, size, .. } = module.types[constant.ty].inner { self.write_array_size(module, base, size)?; @@ -2383,11 +2383,11 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { // decimal part even it's zero crate::Literal::F64(value) => write!(self.out, "{value:?}L")?, crate::Literal::F32(value) => write!(self.out, "{value:?}")?, - crate::Literal::U32(value) => write!(self.out, "{}u", value)?, - crate::Literal::I32(value) => write!(self.out, "{}", value)?, - crate::Literal::U64(value) => write!(self.out, "{}uL", value)?, - crate::Literal::I64(value) => write!(self.out, "{}L", value)?, - crate::Literal::Bool(value) => write!(self.out, "{}", value)?, + crate::Literal::U32(value) => write!(self.out, "{value}u")?, + crate::Literal::I32(value) => write!(self.out, "{value}")?, + crate::Literal::U64(value) => write!(self.out, "{value}uL")?, + crate::Literal::I64(value) => write!(self.out, "{value}L")?, + crate::Literal::Bool(value) => write!(self.out, "{value}")?, crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => { return Err(Error::Custom( "Abstract types should not appear in IR presented to backends".into(), diff --git a/naga/src/back/msl/mod.rs b/naga/src/back/msl/mod.rs index 96dd142a5..4bc06469e 100644 --- a/naga/src/back/msl/mod.rs +++ b/naga/src/back/msl/mod.rs @@ -437,8 +437,7 @@ impl Options { }) } LocationMode::Uniform => Err(Error::GenericValidation(format!( - "Unexpected Binding::Location({}) for the Uniform mode", - location + "Unexpected Binding::Location({location}) for the Uniform mode" ))), }, } diff --git a/naga/src/back/msl/writer.rs b/naga/src/back/msl/writer.rs index e09ca557a..7ab97f491 100644 --- a/naga/src/back/msl/writer.rs +++ b/naga/src/back/msl/writer.rs @@ -3820,12 +3820,11 @@ impl Writer { writeln!(self.out)?; writeln!( self.out, - "{} {defined_func_name}({arg_type_name} arg) {{ + "{struct_name} {defined_func_name}({arg_type_name} arg) {{ {other_type_name} other; {arg_type_name} fract = {NAMESPACE}::{called_func_name}(arg, other); - return {}{{ fract, other }}; -}}", - struct_name, struct_name + return {struct_name}{{ fract, other }}; +}}" )?; } &crate::PredeclaredType::AtomicCompareExchangeWeakResult { .. } => {} diff --git a/naga/src/back/wgsl/writer.rs b/naga/src/back/wgsl/writer.rs index 0f2635eb0..e8b942a62 100644 --- a/naga/src/back/wgsl/writer.rs +++ b/naga/src/back/wgsl/writer.rs @@ -1221,31 +1221,31 @@ impl Writer { match expressions[expr] { Expression::Literal(literal) => match literal { - crate::Literal::F32(value) => write!(self.out, "{}f", value)?, - crate::Literal::U32(value) => write!(self.out, "{}u", value)?, + crate::Literal::F32(value) => write!(self.out, "{value}f")?, + crate::Literal::U32(value) => write!(self.out, "{value}u")?, crate::Literal::I32(value) => { // `-2147483648i` is not valid WGSL. The most negative `i32` // value can only be expressed in WGSL using AbstractInt and // a unary negation operator. if value == i32::MIN { - write!(self.out, "i32({})", value)?; + write!(self.out, "i32({value})")?; } else { - write!(self.out, "{}i", value)?; + write!(self.out, "{value}i")?; } } - crate::Literal::Bool(value) => write!(self.out, "{}", value)?, - crate::Literal::F64(value) => write!(self.out, "{:?}lf", value)?, + crate::Literal::Bool(value) => write!(self.out, "{value}")?, + crate::Literal::F64(value) => write!(self.out, "{value:?}lf")?, crate::Literal::I64(value) => { // `-9223372036854775808li` is not valid WGSL. The most negative `i64` // value can only be expressed in WGSL using AbstractInt and // a unary negation operator. if value == i64::MIN { - write!(self.out, "i64({})", value)?; + write!(self.out, "i64({value})")?; } else { - write!(self.out, "{}li", value)?; + write!(self.out, "{value}li")?; } } - crate::Literal::U64(value) => write!(self.out, "{:?}lu", value)?, + crate::Literal::U64(value) => write!(self.out, "{value:?}lu")?, crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => { return Err(Error::Custom( "Abstract types should not appear in IR presented to backends".into(), diff --git a/naga/src/front/wgsl/error.rs b/naga/src/front/wgsl/error.rs index 3d4ac6218..a7986ec89 100644 --- a/naga/src/front/wgsl/error.rs +++ b/naga/src/front/wgsl/error.rs @@ -790,11 +790,11 @@ impl<'a> Error<'a> { Error::ConcretizationFailed(ref error) => { let ConcretizationFailedError { expr_span, ref expr_type, ref scalar, ref inner } = **error; ParseError { - message: format!("failed to convert expression to a concrete type: {}", inner), + message: format!("failed to convert expression to a concrete type: {inner}"), labels: vec![ ( expr_span, - format!("this expression has type {}", expr_type).into(), + format!("this expression has type {expr_type}").into(), ) ], notes: vec![ diff --git a/player/src/bin/play.rs b/player/src/bin/play.rs index 558eb194b..842a05148 100644 --- a/player/src/bin/play.rs +++ b/player/src/bin/play.rs @@ -91,7 +91,7 @@ fn main() { Some(queue_id), ); if let Err(e) = res { - panic!("{:?}", e); + panic!("{e:?}"); } (device_id, queue_id) } diff --git a/player/src/lib.rs b/player/src/lib.rs index 241c19096..3b0b4149a 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -237,7 +237,7 @@ impl GlobalPlay for wgc::global::Global { let module = ron::de::from_str(&code).unwrap(); wgc::pipeline::ShaderModuleSource::Naga(module) } else { - panic!("Unknown shader {}", data); + panic!("Unknown shader {data}"); }; let (_, error) = self.device_create_shader_module(device, &desc, source, Some(id)); if let Some(e) = error { diff --git a/player/tests/test.rs b/player/tests/test.rs index ec96f5446..107481b74 100644 --- a/player/tests/test.rs +++ b/player/tests/test.rs @@ -58,7 +58,7 @@ struct Test<'a> { fn map_callback(status: Result<(), wgc::resource::BufferAccessError>) { if let Err(e) = status { - panic!("Buffer map error: {}", e); + panic!("Buffer map error: {e}"); } } @@ -88,7 +88,7 @@ impl Test<'_> { .iter() .map(|feature| { wgt::Features::from_name(feature) - .unwrap_or_else(|| panic!("Invalid feature flag {}", feature)) + .unwrap_or_else(|| panic!("Invalid feature flag {feature}")) }) .fold(wgt::Features::empty(), |a, b| a | b); Test { @@ -120,7 +120,7 @@ impl Test<'_> { Some(queue_id), ); if let Err(e) = res { - panic!("{:?}", e); + panic!("{e:?}"); } let mut command_buffer_id_manager = wgc::identity::IdentityManager::new(); @@ -186,8 +186,7 @@ impl Test<'_> { if &expected_data[..] != contents { panic!( - "Test expectation is not met!\nBuffer content was:\n{:?}\nbut expected:\n{:?}", - contents, expected_data + "Test expectation is not met!\nBuffer content was:\n{contents:?}\nbut expected:\n{expected_data:?}" ); } } @@ -209,7 +208,7 @@ const BACKENDS: &[wgt::Backend] = &[ impl Corpus { fn run_from(path: PathBuf) { - println!("Corpus {:?}", path); + println!("Corpus {path:?}"); let dir = path.parent().unwrap(); let corpus: Corpus = ron::de::from_reader(File::open(&path).unwrap()).unwrap(); @@ -219,7 +218,7 @@ impl Corpus { } let mut test_num = 0; for test_path in &corpus.tests { - println!("\t\tTest '{:?}'", test_path); + println!("\t\tTest '{test_path:?}'"); let global = wgc::global::Global::new( "test", @@ -243,7 +242,7 @@ impl Corpus { Err(_) => continue, }; - println!("\tBackend {:?}", backend); + println!("\tBackend {backend:?}"); let supported_features = global.adapter_features(adapter); let downlevel_caps = global.adapter_downlevel_capabilities(adapter); diff --git a/tests/src/image.rs b/tests/src/image.rs index 602d93c4e..f1fe07b10 100644 --- a/tests/src/image.rs +++ b/tests/src/image.rs @@ -188,11 +188,10 @@ pub async fn compare_image_output( sanitize_for_path(&adapter_info.driver) ); // Determine the paths to write out the various intermediate files - let actual_path = Path::new(&path).with_file_name( - OsString::from_str(&format!("{}-{}-actual.png", file_stem, renderer)).unwrap(), - ); + let actual_path = Path::new(&path) + .with_file_name(OsString::from_str(&format!("{file_stem}-{renderer}-actual.png")).unwrap()); let difference_path = Path::new(&path).with_file_name( - OsString::from_str(&format!("{}-{}-difference.png", file_stem, renderer,)).unwrap(), + OsString::from_str(&format!("{file_stem}-{renderer}-difference.png",)).unwrap(), ); let mut all_passed; diff --git a/tests/src/params.rs b/tests/src/params.rs index e5d50a485..d3b7070f3 100644 --- a/tests/src/params.rs +++ b/tests/src/params.rs @@ -151,7 +151,7 @@ impl TestInfo { let names: ArrayVec<_, 4> = reasons.iter_names().map(|(name, _)| name).collect(); let names_text = names.join(" | "); - format!("Skipped Failure: {}", names_text) + format!("Skipped Failure: {names_text}") } else if !unsupported_reasons.is_empty() { skip = true; format!("Unsupported: {}", unsupported_reasons.join(" | ")) diff --git a/tests/tests/subgroup_operations/mod.rs b/tests/tests/subgroup_operations/mod.rs index ecf8adfd7..f874a6bac 100644 --- a/tests/tests/subgroup_operations/mod.rs +++ b/tests/tests/subgroup_operations/mod.rs @@ -123,10 +123,10 @@ static SUBGROUP_OPERATIONS: GpuTestConfiguration = GpuTestConfiguration::new() .enumerate() .filter(|(_, (r, e))| *r != e) { - write!(&mut msg, "thread {} failed tests:", thread).unwrap(); + write!(&mut msg, "thread {thread} failed tests:").unwrap(); let difference = result ^ expected; for i in (0..u32::BITS).filter(|i| (difference & (1 << i)) != 0) { - write!(&mut msg, " {},", i).unwrap(); + write!(&mut msg, " {i},").unwrap(); } writeln!(&mut msg).unwrap(); } diff --git a/tests/tests/vertex_formats/mod.rs b/tests/tests/vertex_formats/mod.rs index d447ac8f7..189120d4a 100644 --- a/tests/tests/vertex_formats/mod.rs +++ b/tests/tests/vertex_formats/mod.rs @@ -352,10 +352,7 @@ async fn vertex_formats_common(ctx: TestingContext, tests: &[Test<'_>]) { let mut deltas = data.iter().zip(expected.iter()).map(|(d, e)| (d - e).abs()); if deltas.any(|x| x > EPSILON) { - eprintln!( - "Failed: Got: {:?} Expected: {:?} - {case_name}", - data, expected, - ); + eprintln!("Failed: Got: {data:?} Expected: {expected:?} - {case_name}",); failed = true; continue; } diff --git a/tests/tests/vertex_indices/mod.rs b/tests/tests/vertex_indices/mod.rs index 5c5ce8a20..300a97eeb 100644 --- a/tests/tests/vertex_indices/mod.rs +++ b/tests/tests/vertex_indices/mod.rs @@ -471,10 +471,7 @@ async fn vertex_index_common(ctx: TestingContext) { test.case, test.id_source, test.draw_call_kind, test.encoder_kind ); if data != expected { - eprintln!( - "Failed: Got: {:?} Expected: {:?} - {case_name}", - data, expected, - ); + eprintln!("Failed: Got: {data:?} Expected: {expected:?} - {case_name}",); failed = true; } else { eprintln!("Passed: {case_name}"); diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 6c3bfc5ed..9f0fc6773 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -2538,8 +2538,7 @@ impl super::DeviceShared { } None => { crate::hal_usage_error(format!( - "no signals reached value {}", - wait_value + "no signals reached value {wait_value}" )); } } diff --git a/wgpu-info/src/human.rs b/wgpu-info/src/human.rs index 24eeec000..8bbd4c006 100644 --- a/wgpu-info/src/human.rs +++ b/wgpu-info/src/human.rs @@ -230,7 +230,7 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize for format in TEXTURE_FORMAT_LIST { let features = texture_format_features[&format]; let format_name = texture::texture_format_name(format); - write!(output, "\t\t{format_name:>0$}", max_format_name_size)?; + write!(output, "\t\t{format_name:>max_format_name_size$}")?; for bit in wgpu::TextureUsages::all().iter() { write!(output, " │ ")?; if features.allowed_usages.contains(bit) { @@ -259,7 +259,7 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize let features = texture_format_features[&format]; let format_name = texture::texture_format_name(format); - write!(output, "\t\t{format_name:>0$}", max_format_name_size)?; + write!(output, "\t\t{format_name:>max_format_name_size$}")?; for bit in wgpu::TextureFormatFeatureFlags::all().iter() { write!(output, " │ ")?; if features.flags.contains(bit) { diff --git a/wgpu-macros/src/lib.rs b/wgpu-macros/src/lib.rs index 19eea678a..f8232c036 100644 --- a/wgpu-macros/src/lib.rs +++ b/wgpu-macros/src/lib.rs @@ -14,8 +14,8 @@ pub fn gpu_test(_attr: TokenStream, item: TokenStream) -> TokenStream { let ident_str = ident.to_string(); let ident_lower = ident_str.to_snake_case(); - let register_test_name = Ident::new(&format!("{}_initializer", ident_lower), ident.span()); - let test_name_webgl = Ident::new(&format!("{}_webgl", ident_lower), ident.span()); + let register_test_name = Ident::new(&format!("{ident_lower}_initializer"), ident.span()); + let test_name_webgl = Ident::new(&format!("{ident_lower}_webgl"), ident.span()); quote! { #[cfg(not(target_arch = "wasm32"))] diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index 1d1ffda20..652df388f 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -359,7 +359,7 @@ impl ContextWgpuCore { print_tree(&mut output, &mut level, err); - format!("Validation Error\n\nCaused by:\n{}", output) + format!("Validation Error\n\nCaused by:\n{output}") } } From 70743809459b41fae850c87575ac39090e7e49bb Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 25 Sep 2024 00:22:38 -0700 Subject: [PATCH 31/59] metal: fix query set result copies (#6322) The NSRange expects length, not end element. See https://github.com/gfx-rs/metal-rs/blob/9bbe74b1d3706e46ddf41bc8aad58ee74b0bf844/src/lib.rs#L51 --- wgpu-hal/src/metal/command.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 069013570..9e38cf865 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -490,7 +490,7 @@ impl crate::CommandEncoder for super::CommandEncoder { wgt::QueryType::Timestamp => { encoder.resolve_counters( set.counter_sample_buffer.as_ref().unwrap(), - metal::NSRange::new(range.start as u64, range.end as u64), + metal::NSRange::new(range.start as u64, (range.end - range.start) as u64), &buffer.raw, offset, ); From e7f891bf2b455927b98e70301c707428c7adf62a Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Wed, 25 Sep 2024 14:10:27 -0700 Subject: [PATCH 32/59] [naga] Remove redundant handle ordering check from validator. (#6321) `Validator::validate_module_handles` already ensures that types refer only to other types appearing earlier in the arena than themselves, so this check in `Validator::validate_type` is redundant. --- naga/src/valid/type.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/naga/src/valid/type.rs b/naga/src/valid/type.rs index 32d5d58f1..0c4466077 100644 --- a/naga/src/valid/type.rs +++ b/naga/src/valid/type.rs @@ -664,9 +664,6 @@ impl super::Validator { ) } Ti::BindingArray { base, size } => { - if base >= handle { - return Err(TypeError::InvalidArrayBaseType(base)); - } let type_info_mask = match size { crate::ArraySize::Constant(_) => TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE, crate::ArraySize::Dynamic => { From 765c20235ea712fd14ad1b9e36bbb2071a86e319 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Thu, 26 Sep 2024 02:47:57 -0400 Subject: [PATCH 33/59] =?UTF-8?q?refactor:=20use=20`include=5Fwgsl!(?= =?UTF-8?q?=E2=80=A6)`=20more=20(#6326)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change uses `include_wgsl!(…)` in usages where an `include_str!("….wgsl")` was used to construct a `ShaderModuleDescriptor`'s `source, but the `label` was set to `None`. This should (1) showcase a nice idiomatic convenience we offer in our examples better, (2) make code more concise, and (3) get some automatically generated labels in diagnostics where it seems it won't hurt. --- CHANGELOG.md | 1 + examples/src/boids/mod.rs | 12 +++--------- examples/src/conservative_raster/mod.rs | 15 +++------------ examples/src/cube/mod.rs | 7 ++----- examples/src/hello_compute/mod.rs | 7 ++----- examples/src/hello_synchronization/mod.rs | 5 +---- examples/src/hello_workgroups/mod.rs | 5 +---- examples/src/mipmap/mod.rs | 12 +++--------- examples/src/msaa_line/mod.rs | 7 ++----- examples/src/render_to_texture/mod.rs | 5 +---- examples/src/repeated_compute/mod.rs | 7 +------ examples/src/shadow/mod.rs | 7 ++----- examples/src/skybox/mod.rs | 7 ++----- examples/src/srgb_blend/mod.rs | 7 ++----- examples/src/stencil_triangles/mod.rs | 6 +----- examples/src/storage_texture/mod.rs | 5 +---- examples/src/timestamp_queries/mod.rs | 5 +---- examples/src/uniform_values/mod.rs | 7 +------ examples/src/water/mod.rs | 12 +++--------- tests/tests/occlusion_query/mod.rs | 7 ++----- tests/tests/partially_bounded_arrays/mod.rs | 7 ++----- tests/tests/subgroup_operations/mod.rs | 7 ++----- 22 files changed, 39 insertions(+), 121 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 894da6ddc..239b1f7d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,6 +142,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). ### Documentation - Removed some OpenGL and Vulkan references from `wgpu-types` documentation. Fixed Storage texel types in examples. By @Nelarius in [#6271](https://github.com/gfx-rs/wgpu/pull/6271) +- Used `wgpu::include_wgsl!(…)` more in examples and tests. By @ErichDonGubler in [#6326](https://github.com/gfx-rs/wgpu/pull/6326). ### Dependency Updates diff --git a/examples/src/boids/mod.rs b/examples/src/boids/mod.rs index c527be96d..6335bed81 100644 --- a/examples/src/boids/mod.rs +++ b/examples/src/boids/mod.rs @@ -2,7 +2,7 @@ // adapted from https://github.com/austinEng/webgpu-samples/blob/master/src/examples/computeBoids.ts use nanorand::{Rng, WyRand}; -use std::{borrow::Cow, mem::size_of}; +use std::mem::size_of; use wgpu::util::DeviceExt; // number of boid particles to simulate @@ -43,14 +43,8 @@ impl crate::framework::Example for Example { device: &wgpu::Device, _queue: &wgpu::Queue, ) -> Self { - let compute_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("compute.wgsl"))), - }); - let draw_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("draw.wgsl"))), - }); + let compute_shader = device.create_shader_module(wgpu::include_wgsl!("compute.wgsl")); + let draw_shader = device.create_shader_module(wgpu::include_wgsl!("draw.wgsl")); // buffer for simulation parameters uniform diff --git a/examples/src/conservative_raster/mod.rs b/examples/src/conservative_raster/mod.rs index 46fb8742a..681c45c83 100644 --- a/examples/src/conservative_raster/mod.rs +++ b/examples/src/conservative_raster/mod.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - const RENDER_TARGET_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb; struct Example { @@ -83,12 +81,8 @@ impl crate::framework::Example for Example { push_constant_ranges: &[], }); - let shader_triangle_and_lines = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!( - "triangle_and_lines.wgsl" - ))), - }); + let shader_triangle_and_lines = + device.create_shader_module(wgpu::include_wgsl!("triangle_and_lines.wgsl")); let pipeline_triangle_conservative = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -203,10 +197,7 @@ impl crate::framework::Example for Example { bind_group_layouts: &[&bind_group_layout], push_constant_ranges: &[], }); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("upscale.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("upscale.wgsl")); ( device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Upscale"), diff --git a/examples/src/cube/mod.rs b/examples/src/cube/mod.rs index 78dc06e06..bc7504411 100644 --- a/examples/src/cube/mod.rs +++ b/examples/src/cube/mod.rs @@ -1,5 +1,5 @@ use bytemuck::{Pod, Zeroable}; -use std::{borrow::Cow, f32::consts, mem::size_of}; +use std::{f32::consts, mem::size_of}; use wgpu::util::DeviceExt; #[repr(C)] @@ -216,10 +216,7 @@ impl crate::framework::Example for Example { label: None, }); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let vertex_buffers = [wgpu::VertexBufferLayout { array_stride: vertex_size as wgpu::BufferAddress, diff --git a/examples/src/hello_compute/mod.rs b/examples/src/hello_compute/mod.rs index e53f49fa4..35cfcfbab 100644 --- a/examples/src/hello_compute/mod.rs +++ b/examples/src/hello_compute/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, mem::size_of_val, str::FromStr}; +use std::{mem::size_of_val, str::FromStr}; use wgpu::util::DeviceExt; // Indicates a u32 overflow in an intermediate Collatz value @@ -66,10 +66,7 @@ async fn execute_gpu_inner( numbers: &[u32], ) -> Option> { // Loads the shader from WGSL - let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let cs_module = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); // Gets the size in bytes of the buffer. let size = size_of_val(numbers) as wgpu::BufferAddress; diff --git a/examples/src/hello_synchronization/mod.rs b/examples/src/hello_synchronization/mod.rs index fad5d7a9d..b186c2bf0 100644 --- a/examples/src/hello_synchronization/mod.rs +++ b/examples/src/hello_synchronization/mod.rs @@ -56,10 +56,7 @@ async fn execute( let mut local_patient_workgroup_results = vec![0u32; result_vec_size]; let mut local_hasty_workgroup_results = local_patient_workgroup_results.clone(); - let shaders_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!("shaders.wgsl"))), - }); + let shaders_module = device.create_shader_module(wgpu::include_wgsl!("shaders.wgsl")); let storage_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: None, diff --git a/examples/src/hello_workgroups/mod.rs b/examples/src/hello_workgroups/mod.rs index 7a653cf3e..38c88a692 100644 --- a/examples/src/hello_workgroups/mod.rs +++ b/examples/src/hello_workgroups/mod.rs @@ -41,10 +41,7 @@ async fn run() { .await .unwrap(); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let storage_buffer_a = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, diff --git a/examples/src/mipmap/mod.rs b/examples/src/mipmap/mod.rs index 179970ad7..edfcf2223 100644 --- a/examples/src/mipmap/mod.rs +++ b/examples/src/mipmap/mod.rs @@ -1,5 +1,5 @@ use bytemuck::{Pod, Zeroable}; -use std::{borrow::Cow, f32::consts, mem::size_of}; +use std::{f32::consts, mem::size_of}; use wgpu::util::DeviceExt; const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb; @@ -81,10 +81,7 @@ impl Example { query_sets: &Option, mip_count: u32, ) { - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("blit.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("blit.wgsl")); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("blit"), @@ -281,10 +278,7 @@ impl crate::framework::Example for Example { }); // Create the render pipeline - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("draw.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("draw.wgsl")); let draw_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("draw"), diff --git a/examples/src/msaa_line/mod.rs b/examples/src/msaa_line/mod.rs index 431fe02ba..1f49b90f4 100644 --- a/examples/src/msaa_line/mod.rs +++ b/examples/src/msaa_line/mod.rs @@ -7,7 +7,7 @@ //! * Set the primitive_topology to PrimitiveTopology::LineList. //! * Vertices and Indices describe the two points that make up a line. -use std::{borrow::Cow, iter, mem::size_of}; +use std::{iter, mem::size_of}; use bytemuck::{Pod, Zeroable}; use wgpu::util::DeviceExt; @@ -156,10 +156,7 @@ impl crate::framework::Example for Example { let sample_count = max_sample_count; - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, diff --git a/examples/src/render_to_texture/mod.rs b/examples/src/render_to_texture/mod.rs index 1d6f488d5..529525498 100644 --- a/examples/src/render_to_texture/mod.rs +++ b/examples/src/render_to_texture/mod.rs @@ -28,10 +28,7 @@ async fn run(_path: Option) { .await .unwrap(); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let render_target = device.create_texture(&wgpu::TextureDescriptor { label: None, diff --git a/examples/src/repeated_compute/mod.rs b/examples/src/repeated_compute/mod.rs index 83dcd4099..7c9ff766d 100644 --- a/examples/src/repeated_compute/mod.rs +++ b/examples/src/repeated_compute/mod.rs @@ -180,12 +180,7 @@ impl WgpuContext { .unwrap(); // Our shader, kindly compiled with Naga. - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!( - "shader.wgsl" - ))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); // This is where the GPU will read from and write to. let storage_buffer = device.create_buffer(&wgpu::BufferDescriptor { diff --git a/examples/src/shadow/mod.rs b/examples/src/shadow/mod.rs index a7edcce7e..0960a49f2 100644 --- a/examples/src/shadow/mod.rs +++ b/examples/src/shadow/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, f32::consts, iter, mem::size_of, ops::Range, sync::Arc}; +use std::{f32::consts, iter, mem::size_of, ops::Range, sync::Arc}; use bytemuck::{Pod, Zeroable}; use wgpu::util::{align_to, DeviceExt}; @@ -447,10 +447,7 @@ impl crate::framework::Example for Example { attributes: &vertex_attr, }; - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let shadow_pass = { let uniform_size = size_of::() as wgpu::BufferAddress; diff --git a/examples/src/skybox/mod.rs b/examples/src/skybox/mod.rs index 82e58ef6d..ae3164379 100644 --- a/examples/src/skybox/mod.rs +++ b/examples/src/skybox/mod.rs @@ -1,5 +1,5 @@ use bytemuck::{Pod, Zeroable}; -use std::{borrow::Cow, f32::consts, mem::size_of}; +use std::{f32::consts, mem::size_of}; use wgpu::{util::DeviceExt, AstcBlock, AstcChannel}; const IMAGE_SIZE: u32 = 256; @@ -168,10 +168,7 @@ impl crate::framework::Example for Example { }); // Create the render pipeline - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let camera = Camera { screen_size: (config.width, config.height), diff --git a/examples/src/srgb_blend/mod.rs b/examples/src/srgb_blend/mod.rs index 822d95d3c..198483d54 100644 --- a/examples/src/srgb_blend/mod.rs +++ b/examples/src/srgb_blend/mod.rs @@ -1,5 +1,5 @@ use bytemuck::{Pod, Zeroable}; -use std::{borrow::Cow, mem}; +use std::mem; use wgpu::util::DeviceExt; #[repr(C)] @@ -103,10 +103,7 @@ impl crate::framework::Example for Example { label: None, }); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let vertex_buffers = [wgpu::VertexBufferLayout { array_stride: vertex_size as wgpu::BufferAddress, diff --git a/examples/src/stencil_triangles/mod.rs b/examples/src/stencil_triangles/mod.rs index bb433af11..fcd37f813 100644 --- a/examples/src/stencil_triangles/mod.rs +++ b/examples/src/stencil_triangles/mod.rs @@ -1,5 +1,4 @@ use bytemuck::{Pod, Zeroable}; -use std::borrow::Cow; use std::mem::size_of; use wgpu::util::DeviceExt; @@ -53,10 +52,7 @@ impl crate::framework::Example for Example { push_constant_ranges: &[], }); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let vertex_buffers = [wgpu::VertexBufferLayout { array_stride: vertex_size as wgpu::BufferAddress, diff --git a/examples/src/storage_texture/mod.rs b/examples/src/storage_texture/mod.rs index a68758419..a394fe9e0 100644 --- a/examples/src/storage_texture/mod.rs +++ b/examples/src/storage_texture/mod.rs @@ -44,10 +44,7 @@ async fn run(_path: Option) { .await .unwrap(); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let storage_texture = device.create_texture(&wgpu::TextureDescriptor { label: None, diff --git a/examples/src/timestamp_queries/mod.rs b/examples/src/timestamp_queries/mod.rs index a253938a3..eb78630e2 100644 --- a/examples/src/timestamp_queries/mod.rs +++ b/examples/src/timestamp_queries/mod.rs @@ -239,10 +239,7 @@ fn submit_render_and_compute_pass_with_queries( device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); let mut queries = Queries::new(device, QueryResults::NUM_QUERIES); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); if device .features() diff --git a/examples/src/uniform_values/mod.rs b/examples/src/uniform_values/mod.rs index 1ef58de09..a5c1e14c5 100644 --- a/examples/src/uniform_values/mod.rs +++ b/examples/src/uniform_values/mod.rs @@ -122,12 +122,7 @@ impl WgpuContext { .await .unwrap(); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!( - "shader.wgsl" - ))), - }); + let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); // (2) let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor { diff --git a/examples/src/water/mod.rs b/examples/src/water/mod.rs index 505f5707e..52acbef37 100644 --- a/examples/src/water/mod.rs +++ b/examples/src/water/mod.rs @@ -3,7 +3,7 @@ mod point_gen; use bytemuck::{Pod, Zeroable}; use glam::Vec3; use nanorand::{Rng, WyRand}; -use std::{borrow::Cow, f32::consts, iter, mem::size_of}; +use std::{f32::consts, iter, mem::size_of}; use wgpu::util::DeviceExt; /// @@ -493,14 +493,8 @@ impl crate::framework::Example for Example { }); // Upload/compile them to GPU code. - let terrain_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("terrain"), - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("terrain.wgsl"))), - }); - let water_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("water"), - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("water.wgsl"))), - }); + let terrain_module = device.create_shader_module(wgpu::include_wgsl!("terrain.wgsl")); + let water_module = device.create_shader_module(wgpu::include_wgsl!("water.wgsl")); // Create the render pipelines. These describe how the data will flow through the GPU, and what // constraints and modifiers it will have. diff --git a/tests/tests/occlusion_query/mod.rs b/tests/tests/occlusion_query/mod.rs index 20c7fff82..2cedab029 100644 --- a/tests/tests/occlusion_query/mod.rs +++ b/tests/tests/occlusion_query/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, mem::size_of}; +use std::mem::size_of; use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; #[gpu_test] @@ -25,10 +25,7 @@ static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new() // Setup pipeline using a simple shader with hardcoded vertices let shader = ctx .device - .create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Shader module"), - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + .create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let pipeline = ctx .device .create_render_pipeline(&wgpu::RenderPipelineDescriptor { diff --git a/tests/tests/partially_bounded_arrays/mod.rs b/tests/tests/partially_bounded_arrays/mod.rs index 4e6d6fc09..eefec6c3f 100644 --- a/tests/tests/partially_bounded_arrays/mod.rs +++ b/tests/tests/partially_bounded_arrays/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, num::NonZeroU32}; +use std::num::NonZeroU32; use wgpu_test::{gpu_test, image::ReadbackBuffers, GpuTestConfiguration, TestParameters}; @@ -53,10 +53,7 @@ static PARTIALLY_BOUNDED_ARRAY: GpuTestConfiguration = GpuTestConfiguration::new }], }); - let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let cs_module = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("main"), diff --git a/tests/tests/subgroup_operations/mod.rs b/tests/tests/subgroup_operations/mod.rs index f874a6bac..973d82422 100644 --- a/tests/tests/subgroup_operations/mod.rs +++ b/tests/tests/subgroup_operations/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, mem::size_of, num::NonZeroU64}; +use std::{mem::size_of, num::NonZeroU64}; use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; @@ -56,10 +56,7 @@ static SUBGROUP_OPERATIONS: GpuTestConfiguration = GpuTestConfiguration::new() }], }); - let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); + let cs_module = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("main"), From 866be693d65e62d4815d207ca6970ecaea2085b5 Mon Sep 17 00:00:00 2001 From: Samson <16504129+sagudev@users.noreply.github.com> Date: Fri, 27 Sep 2024 23:52:53 +0200 Subject: [PATCH 34/59] [naga] Handle phony statements properly by treating them as named expressions (#6328) * [naga wgsl-in] phony assignments add named expressions Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * [naga wgsl-out] write out _naga_phony as phony Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * Add test Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * use statement span Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * every phony has same name Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- naga/src/front/wgsl/lower/mod.rs | 6 ++- naga/src/front/wgsl/parse/ast.rs | 2 +- naga/src/front/wgsl/parse/mod.rs | 2 +- naga/tests/in/phony_assignment.ron | 2 + naga/tests/in/phony_assignment.wgsl | 18 +++++++ .../glsl/phony_assignment.main.Compute.glsl | 23 +++++++++ naga/tests/out/hlsl/phony_assignment.hlsl | 16 +++++++ naga/tests/out/hlsl/phony_assignment.ron | 12 +++++ naga/tests/out/msl/phony_assignment.msl | 24 ++++++++++ naga/tests/out/spv/phony_assignment.spvasm | 48 +++++++++++++++++++ naga/tests/out/wgsl/phony_assignment.wgsl | 15 ++++++ naga/tests/snapshots.rs | 4 ++ 12 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 naga/tests/in/phony_assignment.ron create mode 100644 naga/tests/in/phony_assignment.wgsl create mode 100644 naga/tests/out/glsl/phony_assignment.main.Compute.glsl create mode 100644 naga/tests/out/hlsl/phony_assignment.hlsl create mode 100644 naga/tests/out/hlsl/phony_assignment.ron create mode 100644 naga/tests/out/msl/phony_assignment.msl create mode 100644 naga/tests/out/spv/phony_assignment.spvasm create mode 100644 naga/tests/out/wgsl/phony_assignment.wgsl diff --git a/naga/src/front/wgsl/lower/mod.rs b/naga/src/front/wgsl/lower/mod.rs index 7586f98dc..78e81350b 100644 --- a/naga/src/front/wgsl/lower/mod.rs +++ b/naga/src/front/wgsl/lower/mod.rs @@ -1778,12 +1778,14 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { return Ok(()); } - ast::StatementKind::Ignore(expr) => { + ast::StatementKind::Phony(expr) => { let mut emitter = Emitter::default(); emitter.start(&ctx.function.expressions); - let _ = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?; + let value = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?; block.extend(emitter.finish(&ctx.function.expressions)); + ctx.named_expressions + .insert(value, ("phony".to_string(), stmt.span)); return Ok(()); } }; diff --git a/naga/src/front/wgsl/parse/ast.rs b/naga/src/front/wgsl/parse/ast.rs index c4a798411..4307ca3d9 100644 --- a/naga/src/front/wgsl/parse/ast.rs +++ b/naga/src/front/wgsl/parse/ast.rs @@ -284,7 +284,7 @@ pub enum StatementKind<'a> { }, Increment(Handle>), Decrement(Handle>), - Ignore(Handle>), + Phony(Handle>), ConstAssert(Handle>), } diff --git a/naga/src/front/wgsl/parse/mod.rs b/naga/src/front/wgsl/parse/mod.rs index 0157aa3a7..3b1d60620 100644 --- a/naga/src/front/wgsl/parse/mod.rs +++ b/naga/src/front/wgsl/parse/mod.rs @@ -1696,7 +1696,7 @@ impl Parser { let expr = self.general_expression(lexer, ctx)?; lexer.expect(Token::Separator(';'))?; - ast::StatementKind::Ignore(expr) + ast::StatementKind::Phony(expr) } "let" => { let _ = lexer.next(); diff --git a/naga/tests/in/phony_assignment.ron b/naga/tests/in/phony_assignment.ron new file mode 100644 index 000000000..39679b87b --- /dev/null +++ b/naga/tests/in/phony_assignment.ron @@ -0,0 +1,2 @@ +( +) \ No newline at end of file diff --git a/naga/tests/in/phony_assignment.wgsl b/naga/tests/in/phony_assignment.wgsl new file mode 100644 index 000000000..716a387b0 --- /dev/null +++ b/naga/tests/in/phony_assignment.wgsl @@ -0,0 +1,18 @@ +@group(0) @binding(0) var binding: f32; + +fn five() -> i32 { + return 5; +} + +@compute @workgroup_size(1) fn main( + @builtin(global_invocation_id) id: vec3 +) { + _ = binding; + _ = binding; + let a = 5; + _ = a; + _ = five(); + let b = five(); + // check for name collision + let phony = binding; +} \ No newline at end of file diff --git a/naga/tests/out/glsl/phony_assignment.main.Compute.glsl b/naga/tests/out/glsl/phony_assignment.main.Compute.glsl new file mode 100644 index 000000000..6118e9b4a --- /dev/null +++ b/naga/tests/out/glsl/phony_assignment.main.Compute.glsl @@ -0,0 +1,23 @@ +#version 310 es + +precision highp float; +precision highp int; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +uniform type_block_0Compute { float _group_0_binding_0_cs; }; + + +int five() { + return 5; +} + +void main() { + uvec3 id = gl_GlobalInvocationID; + float phony = _group_0_binding_0_cs; + float phony_1 = _group_0_binding_0_cs; + int _e6 = five(); + int _e7 = five(); + float phony_2 = _group_0_binding_0_cs; +} + diff --git a/naga/tests/out/hlsl/phony_assignment.hlsl b/naga/tests/out/hlsl/phony_assignment.hlsl new file mode 100644 index 000000000..f448290e0 --- /dev/null +++ b/naga/tests/out/hlsl/phony_assignment.hlsl @@ -0,0 +1,16 @@ +cbuffer binding : register(b0) { float binding; } + +int five() +{ + return 5; +} + +[numthreads(1, 1, 1)] +void main(uint3 id : SV_DispatchThreadID) +{ + float phony = binding; + float phony_1 = binding; + const int _e6 = five(); + const int _e7 = five(); + float phony_2 = binding; +} diff --git a/naga/tests/out/hlsl/phony_assignment.ron b/naga/tests/out/hlsl/phony_assignment.ron new file mode 100644 index 000000000..a07b03300 --- /dev/null +++ b/naga/tests/out/hlsl/phony_assignment.ron @@ -0,0 +1,12 @@ +( + vertex:[ + ], + fragment:[ + ], + compute:[ + ( + entry_point:"main", + target_profile:"cs_5_1", + ), + ], +) diff --git a/naga/tests/out/msl/phony_assignment.msl b/naga/tests/out/msl/phony_assignment.msl new file mode 100644 index 000000000..daad57190 --- /dev/null +++ b/naga/tests/out/msl/phony_assignment.msl @@ -0,0 +1,24 @@ +// language: metal1.0 +#include +#include + +using metal::uint; + + +int five( +) { + return 5; +} + +struct main_Input { +}; +kernel void main_( + metal::uint3 id [[thread_position_in_grid]] +, constant float& binding [[user(fake0)]] +) { + float phony = binding; + float phony_1 = binding; + int _e6 = five(); + int _e7 = five(); + float phony_2 = binding; +} diff --git a/naga/tests/out/spv/phony_assignment.spvasm b/naga/tests/out/spv/phony_assignment.spvasm new file mode 100644 index 000000000..842618390 --- /dev/null +++ b/naga/tests/out/spv/phony_assignment.spvasm @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 30 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %19 "main" %16 +OpExecutionMode %19 LocalSize 1 1 1 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +OpDecorate %8 Block +OpMemberDecorate %8 0 Offset 0 +OpDecorate %16 BuiltIn GlobalInvocationId +%2 = OpTypeVoid +%3 = OpTypeFloat 32 +%4 = OpTypeInt 32 1 +%6 = OpTypeInt 32 0 +%5 = OpTypeVector %6 3 +%8 = OpTypeStruct %3 +%9 = OpTypePointer Uniform %8 +%7 = OpVariable %9 Uniform +%12 = OpTypeFunction %4 +%13 = OpConstant %4 5 +%17 = OpTypePointer Input %5 +%16 = OpVariable %17 Input +%20 = OpTypeFunction %2 +%21 = OpTypePointer Uniform %3 +%22 = OpConstant %6 0 +%11 = OpFunction %4 None %12 +%10 = OpLabel +OpBranch %14 +%14 = OpLabel +OpReturnValue %13 +OpFunctionEnd +%19 = OpFunction %2 None %20 +%15 = OpLabel +%18 = OpLoad %5 %16 +%23 = OpAccessChain %21 %7 %22 +OpBranch %24 +%24 = OpLabel +%25 = OpLoad %3 %23 +%26 = OpLoad %3 %23 +%27 = OpFunctionCall %4 %11 +%28 = OpFunctionCall %4 %11 +%29 = OpLoad %3 %23 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/out/wgsl/phony_assignment.wgsl b/naga/tests/out/wgsl/phony_assignment.wgsl new file mode 100644 index 000000000..60987a15c --- /dev/null +++ b/naga/tests/out/wgsl/phony_assignment.wgsl @@ -0,0 +1,15 @@ +@group(0) @binding(0) +var binding: f32; + +fn five() -> i32 { + return 5i; +} + +@compute @workgroup_size(1, 1, 1) +fn main(@builtin(global_invocation_id) id: vec3) { + let phony = binding; + let phony_1 = binding; + let _e6 = five(); + let _e7 = five(); + let phony_2 = binding; +} diff --git a/naga/tests/snapshots.rs b/naga/tests/snapshots.rs index 936203986..596e4cea1 100644 --- a/naga/tests/snapshots.rs +++ b/naga/tests/snapshots.rs @@ -928,6 +928,10 @@ fn convert_wgsl() { "cross", Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL, ), + ( + "phony_assignment", + Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL, + ), ]; for &(name, targets) in inputs.iter() { From 98c4d6f42e1920cd11c3f7d1016d976a6bbb0d22 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 27 Sep 2024 17:00:21 -0700 Subject: [PATCH 35/59] [naga] Permit only structs as binding array elements. (#6333) Require `T` to be a struct in `binding_array`; do not permit arrays. In #5428, the validator was changed to accept binding array types that the SPIR-V backend couldn't properly emit. Specifically, the validator was changed to accept `binding_array>`, but the SPIR-V backend wasn't changed to wrap the binding array elements in a SPIR-V struct type, as Vulkan requires. So the type would be accepted by the validator, and then rejected by the backend. --- naga/src/valid/type.rs | 1 - naga/tests/validation.rs | 110 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/naga/src/valid/type.rs b/naga/src/valid/type.rs index 0c4466077..c0c25dab7 100644 --- a/naga/src/valid/type.rs +++ b/naga/src/valid/type.rs @@ -677,7 +677,6 @@ impl super::Validator { // Currently Naga only supports binding arrays of structs for non-handle types. match gctx.types[base].inner { crate::TypeInner::Struct { .. } => {} - crate::TypeInner::Array { .. } => {} _ => return Err(TypeError::BindingArrayBaseTypeNotStruct(base)), }; } diff --git a/naga/tests/validation.rs b/naga/tests/validation.rs index f20ae688b..b8a30c49b 100644 --- a/naga/tests/validation.rs +++ b/naga/tests/validation.rs @@ -496,3 +496,113 @@ fn main(input: VertexOutput) {{ } } } + +#[allow(dead_code)] +struct BindingArrayFixture { + module: naga::Module, + span: naga::Span, + ty_u32: naga::Handle, + ty_array: naga::Handle, + ty_struct: naga::Handle, + validator: naga::valid::Validator, +} + +impl BindingArrayFixture { + fn new() -> Self { + let mut module = naga::Module::default(); + let span = naga::Span::default(); + let ty_u32 = module.types.insert( + naga::Type { + name: Some("u32".into()), + inner: naga::TypeInner::Scalar(naga::Scalar::U32), + }, + span, + ); + let ty_array = module.types.insert( + naga::Type { + name: Some("array".into()), + inner: naga::TypeInner::Array { + base: ty_u32, + size: naga::ArraySize::Constant(std::num::NonZeroU32::new(10).unwrap()), + stride: 4, + }, + }, + span, + ); + let ty_struct = module.types.insert( + naga::Type { + name: Some("S".into()), + inner: naga::TypeInner::Struct { + members: vec![naga::StructMember { + name: Some("m".into()), + ty: ty_u32, + binding: None, + offset: 0, + }], + span: 4, + }, + }, + span, + ); + let validator = naga::valid::Validator::new(Default::default(), Default::default()); + BindingArrayFixture { + module, + span, + ty_u32, + ty_array, + ty_struct, + validator, + } + } +} + +#[test] +fn binding_arrays_hold_structs() { + let mut t = BindingArrayFixture::new(); + let _binding_array = t.module.types.insert( + naga::Type { + name: Some("binding_array_of_struct".into()), + inner: naga::TypeInner::BindingArray { + base: t.ty_struct, + size: naga::ArraySize::Dynamic, + }, + }, + t.span, + ); + + assert!(t.validator.validate(&t.module).is_ok()); +} + +#[test] +fn binding_arrays_cannot_hold_arrays() { + let mut t = BindingArrayFixture::new(); + let _binding_array = t.module.types.insert( + naga::Type { + name: Some("binding_array_of_array".into()), + inner: naga::TypeInner::BindingArray { + base: t.ty_array, + size: naga::ArraySize::Dynamic, + }, + }, + t.span, + ); + + assert!(t.validator.validate(&t.module).is_err()); +} + +#[test] +fn binding_arrays_cannot_hold_scalars() { + let mut t = BindingArrayFixture::new(); + let _binding_array = t.module.types.insert( + naga::Type { + name: Some("binding_array_of_scalar".into()), + inner: naga::TypeInner::BindingArray { + base: t.ty_u32, + size: naga::ArraySize::Dynamic, + }, + }, + t.span, + ); + + assert!(t.validator.validate(&t.module).is_err()); +} From 04032905efa67865bd19c95610b755d70127e1c3 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Thu, 26 Sep 2024 21:05:24 -0700 Subject: [PATCH 36/59] [naga spv-out] Replace `match` with equivalent `!=`. --- naga/src/back/spv/block.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/naga/src/back/spv/block.rs b/naga/src/back/spv/block.rs index 9fb948586..f0c3bfa84 100644 --- a/naga/src/back/spv/block.rs +++ b/naga/src/back/spv/block.rs @@ -200,10 +200,7 @@ impl<'w> BlockContext<'w> { fn is_intermediate(&self, expr_handle: Handle) -> bool { match self.ir_function.expressions[expr_handle] { crate::Expression::GlobalVariable(handle) => { - match self.ir_module.global_variables[handle].space { - crate::AddressSpace::Handle => false, - _ => true, - } + self.ir_module.global_variables[handle].space != crate::AddressSpace::Handle } crate::Expression::LocalVariable(_) => true, crate::Expression::FunctionArgument(index) => { From 259592b926ef13d20a1c64b65df392f8cd5c8176 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Thu, 26 Sep 2024 21:06:03 -0700 Subject: [PATCH 37/59] [naga spv-out] Update Vulkan spec section number, and provide link. --- naga/src/back/spv/helpers.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/naga/src/back/spv/helpers.rs b/naga/src/back/spv/helpers.rs index 1fb447e38..15c241d44 100644 --- a/naga/src/back/spv/helpers.rs +++ b/naga/src/back/spv/helpers.rs @@ -85,7 +85,7 @@ impl crate::AddressSpace { /// Return true if the global requires a type decorated with `Block`. /// -/// Vulkan spec v1.3 §15.6.2, "Descriptor Set Interface", says: +/// In the Vulkan spec 1.3.296, the section [Descriptor Set Interface][dsi] says: /// /// > Variables identified with the `Uniform` storage class are used to /// > access transparent buffer backed resources. Such variables must @@ -98,6 +98,8 @@ impl crate::AddressSpace { /// > - laid out explicitly using the `Offset`, `ArrayStride`, and /// > `MatrixStride` decorations as specified in §15.6.4, "Offset /// > and Stride Assignment." +/// +/// [dsi]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#interfaces-resources-descset // See `back::spv::GlobalVariable::access_id` for details. pub fn global_needs_wrapper(ir_module: &crate::Module, var: &crate::GlobalVariable) -> bool { match var.space { From 2021e7f29f0204a0b6b22bc59844af1e93984465 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Thu, 26 Sep 2024 21:10:13 -0700 Subject: [PATCH 38/59] [naga spv-out] Document and refactor `write_runtime_array_length`. Document and refactor `naga::back::spv::BlockContext::write_runtime_array_length`. Don't try to handle finding the length of a particular element of a `binding_array>`. The SPIR-V backend doesn't wrap that type correctly anyway; #6333 changes the validator to forbid such types. Instead, assume that the elements of a `binding_array` are always structs whose final members may be a runtime-sized array. Pull out consistency checks after the analysis of the array expression, so that we always carry out all the checks regardless of what path we took to produce the information. --- naga/src/back/spv/index.rs | 174 +++++++++++++++++++++++++------------ 1 file changed, 119 insertions(+), 55 deletions(-) diff --git a/naga/src/back/spv/index.rs b/naga/src/back/spv/index.rs index 84b81b63d..0295d895b 100644 --- a/naga/src/back/spv/index.rs +++ b/naga/src/back/spv/index.rs @@ -38,98 +38,162 @@ impl<'w> BlockContext<'w> { /// /// Given `array`, an expression referring a runtime-sized array, return the /// instruction id for the array's length. + /// + /// Runtime-sized arrays may only appear in the values of global + /// variables, which must have one of the following Naga types: + /// + /// 1. A runtime-sized array. + /// 2. A struct whose last member is a runtime-sized array. + /// 3. A binding array of 2. + /// + /// Thus, the expression `array` has the form of: + /// + /// - An optional [`AccessIndex`], for case 2, applied to... + /// - An optional [`Access`] or [`AccessIndex`], for case 3, applied to... + /// - A [`GlobalVariable`]. + /// + /// The SPIR-V generated takes into account wrapped globals; see + /// [`global_needs_wrapper`]. + /// + /// [`GlobalVariable`]: crate::Expression::GlobalVariable + /// [`AccessIndex`]: crate::Expression::AccessIndex + /// [`Access`]: crate::Expression::Access + /// [`base`]: crate::Expression::Access::base pub(super) fn write_runtime_array_length( &mut self, array: Handle, block: &mut Block, ) -> Result { - // Naga IR permits runtime-sized arrays as global variables, or as the - // final member of a struct that is a global variable, or one of these - // inside a buffer that is itself an element in a buffer bindings array. - // SPIR-V requires that runtime-sized arrays are wrapped in structs. - // See `helpers::global_needs_wrapper` and its uses. - let (opt_array_index_id, global_handle, opt_last_member_index) = match self - .ir_function - .expressions[array] - { + // The index into the binding array, if any. + let binding_array_index_id: Option; + + // The handle to the Naga IR global we're referring to. + let global_handle: Handle; + + // At the Naga type level, if the runtime-sized array is the final member of a + // struct, this is that member's index. + // + // This does not cover wrappers: if this backend wrapped the Naga global's + // type in a synthetic SPIR-V struct (see `global_needs_wrapper`), this is + // `None`. + let opt_last_member_index: Option; + + // Inspect `array` and decide whether we have a binding array and/or an + // enclosing struct. + match self.ir_function.expressions[array] { crate::Expression::AccessIndex { base, index } => { match self.ir_function.expressions[base] { - // The global variable is an array of buffer bindings of structs, - // we are accessing one of them with a static index, - // and the last member of it. crate::Expression::AccessIndex { base: base_outer, index: index_outer, } => match self.ir_function.expressions[base_outer] { + // An `AccessIndex` of an `AccessIndex` must be a + // binding array holding structs whose last members are + // runtime-sized arrays. crate::Expression::GlobalVariable(handle) => { let index_id = self.get_index_constant(index_outer); - (Some(index_id), handle, Some(index)) + binding_array_index_id = Some(index_id); + global_handle = handle; + opt_last_member_index = Some(index); + } + _ => { + return Err(Error::Validation( + "array length expression: AccessIndex(AccessIndex(Global))", + )) } - _ => return Err(Error::Validation("array length expression case-1a")), }, - // The global variable is an array of buffer bindings of structs, - // we are accessing one of them with a dynamic index, - // and the last member of it. crate::Expression::Access { base: base_outer, index: index_outer, } => match self.ir_function.expressions[base_outer] { + // Similarly, an `AccessIndex` of an `Access` must be a + // binding array holding structs whose last members are + // runtime-sized arrays. crate::Expression::GlobalVariable(handle) => { let index_id = self.cached[index_outer]; - (Some(index_id), handle, Some(index)) + binding_array_index_id = Some(index_id); + global_handle = handle; + opt_last_member_index = Some(index); + } + _ => { + return Err(Error::Validation( + "array length expression: AccessIndex(Access(Global))", + )) } - _ => return Err(Error::Validation("array length expression case-1b")), }, - // The global variable is a buffer, and we are accessing the last member. crate::Expression::GlobalVariable(handle) => { - let global = &self.ir_module.global_variables[handle]; - match self.ir_module.types[global.ty].inner { - // The global variable is an array of buffer bindings of run-time arrays. - crate::TypeInner::BindingArray { .. } => (Some(index), handle, None), - // The global variable is a struct, and we are accessing the last member - _ => (None, handle, Some(index)), - } + // An outer `AccessIndex` applied directly to a + // `GlobalVariable`. Since binding arrays can only contain + // structs, this must be referring to the last member of a + // struct that is a runtime-sized array. + binding_array_index_id = None; + global_handle = handle; + opt_last_member_index = Some(index); + } + _ => { + return Err(Error::Validation( + "array length expression: AccessIndex()", + )) } - _ => return Err(Error::Validation("array length expression case-1c")), } } - // The global variable is an array of buffer bindings of arrays. - crate::Expression::Access { base, index } => match self.ir_function.expressions[base] { - crate::Expression::GlobalVariable(handle) => { - let index_id = self.cached[index]; - let global = &self.ir_module.global_variables[handle]; - match self.ir_module.types[global.ty].inner { - crate::TypeInner::BindingArray { .. } => (Some(index_id), handle, None), - _ => return Err(Error::Validation("array length expression case-2a")), - } - } - _ => return Err(Error::Validation("array length expression case-2b")), - }, - // The global variable is a run-time array. crate::Expression::GlobalVariable(handle) => { - let global = &self.ir_module.global_variables[handle]; - if !global_needs_wrapper(self.ir_module, global) { - return Err(Error::Validation("array length expression case-3")); - } - (None, handle, None) + // A direct reference to a global variable. This must hold the + // runtime-sized array directly. + binding_array_index_id = None; + global_handle = handle; + opt_last_member_index = None; } _ => return Err(Error::Validation("array length expression case-4")), }; + // The verifier should have checked this, but make sure the inspection above + // agrees with the type about whether a binding array is involved. + // + // Eventually we do want to support `binding_array>`. This check + // ensures that whoever relaxes the validator will get an error message from + // us, not just bogus SPIR-V. + let global = &self.ir_module.global_variables[global_handle]; + match ( + &self.ir_module.types[global.ty].inner, + binding_array_index_id, + ) { + (&crate::TypeInner::BindingArray { .. }, Some(_)) => {} + (_, None) => {} + _ => { + return Err(Error::Validation( + "array length expression: bad binding array inference", + )) + } + } + + // SPIR-V allows runtime-sized arrays to appear only as the last member of a + // struct. Determine this member's index. let gvar = self.writer.global_variables[global_handle].clone(); let global = &self.ir_module.global_variables[global_handle]; - let (last_member_index, gvar_id) = match opt_last_member_index { - Some(index) => (index, gvar.access_id), - None => { - if !global_needs_wrapper(self.ir_module, global) { - return Err(Error::Validation( - "pointer to a global that is not a wrapped array", - )); - } + let needs_wrapper = global_needs_wrapper(self.ir_module, global); + let (last_member_index, gvar_id) = match (opt_last_member_index, needs_wrapper) { + (Some(index), false) => { + // At the Naga type level, the runtime-sized array appears as the + // final member of a struct, whose index is `index`. We didn't need to + // wrap this, since the Naga type meets SPIR-V's requirements already. + (index, gvar.access_id) + } + (None, true) => { + // At the Naga type level, the runtime-sized array does not appear + // within a struct. We wrapped this in an OpTypeStruct with nothing + // else in it, so the index is zero. OpArrayLength wants the pointer + // to the wrapper struct, so use `gvar.var_id`. (0, gvar.var_id) } + _ => { + return Err(Error::Validation( + "array length expression: bad SPIR-V wrapper struct inference", + )); + } }; - let structure_id = match opt_array_index_id { + + let structure_id = match binding_array_index_id { // We are indexing inside a binding array, generate the access op. Some(index_id) => { let element_type_id = match self.ir_module.types[global.ty].inner { From ec89e06a9245236a06b47742f36f58306c4ecc4b Mon Sep 17 00:00:00 2001 From: Samson <16504129+sagudev@users.noreply.github.com> Date: Sun, 29 Sep 2024 21:13:49 +0200 Subject: [PATCH 39/59] Update CHANGELOG with @sagudev contributions to naga (#6339) --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 239b1f7d9..9d564bb08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,8 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Support constant evaluation for `firstLeadingBit` and `firstTrailingBit` numeric built-ins in WGSL. Front-ends that translate to these built-ins also benefit from constant evaluation. By @ErichDonGubler in [#5101](https://github.com/gfx-rs/wgpu/pull/5101). - Add `first` and `either` sampling types for `@interpolate(flat, …)` in WGSL. By @ErichDonGubler in [#6181](https://github.com/gfx-rs/wgpu/pull/6181). - Support for more atomic ops in the SPIR-V frontend. By @schell in [#5824](https://github.com/gfx-rs/wgpu/pull/5824). +- Support local `const` declarations in WGSL. By @sagudev in [#6156](https://github.com/gfx-rs/wgpu/pull/6156). +- Implemented `const_assert` in WGSL. By @sagudev in [#6198](https://github.com/gfx-rs/wgpu/pull/6198). #### General @@ -98,8 +100,13 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). #### Naga - Accept only `vec3` (not `vecN`) for the `cross` built-in. By @ErichDonGubler in [#6171](https://github.com/gfx-rs/wgpu/pull/6171). -- Configure `SourceLanguage` when enabling debug info in SPV-out. By @kvark in [#6256](https://github.com/gfx-rs/wgpu/pull/6256) +- Configure `SourceLanguage` when enabling debug info in SPV-out. By @kvark in [#6256](https://github.com/gfx-rs/wgpu/pull/6256). - Per-polygon and flat inputs should not be considered subgroup uniform. By @magcius in [#6276](https://github.com/gfx-rs/wgpu/pull/6276). +- Validate all swizzle components are either color (rgba) or dimension (xyzw) in WGSL. By @sagudev in [#6187](https://github.com/gfx-rs/wgpu/pull/6187). +- Fix detection of shl overflows to detect arithmetic overflows. By @sagudev in [#6186](https://github.com/gfx-rs/wgpu/pull/6186). +- Fix type parameters to vec/mat type constructors to also support aliases. By @sagudev in [#6189](https://github.com/gfx-rs/wgpu/pull/6189). +- Accept global `var`s without explicit type. By @sagudev in [#6199](https://github.com/gfx-rs/wgpu/pull/6199). +- Fix handling of phony statements, so they are actually emitted. By @sagudev in [#6328](https://github.com/gfx-rs/wgpu/pull/6328). #### General From 65cb4e752fc26452a47d67bb7efec7323b43cad1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:28:08 +0200 Subject: [PATCH 40/59] build(deps): bump the patch-updates group with 16 updates (#6343) Bumps the patch-updates group with 16 updates: | Package | From | To | | --- | --- | --- | | [libc](https://github.com/rust-lang/libc) | `0.2.158` | `0.2.159` | | [once_cell](https://github.com/matklad/once_cell) | `1.19.0` | `1.20.1` | | [png](https://github.com/image-rs/image-png) | `0.17.13` | `0.17.14` | | [tracy-client](https://github.com/nagisa/rust_tracy_client) | `0.17.3` | `0.17.4` | | [syn](https://github.com/dtolnay/syn) | `2.0.77` | `2.0.79` | | [async-trait](https://github.com/dtolnay/async-trait) | `0.1.82` | `0.1.83` | | [autocfg](https://github.com/cuviper/autocfg) | `1.3.0` | `1.4.0` | | [cc](https://github.com/rust-lang/cc-rs) | `1.1.21` | `1.1.22` | | [const_panic](https://github.com/rodrimati1992/const_panic) | `0.2.9` | `0.2.10` | | [fdeflate](https://github.com/image-rs/fdeflate) | `0.3.4` | `0.3.5` | | [flate2](https://github.com/rust-lang/flate2-rs) | `1.0.33` | `1.0.34` | | [pkg-config](https://github.com/rust-lang/pkg-config-rs) | `0.3.30` | `0.3.31` | | [regex](https://github.com/rust-lang/regex) | `1.10.6` | `1.11.0` | | [toml_edit](https://github.com/toml-rs/toml) | `0.22.21` | `0.22.22` | | [tracy-client-sys](https://github.com/nagisa/rust_tracy_client) | `0.24.0` | `0.24.1` | | [winnow](https://github.com/winnow-rs/winnow) | `0.6.18` | `0.6.20` | Updates `libc` from 0.2.158 to 0.2.159 - [Release notes](https://github.com/rust-lang/libc/releases) - [Changelog](https://github.com/rust-lang/libc/blob/0.2.159/CHANGELOG.md) - [Commits](https://github.com/rust-lang/libc/compare/0.2.158...0.2.159) Updates `once_cell` from 1.19.0 to 1.20.1 - [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md) - [Commits](https://github.com/matklad/once_cell/compare/v1.19.0...v1.20.1) Updates `png` from 0.17.13 to 0.17.14 - [Changelog](https://github.com/image-rs/image-png/blob/master/CHANGES.md) - [Commits](https://github.com/image-rs/image-png/compare/v0.17.13...v0.17.14) Updates `tracy-client` from 0.17.3 to 0.17.4 - [Commits](https://github.com/nagisa/rust_tracy_client/compare/tracy-client-v0.17.3...tracy-client-v0.17.4) Updates `syn` from 2.0.77 to 2.0.79 - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/2.0.77...2.0.79) Updates `async-trait` from 0.1.82 to 0.1.83 - [Release notes](https://github.com/dtolnay/async-trait/releases) - [Commits](https://github.com/dtolnay/async-trait/compare/0.1.82...0.1.83) Updates `autocfg` from 1.3.0 to 1.4.0 - [Commits](https://github.com/cuviper/autocfg/compare/1.3.0...1.4.0) Updates `cc` from 1.1.21 to 1.1.22 - [Release notes](https://github.com/rust-lang/cc-rs/releases) - [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.21...cc-v1.1.22) Updates `const_panic` from 0.2.9 to 0.2.10 - [Release notes](https://github.com/rodrimati1992/const_panic/releases) - [Changelog](https://github.com/rodrimati1992/const_panic/blob/main/Changelog.md) - [Commits](https://github.com/rodrimati1992/const_panic/commits/0.2.10) Updates `fdeflate` from 0.3.4 to 0.3.5 - [Changelog](https://github.com/image-rs/fdeflate/blob/main/CHANGES.md) - [Commits](https://github.com/image-rs/fdeflate/commits) Updates `flate2` from 1.0.33 to 1.0.34 - [Release notes](https://github.com/rust-lang/flate2-rs/releases) - [Changelog](https://github.com/rust-lang/flate2-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.33...1.0.34) Updates `pkg-config` from 0.3.30 to 0.3.31 - [Changelog](https://github.com/rust-lang/pkg-config-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/pkg-config-rs/compare/0.3.30...0.3.31) Updates `regex` from 1.10.6 to 1.11.0 - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.10.6...1.11.0) Updates `toml_edit` from 0.22.21 to 0.22.22 - [Commits](https://github.com/toml-rs/toml/compare/v0.22.21...v0.22.22) Updates `tracy-client-sys` from 0.24.0 to 0.24.1 - [Commits](https://github.com/nagisa/rust_tracy_client/compare/tracy-client-sys-v0.24.0...tracy-client-sys-v0.24.1) Updates `winnow` from 0.6.18 to 0.6.20 - [Changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md) - [Commits](https://github.com/winnow-rs/winnow/compare/v0.6.18...v0.6.20) --- updated-dependencies: - dependency-name: libc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: once_cell dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-updates - dependency-name: png dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: tracy-client dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: async-trait dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: autocfg dependency-type: indirect update-type: version-update:semver-minor dependency-group: patch-updates - dependency-name: cc dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: const_panic dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: fdeflate dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: flate2 dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: pkg-config dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: regex dependency-type: indirect update-type: version-update:semver-minor dependency-group: patch-updates - dependency-name: toml_edit dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: tracy-client-sys dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: winnow dependency-type: indirect update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 93 ++++++++++++++++++++++++++++++------------------------ Cargo.toml | 4 +-- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f7a159109..3c8d1aacc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -236,9 +236,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -253,9 +253,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" @@ -434,9 +434,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.21" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" dependencies = [ "jobserver", "libc", @@ -600,9 +600,9 @@ dependencies = [ [[package]] name = "const_panic" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7782af8f90fe69a4bb41e460abe1727d493403d8b2cc43201a3a3e906b24379f" +checksum = "013b6c2c3a14d678f38cd23994b02da3a1a1b6a5d1eedddfe63a5a5f11b13a81" [[package]] name = "convert_case" @@ -1055,9 +1055,9 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" dependencies = [ "simd-adler32", ] @@ -1079,9 +1079,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -1672,9 +1672,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libfuzzer-sys" @@ -1694,7 +1694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -1843,7 +1843,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", - "simd-adler32", ] [[package]] @@ -1853,6 +1852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -2118,9 +2118,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "oorandom" @@ -2258,9 +2261,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "player" @@ -2306,15 +2309,15 @@ dependencies = [ [[package]] name = "png" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.4", + "miniz_oxide 0.8.0", ] [[package]] @@ -2338,6 +2341,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "pp-rs" version = "0.2.1" @@ -2509,14 +2518,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -2530,13 +2539,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.5", ] [[package]] @@ -2547,9 +2556,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "renderdoc-sys" @@ -2928,9 +2937,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -3082,9 +3091,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", @@ -3142,9 +3151,9 @@ dependencies = [ [[package]] name = "tracy-client" -version = "0.17.3" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373db47331c3407b343538df77eea2516884a0b126cdfb4b135acfd400015dd7" +checksum = "746b078c6a09ebfd5594609049e07116735c304671eaab06ce749854d23435bc" dependencies = [ "loom", "once_cell", @@ -3153,9 +3162,9 @@ dependencies = [ [[package]] name = "tracy-client-sys" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cf0064dcb31c99aa1244c1b93439359e53f72ed217eef5db50abd442241e9a" +checksum = "68613466112302fdbeabc5fa55f7d57462a0b247d5a6b7d7e09401fb471a144d" dependencies = [ "cc", ] @@ -4152,9 +4161,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 73f17b755..fbf06524c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,14 +104,14 @@ nanorand = { version = "0.7", default-features = false, features = ["wyrand"] } noise = { version = "0.8", git = "https://github.com/Razaekel/noise-rs.git", rev = "c6942d4fb70af26db4441edcf41f90fa115333f2" } nv-flip = "0.1" obj = "0.10" -once_cell = "1.19.0" +once_cell = "1.20.1" parking_lot = "0.12.1" pico-args = { version = "0.5.0", features = [ "eq-separator", "short-space-opt", "combined-flags", ] } -png = "0.17.11" +png = "0.17.14" pollster = "0.3" profiling = { version = "1", default-features = false } raw-window-handle = "0.6" From 6db097694ce121f9cd927ef6dbb5608a69906760 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:28:48 +0200 Subject: [PATCH 41/59] build(deps): bump JamesIves/github-pages-deploy-action from 4.6.4 to 4.6.8 (#6342) Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.6.4 to 4.6.8. - [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases) - [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.6.4...v4.6.8) --- updated-dependencies: - dependency-name: JamesIves/github-pages-deploy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 2 +- .github/workflows/publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9e5dc2716..3000e5667 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -41,7 +41,7 @@ jobs: if: ${{ failure() }} - name: Deploy the docs - uses: JamesIves/github-pages-deploy-action@v4.6.4 + uses: JamesIves/github-pages-deploy-action@v4.6.8 if: github.ref == 'refs/heads/trunk' with: token: ${{ secrets.WEB_DEPLOY }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 266d4e7f5..6361ce68f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,7 +41,7 @@ jobs: run: cargo xtask run-wasm --no-serve - name: Deploy WebGPU examples - uses: JamesIves/github-pages-deploy-action@v4.6.4 + uses: JamesIves/github-pages-deploy-action@v4.6.8 if: github.ref == 'refs/heads/trunk' with: token: ${{ secrets.WEB_DEPLOY }} From c9202ee54a1a7b4aa152e9e68cc051d75fc46127 Mon Sep 17 00:00:00 2001 From: Elie Michel Date: Wed, 2 Oct 2024 10:21:30 +0200 Subject: [PATCH 42/59] Remove redundant let binding (#6356) --- wgpu-core/src/device/resource.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 03b183e08..9f0cbf33f 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -437,13 +437,11 @@ impl Device { .last_successful_submission_index .load(Ordering::Acquire); - if let wgt::Maintain::WaitForSubmissionIndex(submission_index) = maintain { - if submission_index > last_successful_submission_index { - return Err(WaitIdleError::WrongSubmissionIndex( - submission_index, - last_successful_submission_index, - )); - } + if submission_index > last_successful_submission_index { + return Err(WaitIdleError::WrongSubmissionIndex( + submission_index, + last_successful_submission_index, + )); } submission_index From 025787bbadb28464a630373604e796d93fbb490a Mon Sep 17 00:00:00 2001 From: teoxoy <28601907+teoxoy@users.noreply.github.com> Date: Thu, 18 Jul 2024 20:02:38 +0200 Subject: [PATCH 43/59] remove duplicate validation --- wgpu-core/src/binding_model.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 2357a8c77..6f43ac523 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -373,10 +373,6 @@ impl BindingTypeMaxCountValidator { limits.max_sampled_textures_per_shader_stage, BindingTypeMaxCountErrorKind::SampledTextures, )?; - self.storage_buffers.validate( - limits.max_storage_buffers_per_shader_stage, - BindingTypeMaxCountErrorKind::StorageBuffers, - )?; self.samplers.validate( limits.max_samplers_per_shader_stage, BindingTypeMaxCountErrorKind::Samplers, From 76af20348d0fefaffc734339dc3f295c51ebebb4 Mon Sep 17 00:00:00 2001 From: teoxoy <28601907+teoxoy@users.noreply.github.com> Date: Fri, 19 Jul 2024 11:26:40 +0200 Subject: [PATCH 44/59] remove old comment --- wgpu-core/src/command/compute.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index 5de03917d..7f396d11b 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -495,8 +495,6 @@ impl Global { state.raw_encoder.begin_compute_pass(&hal_desc); } - // TODO: We should be draining the commands here, avoiding extra copies in the process. - // (A command encoder can't be executed twice!) for command in base.commands { match command { ArcComputeCommand::SetBindGroup { From 0903ba6d47e952ad16c76f2fde0403b959a99a6d Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Thu, 19 Sep 2024 15:02:56 -0400 Subject: [PATCH 45/59] refactor(core): delete unused `CreateDeviceError` --- wgpu-core/src/device/resource.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 9f0cbf33f..18fdf5708 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -38,7 +38,6 @@ use arrayvec::ArrayVec; use once_cell::sync::OnceCell; use smallvec::SmallVec; -use thiserror::Error; use wgt::{ math::align_to, DeviceLostReason, TextureFormat, TextureSampleType, TextureViewDimension, }; @@ -187,14 +186,6 @@ impl Drop for Device { } } -#[derive(Clone, Debug, Error)] -pub enum CreateDeviceError { - #[error("Not enough memory left to create device")] - OutOfMemory, - #[error("Failed to create internal buffer for initializing textures")] - FailedToCreateZeroBuffer(#[from] DeviceError), -} - impl Device { pub(crate) fn raw(&self) -> &dyn hal::DynDevice { self.raw.as_ref() From 7ac533a3128497ea5c06d4062f83154cfa832b64 Mon Sep 17 00:00:00 2001 From: teoxoy <28601907+teoxoy@users.noreply.github.com> Date: Thu, 18 Jul 2024 21:00:14 +0200 Subject: [PATCH 46/59] add missing indirect buffer offset validation --- wgpu-core/src/command/compute.rs | 6 ++++++ wgpu-core/src/command/render.rs | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index 7f396d11b..1540a5f50 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -136,6 +136,8 @@ pub enum ComputePassErrorInner { BindGroupIndexOutOfRange { index: u32, max: u32 }, #[error(transparent)] DestroyedResource(#[from] DestroyedResourceError), + #[error("Indirect buffer offset {0:?} is not a multiple of 4")] + UnalignedIndirectBufferOffset(BufferAddress), #[error("Indirect buffer uses bytes {offset}..{end_offset} which overruns indirect buffer of size {buffer_size}")] IndirectBufferOverrun { offset: u64, @@ -845,6 +847,10 @@ fn dispatch_indirect( .merge_single(&buffer, hal::BufferUses::INDIRECT)?; buffer.check_usage(wgt::BufferUsages::INDIRECT)?; + if offset % 4 != 0 { + return Err(ComputePassErrorInner::UnalignedIndirectBufferOffset(offset)); + } + let end_offset = offset + size_of::() as u64; if end_offset > buffer.size { return Err(ComputePassErrorInner::IndirectBufferOverrun { diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index b6680333c..d22eb5f0d 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -638,6 +638,8 @@ pub enum RenderPassErrorInner { MissingFeatures(#[from] MissingFeatures), #[error(transparent)] MissingDownlevelFlags(#[from] MissingDownlevelFlags), + #[error("Indirect buffer offset {0:?} is not a multiple of 4")] + UnalignedIndirectBufferOffset(BufferAddress), #[error("Indirect draw uses bytes {offset}..{end_offset} {} which overruns indirect buffer of size {buffer_size}", count.map_or_else(String::new, |v| format!("(using count {v})")))] IndirectBufferOverrun { @@ -2450,6 +2452,10 @@ fn multi_draw_indirect( let actual_count = count.map_or(1, |c| c.get()); + if offset % 4 != 0 { + return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset)); + } + let end_offset = offset + stride as u64 * actual_count as u64; if end_offset > indirect_buffer.size { return Err(RenderPassErrorInner::IndirectBufferOverrun { @@ -2534,6 +2540,10 @@ fn multi_draw_indirect_count( count_buffer.check_usage(BufferUsages::INDIRECT)?; let count_raw = count_buffer.try_raw(state.snatch_guard)?; + if offset % 4 != 0 { + return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset)); + } + let end_offset = offset + stride * max_count as u64; if end_offset > indirect_buffer.size { return Err(RenderPassErrorInner::IndirectBufferOverrun { From 38a13b94f0a779727a21b37cede6c5e1c7bcb161 Mon Sep 17 00:00:00 2001 From: teoxoy <28601907+teoxoy@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:23:13 +0200 Subject: [PATCH 47/59] refactor: make `Snatchable::snatch` take `_guard` by `&mut _` --- wgpu-core/src/device/resource.rs | 5 +++-- wgpu-core/src/present.rs | 14 ++++++++++---- wgpu-core/src/resource.rs | 6 ++---- wgpu-core/src/snatch.rs | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 18fdf5708..5a726ef70 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -363,7 +363,7 @@ impl Device { let Some(view) = view.upgrade() else { continue; }; - let Some(raw_view) = view.raw.snatch(self.snatchable_lock.write()) else { + let Some(raw_view) = view.raw.snatch(&mut self.snatchable_lock.write()) else { continue; }; @@ -377,7 +377,8 @@ impl Device { let Some(bind_group) = bind_group.upgrade() else { continue; }; - let Some(raw_bind_group) = bind_group.raw.snatch(self.snatchable_lock.write()) + let Some(raw_bind_group) = + bind_group.raw.snatch(&mut self.snatchable_lock.write()) else { continue; }; diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index c9d0124bf..b12e35328 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -288,8 +288,11 @@ impl Global { .textures .remove(texture.tracker_index()); let suf = surface.raw(device.backend()).unwrap(); - let exclusive_snatch_guard = device.snatchable_lock.write(); - match texture.inner.snatch(exclusive_snatch_guard).unwrap() { + match texture + .inner + .snatch(&mut device.snatchable_lock.write()) + .unwrap() + { resource::TextureInner::Surface { raw, parent_id } => { if surface_id != parent_id { log::error!("Presented frame is from a different surface"); @@ -359,8 +362,11 @@ impl Global { .textures .remove(texture.tracker_index()); let suf = surface.raw(device.backend()); - let exclusive_snatch_guard = device.snatchable_lock.write(); - match texture.inner.snatch(exclusive_snatch_guard).unwrap() { + match texture + .inner + .snatch(&mut device.snatchable_lock.write()) + .unwrap() + { resource::TextureInner::Surface { raw, parent_id } => { if surface_id == parent_id { unsafe { suf.unwrap().discard_texture(raw) }; diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 5df285da5..32fde3dd5 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -737,8 +737,7 @@ impl Buffer { let device = &self.device; let temp = { - let snatch_guard = device.snatchable_lock.write(); - let raw = match self.raw.snatch(snatch_guard) { + let raw = match self.raw.snatch(&mut device.snatchable_lock.write()) { Some(raw) => raw, None => { return Err(DestroyError::AlreadyDestroyed); @@ -1185,8 +1184,7 @@ impl Texture { let device = &self.device; let temp = { - let snatch_guard = device.snatchable_lock.write(); - let raw = match self.inner.snatch(snatch_guard) { + let raw = match self.inner.snatch(&mut device.snatchable_lock.write()) { Some(TextureInner::Native { raw }) => raw, Some(TextureInner::Surface { .. }) => { return Ok(()); diff --git a/wgpu-core/src/snatch.rs b/wgpu-core/src/snatch.rs index 9866b7772..a817e2068 100644 --- a/wgpu-core/src/snatch.rs +++ b/wgpu-core/src/snatch.rs @@ -38,7 +38,7 @@ impl Snatchable { } /// Take the value. Requires a the snatchable lock's write guard. - pub fn snatch(&self, _guard: ExclusiveSnatchGuard) -> Option { + pub fn snatch(&self, _guard: &mut ExclusiveSnatchGuard) -> Option { unsafe { (*self.value.get()).take() } } From 71b4f361e244d8b4be79c67207e19a2c267c73f2 Mon Sep 17 00:00:00 2001 From: teoxoy <28601907+teoxoy@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:23:13 +0200 Subject: [PATCH 48/59] chore(core): more detail on `TODO` for push constants --- wgpu-core/src/command/compute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index 1540a5f50..133a5af35 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -780,7 +780,7 @@ fn set_push_constant( .binder .pipeline_layout .as_ref() - //TODO: don't error here, lazily update the push constants + // TODO: don't error here, lazily update the push constants using `state.push_constants` .ok_or(ComputePassErrorInner::Dispatch( DispatchError::MissingPipeline, ))?; From 2d82054ae4af209c3cbe297039469941b2f3b4e6 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:12:06 +0200 Subject: [PATCH 49/59] [wgsl-in, spv-out] Allow dynamic indexing of arrays by value. Bring https://github.com/gfx-rs/naga/pull/723 back from the dead. Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> Co-authored-by: Dzmitry Malyshau Co-authored-by: Jim Blandy --- naga/src/back/spv/block.rs | 28 +- naga/src/back/spv/mod.rs | 1 + naga/src/back/spv/writer.rs | 53 ++++ naga/src/lib.rs | 21 +- naga/src/proc/mod.rs | 8 +- naga/src/proc/typifier.rs | 7 + naga/src/valid/expression.rs | 7 +- naga/tests/in/access.wgsl | 11 + naga/tests/out/analysis/access.info.ron | 186 ++++++++++++ .../access.assign_through_ptr.Compute.glsl | 12 +- naga/tests/out/glsl/access.foo.Vertex.glsl | 52 ++++ .../out/glsl/access.foo_frag.Fragment.glsl | 12 +- .../out/glsl/access.foo_vert.Vertex.glsl | 16 +- naga/tests/out/hlsl/access.hlsl | 24 +- naga/tests/out/hlsl/access.ron | 4 + naga/tests/out/ir/access.compact.ron | 117 ++++++++ naga/tests/out/ir/access.ron | 117 ++++++++ naga/tests/out/msl/access.msl | 33 ++- naga/tests/out/spv/access.spvasm | 273 ++++++++++-------- naga/tests/out/wgsl/access.wgsl | 23 +- naga/tests/wgsl_errors.rs | 5 - 21 files changed, 837 insertions(+), 173 deletions(-) create mode 100644 naga/tests/out/glsl/access.foo.Vertex.glsl diff --git a/naga/src/back/spv/block.rs b/naga/src/back/spv/block.rs index f0c3bfa84..97db1cb54 100644 --- a/naga/src/back/spv/block.rs +++ b/naga/src/back/spv/block.rs @@ -343,6 +343,32 @@ impl<'w> BlockContext<'w> { load_id } + crate::TypeInner::Array { + base: ty_element, .. + } => { + let index_id = self.cached[index]; + let base_id = self.cached[base]; + let base_ty = match self.fun_info[base].ty { + TypeResolution::Handle(handle) => handle, + TypeResolution::Value(_) => { + return Err(Error::Validation( + "Array types should always be in the arena", + )) + } + }; + let (id, variable) = self.writer.promote_access_expression_to_variable( + &self.ir_module.types, + result_type_id, + base_id, + base_ty, + index_id, + ty_element, + block, + )?; + self.function.internal_variables.push(variable); + id + } + // wgpu#4337: Support `crate::TypeInner::Matrix` ref other => { log::error!( "Unable to access base {:?} of type {:?}", @@ -350,7 +376,7 @@ impl<'w> BlockContext<'w> { other ); return Err(Error::Validation( - "only vectors may be dynamically indexed by value", + "only vectors and arrays may be dynamically indexed by value", )); } } diff --git a/naga/src/back/spv/mod.rs b/naga/src/back/spv/mod.rs index 32bd1fcec..77f915abb 100644 --- a/naga/src/back/spv/mod.rs +++ b/naga/src/back/spv/mod.rs @@ -144,6 +144,7 @@ struct Function { signature: Option, parameters: Vec, variables: crate::FastHashMap, LocalVariable>, + internal_variables: Vec, blocks: Vec, entry_point_context: Option, } diff --git a/naga/src/back/spv/writer.rs b/naga/src/back/spv/writer.rs index 678dcb424..7f41caf29 100644 --- a/naga/src/back/spv/writer.rs +++ b/naga/src/back/spv/writer.rs @@ -32,6 +32,9 @@ impl Function { for local_var in self.variables.values() { local_var.instruction.to_words(sink); } + for internal_var in self.internal_variables.iter() { + internal_var.instruction.to_words(sink); + } } for instruction in block.body.iter() { instruction.to_words(sink); @@ -135,6 +138,56 @@ impl Writer { self.capabilities_used.insert(spirv::Capability::Shader); } + #[allow(clippy::too_many_arguments)] + pub(super) fn promote_access_expression_to_variable( + &mut self, + ir_types: &UniqueArena, + result_type_id: Word, + container_id: Word, + container_ty: Handle, + index_id: Word, + element_ty: Handle, + block: &mut Block, + ) -> Result<(Word, LocalVariable), Error> { + let pointer_type_id = + self.get_pointer_id(ir_types, container_ty, spirv::StorageClass::Function)?; + + let variable = { + let id = self.id_gen.next(); + LocalVariable { + id, + instruction: Instruction::variable( + pointer_type_id, + id, + spirv::StorageClass::Function, + None, + ), + } + }; + block + .body + .push(Instruction::store(variable.id, container_id, None)); + + let element_pointer_id = self.id_gen.next(); + let element_pointer_type_id = + self.get_pointer_id(ir_types, element_ty, spirv::StorageClass::Function)?; + block.body.push(Instruction::access_chain( + element_pointer_type_id, + element_pointer_id, + variable.id, + &[index_id], + )); + let id = self.id_gen.next(); + block.body.push(Instruction::load( + result_type_id, + id, + element_pointer_id, + None, + )); + + Ok((id, variable)) + } + /// Indicate that the code requires any one of the listed capabilities. /// /// If nothing in `capabilities` appears in the available capabilities diff --git a/naga/src/lib.rs b/naga/src/lib.rs index 85fd7a450..038e215a6 100644 --- a/naga/src/lib.rs +++ b/naga/src/lib.rs @@ -1402,21 +1402,20 @@ pub enum Expression { /// ## Dynamic indexing restrictions /// /// To accommodate restrictions in some of the shader languages that Naga - /// targets, it is not permitted to subscript a matrix or array with a - /// dynamically computed index unless that matrix or array appears behind a - /// pointer. In other words, if the inner type of `base` is [`Array`] or - /// [`Matrix`], then `index` must be a constant. But if the type of `base` - /// is a [`Pointer`] to an array or matrix or a [`ValuePointer`] with a - /// `size`, then the index may be any expression of integer type. + /// targets, it is not permitted to subscript a matrix with a dynamically + /// computed index unless that matrix appears behind a pointer. In other + /// words, if the inner type of `base` is [`Matrix`], then `index` must be a + /// constant. But if the type of `base` is a [`Pointer`] to an matrix, then + /// the index may be any expression of integer type. /// /// You can use the [`Expression::is_dynamic_index`] method to determine - /// whether a given index expression requires matrix or array base operands - /// to be behind a pointer. + /// whether a given index expression requires matrix base operands to be + /// behind a pointer. /// /// (It would be simpler to always require the use of `AccessIndex` when - /// subscripting arrays and matrices that are not behind pointers, but to - /// accommodate existing front ends, Naga also permits `Access`, with a - /// restricted `index`.) + /// subscripting matrices that are not behind pointers, but to accommodate + /// existing front ends, Naga also permits `Access`, with a restricted + /// `index`.) /// /// [`Vector`]: TypeInner::Vector /// [`Matrix`]: TypeInner::Matrix diff --git a/naga/src/proc/mod.rs b/naga/src/proc/mod.rs index a5b3ea4e3..abbe0c7e4 100644 --- a/naga/src/proc/mod.rs +++ b/naga/src/proc/mod.rs @@ -521,12 +521,12 @@ impl crate::Expression { } } - /// Return true if this expression is a dynamic array index, for [`Access`]. + /// Return true if this expression is a dynamic array/vector/matrix index, + /// for [`Access`]. /// /// This method returns true if this expression is a dynamically computed - /// index, and as such can only be used to index matrices and arrays when - /// they appear behind a pointer. See the documentation for [`Access`] for - /// details. + /// index, and as such can only be used to index matrices when they appear + /// behind a pointer. See the documentation for [`Access`] for details. /// /// Note, this does not check the _type_ of the given expression. It's up to /// the caller to establish that the `Access` expression is well-typed diff --git a/naga/src/proc/typifier.rs b/naga/src/proc/typifier.rs index d8af0cd23..f29ff40f0 100644 --- a/naga/src/proc/typifier.rs +++ b/naga/src/proc/typifier.rs @@ -92,6 +92,13 @@ pub enum TypeResolution { /// available in the associated arena. However, the `TypeInner` itself may /// contain `Handle` values referring to types from the arena. /// + /// The inner type must only be one of the following variants: + /// - TypeInner::Pointer + /// - TypeInner::ValuePointer + /// - TypeInner::Matrix (generated by matrix multiplication) + /// - TypeInner::Vector + /// - TypeInner::Scalar + /// /// [`TypeInner`]: crate::TypeInner Value(crate::TypeInner), } diff --git a/naga/src/valid/expression.rs b/naga/src/valid/expression.rs index 0b0d115c5..2b479d3a7 100644 --- a/naga/src/valid/expression.rs +++ b/naga/src/valid/expression.rs @@ -240,9 +240,10 @@ impl super::Validator { let base_type = &resolver[base]; // See the documentation for `Expression::Access`. let dynamic_indexing_restricted = match *base_type { - Ti::Vector { .. } => false, - Ti::Matrix { .. } | Ti::Array { .. } => true, - Ti::Pointer { .. } + Ti::Matrix { .. } => true, + Ti::Vector { .. } + | Ti::Array { .. } + | Ti::Pointer { .. } | Ti::ValuePointer { size: Some(_), .. } | Ti::BindingArray { .. } => false, ref other => { diff --git a/naga/tests/in/access.wgsl b/naga/tests/in/access.wgsl index 956a694aa..3336522fd 100644 --- a/naga/tests/in/access.wgsl +++ b/naga/tests/in/access.wgsl @@ -167,3 +167,14 @@ fn assign_through_ptr() { var arr = array, 2>(vec4(6.0), vec4(7.0)); assign_array_through_ptr_fn(&arr); } + +@vertex +fn foo(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4 { + let arr = array(1, 2, 3, 4, 5); + let value = arr[vi]; + return vec4(vec4(value)); +} + +fn array_by_value(a: array, i: i32) -> i32 { + return a[i]; +} diff --git a/naga/tests/out/analysis/access.info.ron b/naga/tests/out/analysis/access.info.ron index 308bb1a8b..830831cb1 100644 --- a/naga/tests/out/analysis/access.info.ron +++ b/naga/tests/out/analysis/access.info.ron @@ -2735,6 +2735,54 @@ sampling: [], dual_source_blending: false, ), + ( + flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), + available_stages: ("VERTEX | FRAGMENT | COMPUTE"), + uniformity: ( + non_uniform_result: Some(0), + requirements: (""), + ), + may_kill: false, + sampling_set: [], + global_uses: [ + (""), + (""), + (""), + (""), + (""), + ], + expressions: [ + ( + uniformity: ( + non_uniform_result: Some(0), + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(25), + ), + ( + uniformity: ( + non_uniform_result: Some(1), + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(0), + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ], + sampling: [], + dual_source_blending: false, + ), ], entry_points: [ ( @@ -3981,6 +4029,144 @@ sampling: [], dual_source_blending: false, ), + ( + flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"), + available_stages: ("VERTEX | FRAGMENT | COMPUTE"), + uniformity: ( + non_uniform_result: Some(0), + requirements: (""), + ), + may_kill: false, + sampling_set: [], + global_uses: [ + (""), + (""), + (""), + (""), + (""), + ], + expressions: [ + ( + uniformity: ( + non_uniform_result: Some(0), + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(0), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar(( + kind: Sint, + width: 4, + ))), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar(( + kind: Sint, + width: 4, + ))), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar(( + kind: Sint, + width: 4, + ))), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar(( + kind: Sint, + width: 4, + ))), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar(( + kind: Sint, + width: 4, + ))), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(25), + ), + ( + uniformity: ( + non_uniform_result: Some(0), + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(0), + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Vector( + size: Quad, + scalar: ( + kind: Sint, + width: 4, + ), + )), + ), + ( + uniformity: ( + non_uniform_result: Some(0), + requirements: (""), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Vector( + size: Quad, + scalar: ( + kind: Float, + width: 4, + ), + )), + ), + ], + sampling: [], + dual_source_blending: false, + ), ], const_expression_types: [ Value(Scalar(( diff --git a/naga/tests/out/glsl/access.assign_through_ptr.Compute.glsl b/naga/tests/out/glsl/access.assign_through_ptr.Compute.glsl index 2e51bbde6..4a4791c99 100644 --- a/naga/tests/out/glsl/access.assign_through_ptr.Compute.glsl +++ b/naga/tests/out/glsl/access.assign_through_ptr.Compute.glsl @@ -20,8 +20,8 @@ struct MatCx2InArray { mat4x2 am[2]; }; -float read_from_private(inout float foo_1) { - float _e1 = foo_1; +float read_from_private(inout float foo_2) { + float _e1 = foo_2; return _e1; } @@ -34,11 +34,15 @@ void assign_through_ptr_fn(inout uint p) { return; } -void assign_array_through_ptr_fn(inout vec4 foo_2[2]) { - foo_2 = vec4[2](vec4(1.0), vec4(2.0)); +void assign_array_through_ptr_fn(inout vec4 foo_3[2]) { + foo_3 = vec4[2](vec4(1.0), vec4(2.0)); return; } +int array_by_value(int a_1[5], int i) { + return a_1[i]; +} + void main() { uint val = 33u; vec4 arr[2] = vec4[2](vec4(6.0), vec4(7.0)); diff --git a/naga/tests/out/glsl/access.foo.Vertex.glsl b/naga/tests/out/glsl/access.foo.Vertex.glsl new file mode 100644 index 000000000..e1f313840 --- /dev/null +++ b/naga/tests/out/glsl/access.foo.Vertex.glsl @@ -0,0 +1,52 @@ +#version 310 es + +precision highp float; +precision highp int; + +struct GlobalConst { + uint a; + uvec3 b; + int c; +}; +struct AlignedWrapper { + int value; +}; +struct Baz { + mat3x2 m; +}; +struct MatCx2InArray { + mat4x2 am[2]; +}; + +float read_from_private(inout float foo_2) { + float _e1 = foo_2; + return _e1; +} + +float test_arr_as_arg(float a[5][10]) { + return a[4][9]; +} + +void assign_through_ptr_fn(inout uint p) { + p = 42u; + return; +} + +void assign_array_through_ptr_fn(inout vec4 foo_3[2]) { + foo_3 = vec4[2](vec4(1.0), vec4(2.0)); + return; +} + +int array_by_value(int a_1[5], int i) { + return a_1[i]; +} + +void main() { + uint vi_1 = uint(gl_VertexID); + int arr_1[5] = int[5](1, 2, 3, 4, 5); + int value = arr_1[vi_1]; + gl_Position = vec4(ivec4(value)); + gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w); + return; +} + diff --git a/naga/tests/out/glsl/access.foo_frag.Fragment.glsl b/naga/tests/out/glsl/access.foo_frag.Fragment.glsl index aacdda013..eca6bc54c 100644 --- a/naga/tests/out/glsl/access.foo_frag.Fragment.glsl +++ b/naga/tests/out/glsl/access.foo_frag.Fragment.glsl @@ -30,8 +30,8 @@ layout(std430) buffer type_13_block_1Fragment { ivec2 _group_0_binding_2_fs; }; layout(location = 0) out vec4 _fs2p_location0; -float read_from_private(inout float foo_1) { - float _e1 = foo_1; +float read_from_private(inout float foo_2) { + float _e1 = foo_2; return _e1; } @@ -44,11 +44,15 @@ void assign_through_ptr_fn(inout uint p) { return; } -void assign_array_through_ptr_fn(inout vec4 foo_2[2]) { - foo_2 = vec4[2](vec4(1.0), vec4(2.0)); +void assign_array_through_ptr_fn(inout vec4 foo_3[2]) { + foo_3 = vec4[2](vec4(1.0), vec4(2.0)); return; } +int array_by_value(int a_1[5], int i) { + return a_1[i]; +} + void main() { _group_0_binding_0_fs._matrix[1][2] = 1.0; _group_0_binding_0_fs._matrix = mat4x3(vec3(0.0), vec3(1.0), vec3(2.0), vec3(3.0)); diff --git a/naga/tests/out/glsl/access.foo_vert.Vertex.glsl b/naga/tests/out/glsl/access.foo_vert.Vertex.glsl index d4a9b9294..a926eadf7 100644 --- a/naga/tests/out/glsl/access.foo_vert.Vertex.glsl +++ b/naga/tests/out/glsl/access.foo_vert.Vertex.glsl @@ -103,8 +103,8 @@ void test_matrix_within_array_within_struct_accesses() { return; } -float read_from_private(inout float foo_1) { - float _e1 = foo_1; +float read_from_private(inout float foo_2) { + float _e1 = foo_2; return _e1; } @@ -117,11 +117,15 @@ void assign_through_ptr_fn(inout uint p) { return; } -void assign_array_through_ptr_fn(inout vec4 foo_2[2]) { - foo_2 = vec4[2](vec4(1.0), vec4(2.0)); +void assign_array_through_ptr_fn(inout vec4 foo_3[2]) { + foo_3 = vec4[2](vec4(1.0), vec4(2.0)); return; } +int array_by_value(int a_1[5], int i) { + return a_1[i]; +} + void main() { uint vi = uint(gl_VertexID); float foo = 0.0; @@ -133,10 +137,10 @@ void main() { mat4x3 _matrix = _group_0_binding_0_vs._matrix; uvec2 arr_1[2] = _group_0_binding_0_vs.arr; float b = _group_0_binding_0_vs._matrix[3u][0]; - int a_1 = _group_0_binding_0_vs.data[(uint(_group_0_binding_0_vs.data.length()) - 2u)].value; + int a_2 = _group_0_binding_0_vs.data[(uint(_group_0_binding_0_vs.data.length()) - 2u)].value; ivec2 c = _group_0_binding_2_vs; float _e33 = read_from_private(foo); - c2_ = int[5](a_1, int(b), 3, 4, 5); + c2_ = int[5](a_2, int(b), 3, 4, 5); c2_[(vi + 1u)] = 42; int value = c2_[vi]; float _e47 = test_arr_as_arg(float[5][10](float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0))); diff --git a/naga/tests/out/hlsl/access.hlsl b/naga/tests/out/hlsl/access.hlsl index 142083be6..543b84196 100644 --- a/naga/tests/out/hlsl/access.hlsl +++ b/naga/tests/out/hlsl/access.hlsl @@ -201,9 +201,9 @@ void test_matrix_within_array_within_struct_accesses() return; } -float read_from_private(inout float foo_1) +float read_from_private(inout float foo_2) { - float _e1 = foo_1; + float _e1 = foo_2; return _e1; } @@ -224,12 +224,17 @@ ret_Constructarray2_float4_ Constructarray2_float4_(float4 arg0, float4 arg1) { return ret; } -void assign_array_through_ptr_fn(inout float4 foo_2[2]) +void assign_array_through_ptr_fn(inout float4 foo_3[2]) { - foo_2 = Constructarray2_float4_((1.0).xxxx, (2.0).xxxx); + foo_3 = Constructarray2_float4_((1.0).xxxx, (2.0).xxxx); return; } +int array_by_value(int a_1[5], int i) +{ + return a_1[i]; +} + typedef int ret_Constructarray5_int_[5]; ret_Constructarray5_int_ Constructarray5_int_(int arg0, int arg1, int arg2, int arg3, int arg4) { int ret[5] = { arg0, arg1, arg2, arg3, arg4 }; @@ -266,10 +271,10 @@ float4 foo_vert(uint vi : SV_VertexID) : SV_Position float4x3 _matrix = float4x3(asfloat(bar.Load3(0+0)), asfloat(bar.Load3(0+16)), asfloat(bar.Load3(0+32)), asfloat(bar.Load3(0+48))); uint2 arr_1[2] = Constructarray2_uint2_(asuint(bar.Load2(144+0)), asuint(bar.Load2(144+8))); float b = asfloat(bar.Load(0+3u*16+0)); - int a_1 = asint(bar.Load(0+(((NagaBufferLengthRW(bar) - 160) / 8) - 2u)*8+160)); + int a_2 = asint(bar.Load(0+(((NagaBufferLengthRW(bar) - 160) / 8) - 2u)*8+160)); int2 c = asint(qux.Load2(0)); const float _e33 = read_from_private(foo); - c2_ = Constructarray5_int_(a_1, int(b), 3, 4, 5); + c2_ = Constructarray5_int_(a_2, int(b), 3, 4, 5); c2_[(vi + 1u)] = 42; int value = c2_[vi]; const float _e47 = test_arr_as_arg(ZeroValuearray5_array10_float__()); @@ -310,3 +315,10 @@ void assign_through_ptr() assign_array_through_ptr_fn(arr); return; } + +float4 foo_1(uint vi_1 : SV_VertexID) : SV_Position +{ + int arr_2[5] = Constructarray5_int_(1, 2, 3, 4, 5); + int value_1 = arr_2[vi_1]; + return float4((value_1).xxxx); +} diff --git a/naga/tests/out/hlsl/access.ron b/naga/tests/out/hlsl/access.ron index 73c9e4444..8960a612e 100644 --- a/naga/tests/out/hlsl/access.ron +++ b/naga/tests/out/hlsl/access.ron @@ -4,6 +4,10 @@ entry_point:"foo_vert", target_profile:"vs_5_1", ), + ( + entry_point:"foo_1", + target_profile:"vs_5_1", + ), ], fragment:[ ( diff --git a/naga/tests/out/ir/access.compact.ron b/naga/tests/out/ir/access.compact.ron index 1b95742ff..2d066b8ff 100644 --- a/naga/tests/out/ir/access.compact.ron +++ b/naga/tests/out/ir/access.compact.ron @@ -1655,6 +1655,47 @@ ), ], ), + ( + name: Some("array_by_value"), + arguments: [ + ( + name: Some("a"), + ty: 25, + binding: None, + ), + ( + name: Some("i"), + ty: 2, + binding: None, + ), + ], + result: Some(( + ty: 2, + binding: None, + )), + local_variables: [], + expressions: [ + FunctionArgument(0), + FunctionArgument(1), + Access( + base: 0, + index: 1, + ), + ], + named_expressions: { + 0: "a", + 1: "i", + }, + body: [ + Emit(( + start: 2, + end: 3, + )), + Return( + value: Some(2), + ), + ], + ), ], entry_points: [ ( @@ -2230,5 +2271,81 @@ ], ), ), + ( + name: "foo", + stage: Vertex, + early_depth_test: None, + workgroup_size: (0, 0, 0), + function: ( + name: Some("foo"), + arguments: [ + ( + name: Some("vi"), + ty: 0, + binding: Some(BuiltIn(VertexIndex)), + ), + ], + result: Some(( + ty: 24, + binding: Some(BuiltIn(Position( + invariant: false, + ))), + )), + local_variables: [], + expressions: [ + FunctionArgument(0), + Literal(I32(1)), + Literal(I32(2)), + Literal(I32(3)), + Literal(I32(4)), + Literal(I32(5)), + Compose( + ty: 25, + components: [ + 1, + 2, + 3, + 4, + 5, + ], + ), + Access( + base: 6, + index: 0, + ), + Splat( + size: Quad, + value: 7, + ), + As( + expr: 8, + kind: Float, + convert: Some(4), + ), + ], + named_expressions: { + 0: "vi", + 6: "arr", + 7: "value", + }, + body: [ + Emit(( + start: 6, + end: 7, + )), + Emit(( + start: 7, + end: 8, + )), + Emit(( + start: 8, + end: 10, + )), + Return( + value: Some(9), + ), + ], + ), + ), ], ) \ No newline at end of file diff --git a/naga/tests/out/ir/access.ron b/naga/tests/out/ir/access.ron index 1b95742ff..2d066b8ff 100644 --- a/naga/tests/out/ir/access.ron +++ b/naga/tests/out/ir/access.ron @@ -1655,6 +1655,47 @@ ), ], ), + ( + name: Some("array_by_value"), + arguments: [ + ( + name: Some("a"), + ty: 25, + binding: None, + ), + ( + name: Some("i"), + ty: 2, + binding: None, + ), + ], + result: Some(( + ty: 2, + binding: None, + )), + local_variables: [], + expressions: [ + FunctionArgument(0), + FunctionArgument(1), + Access( + base: 0, + index: 1, + ), + ], + named_expressions: { + 0: "a", + 1: "i", + }, + body: [ + Emit(( + start: 2, + end: 3, + )), + Return( + value: Some(2), + ), + ], + ), ], entry_points: [ ( @@ -2230,5 +2271,81 @@ ], ), ), + ( + name: "foo", + stage: Vertex, + early_depth_test: None, + workgroup_size: (0, 0, 0), + function: ( + name: Some("foo"), + arguments: [ + ( + name: Some("vi"), + ty: 0, + binding: Some(BuiltIn(VertexIndex)), + ), + ], + result: Some(( + ty: 24, + binding: Some(BuiltIn(Position( + invariant: false, + ))), + )), + local_variables: [], + expressions: [ + FunctionArgument(0), + Literal(I32(1)), + Literal(I32(2)), + Literal(I32(3)), + Literal(I32(4)), + Literal(I32(5)), + Compose( + ty: 25, + components: [ + 1, + 2, + 3, + 4, + 5, + ], + ), + Access( + base: 6, + index: 0, + ), + Splat( + size: Quad, + value: 7, + ), + As( + expr: 8, + kind: Float, + convert: Some(4), + ), + ], + named_expressions: { + 0: "vi", + 6: "arr", + 7: "value", + }, + body: [ + Emit(( + start: 6, + end: 7, + )), + Emit(( + start: 7, + end: 8, + )), + Emit(( + start: 8, + end: 10, + )), + Return( + value: Some(9), + ), + ], + ), + ), ], ) \ No newline at end of file diff --git a/naga/tests/out/msl/access.msl b/naga/tests/out/msl/access.msl index 65dba4910..924b604e4 100644 --- a/naga/tests/out/msl/access.msl +++ b/naga/tests/out/msl/access.msl @@ -133,9 +133,9 @@ void test_matrix_within_array_within_struct_accesses( } float read_from_private( - thread float& foo_1 + thread float& foo_2 ) { - float _e1 = foo_1; + float _e1 = foo_2; return _e1; } @@ -153,12 +153,19 @@ void assign_through_ptr_fn( } void assign_array_through_ptr_fn( - thread type_22& foo_2 + thread type_22& foo_3 ) { - foo_2 = type_22 {metal::float4(1.0), metal::float4(2.0)}; + foo_3 = type_22 {metal::float4(1.0), metal::float4(2.0)}; return; } +int array_by_value( + type_20 a_1, + int i +) { + return a_1.inner[i]; +} + struct foo_vertInput { }; struct foo_vertOutput { @@ -181,10 +188,10 @@ vertex foo_vertOutput foo_vert( metal::float4x3 _matrix = bar._matrix; type_10 arr_1 = bar.arr; float b = bar._matrix[3u].x; - int a_1 = bar.data[(1 + (_buffer_sizes.size1 - 160 - 8) / 8) - 2u].value; + int a_2 = bar.data[(1 + (_buffer_sizes.size1 - 160 - 8) / 8) - 2u].value; metal::int2 c = qux; float _e33 = read_from_private(foo); - c2_ = type_20 {a_1, static_cast(b), 3, 4, 5}; + c2_ = type_20 {a_2, static_cast(b), 3, 4, 5}; c2_.inner[vi + 1u] = 42; int value = c2_.inner[vi]; float _e47 = test_arr_as_arg(type_18 {}); @@ -217,3 +224,17 @@ kernel void assign_through_ptr( assign_array_through_ptr_fn(arr); return; } + + +struct foo_1Input { +}; +struct foo_1Output { + metal::float4 member_3 [[position]]; +}; +vertex foo_1Output foo_1( + uint vi_1 [[vertex_id]] +) { + type_20 arr_2 = type_20 {1, 2, 3, 4, 5}; + int value_1 = arr_2.inner[vi_1]; + return foo_1Output { static_cast(metal::int4(value_1)) }; +} diff --git a/naga/tests/out/spv/access.spvasm b/naga/tests/out/spv/access.spvasm index ab0112870..6cb87a3b1 100644 --- a/naga/tests/out/spv/access.spvasm +++ b/naga/tests/out/spv/access.spvasm @@ -1,16 +1,17 @@ ; SPIR-V ; Version: 1.1 ; Generator: rspirv -; Bound: 301 +; Bound: 323 OpCapability Shader OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -OpEntryPoint Vertex %219 "foo_vert" %214 %217 -OpEntryPoint Fragment %273 "foo_frag" %272 -OpEntryPoint GLCompute %291 "assign_through_ptr" -OpExecutionMode %273 OriginUpperLeft -OpExecutionMode %291 LocalSize 1 1 1 +OpEntryPoint Vertex %229 "foo_vert" %224 %227 +OpEntryPoint Fragment %282 "foo_frag" %281 +OpEntryPoint GLCompute %300 "assign_through_ptr" +OpEntryPoint Vertex %314 "foo" %311 %313 +OpExecutionMode %282 OriginUpperLeft +OpExecutionMode %300 LocalSize 1 1 1 OpMemberName %6 0 "a" OpMemberName %6 1 "b" OpMemberName %6 2 "c" @@ -47,14 +48,19 @@ OpName %200 "p" OpName %201 "assign_through_ptr_fn" OpName %206 "foo" OpName %207 "assign_array_through_ptr_fn" -OpName %214 "vi" -OpName %219 "foo_vert" -OpName %231 "foo" -OpName %232 "c2" -OpName %273 "foo_frag" -OpName %291 "assign_through_ptr" -OpName %296 "val" -OpName %297 "arr" +OpName %214 "a" +OpName %215 "i" +OpName %216 "array_by_value" +OpName %224 "vi" +OpName %229 "foo_vert" +OpName %241 "foo" +OpName %242 "c2" +OpName %282 "foo_frag" +OpName %300 "assign_through_ptr" +OpName %305 "val" +OpName %306 "arr" +OpName %311 "vi" +OpName %314 "foo" OpMemberDecorate %6 0 Offset 0 OpMemberDecorate %6 1 Offset 16 OpMemberDecorate %6 2 Offset 28 @@ -99,9 +105,11 @@ OpDecorate %50 DescriptorSet 0 OpDecorate %50 Binding 3 OpDecorate %51 Block OpMemberDecorate %51 0 Offset 0 -OpDecorate %214 BuiltIn VertexIndex -OpDecorate %217 BuiltIn Position -OpDecorate %272 Location 0 +OpDecorate %224 BuiltIn VertexIndex +OpDecorate %227 BuiltIn Position +OpDecorate %281 Location 0 +OpDecorate %311 BuiltIn VertexIndex +OpDecorate %313 BuiltIn Position %2 = OpTypeVoid %3 = OpTypeInt 32 0 %4 = OpTypeVector %3 3 @@ -209,44 +217,49 @@ OpDecorate %272 Location 0 %209 = OpConstantComposite %31 %59 %59 %59 %59 %210 = OpConstantComposite %31 %61 %61 %61 %61 %211 = OpConstantComposite %34 %209 %210 -%215 = OpTypePointer Input %3 -%214 = OpVariable %215 Input -%218 = OpTypePointer Output %31 -%217 = OpVariable %218 Output -%221 = OpTypePointer StorageBuffer %23 -%224 = OpConstant %8 0.0 -%225 = OpConstant %3 3 -%226 = OpConstant %5 3 -%227 = OpConstant %5 4 -%228 = OpConstant %5 5 -%229 = OpConstant %5 42 -%230 = OpConstantNull %29 -%233 = OpTypePointer Function %32 -%234 = OpConstantNull %32 -%239 = OpTypePointer StorageBuffer %9 -%242 = OpTypePointer StorageBuffer %18 -%243 = OpConstant %3 4 -%246 = OpTypePointer StorageBuffer %10 -%247 = OpTypePointer StorageBuffer %8 -%250 = OpTypePointer StorageBuffer %19 -%253 = OpTypePointer StorageBuffer %7 -%254 = OpTypePointer StorageBuffer %5 -%266 = OpTypeVector %5 4 -%272 = OpVariable %218 Output -%275 = OpConstantComposite %10 %224 %224 %224 -%276 = OpConstantComposite %10 %59 %59 %59 -%277 = OpConstantComposite %10 %61 %61 %61 -%278 = OpConstantComposite %10 %63 %63 %63 -%279 = OpConstantComposite %9 %275 %276 %277 %278 -%280 = OpConstantComposite %17 %36 %36 -%281 = OpConstantComposite %17 %99 %99 -%282 = OpConstantComposite %18 %280 %281 -%283 = OpConstantNull %23 -%284 = OpConstantComposite %31 %224 %224 %224 %224 -%292 = OpConstant %3 33 -%293 = OpConstantComposite %31 %67 %67 %67 %67 -%294 = OpConstantComposite %31 %137 %137 %137 %137 -%295 = OpConstantComposite %34 %293 %294 +%217 = OpTypeFunction %5 %32 %5 +%219 = OpTypePointer Function %32 +%225 = OpTypePointer Input %3 +%224 = OpVariable %225 Input +%228 = OpTypePointer Output %31 +%227 = OpVariable %228 Output +%231 = OpTypePointer StorageBuffer %23 +%234 = OpConstant %8 0.0 +%235 = OpConstant %3 3 +%236 = OpConstant %5 3 +%237 = OpConstant %5 4 +%238 = OpConstant %5 5 +%239 = OpConstant %5 42 +%240 = OpConstantNull %29 +%243 = OpConstantNull %32 +%248 = OpTypePointer StorageBuffer %9 +%251 = OpTypePointer StorageBuffer %18 +%252 = OpConstant %3 4 +%255 = OpTypePointer StorageBuffer %10 +%256 = OpTypePointer StorageBuffer %8 +%259 = OpTypePointer StorageBuffer %19 +%262 = OpTypePointer StorageBuffer %7 +%263 = OpTypePointer StorageBuffer %5 +%275 = OpTypeVector %5 4 +%281 = OpVariable %228 Output +%284 = OpConstantComposite %10 %234 %234 %234 +%285 = OpConstantComposite %10 %59 %59 %59 +%286 = OpConstantComposite %10 %61 %61 %61 +%287 = OpConstantComposite %10 %63 %63 %63 +%288 = OpConstantComposite %9 %284 %285 %286 %287 +%289 = OpConstantComposite %17 %36 %36 +%290 = OpConstantComposite %17 %99 %99 +%291 = OpConstantComposite %18 %289 %290 +%292 = OpConstantNull %23 +%293 = OpConstantComposite %31 %234 %234 %234 %234 +%301 = OpConstant %3 33 +%302 = OpConstantComposite %31 %67 %67 %67 %67 +%303 = OpConstantComposite %31 %137 %137 %137 %137 +%304 = OpConstantComposite %34 %302 %303 +%311 = OpVariable %225 Input +%313 = OpVariable %228 Output +%315 = OpConstant %5 2 +%316 = OpConstantComposite %32 %58 %315 %236 %237 %238 %54 = OpFunction %2 None %55 %53 = OpLabel %82 = OpVariable %83 Function %58 @@ -390,72 +403,98 @@ OpBranch %212 OpStore %206 %211 OpReturn OpFunctionEnd -%219 = OpFunction %2 None %55 +%216 = OpFunction %5 None %217 +%214 = OpFunctionParameter %32 +%215 = OpFunctionParameter %5 %213 = OpLabel -%231 = OpVariable %27 Function %224 -%232 = OpVariable %233 Function %234 -%216 = OpLoad %3 %214 -%220 = OpAccessChain %56 %44 %36 -%222 = OpAccessChain %221 %47 %36 -%223 = OpAccessChain %131 %50 %36 -OpBranch %235 -%235 = OpLabel -%236 = OpLoad %8 %231 -OpStore %231 %59 -%237 = OpFunctionCall %2 %54 -%238 = OpFunctionCall %2 %130 -%240 = OpAccessChain %239 %42 %36 -%241 = OpLoad %9 %240 -%244 = OpAccessChain %242 %42 %243 -%245 = OpLoad %18 %244 -%248 = OpAccessChain %247 %42 %36 %225 %36 -%249 = OpLoad %8 %248 -%251 = OpArrayLength %3 %42 5 -%252 = OpISub %3 %251 %14 -%255 = OpAccessChain %254 %42 %30 %252 %36 -%256 = OpLoad %5 %255 -%257 = OpLoad %23 %222 -%258 = OpFunctionCall %8 %188 %231 -%259 = OpConvertFToS %5 %249 -%260 = OpCompositeConstruct %32 %256 %259 %226 %227 %228 -OpStore %232 %260 -%261 = OpIAdd %3 %216 %99 -%262 = OpAccessChain %83 %232 %261 -OpStore %262 %229 -%263 = OpAccessChain %83 %232 %216 -%264 = OpLoad %5 %263 -%265 = OpFunctionCall %8 %194 %230 -%267 = OpCompositeConstruct %266 %264 %264 %264 %264 -%268 = OpConvertSToF %31 %267 -%269 = OpMatrixTimesVector %10 %241 %268 -%270 = OpCompositeConstruct %31 %269 %61 -OpStore %217 %270 +%220 = OpVariable %219 Function +OpBranch %218 +%218 = OpLabel +OpStore %220 %214 +%221 = OpAccessChain %83 %220 %215 +%222 = OpLoad %5 %221 +OpReturnValue %222 +OpFunctionEnd +%229 = OpFunction %2 None %55 +%223 = OpLabel +%241 = OpVariable %27 Function %234 +%242 = OpVariable %219 Function %243 +%226 = OpLoad %3 %224 +%230 = OpAccessChain %56 %44 %36 +%232 = OpAccessChain %231 %47 %36 +%233 = OpAccessChain %131 %50 %36 +OpBranch %244 +%244 = OpLabel +%245 = OpLoad %8 %241 +OpStore %241 %59 +%246 = OpFunctionCall %2 %54 +%247 = OpFunctionCall %2 %130 +%249 = OpAccessChain %248 %42 %36 +%250 = OpLoad %9 %249 +%253 = OpAccessChain %251 %42 %252 +%254 = OpLoad %18 %253 +%257 = OpAccessChain %256 %42 %36 %235 %36 +%258 = OpLoad %8 %257 +%260 = OpArrayLength %3 %42 5 +%261 = OpISub %3 %260 %14 +%264 = OpAccessChain %263 %42 %30 %261 %36 +%265 = OpLoad %5 %264 +%266 = OpLoad %23 %232 +%267 = OpFunctionCall %8 %188 %241 +%268 = OpConvertFToS %5 %258 +%269 = OpCompositeConstruct %32 %265 %268 %236 %237 %238 +OpStore %242 %269 +%270 = OpIAdd %3 %226 %99 +%271 = OpAccessChain %83 %242 %270 +OpStore %271 %239 +%272 = OpAccessChain %83 %242 %226 +%273 = OpLoad %5 %272 +%274 = OpFunctionCall %8 %194 %240 +%276 = OpCompositeConstruct %275 %273 %273 %273 %273 +%277 = OpConvertSToF %31 %276 +%278 = OpMatrixTimesVector %10 %250 %277 +%279 = OpCompositeConstruct %31 %278 %61 +OpStore %227 %279 OpReturn OpFunctionEnd -%273 = OpFunction %2 None %55 -%271 = OpLabel -%274 = OpAccessChain %221 %47 %36 -OpBranch %285 -%285 = OpLabel -%286 = OpAccessChain %247 %42 %36 %99 %14 -OpStore %286 %59 -%287 = OpAccessChain %239 %42 %36 -OpStore %287 %279 -%288 = OpAccessChain %242 %42 %243 -OpStore %288 %282 -%289 = OpAccessChain %254 %42 %30 %99 %36 -OpStore %289 %58 -OpStore %274 %283 -OpStore %272 %284 +%282 = OpFunction %2 None %55 +%280 = OpLabel +%283 = OpAccessChain %231 %47 %36 +OpBranch %294 +%294 = OpLabel +%295 = OpAccessChain %256 %42 %36 %99 %14 +OpStore %295 %59 +%296 = OpAccessChain %248 %42 %36 +OpStore %296 %288 +%297 = OpAccessChain %251 %42 %252 +OpStore %297 %291 +%298 = OpAccessChain %263 %42 %30 %99 %36 +OpStore %298 %58 +OpStore %283 %292 +OpStore %281 %293 OpReturn OpFunctionEnd -%291 = OpFunction %2 None %55 -%290 = OpLabel -%296 = OpVariable %33 Function %292 -%297 = OpVariable %35 Function %295 -OpBranch %298 -%298 = OpLabel -%299 = OpFunctionCall %2 %201 %296 -%300 = OpFunctionCall %2 %207 %297 +%300 = OpFunction %2 None %55 +%299 = OpLabel +%305 = OpVariable %33 Function %301 +%306 = OpVariable %35 Function %304 +OpBranch %307 +%307 = OpLabel +%308 = OpFunctionCall %2 %201 %305 +%309 = OpFunctionCall %2 %207 %306 +OpReturn +OpFunctionEnd +%314 = OpFunction %2 None %55 +%310 = OpLabel +%318 = OpVariable %219 Function +%312 = OpLoad %3 %311 +OpBranch %317 +%317 = OpLabel +OpStore %318 %316 +%319 = OpAccessChain %83 %318 %312 +%320 = OpLoad %5 %319 +%321 = OpCompositeConstruct %275 %320 %320 %320 %320 +%322 = OpConvertSToF %31 %321 +OpStore %313 %322 OpReturn OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/out/wgsl/access.wgsl b/naga/tests/out/wgsl/access.wgsl index 1409e80b1..0c29e5d60 100644 --- a/naga/tests/out/wgsl/access.wgsl +++ b/naga/tests/out/wgsl/access.wgsl @@ -107,8 +107,8 @@ fn test_matrix_within_array_within_struct_accesses() { return; } -fn read_from_private(foo_1: ptr) -> f32 { - let _e1 = (*foo_1); +fn read_from_private(foo_2: ptr) -> f32 { + let _e1 = (*foo_2); return _e1; } @@ -121,11 +121,15 @@ fn assign_through_ptr_fn(p: ptr) { return; } -fn assign_array_through_ptr_fn(foo_2: ptr, 2>>) { - (*foo_2) = array, 2>(vec4(1f), vec4(2f)); +fn assign_array_through_ptr_fn(foo_3: ptr, 2>>) { + (*foo_3) = array, 2>(vec4(1f), vec4(2f)); return; } +fn array_by_value(a_1: array, i: i32) -> i32 { + return a_1[i]; +} + @vertex fn foo_vert(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4 { var foo: f32 = 0f; @@ -138,11 +142,11 @@ fn foo_vert(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4 { let _matrix = bar._matrix; let arr_1 = bar.arr; let b = bar._matrix[3u][0]; - let a_1 = bar.data[(arrayLength((&bar.data)) - 2u)].value; + let a_2 = bar.data[(arrayLength((&bar.data)) - 2u)].value; let c = qux; let data_pointer = (&bar.data[0].value); let _e33 = read_from_private((&foo)); - c2_ = array(a_1, i32(b), 3i, 4i, 5i); + c2_ = array(a_2, i32(b), 3i, 4i, 5i); c2_[(vi + 1u)] = 42i; let value = c2_[vi]; let _e47 = test_arr_as_arg(array, 5>()); @@ -168,3 +172,10 @@ fn assign_through_ptr() { assign_array_through_ptr_fn((&arr)); return; } + +@vertex +fn foo_1(@builtin(vertex_index) vi_1: u32) -> @builtin(position) vec4 { + const arr_2 = array(1i, 2i, 3i, 4i, 5i); + let value_1 = arr_2[vi_1]; + return vec4(vec4(value_1)); +} diff --git a/naga/tests/wgsl_errors.rs b/naga/tests/wgsl_errors.rs index 2d91ba01c..e5fb77644 100644 --- a/naga/tests/wgsl_errors.rs +++ b/naga/tests/wgsl_errors.rs @@ -1359,11 +1359,6 @@ fn missing_bindings2() { #[test] fn invalid_access() { check_validation! { - " - fn array_by_value(a: array, i: i32) -> i32 { - return a[i]; - } - ", " fn matrix_by_value(m: mat4x4, i: i32) -> vec4 { return m[i]; From 215f0fc8874bb8238a357ff9280fade77f62e886 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Mon, 30 Sep 2024 13:54:02 -0700 Subject: [PATCH 50/59] [naga spv-out] Internal doc fixes for `back::spv::index`. In `naga::back::spv::index`, clarify documentation for: - `BoundsCheckResult` - `write_index_comparison` - `write_bounds_check` --- naga/src/back/spv/index.rs | 39 +++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/naga/src/back/spv/index.rs b/naga/src/back/spv/index.rs index 0295d895b..618770a5d 100644 --- a/naga/src/back/spv/index.rs +++ b/naga/src/back/spv/index.rs @@ -11,16 +11,31 @@ use crate::{arena::Handle, proc::BoundsCheckPolicy}; /// The results of performing a bounds check. /// -/// On success, `write_bounds_check` returns a value of this type. +/// On success, `write_bounds_check` returns a value of this type. The +/// caller can assume that the right policy has been applied, and +/// simply do what the variant says. pub(super) enum BoundsCheckResult { /// The index is statically known and in bounds, with the given value. KnownInBounds(u32), /// The given instruction computes the index to be used. + /// + /// When [`BoundsCheckPolicy::Restrict`] is in force, this is a + /// clamped version of the index the user supplied. + /// + /// When [`BoundsCheckPolicy::Unchecked`] is in force, this is + /// simply the index the user supplied. This variant indicates + /// that we couldn't prove statically that the index was in + /// bounds; otherwise we would have returned [`KnownInBounds`]. + /// + /// [`KnownInBounds`]: BoundsCheckResult::KnownInBounds Computed(Word), /// The given instruction computes a boolean condition which is true /// if the index is in bounds. + /// + /// This is returned when [`BoundsCheckPolicy::ReadZeroSkipWrite`] + /// is in force. Conditional(Word), } @@ -357,6 +372,8 @@ impl<'w> BlockContext<'w> { /// Write an index bounds comparison to `block`, if needed. /// + /// This is used to implement [`BoundsCheckPolicy::ReadZeroSkipWrite`]. + /// /// If we're able to determine statically that `index` is in bounds for /// `sequence`, return `KnownInBounds(value)`, where `value` is the actual /// value of the index. (In principle, one could know that the index is in @@ -477,11 +494,23 @@ impl<'w> BlockContext<'w> { /// Emit code for bounds checks for an array, vector, or matrix access. /// - /// This implements either `index_bounds_check_policy` or - /// `buffer_bounds_check_policy`, depending on the address space of the - /// pointer being accessed. + /// This tries to handle all the critical steps for bounds checks: /// - /// Return a `BoundsCheckResult` indicating how the index should be + /// - First, select the appropriate bounds check policy for `base`, + /// depending on its address space. + /// + /// - Next, analyze `index` to see if its value is known at + /// compile time, in which case we can decide statically whether + /// the index is in bounds. + /// + /// - If the index's value is not known at compile time, emit code to: + /// + /// - restrict its value (for [`BoundsCheckPolicy::Restrict`]), or + /// + /// - check whether it's in bounds (for + /// [`BoundsCheckPolicy::ReadZeroSkipWrite`]). + /// + /// Return a [`BoundsCheckResult`] indicating how the index should be /// consumed. See that type's documentation for details. pub(super) fn write_bounds_check( &mut self, From 3d584f99edfc57055000fdc88c3131f5bacf3ee7 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Wed, 2 Oct 2024 22:16:53 -0400 Subject: [PATCH 51/59] refactor(spv-out): linkify docs. ref. to `write_bounds_check` --- naga/src/back/spv/index.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/naga/src/back/spv/index.rs b/naga/src/back/spv/index.rs index 618770a5d..832f806fd 100644 --- a/naga/src/back/spv/index.rs +++ b/naga/src/back/spv/index.rs @@ -11,9 +11,9 @@ use crate::{arena::Handle, proc::BoundsCheckPolicy}; /// The results of performing a bounds check. /// -/// On success, `write_bounds_check` returns a value of this type. The -/// caller can assume that the right policy has been applied, and -/// simply do what the variant says. +/// On success, [`write_bounds_check`](BlockContext::write_bounds_check) +/// returns a value of this type. The caller can assume that the right +/// policy has been applied, and simply do what the variant says. pub(super) enum BoundsCheckResult { /// The index is statically known and in bounds, with the given value. KnownInBounds(u32), From 78928654a29acd87b262e211b7bea124821f238e Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 1 Oct 2024 11:37:53 -0400 Subject: [PATCH 52/59] style(wgsl-in): unblock `rustfmt` in `Error::as_parse_error` --- naga/src/front/wgsl/error.rs | 186 ++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 79 deletions(-) diff --git a/naga/src/front/wgsl/error.rs b/naga/src/front/wgsl/error.rs index a7986ec89..7c65d93de 100644 --- a/naga/src/front/wgsl/error.rs +++ b/naga/src/front/wgsl/error.rs @@ -298,32 +298,42 @@ impl<'a> Error<'a> { match *self { Error::Unexpected(unexpected_span, expected) => { let expected_str = match expected { - ExpectedToken::Token(token) => { - match token { - Token::Separator(c) => format!("'{c}'"), - Token::Paren(c) => format!("'{c}'"), - Token::Attribute => "@".to_string(), - Token::Number(_) => "number".to_string(), - Token::Word(s) => s.to_string(), - Token::Operation(c) => format!("operation ('{c}')"), - Token::LogicalOperation(c) => format!("logical operation ('{c}')"), - Token::ShiftOperation(c) => format!("bitshift ('{c}{c}')"), - Token::AssignmentOperation(c) if c=='<' || c=='>' => format!("bitshift ('{c}{c}=')"), - Token::AssignmentOperation(c) => format!("operation ('{c}=')"), - Token::IncrementOperation => "increment operation".to_string(), - Token::DecrementOperation => "decrement operation".to_string(), - Token::Arrow => "->".to_string(), - Token::Unknown(c) => format!("unknown ('{c}')"), - Token::Trivia => "trivia".to_string(), - Token::End => "end".to_string(), + ExpectedToken::Token(token) => match token { + Token::Separator(c) => format!("'{c}'"), + Token::Paren(c) => format!("'{c}'"), + Token::Attribute => "@".to_string(), + Token::Number(_) => "number".to_string(), + Token::Word(s) => s.to_string(), + Token::Operation(c) => format!("operation ('{c}')"), + Token::LogicalOperation(c) => format!("logical operation ('{c}')"), + Token::ShiftOperation(c) => format!("bitshift ('{c}{c}')"), + Token::AssignmentOperation(c) if c == '<' || c == '>' => { + format!("bitshift ('{c}{c}=')") } - } + Token::AssignmentOperation(c) => format!("operation ('{c}=')"), + Token::IncrementOperation => "increment operation".to_string(), + Token::DecrementOperation => "decrement operation".to_string(), + Token::Arrow => "->".to_string(), + Token::Unknown(c) => format!("unknown ('{c}')"), + Token::Trivia => "trivia".to_string(), + Token::End => "end".to_string(), + }, ExpectedToken::Identifier => "identifier".to_string(), ExpectedToken::PrimaryExpression => "expression".to_string(), ExpectedToken::Assignment => "assignment or increment/decrement".to_string(), - ExpectedToken::SwitchItem => "switch item ('case' or 'default') or a closing curly bracket to signify the end of the switch statement ('}')".to_string(), - ExpectedToken::WorkgroupSizeSeparator => "workgroup size separator (',') or a closing parenthesis".to_string(), - ExpectedToken::GlobalItem => "global item ('struct', 'const', 'var', 'alias', ';', 'fn') or the end of the file".to_string(), + ExpectedToken::SwitchItem => concat!( + "switch item ('case' or 'default') or a closing curly bracket ", + "to signify the end of the switch statement ('}')" + ) + .to_string(), + ExpectedToken::WorkgroupSizeSeparator => { + "workgroup size separator (',') or a closing parenthesis".to_string() + } + ExpectedToken::GlobalItem => concat!( + "global item ('struct', 'const', 'var', 'alias', ';', 'fn') ", + "or the end of the file" + ) + .to_string(), ExpectedToken::Type => "type".to_string(), ExpectedToken::Variable => "variable access".to_string(), ExpectedToken::Function => "function name".to_string(), @@ -384,9 +394,11 @@ impl<'a> Error<'a> { notes: vec![], }, Error::BadIncrDecrReferenceType(span) => ParseError { - message: - "increment/decrement operation requires reference type to be one of i32 or u32" - .to_string(), + message: concat!( + "increment/decrement operation requires ", + "reference type to be one of i32 or u32" + ) + .to_string(), labels: vec![(span, "must be a reference type of i32 or u32".into())], notes: vec![], }, @@ -527,25 +539,24 @@ impl<'a> Error<'a> { labels: vec![(span, "type can't be inferred".into())], notes: vec![], }, - Error::InitializationTypeMismatch { name, ref expected, ref got } => { - ParseError { - message: format!( - "the type of `{}` is expected to be `{}`, but got `{}`", - &source[name], expected, got, - ), - labels: vec![( - name, - format!("definition of `{}`", &source[name]).into(), - )], - notes: vec![], - } - } + Error::InitializationTypeMismatch { + name, + ref expected, + ref got, + } => ParseError { + message: format!( + "the type of `{}` is expected to be `{}`, but got `{}`", + &source[name], expected, got, + ), + labels: vec![(name, format!("definition of `{}`", &source[name]).into())], + notes: vec![], + }, Error::DeclMissingTypeAndInit(name_span) => ParseError { - message: format!("declaration of `{}` needs a type specifier or initializer", &source[name_span]), - labels: vec![( - name_span, - "needs a type specifier or initializer".into(), - )], + message: format!( + "declaration of `{}` needs a type specifier or initializer", + &source[name_span] + ), + labels: vec![(name_span, "needs a type specifier or initializer".into())], notes: vec![], }, Error::MissingAttribute(name, name_span) => ParseError { @@ -725,7 +736,11 @@ impl<'a> Error<'a> { notes: vec![message.into()], }, Error::ExpectedConstExprConcreteIntegerScalar(span) => ParseError { - message: "must be a const-expression that resolves to a concrete integer scalar (u32 or i32)".to_string(), + message: concat!( + "must be a const-expression that ", + "resolves to a concrete integer scalar (u32 or i32)" + ) + .to_string(), labels: vec![(span, "must resolve to u32 or i32".into())], notes: vec![], }, @@ -754,9 +769,17 @@ impl<'a> Error<'a> { }, Error::AutoConversion(ref error) => { // destructuring ensures all fields are handled - let AutoConversionError { dest_span, ref dest_type, source_span, ref source_type } = **error; + let AutoConversionError { + dest_span, + ref dest_type, + source_span, + ref source_type, + } = **error; ParseError { - message: format!("automatic conversions cannot convert `{source_type}` to `{dest_type}`"), + message: format!( + "automatic conversions cannot convert `{}` to `{}`", + source_type, dest_type + ), labels: vec![ ( dest_span, @@ -765,72 +788,77 @@ impl<'a> Error<'a> { ( source_span, format!("this expression has type {source_type}").into(), - ) + ), ], notes: vec![], } - }, + } Error::AutoConversionLeafScalar(ref error) => { - let AutoConversionLeafScalarError { dest_span, ref dest_scalar, source_span, ref source_type } = **error; + let AutoConversionLeafScalarError { + dest_span, + ref dest_scalar, + source_span, + ref source_type, + } = **error; ParseError { - message: format!("automatic conversions cannot convert elements of `{source_type}` to `{dest_scalar}`"), + message: format!( + "automatic conversions cannot convert elements of `{}` to `{}`", + source_type, dest_scalar + ), labels: vec![ ( dest_span, - format!("a value with elements of type {dest_scalar} is required here").into(), + format!( + "a value with elements of type {} is required here", + dest_scalar + ) + .into(), ), ( source_span, format!("this expression has type {source_type}").into(), - ) + ), ], notes: vec![], } - }, + } Error::ConcretizationFailed(ref error) => { - let ConcretizationFailedError { expr_span, ref expr_type, ref scalar, ref inner } = **error; + let ConcretizationFailedError { + expr_span, + ref expr_type, + ref scalar, + ref inner, + } = **error; ParseError { message: format!("failed to convert expression to a concrete type: {inner}"), - labels: vec![ - ( - expr_span, - format!("this expression has type {expr_type}").into(), - ) - ], - notes: vec![ - format!("the expression should have been converted to have {} scalar type", scalar), - ] + labels: vec![( + expr_span, + format!("this expression has type {expr_type}").into(), + )], + notes: vec![format!( + "the expression should have been converted to have {} scalar type", + scalar + )], } - }, + } Error::ExceededLimitForNestedBraces { span, limit } => ParseError { message: "brace nesting limit reached".into(), labels: vec![(span, "limit reached at this brace".into())], - notes: vec![ - format!("nesting limit is currently set to {limit}"), - ], + notes: vec![format!("nesting limit is currently set to {limit}")], }, Error::PipelineConstantIDValue(span) => ParseError { message: "pipeline constant ID must be between 0 and 65535 inclusive".to_string(), - labels: vec![( - span, - "must be between 0 and 65535 inclusive".into(), - )], + labels: vec![(span, "must be between 0 and 65535 inclusive".into())], notes: vec![], }, Error::NotBool(span) => ParseError { message: "must be a const-expression that resolves to a bool".to_string(), - labels: vec![( - span, - "must resolve to bool".into(), - )], + labels: vec![(span, "must resolve to bool".into())], notes: vec![], }, Error::ConstAssertFailed(span) => ParseError { message: "const_assert failure".to_string(), - labels: vec![( - span, - "evaluates to false".into(), - )], + labels: vec![(span, "evaluates to false".into())], notes: vec![], }, } From 25b2b6afa16090ab6bf80b3b2e317107b3ff187a Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Mon, 30 Sep 2024 09:29:13 -0400 Subject: [PATCH 53/59] style: newline b/w `Span::{UNDEFINED, new}` --- naga/src/span.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/naga/src/span.rs b/naga/src/span.rs index 82cfbe5a4..f8a0f67fb 100644 --- a/naga/src/span.rs +++ b/naga/src/span.rs @@ -11,6 +11,7 @@ pub struct Span { impl Span { pub const UNDEFINED: Self = Self { start: 0, end: 0 }; + /// Creates a new `Span` from a range of byte indices /// /// Note: end is exclusive, it doesn't belong to the `Span` From 9f275f7655f1c1bcf1116fdc486daf230a230c6c Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 1 Oct 2024 15:02:51 -0400 Subject: [PATCH 54/59] style: remove comments from `MathFunction` type res. that stop `rustfmt` --- naga/src/proc/typifier.rs | 283 +++++++++++++++++++------------------- 1 file changed, 145 insertions(+), 138 deletions(-) diff --git a/naga/src/proc/typifier.rs b/naga/src/proc/typifier.rs index f29ff40f0..04c776365 100644 --- a/naga/src/proc/typifier.rs +++ b/naga/src/proc/typifier.rs @@ -638,41 +638,37 @@ impl<'a> ResolveContext<'a> { use crate::MathFunction as Mf; let res_arg = past(arg)?; match fun { - // comparison - Mf::Abs | - Mf::Min | - Mf::Max | - Mf::Clamp | - Mf::Saturate | - // trigonometry - Mf::Cos | - Mf::Cosh | - Mf::Sin | - Mf::Sinh | - Mf::Tan | - Mf::Tanh | - Mf::Acos | - Mf::Asin | - Mf::Atan | - Mf::Atan2 | - Mf::Asinh | - Mf::Acosh | - Mf::Atanh | - Mf::Radians | - Mf::Degrees | - // decomposition - Mf::Ceil | - Mf::Floor | - Mf::Round | - Mf::Fract | - Mf::Trunc | - Mf::Ldexp | - // exponent - Mf::Exp | - Mf::Exp2 | - Mf::Log | - Mf::Log2 | - Mf::Pow => res_arg.clone(), + Mf::Abs + | Mf::Min + | Mf::Max + | Mf::Clamp + | Mf::Saturate + | Mf::Cos + | Mf::Cosh + | Mf::Sin + | Mf::Sinh + | Mf::Tan + | Mf::Tanh + | Mf::Acos + | Mf::Asin + | Mf::Atan + | Mf::Atan2 + | Mf::Asinh + | Mf::Acosh + | Mf::Atanh + | Mf::Radians + | Mf::Degrees + | Mf::Ceil + | Mf::Floor + | Mf::Round + | Mf::Fract + | Mf::Trunc + | Mf::Ldexp + | Mf::Exp + | Mf::Exp2 + | Mf::Log + | Mf::Log2 + | Mf::Pow => res_arg.clone(), Mf::Modf | Mf::Frexp => { let (size, width) = match res_arg.inner_with(types) { &Ti::Scalar(crate::Scalar { @@ -680,77 +676,81 @@ impl<'a> ResolveContext<'a> { width, }) => (None, width), &Ti::Vector { - scalar: crate::Scalar { - kind: crate::ScalarKind::Float, - width, - }, + scalar: + crate::Scalar { + kind: crate::ScalarKind::Float, + width, + }, size, } => (Some(size), width), - ref other => - return Err(ResolveError::IncompatibleOperands(format!("{fun:?}({other:?}, _)"))) + ref other => { + return Err(ResolveError::IncompatibleOperands(format!( + "{fun:?}({other:?}, _)" + ))) + } }; let result = self - .special_types - .predeclared_types - .get(&if fun == Mf::Modf { - crate::PredeclaredType::ModfResult { size, width } - } else { - crate::PredeclaredType::FrexpResult { size, width } - }) - .ok_or(ResolveError::MissingSpecialType)?; + .special_types + .predeclared_types + .get(&if fun == Mf::Modf { + crate::PredeclaredType::ModfResult { size, width } + } else { + crate::PredeclaredType::FrexpResult { size, width } + }) + .ok_or(ResolveError::MissingSpecialType)?; TypeResolution::Handle(*result) - }, - // geometry + } Mf::Dot => match *res_arg.inner_with(types) { - Ti::Vector { - size: _, - scalar, - } => TypeResolution::Value(Ti::Scalar(scalar)), - ref other => - return Err(ResolveError::IncompatibleOperands( - format!("{fun:?}({other:?}, _)") - )), + Ti::Vector { size: _, scalar } => TypeResolution::Value(Ti::Scalar(scalar)), + ref other => { + return Err(ResolveError::IncompatibleOperands(format!( + "{fun:?}({other:?}, _)" + ))) + } }, Mf::Outer => { - let arg1 = arg1.ok_or_else(|| ResolveError::IncompatibleOperands( - format!("{fun:?}(_, None)") - ))?; + let arg1 = arg1.ok_or_else(|| { + ResolveError::IncompatibleOperands(format!("{fun:?}(_, None)")) + })?; match (res_arg.inner_with(types), past(arg1)?.inner_with(types)) { ( - &Ti::Vector { size: columns, scalar }, - &Ti::Vector{ size: rows, .. } + &Ti::Vector { + size: columns, + scalar, + }, + &Ti::Vector { size: rows, .. }, ) => TypeResolution::Value(Ti::Matrix { columns, rows, scalar, }), - (left, right) => - return Err(ResolveError::IncompatibleOperands( - format!("{fun:?}({left:?}, {right:?})") - )), + (left, right) => { + return Err(ResolveError::IncompatibleOperands(format!( + "{fun:?}({left:?}, {right:?})" + ))) + } + } + } + Mf::Cross => res_arg.clone(), + Mf::Distance | Mf::Length => match *res_arg.inner_with(types) { + Ti::Scalar(scalar) | Ti::Vector { scalar, size: _ } => { + TypeResolution::Value(Ti::Scalar(scalar)) + } + ref other => { + return Err(ResolveError::IncompatibleOperands(format!( + "{fun:?}({other:?})" + ))) } }, - Mf::Cross => res_arg.clone(), - Mf::Distance | - Mf::Length => match *res_arg.inner_with(types) { - Ti::Scalar(scalar) | - Ti::Vector {scalar,size:_} => TypeResolution::Value(Ti::Scalar(scalar)), - ref other => return Err(ResolveError::IncompatibleOperands( - format!("{fun:?}({other:?})") - )), - }, - Mf::Normalize | - Mf::FaceForward | - Mf::Reflect | - Mf::Refract => res_arg.clone(), + Mf::Normalize | Mf::FaceForward | Mf::Reflect | Mf::Refract => res_arg.clone(), // computational - Mf::Sign | - Mf::Fma | - Mf::Mix | - Mf::Step | - Mf::SmoothStep | - Mf::Sqrt | - Mf::InverseSqrt => res_arg.clone(), + Mf::Sign + | Mf::Fma + | Mf::Mix + | Mf::Step + | Mf::SmoothStep + | Mf::Sqrt + | Mf::InverseSqrt => res_arg.clone(), Mf::Transpose => match *res_arg.inner_with(types) { Ti::Matrix { columns, @@ -761,9 +761,11 @@ impl<'a> ResolveContext<'a> { rows: columns, scalar, }), - ref other => return Err(ResolveError::IncompatibleOperands( - format!("{fun:?}({other:?})") - )), + ref other => { + return Err(ResolveError::IncompatibleOperands(format!( + "{fun:?}({other:?})" + ))) + } }, Mf::Inverse => match *res_arg.inner_with(types) { Ti::Matrix { @@ -775,70 +777,75 @@ impl<'a> ResolveContext<'a> { rows, scalar, }), - ref other => return Err(ResolveError::IncompatibleOperands( - format!("{fun:?}({other:?})") - )), + ref other => { + return Err(ResolveError::IncompatibleOperands(format!( + "{fun:?}({other:?})" + ))) + } }, Mf::Determinant => match *res_arg.inner_with(types) { - Ti::Matrix { - scalar, - .. - } => TypeResolution::Value(Ti::Scalar(scalar)), - ref other => return Err(ResolveError::IncompatibleOperands( - format!("{fun:?}({other:?})") - )), + Ti::Matrix { scalar, .. } => TypeResolution::Value(Ti::Scalar(scalar)), + ref other => { + return Err(ResolveError::IncompatibleOperands(format!( + "{fun:?}({other:?})" + ))) + } }, // bits - Mf::CountTrailingZeros | - Mf::CountLeadingZeros | - Mf::CountOneBits | - Mf::ReverseBits | - Mf::ExtractBits | - Mf::InsertBits | - Mf::FirstTrailingBit | - Mf::FirstLeadingBit => match *res_arg.inner_with(types) { - Ti::Scalar(scalar @ crate::Scalar { - kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint, - .. - }) => TypeResolution::Value(Ti::Scalar(scalar)), - Ti::Vector { - size, - scalar: scalar @ crate::Scalar { + Mf::CountTrailingZeros + | Mf::CountLeadingZeros + | Mf::CountOneBits + | Mf::ReverseBits + | Mf::ExtractBits + | Mf::InsertBits + | Mf::FirstTrailingBit + | Mf::FirstLeadingBit => match *res_arg.inner_with(types) { + Ti::Scalar( + scalar @ crate::Scalar { kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint, .. - } + }, + ) => TypeResolution::Value(Ti::Scalar(scalar)), + Ti::Vector { + size, + scalar: + scalar @ crate::Scalar { + kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint, + .. + }, } => TypeResolution::Value(Ti::Vector { size, scalar }), - ref other => return Err(ResolveError::IncompatibleOperands( - format!("{fun:?}({other:?})") - )), + ref other => { + return Err(ResolveError::IncompatibleOperands(format!( + "{fun:?}({other:?})" + ))) + } }, // data packing - Mf::Pack4x8snorm | - Mf::Pack4x8unorm | - Mf::Pack2x16snorm | - Mf::Pack2x16unorm | - Mf::Pack2x16float | - Mf::Pack4xI8 | - Mf::Pack4xU8 => TypeResolution::Value(Ti::Scalar(crate::Scalar::U32)), + Mf::Pack4x8snorm + | Mf::Pack4x8unorm + | Mf::Pack2x16snorm + | Mf::Pack2x16unorm + | Mf::Pack2x16float + | Mf::Pack4xI8 + | Mf::Pack4xU8 => TypeResolution::Value(Ti::Scalar(crate::Scalar::U32)), // data unpacking - Mf::Unpack4x8snorm | - Mf::Unpack4x8unorm => TypeResolution::Value(Ti::Vector { + Mf::Unpack4x8snorm | Mf::Unpack4x8unorm => TypeResolution::Value(Ti::Vector { size: crate::VectorSize::Quad, - scalar: crate::Scalar::F32 - }), - Mf::Unpack2x16snorm | - Mf::Unpack2x16unorm | - Mf::Unpack2x16float => TypeResolution::Value(Ti::Vector { - size: crate::VectorSize::Bi, - scalar: crate::Scalar::F32 + scalar: crate::Scalar::F32, }), + Mf::Unpack2x16snorm | Mf::Unpack2x16unorm | Mf::Unpack2x16float => { + TypeResolution::Value(Ti::Vector { + size: crate::VectorSize::Bi, + scalar: crate::Scalar::F32, + }) + } Mf::Unpack4xI8 => TypeResolution::Value(Ti::Vector { size: crate::VectorSize::Quad, - scalar: crate::Scalar::I32 + scalar: crate::Scalar::I32, }), Mf::Unpack4xU8 => TypeResolution::Value(Ti::Vector { size: crate::VectorSize::Quad, - scalar: crate::Scalar::U32 + scalar: crate::Scalar::U32, }), } } From 6e528aaf2416eb591e727596c3752f56b5d77df1 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 1 Oct 2024 15:02:51 -0400 Subject: [PATCH 55/59] style: split (most) string literals over 100-column line width limit --- naga/src/back/pipeline_constants.rs | 5 +++- naga/src/front/glsl/context.rs | 9 +++++--- naga/src/front/glsl/functions.rs | 3 ++- naga/src/front/glsl/parser/expressions.rs | 8 ++++++- naga/src/front/glsl/variables.rs | 19 ++++++++------- naga/src/front/spv/error.rs | 8 ++++++- wgpu-hal/examples/ray-traced-triangle/main.rs | 11 +++++++-- wgpu-hal/src/auxil/renderdoc.rs | 6 +++-- wgpu-hal/src/gles/mod.rs | 23 +++++++++++++++---- wgpu-hal/src/gles/web.rs | 7 +++--- wgpu-hal/src/vulkan/instance.rs | 12 ++++++++-- wgpu-hal/src/vulkan/mod.rs | 8 ++++++- 12 files changed, 89 insertions(+), 30 deletions(-) diff --git a/naga/src/back/pipeline_constants.rs b/naga/src/back/pipeline_constants.rs index 5f82862f7..0005cbcb0 100644 --- a/naga/src/back/pipeline_constants.rs +++ b/naga/src/back/pipeline_constants.rs @@ -14,7 +14,10 @@ use thiserror::Error; pub enum PipelineConstantError { #[error("Missing value for pipeline-overridable constant with identifier string: '{0}'")] MissingValue(String), - #[error("Source f64 value needs to be finite (NaNs and Inifinites are not allowed) for number destinations")] + #[error( + "Source f64 value needs to be finite ({}) for number destinations", + "NaNs and Inifinites are not allowed" + )] SrcNeedsToBeFinite, #[error("Source f64 value doesn't fit in destination")] DstRangeTooSmall, diff --git a/naga/src/front/glsl/context.rs b/naga/src/front/glsl/context.rs index ee1fcc04b..b4cb1c874 100644 --- a/naga/src/front/glsl/context.rs +++ b/naga/src/front/glsl/context.rs @@ -630,7 +630,8 @@ impl<'a> Context<'a> { frontend.errors.push(Error { kind: ErrorKind::SemanticError( format!( - "Cannot apply operation to {left_inner:?} and {right_inner:?}" + "Cannot apply operation to {:?} and {:?}", + left_inner, right_inner ) .into(), ), @@ -828,7 +829,8 @@ impl<'a> Context<'a> { frontend.errors.push(Error { kind: ErrorKind::SemanticError( format!( - "Cannot apply operation to {left_inner:?} and {right_inner:?}" + "Cannot apply operation to {:?} and {:?}", + left_inner, right_inner ) .into(), ), @@ -908,7 +910,8 @@ impl<'a> Context<'a> { frontend.errors.push(Error { kind: ErrorKind::SemanticError( format!( - "Cannot apply operation to {left_inner:?} and {right_inner:?}" + "Cannot apply operation to {:?} and {:?}", + left_inner, right_inner ) .into(), ), diff --git a/naga/src/front/glsl/functions.rs b/naga/src/front/glsl/functions.rs index a1a603826..2a63c7a02 100644 --- a/naga/src/front/glsl/functions.rs +++ b/naga/src/front/glsl/functions.rs @@ -634,7 +634,8 @@ impl Frontend { self.errors.push(Error { kind: ErrorKind::SemanticError( format!( - "'{name}': image needs {overload_access:?} access but only {call_access:?} was provided" + "'{}': image needs {:?} access but only {:?} was provided", + name, overload_access, call_access ) .into(), ), diff --git a/naga/src/front/glsl/parser/expressions.rs b/naga/src/front/glsl/parser/expressions.rs index 594ad6a6c..c218e7b11 100644 --- a/naga/src/front/glsl/parser/expressions.rs +++ b/naga/src/front/glsl/parser/expressions.rs @@ -38,7 +38,13 @@ impl<'source> ParsingContext<'source> { TokenValue::FloatConstant(float) => { if float.width != 32 { frontend.errors.push(Error { - kind: ErrorKind::SemanticError("Unsupported floating-point value (expected single-precision floating-point number)".into()), + kind: ErrorKind::SemanticError( + concat!( + "Unsupported floating-point value ", + "(expected single-precision floating-point number)" + ) + .into(), + ), meta: token.meta, }); } diff --git a/naga/src/front/glsl/variables.rs b/naga/src/front/glsl/variables.rs index 6b74b254b..16c5bb65d 100644 --- a/naga/src/front/glsl/variables.rs +++ b/naga/src/front/glsl/variables.rs @@ -294,14 +294,17 @@ impl Frontend { .any(|i| components[i..].contains(&components[i - 1])); if not_unique { self.errors.push(Error { - kind: - ErrorKind::SemanticError( - format!( - "swizzle cannot have duplicate components in left-hand-side expression for \"{name:?}\"" - ) - .into(), - ), - meta , + kind: ErrorKind::SemanticError( + format!( + concat!( + "swizzle cannot have duplicate components in ", + "left-hand-side expression for \"{:?}\"" + ), + name + ) + .into(), + ), + meta, }) } } diff --git a/naga/src/front/spv/error.rs b/naga/src/front/spv/error.rs index 219048e10..898113d44 100644 --- a/naga/src/front/spv/error.rs +++ b/naga/src/front/spv/error.rs @@ -47,7 +47,13 @@ pub enum Error { UnsupportedBinaryOperator(spirv::Word), #[error("Naga supports OpTypeRuntimeArray in the StorageBuffer storage class only")] UnsupportedRuntimeArrayStorageClass, - #[error("unsupported matrix stride {stride} for a {columns}x{rows} matrix with scalar width={width}")] + #[error( + "unsupported matrix stride {} for a {}x{} matrix with scalar width={}", + stride, + columns, + rows, + width + )] UnsupportedMatrixStride { stride: u32, columns: u8, diff --git a/wgpu-hal/examples/ray-traced-triangle/main.rs b/wgpu-hal/examples/ray-traced-triangle/main.rs index dd9184373..4eedfe781 100644 --- a/wgpu-hal/examples/ray-traced-triangle/main.rs +++ b/wgpu-hal/examples/ray-traced-triangle/main.rs @@ -124,7 +124,12 @@ impl AccelerationStructureInstance { &mut self, shader_binding_table_record_offset: u32, ) { - debug_assert!(shader_binding_table_record_offset <= Self::MAX_U24, "shader_binding_table_record_offset uses more than 24 bits! {shader_binding_table_record_offset} > {}", Self::MAX_U24); + debug_assert!( + shader_binding_table_record_offset <= Self::MAX_U24, + "shader_binding_table_record_offset uses more than 24 bits! {} > {}", + shader_binding_table_record_offset, + Self::MAX_U24 + ); self.shader_binding_table_record_offset_and_flags = (shader_binding_table_record_offset & Self::LOW_24_MASK) | (self.shader_binding_table_record_offset_and_flags & !Self::LOW_24_MASK) @@ -151,7 +156,9 @@ impl AccelerationStructureInstance { ); debug_assert!( shader_binding_table_record_offset <= Self::MAX_U24, - "shader_binding_table_record_offset uses more than 24 bits! {shader_binding_table_record_offset} > {}", Self::MAX_U24 + "shader_binding_table_record_offset uses more than 24 bits! {} > {}", + shader_binding_table_record_offset, + Self::MAX_U24 ); AccelerationStructureInstance { transform: Self::affine_to_rows(transform), diff --git a/wgpu-hal/src/auxil/renderdoc.rs b/wgpu-hal/src/auxil/renderdoc.rs index 3b08955fa..3879bb954 100644 --- a/wgpu-hal/src/auxil/renderdoc.rs +++ b/wgpu-hal/src/auxil/renderdoc.rs @@ -74,7 +74,8 @@ impl RenderDoc { Err(e) => { return RenderDoc::NotAvailable { reason: format!( - "Unable to get RENDERDOC_GetAPI from renderdoc library '{renderdoc_filename}': {e:?}" + "Unable to get RENDERDOC_GetAPI from renderdoc library '{}': {e:?}", + renderdoc_filename ), } } @@ -89,7 +90,8 @@ impl RenderDoc { }, return_value => RenderDoc::NotAvailable { reason: format!( - "Unable to get API from renderdoc library '{renderdoc_filename}': {return_value}" + "Unable to get API from renderdoc library '{}': {}", + renderdoc_filename, return_value ), }, } diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index df5977806..8eb280089 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -457,11 +457,24 @@ impl Texture { }; log::error!( - "wgpu-hal heuristics assumed that the view dimension will be equal to `{got}` rather than `{view_dimension:?}`.\n{}\n{}\n{}\n{}", - "`D2` textures with `depth_or_array_layers == 1` are assumed to have view dimension `D2`", - "`D2` textures with `depth_or_array_layers > 1` are assumed to have view dimension `D2Array`", - "`D2` textures with `depth_or_array_layers == 6` are assumed to have view dimension `Cube`", - "`D2` textures with `depth_or_array_layers > 6 && depth_or_array_layers % 6 == 0` are assumed to have view dimension `CubeArray`", + concat!( + "wgpu-hal heuristics assumed that ", + "the view dimension will be equal to `{}` rather than `{:?}`.\n", + "`D2` textures with ", + "`depth_or_array_layers == 1` ", + "are assumed to have view dimension `D2`\n", + "`D2` textures with ", + "`depth_or_array_layers > 1` ", + "are assumed to have view dimension `D2Array`\n", + "`D2` textures with ", + "`depth_or_array_layers == 6` ", + "are assumed to have view dimension `Cube`\n", + "`D2` textures with ", + "`depth_or_array_layers > 6 && depth_or_array_layers % 6 == 0` ", + "are assumed to have view dimension `CubeArray`\n", + ), + got, + view_dimension, ); } } diff --git a/wgpu-hal/src/gles/web.rs b/wgpu-hal/src/gles/web.rs index ae7f83622..06bd87124 100644 --- a/wgpu-hal/src/gles/web.rs +++ b/wgpu-hal/src/gles/web.rs @@ -64,9 +64,10 @@ impl Instance { // “not supported” could include “insufficient GPU resources” or “the GPU process // previously crashed”. So, we must return it as an `Err` since it could occur // for circumstances outside the application author's control. - return Err(crate::InstanceError::new(String::from( - "canvas.getContext() returned null; webgl2 not available or canvas already in use" - ))); + return Err(crate::InstanceError::new(String::from(concat!( + "canvas.getContext() returned null; ", + "webgl2 not available or canvas already in use" + )))); } Err(js_error) => { // diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index 5673859e4..6d56ecf96 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -745,7 +745,12 @@ impl crate::Instance for super::Instance { Ok(sdk_ver) => sdk_ver, Err(err) => { log::error!( - "Couldn't parse Android's ro.build.version.sdk system property ({val}): {err}" + concat!( + "Couldn't parse Android's ", + "ro.build.version.sdk system property ({}): {}", + ), + val, + err, ); 0 } @@ -931,7 +936,10 @@ impl crate::Instance for super::Instance { if version < (21, 2) { // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688 log::warn!( - "Disabling presentation on '{}' (id {:?}) due to NV Optimus and Intel Mesa < v21.2", + concat!( + "Disabling presentation on '{}' (id {:?}) ", + "due to NV Optimus and Intel Mesa < v21.2" + ), exposed.info.name, exposed.adapter.raw ); diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 843b836f4..3b0f026fd 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -418,7 +418,13 @@ impl Surface { swapchain.next_present_time = Some(present_timing); } else { // Ideally we'd use something like `device.required_features` here, but that's in `wgpu-core`, which we are a dependency of - panic!("Tried to set display timing properties without the corresponding feature ({features:?}) enabled."); + panic!( + concat!( + "Tried to set display timing properties ", + "without the corresponding feature ({:?}) enabled." + ), + features + ); } } } From d3e09dd63ad54e2982ef744a6d74df468e232ff0 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 27 Sep 2024 17:10:15 -0700 Subject: [PATCH 56/59] [naga spv-out] Consolidate explanation of global wrapping. Consolidate the explanation of why and how we wrap Naga global variables in structs to satisfy Vulkan's requirements, and include it in the documentation for `back::spv::GlobalVariable`. Clarify `GlobalVariable`'s members documentation. --- naga/src/back/spv/helpers.rs | 17 +------- naga/src/back/spv/index.rs | 5 ++- naga/src/back/spv/mod.rs | 77 ++++++++++++++++++++++++++---------- 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/naga/src/back/spv/helpers.rs b/naga/src/back/spv/helpers.rs index 15c241d44..63144abc0 100644 --- a/naga/src/back/spv/helpers.rs +++ b/naga/src/back/spv/helpers.rs @@ -85,22 +85,9 @@ impl crate::AddressSpace { /// Return true if the global requires a type decorated with `Block`. /// -/// In the Vulkan spec 1.3.296, the section [Descriptor Set Interface][dsi] says: +/// See [`back::spv::GlobalVariable`] for details. /// -/// > Variables identified with the `Uniform` storage class are used to -/// > access transparent buffer backed resources. Such variables must -/// > be: -/// > -/// > - typed as `OpTypeStruct`, or an array of this type, -/// > -/// > - identified with a `Block` or `BufferBlock` decoration, and -/// > -/// > - laid out explicitly using the `Offset`, `ArrayStride`, and -/// > `MatrixStride` decorations as specified in §15.6.4, "Offset -/// > and Stride Assignment." -/// -/// [dsi]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#interfaces-resources-descset -// See `back::spv::GlobalVariable::access_id` for details. +/// [`back::spv::GlobalVariable`]: super::GlobalVariable pub fn global_needs_wrapper(ir_module: &crate::Module, var: &crate::GlobalVariable) -> bool { match var.space { crate::AddressSpace::Uniform diff --git a/naga/src/back/spv/index.rs b/naga/src/back/spv/index.rs index 832f806fd..1b7cce997 100644 --- a/naga/src/back/spv/index.rs +++ b/naga/src/back/spv/index.rs @@ -67,13 +67,14 @@ impl<'w> BlockContext<'w> { /// - An optional [`Access`] or [`AccessIndex`], for case 3, applied to... /// - A [`GlobalVariable`]. /// - /// The SPIR-V generated takes into account wrapped globals; see - /// [`global_needs_wrapper`]. + /// The generated SPIR-V takes into account wrapped globals; see + /// [`back::spv::GlobalVariable`] for details. /// /// [`GlobalVariable`]: crate::Expression::GlobalVariable /// [`AccessIndex`]: crate::Expression::AccessIndex /// [`Access`]: crate::Expression::Access /// [`base`]: crate::Expression::Access::base + /// [`back::spv::GlobalVariable`]: super::GlobalVariable pub(super) fn write_runtime_array_length( &mut self, array: Handle, diff --git a/naga/src/back/spv/mod.rs b/naga/src/back/spv/mod.rs index 77f915abb..764d06c90 100644 --- a/naga/src/back/spv/mod.rs +++ b/naga/src/back/spv/mod.rs @@ -467,38 +467,75 @@ enum CachedConstant { ZeroValue(Word), } +/// The SPIR-V representation of a [`crate::GlobalVariable`]. +/// +/// In the Vulkan spec 1.3.296, the section [Descriptor Set Interface][dsi] says: +/// +/// > Variables identified with the `Uniform` storage class are used to access +/// > transparent buffer backed resources. Such variables *must* be: +/// > +/// > - typed as `OpTypeStruct`, or an array of this type, +/// > +/// > - identified with a `Block` or `BufferBlock` decoration, and +/// > +/// > - laid out explicitly using the `Offset`, `ArrayStride`, and `MatrixStride` +/// > decorations as specified in "Offset and Stride Assignment". +/// +/// This is followed by identical language for the `StorageBuffer`, +/// except that a `BufferBlock` decoration is not allowed. +/// +/// When we encounter a global variable in the [`Storage`] or [`Uniform`] +/// address spaces whose type is not already [`Struct`], this backend implicitly +/// wraps the global variable in a struct: we generate a SPIR-V global variable +/// holding an `OpTypeStruct` with a single member, whose type is what the Naga +/// global's type would suggest, decorated as required above. +/// +/// The [`helpers::global_needs_wrapper`] function determines whether a given +/// [`crate::GlobalVariable`] needs to be wrapped. +/// +/// [dsi]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#interfaces-resources-descset +/// [`Storage`]: crate::AddressSpace::Storage +/// [`Uniform`]: crate::AddressSpace::Uniform +/// [`Struct`]: crate::TypeInner::Struct #[derive(Clone)] struct GlobalVariable { - /// ID of the OpVariable that declares the global. + /// The SPIR-V id of the `OpVariable` that declares the global. /// - /// If you need the variable's value, use [`access_id`] instead of this - /// field. If we wrapped the Naga IR `GlobalVariable`'s type in a struct to - /// comply with Vulkan's requirements, then this points to the `OpVariable` - /// with the synthesized struct type, whereas `access_id` points to the - /// field of said struct that holds the variable's actual value. + /// If this global has been implicitly wrapped in an `OpTypeStruct`, this id + /// refers to the wrapper, not the original Naga value it contains. If you + /// need the Naga value, use [`access_id`] instead of this field. + /// + /// If this global is not implicitly wrapped, this is the same as + /// [`access_id`]. /// /// This is used to compute the `access_id` pointer in function prologues, - /// and used for `ArrayLength` expressions, which do need the struct. + /// and used for `ArrayLength` expressions, which need to pass the wrapper + /// struct. /// /// [`access_id`]: GlobalVariable::access_id var_id: Word, - /// For `AddressSpace::Handle` variables, this ID is recorded in the function - /// prelude block (and reset before every function) as `OpLoad` of the variable. - /// It is then used for all the global ops, such as `OpImageSample`. + /// The loaded value of a `AddressSpace::Handle` global variable. + /// + /// If the current function uses this global variable, this is the id of an + /// `OpLoad` instruction in the function's prologue that loads its value. + /// (This value is assigned as we write the prologue code of each function.) + /// It is then used for all operations on the global, such as `OpImageSample`. handle_id: Word, - /// Actual ID used to access this variable. - /// For wrapped buffer variables, this ID is `OpAccessChain` into the - /// wrapper. Otherwise, the same as `var_id`. + /// The SPIR-V id of a pointer to this variable's Naga IR value. /// - /// Vulkan requires that globals in the `StorageBuffer` and `Uniform` storage - /// classes must be structs with the `Block` decoration, but WGSL and Naga IR - /// make no such requirement. So for such variables, we generate a wrapper struct - /// type with a single element of the type given by Naga, generate an - /// `OpAccessChain` for that member in the function prelude, and use that pointer - /// to refer to the global in the function body. This is the id of that access, - /// updated for each function in `write_function`. + /// If the current function uses this global variable, and it has been + /// implicitly wrapped in an `OpTypeStruct`, this is the id of an + /// `OpAccessChain` instruction in the function's prologue that refers to + /// the wrapped value inside the struct. (This value is assigned as we write + /// the prologue code of each function.) If you need the wrapper struct + /// itself, use [`var_id`] instead of this field. + /// + /// If this global is not implicitly wrapped, this is the same as + /// [`var_id`]. + /// + /// [`var_id`]: GlobalVariable::var_id access_id: Word, } From c5a4b4ecc3729a641a96ada0ed8275d08c16de33 Mon Sep 17 00:00:00 2001 From: Imbris <2002109+Imberflur@users.noreply.github.com> Date: Thu, 3 Oct 2024 16:40:43 -0400 Subject: [PATCH 57/59] Fix unaligned slice::from_raw_parts in gles push contant handling. (#6341) --- CHANGELOG.md | 1 + Cargo.lock | 1 + Cargo.toml | 2 +- examples/Cargo.toml | 2 +- wgpu-hal/Cargo.toml | 2 ++ wgpu-hal/src/gles/queue.rs | 58 +++++++++++++++++--------------------- 6 files changed, 32 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d564bb08..fc094bcfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,6 +126,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). #### GLES / OpenGL - Fix GL debug message callbacks not being properly cleaned up (causing UB). By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114) +- Fix calling `slice::from_raw_parts` with unaligned pointers in push constant handling. By @Imberflur in [#6341](https://github.com/gfx-rs/wgpu/pull/6341) #### WebGPU diff --git a/Cargo.lock b/Cargo.lock index 3c8d1aacc..74910d929 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3681,6 +3681,7 @@ dependencies = [ "bit-set", "bitflags 2.6.0", "block", + "bytemuck", "cfg-if", "cfg_aliases", "core-graphics-types", diff --git a/Cargo.toml b/Cargo.toml index fbf06524c..68c29b671 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ arrayvec = "0.7" bincode = "1" bit-vec = "0.8" bitflags = "2.6" -bytemuck = { version = "1.18", features = ["derive"] } +bytemuck = { version = "1.18" } cfg_aliases = "0.1" cfg-if = "1" criterion = "0.5" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index f5beed1d2..1f4d4951f 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -29,7 +29,7 @@ webgl = ["wgpu/webgl"] webgpu = ["wgpu/webgpu"] [dependencies] -bytemuck.workspace = true +bytemuck = { workspace = true, features = ["derive"] } cfg-if.workspace = true encase = { workspace = true, features = ["glam"] } flume.workspace = true diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index d3a05f079..03af10b96 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -56,6 +56,7 @@ vulkan = [ ] gles = [ "naga/glsl-out", + "dep:bytemuck", "dep:glow", "dep:glutin_wgl_sys", "dep:khronos-egl", @@ -126,6 +127,7 @@ rustc-hash.workspace = true log.workspace = true # backend: Gles +bytemuck = { workspace = true, optional = true } glow = { workspace = true, optional = true } [dependencies.wgt] diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index 39315f72b..fc106eb23 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -1603,19 +1603,13 @@ impl super::Queue { ref uniform, offset, } => { - // T must be POD - // - // This function is absolutely sketchy and we really should be using bytemuck. - unsafe fn get_data(data: &[u8], offset: u32) -> &[T; COUNT] { + fn get_data(data: &[u8], offset: u32) -> [T; COUNT] + where + [T; COUNT]: bytemuck::AnyBitPattern, + { let data_required = size_of::() * COUNT; - let raw = &data[(offset as usize)..][..data_required]; - - debug_assert_eq!(data_required, raw.len()); - - let slice: &[T] = unsafe { slice::from_raw_parts(raw.as_ptr().cast(), COUNT) }; - - slice.try_into().unwrap() + bytemuck::pod_read_unaligned(raw) } let location = Some(&uniform.location); @@ -1625,28 +1619,28 @@ impl super::Queue { // --- Float 1-4 Component --- // naga::TypeInner::Scalar(naga::Scalar::F32) => { - let data = unsafe { get_data::(data_bytes, offset)[0] }; + let data = get_data::(data_bytes, offset)[0]; unsafe { gl.uniform_1_f32(location, data) }; } naga::TypeInner::Vector { size: naga::VectorSize::Bi, scalar: naga::Scalar::F32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_2_f32_slice(location, data) }; } naga::TypeInner::Vector { size: naga::VectorSize::Tri, scalar: naga::Scalar::F32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_3_f32_slice(location, data) }; } naga::TypeInner::Vector { size: naga::VectorSize::Quad, scalar: naga::Scalar::F32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_4_f32_slice(location, data) }; } @@ -1654,28 +1648,28 @@ impl super::Queue { // --- Int 1-4 Component --- // naga::TypeInner::Scalar(naga::Scalar::I32) => { - let data = unsafe { get_data::(data_bytes, offset)[0] }; + let data = get_data::(data_bytes, offset)[0]; unsafe { gl.uniform_1_i32(location, data) }; } naga::TypeInner::Vector { size: naga::VectorSize::Bi, scalar: naga::Scalar::I32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_2_i32_slice(location, data) }; } naga::TypeInner::Vector { size: naga::VectorSize::Tri, scalar: naga::Scalar::I32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_3_i32_slice(location, data) }; } naga::TypeInner::Vector { size: naga::VectorSize::Quad, scalar: naga::Scalar::I32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_4_i32_slice(location, data) }; } @@ -1683,28 +1677,28 @@ impl super::Queue { // --- Uint 1-4 Component --- // naga::TypeInner::Scalar(naga::Scalar::U32) => { - let data = unsafe { get_data::(data_bytes, offset)[0] }; + let data = get_data::(data_bytes, offset)[0]; unsafe { gl.uniform_1_u32(location, data) }; } naga::TypeInner::Vector { size: naga::VectorSize::Bi, scalar: naga::Scalar::U32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_2_u32_slice(location, data) }; } naga::TypeInner::Vector { size: naga::VectorSize::Tri, scalar: naga::Scalar::U32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_3_u32_slice(location, data) }; } naga::TypeInner::Vector { size: naga::VectorSize::Quad, scalar: naga::Scalar::U32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_4_u32_slice(location, data) }; } @@ -1716,7 +1710,7 @@ impl super::Queue { rows: naga::VectorSize::Bi, scalar: naga::Scalar::F32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_matrix_2_f32_slice(location, false, data) }; } naga::TypeInner::Matrix { @@ -1725,7 +1719,7 @@ impl super::Queue { scalar: naga::Scalar::F32, } => { // repack 2 vec3s into 6 values. - let unpacked_data = unsafe { get_data::(data_bytes, offset) }; + let unpacked_data = &get_data::(data_bytes, offset); #[rustfmt::skip] let packed_data = [ unpacked_data[0], unpacked_data[1], unpacked_data[2], @@ -1738,7 +1732,7 @@ impl super::Queue { rows: naga::VectorSize::Quad, scalar: naga::Scalar::F32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_matrix_2x4_f32_slice(location, false, data) }; } @@ -1750,7 +1744,7 @@ impl super::Queue { rows: naga::VectorSize::Bi, scalar: naga::Scalar::F32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_matrix_3x2_f32_slice(location, false, data) }; } naga::TypeInner::Matrix { @@ -1759,7 +1753,7 @@ impl super::Queue { scalar: naga::Scalar::F32, } => { // repack 3 vec3s into 9 values. - let unpacked_data = unsafe { get_data::(data_bytes, offset) }; + let unpacked_data = &get_data::(data_bytes, offset); #[rustfmt::skip] let packed_data = [ unpacked_data[0], unpacked_data[1], unpacked_data[2], @@ -1773,7 +1767,7 @@ impl super::Queue { rows: naga::VectorSize::Quad, scalar: naga::Scalar::F32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_matrix_3x4_f32_slice(location, false, data) }; } @@ -1785,7 +1779,7 @@ impl super::Queue { rows: naga::VectorSize::Bi, scalar: naga::Scalar::F32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_matrix_4x2_f32_slice(location, false, data) }; } naga::TypeInner::Matrix { @@ -1794,7 +1788,7 @@ impl super::Queue { scalar: naga::Scalar::F32, } => { // repack 4 vec3s into 12 values. - let unpacked_data = unsafe { get_data::(data_bytes, offset) }; + let unpacked_data = &get_data::(data_bytes, offset); #[rustfmt::skip] let packed_data = [ unpacked_data[0], unpacked_data[1], unpacked_data[2], @@ -1809,7 +1803,7 @@ impl super::Queue { rows: naga::VectorSize::Quad, scalar: naga::Scalar::F32, } => { - let data = unsafe { get_data::(data_bytes, offset) }; + let data = &get_data::(data_bytes, offset); unsafe { gl.uniform_matrix_4_f32_slice(location, false, data) }; } _ => panic!("Unsupported uniform datatype: {:?}!", uniform.ty), From 04182c24ecd9db68d03ee591a9fecde38401fed1 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Wed, 2 Oct 2024 16:16:10 -0700 Subject: [PATCH 58/59] [naga spv-out] Make `write_block` and its types private. Make `naga::back::spv::BlockContext::write_block` private to `naga::back::spv::block`. Introduce a new `pub(super)` function, `write_function_body`, for `Writer::write_function` to call. Make `BlockExit` private to `naga::back::spv::block`. Move `LoopContext` from `naga::back::spv` into `naga::back::spv::block`, and make it private. --- naga/src/back/spv/block.rs | 29 +++++++++++++++++++++++++---- naga/src/back/spv/mod.rs | 6 ------ naga/src/back/spv/writer.rs | 10 ++-------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/naga/src/back/spv/block.rs b/naga/src/back/spv/block.rs index 97db1cb54..4076fc5a8 100644 --- a/naga/src/back/spv/block.rs +++ b/naga/src/back/spv/block.rs @@ -4,8 +4,7 @@ Implementations for `BlockContext` methods. use super::{ helpers, index::BoundsCheckResult, make_local, selection::Selection, Block, BlockContext, - Dimension, Error, Instruction, LocalType, LookupType, LoopContext, ResultMember, Writer, - WriterFlags, + Dimension, Error, Instruction, LocalType, LookupType, ResultMember, Writer, WriterFlags, }; use crate::{arena::Handle, proc::TypeResolution, Statement}; use spirv::Word; @@ -39,7 +38,7 @@ enum ExpressionPointer { } /// The termination statement to be added to the end of the block -pub enum BlockExit { +enum BlockExit { /// Generates an OpReturn (void return) Return, /// Generates an OpBranch to the specified block @@ -60,6 +59,12 @@ pub enum BlockExit { }, } +#[derive(Clone, Copy, Default)] +struct LoopContext { + continuing_id: Option, + break_id: Option, +} + #[derive(Debug)] pub(crate) struct DebugInfoInner<'a> { pub source_code: &'a str, @@ -2060,7 +2065,7 @@ impl<'w> BlockContext<'w> { } } - pub(super) fn write_block( + fn write_block( &mut self, label_id: Word, naga_block: &crate::Block, @@ -2721,4 +2726,20 @@ impl<'w> BlockContext<'w> { self.function.consume(block, termination); Ok(()) } + + pub(super) fn write_function_body( + &mut self, + entry_id: Word, + debug_info: Option<&DebugInfoInner>, + ) -> Result<(), Error> { + self.write_block( + entry_id, + &self.ir_function.body, + super::block::BlockExit::Return, + LoopContext::default(), + debug_info, + )?; + + Ok(()) + } } diff --git a/naga/src/back/spv/mod.rs b/naga/src/back/spv/mod.rs index 764d06c90..e6397017c 100644 --- a/naga/src/back/spv/mod.rs +++ b/naga/src/back/spv/mod.rs @@ -665,12 +665,6 @@ impl BlockContext<'_> { } } -#[derive(Clone, Copy, Default)] -struct LoopContext { - continuing_id: Option, - break_id: Option, -} - pub struct Writer { physical_layout: PhysicalLayout, logical_layout: LogicalLayout, diff --git a/naga/src/back/spv/writer.rs b/naga/src/back/spv/writer.rs index 7f41caf29..14f1fc002 100644 --- a/naga/src/back/spv/writer.rs +++ b/naga/src/back/spv/writer.rs @@ -3,7 +3,7 @@ use super::{ helpers::{contains_builtin, global_needs_wrapper, map_storage_class}, make_local, Block, BlockContext, CachedConstant, CachedExpressions, DebugInfo, EntryPointContext, Error, Function, FunctionArgument, GlobalVariable, IdGenerator, Instruction, - LocalType, LocalVariable, LogicalLayout, LookupFunctionType, LookupType, LoopContext, Options, + LocalType, LocalVariable, LogicalLayout, LookupFunctionType, LookupType, Options, PhysicalLayout, PipelineOptions, ResultMember, Writer, WriterFlags, BITS_PER_BYTE, }; use crate::{ @@ -756,13 +756,7 @@ impl Writer { next_id }; - context.write_block( - main_id, - &ir_function.body, - super::block::BlockExit::Return, - LoopContext::default(), - debug_info.as_ref(), - )?; + context.write_function_body(main_id, debug_info.as_ref())?; // Consume the `BlockContext`, ending its borrows and letting the // `Writer` steal back its cached expression table and temp_list. From e432980a7319482c65bf0e54cb4a0ca148407b5c Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Wed, 2 Oct 2024 17:13:07 -0700 Subject: [PATCH 59/59] [naga spv-out] Don't emit unreachable blocks that jump into loops. When generating SPIR-V, avoid generating unreachable blocks following statements like `break`, `return`, and so on that cause non-local exits. These unreachable blocks can cause SPIR-V validation to fail. Fixes #6220. --- naga/src/back/spv/block.rs | 101 +++++++++++++++--- naga/tests/in/6220-break-from-loop.param.ron | 2 + naga/tests/in/6220-break-from-loop.wgsl | 43 ++++++++ .../tests/out/spv/6220-break-from-loop.spvasm | 44 ++++++++ naga/tests/snapshots.rs | 1 + 5 files changed, 176 insertions(+), 15 deletions(-) create mode 100644 naga/tests/in/6220-break-from-loop.param.ron create mode 100644 naga/tests/in/6220-break-from-loop.wgsl create mode 100644 naga/tests/out/spv/6220-break-from-loop.spvasm diff --git a/naga/src/back/spv/block.rs b/naga/src/back/spv/block.rs index 4076fc5a8..c6adba85b 100644 --- a/naga/src/back/spv/block.rs +++ b/naga/src/back/spv/block.rs @@ -59,6 +59,30 @@ enum BlockExit { }, } +/// What code generation did with a provided [`BlockExit`] value. +/// +/// A function that accepts a [`BlockExit`] argument should return a value of +/// this type, to indicate whether the code it generated ended up using the +/// provided exit, or ignored it and did a non-local exit of some other kind +/// (say, [`Break`] or [`Continue`]). Some callers must use this information to +/// decide whether to generate the target block at all. +/// +/// [`Break`]: Statement::Break +/// [`Continue`]: Statement::Continue +#[must_use] +enum BlockExitDisposition { + /// The generated code used the provided `BlockExit` value. If it included a + /// block label, the caller should be sure to actually emit the block it + /// refers to. + Used, + + /// The generated code did not use the provided `BlockExit` value. If it + /// included a block label, the caller should not bother to actually emit + /// the block it refers to, unless it knows the block is needed for + /// something else. + Discarded, +} + #[derive(Clone, Copy, Default)] struct LoopContext { continuing_id: Option, @@ -2065,6 +2089,22 @@ impl<'w> BlockContext<'w> { } } + /// Generate one or more SPIR-V blocks for `naga_block`. + /// + /// Use `label_id` as the label for the SPIR-V entry point block. + /// + /// If control reaches the end of the SPIR-V block, terminate it according + /// to `exit`. This function's return value indicates whether it acted on + /// this parameter or not; see [`BlockExitDisposition`]. + /// + /// If the block contains [`Break`] or [`Continue`] statements, + /// `loop_context` supplies the labels of the SPIR-V blocks to jump to. If + /// either of these labels are `None`, then it should have been a Naga + /// validation error for the corresponding statement to occur in this + /// context. + /// + /// [`Break`]: Statement::Break + /// [`Continue`]: Statement::Continue fn write_block( &mut self, label_id: Word, @@ -2072,7 +2112,7 @@ impl<'w> BlockContext<'w> { exit: BlockExit, loop_context: LoopContext, debug_info: Option<&DebugInfoInner>, - ) -> Result<(), Error> { + ) -> Result { let mut block = Block::new(label_id); for (statement, span) in naga_block.span_iter() { if let (Some(debug_info), false) = ( @@ -2108,7 +2148,7 @@ impl<'w> BlockContext<'w> { self.function.consume(block, Instruction::branch(scope_id)); let merge_id = self.gen_id(); - self.write_block( + let merge_used = self.write_block( scope_id, block_statements, BlockExit::Branch { target: merge_id }, @@ -2116,7 +2156,14 @@ impl<'w> BlockContext<'w> { debug_info, )?; - block = Block::new(merge_id); + match merge_used { + BlockExitDisposition::Used => { + block = Block::new(merge_id); + } + BlockExitDisposition::Discarded => { + return Ok(BlockExitDisposition::Discarded); + } + } } Statement::If { condition, @@ -2152,7 +2199,11 @@ impl<'w> BlockContext<'w> { ); if let Some(block_id) = accept_id { - self.write_block( + // We can ignore the `BlockExitDisposition` returned here because, + // even if `merge_id` is not actually reachable, it is always + // referred to by the `OpSelectionMerge` instruction we emitted + // earlier. + let _ = self.write_block( block_id, accept, BlockExit::Branch { target: merge_id }, @@ -2161,7 +2212,11 @@ impl<'w> BlockContext<'w> { )?; } if let Some(block_id) = reject_id { - self.write_block( + // We can ignore the `BlockExitDisposition` returned here because, + // even if `merge_id` is not actually reachable, it is always + // referred to by the `OpSelectionMerge` instruction we emitted + // earlier. + let _ = self.write_block( block_id, reject, BlockExit::Branch { target: merge_id }, @@ -2239,7 +2294,15 @@ impl<'w> BlockContext<'w> { } else { merge_id }; - self.write_block( + // We can ignore the `BlockExitDisposition` returned here because + // `case_finish_id` is always referred to by either: + // + // - the `OpSwitch`, if it's the next case's label for a + // fall-through, or + // + // - the `OpSelectionMerge`, if it's the switch's overall merge + // block because there's no fall-through. + let _ = self.write_block( *label_id, &case.body, BlockExit::Branch { @@ -2285,7 +2348,10 @@ impl<'w> BlockContext<'w> { )); self.function.consume(block, Instruction::branch(body_id)); - self.write_block( + // We can ignore the `BlockExitDisposition` returned here because, + // even if `continuing_id` is not actually reachable, it is always + // referred to by the `OpLoopMerge` instruction we emitted earlier. + let _ = self.write_block( body_id, body, BlockExit::Branch { @@ -2308,7 +2374,10 @@ impl<'w> BlockContext<'w> { }, }; - self.write_block( + // We can ignore the `BlockExitDisposition` returned here because, + // even if `merge_id` is not actually reachable, it is always referred + // to by the `OpLoopMerge` instruction we emitted earlier. + let _ = self.write_block( continuing_id, continuing, exit, @@ -2324,14 +2393,14 @@ impl<'w> BlockContext<'w> { Statement::Break => { self.function .consume(block, Instruction::branch(loop_context.break_id.unwrap())); - return Ok(()); + return Ok(BlockExitDisposition::Discarded); } Statement::Continue => { self.function.consume( block, Instruction::branch(loop_context.continuing_id.unwrap()), ); - return Ok(()); + return Ok(BlockExitDisposition::Discarded); } Statement::Return { value: Some(value) } => { let value_id = self.cached[value]; @@ -2350,15 +2419,15 @@ impl<'w> BlockContext<'w> { None => Instruction::return_value(value_id), }; self.function.consume(block, instruction); - return Ok(()); + return Ok(BlockExitDisposition::Discarded); } Statement::Return { value: None } => { self.function.consume(block, Instruction::return_void()); - return Ok(()); + return Ok(BlockExitDisposition::Discarded); } Statement::Kill => { self.function.consume(block, Instruction::kill()); - return Ok(()); + return Ok(BlockExitDisposition::Discarded); } Statement::Barrier(flags) => { self.writer.write_barrier(flags, &mut block); @@ -2724,7 +2793,7 @@ impl<'w> BlockContext<'w> { }; self.function.consume(block, termination); - Ok(()) + Ok(BlockExitDisposition::Used) } pub(super) fn write_function_body( @@ -2732,7 +2801,9 @@ impl<'w> BlockContext<'w> { entry_id: Word, debug_info: Option<&DebugInfoInner>, ) -> Result<(), Error> { - self.write_block( + // We can ignore the `BlockExitDisposition` returned here because + // `BlockExit::Return` doesn't refer to a block. + let _ = self.write_block( entry_id, &self.ir_function.body, super::block::BlockExit::Return, diff --git a/naga/tests/in/6220-break-from-loop.param.ron b/naga/tests/in/6220-break-from-loop.param.ron new file mode 100644 index 000000000..72873dd66 --- /dev/null +++ b/naga/tests/in/6220-break-from-loop.param.ron @@ -0,0 +1,2 @@ +( +) diff --git a/naga/tests/in/6220-break-from-loop.wgsl b/naga/tests/in/6220-break-from-loop.wgsl new file mode 100644 index 000000000..424886a75 --- /dev/null +++ b/naga/tests/in/6220-break-from-loop.wgsl @@ -0,0 +1,43 @@ +// #6220: Don't generate unreachable SPIR-V blocks that branch into +// structured control flow constructs. +// +// Suppose we have Naga code like this: +// +// Block { +// ... prelude +// Block { ... nested } +// ... postlude +// } +// +// The SPIR-V back end used to always generate three separate SPIR-V +// blocks for the sections labeled "prelude", "nested", and +// "postlude", each block ending with a branch to the next, even if +// they were empty. +// +// However, the function below generates code that includes the +// following structure: +// +// Loop { +// body: Block { +// ... prelude +// Block { Break } +// ... postlude +// } +// continuing: ... +// } +// +// In this case, even though the `Break` renders the "postlude" +// unreachable, we used to generate a SPIR-V block for it anyway, +// ending with a branch to the `Loop`'s "continuing" block. However, +// SPIR-V's structured control flow rules forbid branches to a loop +// construct's continue target from outside the loop, so the SPIR-V +// module containing the unreachable block didn't pass validation. +// +// One might assume that unreachable blocks shouldn't affect +// validation, but the spec doesn't clearly agree, and this doesn't +// seem to be the way validation has been implemented. +fn break_from_loop() { + for (var i = 0; i < 4; i += 1) { + break; + } +} diff --git a/naga/tests/out/spv/6220-break-from-loop.spvasm b/naga/tests/out/spv/6220-break-from-loop.spvasm new file mode 100644 index 000000000..9dabbc207 --- /dev/null +++ b/naga/tests/out/spv/6220-break-from-loop.spvasm @@ -0,0 +1,44 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 26 +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%3 = OpTypeInt 32 1 +%6 = OpTypeFunction %2 +%7 = OpConstant %3 0 +%8 = OpConstant %3 4 +%9 = OpConstant %3 1 +%11 = OpTypePointer Function %3 +%18 = OpTypeBool +%5 = OpFunction %2 None %6 +%4 = OpLabel +%10 = OpVariable %11 Function %7 +OpBranch %12 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +OpLoopMerge %14 %16 None +OpBranch %15 +%15 = OpLabel +%17 = OpLoad %3 %10 +%19 = OpSLessThan %18 %17 %8 +OpSelectionMerge %20 None +OpBranchConditional %19 %20 %21 +%21 = OpLabel +OpBranch %14 +%20 = OpLabel +OpBranch %22 +%22 = OpLabel +OpBranch %14 +%16 = OpLabel +%24 = OpLoad %3 %10 +%25 = OpIAdd %3 %24 %9 +OpStore %10 %25 +OpBranch %13 +%14 = OpLabel +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/snapshots.rs b/naga/tests/snapshots.rs index 596e4cea1..adf67f833 100644 --- a/naga/tests/snapshots.rs +++ b/naga/tests/snapshots.rs @@ -932,6 +932,7 @@ fn convert_wgsl() { "phony_assignment", Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL, ), + ("6220-break-from-loop", Targets::SPIRV), ]; for &(name, targets) in inputs.iter() {