Skip to content

Commit a4cfc24

Browse files
authored
make material draw functions instance independent (#21021)
# Objective #19667 introduced a type-erased material system that effectively puts all material instances through a single cache: during asset preparation (`ErasedRenderAssetPlugin`), materials are processed into a set of `MaterialProperties` that contains all the data needed to render them, and these are all cached and deduplicated as needed. This allows for maximal flexibility (every single material instance could have a different ""type"") but complicates the logic and makes cache keys really big. So, one goal for the material revamp I have (and I think @tychedelia is on board) is to cache materials at two levels: material "types" and material "instances", where material types roughly map to the rust types that currently implement `Material`. Without getting into implementation details, storing mostly static data separate from instance data would let us simplify a lot of the logic, while only requiring a little more work for fully-dynamic use cases. This PR is a first step in that direction, which stores *all* the available draw functions in `MaterialProperties`, and pushes the decision for "what draw function should I use for this material?" to queue time, where before it was split between there and asset preparation. This makes the list of available draw functions instance-independent, and will later allow us to store it with other "static" material data. ## Solution - Make draw function labels 1:1 with render phases, and include all of them in the list in `MaterialProperties` ## Testing - Ran `3d_scene` - Ran `manual_material`
1 parent ffd0121 commit a4cfc24

File tree

4 files changed

+128
-70
lines changed

4 files changed

+128
-70
lines changed

crates/bevy_pbr/src/material.rs

Lines changed: 66 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,15 +1214,17 @@ pub fn queue_material_meshes(
12141214

12151215
// Fetch the slabs that this mesh resides in.
12161216
let (vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1217-
let Some(draw_function) = material.properties.get_draw_function(MaterialDrawFunction)
1218-
else {
1219-
continue;
1220-
};
12211217

12221218
match material.properties.render_phase_type {
12231219
RenderPhaseType::Transmissive => {
12241220
let distance = rangefinder.distance_translation(&mesh_instance.translation)
12251221
+ material.properties.depth_bias;
1222+
let Some(draw_function) = material
1223+
.properties
1224+
.get_draw_function(MainPassTransmissiveDrawFunction)
1225+
else {
1226+
continue;
1227+
};
12261228
transmissive_phase.add(Transmissive3d {
12271229
entity: (*render_entity, *visible_entity),
12281230
draw_function,
@@ -1242,6 +1244,12 @@ pub fn queue_material_meshes(
12421244
opaque_phase.update_cache(*visible_entity, None, current_change_tick);
12431245
continue;
12441246
}
1247+
let Some(draw_function) = material
1248+
.properties
1249+
.get_draw_function(MainPassOpaqueDrawFunction)
1250+
else {
1251+
continue;
1252+
};
12451253
let batch_set_key = Opaque3dBatchSetKey {
12461254
pipeline: pipeline_id,
12471255
draw_function,
@@ -1267,6 +1275,12 @@ pub fn queue_material_meshes(
12671275
}
12681276
// Alpha mask
12691277
RenderPhaseType::AlphaMask => {
1278+
let Some(draw_function) = material
1279+
.properties
1280+
.get_draw_function(MainPassAlphaMaskDrawFunction)
1281+
else {
1282+
continue;
1283+
};
12701284
let batch_set_key = OpaqueNoLightmap3dBatchSetKey {
12711285
draw_function,
12721286
pipeline: pipeline_id,
@@ -1292,6 +1306,12 @@ pub fn queue_material_meshes(
12921306
RenderPhaseType::Transparent => {
12931307
let distance = rangefinder.distance_translation(&mesh_instance.translation)
12941308
+ material.properties.depth_bias;
1309+
let Some(draw_function) = material
1310+
.properties
1311+
.get_draw_function(MainPassTransparentDrawFunction)
1312+
else {
1313+
continue;
1314+
};
12951315
transparent_phase.add(Transparent3d {
12961316
entity: (*render_entity, *visible_entity),
12971317
draw_function,
@@ -1385,13 +1405,23 @@ pub struct MeshletPrepassFragmentShader;
13851405
pub struct MeshletDeferredFragmentShader;
13861406

13871407
#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1388-
pub struct MaterialDrawFunction;
1408+
pub struct MainPassOpaqueDrawFunction;
1409+
#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1410+
pub struct MainPassAlphaMaskDrawFunction;
1411+
#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1412+
pub struct MainPassTransmissiveDrawFunction;
1413+
#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1414+
pub struct MainPassTransparentDrawFunction;
13891415

13901416
#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1391-
pub struct PrepassDrawFunction;
1417+
pub struct PrepassOpaqueDrawFunction;
1418+
#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1419+
pub struct PrepassAlphaMaskDrawFunction;
13921420

13931421
#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1394-
pub struct DeferredDrawFunction;
1422+
pub struct DeferredOpaqueDrawFunction;
1423+
#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1424+
pub struct DeferredAlphaMaskDrawFunction;
13951425

13961426
#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
13971427
pub struct ShadowsDrawFunction;
@@ -1633,17 +1663,37 @@ where
16331663
let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial>();
16341664
let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial>();
16351665
let draw_transparent_pbr = transparent_draw_functions.read().id::<DrawMaterial>();
1636-
let draw_opaque_prepass = opaque_prepass_draw_functions.read().get_id::<DrawPrepass>();
1637-
let draw_alpha_mask_prepass = alpha_mask_prepass_draw_functions
1638-
.read()
1639-
.get_id::<DrawPrepass>();
1640-
let draw_opaque_deferred = opaque_deferred_draw_functions
1641-
.read()
1642-
.get_id::<DrawPrepass>();
1666+
let draw_opaque_prepass = opaque_prepass_draw_functions.read().id::<DrawPrepass>();
1667+
let draw_alpha_mask_prepass = alpha_mask_prepass_draw_functions.read().id::<DrawPrepass>();
1668+
let draw_opaque_deferred = opaque_deferred_draw_functions.read().id::<DrawPrepass>();
16431669
let draw_alpha_mask_deferred = alpha_mask_deferred_draw_functions
16441670
.read()
1645-
.get_id::<DrawPrepass>();
1646-
let shadow_draw_function_id = shadow_draw_functions.read().get_id::<DrawPrepass>();
1671+
.id::<DrawPrepass>();
1672+
let draw_shadows = shadow_draw_functions.read().id::<DrawPrepass>();
1673+
1674+
let draw_functions = SmallVec::from_iter([
1675+
(MainPassOpaqueDrawFunction.intern(), draw_opaque_pbr),
1676+
(MainPassAlphaMaskDrawFunction.intern(), draw_alpha_mask_pbr),
1677+
(
1678+
MainPassTransmissiveDrawFunction.intern(),
1679+
draw_transmissive_pbr,
1680+
),
1681+
(
1682+
MainPassTransparentDrawFunction.intern(),
1683+
draw_transparent_pbr,
1684+
),
1685+
(PrepassOpaqueDrawFunction.intern(), draw_opaque_prepass),
1686+
(
1687+
PrepassAlphaMaskDrawFunction.intern(),
1688+
draw_alpha_mask_prepass,
1689+
),
1690+
(DeferredOpaqueDrawFunction.intern(), draw_opaque_deferred),
1691+
(
1692+
DeferredAlphaMaskDrawFunction.intern(),
1693+
draw_alpha_mask_deferred,
1694+
),
1695+
(ShadowsDrawFunction.intern(), draw_shadows),
1696+
]);
16471697

16481698
let render_method = match material.opaque_render_method() {
16491699
OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
@@ -1669,35 +1719,6 @@ where
16691719
AlphaMode::Mask(_) => RenderPhaseType::AlphaMask,
16701720
};
16711721

1672-
let draw_function_id = match render_phase_type {
1673-
RenderPhaseType::Opaque => draw_opaque_pbr,
1674-
RenderPhaseType::AlphaMask => draw_alpha_mask_pbr,
1675-
RenderPhaseType::Transmissive => draw_transmissive_pbr,
1676-
RenderPhaseType::Transparent => draw_transparent_pbr,
1677-
};
1678-
let prepass_draw_function_id = match render_phase_type {
1679-
RenderPhaseType::Opaque => draw_opaque_prepass,
1680-
RenderPhaseType::AlphaMask => draw_alpha_mask_prepass,
1681-
_ => None,
1682-
};
1683-
let deferred_draw_function_id = match render_phase_type {
1684-
RenderPhaseType::Opaque => draw_opaque_deferred,
1685-
RenderPhaseType::AlphaMask => draw_alpha_mask_deferred,
1686-
_ => None,
1687-
};
1688-
1689-
let mut draw_functions = SmallVec::new();
1690-
draw_functions.push((MaterialDrawFunction.intern(), draw_function_id));
1691-
if let Some(prepass_draw_function_id) = prepass_draw_function_id {
1692-
draw_functions.push((PrepassDrawFunction.intern(), prepass_draw_function_id));
1693-
}
1694-
if let Some(deferred_draw_function_id) = deferred_draw_function_id {
1695-
draw_functions.push((DeferredDrawFunction.intern(), deferred_draw_function_id));
1696-
}
1697-
if let Some(shadow_draw_function_id) = shadow_draw_function_id {
1698-
draw_functions.push((ShadowsDrawFunction.intern(), shadow_draw_function_id));
1699-
}
1700-
17011722
let mut shaders = SmallVec::new();
17021723
let mut add_shader = |label: InternedShaderLabel, shader_ref: ShaderRef| {
17031724
let mayber_shader = match shader_ref {

crates/bevy_pbr/src/prepass/mod.rs

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ mod prepass_bindings;
33
use crate::{
44
alpha_mode_pipeline_key, binding_arrays_are_usable, buffer_layout,
55
collect_meshes_for_gpu_building, init_material_pipeline, set_mesh_motion_vector_flags,
6-
setup_morph_and_skinning_defs, skin, DeferredDrawFunction, DeferredFragmentShader,
7-
DeferredVertexShader, DrawMesh, EntitySpecializationTicks, ErasedMaterialPipelineKey,
8-
MaterialPipeline, MaterialProperties, MeshLayouts, MeshPipeline, MeshPipelineKey,
9-
OpaqueRendererMethod, PreparedMaterial, PrepassDrawFunction, PrepassFragmentShader,
10-
PrepassVertexShader, RenderLightmaps, RenderMaterialInstances, RenderMeshInstanceFlags,
11-
RenderMeshInstances, RenderPhaseType, SetMaterialBindGroup, SetMeshBindGroup, ShadowView,
6+
setup_morph_and_skinning_defs, skin, DeferredAlphaMaskDrawFunction, DeferredFragmentShader,
7+
DeferredOpaqueDrawFunction, DeferredVertexShader, DrawMesh, EntitySpecializationTicks,
8+
ErasedMaterialPipelineKey, MaterialPipeline, MaterialProperties, MeshLayouts, MeshPipeline,
9+
MeshPipelineKey, OpaqueRendererMethod, PreparedMaterial, PrepassAlphaMaskDrawFunction,
10+
PrepassFragmentShader, PrepassOpaqueDrawFunction, PrepassVertexShader, RenderLightmaps,
11+
RenderMaterialInstances, RenderMeshInstanceFlags, RenderMeshInstances, RenderPhaseType,
12+
SetMaterialBindGroup, SetMeshBindGroup, ShadowView,
1213
};
1314
use bevy_app::{App, Plugin, PreUpdate};
1415
use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
@@ -1087,12 +1088,15 @@ pub fn queue_prepass_material_meshes(
10871088
match material.properties.render_phase_type {
10881089
RenderPhaseType::Opaque => {
10891090
if deferred {
1091+
let Some(draw_function) = material
1092+
.properties
1093+
.get_draw_function(DeferredOpaqueDrawFunction)
1094+
else {
1095+
continue;
1096+
};
10901097
opaque_deferred_phase.as_mut().unwrap().add(
10911098
OpaqueNoLightmap3dBatchSetKey {
1092-
draw_function: material
1093-
.properties
1094-
.get_draw_function(DeferredDrawFunction)
1095-
.unwrap(),
1099+
draw_function,
10961100
pipeline: *pipeline_id,
10971101
material_bind_group_index: Some(material.binding.group.0),
10981102
vertex_slab: vertex_slab.unwrap_or_default(),
@@ -1112,12 +1116,15 @@ pub fn queue_prepass_material_meshes(
11121116
} else if let Some(opaque_phase) = opaque_phase.as_mut() {
11131117
let (vertex_slab, index_slab) =
11141118
mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1119+
let Some(draw_function) = material
1120+
.properties
1121+
.get_draw_function(PrepassOpaqueDrawFunction)
1122+
else {
1123+
continue;
1124+
};
11151125
opaque_phase.add(
11161126
OpaqueNoLightmap3dBatchSetKey {
1117-
draw_function: material
1118-
.properties
1119-
.get_draw_function(PrepassDrawFunction)
1120-
.unwrap(),
1127+
draw_function,
11211128
pipeline: *pipeline_id,
11221129
material_bind_group_index: Some(material.binding.group.0),
11231130
vertex_slab: vertex_slab.unwrap_or_default(),
@@ -1140,11 +1147,14 @@ pub fn queue_prepass_material_meshes(
11401147
if deferred {
11411148
let (vertex_slab, index_slab) =
11421149
mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1150+
let Some(draw_function) = material
1151+
.properties
1152+
.get_draw_function(DeferredAlphaMaskDrawFunction)
1153+
else {
1154+
continue;
1155+
};
11431156
let batch_set_key = OpaqueNoLightmap3dBatchSetKey {
1144-
draw_function: material
1145-
.properties
1146-
.get_draw_function(DeferredDrawFunction)
1147-
.unwrap(),
1157+
draw_function,
11481158
pipeline: *pipeline_id,
11491159
material_bind_group_index: Some(material.binding.group.0),
11501160
vertex_slab: vertex_slab.unwrap_or_default(),
@@ -1167,11 +1177,14 @@ pub fn queue_prepass_material_meshes(
11671177
} else if let Some(alpha_mask_phase) = alpha_mask_phase.as_mut() {
11681178
let (vertex_slab, index_slab) =
11691179
mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1180+
let Some(draw_function) = material
1181+
.properties
1182+
.get_draw_function(PrepassAlphaMaskDrawFunction)
1183+
else {
1184+
continue;
1185+
};
11701186
let batch_set_key = OpaqueNoLightmap3dBatchSetKey {
1171-
draw_function: material
1172-
.properties
1173-
.get_draw_function(PrepassDrawFunction)
1174-
.unwrap(),
1187+
draw_function,
11751188
pipeline: *pipeline_id,
11761189
material_bind_group_index: Some(material.binding.group.0),
11771190
vertex_slab: vertex_slab.unwrap_or_default(),

examples/shader_advanced/manual_material.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use bevy::{
99
},
1010
pbr::{
1111
late_sweep_material_instances, DrawMaterial, EntitiesNeedingSpecialization,
12-
EntitySpecializationTickPair, EntitySpecializationTicks, MaterialBindGroupAllocator,
13-
MaterialBindGroupAllocators, MaterialDrawFunction,
12+
EntitySpecializationTickPair, EntitySpecializationTicks, MainPassOpaqueDrawFunction,
13+
MaterialBindGroupAllocator, MaterialBindGroupAllocators,
1414
MaterialExtractEntitiesNeedingSpecializationSystems, MaterialExtractionSystems,
1515
MaterialFragmentShader, MaterialProperties, PreparedMaterial, RenderMaterialBindings,
1616
RenderMaterialInstance, RenderMaterialInstances, SpecializedMaterialPipelineCache,
@@ -200,7 +200,7 @@ impl ErasedRenderAsset for ImageMaterial {
200200
material_layout: Some(material_layout),
201201
..Default::default()
202202
};
203-
properties.add_draw_function(MaterialDrawFunction, draw_function_id);
203+
properties.add_draw_function(MainPassOpaqueDrawFunction, draw_function_id);
204204
properties.add_shader(MaterialFragmentShader, asset_server.load(SHADER_ASSET_PATH));
205205

206206
Ok(PreparedMaterial {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
title: "Per-RenderPhase Draw Functions"
3+
pull_requests: [21021]
4+
---
5+
6+
This PR makes draw function labels in `MaterialProperties` per-`RenderPhase` instead
7+
of per-pass. This should only affect users of the low-level "manual Material" API,
8+
and not users of the broader Material API. Specifying all draw functions is not
9+
mandatory, but users should specify draw functions for all render phases the
10+
material may queue to, or the material may not render.
11+
12+
- Removed `MaterialDrawFunction` in favor of:
13+
- `MainPassOpaqueDrawFunction`
14+
- `MainPassAlphaMaskDrawFunction`
15+
- `MainPassTransmissiveDrawFunction`
16+
- `MainPassTransparentDrawFunction`
17+
18+
- Removed `PrepassDrawFunction` in favor of:
19+
- `PrepassOpaqueDrawFunction`
20+
- `PrepassAlphaMaskDrawFunction`
21+
22+
- Removed `DeferredDrawFunction` in favor of:
23+
- `DeferredOpaqueDrawFunction`
24+
- `DeferredAlphaMaskDrawFunction`

0 commit comments

Comments
 (0)