diff --git a/Cargo.lock b/Cargo.lock index bda80b068e..962fcb1de5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1032,7 +1032,7 @@ dependencies = [ [[package]] name = "naga" version = "0.8.0" -source = "git+https://github.com/gfx-rs/naga?rev=85056524#850565243d1d0d03215f13246c94d63e1d4c51cd" +source = "git+https://github.com/gfx-rs/naga?rev=1aa91549#1aa9154964238af8c692cf521ff90e1f2395e147" dependencies = [ "bit-set", "bitflags", diff --git a/deno_webgpu/src/lib.rs b/deno_webgpu/src/lib.rs index 63bd720ebb..96cb351407 100644 --- a/deno_webgpu/src/lib.rs +++ b/deno_webgpu/src/lib.rs @@ -170,9 +170,6 @@ fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> { ) { return_features.push("uniform-buffer-and-storage-buffer-texture-non-uniform-indexing"); } - if features.contains(wgpu_types::Features::UNSIZED_BINDING_ARRAY) { - return_features.push("unsized-binding-array"); - } if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) { return_features.push("address-mode-clamp-to-border"); } @@ -341,10 +338,6 @@ impl From for wgpu_types::Features { .0 .contains("uniform-buffer-and-storage-buffer-texture-non-uniform-indexing"), ); - features.set( - wgpu_types::Features::UNSIZED_BINDING_ARRAY, - required_features.0.contains("unsized-binding-array"), - ); features.set( wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER, required_features.0.contains("address-mode-clamp-to-border"), diff --git a/player/tests/data/clear-buffer-texture.ron b/player/tests/data/clear-buffer-texture.ron index bd01fb61d7..a4e2485c3d 100644 --- a/player/tests/data/clear-buffer-texture.ron +++ b/player/tests/data/clear-buffer-texture.ron @@ -1,5 +1,5 @@ ( - features: 0x0000_0020_0000_0000, + features: 0x0000_0010_0000_0000, expectations: [ ( name: "Quad", diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 3711818264..cafa3d5436 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -40,7 +40,7 @@ thiserror = "1" [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "85056524" +rev = "1aa91549" #version = "0.8" features = ["span", "validate", "wgsl-in"] diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index b951c318bd..7a8bdc89e5 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -1142,6 +1142,25 @@ impl Device { self.features .contains(wgt::Features::SHADER_PRIMITIVE_INDEX), ); + caps.set( + Caps::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + self.features.contains( + wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + ), + ); + caps.set( + Caps::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + self.features.contains( + wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + ), + ); + // TODO: This needs a proper wgpu feature + caps.set( + Caps::SAMPLER_NON_UNIFORM_INDEXING, + self.features.contains( + wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + ), + ); let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps) .validate(&module) .map_err(|inner| { diff --git a/wgpu-core/src/validation.rs b/wgpu-core/src/validation.rs index 921ec49275..5596de8369 100644 --- a/wgpu-core/src/validation.rs +++ b/wgpu-core/src/validation.rs @@ -867,7 +867,14 @@ impl Interface { Some(ref br) => br.clone(), _ => continue, }; - let ty = match module.types[var.ty].inner { + let naga_ty = &module.types[var.ty].inner; + + let inner_ty = match *naga_ty { + naga::TypeInner::BindingArray { base, .. } => &module.types[base].inner, + ref ty => ty, + }; + + let ty = match *inner_ty { naga::TypeInner::Image { dim, arrayed, diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index f5d6c52d73..529fc4f174 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -91,14 +91,14 @@ js-sys = { version = "0.3" } [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "85056524" +rev = "1aa91549" #version = "0.8" # DEV dependencies [dev-dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "85056524" +rev = "1aa91549" #version = "0.8" features = ["wgsl-in"] diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 88959bdb47..3be86494d8 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -91,6 +91,18 @@ impl super::Adapter { ) }); + let mut shader_model_support: d3d12::D3D12_FEATURE_DATA_SHADER_MODEL = + d3d12::D3D12_FEATURE_DATA_SHADER_MODEL { + HighestShaderModel: d3d12::D3D_SHADER_MODEL_6_0, + }; + assert_eq!(0, unsafe { + device.CheckFeatureSupport( + d3d12::D3D12_FEATURE_SHADER_MODEL, + &mut shader_model_support as *mut _ as *mut _, + mem::size_of::() as _, + ) + }); + let mut workarounds = super::Workarounds::default(); let info = wgt::AdapterInfo { @@ -175,11 +187,6 @@ impl super::Adapter { | wgt::Features::DEPTH_CLIP_CONTROL | wgt::Features::INDIRECT_FIRST_INSTANCE | wgt::Features::MAPPABLE_PRIMARY_BUFFERS - //TODO: Naga part - //| wgt::Features::TEXTURE_BINDING_ARRAY - //| wgt::Features::BUFFER_BINDING_ARRAY - //| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY - //| wgt::Features::UNSIZED_BINDING_ARRAY | wgt::Features::MULTI_DRAW_INDIRECT | wgt::Features::MULTI_DRAW_INDIRECT_COUNT | wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER @@ -204,6 +211,13 @@ impl super::Adapter { != d3d12::D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED, ); + features.set( + wgt::Features::TEXTURE_BINDING_ARRAY + | wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING + | wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + shader_model_support.HighestShaderModel >= d3d12::D3D_SHADER_MODEL_5_1, + ); + let base = wgt::Limits::default(); Some(crate::ExposedAdapter { @@ -282,7 +296,7 @@ impl super::Adapter { impl crate::Adapter for super::Adapter { unsafe fn open( &self, - features: wgt::Features, + _features: wgt::Features, _limits: &wgt::Limits, ) -> Result, crate::DeviceError> { let queue = { @@ -297,13 +311,7 @@ impl crate::Adapter for super::Adapter { .into_device_result("Queue creation")? }; - let device = super::Device::new( - self.device, - queue, - features, - self.private_caps, - &self.library, - )?; + let device = super::Device::new(self.device, queue, self.private_caps, &self.library)?; Ok(crate::OpenDevice { device, queue: super::Queue { diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index f87111f6bc..2f92cab44c 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -21,7 +21,6 @@ impl super::Device { pub(super) fn new( raw: native::Device, present_queue: native::CommandQueue, - features: wgt::Features, private_caps: super::PrivateCapabilities, library: &Arc, ) -> Result { @@ -87,7 +86,6 @@ impl super::Device { let capacity_samplers = 2_048; let shared = super::DeviceShared { - features, zero_buffer, cmd_signatures: super::CommandSignatures { draw: raw @@ -222,13 +220,6 @@ impl super::Device { compile_flags |= d3dcompiler::D3DCOMPILE_DEBUG | d3dcompiler::D3DCOMPILE_SKIP_OPTIMIZATION; } - if self - .shared - .features - .contains(wgt::Features::UNSIZED_BINDING_ARRAY) - { - compile_flags |= d3dcompiler::D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES; - } let source_name = match stage.module.raw_name { Some(ref cstr) => cstr.as_c_str().as_ptr(), @@ -691,16 +682,17 @@ impl crate::Device for super::Device { ) -> Result { let (mut num_buffer_views, mut num_samplers, mut num_texture_views) = (0, 0, 0); for entry in desc.entries.iter() { + let count = entry.count.map_or(1, NonZeroU32::get); match entry.ty { wgt::BindingType::Buffer { has_dynamic_offset: true, .. } => {} - wgt::BindingType::Buffer { .. } => num_buffer_views += 1, + wgt::BindingType::Buffer { .. } => num_buffer_views += count, wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => { - num_texture_views += 1 + num_texture_views += count } - wgt::BindingType::Sampler { .. } => num_samplers += 1, + wgt::BindingType::Sampler { .. } => num_samplers += count, } } @@ -858,7 +850,10 @@ impl crate::Device for super::Device { group: index as u32, binding: entry.binding, }, - bt.clone(), + hlsl::BindTarget { + binding_array_size: entry.count.map(NonZeroU32::get), + ..bt.clone() + }, ); ranges.push(native::DescriptorRange::new( range_ty, @@ -866,7 +861,7 @@ impl crate::Device for super::Device { native_binding(bt), d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, )); - bt.register += 1; + bt.register += entry.count.map(NonZeroU32::get).unwrap_or(1); } if ranges.len() > range_base { log::debug!( @@ -894,7 +889,10 @@ impl crate::Device for super::Device { group: index as u32, binding: entry.binding, }, - bind_sampler.clone(), + hlsl::BindTarget { + binding_array_size: entry.count.map(NonZeroU32::get), + ..bind_sampler.clone() + }, ); ranges.push(native::DescriptorRange::new( range_ty, @@ -902,7 +900,7 @@ impl crate::Device for super::Device { native_binding(&bind_sampler), d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, )); - bind_sampler.register += 1; + bind_sampler.register += entry.count.map(NonZeroU32::get).unwrap_or(1); } if ranges.len() > range_base { log::debug!( @@ -953,7 +951,10 @@ impl crate::Device for super::Device { group: index as u32, binding: entry.binding, }, - bt.clone(), + hlsl::BindTarget { + binding_array_size: entry.count.map(NonZeroU32::get), + ..bt.clone() + }, ); info.dynamic_buffers.push(kind); @@ -969,7 +970,7 @@ impl crate::Device for super::Device { native_binding(bt), )); - bt.register += 1; + bt.register += entry.count.map_or(1, NonZeroU32::get); } bind_group_infos.push(info); @@ -1082,82 +1083,97 @@ impl crate::Device for super::Device { has_dynamic_offset: true, .. } => { - let data = &desc.buffers[entry.resource_index as usize]; - dynamic_buffers.push(data.resolve_address()); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.buffers[start..end] { + dynamic_buffers.push(data.resolve_address()); + } } wgt::BindingType::Buffer { ty, .. } => { - let data = &desc.buffers[entry.resource_index as usize]; - let gpu_address = data.resolve_address(); - let size = data.resolve_size() as u32; - let inner = cpu_views.as_mut().unwrap(); - let cpu_index = inner.stage.len() as u32; - let handle = desc.layout.cpu_heap_views.as_ref().unwrap().at(cpu_index); - match ty { - wgt::BufferBindingType::Uniform => { - let size_mask = - d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1; - let raw_desc = d3d12::D3D12_CONSTANT_BUFFER_VIEW_DESC { - BufferLocation: gpu_address, - SizeInBytes: ((size - 1) | size_mask) + 1, - }; - self.raw.CreateConstantBufferView(&raw_desc, handle); - } - wgt::BufferBindingType::Storage { read_only: true } => { - let mut raw_desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { - Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, - Shader4ComponentMapping: - view::D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, - ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER, - u: mem::zeroed(), - }; - *raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV { - FirstElement: data.offset / 4, - NumElements: size / 4, - StructureByteStride: 0, - Flags: d3d12::D3D12_BUFFER_SRV_FLAG_RAW, - }; - self.raw.CreateShaderResourceView( - data.buffer.resource.as_mut_ptr(), - &raw_desc, - handle, - ); - } - wgt::BufferBindingType::Storage { read_only: false } => { - let mut raw_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { - Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, - ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, - u: mem::zeroed(), - }; - *raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { - FirstElement: data.offset / 4, - NumElements: size / 4, - StructureByteStride: 0, - CounterOffsetInBytes: 0, - Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, - }; - self.raw.CreateUnorderedAccessView( - data.buffer.resource.as_mut_ptr(), - ptr::null_mut(), - &raw_desc, - handle, - ); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.buffers[start..end] { + let gpu_address = data.resolve_address(); + let size = data.resolve_size() as u32; + let inner = cpu_views.as_mut().unwrap(); + let cpu_index = inner.stage.len() as u32; + let handle = desc.layout.cpu_heap_views.as_ref().unwrap().at(cpu_index); + match ty { + wgt::BufferBindingType::Uniform => { + let size_mask = + d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1; + let raw_desc = d3d12::D3D12_CONSTANT_BUFFER_VIEW_DESC { + BufferLocation: gpu_address, + SizeInBytes: ((size - 1) | size_mask) + 1, + }; + self.raw.CreateConstantBufferView(&raw_desc, handle); + } + wgt::BufferBindingType::Storage { read_only: true } => { + let mut raw_desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { + Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, + Shader4ComponentMapping: + view::D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, + ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER, + u: mem::zeroed(), + }; + *raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV { + FirstElement: data.offset / 4, + NumElements: size / 4, + StructureByteStride: 0, + Flags: d3d12::D3D12_BUFFER_SRV_FLAG_RAW, + }; + self.raw.CreateShaderResourceView( + data.buffer.resource.as_mut_ptr(), + &raw_desc, + handle, + ); + } + wgt::BufferBindingType::Storage { read_only: false } => { + let mut raw_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { + Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, + ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, + u: mem::zeroed(), + }; + *raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { + FirstElement: data.offset / 4, + NumElements: size / 4, + StructureByteStride: 0, + CounterOffsetInBytes: 0, + Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, + }; + self.raw.CreateUnorderedAccessView( + data.buffer.resource.as_mut_ptr(), + ptr::null_mut(), + &raw_desc, + handle, + ); + } } + inner.stage.push(handle); } - inner.stage.push(handle); } wgt::BindingType::Texture { .. } => { - let data = &desc.textures[entry.resource_index as usize]; - let handle = data.view.handle_srv.unwrap(); - cpu_views.as_mut().unwrap().stage.push(handle.raw); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.textures[start..end] { + let handle = data.view.handle_srv.unwrap(); + cpu_views.as_mut().unwrap().stage.push(handle.raw); + } } wgt::BindingType::StorageTexture { .. } => { - let data = &desc.textures[entry.resource_index as usize]; - let handle = data.view.handle_uav.unwrap(); - cpu_views.as_mut().unwrap().stage.push(handle.raw); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.textures[start..end] { + let handle = data.view.handle_uav.unwrap(); + cpu_views.as_mut().unwrap().stage.push(handle.raw); + } } wgt::BindingType::Sampler { .. } => { - let data = &desc.samplers[entry.resource_index as usize]; - cpu_samplers.as_mut().unwrap().stage.push(data.handle.raw); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.samplers[start..end] { + cpu_samplers.as_mut().unwrap().stage.push(data.handle.raw); + } } } } diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index ffef5a3ada..4b048ff009 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -199,7 +199,6 @@ impl CommandSignatures { } struct DeviceShared { - features: wgt::Features, zero_buffer: native::Resource, cmd_signatures: CommandSignatures, heap_views: descriptor::GeneralHeap, diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index b560bd0670..4b36e3d757 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -1,5 +1,6 @@ use parking_lot::Mutex; use std::{ + num::NonZeroU32, ptr, sync::{atomic, Arc}, thread, time, @@ -73,6 +74,13 @@ impl super::Device { ) .map_err(|e| crate::PipelineError::Linkage(stage_bit, format!("MSL: {:?}", e)))?; + log::debug!( + "Naga generated shader for entry point '{}' and stage {:?}\n{}", + stage.entry_point, + naga_stage, + &source + ); + let options = mtl::CompileOptions::new(); options.set_language_version(self.shared.private_caps.msl_version); @@ -558,10 +566,12 @@ impl crate::Device for super::Device { } let mut target = naga::back::msl::BindTarget::default(); + let count = entry.count.map_or(1, NonZeroU32::get); + target.binding_array_size = entry.count.map(NonZeroU32::get); match entry.ty { wgt::BindingType::Buffer { ty, .. } => { target.buffer = Some(info.counters.buffers as _); - info.counters.buffers += 1; + info.counters.buffers += count; if let wgt::BufferBindingType::Storage { read_only } = ty { target.mutable = !read_only; } @@ -570,15 +580,15 @@ impl crate::Device for super::Device { target.sampler = Some(naga::back::msl::BindSamplerTarget::Resource( info.counters.samplers as _, )); - info.counters.samplers += 1; + info.counters.samplers += count; } wgt::BindingType::Texture { .. } => { target.texture = Some(info.counters.textures as _); - info.counters.textures += 1; + info.counters.textures += count; } wgt::BindingType::StorageTexture { access, .. } => { target.texture = Some(info.counters.textures as _); - info.counters.textures += 1; + info.counters.textures += count; target.mutable = match access { wgt::StorageTextureAccess::ReadOnly => false, wgt::StorageTextureAccess::WriteOnly => true, @@ -667,6 +677,8 @@ impl crate::Device for super::Device { index: naga::proc::BoundsCheckPolicy::ReadZeroSkipWrite, buffer: naga::proc::BoundsCheckPolicy::ReadZeroSkipWrite, image: naga::proc::BoundsCheckPolicy::ReadZeroSkipWrite, + // TODO: support bounds checks on binding arrays + binding_array: naga::proc::BoundsCheckPolicy::Unchecked, }, }, total_push_constants, @@ -689,7 +701,7 @@ impl crate::Device for super::Device { .. } = layout.ty { - dynamic_offsets_count += 1; + dynamic_offsets_count += size; } if !layout.visibility.contains(stage_bit) { continue; @@ -700,39 +712,44 @@ impl crate::Device for super::Device { has_dynamic_offset, .. } => { - debug_assert_eq!(size, 1); - let source = &desc.buffers[entry.resource_index as usize]; - let remaining_size = - wgt::BufferSize::new(source.buffer.size - source.offset); - let binding_size = match ty { - wgt::BufferBindingType::Storage { .. } => { - source.size.or(remaining_size) - } - _ => None, - }; - bg.buffers.push(super::BufferResource { - ptr: source.buffer.as_raw(), - offset: source.offset, - dynamic_index: if has_dynamic_offset { - Some(dynamic_offsets_count - 1) - } else { - None - }, - binding_size, - binding_location: layout.binding, - }); + let start = entry.resource_index as usize; + let end = start + size as usize; + bg.buffers + .extend(desc.buffers[start..end].iter().map(|source| { + let remaining_size = + wgt::BufferSize::new(source.buffer.size - source.offset); + let binding_size = match ty { + wgt::BufferBindingType::Storage { .. } => { + source.size.or(remaining_size) + } + _ => None, + }; + super::BufferResource { + ptr: source.buffer.as_raw(), + offset: source.offset, + dynamic_index: if has_dynamic_offset { + Some(dynamic_offsets_count - 1) + } else { + None + }, + binding_size, + binding_location: layout.binding, + } + })); counter.buffers += 1; } wgt::BindingType::Sampler { .. } => { - let res = desc.samplers[entry.resource_index as usize].as_raw(); - bg.samplers.push(res); - counter.samplers += 1; + let start = entry.resource_index as usize; + let end = start + size as usize; + bg.samplers + .extend(desc.samplers[start..end].iter().map(|samp| samp.as_raw())); + counter.samplers += size; } wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => { - let start = entry.resource_index; - let end = start + size; + let start = entry.resource_index as usize; + let end = start + size as usize; bg.textures.extend( - desc.textures[start as usize..end as usize] + desc.textures[start..end] .iter() .map(|tex| tex.view.as_raw()), ); diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 20065e3a7f..8fb81c50cc 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -3,13 +3,12 @@ use super::conv; use ash::{extensions::khr, vk}; use parking_lot::Mutex; -use std::{ffi::CStr, sync::Arc}; +use std::{collections::BTreeMap, ffi::CStr, sync::Arc}; //TODO: const fn? fn indexing_features() -> wgt::Features { wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING | wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING - | wgt::Features::UNSIZED_BINDING_ARRAY } /// Aggregate of the `vk::PhysicalDevice*Features` structs used by `gfx`. @@ -212,9 +211,6 @@ impl PhysicalDeviceFeatures { uab_types.contains(super::UpdateAfterBindTypes::STORAGE_BUFFER), ) .descriptor_binding_partially_bound(needs_partially_bound) - .runtime_descriptor_array( - requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY), - ) //.sampler_filter_minmax(requested_features.contains(wgt::Features::SAMPLER_REDUCTION)) .imageless_framebuffer(private_caps.imageless_framebuffers) .timeline_semaphore(private_caps.timeline_semaphores) @@ -253,9 +249,6 @@ impl PhysicalDeviceFeatures { uab_types.contains(super::UpdateAfterBindTypes::STORAGE_BUFFER), ) .descriptor_binding_partially_bound(needs_partially_bound) - .runtime_descriptor_array( - requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY), - ) .build(), ) } else { @@ -471,9 +464,6 @@ impl PhysicalDeviceFeatures { ) { features.insert(F::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING); } - if vulkan_1_2.runtime_descriptor_array != 0 { - features |= F::UNSIZED_BINDING_ARRAY; - } if vulkan_1_2.descriptor_binding_partially_bound != 0 && !intel_windows { features |= F::PARTIALLY_BOUND_BINDING_ARRAY; } @@ -519,9 +509,6 @@ impl PhysicalDeviceFeatures { if descriptor_indexing.descriptor_binding_partially_bound != 0 && !intel_windows { features |= F::PARTIALLY_BOUND_BINDING_ARRAY; } - if descriptor_indexing.runtime_descriptor_array != 0 { - features |= F::UNSIZED_BINDING_ARRAY; - } } if let Some(ref feature) = self.depth_clip_enable { @@ -1183,6 +1170,13 @@ impl super::Adapter { capabilities.push(spv::Capability::MultiView); } + if features.intersects( + wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING + | wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + ) { + capabilities.push(spv::Capability::ShaderNonUniform); + } + let mut flags = spv::WriterFlags::empty(); flags.set( spv::WriterFlags::DEBUG, @@ -1215,7 +1209,11 @@ impl super::Adapter { } else { naga::proc::BoundsCheckPolicy::Restrict }, + // TODO: support bounds checks on binding arrays + binding_array: naga::proc::BoundsCheckPolicy::Unchecked, }, + // We need to build this separately for each invocation, so just default it out here + binding_map: BTreeMap::default(), } }; diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 55dcd82f9d..79127eb178 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -6,7 +6,12 @@ use inplace_it::inplace_or_alloc_from_iter; use parking_lot::Mutex; use std::{ - borrow::Cow, collections::hash_map::Entry, ffi::CString, num::NonZeroU32, ptr, sync::Arc, + borrow::Cow, + collections::{hash_map::Entry, BTreeMap}, + ffi::CString, + num::NonZeroU32, + ptr, + sync::Arc, }; impl super::DeviceShared { @@ -628,6 +633,7 @@ impl super::Device { &self, stage: &crate::ProgrammableStage, naga_stage: naga::ShaderStage, + binding_map: &naga::back::spv::BindingMap, ) -> Result { let stage_flags = crate::auxil::map_naga_stage(naga_stage); let vk_module = match *stage.module { @@ -640,16 +646,21 @@ impl super::Device { entry_point: stage.entry_point.to_string(), shader_stage: naga_stage, }; - let temp_options; - let options = if !runtime_checks { - temp_options = naga::back::spv::Options { - bounds_check_policies: naga::proc::BoundsCheckPolicies { + let needs_temp_options = !runtime_checks || !binding_map.is_empty(); + let mut temp_options; + let options = if needs_temp_options { + temp_options = self.naga_options.clone(); + if !runtime_checks { + temp_options.bounds_check_policies = naga::proc::BoundsCheckPolicies { index: naga::proc::BoundsCheckPolicy::Unchecked, buffer: naga::proc::BoundsCheckPolicy::Unchecked, image: naga::proc::BoundsCheckPolicy::Unchecked, - }, - ..self.naga_options.clone() - }; + binding_array: naga::proc::BoundsCheckPolicy::Unchecked, + }; + } + if !binding_map.is_empty() { + temp_options.binding_map = binding_map.clone(); + } &temp_options } else { &self.naga_options @@ -1100,6 +1111,13 @@ impl crate::Device for super::Device { let vk_info = vk::DescriptorSetLayoutCreateInfo::builder().bindings(&vk_bindings); + let binding_arrays = desc + .entries + .iter() + .enumerate() + .filter_map(|(idx, entry)| entry.count.map(|count| (idx as u32, count))) + .collect(); + let mut binding_flag_info; let binding_flag_vec; let mut requires_update_after_bind = false; @@ -1176,6 +1194,7 @@ impl crate::Device for super::Device { raw, desc_count, types: types.into_boxed_slice(), + binding_arrays, requires_update_after_bind, }) } @@ -1220,7 +1239,25 @@ impl crate::Device for super::Device { .set_object_name(vk::ObjectType::PIPELINE_LAYOUT, raw, label); } - Ok(super::PipelineLayout { raw }) + let mut binding_arrays = BTreeMap::new(); + for (group, &layout) in desc.bind_group_layouts.iter().enumerate() { + for &(binding, binding_array_size) in &layout.binding_arrays { + binding_arrays.insert( + naga::ResourceBinding { + group: group as u32, + binding, + }, + naga::back::spv::BindingInfo { + binding_array_size: Some(binding_array_size.get()), + }, + ); + } + } + + Ok(super::PipelineLayout { + raw, + binding_arrays, + }) } unsafe fn destroy_pipeline_layout(&self, pipeline_layout: super::PipelineLayout) { self.shared @@ -1266,11 +1303,15 @@ impl crate::Device for super::Device { write = match ty { vk::DescriptorType::SAMPLER => { let index = sampler_infos.len(); - let binding = desc.samplers[entry.resource_index as usize]; - let vk_info = vk::DescriptorImageInfo::builder() - .sampler(binding.raw) - .build(); - sampler_infos.push(vk_info); + let start = entry.resource_index; + let end = start + entry.count; + sampler_infos.extend(desc.samplers[start as usize..end as usize].iter().map( + |binding| { + vk::DescriptorImageInfo::builder() + .sampler(binding.raw) + .build() + }, + )); write.image_info(&sampler_infos[index..]) } vk::DescriptorType::SAMPLED_IMAGE | vk::DescriptorType::STORAGE_IMAGE => { @@ -1344,6 +1385,7 @@ impl crate::Device for super::Device { index: naga::proc::BoundsCheckPolicy::Unchecked, buffer: naga::proc::BoundsCheckPolicy::Unchecked, image: naga::proc::BoundsCheckPolicy::Unchecked, + binding_array: naga::proc::BoundsCheckPolicy::Unchecked, }; } Cow::Owned( @@ -1425,11 +1467,19 @@ impl crate::Device for super::Device { .primitive_restart_enable(desc.primitive.strip_index_format.is_some()) .build(); - let compiled_vs = self.compile_stage(&desc.vertex_stage, naga::ShaderStage::Vertex)?; + let compiled_vs = self.compile_stage( + &desc.vertex_stage, + naga::ShaderStage::Vertex, + &desc.layout.binding_arrays, + )?; stages.push(compiled_vs.create_info); let compiled_fs = match desc.fragment_stage { Some(ref stage) => { - let compiled = self.compile_stage(stage, naga::ShaderStage::Fragment)?; + let compiled = self.compile_stage( + stage, + naga::ShaderStage::Fragment, + &desc.layout.binding_arrays, + )?; stages.push(compiled.create_info); Some(compiled) } @@ -1604,7 +1654,11 @@ impl crate::Device for super::Device { &self, desc: &crate::ComputePipelineDescriptor, ) -> Result { - let compiled = self.compile_stage(&desc.stage, naga::ShaderStage::Compute)?; + let compiled = self.compile_stage( + &desc.stage, + naga::ShaderStage::Compute, + &desc.layout.binding_arrays, + )?; let vk_infos = [{ vk::ComputePipelineCreateInfo::builder() diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 41690095bc..0b7e116437 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -398,12 +398,15 @@ pub struct BindGroupLayout { raw: vk::DescriptorSetLayout, desc_count: gpu_descriptor::DescriptorTotalCount, types: Box<[(vk::DescriptorType, u32)]>, + /// Map of binding index to size, + binding_arrays: Vec<(u32, NonZeroU32)>, requires_update_after_bind: bool, } #[derive(Debug)] pub struct PipelineLayout { raw: vk::PipelineLayout, + binding_arrays: naga::back::spv::BindingMap, } #[derive(Debug)] diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 59b202059d..ec5a4b5b48 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -348,16 +348,6 @@ bitflags::bitflags! { /// /// This is a native only feature. const PARTIALLY_BOUND_BINDING_ARRAY = 1 << 22; - /// Allows the user to create unsized uniform arrays of bindings: - /// - /// eg. `uniform texture2D textures[]`. - /// - /// Supported platforms: - /// - DX12 - /// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s runtimeDescriptorArray feature - /// - /// This is a native only feature. - const UNSIZED_BINDING_ARRAY = 1 << 23; /// Allows the user to call [`RenderPass::multi_draw_indirect`] and [`RenderPass::multi_draw_indexed_indirect`]. /// /// Allows multiple indirect calls to be dispatched from a single buffer. @@ -367,7 +357,7 @@ bitflags::bitflags! { /// - Vulkan /// /// This is a native only feature. - const MULTI_DRAW_INDIRECT = 1 << 24; + const MULTI_DRAW_INDIRECT = 1 << 23; /// Allows the user to call [`RenderPass::multi_draw_indirect_count`] and [`RenderPass::multi_draw_indexed_indirect_count`]. /// /// This allows the use of a buffer containing the actual number of draw calls. @@ -377,7 +367,7 @@ bitflags::bitflags! { /// - Vulkan 1.2+ (or VK_KHR_draw_indirect_count) /// /// This is a native only feature. - const MULTI_DRAW_INDIRECT_COUNT = 1 << 25; + const MULTI_DRAW_INDIRECT_COUNT = 1 << 24; /// Allows the use of push constants: small, fast bits of memory that can be updated /// inside a [`RenderPass`]. /// @@ -394,7 +384,7 @@ bitflags::bitflags! { /// - OpenGL (emulated with uniforms) /// /// This is a native only feature. - const PUSH_CONSTANTS = 1 << 26; + const PUSH_CONSTANTS = 1 << 25; /// Allows the use of [`AddressMode::ClampToBorder`] with a border color /// other than [`SamplerBorderColor::Zero`]. /// @@ -406,7 +396,7 @@ bitflags::bitflags! { /// - OpenGL /// /// This is a web and native feature. - const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 27; + const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 26; /// Allows the user to set [`PolygonMode::Line`] in [`PrimitiveState::polygon_mode`] /// /// This allows drawing polygons/triangles as lines (wireframe) instead of filled @@ -417,7 +407,7 @@ bitflags::bitflags! { /// - Metal /// /// This is a native only feature. - const POLYGON_MODE_LINE = 1 << 28; + const POLYGON_MODE_LINE = 1 << 27; /// Allows the user to set [`PolygonMode::Point`] in [`PrimitiveState::polygon_mode`] /// /// This allows only drawing the vertices of polygons/triangles instead of filled @@ -427,7 +417,7 @@ bitflags::bitflags! { /// - Vulkan /// /// This is a native only feature. - const POLYGON_MODE_POINT = 1 << 29; + const POLYGON_MODE_POINT = 1 << 28; /// Enables ETC family of compressed textures. All ETC textures use 4x4 pixel blocks. /// ETC2 RGB and RGBA1 are 8 bytes per block. RTC2 RGBA8 and EAC are 16 bytes per block. /// @@ -442,7 +432,7 @@ bitflags::bitflags! { /// - Mobile (some) /// /// This is a native-only feature. - const TEXTURE_COMPRESSION_ETC2 = 1 << 30; + const TEXTURE_COMPRESSION_ETC2 = 1 << 29; /// Enables ASTC family of compressed textures. ASTC textures use pixel blocks varying from 4x4 to 12x12. /// Blocks are always 16 bytes. /// @@ -457,7 +447,7 @@ bitflags::bitflags! { /// - Mobile (some) /// /// This is a native-only feature. - const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 31; + const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 30; /// Enables device specific texture format features. /// /// See `TextureFormatFeatures` for a listing of the features in question. @@ -469,7 +459,7 @@ bitflags::bitflags! { /// This extension does not enable additional formats. /// /// This is a native-only feature. - const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 32; + const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 31; /// Enables 64-bit floating point types in SPIR-V shaders. /// /// Note: even when supported by GPU hardware, 64-bit floating point operations are @@ -479,7 +469,7 @@ bitflags::bitflags! { /// - Vulkan /// /// This is a native-only feature. - const SHADER_FLOAT64 = 1 << 33; + const SHADER_FLOAT64 = 1 << 32; /// Enables using 64-bit types for vertex attributes. /// /// Requires SHADER_FLOAT64. @@ -487,7 +477,7 @@ bitflags::bitflags! { /// Supported Platforms: N/A /// /// This is a native-only feature. - const VERTEX_ATTRIBUTE_64BIT = 1 << 34; + const VERTEX_ATTRIBUTE_64BIT = 1 << 33; /// Allows the user to set a overestimation-conservative-rasterization in [`PrimitiveState::conservative`] /// /// Processing of degenerate triangles/lines is hardware specific. @@ -497,7 +487,7 @@ bitflags::bitflags! { /// - Vulkan /// /// This is a native only feature. - const CONSERVATIVE_RASTERIZATION = 1 << 35; + const CONSERVATIVE_RASTERIZATION = 1 << 34; /// Enables bindings of writable storage buffers and textures visible to vertex shaders. /// /// Note: some (tiled-based) platforms do not support vertex shaders with any side-effects. @@ -506,14 +496,14 @@ bitflags::bitflags! { /// - All /// /// This is a native-only feature. - const VERTEX_WRITABLE_STORAGE = 1 << 36; + const VERTEX_WRITABLE_STORAGE = 1 << 35; /// Enables clear to zero for textures. /// /// Supported platforms: /// - All /// /// This is a native only feature. - const CLEAR_TEXTURE = 1 << 37; + const CLEAR_TEXTURE = 1 << 36; /// Enables creating shader modules from SPIR-V binary data (unsafe). /// /// SPIR-V data is not parsed or interpreted in any way; you can use @@ -525,7 +515,7 @@ bitflags::bitflags! { /// Vulkan implementation. /// /// This is a native only feature. - const SPIRV_SHADER_PASSTHROUGH = 1 << 38; + const SPIRV_SHADER_PASSTHROUGH = 1 << 37; /// Enables `builtin(primitive_index)` in fragment shaders. /// /// Note: enables geometry processing for pipelines using the builtin. @@ -536,14 +526,14 @@ bitflags::bitflags! { /// - Vulkan /// /// This is a native only feature. - const SHADER_PRIMITIVE_INDEX = 1 << 39; + const SHADER_PRIMITIVE_INDEX = 1 << 38; /// Enables multiview render passes and `builtin(view_index)` in vertex shaders. /// /// Supported platforms: /// - Vulkan /// /// This is a native only feature. - const MULTIVIEW = 1 << 40; + const MULTIVIEW = 1 << 39; /// Enables normalized `16-bit` texture formats. /// /// Supported platforms: @@ -552,7 +542,7 @@ bitflags::bitflags! { /// - Metal /// /// This is a native only feature. - const TEXTURE_FORMAT_16BIT_NORM = 1 << 41; + const TEXTURE_FORMAT_16BIT_NORM = 1 << 40; /// Allows the use of [`AddressMode::ClampToBorder`] with a border color /// of [`SamplerBorderColor::Zero`]. /// @@ -564,12 +554,12 @@ bitflags::bitflags! { /// - OpenGL /// /// This is a native only feature. - const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 42; + const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 41; /// Supported Platforms: /// - Metal /// /// This is a native-only feature. - const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 43; + const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 42; } } diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 63f0dbb338..404c72a9d5 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -69,7 +69,6 @@ test = true [[example]] name="texture-arrays" -required-features = ["spirv"] test = true [[example]] @@ -139,20 +138,20 @@ env_logger = "0.9" [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "85056524" +rev = "1aa91549" #version = "0.8" optional = true # used to test all the example shaders [dev-dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "85056524" +rev = "1aa91549" #version = "0.8" features = ["wgsl-in"] [target.'cfg(target_arch = "wasm32")'.dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "85056524" +rev = "1aa91549" #version = "0.8" features = ["wgsl-out"] diff --git a/wgpu/examples/README.md b/wgpu/examples/README.md index 70b01aadda..efbf6fa776 100644 --- a/wgpu/examples/README.md +++ b/wgpu/examples/README.md @@ -21,7 +21,6 @@ All framework-based examples render to the window and are reftested against the | implicit layout | | | | :star: | | | | | | | | sampled color textures | :star: | :star: | :star: | :star: | | | :star: | :star: | :star: | :star: | | storage textures | :star: | | | | | | | | | | -| binding array | | | | | | | | :star: | | | | comparison samplers | | | | | | :star: | | | | | | subresource views | | | | :star: | | :star: | | | | | | cubemaps | | | | | | | :star: | | | | @@ -36,9 +35,9 @@ All framework-based examples render to the window and are reftested against the | compute passes | :star: | | | | | | | | | | | error scopes | | | :star: | | | | | | | | | *optional extensions* | | | | | | | | :star: | | | -| - SPIR-V shaders | | | | | | | | :star: | | | -| - binding indexing | | | | | | | | :star: | | | -| - push constants | | | | | | | | :star: | | | +| - SPIR-V shaders | | | | | | | | | | | +| - binding array | | | | | | | | :star: | | | +| - push constants | | | | | | | | | | | | - depth clamping | | | | | | :star: | | | | | | - compressed textures | | | | | | | :star: | | | | | - polygon mode | | | :star: | | | | | | | | diff --git a/wgpu/examples/texture-arrays/constant.frag b/wgpu/examples/texture-arrays/constant.frag deleted file mode 100644 index a622de3799..0000000000 --- a/wgpu/examples/texture-arrays/constant.frag +++ /dev/null @@ -1,19 +0,0 @@ -#version 450 - -layout(location = 0) in vec2 v_TexCoord; -layout(location = 1) flat in int v_Index; // dynamically non-uniform -layout(location = 0) out vec4 o_Color; - -layout(set = 0, binding = 0) uniform texture2D u_Textures[2]; -layout(set = 0, binding = 1) uniform sampler u_Sampler[2]; - -void main() { - if (v_Index == 0) { - o_Color = vec4(texture(sampler2D(u_Textures[0], u_Sampler[0]), v_TexCoord).rgb, 1.0); - } else if (v_Index == 1) { - o_Color = vec4(texture(sampler2D(u_Textures[1], u_Sampler[1]), v_TexCoord).rgb, 1.0); - } else { - // We need to write something to output color - o_Color = vec4(0.0, 0.0, 1.0, 0.0); - } -} diff --git a/wgpu/examples/texture-arrays/constant.frag.spv b/wgpu/examples/texture-arrays/constant.frag.spv deleted file mode 100644 index 2ac4287204..0000000000 Binary files a/wgpu/examples/texture-arrays/constant.frag.spv and /dev/null differ diff --git a/wgpu/examples/texture-arrays/indexing.wgsl b/wgpu/examples/texture-arrays/indexing.wgsl new file mode 100644 index 0000000000..0f952264c4 --- /dev/null +++ b/wgpu/examples/texture-arrays/indexing.wgsl @@ -0,0 +1,61 @@ +struct VertexInput { + @location(0) position: vec2, + @location(1) tex_coord: vec2, + @location(2) index: i32, +} + +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) tex_coord: vec2, + @location(1) index: i32, +} + +@vertex +fn vert_main(vertex: VertexInput) -> VertexOutput { + var outval: VertexOutput; + outval.position = vec4(vertex.position.x, vertex.position.y, 0.0, 1.0); + outval.tex_coord = vertex.tex_coord; + outval.index = vertex.index; + return outval; +} + +struct FragmentInput { + @location(0) tex_coord: vec2, + @location(1) index: i32, +} + +@group(0) @binding(0) +var texture_array_top: binding_array>; +@group(0) @binding(1) +var texture_array_bottom: binding_array>; +@group(0) @binding(2) +var sampler_array: binding_array; + +struct Uniforms { + index: u32, +} + +@group(0) @binding(3) +var uniforms: Uniforms; + +@fragment +fn uniform_main(fragment: FragmentInput) -> @location(0) vec4 { + var outval: vec3; + if fragment.tex_coord.y <= 0.5 { + outval = textureSampleLevel( + texture_array_top[uniforms.index], + sampler_array[uniforms.index], + fragment.tex_coord, + 0.0 + ).rgb; + } else { + outval = textureSampleLevel( + texture_array_bottom[uniforms.index], + sampler_array[uniforms.index], + fragment.tex_coord, + 0.0 + ).rgb; + } + + return vec4(outval.x, outval.y, outval.z, 1.0); +} diff --git a/wgpu/examples/texture-arrays/main.rs b/wgpu/examples/texture-arrays/main.rs index af074e21d9..8c68585cc4 100644 --- a/wgpu/examples/texture-arrays/main.rs +++ b/wgpu/examples/texture-arrays/main.rs @@ -2,7 +2,7 @@ mod framework; use bytemuck::{Pod, Zeroable}; -use std::num::NonZeroU32; +use std::num::{NonZeroU32, NonZeroU64}; use wgpu::util::DeviceExt; #[repr(C)] @@ -51,12 +51,16 @@ fn create_indices() -> Vec { enum Color { Red, Green, + Blue, + White, } fn create_texture_data(color: Color) -> [u8; 4] { match color { Color::Red => [255, 0, 0, 255], Color::Green => [0, 255, 0, 255], + Color::Blue => [0, 0, 255, 255], + Color::White => [255, 255, 255, 255], } } @@ -71,18 +75,10 @@ struct Example { impl framework::Example for Example { fn optional_features() -> wgpu::Features { - wgpu::Features::UNSIZED_BINDING_ARRAY - | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING - | wgpu::Features::PUSH_CONSTANTS + wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING } fn required_features() -> wgpu::Features { - wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::SPIRV_SHADER_PASSTHROUGH - } - fn required_limits() -> wgpu::Limits { - wgpu::Limits { - max_push_constant_size: 4, - ..wgpu::Limits::default() - } + wgpu::Features::TEXTURE_BINDING_ARRAY } fn init( config: &wgpu::SurfaceConfiguration, @@ -91,24 +87,45 @@ impl framework::Example for Example { queue: &wgpu::Queue, ) -> Self { let mut uniform_workaround = false; - let vs_module = device.create_shader_module(&wgpu::include_spirv!("shader.vert.spv")); - let fs_source = match device.features() { - f if f.contains(wgpu::Features::UNSIZED_BINDING_ARRAY) => { - wgpu::include_spirv_raw!("unsized-non-uniform.frag.spv") + let base_shader_module = device.create_shader_module(&wgpu::include_wgsl!("indexing.wgsl")); + let env_override = match std::env::var("WGPU_TEXTURE_ARRAY_STYLE") { + Ok(value) => match &*value.to_lowercase() { + "nonuniform" | "non_uniform" => Some(true), + "uniform" => Some(false), + _ => None, + }, + Err(_) => None, + }; + let fragment_entry_point = match (device.features(), env_override) { + (_, Some(false)) => { + uniform_workaround = true; + "uniform_main" } - f if f.contains( - wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - ) => + (_, Some(true)) => "non_uniform_main", + (f, _) + if f.contains( + wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + ) => { - wgpu::include_spirv_raw!("non-uniform.frag.spv") + "non_uniform_main" } - f if f.contains(wgpu::Features::TEXTURE_BINDING_ARRAY) => { + _ => { uniform_workaround = true; - wgpu::include_spirv_raw!("uniform.frag.spv") + "uniform_main" } - _ => unreachable!(), }; - let fs_module = unsafe { device.create_shader_module_spirv(&fs_source) }; + let non_uniform_shader_module; + // TODO: Because naga's capibilities are evaluated on validate, not on write, we cannot make a shader module with unsupported + // capabilities even if we don't use it. So for now put it in a separate module. + let fragment_shader_module = if !uniform_workaround { + non_uniform_shader_module = + device.create_shader_module(&wgpu::include_wgsl!("non_uniform_indexing.wgsl")); + &non_uniform_shader_module + } else { + &base_shader_module + }; + + println!("Using fragment entry point '{}'", fragment_entry_point); let vertex_size = std::mem::size_of::(); let vertex_data = create_vertices(); @@ -125,8 +142,19 @@ impl framework::Example for Example { usage: wgpu::BufferUsages::INDEX, }); + let mut texture_index_buffer_contents = vec![0u32; 128]; + texture_index_buffer_contents[0] = 0; + texture_index_buffer_contents[64] = 1; + let texture_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&texture_index_buffer_contents), + usage: wgpu::BufferUsages::UNIFORM, + }); + let red_texture_data = create_texture_data(Color::Red); let green_texture_data = create_texture_data(Color::Green); + let blue_texture_data = create_texture_data(Color::Blue); + let white_texture_data = create_texture_data(Color::White); let texture_descriptor = wgpu::TextureDescriptor { size: wgpu::Extent3d::default(), @@ -145,9 +173,19 @@ impl framework::Example for Example { label: Some("green"), ..texture_descriptor }); + let blue_texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("blue"), + ..texture_descriptor + }); + let white_texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("white"), + ..texture_descriptor + }); let red_texture_view = red_texture.create_view(&wgpu::TextureViewDescriptor::default()); let green_texture_view = green_texture.create_view(&wgpu::TextureViewDescriptor::default()); + let blue_texture_view = blue_texture.create_view(&wgpu::TextureViewDescriptor::default()); + let white_texture_view = white_texture.create_view(&wgpu::TextureViewDescriptor::default()); queue.write_texture( red_texture.as_image_copy(), @@ -169,6 +207,26 @@ impl framework::Example for Example { }, wgpu::Extent3d::default(), ); + queue.write_texture( + blue_texture.as_image_copy(), + &blue_texture_data, + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(NonZeroU32::new(4).unwrap()), + rows_per_image: None, + }, + wgpu::Extent3d::default(), + ); + queue.write_texture( + white_texture.as_image_copy(), + &white_texture_data, + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(NonZeroU32::new(4).unwrap()), + rows_per_image: None, + }, + wgpu::Extent3d::default(), + ); let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default()); @@ -188,9 +246,29 @@ impl framework::Example for Example { wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: NonZeroU32::new(2), + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), count: NonZeroU32::new(2), }, + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(NonZeroU64::new(4).unwrap()), + }, + count: None, + }, ], }); @@ -205,8 +283,23 @@ impl framework::Example for Example { }, wgpu::BindGroupEntry { binding: 1, + resource: wgpu::BindingResource::TextureViewArray(&[ + &blue_texture_view, + &white_texture_view, + ]), + }, + wgpu::BindGroupEntry { + binding: 2, resource: wgpu::BindingResource::SamplerArray(&[&sampler, &sampler]), }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { + buffer: &texture_index_buffer, + offset: 0, + size: Some(NonZeroU64::new(4).unwrap()), + }), + }, ], layout: &bind_group_layout, label: Some("bind group"), @@ -215,14 +308,7 @@ impl framework::Example for Example { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("main"), bind_group_layouts: &[&bind_group_layout], - push_constant_ranges: if uniform_workaround { - &[wgpu::PushConstantRange { - stages: wgpu::ShaderStages::FRAGMENT, - range: 0..4, - }] - } else { - &[] - }, + push_constant_ranges: &[], }); let index_format = wgpu::IndexFormat::Uint16; @@ -231,8 +317,8 @@ impl framework::Example for Example { label: None, layout: Some(&pipeline_layout), vertex: wgpu::VertexState { - module: &vs_module, - entry_point: "main", + module: &base_shader_module, + entry_point: "vert_main", buffers: &[wgpu::VertexBufferLayout { array_stride: vertex_size as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, @@ -240,8 +326,8 @@ impl framework::Example for Example { }], }, fragment: Some(wgpu::FragmentState { - module: &fs_module, - entry_point: "main", + module: fragment_shader_module, + entry_point: fragment_entry_point, targets: &[config.format.into()], }), primitive: wgpu::PrimitiveState { @@ -298,15 +384,15 @@ impl framework::Example for Example { }); rpass.set_pipeline(&self.pipeline); - rpass.set_bind_group(0, &self.bind_group, &[]); rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); rpass.set_index_buffer(self.index_buffer.slice(..), self.index_format); if self.uniform_workaround { - rpass.set_push_constants(wgpu::ShaderStages::FRAGMENT, 0, bytemuck::cast_slice(&[0])); + rpass.set_bind_group(0, &self.bind_group, &[0]); rpass.draw_indexed(0..6, 0, 0..1); - rpass.set_push_constants(wgpu::ShaderStages::FRAGMENT, 0, bytemuck::cast_slice(&[1])); + rpass.set_bind_group(0, &self.bind_group, &[256]); rpass.draw_indexed(6..12, 0, 0..1); } else { + rpass.set_bind_group(0, &self.bind_group, &[0]); rpass.draw_indexed(0..12, 0, 0..1); } @@ -320,60 +406,28 @@ fn main() { framework::run::("texture-arrays"); } -// This fails due to an issue with naga https://github.com/gfx-rs/wgpu/issues/1532 -#[test] -fn texture_arrays_constant() { - framework::test::(framework::FrameworkRefTest { - image_path: "/examples/texture-arrays/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::default(), - base_test_parameters: framework::test_common::TestParameters::default().failure(), - tolerance: 0, - max_outliers: 0, - }); -} - -// This fails due to an issue with naga https://github.com/gfx-rs/wgpu/issues/1532 #[test] fn texture_arrays_uniform() { framework::test::(framework::FrameworkRefTest { image_path: "/examples/texture-arrays/screenshot.png", width: 1024, height: 768, - optional_features: wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::PUSH_CONSTANTS, - base_test_parameters: framework::test_common::TestParameters::default().failure(), + optional_features: wgpu::Features::empty(), + base_test_parameters: framework::test_common::TestParameters::default(), tolerance: 0, max_outliers: 0, }); } -// This fails due to an issue with naga https://github.com/gfx-rs/wgpu/issues/1532 #[test] fn texture_arrays_non_uniform() { framework::test::(framework::FrameworkRefTest { image_path: "/examples/texture-arrays/screenshot.png", width: 1024, height: 768, - optional_features: wgpu::Features::TEXTURE_BINDING_ARRAY - | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - base_test_parameters: framework::test_common::TestParameters::default().failure(), - tolerance: 0, - max_outliers: 0, - }); -} - -// This fails due to an issue with naga https://github.com/gfx-rs/wgpu/issues/1532 -#[test] -fn texture_arrays_unsized_non_uniform() { - framework::test::(framework::FrameworkRefTest { - image_path: "/examples/texture-arrays/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::TEXTURE_BINDING_ARRAY - | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING - | wgpu::Features::UNSIZED_BINDING_ARRAY, - base_test_parameters: framework::test_common::TestParameters::default().failure(), + optional_features: + wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + base_test_parameters: framework::test_common::TestParameters::default(), tolerance: 0, max_outliers: 0, }); diff --git a/wgpu/examples/texture-arrays/non-uniform.frag b/wgpu/examples/texture-arrays/non-uniform.frag deleted file mode 100644 index 990d0dd366..0000000000 --- a/wgpu/examples/texture-arrays/non-uniform.frag +++ /dev/null @@ -1,14 +0,0 @@ -#version 450 - -#extension GL_EXT_nonuniform_qualifier : require - -layout(location = 0) in vec2 v_TexCoord; -layout(location = 1) nonuniformEXT flat in int v_Index; // dynamically non-uniform -layout(location = 0) out vec4 o_Color; - -layout(set = 0, binding = 0) uniform texture2D u_Textures[2]; -layout(set = 0, binding = 1) uniform sampler u_Sampler[2]; - -void main() { - o_Color = vec4(texture(sampler2D(u_Textures[v_Index], u_Sampler[v_Index]), v_TexCoord).rgb, 1.0); -} diff --git a/wgpu/examples/texture-arrays/non-uniform.frag.spv b/wgpu/examples/texture-arrays/non-uniform.frag.spv deleted file mode 100644 index 1d0844771d..0000000000 Binary files a/wgpu/examples/texture-arrays/non-uniform.frag.spv and /dev/null differ diff --git a/wgpu/examples/texture-arrays/non_uniform_indexing.wgsl b/wgpu/examples/texture-arrays/non_uniform_indexing.wgsl new file mode 100644 index 0000000000..7fb30e7929 --- /dev/null +++ b/wgpu/examples/texture-arrays/non_uniform_indexing.wgsl @@ -0,0 +1,33 @@ +struct FragmentInput { + @location(0) tex_coord: vec2, + @location(1) index: i32, +} + +@group(0) @binding(0) +var texture_array_top: binding_array>; +@group(0) @binding(1) +var texture_array_bottom: binding_array>; +@group(0) @binding(2) +var sampler_array: binding_array; + +@fragment +fn non_uniform_main(fragment: FragmentInput) -> @location(0) vec4 { + var outval: vec3; + if fragment.tex_coord.y <= 0.5 { + outval = textureSampleLevel( + texture_array_top[fragment.index], + sampler_array[fragment.index], + fragment.tex_coord, + 0.0 + ).rgb; + } else { + outval = textureSampleLevel( + texture_array_bottom[fragment.index], + sampler_array[fragment.index], + fragment.tex_coord, + 0.0 + ).rgb; + } + + return vec4(outval.x, outval.y, outval.z, 1.0); +} diff --git a/wgpu/examples/texture-arrays/screenshot.png b/wgpu/examples/texture-arrays/screenshot.png new file mode 100644 index 0000000000..3bafceb552 Binary files /dev/null and b/wgpu/examples/texture-arrays/screenshot.png differ diff --git a/wgpu/examples/texture-arrays/shader.vert b/wgpu/examples/texture-arrays/shader.vert deleted file mode 100644 index cd7d2c7694..0000000000 --- a/wgpu/examples/texture-arrays/shader.vert +++ /dev/null @@ -1,13 +0,0 @@ -#version 450 - -layout(location = 0) in vec2 a_Pos; -layout(location = 1) in vec2 a_TexCoord; -layout(location = 2) in int a_Index; -layout(location = 0) out vec2 v_TexCoord; -layout(location = 1) flat out int v_Index; - -void main() { - v_TexCoord = a_TexCoord; - v_Index = a_Index; - gl_Position = vec4(a_Pos, 0.0, 1.0); -} diff --git a/wgpu/examples/texture-arrays/shader.vert.spv b/wgpu/examples/texture-arrays/shader.vert.spv deleted file mode 100644 index c1ca86d7fb..0000000000 Binary files a/wgpu/examples/texture-arrays/shader.vert.spv and /dev/null differ diff --git a/wgpu/examples/texture-arrays/uniform.frag b/wgpu/examples/texture-arrays/uniform.frag deleted file mode 100644 index bda88f3ed7..0000000000 --- a/wgpu/examples/texture-arrays/uniform.frag +++ /dev/null @@ -1,15 +0,0 @@ -#version 450 - -layout(location = 0) in vec2 v_TexCoord; -layout(location = 1) flat in int v_Index; // dynamically non-uniform -layout(location = 0) out vec4 o_Color; - -layout(set = 0, binding = 0) uniform texture2D u_Textures[2]; -layout(set = 0, binding = 1) uniform sampler u_Sampler[2]; -layout(push_constant) uniform Uniforms { - int u_Index; // dynamically uniform -}; - -void main() { - o_Color = vec4(texture(sampler2D(u_Textures[u_Index], u_Sampler[u_Index]), v_TexCoord).rgb, 1.0); -} diff --git a/wgpu/examples/texture-arrays/uniform.frag.spv b/wgpu/examples/texture-arrays/uniform.frag.spv deleted file mode 100644 index aa3ee039c5..0000000000 Binary files a/wgpu/examples/texture-arrays/uniform.frag.spv and /dev/null differ diff --git a/wgpu/examples/texture-arrays/unsized-non-uniform.frag b/wgpu/examples/texture-arrays/unsized-non-uniform.frag deleted file mode 100644 index e49a83103e..0000000000 --- a/wgpu/examples/texture-arrays/unsized-non-uniform.frag +++ /dev/null @@ -1,14 +0,0 @@ -#version 450 - -#extension GL_EXT_nonuniform_qualifier : require - -layout(location = 0) in vec2 v_TexCoord; -layout(location = 1) nonuniformEXT flat in int v_Index; // dynamically non-uniform -layout(location = 0) out vec4 o_Color; - -layout(set = 0, binding = 0) uniform texture2D u_Textures[]; -layout(set = 0, binding = 1) uniform sampler u_Sampler[]; - -void main() { - o_Color = vec4(texture(sampler2D(u_Textures[v_Index], u_Sampler[v_Index]), v_TexCoord).rgb, 1.0); -} diff --git a/wgpu/examples/texture-arrays/unsized-non-uniform.frag.spv b/wgpu/examples/texture-arrays/unsized-non-uniform.frag.spv deleted file mode 100644 index 1c98e94211..0000000000 Binary files a/wgpu/examples/texture-arrays/unsized-non-uniform.frag.spv and /dev/null differ diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index d6478044b1..8c3ff54a34 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -1325,8 +1325,8 @@ impl crate::Context for Context { )); if let Some(cause) = error { if let wgc::pipeline::CreateRenderPipelineError::Internal { stage, ref error } = cause { - log::warn!("Shader translation error for stage {:?}: {}", stage, error); - log::warn!("Please report it to https://github.com/gfx-rs/naga"); + log::error!("Shader translation error for stage {:?}: {}", stage, error); + log::error!("Please report it to https://github.com/gfx-rs/naga"); } self.handle_error( &device.error_sink,