mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
Switch read-only storage textures to be exclusive and behind a feature
This commit is contained in:
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -737,8 +737,8 @@ impl<A: HalApi> Device<A> {
|
||||
}
|
||||
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<A: HalApi> Device<A> {
|
||||
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<A: HalApi> Device<A> {
|
||||
);
|
||||
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<A: HalApi> Device<A> {
|
||||
| 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<A: HalApi> Device<A> {
|
||||
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<A: HalApi> Device<A> {
|
||||
});
|
||||
}
|
||||
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))
|
||||
|
||||
@@ -8,7 +8,7 @@ impl PendingTransition<BufferState> {
|
||||
fn collapse(self) -> Result<BufferUses, Self> {
|
||||
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,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ impl PendingTransition<TextureState> {
|
||||
fn collapse(self) -> Result<TextureUses, Self> {
|
||||
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!(
|
||||
|
||||
@@ -107,6 +107,7 @@ struct EntryPoint {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Interface {
|
||||
features: wgt::Features,
|
||||
resources: naga::Arena<Resource>,
|
||||
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<BindingType, BindingError> {
|
||||
fn derive_binding_type(
|
||||
&self,
|
||||
shader_usage: GlobalUse,
|
||||
features: wgt::Features,
|
||||
) -> Result<BindingType, BindingError> {
|
||||
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)
|
||||
|
||||
@@ -124,7 +124,7 @@ impl crate::CommandEncoder<super::Api> 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<super::Api> 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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -792,13 +792,13 @@ impl crate::Device<super::Api> 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<super::Api> 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);
|
||||
|
||||
@@ -196,7 +196,7 @@ impl crate::CommandEncoder<super::Api> 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<super::Api> 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user