diff --git a/crates/bevy_render/src/pipeline/binding.rs b/crates/bevy_render/src/pipeline/binding.rs index a467d8fd91662..80de9b386239c 100644 --- a/crates/bevy_render/src/pipeline/binding.rs +++ b/crates/bevy_render/src/pipeline/binding.rs @@ -1,5 +1,5 @@ use super::UniformProperty; -use crate::texture::{TextureComponentType, TextureFormat, TextureViewDimension}; +use crate::texture::{TextureSampleType, TextureFormat, TextureViewDimension, StorageTextureAccess}; bitflags::bitflags! { pub struct BindingShaderStage: u32 { @@ -20,25 +20,35 @@ pub struct BindingDescriptor { #[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum BindType { Uniform { - dynamic: bool, + has_dynamic_offset: bool, property: UniformProperty, }, StorageBuffer { - dynamic: bool, + has_dynamic_offset: bool, readonly: bool, }, Sampler { + /// The sampling result is produced based on more than a single color sample from a texture, + /// e.g. when bilinear interpolation is enabled. + /// + /// A filtering sampler can only be used with a filterable texture. + filtering: bool, + /// Use as a comparison sampler instead of a normal sampler. + /// For more info take a look at the analogous functionality in OpenGL: https://www.khronos.org/opengl/wiki/Sampler_Object#Comparison_mode. comparison: bool, }, - SampledTexture { + Texture { multisampled: bool, - dimension: TextureViewDimension, - component_type: TextureComponentType, + view_dimension: TextureViewDimension, + sample_type: TextureSampleType, }, StorageTexture { - dimension: TextureViewDimension, + /// Allowed access to this texture. + access: StorageTextureAccess, + /// Format of the texture. format: TextureFormat, - readonly: bool, + /// Dimension of the texture view that is going to be sampled. + view_dimension: TextureViewDimension, }, } diff --git a/crates/bevy_render/src/pipeline/pipeline_compiler.rs b/crates/bevy_render/src/pipeline/pipeline_compiler.rs index 98c041160fa82..86bee55718f47 100644 --- a/crates/bevy_render/src/pipeline/pipeline_compiler.rs +++ b/crates/bevy_render/src/pipeline/pipeline_compiler.rs @@ -178,10 +178,10 @@ impl PipelineCompiler { .any(|b| b == &binding.name) { if let BindType::Uniform { - ref mut dynamic, .. + ref mut has_dynamic_offset, .. } = binding.bind_type { - *dynamic = true; + *has_dynamic_offset = true; binding_changed = true; } } diff --git a/crates/bevy_render/src/render_graph/nodes/pass_node.rs b/crates/bevy_render/src/render_graph/nodes/pass_node.rs index c9de46d256b62..345cca9cf0dca 100644 --- a/crates/bevy_render/src/render_graph/nodes/pass_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/pass_node.rs @@ -109,7 +109,7 @@ impl PassNode { name: "Camera".to_string(), index: 0, bind_type: BindType::Uniform { - dynamic: false, + has_dynamic_offset: false, property: UniformProperty::Struct(vec![UniformProperty::Mat4]), }, shader_stage: BindingShaderStage::VERTEX | BindingShaderStage::FRAGMENT, diff --git a/crates/bevy_render/src/shader/shader_reflect.rs b/crates/bevy_render/src/shader/shader_reflect.rs index 695b969727834..a65b98e073967 100644 --- a/crates/bevy_render/src/shader/shader_reflect.rs +++ b/crates/bevy_render/src/shader/shader_reflect.rs @@ -4,7 +4,7 @@ use crate::{ UniformProperty, VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat, }, shader::{ShaderLayout, GL_INSTANCE_INDEX, GL_VERTEX_INDEX}, - texture::{TextureComponentType, TextureViewDimension}, + texture::{TextureSampleType, TextureViewDimension}, }; use bevy_core::AsBytes; use spirv_reflect::{ @@ -123,27 +123,34 @@ fn reflect_binding( ReflectDescriptorType::UniformBuffer => ( &type_description.type_name, BindType::Uniform { - dynamic: false, + has_dynamic_offset: false, property: reflect_uniform(type_description), }, ), ReflectDescriptorType::SampledImage => ( &binding.name, - BindType::SampledTexture { - dimension: reflect_dimension(type_description), - component_type: TextureComponentType::Float, + BindType::Texture { + view_dimension: reflect_dimension(type_description), + sample_type: TextureSampleType::Float { filterable: false }, multisampled: false, }, ), ReflectDescriptorType::StorageBuffer => ( &type_description.type_name, BindType::StorageBuffer { - dynamic: false, + has_dynamic_offset: false, readonly: true, }, ), // TODO: detect comparison "true" case: https://github.com/gpuweb/gpuweb/issues/552 - ReflectDescriptorType::Sampler => (&binding.name, BindType::Sampler { comparison: false }), + // TODO: detect filtering "true" case + ReflectDescriptorType::Sampler => ( + &binding.name, + BindType::Sampler { + comparison: false, + filtering: false, + } + ), _ => panic!("Unsupported bind type {:?}.", binding.descriptor_type), }; diff --git a/crates/bevy_render/src/texture/texture_descriptor.rs b/crates/bevy_render/src/texture/texture_descriptor.rs index 15ab341ccda56..14d64a525f8d2 100644 --- a/crates/bevy_render/src/texture/texture_descriptor.rs +++ b/crates/bevy_render/src/texture/texture_descriptor.rs @@ -40,3 +40,30 @@ impl Default for TextureDescriptor { } } } + +#[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +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: + /// ```cpp,ignore + /// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage; + /// ``` + WriteOnly, + /// The texture can be both read and written in the shader. + /// [`Features::STORAGE_TEXTURE_ACCESS_READ_WRITE`] must be enabled to use this access mode. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(set=0, binding=0, r32f) uniform image2D myStorageImage; + /// ``` + ReadWrite, +} + diff --git a/crates/bevy_render/src/texture/texture_dimension.rs b/crates/bevy_render/src/texture/texture_dimension.rs index d230be2303c7e..c5b8e8552fd0a 100644 --- a/crates/bevy_render/src/texture/texture_dimension.rs +++ b/crates/bevy_render/src/texture/texture_dimension.rs @@ -49,9 +49,41 @@ impl Extent3d { /// Type of data shaders will read from a texture. #[derive(Copy, Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub enum TextureComponentType { - Float, +pub enum TextureSampleType { + /// Sampling returns floats. + /// + /// If `filterable` is false, the texture can't be sampled with + /// a filtering sampler. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(binding = 0) + /// uniform texture2D t; + /// ``` + Float { filterable: bool }, + /// Sampling does the depth reference comparison. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(binding = 0) + /// uniform texture2DShadow t; + /// ``` + Depth, + /// Sampling returns signed integers. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(binding = 0) + /// uniform itexture2D t; + /// ``` Sint, + /// Sampling returns unsigned integers. + /// + /// Example GLSL syntax: + /// ```cpp,ignore + /// layout(binding = 0) + /// uniform utexture2D t; + /// ``` Uint, } diff --git a/crates/bevy_wgpu/src/wgpu_type_converter.rs b/crates/bevy_wgpu/src/wgpu_type_converter.rs index e6e176263e045..024aae7cea0d0 100644 --- a/crates/bevy_wgpu/src/wgpu_type_converter.rs +++ b/crates/bevy_wgpu/src/wgpu_type_converter.rs @@ -10,11 +10,13 @@ use bevy_render::{ }, renderer::BufferUsage, texture::{ - AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureComponentType, + AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureSampleType, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage, TextureViewDimension, }, }; use bevy_window::Window; +use wgpu::BufferBindingType; +use bevy_render::texture::StorageTextureAccess; pub trait WgpuFrom { fn from(val: T) -> Self; @@ -181,46 +183,61 @@ where impl WgpuFrom<&BindType> for wgpu::BindingType { fn from(bind_type: &BindType) -> Self { match bind_type { - BindType::Uniform { dynamic, .. } => wgpu::BindingType::UniformBuffer { - dynamic: *dynamic, + BindType::Uniform { has_dynamic_offset, .. } => wgpu::BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: *has_dynamic_offset, min_binding_size: bind_type.get_uniform_size().and_then(wgpu::BufferSize::new), }, - BindType::StorageBuffer { dynamic, readonly } => wgpu::BindingType::StorageBuffer { - dynamic: *dynamic, - readonly: *readonly, + BindType::StorageBuffer { has_dynamic_offset, readonly } => wgpu::BindingType::Buffer { + ty: BufferBindingType::Storage { + read_only: *readonly + }, + has_dynamic_offset: *has_dynamic_offset, min_binding_size: bind_type.get_uniform_size().and_then(wgpu::BufferSize::new), }, - BindType::SampledTexture { - dimension, + BindType::Texture { + view_dimension, multisampled, - component_type, - } => wgpu::BindingType::SampledTexture { - dimension: (*dimension).wgpu_into(), + sample_type, + } => wgpu::BindingType::Texture { + view_dimension: (*view_dimension).wgpu_into(), multisampled: *multisampled, - component_type: (*component_type).wgpu_into(), + sample_type: (*sample_type).wgpu_into(), }, - BindType::Sampler { comparison } => wgpu::BindingType::Sampler { + BindType::Sampler { comparison, filtering } => wgpu::BindingType::Sampler { + filtering: *filtering, comparison: *comparison, }, BindType::StorageTexture { - dimension, + view_dimension, format, - readonly, + access, } => wgpu::BindingType::StorageTexture { - dimension: (*dimension).wgpu_into(), + access: (*access).wgpu_into(), + view_dimension: (*view_dimension).wgpu_into(), format: (*format).wgpu_into(), - readonly: *readonly, }, } } } -impl WgpuFrom for wgpu::TextureComponentType { - fn from(texture_component_type: TextureComponentType) -> Self { +impl WgpuFrom for wgpu::TextureSampleType { + fn from(texture_component_type: TextureSampleType) -> Self { match texture_component_type { - TextureComponentType::Float => wgpu::TextureComponentType::Float, - TextureComponentType::Sint => wgpu::TextureComponentType::Sint, - TextureComponentType::Uint => wgpu::TextureComponentType::Uint, + TextureSampleType::Float { filterable } => wgpu::TextureSampleType::Float { filterable }, + TextureSampleType::Sint => wgpu::TextureSampleType::Sint, + TextureSampleType::Uint => wgpu::TextureSampleType::Uint, + TextureSampleType::Depth => wgpu::TextureSampleType::Depth, + } + } +} + +impl WgpuFrom for wgpu::StorageTextureAccess { + fn from(storage_texture_access: StorageTextureAccess) -> Self { + match storage_texture_access { + StorageTextureAccess::ReadOnly => wgpu::StorageTextureAccess::ReadOnly, + StorageTextureAccess::WriteOnly => wgpu::StorageTextureAccess::WriteOnly, + StorageTextureAccess::ReadWrite => wgpu::StorageTextureAccess::ReadWrite, } } }