-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[Merged by Bors] - Request WGPU Capabilities for Non-uniform Indexing #6995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a666933
0c97cad
a734fca
853b9a4
41f9616
6bcd1b7
c50e987
bd0f163
49d50bd
eb03ca5
792c169
4e3d454
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
@group(1) @binding(0) | ||
var textures: binding_array<texture_2d<f32>>; | ||
@group(1) @binding(1) | ||
var samplers: binding_array<sampler>; | ||
|
||
@fragment | ||
fn fragment( | ||
#import bevy_pbr::mesh_vertex_output | ||
) -> @location(0) vec4<f32> { | ||
// Select the texture to sample from using non-uniform uv coordinates | ||
let coords = clamp(vec2<u32>(uv * 4.0), vec2<u32>(0u), vec2<u32>(3u)); | ||
let index = coords.y * 4u + coords.x; | ||
let inner_uv = fract(uv * 4.0); | ||
return textureSample(textures[index], samplers[index], inner_uv); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -141,6 +141,18 @@ impl ProcessedShader { | |
Features::SHADER_PRIMITIVE_INDEX, | ||
Capabilities::PRIMITIVE_INDEX, | ||
), | ||
( | ||
Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, | ||
Capabilities::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, | ||
), | ||
( | ||
Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, | ||
Capabilities::SAMPLER_NON_UNIFORM_INDEXING, | ||
Comment on lines
+149
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this the right feature->capability pair? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no |
||
), | ||
( | ||
Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, | ||
Capabilities::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, | ||
), | ||
]; | ||
let mut capabilities = Capabilities::empty(); | ||
for (feature, capability) in CAPABILITIES { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
//! A shader that binds several textures onto one | ||
//! `binding_array<texture<f32>>` shader binding slot and sample non-uniformly. | ||
|
||
use bevy::{ | ||
prelude::*, | ||
reflect::TypeUuid, | ||
render::{ | ||
render_asset::RenderAssets, | ||
render_resource::{AsBindGroupError, PreparedBindGroup, *}, | ||
renderer::RenderDevice, | ||
texture::FallbackImage, | ||
}, | ||
}; | ||
use std::num::NonZeroU32; | ||
|
||
fn main() { | ||
App::new() | ||
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) | ||
.add_plugin(MaterialPlugin::<BindlessMaterial>::default()) | ||
.add_startup_system(setup) | ||
.run(); | ||
} | ||
|
||
const MAX_TEXTURE_COUNT: usize = 16; | ||
const TILE_ID: [usize; 16] = [ | ||
19, 23, 4, 33, 12, 69, 30, 48, 10, 65, 40, 47, 57, 41, 44, 46, | ||
]; | ||
|
||
fn setup( | ||
mut commands: Commands, | ||
mut meshes: ResMut<Assets<Mesh>>, | ||
mut materials: ResMut<Assets<BindlessMaterial>>, | ||
asset_server: Res<AssetServer>, | ||
render_device: Res<RenderDevice>, | ||
) { | ||
// check if the device support the required feature | ||
if !render_device | ||
.features() | ||
.contains(WgpuFeatures::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING) | ||
{ | ||
error!( | ||
"Render device doesn't support feature \ | ||
SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, \ | ||
which is required for texture binding arrays" | ||
); | ||
return; | ||
} | ||
|
||
commands.spawn(Camera3dBundle { | ||
transform: Transform::from_xyz(2.0, 2.0, 2.0).looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y), | ||
..Default::default() | ||
}); | ||
|
||
// load 16 textures | ||
let textures: Vec<_> = TILE_ID | ||
.iter() | ||
.map(|id| { | ||
let path = format!("textures/rpg/tiles/generic-rpg-tile{:0>2}.png", id); | ||
asset_server.load(path) | ||
}) | ||
.collect(); | ||
|
||
// a cube with multiple textures | ||
commands.spawn(MaterialMeshBundle { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we pls check the capabilities in the demo, (both to show how it's done, and what is required)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, will do! |
||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), | ||
material: materials.add(BindlessMaterial { textures }), | ||
..Default::default() | ||
}); | ||
} | ||
|
||
#[derive(Debug, Clone, TypeUuid)] | ||
#[uuid = "8dd2b424-45a2-4a53-ac29-7ce356b2d5fe"] | ||
struct BindlessMaterial { | ||
textures: Vec<Handle<Image>>, | ||
} | ||
|
||
impl AsBindGroup for BindlessMaterial { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would be great to get this into the derive macro. i don't think we need to block on that but lets add an issue at least when this is merged. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. It's worth noting that |
||
type Data = (); | ||
|
||
fn as_bind_group( | ||
&self, | ||
layout: &BindGroupLayout, | ||
render_device: &RenderDevice, | ||
image_assets: &RenderAssets<Image>, | ||
fallback_image: &FallbackImage, | ||
) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> { | ||
// retrieve the render resources from handles | ||
let mut images = vec![]; | ||
for handle in self.textures.iter().take(MAX_TEXTURE_COUNT) { | ||
match image_assets.get(handle) { | ||
Some(image) => images.push(image), | ||
None => return Err(AsBindGroupError::RetryNextUpdate), | ||
} | ||
} | ||
|
||
let textures = vec![&fallback_image.texture_view; MAX_TEXTURE_COUNT]; | ||
let samplers = vec![&fallback_image.sampler; MAX_TEXTURE_COUNT]; | ||
|
||
// convert bevy's resource types to WGPU's references | ||
let mut textures: Vec<_> = textures.into_iter().map(|texture| &**texture).collect(); | ||
let mut samplers: Vec<_> = samplers.into_iter().map(|sampler| &**sampler).collect(); | ||
|
||
// fill in up to the first `MAX_TEXTURE_COUNT` textures and samplers to the arrays | ||
for (id, image) in images.into_iter().enumerate() { | ||
textures[id] = &*image.texture_view; | ||
samplers[id] = &*image.sampler; | ||
} | ||
|
||
let bind_group = render_device.create_bind_group(&BindGroupDescriptor { | ||
label: "bindless_material_bind_group".into(), | ||
layout, | ||
entries: &[ | ||
BindGroupEntry { | ||
binding: 0, | ||
resource: BindingResource::TextureViewArray(&textures[..]), | ||
}, | ||
BindGroupEntry { | ||
binding: 1, | ||
resource: BindingResource::SamplerArray(&samplers[..]), | ||
}, | ||
], | ||
}); | ||
|
||
Ok(PreparedBindGroup { | ||
bindings: vec![], | ||
bind_group, | ||
data: (), | ||
}) | ||
} | ||
|
||
fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout | ||
where | ||
Self: Sized, | ||
{ | ||
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { | ||
label: "bindless_material_layout".into(), | ||
entries: &[ | ||
// @group(1) @binding(0) var textures: binding_array<texture_2d<f32>>; | ||
BindGroupLayoutEntry { | ||
binding: 0, | ||
visibility: ShaderStages::FRAGMENT, | ||
ty: BindingType::Texture { | ||
sample_type: TextureSampleType::Float { filterable: true }, | ||
view_dimension: TextureViewDimension::D2, | ||
multisampled: false, | ||
}, | ||
count: NonZeroU32::new(MAX_TEXTURE_COUNT as u32), | ||
}, | ||
// @group(1) @binding(1) var samplers: binding_array<sampler>; | ||
BindGroupLayoutEntry { | ||
binding: 1, | ||
visibility: ShaderStages::FRAGMENT, | ||
ty: BindingType::Sampler(SamplerBindingType::Filtering), | ||
count: NonZeroU32::new(MAX_TEXTURE_COUNT as u32), | ||
}, | ||
], | ||
}) | ||
} | ||
} | ||
|
||
impl Material for BindlessMaterial { | ||
fn fragment_shader() -> ShaderRef { | ||
"shaders/texture_binding_array.wgsl".into() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens when those features are not available? Should it just be user configurable instead of always asking for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to
bevy/crates/bevy_render/src/render_resource/shader.rs
Lines 184 to 211 in 49d50bd
bevy/crates/bevy_render/src/render_resource/pipeline_cache.rs
Line 206 in 49d50bd