From 8cd17aafe39f6ab46b2a91b34246422bdc46bf92 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 14 Jul 2021 01:04:37 -0400 Subject: [PATCH] Switch read-only storage textures to be exclusive and behind a feature --- wgpu-core/src/binding_model.rs | 4 ++-- wgpu-core/src/conv.rs | 4 ++-- wgpu-core/src/device/mod.rs | 35 +++++++++++++++++++++++----------- wgpu-core/src/track/buffer.rs | 20 +++++++++---------- wgpu-core/src/track/texture.rs | 4 ++-- wgpu-core/src/validation.rs | 32 ++++++++++++++++++++++++++----- wgpu-hal/src/dx12/command.rs | 4 ++-- wgpu-hal/src/dx12/conv.rs | 20 ++++++++----------- wgpu-hal/src/dx12/device.rs | 10 +++------- wgpu-hal/src/gles/command.rs | 4 ++-- wgpu-hal/src/gles/queue.rs | 4 ++-- wgpu-hal/src/lib.rs | 34 ++++++++++++++++----------------- wgpu-hal/src/metal/conv.rs | 4 ++-- wgpu-hal/src/vulkan/conv.rs | 14 +++++++------- wgpu-types/src/lib.rs | 21 +++++++++++--------- wgpu/src/backend/web.rs | 6 +++--- 16 files changed, 125 insertions(+), 95 deletions(-) diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 0e0cd78c13..b20e314825 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -149,8 +149,8 @@ pub enum CreateBindGroupError { }, #[error("bound texture views can not have both depth and stencil aspects enabled")] DepthStencilAspect, - #[error("the adapter does not support simultaneous read + write storage texture access for the format {0:?}")] - StorageReadWriteNotSupported(wgt::TextureFormat), + #[error("the adapter does not support read access for storages texture of format {0:?}")] + StorageReadNotSupported(wgt::TextureFormat), #[error(transparent)] ResourceUsageConflict(#[from] UsageConflict), } diff --git a/wgpu-core/src/conv.rs b/wgpu-core/src/conv.rs index b6fef4a8ed..e350138a83 100644 --- a/wgpu-core/src/conv.rs +++ b/wgpu-core/src/conv.rs @@ -51,7 +51,7 @@ pub fn map_buffer_usage(usage: wgt::BufferUsages) -> hal::BufferUses { usage.contains(wgt::BufferUsages::UNIFORM), ); u.set( - hal::BufferUses::STORAGE_LOAD | hal::BufferUses::STORAGE_STORE, + hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_WRITE, usage.contains(wgt::BufferUsages::STORAGE), ); u.set( @@ -79,7 +79,7 @@ pub fn map_texture_usage( usage.contains(wgt::TextureUsages::SAMPLED), ); u.set( - hal::TextureUses::STORAGE_LOAD | hal::TextureUses::STORAGE_STORE, + hal::TextureUses::STORAGE_READ | hal::TextureUses::STORAGE_WRITE, usage.contains(wgt::TextureUsages::STORAGE), ); let is_color = aspect.contains(hal::FormatAspects::COLOR); diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 1b9bd3ac34..a38f8c8753 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -737,8 +737,8 @@ impl Device { } wgt::TextureViewDimension::D3 => { hal::TextureUses::SAMPLED - | hal::TextureUses::STORAGE_LOAD - | hal::TextureUses::STORAGE_STORE + | hal::TextureUses::STORAGE_READ + | hal::TextureUses::STORAGE_WRITE } _ => hal::TextureUses::all(), }; @@ -785,7 +785,7 @@ impl Device { samples: texture.desc.sample_count, // once a storage - forever a storage sampled_internal_use: if texture.desc.usage.contains(wgt::TextureUsages::STORAGE) { - hal::TextureUses::SAMPLED | hal::TextureUses::STORAGE_LOAD + hal::TextureUses::SAMPLED | hal::TextureUses::STORAGE_READ } else { hal::TextureUses::SAMPLED }, @@ -900,7 +900,7 @@ impl Device { ); let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps) .validate(&module)?; - let interface = validation::Interface::new(&module, &info); + let interface = validation::Interface::new(&module, &info, self.features); let hal_shader = hal::ShaderInput::Naga(hal::NagaShader { module, info }); let hal_desc = hal::ShaderModuleDescriptor { @@ -1054,8 +1054,12 @@ impl Device { | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY, ), match access { - wgt::StorageTextureAccess::ReadOnly => WritableStorage::No, wgt::StorageTextureAccess::WriteOnly => WritableStorage::Yes, + wgt::StorageTextureAccess::ReadOnly => { + required_features |= + wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES; + WritableStorage::No + } wgt::StorageTextureAccess::ReadWrite => { required_features |= wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES; @@ -1174,9 +1178,9 @@ impl Device { wgt::BufferBindingType::Storage { read_only } => ( wgt::BufferUsages::STORAGE, if read_only { - hal::BufferUses::STORAGE_LOAD + hal::BufferUses::STORAGE_READ } else { - hal::BufferUses::STORAGE_STORE + hal::BufferUses::STORAGE_WRITE }, limits.max_storage_buffer_binding_size, ), @@ -1594,18 +1598,27 @@ impl Device { }); } let internal_use = match access { - wgt::StorageTextureAccess::ReadOnly => hal::TextureUses::STORAGE_LOAD, - wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_STORE, + wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_WRITE, + wgt::StorageTextureAccess::ReadOnly => { + if !view + .format_features + .flags + .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE) + { + return Err(Error::StorageReadNotSupported(view.desc.format)); + } + hal::TextureUses::STORAGE_READ + } wgt::StorageTextureAccess::ReadWrite => { if !view .format_features .flags .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE) { - return Err(Error::StorageReadWriteNotSupported(view.desc.format)); + return Err(Error::StorageReadNotSupported(view.desc.format)); } - hal::TextureUses::STORAGE_STORE | hal::TextureUses::STORAGE_LOAD + hal::TextureUses::STORAGE_WRITE | hal::TextureUses::STORAGE_READ } }; Ok((wgt::TextureUsages::STORAGE, internal_use)) diff --git a/wgpu-core/src/track/buffer.rs b/wgpu-core/src/track/buffer.rs index 35cceb253a..5e140fe113 100644 --- a/wgpu-core/src/track/buffer.rs +++ b/wgpu-core/src/track/buffer.rs @@ -8,7 +8,7 @@ impl PendingTransition { fn collapse(self) -> Result { if self.usage.start.is_empty() || self.usage.start == self.usage.end - || !BufferUses::WRITE_ALL.intersects(self.usage.start | self.usage.end) + || !BufferUses::EXCLUSIVE.intersects(self.usage.start | self.usage.end) { Ok(self.usage.start | self.usage.end) } else { @@ -129,11 +129,11 @@ mod test { }; let id = Id::dummy(); assert_eq!( - bs.change(id, (), BufferUses::STORAGE_STORE, None), + bs.change(id, (), BufferUses::STORAGE_WRITE, None), Err(PendingTransition { id, selector: (), - usage: BufferUses::INDEX..BufferUses::STORAGE_STORE, + usage: BufferUses::INDEX..BufferUses::STORAGE_WRITE, }), ); bs.change(id, (), BufferUses::VERTEX, None).unwrap(); @@ -145,7 +145,7 @@ mod test { fn change_replace() { let mut bs = Unit { first: None, - last: BufferUses::STORAGE_STORE, + last: BufferUses::STORAGE_WRITE, }; let id = Id::dummy(); let mut list = Vec::new(); @@ -156,33 +156,33 @@ mod test { &[PendingTransition { id, selector: (), - usage: BufferUses::STORAGE_STORE..BufferUses::VERTEX, + usage: BufferUses::STORAGE_WRITE..BufferUses::VERTEX, }], ); assert_eq!( bs, Unit { - first: Some(BufferUses::STORAGE_STORE), + first: Some(BufferUses::STORAGE_WRITE), last: BufferUses::VERTEX, } ); list.clear(); - bs.change(id, (), BufferUses::STORAGE_STORE, Some(&mut list)) + bs.change(id, (), BufferUses::STORAGE_WRITE, Some(&mut list)) .unwrap(); assert_eq!( &list, &[PendingTransition { id, selector: (), - usage: BufferUses::VERTEX..BufferUses::STORAGE_STORE, + usage: BufferUses::VERTEX..BufferUses::STORAGE_WRITE, }], ); assert_eq!( bs, Unit { - first: Some(BufferUses::STORAGE_STORE), - last: BufferUses::STORAGE_STORE, + first: Some(BufferUses::STORAGE_WRITE), + last: BufferUses::STORAGE_WRITE, } ); } diff --git a/wgpu-core/src/track/texture.rs b/wgpu-core/src/track/texture.rs index d3fe33528f..d1cb88ddf7 100644 --- a/wgpu-core/src/track/texture.rs +++ b/wgpu-core/src/track/texture.rs @@ -27,7 +27,7 @@ impl PendingTransition { fn collapse(self) -> Result { if self.usage.start.is_empty() || self.usage.start == self.usage.end - || !TextureUses::WRITE_ALL.intersects(self.usage.start | self.usage.end) + || !TextureUses::EXCLUSIVE.intersects(self.usage.start | self.usage.end) { Ok(self.usage.start | self.usage.end) } else { @@ -243,7 +243,7 @@ mod test { ts.mips.push(PlaneStates::from_slice(&[ (1..3, Unit::new(TextureUses::SAMPLED)), (3..5, Unit::new(TextureUses::SAMPLED)), - (5..6, Unit::new(TextureUses::STORAGE_LOAD)), + (5..6, Unit::new(TextureUses::STORAGE_READ)), ])); assert_eq!( diff --git a/wgpu-core/src/validation.rs b/wgpu-core/src/validation.rs index fd8c5fefb6..1f063b7486 100644 --- a/wgpu-core/src/validation.rs +++ b/wgpu-core/src/validation.rs @@ -107,6 +107,7 @@ struct EntryPoint { #[derive(Debug)] pub struct Interface { + features: wgt::Features, resources: naga::Arena, entry_points: FastHashMap<(naga::ShaderStage, String), EntryPoint>, } @@ -188,6 +189,10 @@ pub enum BindingError { InconsistentlyDerivedType, #[error("texture format {0:?} is not supported for storage use")] BadStorageFormat(wgt::TextureFormat), + #[error( + "storage texture usage {0:?} doesn't have a matching supported `StorageTextureAccess`" + )] + UnsupportedTextureStorageAccess(GlobalUse), } #[derive(Clone, Debug, Error)] @@ -460,7 +465,11 @@ impl Resource { } } - fn derive_binding_type(&self, shader_usage: GlobalUse) -> Result { + fn derive_binding_type( + &self, + shader_usage: GlobalUse, + features: wgt::Features, + ) -> Result { Ok(match self.ty { ResourceType::Buffer { size } => BindingType::Buffer { ty: match self.class { @@ -509,10 +518,18 @@ impl Resource { multisampled: false, }, naga::ImageClass::Storage(format) => BindingType::StorageTexture { - access: if shader_usage.contains(GlobalUse::WRITE) { + access: if shader_usage == GlobalUse::WRITE || shader_usage.is_empty() { wgt::StorageTextureAccess::WriteOnly - } else { + } else if !features + .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES) + { + return Err(BindingError::UnsupportedTextureStorageAccess( + shader_usage, + )); + } else if shader_usage == GlobalUse::READ { wgt::StorageTextureAccess::ReadOnly + } else { + wgt::StorageTextureAccess::ReadWrite }, view_dimension, format: { @@ -783,7 +800,11 @@ impl Interface { list.push(varying); } - pub fn new(module: &naga::Module, info: &naga::valid::ModuleInfo) -> Self { + pub fn new( + module: &naga::Module, + info: &naga::valid::ModuleInfo, + features: wgt::Features, + ) -> Self { let mut resources = naga::Arena::new(); let mut resource_mapping = FastHashMap::default(); for (var_handle, var) in module.global_variables.iter() { @@ -854,6 +875,7 @@ impl Interface { } Interface { + features, resources, entry_points, } @@ -901,7 +923,7 @@ impl Interface { .get_mut(res.bind.group as usize) .ok_or(BindingError::Missing) .and_then(|set| { - let ty = res.derive_binding_type(usage)?; + let ty = res.derive_binding_type(usage, self.features)?; match set.entry(res.bind.binding) { Entry::Occupied(e) if e.get().ty != ty => { return Err(BindingError::InconsistentlyDerivedType) diff --git a/wgpu-hal/src/dx12/command.rs b/wgpu-hal/src/dx12/command.rs index 9f3ea39e98..7f82240209 100644 --- a/wgpu-hal/src/dx12/command.rs +++ b/wgpu-hal/src/dx12/command.rs @@ -124,7 +124,7 @@ impl crate::CommandEncoder for super::CommandEncoder { StateAfter: s1, }; self.temp.barriers.push(raw); - } else if barrier.usage.start == crate::BufferUses::STORAGE_STORE { + } else if barrier.usage.start == crate::BufferUses::STORAGE_WRITE { let mut raw = d3d12::D3D12_RESOURCE_BARRIER { Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, @@ -196,7 +196,7 @@ impl crate::CommandEncoder for super::CommandEncoder { } } } - } else if barrier.usage.start == crate::TextureUses::STORAGE_STORE { + } else if barrier.usage.start == crate::TextureUses::STORAGE_WRITE { let mut raw = d3d12::D3D12_RESOURCE_BARRIER { Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, diff --git a/wgpu-hal/src/dx12/conv.rs b/wgpu-hal/src/dx12/conv.rs index cfe11a638d..bd2edd85f6 100644 --- a/wgpu-hal/src/dx12/conv.rs +++ b/wgpu-hal/src/dx12/conv.rs @@ -185,7 +185,7 @@ pub fn map_acomposite_alpha_mode(mode: crate::CompositeAlphaMode) -> dxgi1_2::DX pub fn map_buffer_usage_to_resource_flags(usage: crate::BufferUses) -> d3d12::D3D12_RESOURCE_FLAGS { let mut flags = 0; - if usage.contains(crate::BufferUses::STORAGE_STORE) { + if usage.contains(crate::BufferUses::STORAGE_WRITE) { flags |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } flags @@ -211,11 +211,11 @@ pub fn map_texture_usage_to_resource_flags( crate::TextureUses::DEPTH_STENCIL_READ | crate::TextureUses::DEPTH_STENCIL_WRITE, ) { flags |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - if !usage.intersects(crate::TextureUses::SAMPLED | crate::TextureUses::STORAGE_LOAD) { + if !usage.contains(crate::TextureUses::SAMPLED) { flags |= d3d12::D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; } } - if usage.contains(crate::TextureUses::STORAGE_STORE) { + if usage.contains(crate::TextureUses::STORAGE_WRITE) { flags |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } @@ -283,11 +283,7 @@ pub fn map_binding_type(ty: &wgt::BindingType) -> native::DescriptorRangeType { ty: wgt::BufferBindingType::Storage { read_only: true }, .. } - | Bt::Texture { .. } - | Bt::StorageTexture { - access: wgt::StorageTextureAccess::ReadOnly, - .. - } => native::DescriptorRangeType::SRV, + | Bt::Texture { .. } => native::DescriptorRangeType::SRV, Bt::Buffer { ty: wgt::BufferBindingType::Storage { read_only: false }, .. @@ -316,11 +312,11 @@ pub fn map_buffer_usage_to_state(usage: crate::BufferUses) -> d3d12::D3D12_RESOU if usage.intersects(Bu::VERTEX | Bu::UNIFORM) { state |= d3d12::D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; } - if usage.intersects(Bu::STORAGE_LOAD) { + if usage.intersects(Bu::STORAGE_READ) { state |= d3d12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | d3d12::D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; } - if usage.intersects(Bu::STORAGE_STORE) { + if usage.intersects(Bu::STORAGE_WRITE) { state |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS; } if usage.intersects(Bu::INDIRECT) { @@ -344,7 +340,7 @@ pub fn map_texture_usage_to_state(usage: crate::TextureUses) -> d3d12::D3D12_RES if usage.intersects(Tu::COPY_DST) { state |= d3d12::D3D12_RESOURCE_STATE_COPY_DEST; } - if usage.intersects(Tu::SAMPLED | Tu::STORAGE_LOAD) { + if usage.intersects(Tu::SAMPLED) { state |= d3d12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | d3d12::D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; } @@ -357,7 +353,7 @@ pub fn map_texture_usage_to_state(usage: crate::TextureUses) -> d3d12::D3D12_RES if usage.intersects(Tu::DEPTH_STENCIL_WRITE) { state |= d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE; } - if usage.intersects(Tu::STORAGE_STORE) { + if usage.intersects(Tu::STORAGE_READ | Tu::STORAGE_WRITE) { state |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS; } state diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index 0f5f5a3570..bd475e7182 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -792,13 +792,13 @@ impl crate::Device for super::Device { ), handle_srv: if desc .usage - .intersects(crate::TextureUses::SAMPLED | crate::TextureUses::STORAGE_LOAD) + .intersects(crate::TextureUses::SAMPLED | crate::TextureUses::STORAGE_READ) { Some(self.view_texture_as_shader_resource(texture, desc)) } else { None }, - handle_uav: if desc.usage.intersects(crate::TextureUses::STORAGE_STORE) { + handle_uav: if desc.usage.intersects(crate::TextureUses::STORAGE_WRITE) { Some(self.view_texture_as_unoredered_access(texture, desc)) } else { None @@ -1295,11 +1295,7 @@ impl crate::Device for super::Device { } inner.stage.push(handle); } - wgt::BindingType::Texture { .. } - | wgt::BindingType::StorageTexture { - access: wgt::StorageTextureAccess::ReadOnly, - .. - } => { + 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); diff --git a/wgpu-hal/src/gles/command.rs b/wgpu-hal/src/gles/command.rs index b5e8ff8355..2bd1f205c8 100644 --- a/wgpu-hal/src/gles/command.rs +++ b/wgpu-hal/src/gles/command.rs @@ -196,7 +196,7 @@ impl crate::CommandEncoder for super::CommandEncoder { } for bar in barriers { // GLES only synchronizes storage -> anything explicitly - if !bar.usage.start.contains(crate::BufferUses::STORAGE_STORE) { + if !bar.usage.start.contains(crate::BufferUses::STORAGE_WRITE) { continue; } self.cmd_buffer @@ -219,7 +219,7 @@ impl crate::CommandEncoder for super::CommandEncoder { let mut combined_usage = crate::TextureUses::empty(); for bar in barriers { // GLES only synchronizes storage -> anything explicitly - if !bar.usage.start.contains(crate::TextureUses::STORAGE_STORE) { + if !bar.usage.start.contains(crate::TextureUses::STORAGE_WRITE) { continue; } // unlike buffers, there is no need for a concrete texture diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index 2b0dbc522a..cddde6c9b1 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -604,7 +604,7 @@ impl super::Queue { flags |= glow::BUFFER_UPDATE_BARRIER_BIT; } if usage - .intersects(crate::BufferUses::STORAGE_LOAD | crate::BufferUses::STORAGE_STORE) + .intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_WRITE) { flags |= glow::SHADER_STORAGE_BARRIER_BIT; } @@ -616,7 +616,7 @@ impl super::Queue { flags |= glow::TEXTURE_FETCH_BARRIER_BIT; } if usage.intersects( - crate::TextureUses::STORAGE_LOAD | crate::TextureUses::STORAGE_STORE, + crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE, ) { flags |= glow::SHADER_IMAGE_ACCESS_BARRIER_BIT; } diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 4e42368746..e79ab9b8d3 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -515,9 +515,9 @@ bitflags!( /// Format can be sampled with a min/max reduction sampler. const SAMPLED_MINMAX = 0x4; - /// Format can be used as storage with exclusive read & write access. + /// Format can be used as storage with write-only access. const STORAGE = 0x10; - /// Format can be used as storage with simultaneous read/write access. + /// Format can be used as storage with read and read/write access. const STORAGE_READ_WRITE = 0x20; /// Format can be used as storage with atomics. const STORAGE_ATOMIC = 0x40; @@ -591,19 +591,19 @@ bitflags::bitflags! { const INDEX = 16; const VERTEX = 32; const UNIFORM = 64; - const STORAGE_LOAD = 128; - const STORAGE_STORE = 256; + const STORAGE_READ = 128; + const STORAGE_WRITE = 256; const INDIRECT = 512; - /// The combination of all read-only usages. - const READ_ALL = Self::MAP_READ.bits | Self::COPY_SRC.bits | + /// The combination of usages that can be used together (read-only). + const INCLUSIVE = Self::MAP_READ.bits | Self::COPY_SRC.bits | Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits | - Self::STORAGE_LOAD.bits | Self::INDIRECT.bits; - /// The combination of all write-only and read-write usages. - const WRITE_ALL = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_STORE.bits; + Self::STORAGE_READ.bits | Self::INDIRECT.bits; + /// The combination of exclusive usages (write-only and read-write). + const EXCLUSIVE = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_WRITE.bits; /// The combination of all usages that the are guaranteed to be be ordered by the hardware. /// If a usage is not ordered, then even if it doesn't change between draw calls, there /// still need to be pipeline barriers inserted for synchronization. - const ORDERED = Self::READ_ALL.bits | Self::MAP_WRITE.bits | Self::COPY_DST.bits; + const ORDERED = Self::INCLUSIVE.bits | Self::MAP_WRITE.bits | Self::COPY_DST.bits; } } @@ -616,16 +616,16 @@ bitflags::bitflags! { const COLOR_TARGET = 8; const DEPTH_STENCIL_READ = 16; const DEPTH_STENCIL_WRITE = 32; - const STORAGE_LOAD = 64; - const STORAGE_STORE = 128; - /// The combination of all read-only usages. - const READ_ALL = Self::COPY_SRC.bits | Self::SAMPLED.bits | Self::DEPTH_STENCIL_READ.bits | Self::STORAGE_LOAD.bits; - /// The combination of all write-only and read-write usages. - const WRITE_ALL = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_STORE.bits; + const STORAGE_READ = 64; + const STORAGE_WRITE = 128; + /// The combination of usages that can be used together (read-only). + const INCLUSIVE = Self::COPY_SRC.bits | Self::SAMPLED.bits | Self::DEPTH_STENCIL_READ.bits; + /// The combination of exclusive usages (write-only and read-write). + const EXCLUSIVE = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits | Self::STORAGE_WRITE.bits; /// The combination of all usages that the are guaranteed to be be ordered by the hardware. /// If a usage is not ordered, then even if it doesn't change between draw calls, there /// still need to be pipeline barriers inserted for synchronization. - const ORDERED = Self::READ_ALL.bits | Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits; + const ORDERED = Self::INCLUSIVE.bits | Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits; //TODO: remove this const UNINITIALIZED = 0xFFFF; } diff --git a/wgpu-hal/src/metal/conv.rs b/wgpu-hal/src/metal/conv.rs index b1c7e0b303..bc15fe1ef0 100644 --- a/wgpu-hal/src/metal/conv.rs +++ b/wgpu-hal/src/metal/conv.rs @@ -9,11 +9,11 @@ pub fn map_texture_usage(usage: crate::TextureUses) -> mtl::MTLTextureUsage { ); mtl_usage.set( mtl::MTLTextureUsage::ShaderRead, - usage.intersects(Tu::SAMPLED | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_LOAD), + usage.intersects(Tu::SAMPLED | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_READ), ); mtl_usage.set( mtl::MTLTextureUsage::ShaderWrite, - usage.intersects(Tu::STORAGE_STORE), + usage.intersects(Tu::STORAGE_WRITE), ); mtl_usage diff --git a/wgpu-hal/src/vulkan/conv.rs b/wgpu-hal/src/vulkan/conv.rs index 208278fbd9..40a18b5c4f 100644 --- a/wgpu-hal/src/vulkan/conv.rs +++ b/wgpu-hal/src/vulkan/conv.rs @@ -197,7 +197,7 @@ pub fn map_texture_usage(usage: crate::TextureUses) -> vk::ImageUsageFlags { ) { flags |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT; } - if usage.intersects(crate::TextureUses::STORAGE_LOAD | crate::TextureUses::STORAGE_STORE) { + if usage.intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE) { flags |= vk::ImageUsageFlags::STORAGE; } flags @@ -239,11 +239,11 @@ pub fn map_texture_usage_to_barrier( access |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE; } - if usage.contains(crate::TextureUses::STORAGE_LOAD) { + if usage.contains(crate::TextureUses::STORAGE_READ) { stages |= shader_stages; access |= vk::AccessFlags::SHADER_READ; } - if usage.contains(crate::TextureUses::STORAGE_STORE) { + if usage.contains(crate::TextureUses::STORAGE_WRITE) { stages |= shader_stages; access |= vk::AccessFlags::SHADER_WRITE; } @@ -276,7 +276,7 @@ pub fn map_vk_image_usage(usage: vk::ImageUsageFlags) -> crate::TextureUses { bits |= crate::TextureUses::DEPTH_STENCIL_READ | crate::TextureUses::DEPTH_STENCIL_WRITE; } if usage.contains(vk::ImageUsageFlags::STORAGE) { - bits |= crate::TextureUses::STORAGE_LOAD | crate::TextureUses::STORAGE_STORE; + bits |= crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE; } bits } @@ -424,7 +424,7 @@ pub fn map_buffer_usage(usage: crate::BufferUses) -> vk::BufferUsageFlags { if usage.contains(crate::BufferUses::UNIFORM) { flags |= vk::BufferUsageFlags::UNIFORM_BUFFER; } - if usage.intersects(crate::BufferUses::STORAGE_LOAD | crate::BufferUses::STORAGE_STORE) { + if usage.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_WRITE) { flags |= vk::BufferUsageFlags::STORAGE_BUFFER; } if usage.contains(crate::BufferUses::INDEX) { @@ -468,11 +468,11 @@ pub fn map_buffer_usage_to_barrier( stages |= shader_stages; access |= vk::AccessFlags::UNIFORM_READ; } - if usage.intersects(crate::BufferUses::STORAGE_LOAD) { + if usage.intersects(crate::BufferUses::STORAGE_READ) { stages |= shader_stages; access |= vk::AccessFlags::SHADER_READ; } - if usage.intersects(crate::BufferUses::STORAGE_STORE) { + if usage.intersects(crate::BufferUses::STORAGE_WRITE) { stages |= shader_stages; access |= vk::AccessFlags::SHADER_WRITE; } diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index f188fc820b..f81be32a14 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -1213,9 +1213,11 @@ bitflags::bitflags! { #[cfg_attr(feature = "trace", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct TextureFormatFeatureFlags: u32 { - /// When used as a STORAGE texture, then a texture with this format can be bound with `StorageTextureAccess::ReadWrite`. + /// When used as a STORAGE texture, then a texture with this format can be bound with + /// [`StorageTextureAccess::ReadOnly`] or [`StorageTextureAccess::ReadWrite`]. const STORAGE_READ_WRITE = 1; - /// When used as a STORAGE texture, then a texture with this format can be written to with atomics. TODO: No access flag exposed as of writing + /// When used as a STORAGE texture, then a texture with this format can be written to with atomics. + // TODO: No access flag exposed as of writing const STORAGE_ATOMICS = 2; } } @@ -2899,13 +2901,6 @@ impl Default for TextureSampleType { #[cfg_attr(feature = "trace", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum StorageTextureAccess { - /// The texture can only be read in the shader and it must be annotated with `readonly`. - /// - /// Example GLSL syntax: - /// ```cpp,ignore - /// layout(set=0, binding=0, r32f) readonly uniform image2D myStorageImage; - /// ``` - ReadOnly, /// The texture can only be written in the shader and it must be annotated with `writeonly`. /// /// Example GLSL syntax: @@ -2913,6 +2908,14 @@ pub enum StorageTextureAccess { /// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage; /// ``` WriteOnly, + /// The texture can only be read in the shader and it must be annotated with `readonly`. + /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] must be enabled to use this access mode, + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(set=0, binding=0, r32f) readonly uniform image2D myStorageImage; + /// ``` + ReadOnly, /// The texture can be both read and written in the shader. /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] must be enabled to use this access mode. /// diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index 232b794343..ca124685ea 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -1232,12 +1232,12 @@ impl crate::Context for Context { view_dimension, } => { let mapped_access = match access { - wgt::StorageTextureAccess::ReadOnly => { - web_sys::GpuStorageTextureAccess::ReadOnly - } wgt::StorageTextureAccess::WriteOnly => { web_sys::GpuStorageTextureAccess::WriteOnly } + wgt::StorageTextureAccess::ReadOnly => { + panic!("ReadOnly is not available") + } wgt::StorageTextureAccess::ReadWrite => { panic!("ReadWrite is not available") }