Skip to content

Commit 160c1d4

Browse files
pcwaltonBD103
authored andcommitted
Make StandardMaterial bindless. (#16644)
This commit makes `StandardMaterial` use bindless textures, as implemented in PR #16368. Non-bindless mode, as used for example in Metal and WebGL 2, remains fully supported via a plethora of `#ifdef BINDLESS` preprocessor definitions. Unfortunately, this PR introduces quite a bit of unsightliness into the PBR shaders. This is a result of the fact that WGSL supports neither passing binding arrays to functions nor passing individual *elements* of binding arrays to functions, except directly to texture sample functions. Thus we're unable to use the `sample_texture` abstraction that helped abstract over the meshlet and non-meshlet paths. I don't think there's anything we can do to help this other than to suggest improvements to upstream Naga.
1 parent b45248b commit 160c1d4

File tree

14 files changed

+574
-166
lines changed

14 files changed

+574
-166
lines changed

crates/bevy_pbr/src/extended_material.rs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,32 @@ impl<B: Material, E: MaterialExtension> AsBindGroup for ExtendedMaterial<B, E> {
155155
layout: &BindGroupLayout,
156156
render_device: &RenderDevice,
157157
(base_param, extended_param): &mut SystemParamItem<'_, '_, Self::Param>,
158+
mut force_no_bindless: bool,
158159
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError> {
160+
// Only allow bindless mode if both the base material and the extension
161+
// support it.
162+
force_no_bindless = force_no_bindless
163+
|| B::BINDLESS_SLOT_COUNT.is_none()
164+
|| E::BINDLESS_SLOT_COUNT.is_none();
165+
159166
// add together the bindings of the base material and the user material
160167
let UnpreparedBindGroup {
161168
mut bindings,
162169
data: base_data,
163-
} = B::unprepared_bind_group(&self.base, layout, render_device, base_param)?;
164-
let extended_bindgroup =
165-
E::unprepared_bind_group(&self.extension, layout, render_device, extended_param)?;
170+
} = B::unprepared_bind_group(
171+
&self.base,
172+
layout,
173+
render_device,
174+
base_param,
175+
force_no_bindless,
176+
)?;
177+
let extended_bindgroup = E::unprepared_bind_group(
178+
&self.extension,
179+
layout,
180+
render_device,
181+
extended_param,
182+
force_no_bindless,
183+
)?;
166184

167185
bindings.extend(extended_bindgroup.bindings.0);
168186

@@ -174,13 +192,23 @@ impl<B: Material, E: MaterialExtension> AsBindGroup for ExtendedMaterial<B, E> {
174192

175193
fn bind_group_layout_entries(
176194
render_device: &RenderDevice,
195+
mut force_no_bindless: bool,
177196
) -> Vec<bevy_render::render_resource::BindGroupLayoutEntry>
178197
where
179198
Self: Sized,
180199
{
200+
// Only allow bindless mode if both the base material and the extension
201+
// support it.
202+
force_no_bindless = force_no_bindless
203+
|| B::BINDLESS_SLOT_COUNT.is_none()
204+
|| E::BINDLESS_SLOT_COUNT.is_none();
205+
181206
// add together the bindings of the standard material and the user material
182-
let mut entries = B::bind_group_layout_entries(render_device);
183-
entries.extend(E::bind_group_layout_entries(render_device));
207+
let mut entries = B::bind_group_layout_entries(render_device, force_no_bindless);
208+
entries.extend(E::bind_group_layout_entries(
209+
render_device,
210+
force_no_bindless,
211+
));
184212
entries
185213
}
186214
}

crates/bevy_pbr/src/material.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,7 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
10621062
&pipeline.material_layout,
10631063
render_device,
10641064
material_param,
1065+
false,
10651066
) {
10661067
Ok(unprepared) => {
10671068
bind_group_allocator.init(render_device, *material_binding_id, unprepared);

crates/bevy_pbr/src/material_bind_groups.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ where
767767
fn from_world(world: &mut World) -> Self {
768768
// Create a new bind group allocator.
769769
let render_device = world.resource::<RenderDevice>();
770-
let bind_group_layout_entries = M::bind_group_layout_entries(render_device);
770+
let bind_group_layout_entries = M::bind_group_layout_entries(render_device, false);
771771
let bind_group_layout =
772772
render_device.create_bind_group_layout(M::label(), &bind_group_layout_entries);
773773
let fallback_buffers =

crates/bevy_pbr/src/pbr_material.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub enum UvChannel {
3131
#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
3232
#[bind_group_data(StandardMaterialKey)]
3333
#[uniform(0, StandardMaterialUniform)]
34+
#[bindless(16)]
3435
#[reflect(Default, Debug)]
3536
pub struct StandardMaterial {
3637
/// The color of the surface of the material before lighting.

crates/bevy_pbr/src/prepass/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,11 @@ where
494494
shader_defs.push("HAS_PREVIOUS_MORPH".into());
495495
}
496496

497+
// If bindless mode is on, add a `BINDLESS` define.
498+
if self.material_pipeline.bindless {
499+
shader_defs.push("BINDLESS".into());
500+
}
501+
497502
if key
498503
.mesh_key
499504
.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER)

crates/bevy_pbr/src/render/parallax_mapping.wgsl

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
#define_import_path bevy_pbr::parallax_mapping
22

3-
#import bevy_pbr::pbr_bindings::{depth_map_texture, depth_map_sampler}
3+
#import bevy_pbr::{
4+
pbr_bindings::{depth_map_texture, depth_map_sampler},
5+
mesh_bindings::mesh
6+
}
47

5-
fn sample_depth_map(uv: vec2<f32>) -> f32 {
8+
fn sample_depth_map(uv: vec2<f32>, instance_index: u32) -> f32 {
9+
let slot = mesh[instance_index].material_bind_group_slot;
610
// We use `textureSampleLevel` over `textureSample` because the wgpu DX12
711
// backend (Fxc) panics when using "gradient instructions" inside a loop.
812
// It results in the whole loop being unrolled by the shader compiler,
@@ -13,7 +17,17 @@ fn sample_depth_map(uv: vec2<f32>) -> f32 {
1317
// the MIP level, so no gradient instructions are used, and we can use
1418
// sample_depth_map in our loop.
1519
// See https://stackoverflow.com/questions/56581141/direct3d11-gradient-instruction-used-in-a-loop-with-varying-iteration-forcing
16-
return textureSampleLevel(depth_map_texture, depth_map_sampler, uv, 0.0).r;
20+
return textureSampleLevel(
21+
#ifdef BINDLESS
22+
depth_map_texture[slot],
23+
depth_map_sampler[slot],
24+
#else // BINDLESS
25+
depth_map_texture,
26+
depth_map_sampler,
27+
#endif // BINDLESS
28+
uv,
29+
0.0
30+
).r;
1731
}
1832

1933
// An implementation of parallax mapping, see https://en.wikipedia.org/wiki/Parallax_mapping
@@ -26,6 +40,7 @@ fn parallaxed_uv(
2640
original_uv: vec2<f32>,
2741
// The vector from the camera to the fragment at the surface in tangent space
2842
Vt: vec3<f32>,
43+
instance_index: u32,
2944
) -> vec2<f32> {
3045
if max_layer_count < 1.0 {
3146
return original_uv;
@@ -53,15 +68,15 @@ fn parallaxed_uv(
5368
var delta_uv = depth_scale * layer_depth * Vt.xy * vec2(1.0, -1.0) / view_steepness;
5469

5570
var current_layer_depth = 0.0;
56-
var texture_depth = sample_depth_map(uv);
71+
var texture_depth = sample_depth_map(uv, instance_index);
5772

5873
// texture_depth > current_layer_depth means the depth map depth is deeper
5974
// than the depth the ray would be at this UV offset so the ray has not
6075
// intersected the surface
6176
for (var i: i32 = 0; texture_depth > current_layer_depth && i <= i32(layer_count); i++) {
6277
current_layer_depth += layer_depth;
6378
uv += delta_uv;
64-
texture_depth = sample_depth_map(uv);
79+
texture_depth = sample_depth_map(uv, instance_index);
6580
}
6681

6782
#ifdef RELIEF_MAPPING
@@ -79,7 +94,7 @@ fn parallaxed_uv(
7994
current_layer_depth -= delta_depth;
8095

8196
for (var i: u32 = 0u; i < max_steps; i++) {
82-
texture_depth = sample_depth_map(uv);
97+
texture_depth = sample_depth_map(uv, instance_index);
8398

8499
// Halve the deltas for the next step
85100
delta_uv *= 0.5;
@@ -103,7 +118,7 @@ fn parallaxed_uv(
103118
// may skip small details and result in writhing material artifacts.
104119
let previous_uv = uv - delta_uv;
105120
let next_depth = texture_depth - current_layer_depth;
106-
let previous_depth = sample_depth_map(previous_uv) - current_layer_depth + layer_depth;
121+
let previous_depth = sample_depth_map(previous_uv, instance_index) - current_layer_depth + layer_depth;
107122

108123
let weight = next_depth / (next_depth - previous_depth);
109124

crates/bevy_pbr/src/render/pbr_bindings.wgsl

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
#import bevy_pbr::pbr_types::StandardMaterial
44

5+
#ifdef BINDLESS
6+
@group(2) @binding(0) var<storage> material: binding_array<StandardMaterial, 16>;
7+
@group(2) @binding(1) var base_color_texture: binding_array<texture_2d<f32>, 16>;
8+
@group(2) @binding(2) var base_color_sampler: binding_array<sampler, 16>;
9+
@group(2) @binding(3) var emissive_texture: binding_array<texture_2d<f32>, 16>;
10+
@group(2) @binding(4) var emissive_sampler: binding_array<sampler, 16>;
11+
@group(2) @binding(5) var metallic_roughness_texture: binding_array<texture_2d<f32>, 16>;
12+
@group(2) @binding(6) var metallic_roughness_sampler: binding_array<sampler, 16>;
13+
@group(2) @binding(7) var occlusion_texture: binding_array<texture_2d<f32>, 16>;
14+
@group(2) @binding(8) var occlusion_sampler: binding_array<sampler, 16>;
15+
@group(2) @binding(9) var normal_map_texture: binding_array<texture_2d<f32>, 16>;
16+
@group(2) @binding(10) var normal_map_sampler: binding_array<sampler, 16>;
17+
@group(2) @binding(11) var depth_map_texture: binding_array<texture_2d<f32>, 16>;
18+
@group(2) @binding(12) var depth_map_sampler: binding_array<sampler, 16>;
19+
#else // BINDLESS
520
@group(2) @binding(0) var<uniform> material: StandardMaterial;
621
@group(2) @binding(1) var base_color_texture: texture_2d<f32>;
722
@group(2) @binding(2) var base_color_sampler: sampler;
@@ -15,23 +30,50 @@
1530
@group(2) @binding(10) var normal_map_sampler: sampler;
1631
@group(2) @binding(11) var depth_map_texture: texture_2d<f32>;
1732
@group(2) @binding(12) var depth_map_sampler: sampler;
33+
#endif // BINDLESS
34+
1835
#ifdef PBR_ANISOTROPY_TEXTURE_SUPPORTED
36+
#ifdef BINDLESS
37+
@group(2) @binding(13) var anisotropy_texture: binding_array<texture_2d<f32>, 16>;
38+
@group(2) @binding(14) var anisotropy_sampler: binding_array<sampler, 16>;
39+
#else // BINDLESS
1940
@group(2) @binding(13) var anisotropy_texture: texture_2d<f32>;
2041
@group(2) @binding(14) var anisotropy_sampler: sampler;
21-
#endif
42+
#endif // BINDLESS
43+
#endif // PBR_ANISOTROPY_TEXTURE_SUPPORTED
44+
2245
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
46+
#ifdef BINDLESS
47+
@group(2) @binding(15) var specular_transmission_texture: binding_array<texture_2d<f32>, 16>;
48+
@group(2) @binding(16) var specular_transmission_sampler: binding_array<sampler, 16>;
49+
@group(2) @binding(17) var thickness_texture: binding_array<texture_2d<f32>, 16>;
50+
@group(2) @binding(18) var thickness_sampler: binding_array<sampler, 16>;
51+
@group(2) @binding(19) var diffuse_transmission_texture: binding_array<texture_2d<f32>, 16>;
52+
@group(2) @binding(20) var diffuse_transmission_sampler: binding_array<sampler, 16>;
53+
#else // BINDLESS
2354
@group(2) @binding(15) var specular_transmission_texture: texture_2d<f32>;
2455
@group(2) @binding(16) var specular_transmission_sampler: sampler;
2556
@group(2) @binding(17) var thickness_texture: texture_2d<f32>;
2657
@group(2) @binding(18) var thickness_sampler: sampler;
2758
@group(2) @binding(19) var diffuse_transmission_texture: texture_2d<f32>;
2859
@group(2) @binding(20) var diffuse_transmission_sampler: sampler;
29-
#endif
60+
#endif // BINDLESS
61+
#endif // PBR_TRANSMISSION_TEXTURES_SUPPORTED
62+
3063
#ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED
64+
#ifdef BINDLESS
65+
@group(2) @binding(21) var clearcoat_texture: binding_array<texture_2d<f32>, 16>;
66+
@group(2) @binding(22) var clearcoat_sampler: binding_array<sampler, 16>;
67+
@group(2) @binding(23) var clearcoat_roughness_texture: binding_array<texture_2d<f32>, 16>;
68+
@group(2) @binding(24) var clearcoat_roughness_sampler: binding_array<sampler, 16>;
69+
@group(2) @binding(25) var clearcoat_normal_texture: binding_array<texture_2d<f32>, 16>;
70+
@group(2) @binding(26) var clearcoat_normal_sampler: binding_array<sampler, 16>;
71+
#else // BINDLESS
3172
@group(2) @binding(21) var clearcoat_texture: texture_2d<f32>;
3273
@group(2) @binding(22) var clearcoat_sampler: sampler;
3374
@group(2) @binding(23) var clearcoat_roughness_texture: texture_2d<f32>;
3475
@group(2) @binding(24) var clearcoat_roughness_sampler: sampler;
3576
@group(2) @binding(25) var clearcoat_normal_texture: texture_2d<f32>;
3677
@group(2) @binding(26) var clearcoat_normal_sampler: sampler;
37-
#endif
78+
#endif // BINDLESS
79+
#endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED

0 commit comments

Comments
 (0)