From a666933b9d6f015ac4090e129a281078d1f52839 Mon Sep 17 00:00:00 2001 From: cryscan Date: Wed, 21 Dec 2022 00:32:55 -0500 Subject: [PATCH 01/11] Add capabilities for non-uniform indexing. --- crates/bevy_render/src/render_resource/shader.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index 569234abeeb6d..f6c7dda2b2926 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -152,6 +152,14 @@ 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::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 { From 0c97cadca9bcdd102f3484ab944d384ae84706c0 Mon Sep 17 00:00:00 2001 From: cryscan Date: Wed, 21 Dec 2022 02:53:59 -0500 Subject: [PATCH 02/11] Also add `SAMPLER_NON_UNIFORM_INDEXING` capability. --- crates/bevy_render/src/render_resource/shader.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index f6c7dda2b2926..f056bc0d4c5b3 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -156,6 +156,10 @@ impl ProcessedShader { 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, + ), ( Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, Capabilities::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, From a734fcab3785704258155b54c38713bd0210663c Mon Sep 17 00:00:00 2001 From: cryscan Date: Wed, 21 Dec 2022 03:08:03 -0500 Subject: [PATCH 03/11] Add example illustrating bindless textures. --- Cargo.toml | 10 ++ assets/shaders/texture_binding_array.wgsl | 17 ++ examples/shader/texture_binding_array.rs | 179 ++++++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 assets/shaders/texture_binding_array.wgsl create mode 100644 examples/shader/texture_binding_array.rs diff --git a/Cargo.toml b/Cargo.toml index c0e8fde052ba4..9de81c6c24215 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1262,6 +1262,16 @@ description = "A shader that shows how to reuse the core bevy PBR shading functi category = "Shaders" wasm = true +[[example]] +name = "texture_binding_array" +path = "examples/shader/texture_binding_array.rs" + +[package.metadata.example.texture_binding_array] +name = "Texture Binding Array (Bindless Textures)" +description = "A shader that shows how sample a texture binding array non-uniformly (a.k.a. bindless textures)." +category = "Shaders" +wasm = false + # Stress tests [[package.metadata.category]] name = "Stress Tests" diff --git a/assets/shaders/texture_binding_array.wgsl b/assets/shaders/texture_binding_array.wgsl new file mode 100644 index 0000000000000..ea257d82c22c5 --- /dev/null +++ b/assets/shaders/texture_binding_array.wgsl @@ -0,0 +1,17 @@ +@group(1) @binding(0) +var textures: binding_array>; +@group(1) @binding(1) +var samplers: binding_array; + +@fragment +fn fragment( + #import bevy_pbr::mesh_vertex_output +) -> @location(0) vec4 { +#ifdef VERTEX_UVS + let coords = clamp(vec2(uv * 4.0), vec2(0u), vec2(3u)); + let index = coords.y * 4u + coords.x; + return textureSample(textures[index], samplers[index], uv * 4.0); +#else + return vec4(1.0, 0.0, 1.0, 1.0); +#endif +} diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs new file mode 100644 index 0000000000000..4b32867ec72c4 --- /dev/null +++ b/examples/shader/texture_binding_array.rs @@ -0,0 +1,179 @@ +use bevy::{ + prelude::*, + reflect::TypeUuid, + render::{ + render_asset::RenderAssets, + render_resource::{AsBindGroupError, PreparedBindGroup, *}, + renderer::RenderDevice, + texture::{BevyDefault, FallbackImage, ImageSampler}, + }, +}; +use std::num::NonZeroU32; + +/// This example illustrates how to bind several textures in one +/// `binding_array>` shader binding slot and sample non-uniformly. +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_plugin(MaterialPlugin::::default()) + .init_resource::() + .add_startup_system(setup) + .run(); +} + +const MAX_TEXTURE_COUNT: usize = 16; + +#[derive(Resource, Deref)] +struct ColorTextures(Vec>); + +impl FromWorld for ColorTextures { + fn from_world(world: &mut World) -> Self { + let mut images = world.resource_mut::>(); + + // Create 16 textures with different color gradient + let handles = (1..=MAX_TEXTURE_COUNT) + .map(|id| { + let mut pixel = vec![(256 / id - 1) as u8; 64]; + for y in 0..3 { + for x in 0..3 { + pixel[16 * y + 4 * x + 1] = (256 / (y + 1) - 1) as u8; + pixel[16 * y + 4 * x + 2] = (256 / (x + 1) - 1) as u8; + pixel[16 * y + 4 * x + 3] = 255; + } + } + + let mut image = Image::new_fill( + Extent3d { + width: 4, + height: 4, + depth_or_array_layers: 1, + }, + TextureDimension::D2, + &pixel[..], + TextureFormat::bevy_default(), + ); + image.sampler_descriptor = ImageSampler::Descriptor(SamplerDescriptor { + address_mode_u: AddressMode::Repeat, + address_mode_v: AddressMode::Repeat, + address_mode_w: AddressMode::Repeat, + ..Default::default() + }); + images.add(image) + }) + .collect(); + + Self(handles) + } +} + +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + color_textures: Res, +) { + 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() + }); + + commands.spawn(MaterialMeshBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(BindlessMaterial { + textures: color_textures.clone(), + }), + ..Default::default() + }); +} + +#[derive(Debug, Clone, TypeUuid)] +#[uuid = "8dd2b424-45a2-4a53-ac29-7ce356b2d5fe"] +struct BindlessMaterial { + textures: Vec>, +} + +impl AsBindGroup for BindlessMaterial { + type Data = (); + + fn as_bind_group( + &self, + layout: &BindGroupLayout, + render_device: &RenderDevice, + images: &RenderAssets, + fallback_image: &FallbackImage, + ) -> Result, AsBindGroupError> { + 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(); + + for (id, image) in self + .textures + .iter() + .filter_map(|handle| images.get(handle)) + .take(MAX_TEXTURE_COUNT) + .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>; + 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; + 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() + } +} From 853b9a4f4952837ce7a0974208374f7da6274c0f Mon Sep 17 00:00:00 2001 From: cryscan Date: Wed, 21 Dec 2022 03:27:09 -0500 Subject: [PATCH 04/11] Use RGBA8Unorm texture format in the example. --- assets/shaders/texture_binding_array.wgsl | 2 +- examples/shader/texture_binding_array.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/shaders/texture_binding_array.wgsl b/assets/shaders/texture_binding_array.wgsl index ea257d82c22c5..0b5bcac3ca55d 100644 --- a/assets/shaders/texture_binding_array.wgsl +++ b/assets/shaders/texture_binding_array.wgsl @@ -10,7 +10,7 @@ fn fragment( #ifdef VERTEX_UVS let coords = clamp(vec2(uv * 4.0), vec2(0u), vec2(3u)); let index = coords.y * 4u + coords.x; - return textureSample(textures[index], samplers[index], uv * 4.0); + return textureSample(textures[index], samplers[index], uv * 3.9 + 0.05); #else return vec4(1.0, 0.0, 1.0, 1.0); #endif diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index 4b32867ec72c4..68d0a58bacc60 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -5,7 +5,7 @@ use bevy::{ render_asset::RenderAssets, render_resource::{AsBindGroupError, PreparedBindGroup, *}, renderer::RenderDevice, - texture::{BevyDefault, FallbackImage, ImageSampler}, + texture::{FallbackImage, ImageSampler}, }, }; use std::num::NonZeroU32; @@ -50,7 +50,7 @@ impl FromWorld for ColorTextures { }, TextureDimension::D2, &pixel[..], - TextureFormat::bevy_default(), + TextureFormat::Rgba8Unorm, ); image.sampler_descriptor = ImageSampler::Descriptor(SamplerDescriptor { address_mode_u: AddressMode::Repeat, From 41f9616782cfc734be1ae45e30b515325b65b6c3 Mon Sep 17 00:00:00 2001 From: cryscan Date: Wed, 21 Dec 2022 03:43:39 -0500 Subject: [PATCH 05/11] Update README.md --- Cargo.toml | 2 +- examples/README.md | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9de81c6c24215..62f73262d616a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1268,7 +1268,7 @@ path = "examples/shader/texture_binding_array.rs" [package.metadata.example.texture_binding_array] name = "Texture Binding Array (Bindless Textures)" -description = "A shader that shows how sample a texture binding array non-uniformly (a.k.a. bindless textures)." +description = "A shader that shows how bind and sample multiple textures as a binding array (a.k.a. bindless textures)." category = "Shaders" wasm = false diff --git a/examples/README.md b/examples/README.md index e354e6f283120..57891e9afafd4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -56,19 +56,22 @@ git checkout v0.4.0 - [Transforms](#transforms) - [UI (User Interface)](#ui-user-interface) - [Window](#window) - - [Tests](#tests) - [Platform-Specific Examples](#platform-specific-examples) - [Android](#android) - [Setup](#setup) - - [Build & Run](#build--run) + - [Build \& Run](#build--run) + - [Debugging](#debugging) - [Old phones](#old-phones) - [iOS](#ios) - [Setup](#setup-1) - - [Build & Run](#build--run-1) + - [Build \& Run](#build--run-1) - [WASM](#wasm) - [Setup](#setup-2) - - [Build & Run](#build--run-2) + - [Build \& Run](#build--run-2) + - [Optimizing](#optimizing) + - [1. Tweak your `Cargo.toml`](#1-tweak-your-cargotoml) + - [2. Use `wasm-opt` from the binaryen package](#2-use-wasm-opt-from-the-binaryen-package) - [Loading Assets](#loading-assets) # The Bare Minimum @@ -260,6 +263,7 @@ Example | Description --- | --- [Animated](../examples/shader/animate_shader.rs) | A shader that uses dynamic data like the time since startup [Array Texture](../examples/shader/array_texture.rs) | A shader that shows how to reuse the core bevy PBR shading functionality in a custom material that obtains the base color from an array texture. +[Texture Binding Array](../examples/shader/texture_binding_array.rs) | A shader that shows how bind and sample multiple textures as a binding array (a.k.a. bindless textures). [Compute - Game of Life](../examples/shader/compute_shader_game_of_life.rs) | A compute shader that simulates Conway's Game of Life [Custom Vertex Attribute](../examples/shader/custom_vertex_attribute.rs) | A shader that reads a mesh's custom vertex attribute [Instancing](../examples/shader/shader_instancing.rs) | A shader that renders a mesh multiple times in one draw call From 6bcd1b755276cfc08db90dbc970f09b0d3e3e565 Mon Sep 17 00:00:00 2001 From: cryscan Date: Wed, 21 Dec 2022 03:55:09 -0500 Subject: [PATCH 06/11] Update README.md x2. --- examples/README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/examples/README.md b/examples/README.md index 57891e9afafd4..ed4bbf3f34fb6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -56,22 +56,19 @@ git checkout v0.4.0 - [Transforms](#transforms) - [UI (User Interface)](#ui-user-interface) - [Window](#window) + - [Tests](#tests) - [Platform-Specific Examples](#platform-specific-examples) - [Android](#android) - [Setup](#setup) - - [Build \& Run](#build--run) - - [Debugging](#debugging) + - [Build & Run](#build--run) - [Old phones](#old-phones) - [iOS](#ios) - [Setup](#setup-1) - - [Build \& Run](#build--run-1) + - [Build & Run](#build--run-1) - [WASM](#wasm) - [Setup](#setup-2) - - [Build \& Run](#build--run-2) - - [Optimizing](#optimizing) - - [1. Tweak your `Cargo.toml`](#1-tweak-your-cargotoml) - - [2. Use `wasm-opt` from the binaryen package](#2-use-wasm-opt-from-the-binaryen-package) + - [Build & Run](#build--run-2) - [Loading Assets](#loading-assets) # The Bare Minimum @@ -263,7 +260,6 @@ Example | Description --- | --- [Animated](../examples/shader/animate_shader.rs) | A shader that uses dynamic data like the time since startup [Array Texture](../examples/shader/array_texture.rs) | A shader that shows how to reuse the core bevy PBR shading functionality in a custom material that obtains the base color from an array texture. -[Texture Binding Array](../examples/shader/texture_binding_array.rs) | A shader that shows how bind and sample multiple textures as a binding array (a.k.a. bindless textures). [Compute - Game of Life](../examples/shader/compute_shader_game_of_life.rs) | A compute shader that simulates Conway's Game of Life [Custom Vertex Attribute](../examples/shader/custom_vertex_attribute.rs) | A shader that reads a mesh's custom vertex attribute [Instancing](../examples/shader/shader_instancing.rs) | A shader that renders a mesh multiple times in one draw call @@ -272,6 +268,7 @@ Example | Description [Material - Screenspace Texture](../examples/shader/shader_material_screenspace_texture.rs) | A shader that samples a texture with view-independent UV coordinates [Post Processing](../examples/shader/post_processing.rs) | A custom post processing effect, using two cameras, with one reusing the render texture of the first one [Shader Defs](../examples/shader/shader_defs.rs) | A shader that uses "shaders defs" (a bevy tool to selectively toggle parts of a shader) +[Texture Binding Array (Bindless Textures)](../examples/shader/texture_binding_array.rs) | A shader that shows how bind and sample multiple textures as a binding array (a.k.a. bindless textures). ## Stress Tests From c50e987fc49ef56e14962c204f6a67ca8942bebe Mon Sep 17 00:00:00 2001 From: cryscan Date: Sat, 24 Dec 2022 08:36:09 -0500 Subject: [PATCH 07/11] Add more comments in the example. --- assets/shaders/texture_binding_array.wgsl | 1 + examples/shader/texture_binding_array.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/shaders/texture_binding_array.wgsl b/assets/shaders/texture_binding_array.wgsl index 0b5bcac3ca55d..0431fb31c2f89 100644 --- a/assets/shaders/texture_binding_array.wgsl +++ b/assets/shaders/texture_binding_array.wgsl @@ -8,6 +8,7 @@ fn fragment( #import bevy_pbr::mesh_vertex_output ) -> @location(0) vec4 { #ifdef VERTEX_UVS + // Select the texture to sample from using non-uniform uv coordinates let coords = clamp(vec2(uv * 4.0), vec2(0u), vec2(3u)); let index = coords.y * 4u + coords.x; return textureSample(textures[index], samplers[index], uv * 3.9 + 0.05); diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index 68d0a58bacc60..4b30402aa4f2b 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -30,7 +30,7 @@ impl FromWorld for ColorTextures { fn from_world(world: &mut World) -> Self { let mut images = world.resource_mut::>(); - // Create 16 textures with different color gradient + // Create 16 textures with different color gradients let handles = (1..=MAX_TEXTURE_COUNT) .map(|id| { let mut pixel = vec![(256 / id - 1) as u8; 64]; @@ -109,6 +109,7 @@ impl AsBindGroup for BindlessMaterial { 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 self .textures .iter() From bd0f16368fb4f5a27498a9d5c28bfeee5f9f612f Mon Sep 17 00:00:00 2001 From: cryscan Date: Sat, 24 Dec 2022 13:46:08 -0500 Subject: [PATCH 08/11] Use loaded RPG tiles as textures. --- assets/shaders/texture_binding_array.wgsl | 3 +- examples/shader/texture_binding_array.rs | 88 ++++++++--------------- 2 files changed, 31 insertions(+), 60 deletions(-) diff --git a/assets/shaders/texture_binding_array.wgsl b/assets/shaders/texture_binding_array.wgsl index 0431fb31c2f89..82f472a688a25 100644 --- a/assets/shaders/texture_binding_array.wgsl +++ b/assets/shaders/texture_binding_array.wgsl @@ -11,7 +11,8 @@ fn fragment( // Select the texture to sample from using non-uniform uv coordinates let coords = clamp(vec2(uv * 4.0), vec2(0u), vec2(3u)); let index = coords.y * 4u + coords.x; - return textureSample(textures[index], samplers[index], uv * 3.9 + 0.05); + let inner_uv = fract(uv * 4.0); + return textureSample(textures[index], samplers[index], inner_uv); #else return vec4(1.0, 0.0, 1.0, 1.0); #endif diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index 4b30402aa4f2b..e019dd4652e62 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -5,7 +5,7 @@ use bevy::{ render_asset::RenderAssets, render_resource::{AsBindGroupError, PreparedBindGroup, *}, renderer::RenderDevice, - texture::{FallbackImage, ImageSampler}, + texture::FallbackImage, }, }; use std::num::NonZeroU32; @@ -16,72 +16,39 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(MaterialPlugin::::default()) - .init_resource::() .add_startup_system(setup) .run(); } const MAX_TEXTURE_COUNT: usize = 16; - -#[derive(Resource, Deref)] -struct ColorTextures(Vec>); - -impl FromWorld for ColorTextures { - fn from_world(world: &mut World) -> Self { - let mut images = world.resource_mut::>(); - - // Create 16 textures with different color gradients - let handles = (1..=MAX_TEXTURE_COUNT) - .map(|id| { - let mut pixel = vec![(256 / id - 1) as u8; 64]; - for y in 0..3 { - for x in 0..3 { - pixel[16 * y + 4 * x + 1] = (256 / (y + 1) - 1) as u8; - pixel[16 * y + 4 * x + 2] = (256 / (x + 1) - 1) as u8; - pixel[16 * y + 4 * x + 3] = 255; - } - } - - let mut image = Image::new_fill( - Extent3d { - width: 4, - height: 4, - depth_or_array_layers: 1, - }, - TextureDimension::D2, - &pixel[..], - TextureFormat::Rgba8Unorm, - ); - image.sampler_descriptor = ImageSampler::Descriptor(SamplerDescriptor { - address_mode_u: AddressMode::Repeat, - address_mode_v: AddressMode::Repeat, - address_mode_w: AddressMode::Repeat, - ..Default::default() - }); - images.add(image) - }) - .collect(); - - Self(handles) - } -} +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>, mut materials: ResMut>, - color_textures: Res, + asset_server: Res, ) { 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 { mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), - material: materials.add(BindlessMaterial { - textures: color_textures.clone(), - }), + material: materials.add(BindlessMaterial { textures }), ..Default::default() }); } @@ -99,24 +66,27 @@ impl AsBindGroup for BindlessMaterial { &self, layout: &BindGroupLayout, render_device: &RenderDevice, - images: &RenderAssets, + image_assets: &RenderAssets, fallback_image: &FallbackImage, ) -> Result, 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 + // 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 self - .textures - .iter() - .filter_map(|handle| images.get(handle)) - .take(MAX_TEXTURE_COUNT) - .enumerate() - { + // 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; } From 49d50bd98011b1a4751896acdf368bc851c057af Mon Sep 17 00:00:00 2001 From: cryscan Date: Sat, 24 Dec 2022 13:52:34 -0500 Subject: [PATCH 09/11] Default sampler nearest. --- examples/shader/texture_binding_array.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index e019dd4652e62..c2ef3cced7b02 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -14,7 +14,7 @@ use std::num::NonZeroU32; /// `binding_array>` shader binding slot and sample non-uniformly. fn main() { App::new() - .add_plugins(DefaultPlugins) + .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugin(MaterialPlugin::::default()) .add_startup_system(setup) .run(); From eb03ca5643a987da7c4903cd03659400e8d8a559 Mon Sep 17 00:00:00 2001 From: cryscan Date: Sun, 22 Jan 2023 16:06:03 -0500 Subject: [PATCH 10/11] Docs and simplified shader. --- Cargo.toml | 2 +- assets/shaders/texture_binding_array.wgsl | 4 ---- examples/README.md | 2 +- examples/shader/texture_binding_array.rs | 5 +++-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 62f73262d616a..11b8e1ef59585 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1268,7 +1268,7 @@ path = "examples/shader/texture_binding_array.rs" [package.metadata.example.texture_binding_array] name = "Texture Binding Array (Bindless Textures)" -description = "A shader that shows how bind and sample multiple textures as a binding array (a.k.a. bindless textures)." +description = "A shader that shows how to bind and sample multiple textures as a binding array (a.k.a. bindless textures)." category = "Shaders" wasm = false diff --git a/assets/shaders/texture_binding_array.wgsl b/assets/shaders/texture_binding_array.wgsl index 82f472a688a25..00c310ba6aa55 100644 --- a/assets/shaders/texture_binding_array.wgsl +++ b/assets/shaders/texture_binding_array.wgsl @@ -7,13 +7,9 @@ var samplers: binding_array; fn fragment( #import bevy_pbr::mesh_vertex_output ) -> @location(0) vec4 { -#ifdef VERTEX_UVS // Select the texture to sample from using non-uniform uv coordinates let coords = clamp(vec2(uv * 4.0), vec2(0u), vec2(3u)); let index = coords.y * 4u + coords.x; let inner_uv = fract(uv * 4.0); return textureSample(textures[index], samplers[index], inner_uv); -#else - return vec4(1.0, 0.0, 1.0, 1.0); -#endif } diff --git a/examples/README.md b/examples/README.md index ed4bbf3f34fb6..bc4c02fdc41cd 100644 --- a/examples/README.md +++ b/examples/README.md @@ -268,7 +268,7 @@ Example | Description [Material - Screenspace Texture](../examples/shader/shader_material_screenspace_texture.rs) | A shader that samples a texture with view-independent UV coordinates [Post Processing](../examples/shader/post_processing.rs) | A custom post processing effect, using two cameras, with one reusing the render texture of the first one [Shader Defs](../examples/shader/shader_defs.rs) | A shader that uses "shaders defs" (a bevy tool to selectively toggle parts of a shader) -[Texture Binding Array (Bindless Textures)](../examples/shader/texture_binding_array.rs) | A shader that shows how bind and sample multiple textures as a binding array (a.k.a. bindless textures). +[Texture Binding Array (Bindless Textures)](../examples/shader/texture_binding_array.rs) | A shader that shows how to bind and sample multiple textures as a binding array (a.k.a. bindless textures). ## Stress Tests diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index c2ef3cced7b02..7536be80252f1 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -1,3 +1,6 @@ +//! A shader that binds several textures onto one +//! `binding_array>` shader binding slot and sample non-uniformly. + use bevy::{ prelude::*, reflect::TypeUuid, @@ -10,8 +13,6 @@ use bevy::{ }; use std::num::NonZeroU32; -/// This example illustrates how to bind several textures in one -/// `binding_array>` shader binding slot and sample non-uniformly. fn main() { App::new() .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) From 792c169d8ecf58bdcdc0618dd316e2c9219d7b96 Mon Sep 17 00:00:00 2001 From: cryscan Date: Tue, 24 Jan 2023 18:13:30 -0500 Subject: [PATCH 11/11] Add render device feature check. --- examples/shader/texture_binding_array.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index 7536be80252f1..def63807c4d3c 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -31,7 +31,21 @@ fn setup( mut meshes: ResMut>, mut materials: ResMut>, asset_server: Res, + render_device: Res, ) { + // 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()