forked from bevyengine/bevy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Variable
MeshPipeline
View Bind Group Layout (bevyengine#10156)
# Objective This PR aims to make it so that we don't accidentally go over `MAX_TEXTURE_IMAGE_UNITS` (in WebGL) or `maxSampledTexturesPerShaderStage` (in WebGPU), giving us some extra leeway to add more view bind group textures. (This PR is extracted from—and unblocks—bevyengine#8015) ## Solution - We replace the existing `view_layout` and `view_layout_multisampled` pair with an array of 32 bind group layouts, generated ahead of time; - For now, these layouts cover all the possible combinations of: `multisampled`, `depth_prepass`, `normal_prepass`, `motion_vector_prepass` and `deferred_prepass`: - In the future, as @JMS55 pointed out, we can likely take out `motion_vector_prepass` and `deferred_prepass`, as these are not really needed for the mesh pipeline and can use separate pipelines. This would bring the possible combinations down to 8; - We can also add more "optional" textures as they become needed, allowing the engine to scale to a wider variety of use cases in lower end/web environments (e.g. some apps might just want normal and depth prepasses, others might only want light probes), while still keeping a high ceiling for high end native environments where more textures are supported. - While preallocating bind group layouts is relatively cheap, the number of combinations grows exponentially, so we should likely limit ourselves to something like at most 256–1024 total layouts until we find a better solution (like generating them lazily) - To make this mechanism a little bit more explicit/discoverable, so that compatibility with WebGPU/WebGL is not broken by accident, we add a `MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES` const and warn whenever the number of textures in the layout crosses it. - The warning is gated by `#[cfg(debug_assertions)]` and not issued in release builds; - We're counting the actual textures in the bind group layout instead of using some roundabout metric so it should be accurate; - Right now `MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES` is set to 10 in order to leave 6 textures free for other groups; - Currently there's no combination that would cause us to go over the limit, but that will change once bevyengine#8015 lands. --- ## Changelog - `MeshPipeline` view bind group layouts now vary based on the current multisampling and prepass states, saving a couple of texture binding entries when prepasses are not in use. ## Migration Guide - `MeshPipeline::view_layout` and `MeshPipeline::view_layout_multisampled` have been replaced with a private array to accomodate for variable view bind group layouts. To obtain a view bind group layout for the current pipeline state, use the new `MeshPipeline::get_view_layout()` or `MeshPipeline::get_view_layout_from_key()` methods.
- Loading branch information
Showing
9 changed files
with
760 additions
and
515 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
doc-valid-idents = ["sRGB", "NaN", "iOS", "glTF", "GitHub", "WebGPU", "GilRs"] | ||
doc-valid-idents = ["sRGB", "NaN", "iOS", "glTF", "GitHub", "WebGL", "WebGPU", "GilRs"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
use bevy_core_pipeline::prepass::ViewPrepassTextures; | ||
use bevy_render::render_resource::{ | ||
BindGroupEntry, BindGroupLayoutEntry, BindingResource, BindingType, ShaderStages, | ||
TextureAspect, TextureSampleType, TextureView, TextureViewDescriptor, TextureViewDimension, | ||
}; | ||
use bevy_utils::default; | ||
use smallvec::SmallVec; | ||
|
||
use crate::MeshPipelineViewLayoutKey; | ||
|
||
pub fn get_bind_group_layout_entries( | ||
bindings: [u32; 4], | ||
layout_key: MeshPipelineViewLayoutKey, | ||
) -> SmallVec<[BindGroupLayoutEntry; 4]> { | ||
let mut result = SmallVec::<[BindGroupLayoutEntry; 4]>::new(); | ||
|
||
let multisampled = layout_key.contains(MeshPipelineViewLayoutKey::MULTISAMPLED); | ||
|
||
if layout_key.contains(MeshPipelineViewLayoutKey::DEPTH_PREPASS) { | ||
result.push( | ||
// Depth texture | ||
BindGroupLayoutEntry { | ||
binding: bindings[0], | ||
visibility: ShaderStages::FRAGMENT, | ||
ty: BindingType::Texture { | ||
multisampled, | ||
sample_type: TextureSampleType::Depth, | ||
view_dimension: TextureViewDimension::D2, | ||
}, | ||
count: None, | ||
}, | ||
); | ||
} | ||
|
||
if layout_key.contains(MeshPipelineViewLayoutKey::NORMAL_PREPASS) { | ||
result.push( | ||
// Normal texture | ||
BindGroupLayoutEntry { | ||
binding: bindings[1], | ||
visibility: ShaderStages::FRAGMENT, | ||
ty: BindingType::Texture { | ||
multisampled, | ||
sample_type: TextureSampleType::Float { filterable: false }, | ||
view_dimension: TextureViewDimension::D2, | ||
}, | ||
count: None, | ||
}, | ||
); | ||
} | ||
|
||
if layout_key.contains(MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS) { | ||
result.push( | ||
// Motion Vectors texture | ||
BindGroupLayoutEntry { | ||
binding: bindings[2], | ||
visibility: ShaderStages::FRAGMENT, | ||
ty: BindingType::Texture { | ||
multisampled, | ||
sample_type: TextureSampleType::Float { filterable: false }, | ||
view_dimension: TextureViewDimension::D2, | ||
}, | ||
count: None, | ||
}, | ||
); | ||
} | ||
|
||
if layout_key.contains(MeshPipelineViewLayoutKey::DEFERRED_PREPASS) { | ||
result.push( | ||
// Deferred texture | ||
BindGroupLayoutEntry { | ||
binding: bindings[3], | ||
visibility: ShaderStages::FRAGMENT, | ||
ty: BindingType::Texture { | ||
multisampled: false, | ||
sample_type: TextureSampleType::Uint, | ||
view_dimension: TextureViewDimension::D2, | ||
}, | ||
count: None, | ||
}, | ||
); | ||
} | ||
|
||
result | ||
} | ||
|
||
// Needed so the texture views can live long enough. | ||
pub struct PrepassBindingsSet { | ||
depth_view: Option<TextureView>, | ||
normal_view: Option<TextureView>, | ||
motion_vectors_view: Option<TextureView>, | ||
deferred_view: Option<TextureView>, | ||
} | ||
|
||
impl PrepassBindingsSet { | ||
pub fn get_entries(&self, bindings: [u32; 4]) -> SmallVec<[BindGroupEntry; 4]> { | ||
let mut result = SmallVec::<[BindGroupEntry; 4]>::new(); | ||
|
||
if let Some(ref depth_view) = self.depth_view { | ||
result.push(BindGroupEntry { | ||
binding: bindings[0], | ||
resource: BindingResource::TextureView(depth_view), | ||
}); | ||
} | ||
|
||
if let Some(ref normal_view) = self.normal_view { | ||
result.push(BindGroupEntry { | ||
binding: bindings[1], | ||
resource: BindingResource::TextureView(normal_view), | ||
}); | ||
} | ||
|
||
if let Some(ref motion_vectors_view) = self.motion_vectors_view { | ||
result.push(BindGroupEntry { | ||
binding: bindings[2], | ||
resource: BindingResource::TextureView(motion_vectors_view), | ||
}); | ||
} | ||
|
||
if let Some(ref deferred_view) = self.deferred_view { | ||
result.push(BindGroupEntry { | ||
binding: bindings[3], | ||
resource: BindingResource::TextureView(deferred_view), | ||
}); | ||
} | ||
|
||
result | ||
} | ||
} | ||
|
||
pub fn get_bindings(prepass_textures: Option<&ViewPrepassTextures>) -> PrepassBindingsSet { | ||
let depth_desc = TextureViewDescriptor { | ||
label: Some("prepass_depth"), | ||
aspect: TextureAspect::DepthOnly, | ||
..default() | ||
}; | ||
let depth_view = prepass_textures | ||
.and_then(|x| x.depth.as_ref()) | ||
.map(|texture| texture.texture.create_view(&depth_desc)); | ||
|
||
let normal_view = prepass_textures | ||
.and_then(|x| x.normal.as_ref()) | ||
.map(|texture| texture.default_view.clone()); | ||
|
||
let motion_vectors_view = prepass_textures | ||
.and_then(|x| x.motion_vectors.as_ref()) | ||
.map(|texture| texture.default_view.clone()); | ||
|
||
let deferred_view = prepass_textures | ||
.and_then(|x| x.deferred.as_ref()) | ||
.map(|texture| texture.default_view.clone()); | ||
|
||
PrepassBindingsSet { | ||
depth_view, | ||
normal_view, | ||
motion_vectors_view, | ||
deferred_view, | ||
} | ||
} |
Oops, something went wrong.