From 7ed1f327d91a73b3c96f306ae15e53bcc39192f3 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 10 Dec 2024 09:48:56 -0800 Subject: [PATCH] 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. --- crates/bevy_pbr/src/extended_material.rs | 38 +- crates/bevy_pbr/src/material.rs | 1 + crates/bevy_pbr/src/material_bind_groups.rs | 2 +- crates/bevy_pbr/src/pbr_material.rs | 1 + crates/bevy_pbr/src/prepass/mod.rs | 5 + .../bevy_pbr/src/render/parallax_mapping.wgsl | 29 +- crates/bevy_pbr/src/render/pbr_bindings.wgsl | 48 +- crates/bevy_pbr/src/render/pbr_fragment.wgsl | 492 ++++++++++++++---- crates/bevy_pbr/src/render/pbr_functions.wgsl | 15 - crates/bevy_pbr/src/render/pbr_prepass.wgsl | 27 +- .../src/render/pbr_prepass_functions.wgsl | 36 +- .../bevy_render/macros/src/as_bind_group.rs | 12 +- .../src/render_resource/bind_group.rs | 31 +- examples/shader/texture_binding_array.rs | 3 +- 14 files changed, 574 insertions(+), 166 deletions(-) diff --git a/crates/bevy_pbr/src/extended_material.rs b/crates/bevy_pbr/src/extended_material.rs index ae8f9f710e0d2..a443b953d5cfc 100644 --- a/crates/bevy_pbr/src/extended_material.rs +++ b/crates/bevy_pbr/src/extended_material.rs @@ -155,14 +155,32 @@ impl AsBindGroup for ExtendedMaterial { layout: &BindGroupLayout, render_device: &RenderDevice, (base_param, extended_param): &mut SystemParamItem<'_, '_, Self::Param>, + mut force_no_bindless: bool, ) -> Result, AsBindGroupError> { + // Only allow bindless mode if both the base material and the extension + // support it. + force_no_bindless = force_no_bindless + || B::BINDLESS_SLOT_COUNT.is_none() + || E::BINDLESS_SLOT_COUNT.is_none(); + // add together the bindings of the base material and the user material let UnpreparedBindGroup { mut bindings, data: base_data, - } = B::unprepared_bind_group(&self.base, layout, render_device, base_param)?; - let extended_bindgroup = - E::unprepared_bind_group(&self.extension, layout, render_device, extended_param)?; + } = B::unprepared_bind_group( + &self.base, + layout, + render_device, + base_param, + force_no_bindless, + )?; + let extended_bindgroup = E::unprepared_bind_group( + &self.extension, + layout, + render_device, + extended_param, + force_no_bindless, + )?; bindings.extend(extended_bindgroup.bindings.0); @@ -174,13 +192,23 @@ impl AsBindGroup for ExtendedMaterial { fn bind_group_layout_entries( render_device: &RenderDevice, + mut force_no_bindless: bool, ) -> Vec where Self: Sized, { + // Only allow bindless mode if both the base material and the extension + // support it. + force_no_bindless = force_no_bindless + || B::BINDLESS_SLOT_COUNT.is_none() + || E::BINDLESS_SLOT_COUNT.is_none(); + // add together the bindings of the standard material and the user material - let mut entries = B::bind_group_layout_entries(render_device); - entries.extend(E::bind_group_layout_entries(render_device)); + let mut entries = B::bind_group_layout_entries(render_device, force_no_bindless); + entries.extend(E::bind_group_layout_entries( + render_device, + force_no_bindless, + )); entries } } diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 22bb037d7ce4c..8ee7a45a57509 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -1062,6 +1062,7 @@ impl RenderAsset for PreparedMaterial { &pipeline.material_layout, render_device, material_param, + false, ) { Ok(unprepared) => { bind_group_allocator.init(render_device, *material_binding_id, unprepared); diff --git a/crates/bevy_pbr/src/material_bind_groups.rs b/crates/bevy_pbr/src/material_bind_groups.rs index b64ca339519ab..4122068229a9e 100644 --- a/crates/bevy_pbr/src/material_bind_groups.rs +++ b/crates/bevy_pbr/src/material_bind_groups.rs @@ -767,7 +767,7 @@ where fn from_world(world: &mut World) -> Self { // Create a new bind group allocator. let render_device = world.resource::(); - let bind_group_layout_entries = M::bind_group_layout_entries(render_device); + let bind_group_layout_entries = M::bind_group_layout_entries(render_device, false); let bind_group_layout = render_device.create_bind_group_layout(M::label(), &bind_group_layout_entries); let fallback_buffers = diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 1a679c08fa572..2e93b68094e17 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -31,6 +31,7 @@ pub enum UvChannel { #[derive(Asset, AsBindGroup, Reflect, Debug, Clone)] #[bind_group_data(StandardMaterialKey)] #[uniform(0, StandardMaterialUniform)] +#[bindless(16)] #[reflect(Default, Debug)] pub struct StandardMaterial { /// The color of the surface of the material before lighting. diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 05cbc9cda9fe2..2af3c7780c720 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -494,6 +494,11 @@ where shader_defs.push("HAS_PREVIOUS_MORPH".into()); } + // If bindless mode is on, add a `BINDLESS` define. + if self.material_pipeline.bindless { + shader_defs.push("BINDLESS".into()); + } + if key .mesh_key .contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) diff --git a/crates/bevy_pbr/src/render/parallax_mapping.wgsl b/crates/bevy_pbr/src/render/parallax_mapping.wgsl index a375c83bafc23..d6fe4f9bbec95 100644 --- a/crates/bevy_pbr/src/render/parallax_mapping.wgsl +++ b/crates/bevy_pbr/src/render/parallax_mapping.wgsl @@ -1,8 +1,12 @@ #define_import_path bevy_pbr::parallax_mapping -#import bevy_pbr::pbr_bindings::{depth_map_texture, depth_map_sampler} +#import bevy_pbr::{ + pbr_bindings::{depth_map_texture, depth_map_sampler}, + mesh_bindings::mesh +} -fn sample_depth_map(uv: vec2) -> f32 { +fn sample_depth_map(uv: vec2, instance_index: u32) -> f32 { + let slot = mesh[instance_index].material_bind_group_slot; // We use `textureSampleLevel` over `textureSample` because the wgpu DX12 // backend (Fxc) panics when using "gradient instructions" inside a loop. // It results in the whole loop being unrolled by the shader compiler, @@ -13,7 +17,17 @@ fn sample_depth_map(uv: vec2) -> f32 { // the MIP level, so no gradient instructions are used, and we can use // sample_depth_map in our loop. // See https://stackoverflow.com/questions/56581141/direct3d11-gradient-instruction-used-in-a-loop-with-varying-iteration-forcing - return textureSampleLevel(depth_map_texture, depth_map_sampler, uv, 0.0).r; + return textureSampleLevel( +#ifdef BINDLESS + depth_map_texture[slot], + depth_map_sampler[slot], +#else // BINDLESS + depth_map_texture, + depth_map_sampler, +#endif // BINDLESS + uv, + 0.0 + ).r; } // An implementation of parallax mapping, see https://en.wikipedia.org/wiki/Parallax_mapping @@ -26,6 +40,7 @@ fn parallaxed_uv( original_uv: vec2, // The vector from the camera to the fragment at the surface in tangent space Vt: vec3, + instance_index: u32, ) -> vec2 { if max_layer_count < 1.0 { return original_uv; @@ -53,7 +68,7 @@ fn parallaxed_uv( var delta_uv = depth_scale * layer_depth * Vt.xy * vec2(1.0, -1.0) / view_steepness; var current_layer_depth = 0.0; - var texture_depth = sample_depth_map(uv); + var texture_depth = sample_depth_map(uv, instance_index); // texture_depth > current_layer_depth means the depth map depth is deeper // than the depth the ray would be at this UV offset so the ray has not @@ -61,7 +76,7 @@ fn parallaxed_uv( for (var i: i32 = 0; texture_depth > current_layer_depth && i <= i32(layer_count); i++) { current_layer_depth += layer_depth; uv += delta_uv; - texture_depth = sample_depth_map(uv); + texture_depth = sample_depth_map(uv, instance_index); } #ifdef RELIEF_MAPPING @@ -79,7 +94,7 @@ fn parallaxed_uv( current_layer_depth -= delta_depth; for (var i: u32 = 0u; i < max_steps; i++) { - texture_depth = sample_depth_map(uv); + texture_depth = sample_depth_map(uv, instance_index); // Halve the deltas for the next step delta_uv *= 0.5; @@ -103,7 +118,7 @@ fn parallaxed_uv( // may skip small details and result in writhing material artifacts. let previous_uv = uv - delta_uv; let next_depth = texture_depth - current_layer_depth; - let previous_depth = sample_depth_map(previous_uv) - current_layer_depth + layer_depth; + let previous_depth = sample_depth_map(previous_uv, instance_index) - current_layer_depth + layer_depth; let weight = next_depth / (next_depth - previous_depth); diff --git a/crates/bevy_pbr/src/render/pbr_bindings.wgsl b/crates/bevy_pbr/src/render/pbr_bindings.wgsl index 731fba343f84a..9b9d9dcc92825 100644 --- a/crates/bevy_pbr/src/render/pbr_bindings.wgsl +++ b/crates/bevy_pbr/src/render/pbr_bindings.wgsl @@ -2,6 +2,21 @@ #import bevy_pbr::pbr_types::StandardMaterial +#ifdef BINDLESS +@group(2) @binding(0) var material: binding_array; +@group(2) @binding(1) var base_color_texture: binding_array, 16>; +@group(2) @binding(2) var base_color_sampler: binding_array; +@group(2) @binding(3) var emissive_texture: binding_array, 16>; +@group(2) @binding(4) var emissive_sampler: binding_array; +@group(2) @binding(5) var metallic_roughness_texture: binding_array, 16>; +@group(2) @binding(6) var metallic_roughness_sampler: binding_array; +@group(2) @binding(7) var occlusion_texture: binding_array, 16>; +@group(2) @binding(8) var occlusion_sampler: binding_array; +@group(2) @binding(9) var normal_map_texture: binding_array, 16>; +@group(2) @binding(10) var normal_map_sampler: binding_array; +@group(2) @binding(11) var depth_map_texture: binding_array, 16>; +@group(2) @binding(12) var depth_map_sampler: binding_array; +#else // BINDLESS @group(2) @binding(0) var material: StandardMaterial; @group(2) @binding(1) var base_color_texture: texture_2d; @group(2) @binding(2) var base_color_sampler: sampler; @@ -15,23 +30,50 @@ @group(2) @binding(10) var normal_map_sampler: sampler; @group(2) @binding(11) var depth_map_texture: texture_2d; @group(2) @binding(12) var depth_map_sampler: sampler; +#endif // BINDLESS + #ifdef PBR_ANISOTROPY_TEXTURE_SUPPORTED +#ifdef BINDLESS +@group(2) @binding(13) var anisotropy_texture: binding_array, 16>; +@group(2) @binding(14) var anisotropy_sampler: binding_array; +#else // BINDLESS @group(2) @binding(13) var anisotropy_texture: texture_2d; @group(2) @binding(14) var anisotropy_sampler: sampler; -#endif +#endif // BINDLESS +#endif // PBR_ANISOTROPY_TEXTURE_SUPPORTED + #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED +#ifdef BINDLESS +@group(2) @binding(15) var specular_transmission_texture: binding_array, 16>; +@group(2) @binding(16) var specular_transmission_sampler: binding_array; +@group(2) @binding(17) var thickness_texture: binding_array, 16>; +@group(2) @binding(18) var thickness_sampler: binding_array; +@group(2) @binding(19) var diffuse_transmission_texture: binding_array, 16>; +@group(2) @binding(20) var diffuse_transmission_sampler: binding_array; +#else // BINDLESS @group(2) @binding(15) var specular_transmission_texture: texture_2d; @group(2) @binding(16) var specular_transmission_sampler: sampler; @group(2) @binding(17) var thickness_texture: texture_2d; @group(2) @binding(18) var thickness_sampler: sampler; @group(2) @binding(19) var diffuse_transmission_texture: texture_2d; @group(2) @binding(20) var diffuse_transmission_sampler: sampler; -#endif +#endif // BINDLESS +#endif // PBR_TRANSMISSION_TEXTURES_SUPPORTED + #ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED +#ifdef BINDLESS +@group(2) @binding(21) var clearcoat_texture: binding_array, 16>; +@group(2) @binding(22) var clearcoat_sampler: binding_array; +@group(2) @binding(23) var clearcoat_roughness_texture: binding_array, 16>; +@group(2) @binding(24) var clearcoat_roughness_sampler: binding_array; +@group(2) @binding(25) var clearcoat_normal_texture: binding_array, 16>; +@group(2) @binding(26) var clearcoat_normal_sampler: binding_array; +#else // BINDLESS @group(2) @binding(21) var clearcoat_texture: texture_2d; @group(2) @binding(22) var clearcoat_sampler: sampler; @group(2) @binding(23) var clearcoat_roughness_texture: texture_2d; @group(2) @binding(24) var clearcoat_roughness_sampler: sampler; @group(2) @binding(25) var clearcoat_normal_texture: texture_2d; @group(2) @binding(26) var clearcoat_normal_sampler: sampler; -#endif +#endif // BINDLESS +#endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED diff --git a/crates/bevy_pbr/src/render/pbr_fragment.wgsl b/crates/bevy_pbr/src/render/pbr_fragment.wgsl index 91e104ede525f..7bdc8632ec334 100644 --- a/crates/bevy_pbr/src/render/pbr_fragment.wgsl +++ b/crates/bevy_pbr/src/render/pbr_fragment.wgsl @@ -70,12 +70,23 @@ fn pbr_input_from_standard_material( in: VertexOutput, is_front: bool, ) -> pbr_types::PbrInput { - let double_sided = (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u; +#ifdef BINDLESS + let slot = mesh[in.instance_index].material_bind_group_slot; + let flags = pbr_bindings::material[slot].flags; + let base_color = pbr_bindings::material[slot].base_color; + let deferred_lighting_pass_id = pbr_bindings::material[slot].deferred_lighting_pass_id; +#else // BINDLESS + let flags = pbr_bindings::material.flags; + let base_color = pbr_bindings::material.base_color; + let deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id; +#endif + + let double_sided = (flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u; var pbr_input: pbr_types::PbrInput = pbr_input_from_vertex_output(in, is_front, double_sided); - pbr_input.material.flags = pbr_bindings::material.flags; - pbr_input.material.base_color *= pbr_bindings::material.base_color; - pbr_input.material.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id; + pbr_input.material.flags = flags; + pbr_input.material.base_color *= base_color; + pbr_input.material.deferred_lighting_pass_id = deferred_lighting_pass_id; // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001); @@ -91,7 +102,13 @@ fn pbr_input_from_standard_material( // TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass #ifdef VERTEX_UVS + +#ifdef BINDLESS + let uv_transform = pbr_bindings::material[slot].uv_transform; +#else // BINDLESS let uv_transform = pbr_bindings::material.uv_transform; +#endif // BINDLESS + #ifdef VERTEX_UVS_A var uv = (uv_transform * vec3(in.uv, 1.0)).xy; #endif @@ -104,7 +121,7 @@ fn pbr_input_from_standard_material( #endif #ifdef VERTEX_TANGENTS - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { let V = pbr_input.V; let TBN = pbr_functions::calculate_tbn_mikktspace(in.world_normal, in.world_tangent); let T = TBN[0]; @@ -115,28 +132,42 @@ fn pbr_input_from_standard_material( #ifdef VERTEX_UVS_A // TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass uv = parallaxed_uv( +#ifdef BINDLESS + pbr_bindings::material[slot].parallax_depth_scale, + pbr_bindings::material[slot].max_parallax_layer_count, + pbr_bindings::material[slot].max_relief_mapping_search_steps, +#else // BINDLESS pbr_bindings::material.parallax_depth_scale, pbr_bindings::material.max_parallax_layer_count, pbr_bindings::material.max_relief_mapping_search_steps, +#endif // BINDLESS uv, // Flip the direction of Vt to go toward the surface to make the // parallax mapping algorithm easier to understand and reason // about. -Vt, + in.instance_index, ); #endif #ifdef VERTEX_UVS_B // TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass uv_b = parallaxed_uv( +#ifdef BINDLESS + pbr_bindings::material[slot].parallax_depth_scale, + pbr_bindings::material[slot].max_parallax_layer_count, + pbr_bindings::material[slot].max_relief_mapping_search_steps, +#else // BINDLESS pbr_bindings::material.parallax_depth_scale, pbr_bindings::material.max_parallax_layer_count, pbr_bindings::material.max_relief_mapping_search_steps, +#endif // BINDLESS uv_b, // Flip the direction of Vt to go toward the surface to make the // parallax mapping algorithm easier to understand and reason // about. -Vt, + in.instance_index, ); #else uv_b = uv; @@ -144,27 +175,47 @@ fn pbr_input_from_standard_material( } #endif // VERTEX_TANGENTS - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { - pbr_input.material.base_color *= pbr_functions::sample_texture( - pbr_bindings::base_color_texture, - pbr_bindings::base_color_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { + pbr_input.material.base_color *= +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::base_color_texture[slot], + pbr_bindings::base_color_sampler[slot], +#else // BINDLESS + pbr_bindings::base_color_texture, + pbr_bindings::base_color_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_BASE_COLOR_UV_B - uv_b, + uv_b, #else - uv, + uv, #endif - bias, +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS ); #ifdef ALPHA_TO_COVERAGE // Sharpen alpha edges. // // https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f - let alpha_mode = pbr_bindings::material.flags & - pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; + let alpha_mode = flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ALPHA_TO_COVERAGE { - pbr_input.material.base_color.a = (pbr_input.material.base_color.a - - pbr_bindings::material.alpha_cutoff) / + +#ifdef BINDLESS + let alpha_cutoff = pbr_bindings::material[slot].alpha_cutoff; +#else // BINDLESS + let alpha_cutoff = pbr_bindings::material.alpha_cutoff; +#endif // BINDLESS + + pbr_input.material.base_color.a = (pbr_input.material.base_color.a - alpha_cutoff) / max(fwidth(pbr_input.material.base_color.a), 0.0001) + 0.5; } #endif // ALPHA_TO_COVERAGE @@ -172,50 +223,100 @@ fn pbr_input_from_standard_material( } #endif // VERTEX_UVS - pbr_input.material.flags = pbr_bindings::material.flags; + pbr_input.material.flags = flags; // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { +#ifdef BINDLESS + pbr_input.material.reflectance = pbr_bindings::material[slot].reflectance; + pbr_input.material.ior = pbr_bindings::material[slot].ior; + pbr_input.material.attenuation_color = pbr_bindings::material[slot].attenuation_color; + pbr_input.material.attenuation_distance = pbr_bindings::material[slot].attenuation_distance; + pbr_input.material.alpha_cutoff = pbr_bindings::material[slot].alpha_cutoff; +#else // BINDLESS pbr_input.material.reflectance = pbr_bindings::material.reflectance; pbr_input.material.ior = pbr_bindings::material.ior; pbr_input.material.attenuation_color = pbr_bindings::material.attenuation_color; pbr_input.material.attenuation_distance = pbr_bindings::material.attenuation_distance; pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff; +#endif // BINDLESS // emissive +#ifdef BINDLESS + var emissive: vec4 = pbr_bindings::material[slot].emissive; +#else // BINDLESS var emissive: vec4 = pbr_bindings::material.emissive; +#endif // BINDLESS + #ifdef VERTEX_UVS - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { - emissive = vec4(emissive.rgb * pbr_functions::sample_texture( - pbr_bindings::emissive_texture, - pbr_bindings::emissive_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { + emissive = vec4(emissive.rgb * +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::emissive_texture[slot], + pbr_bindings::emissive_sampler[slot], +#else // BINDLESS + pbr_bindings::emissive_texture, + pbr_bindings::emissive_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_EMISSIVE_UV_B - uv_b, + uv_b, #else - uv, + uv, #endif - bias, - ).rgb, emissive.a); +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).rgb, + emissive.a); } #endif pbr_input.material.emissive = emissive; // metallic and perceptual roughness +#ifdef BINDLESS + var metallic: f32 = pbr_bindings::material[slot].metallic; + var perceptual_roughness: f32 = pbr_bindings::material[slot].perceptual_roughness; +#else // BINDLESS var metallic: f32 = pbr_bindings::material.metallic; var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness; +#endif // BINDLESS + let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness); #ifdef VERTEX_UVS - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { - let metallic_roughness = pbr_functions::sample_texture( - pbr_bindings::metallic_roughness_texture, - pbr_bindings::metallic_roughness_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { + let metallic_roughness = +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::metallic_roughness_texture[slot], + pbr_bindings::metallic_roughness_sampler[slot], +#else // BINDLESS + pbr_bindings::metallic_roughness_texture, + pbr_bindings::metallic_roughness_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_METALLIC_ROUGHNESS_UV_B - uv_b, + uv_b, #else - uv, + uv, #endif - bias, - ); +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ); // Sampling from GLTF standard channels for now metallic *= metallic_roughness.b; perceptual_roughness *= metallic_roughness.g; @@ -225,76 +326,158 @@ fn pbr_input_from_standard_material( pbr_input.material.perceptual_roughness = perceptual_roughness; // Clearcoat factor +#ifdef BINDLESS + pbr_input.material.clearcoat = pbr_bindings::material[slot].clearcoat; +#else // BINDLESS pbr_input.material.clearcoat = pbr_bindings::material.clearcoat; +#endif // BINDLESS + #ifdef VERTEX_UVS #ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_CLEARCOAT_TEXTURE_BIT) != 0u) { - pbr_input.material.clearcoat *= pbr_functions::sample_texture( - pbr_bindings::clearcoat_texture, - pbr_bindings::clearcoat_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_CLEARCOAT_TEXTURE_BIT) != 0u) { + pbr_input.material.clearcoat *= +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::clearcoat_texture[slot], + pbr_bindings::clearcoat_sampler[slot], +#else // BINDLESS + pbr_bindings::clearcoat_texture, + pbr_bindings::clearcoat_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_CLEARCOAT_UV_B - uv_b, + uv_b, #else - uv, + uv, #endif - bias, - ).r; +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).r; } #endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #endif // VERTEX_UVS // Clearcoat roughness - pbr_input.material.clearcoat_perceptual_roughness = pbr_bindings::material.clearcoat_perceptual_roughness; +#ifdef BINDLESS + pbr_input.material.clearcoat_perceptual_roughness = + pbr_bindings::material[slot].clearcoat_perceptual_roughness; +#else // BINDLESS + pbr_input.material.clearcoat_perceptual_roughness = + pbr_bindings::material.clearcoat_perceptual_roughness; +#endif // BINDLESS + #ifdef VERTEX_UVS #ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_CLEARCOAT_ROUGHNESS_TEXTURE_BIT) != 0u) { - pbr_input.material.clearcoat_perceptual_roughness *= pbr_functions::sample_texture( - pbr_bindings::clearcoat_roughness_texture, - pbr_bindings::clearcoat_roughness_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_CLEARCOAT_ROUGHNESS_TEXTURE_BIT) != 0u) { + pbr_input.material.clearcoat_perceptual_roughness *= +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::clearcoat_roughness_texture[slot], + pbr_bindings::clearcoat_roughness_sampler[slot], +#else // BINDLESS + pbr_bindings::clearcoat_roughness_texture, + pbr_bindings::clearcoat_roughness_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_CLEARCOAT_ROUGHNESS_UV_B - uv_b, + uv_b, #else - uv, + uv, #endif - bias, - ).g; +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).g; } #endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #endif // VERTEX_UVS +#ifdef BINDLESS + var specular_transmission: f32 = pbr_bindings::material[slot].specular_transmission; +#else // BINDLESS var specular_transmission: f32 = pbr_bindings::material.specular_transmission; +#endif // BINDLESS + #ifdef VERTEX_UVS #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_SPECULAR_TRANSMISSION_TEXTURE_BIT) != 0u) { - specular_transmission *= pbr_functions::sample_texture( - pbr_bindings::specular_transmission_texture, - pbr_bindings::specular_transmission_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_SPECULAR_TRANSMISSION_TEXTURE_BIT) != 0u) { + specular_transmission *= +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::specular_transmission_texture[slot], + pbr_bindings::specular_transmission_sampler[slot], +#else // BINDLESS + pbr_bindings::specular_transmission_texture, + pbr_bindings::specular_transmission_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_SPECULAR_TRANSMISSION_UV_B - uv_b, + uv_b, #else - uv, + uv, #endif - bias, - ).r; +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).r; } #endif #endif pbr_input.material.specular_transmission = specular_transmission; +#ifdef BINDLESS + var thickness: f32 = pbr_bindings::material[slot].thickness; +#else // BINDLESS var thickness: f32 = pbr_bindings::material.thickness; +#endif // BINDLESS + #ifdef VERTEX_UVS #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_THICKNESS_TEXTURE_BIT) != 0u) { - thickness *= pbr_functions::sample_texture( - pbr_bindings::thickness_texture, - pbr_bindings::thickness_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_THICKNESS_TEXTURE_BIT) != 0u) { + thickness *= +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::thickness_texture[slot], + pbr_bindings::thickness_sampler[slot], +#else // BINDLESS + pbr_bindings::thickness_texture, + pbr_bindings::thickness_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_THICKNESS_UV_B - uv_b, + uv_b, #else - uv, + uv, #endif - bias, - ).g; +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).g; } #endif #endif @@ -307,20 +490,40 @@ fn pbr_input_from_standard_material( #endif pbr_input.material.thickness = thickness; +#ifdef BINDLESS + var diffuse_transmission = pbr_bindings::material[slot].diffuse_transmission; +#else // BINDLESS var diffuse_transmission = pbr_bindings::material.diffuse_transmission; +#endif // BINDLESS + #ifdef VERTEX_UVS #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DIFFUSE_TRANSMISSION_TEXTURE_BIT) != 0u) { - diffuse_transmission *= pbr_functions::sample_texture( - pbr_bindings::diffuse_transmission_texture, - pbr_bindings::diffuse_transmission_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_DIFFUSE_TRANSMISSION_TEXTURE_BIT) != 0u) { + diffuse_transmission *= +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::diffuse_transmission_texture[slot], + pbr_bindings::diffuse_transmission_sampler[slot], +#else // BINDLESS + pbr_bindings::diffuse_transmission_texture, + pbr_bindings::diffuse_transmission_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION_UV_B - uv_b, + uv_b, #else - uv, + uv, #endif - bias, - ).a; +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).a; } #endif #endif @@ -329,17 +532,32 @@ fn pbr_input_from_standard_material( var diffuse_occlusion: vec3 = vec3(1.0); var specular_occlusion: f32 = 1.0; #ifdef VERTEX_UVS - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { - diffuse_occlusion *= pbr_functions::sample_texture( - pbr_bindings::occlusion_texture, - pbr_bindings::occlusion_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { + diffuse_occlusion *= +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::occlusion_texture[slot], + pbr_bindings::occlusion_sampler[slot], +#else // BINDLESS + pbr_bindings::occlusion_texture, + pbr_bindings::occlusion_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_OCCLUSION_UV_B - uv_b, + uv_b, #else - uv, + uv, #endif - bias, - ).r; +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).r; } #endif #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION @@ -366,24 +584,33 @@ fn pbr_input_from_standard_material( #ifdef STANDARD_MATERIAL_NORMAL_MAP - let Nt = pbr_functions::sample_texture( - pbr_bindings::normal_map_texture, - pbr_bindings::normal_map_sampler, + let Nt = +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::normal_map_texture[slot], + pbr_bindings::normal_map_sampler[slot], +#else // BINDLESS + pbr_bindings::normal_map_texture, + pbr_bindings::normal_map_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_NORMAL_MAP_UV_B uv_b, #else uv, #endif - bias, - ).rgb; +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).rgb; - pbr_input.N = pbr_functions::apply_normal_mapping( - pbr_bindings::material.flags, - TBN, - double_sided, - is_front, - Nt, - ); + pbr_input.N = pbr_functions::apply_normal_mapping(flags, TBN, double_sided, is_front, Nt); #endif // STANDARD_MATERIAL_NORMAL_MAP @@ -395,19 +622,34 @@ fn pbr_input_from_standard_material( #ifdef STANDARD_MATERIAL_CLEARCOAT_NORMAL_MAP - let clearcoat_Nt = pbr_functions::sample_texture( - pbr_bindings::clearcoat_normal_texture, - pbr_bindings::clearcoat_normal_sampler, + let clearcoat_Nt = +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::clearcoat_normal_texture[slot], + pbr_bindings::clearcoat_normal_sampler[slot], +#else // BINDLESS + pbr_bindings::clearcoat_normal_texture, + pbr_bindings::clearcoat_normal_sampler, +#endif // BINDLESS #ifdef STANDARD_MATERIAL_CLEARCOAT_NORMAL_UV_B uv_b, #else uv, #endif - bias, - ).rgb; +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).rgb; pbr_input.clearcoat_N = pbr_functions::apply_normal_mapping( - pbr_bindings::material.flags, + flags, TBN, double_sided, is_front, @@ -429,21 +671,41 @@ fn pbr_input_from_standard_material( #ifdef VERTEX_TANGENTS #ifdef STANDARD_MATERIAL_ANISOTROPY +#ifdef BINDLESS + var anisotropy_strength = pbr_bindings::material[slot].anisotropy_strength; + var anisotropy_direction = pbr_bindings::material[slot].anisotropy_rotation; +#else // BINDLESS var anisotropy_strength = pbr_bindings::material.anisotropy_strength; var anisotropy_direction = pbr_bindings::material.anisotropy_rotation; +#endif // BINDLESS // Adjust based on the anisotropy map if there is one. - if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ANISOTROPY_TEXTURE_BIT) != 0u) { - let anisotropy_texel = pbr_functions::sample_texture( - pbr_bindings::anisotropy_texture, - pbr_bindings::anisotropy_sampler, + if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_ANISOTROPY_TEXTURE_BIT) != 0u) { + let anisotropy_texel = +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::anisotropy_texture[slot], + pbr_bindings::anisotropy_sampler[slot], +#else // BINDLESS + pbr_bindings::anisotropy_texture, + pbr_bindings::anisotropy_sampler, +#endif #ifdef STANDARD_MATERIAL_ANISOTROPY_UV_B - uv_b, + uv_b, #else // STANDARD_MATERIAL_ANISOTROPY_UV_B - uv, + uv, #endif // STANDARD_MATERIAL_ANISOTROPY_UV_B - bias, - ).rgb; +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).rgb; let anisotropy_direction_from_texture = normalize(anisotropy_texel.rg * 2.0 - 1.0); // Rotate by the anisotropy direction. @@ -468,10 +730,14 @@ fn pbr_input_from_standard_material( // TODO: Meshlet support #ifdef LIGHTMAP - pbr_input.lightmap_light = lightmap( - in.uv_b, - pbr_bindings::material.lightmap_exposure, - in.instance_index); + +#ifdef BINDLESS + let lightmap_exposure = pbr_bindings::material[slot].lightmap_exposure; +#else // BINDLESS + let lightmap_exposure = pbr_bindings::material.lightmap_exposure; +#endif // BINDLESS + + pbr_input.lightmap_light = lightmap(in.uv_b, lightmap_exposure, in.instance_index); #endif } diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index 093af38e591a8..1983257199577 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -121,21 +121,6 @@ fn alpha_discard(material: pbr_types::StandardMaterial, output_color: vec4) return color; } -// Samples a texture using the appropriate biasing metric for the type of mesh -// in use (mesh vs. meshlet). -fn sample_texture( - texture: texture_2d, - samp: sampler, - uv: vec2, - bias: SampleBias, -) -> vec4 { -#ifdef MESHLET_MESH_MATERIAL_PASS - return textureSampleGrad(texture, samp, uv, bias.ddx_uv, bias.ddy_uv); -#else - return textureSampleBias(texture, samp, uv, bias.mip_bias); -#endif -} - fn prepare_world_normal( world_normal: vec3, double_sided: bool, diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index a2758ce6981f7..8acf21d4aaf15 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -76,12 +76,27 @@ fn fragment( bias.mip_bias = view.mip_bias; #endif // MESHLET_MESH_MATERIAL_PASS - let Nt = pbr_functions::sample_texture( - pbr_bindings::normal_map_texture, - pbr_bindings::normal_map_sampler, - uv, - bias, - ).rgb; + let Nt = +#ifdef MESHLET_MESH_MATERIAL_PASS + textureSampleGrad( +#else // MESHLET_MESH_MATERIAL_PASS + textureSampleBias( +#endif // MESHLET_MESH_MATERIAL_PASS +#ifdef BINDLESS + pbr_bindings::normal_map_texture[slot], + pbr_bindings::normal_map_sampler[slot], +#else // BINDLESS + pbr_bindings::normal_map_texture, + pbr_bindings::normal_map_sampler, +#endif // BINDLESS + uv, +#ifdef MESHLET_MESH_MATERIAL_PASS + bias.ddx_uv, + bias.ddy_uv, +#else // MESHLET_MESH_MATERIAL_PASS + bias.mip_bias, +#endif // MESHLET_MESH_MATERIAL_PASS + ).rgb; let TBN = pbr_functions::calculate_tbn_mikktspace(normal, in.world_tangent); normal = pbr_functions::apply_normal_mapping( diff --git a/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl b/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl index abdaccbd303f6..493e56f27781d 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl @@ -3,6 +3,7 @@ #import bevy_pbr::{ prepass_io::VertexOutput, prepass_bindings::previous_view_uniforms, + mesh_bindings::mesh, mesh_view_bindings::view, pbr_bindings, pbr_types, @@ -15,7 +16,12 @@ const PREMULTIPLIED_ALPHA_CUTOFF = 0.05; fn prepass_alpha_discard(in: VertexOutput) { #ifdef MAY_DISCARD +#ifdef BINDLESS + let slot = mesh[in.instance_index].material_bind_group_slot; + var output_color: vec4 = pbr_bindings::material[slot].base_color; +#else // BINDLESS var output_color: vec4 = pbr_bindings::material.base_color; +#endif // BINDLESS #ifdef VERTEX_UVS #ifdef STANDARD_MATERIAL_BASE_COLOR_UV_B @@ -24,16 +30,38 @@ fn prepass_alpha_discard(in: VertexOutput) { var uv = in.uv; #endif // STANDARD_MATERIAL_BASE_COLOR_UV_B +#ifdef BINDLESS + let uv_transform = pbr_bindings::material[slot].uv_transform; + let flags = pbr_bindings::material[slot].flags; +#else // BINDLESS let uv_transform = pbr_bindings::material.uv_transform; + let flags = pbr_bindings::material.flags; +#endif // BINDLESS + uv = (uv_transform * vec3(uv, 1.0)).xy; - if (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u { - output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, view.mip_bias); + if (flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u { + output_color = output_color * textureSampleBias( +#ifdef BINDLESS + pbr_bindings::base_color_texture[slot], + pbr_bindings::base_color_sampler[slot], +#else // BINDLESS + pbr_bindings::base_color_texture, + pbr_bindings::base_color_sampler, +#endif // BINDLESS + uv, + view.mip_bias + ); } #endif // VERTEX_UVS - let alpha_mode = pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; + let alpha_mode = flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK { - if output_color.a < pbr_bindings::material.alpha_cutoff { +#ifdef BINDLESS + let alpha_cutoff = pbr_bindings::material[slot].alpha_cutoff; +#else // BINDLESS + let alpha_cutoff = pbr_bindings::material.alpha_cutoff; +#endif // BINDLESS + if output_color.a < alpha_cutoff { discard; } } else if (alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || diff --git a/crates/bevy_render/macros/src/as_bind_group.rs b/crates/bevy_render/macros/src/as_bind_group.rs index 0fceb795bd7ee..a31db478fba8f 100644 --- a/crates/bevy_render/macros/src/as_bind_group.rs +++ b/crates/bevy_render/macros/src/as_bind_group.rs @@ -443,7 +443,8 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { if render_device.features().contains( #render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY | #render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY - ) && render_device.limits().max_storage_buffers_per_shader_stage > 0 { + ) && render_device.limits().max_storage_buffers_per_shader_stage > 0 && + !force_no_bindless { ( #render_path::render_resource::BufferBindingType::Storage { read_only: true }, #render_path::render_resource::BufferUsages::STORAGE, @@ -571,7 +572,8 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { let #actual_bindless_slot_count = if render_device.features().contains( #render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY | #render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY - ) && render_device.limits().max_storage_buffers_per_shader_stage > 0 { + ) && render_device.limits().max_storage_buffers_per_shader_stage > 0 && + !force_no_bindless { ::core::num::NonZeroU32::new(#bindless_count) } else { None @@ -607,6 +609,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { layout: &#render_path::render_resource::BindGroupLayout, render_device: &#render_path::renderer::RenderDevice, (images, fallback_image, storage_buffers): &mut #ecs_path::system::SystemParamItem<'_, '_, Self::Param>, + force_no_bindless: bool, ) -> Result<#render_path::render_resource::UnpreparedBindGroup, #render_path::render_resource::AsBindGroupError> { #uniform_binding_type_declarations @@ -618,7 +621,10 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { }) } - fn bind_group_layout_entries(render_device: &#render_path::renderer::RenderDevice) -> Vec<#render_path::render_resource::BindGroupLayoutEntry> { + fn bind_group_layout_entries( + render_device: &#render_path::renderer::RenderDevice, + force_no_bindless: bool + ) -> Vec<#render_path::render_resource::BindGroupLayoutEntry> { #actual_bindless_slot_count_declaration #uniform_binding_type_declarations diff --git a/crates/bevy_render/src/render_resource/bind_group.rs b/crates/bevy_render/src/render_resource/bind_group.rs index 99f1812319f74..1a42eb00415b9 100644 --- a/crates/bevy_render/src/render_resource/bind_group.rs +++ b/crates/bevy_render/src/render_resource/bind_group.rs @@ -352,7 +352,7 @@ pub trait AsBindGroup { param: &mut SystemParamItem<'_, '_, Self::Param>, ) -> Result, AsBindGroupError> { let UnpreparedBindGroup { bindings, data } = - Self::unprepared_bind_group(self, layout, render_device, param)?; + Self::unprepared_bind_group(self, layout, render_device, param, false)?; let entries = bindings .iter() @@ -372,30 +372,45 @@ pub trait AsBindGroup { } /// Returns a vec of (binding index, `OwnedBindingResource`). - /// In cases where `OwnedBindingResource` is not available (as for bindless texture arrays currently), - /// an implementor may return `AsBindGroupError::CreateBindGroupDirectly` - /// from this function and instead define `as_bind_group` directly. This may - /// prevent certain features, such as bindless mode, from working correctly. + /// + /// In cases where `OwnedBindingResource` is not available (as for bindless + /// texture arrays currently), an implementor may return + /// `AsBindGroupError::CreateBindGroupDirectly` from this function and + /// instead define `as_bind_group` directly. This may prevent certain + /// features, such as bindless mode, from working correctly. + /// + /// Set `force_no_bindless` to true to require that bindless textures *not* + /// be used. `ExtendedMaterial` uses this in order to ensure that the base + /// material doesn't use bindless mode if the extension doesn't. fn unprepared_bind_group( &self, layout: &BindGroupLayout, render_device: &RenderDevice, param: &mut SystemParamItem<'_, '_, Self::Param>, + force_no_bindless: bool, ) -> Result, AsBindGroupError>; - /// Creates the bind group layout matching all bind groups returned by [`AsBindGroup::as_bind_group`] + /// Creates the bind group layout matching all bind groups returned by + /// [`AsBindGroup::as_bind_group`] fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout where Self: Sized, { render_device.create_bind_group_layout( Self::label(), - &Self::bind_group_layout_entries(render_device), + &Self::bind_group_layout_entries(render_device, false), ) } /// Returns a vec of bind group layout entries - fn bind_group_layout_entries(render_device: &RenderDevice) -> Vec + /// + /// Set `force_no_bindless` to true to require that bindless textures *not* + /// be used. `ExtendedMaterial` uses this in order to ensure that the base + /// material doesn't use bindless mode if the extension doesn't. + fn bind_group_layout_entries( + render_device: &RenderDevice, + force_no_bindless: bool, + ) -> Vec where Self: Sized; } diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index 61cb6403e6e93..bf5dc3da9debe 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -144,6 +144,7 @@ impl AsBindGroup for BindlessMaterial { _layout: &BindGroupLayout, _render_device: &RenderDevice, _param: &mut SystemParamItem<'_, '_, Self::Param>, + _force_no_bindless: bool, ) -> Result, AsBindGroupError> { // We implement `as_bind_group`` directly because bindless texture // arrays can't be owned. @@ -152,7 +153,7 @@ impl AsBindGroup for BindlessMaterial { Err(AsBindGroupError::CreateBindGroupDirectly) } - fn bind_group_layout_entries(_: &RenderDevice) -> Vec + fn bind_group_layout_entries(_: &RenderDevice, _: bool) -> Vec where Self: Sized, {