#[cfg(feature = "meshlet")]
use crate::meshlet::{
prepare_material_meshlet_meshes_main_opaque_pass, queue_material_meshlet_meshes,
MeshletGpuScene,
};
use crate::*;
use bevy_asset::{Asset, AssetId, AssetServer};
use bevy_core_pipeline::{
core_3d::{
AlphaMask3d, Camera3d, Opaque3d, Opaque3dBinKey, ScreenSpaceTransmissionQuality,
Transmissive3d, Transparent3d,
},
prepass::{
DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, OpaqueNoLightmap3dBinKey,
},
tonemapping::{DebandDither, Tonemapping},
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
prelude::*,
system::{lifetimeless::SRes, SystemParamItem},
};
use bevy_reflect::Reflect;
use bevy_render::{
camera::TemporalJitter,
extract_instances::{ExtractInstancesPlugin, ExtractedInstances},
extract_resource::ExtractResource,
mesh::{GpuMesh, MeshVertexBufferLayoutRef},
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
render_phase::*,
render_resource::*,
renderer::RenderDevice,
texture::FallbackImage,
view::{ExtractedView, Msaa, RenderVisibilityRanges, VisibleEntities, WithMesh},
};
use bevy_utils::tracing::error;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicU32, Ordering};
use std::{hash::Hash, num::NonZeroU32};
use self::{irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight};
pub trait Material: Asset + AsBindGroup + Clone + Sized {
fn vertex_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
fn fragment_shader() -> ShaderRef {
ShaderRef::Default
}
#[inline]
fn alpha_mode(&self) -> AlphaMode {
AlphaMode::Opaque
}
#[inline]
fn opaque_render_method(&self) -> OpaqueRendererMethod {
OpaqueRendererMethod::Forward
}
#[inline]
fn depth_bias(&self) -> f32 {
0.0
}
#[inline]
fn reads_view_transmission_texture(&self) -> bool {
false
}
fn prepass_vertex_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
fn prepass_fragment_shader() -> ShaderRef {
ShaderRef::Default
}
fn deferred_vertex_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
fn deferred_fragment_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
#[cfg(feature = "meshlet")]
fn meshlet_mesh_fragment_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
#[cfg(feature = "meshlet")]
fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
#[cfg(feature = "meshlet")]
fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
#[inline]
fn specialize(
pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayoutRef,
key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> {
Ok(())
}
}
pub struct MaterialPlugin<M: Material> {
pub prepass_enabled: bool,
pub shadows_enabled: bool,
pub _marker: PhantomData<M>,
}
impl<M: Material> Default for MaterialPlugin<M> {
fn default() -> Self {
Self {
prepass_enabled: true,
shadows_enabled: true,
_marker: Default::default(),
}
}
}
impl<M: Material> Plugin for MaterialPlugin<M>
where
M::Data: PartialEq + Eq + Hash + Clone,
{
fn build(&self, app: &mut App) {
app.init_asset::<M>().add_plugins((
ExtractInstancesPlugin::<AssetId<M>>::extract_visible(),
RenderAssetPlugin::<PreparedMaterial<M>>::default(),
));
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<DrawFunctions<Shadow>>()
.add_render_command::<Shadow, DrawPrepass<M>>()
.add_render_command::<Transmissive3d, DrawMaterial<M>>()
.add_render_command::<Transparent3d, DrawMaterial<M>>()
.add_render_command::<Opaque3d, DrawMaterial<M>>()
.add_render_command::<AlphaMask3d, DrawMaterial<M>>()
.init_resource::<SpecializedMeshPipelines<MaterialPipeline<M>>>()
.add_systems(
Render,
queue_material_meshes::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_assets::<PreparedMaterial<M>>),
);
if self.shadows_enabled {
render_app.add_systems(
Render,
queue_shadows::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_assets::<PreparedMaterial<M>>),
);
}
#[cfg(feature = "meshlet")]
render_app.add_systems(
Render,
queue_material_meshlet_meshes::<M>
.in_set(RenderSet::QueueMeshes)
.run_if(resource_exists::<MeshletGpuScene>),
);
#[cfg(feature = "meshlet")]
render_app.add_systems(
Render,
prepare_material_meshlet_meshes_main_opaque_pass::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_assets::<PreparedMaterial<M>>)
.before(queue_material_meshlet_meshes::<M>)
.run_if(resource_exists::<MeshletGpuScene>),
);
}
if self.shadows_enabled || self.prepass_enabled {
app.add_plugins(PrepassPipelinePlugin::<M>::default());
}
if self.prepass_enabled {
app.add_plugins(PrepassPlugin::<M>::default());
}
}
fn finish(&self, app: &mut App) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<MaterialPipeline<M>>();
}
}
}
pub struct MaterialPipelineKey<M: Material> {
pub mesh_key: MeshPipelineKey,
pub bind_group_data: M::Data,
}
impl<M: Material> Eq for MaterialPipelineKey<M> where M::Data: PartialEq {}
impl<M: Material> PartialEq for MaterialPipelineKey<M>
where
M::Data: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.mesh_key == other.mesh_key && self.bind_group_data == other.bind_group_data
}
}
impl<M: Material> Clone for MaterialPipelineKey<M>
where
M::Data: Clone,
{
fn clone(&self) -> Self {
Self {
mesh_key: self.mesh_key,
bind_group_data: self.bind_group_data.clone(),
}
}
}
impl<M: Material> Hash for MaterialPipelineKey<M>
where
M::Data: Hash,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.mesh_key.hash(state);
self.bind_group_data.hash(state);
}
}
#[derive(Resource)]
pub struct MaterialPipeline<M: Material> {
pub mesh_pipeline: MeshPipeline,
pub material_layout: BindGroupLayout,
pub vertex_shader: Option<Handle<Shader>>,
pub fragment_shader: Option<Handle<Shader>>,
pub marker: PhantomData<M>,
}
impl<M: Material> Clone for MaterialPipeline<M> {
fn clone(&self) -> Self {
Self {
mesh_pipeline: self.mesh_pipeline.clone(),
material_layout: self.material_layout.clone(),
vertex_shader: self.vertex_shader.clone(),
fragment_shader: self.fragment_shader.clone(),
marker: PhantomData,
}
}
}
impl<M: Material> SpecializedMeshPipeline for MaterialPipeline<M>
where
M::Data: PartialEq + Eq + Hash + Clone,
{
type Key = MaterialPipelineKey<M>;
fn specialize(
&self,
key: Self::Key,
layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?;
if let Some(vertex_shader) = &self.vertex_shader {
descriptor.vertex.shader = vertex_shader.clone();
}
if let Some(fragment_shader) = &self.fragment_shader {
descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone();
}
descriptor.layout.insert(2, self.material_layout.clone());
M::specialize(self, &mut descriptor, layout, key)?;
Ok(descriptor)
}
}
impl<M: Material> FromWorld for MaterialPipeline<M> {
fn from_world(world: &mut World) -> Self {
let asset_server = world.resource::<AssetServer>();
let render_device = world.resource::<RenderDevice>();
MaterialPipeline {
mesh_pipeline: world.resource::<MeshPipeline>().clone(),
material_layout: M::bind_group_layout(render_device),
vertex_shader: match M::vertex_shader() {
ShaderRef::Default => None,
ShaderRef::Handle(handle) => Some(handle),
ShaderRef::Path(path) => Some(asset_server.load(path)),
},
fragment_shader: match M::fragment_shader() {
ShaderRef::Default => None,
ShaderRef::Handle(handle) => Some(handle),
ShaderRef::Path(path) => Some(asset_server.load(path)),
},
marker: PhantomData,
}
}
}
type DrawMaterial<M> = (
SetItemPipeline,
SetMeshViewBindGroup<0>,
SetMeshBindGroup<1>,
SetMaterialBindGroup<M, 2>,
DrawMesh,
);
pub struct SetMaterialBindGroup<M: Material, const I: usize>(PhantomData<M>);
impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterialBindGroup<M, I> {
type Param = (
SRes<RenderAssets<PreparedMaterial<M>>>,
SRes<RenderMaterialInstances<M>>,
);
type ViewQuery = ();
type ItemQuery = ();
#[inline]
fn render<'w>(
item: &P,
_view: (),
_item_query: Option<()>,
(materials, material_instances): SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let materials = materials.into_inner();
let material_instances = material_instances.into_inner();
let Some(material_asset_id) = material_instances.get(&item.entity()) else {
return RenderCommandResult::Failure;
};
let Some(material) = materials.get(*material_asset_id) else {
return RenderCommandResult::Failure;
};
pass.set_bind_group(I, &material.bind_group, &[]);
RenderCommandResult::Success
}
}
pub type RenderMaterialInstances<M> = ExtractedInstances<AssetId<M>>;
pub const fn alpha_mode_pipeline_key(alpha_mode: AlphaMode, msaa: &Msaa) -> MeshPipelineKey {
match alpha_mode {
AlphaMode::Premultiplied | AlphaMode::Add => MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA,
AlphaMode::Blend => MeshPipelineKey::BLEND_ALPHA,
AlphaMode::Multiply => MeshPipelineKey::BLEND_MULTIPLY,
AlphaMode::Mask(_) => MeshPipelineKey::MAY_DISCARD,
AlphaMode::AlphaToCoverage => match *msaa {
Msaa::Off => MeshPipelineKey::MAY_DISCARD,
_ => MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE,
},
_ => MeshPipelineKey::NONE,
}
}
pub const fn tonemapping_pipeline_key(tonemapping: Tonemapping) -> MeshPipelineKey {
match tonemapping {
Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE,
Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
Tonemapping::ReinhardLuminance => MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE,
Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
Tonemapping::SomewhatBoringDisplayTransform => {
MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
}
Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
}
}
pub const fn screen_space_specular_transmission_pipeline_key(
screen_space_transmissive_blur_quality: ScreenSpaceTransmissionQuality,
) -> MeshPipelineKey {
match screen_space_transmissive_blur_quality {
ScreenSpaceTransmissionQuality::Low => {
MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW
}
ScreenSpaceTransmissionQuality::Medium => {
MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM
}
ScreenSpaceTransmissionQuality::High => {
MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH
}
ScreenSpaceTransmissionQuality::Ultra => {
MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn queue_material_meshes<M: Material>(
(
opaque_draw_functions,
alpha_mask_draw_functions,
transmissive_draw_functions,
transparent_draw_functions,
): (
Res<DrawFunctions<Opaque3d>>,
Res<DrawFunctions<AlphaMask3d>>,
Res<DrawFunctions<Transmissive3d>>,
Res<DrawFunctions<Transparent3d>>,
),
material_pipeline: Res<MaterialPipeline<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<GpuMesh>>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
render_mesh_instances: Res<RenderMeshInstances>,
render_material_instances: Res<RenderMaterialInstances<M>>,
render_lightmaps: Res<RenderLightmaps>,
render_visibility_ranges: Res<RenderVisibilityRanges>,
mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
mut alpha_mask_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
mut transmissive_render_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
mut views: Query<(
Entity,
&ExtractedView,
&VisibleEntities,
Option<&Tonemapping>,
Option<&DebandDither>,
Option<&ShadowFilteringMethod>,
Has<ScreenSpaceAmbientOcclusionSettings>,
(
Has<NormalPrepass>,
Has<DepthPrepass>,
Has<MotionVectorPrepass>,
Has<DeferredPrepass>,
),
Option<&Camera3d>,
Has<TemporalJitter>,
Option<&Projection>,
(
Has<RenderViewLightProbes<EnvironmentMapLight>>,
Has<RenderViewLightProbes<IrradianceVolume>>,
),
)>,
) where
M::Data: PartialEq + Eq + Hash + Clone,
{
for (
view_entity,
view,
visible_entities,
tonemapping,
dither,
shadow_filter_method,
ssao,
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
camera_3d,
temporal_jitter,
projection,
(has_environment_maps, has_irradiance_volumes),
) in &mut views
{
let (
Some(opaque_phase),
Some(alpha_mask_phase),
Some(transmissive_phase),
Some(transparent_phase),
) = (
opaque_render_phases.get_mut(&view_entity),
alpha_mask_render_phases.get_mut(&view_entity),
transmissive_render_phases.get_mut(&view_entity),
transparent_render_phases.get_mut(&view_entity),
)
else {
continue;
};
let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial<M>>();
let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial<M>>();
let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial<M>>();
let draw_transparent_pbr = transparent_draw_functions.read().id::<DrawMaterial<M>>();
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
| MeshPipelineKey::from_hdr(view.hdr);
if normal_prepass {
view_key |= MeshPipelineKey::NORMAL_PREPASS;
}
if depth_prepass {
view_key |= MeshPipelineKey::DEPTH_PREPASS;
}
if motion_vector_prepass {
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
}
if deferred_prepass {
view_key |= MeshPipelineKey::DEFERRED_PREPASS;
}
if temporal_jitter {
view_key |= MeshPipelineKey::TEMPORAL_JITTER;
}
if has_environment_maps {
view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
}
if has_irradiance_volumes {
view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;
}
if let Some(projection) = projection {
view_key |= match projection {
Projection::Perspective(_) => MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE,
Projection::Orthographic(_) => MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC,
};
}
match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
ShadowFilteringMethod::Hardware2x2 => {
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
}
ShadowFilteringMethod::Gaussian => {
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;
}
ShadowFilteringMethod::Temporal => {
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;
}
}
if !view.hdr {
if let Some(tonemapping) = tonemapping {
view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
view_key |= tonemapping_pipeline_key(*tonemapping);
}
if let Some(DebandDither::Enabled) = dither {
view_key |= MeshPipelineKey::DEBAND_DITHER;
}
}
if ssao {
view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
}
if let Some(camera_3d) = camera_3d {
view_key |= screen_space_specular_transmission_pipeline_key(
camera_3d.screen_space_specular_transmission_quality,
);
}
let rangefinder = view.rangefinder3d();
for visible_entity in visible_entities.iter::<WithMesh>() {
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
continue;
};
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
else {
continue;
};
let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
continue;
};
let Some(material) = render_materials.get(*material_asset_id) else {
continue;
};
let mut mesh_key = view_key
| MeshPipelineKey::from_bits_retain(mesh.key_bits.bits())
| material.properties.mesh_pipeline_key_bits;
let lightmap_image = render_lightmaps
.render_lightmaps
.get(visible_entity)
.map(|lightmap| lightmap.image);
if lightmap_image.is_some() {
mesh_key |= MeshPipelineKey::LIGHTMAPPED;
}
if render_visibility_ranges.entity_has_crossfading_visibility_ranges(*visible_entity) {
mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
}
if motion_vector_prepass {
if mesh_instance
.flags
.contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
{
mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
}
if mesh_instance
.flags
.contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
{
mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
}
}
let pipeline_id = pipelines.specialize(
&pipeline_cache,
&material_pipeline,
MaterialPipelineKey {
mesh_key,
bind_group_data: material.key.clone(),
},
&mesh.layout,
);
let pipeline_id = match pipeline_id {
Ok(id) => id,
Err(err) => {
error!("{}", err);
continue;
}
};
mesh_instance
.material_bind_group_id
.set(material.get_bind_group_id());
match mesh_key
.intersection(MeshPipelineKey::BLEND_RESERVED_BITS | MeshPipelineKey::MAY_DISCARD)
{
MeshPipelineKey::BLEND_OPAQUE | MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE => {
if material.properties.reads_view_transmission_texture {
let distance = rangefinder.distance_translation(&mesh_instance.translation)
+ material.properties.depth_bias;
transmissive_phase.add(Transmissive3d {
entity: *visible_entity,
draw_function: draw_transmissive_pbr,
pipeline: pipeline_id,
distance,
batch_range: 0..1,
extra_index: PhaseItemExtraIndex::NONE,
});
} else if material.properties.render_method == OpaqueRendererMethod::Forward {
let bin_key = Opaque3dBinKey {
draw_function: draw_opaque_pbr,
pipeline: pipeline_id,
asset_id: mesh_instance.mesh_asset_id.into(),
material_bind_group_id: material.get_bind_group_id().0,
lightmap_image,
};
opaque_phase.add(
bin_key,
*visible_entity,
BinnedRenderPhaseType::mesh(mesh_instance.should_batch()),
);
}
}
MeshPipelineKey::MAY_DISCARD => {
if material.properties.reads_view_transmission_texture {
let distance = rangefinder.distance_translation(&mesh_instance.translation)
+ material.properties.depth_bias;
transmissive_phase.add(Transmissive3d {
entity: *visible_entity,
draw_function: draw_transmissive_pbr,
pipeline: pipeline_id,
distance,
batch_range: 0..1,
extra_index: PhaseItemExtraIndex::NONE,
});
} else if material.properties.render_method == OpaqueRendererMethod::Forward {
let bin_key = OpaqueNoLightmap3dBinKey {
draw_function: draw_alpha_mask_pbr,
pipeline: pipeline_id,
asset_id: mesh_instance.mesh_asset_id.into(),
material_bind_group_id: material.get_bind_group_id().0,
};
alpha_mask_phase.add(
bin_key,
*visible_entity,
BinnedRenderPhaseType::mesh(mesh_instance.should_batch()),
);
}
}
_ => {
let distance = rangefinder.distance_translation(&mesh_instance.translation)
+ material.properties.depth_bias;
transparent_phase.add(Transparent3d {
entity: *visible_entity,
draw_function: draw_transparent_pbr,
pipeline: pipeline_id,
distance,
batch_range: 0..1,
extra_index: PhaseItemExtraIndex::NONE,
});
}
}
}
}
}
#[derive(Default, Resource, Clone, Debug, ExtractResource, Reflect)]
pub struct DefaultOpaqueRendererMethod(OpaqueRendererMethod);
impl DefaultOpaqueRendererMethod {
pub fn forward() -> Self {
DefaultOpaqueRendererMethod(OpaqueRendererMethod::Forward)
}
pub fn deferred() -> Self {
DefaultOpaqueRendererMethod(OpaqueRendererMethod::Deferred)
}
pub fn set_to_forward(&mut self) {
self.0 = OpaqueRendererMethod::Forward;
}
pub fn set_to_deferred(&mut self) {
self.0 = OpaqueRendererMethod::Deferred;
}
}
#[derive(Default, Clone, Copy, Debug, PartialEq, Reflect)]
pub enum OpaqueRendererMethod {
#[default]
Forward,
Deferred,
Auto,
}
pub struct MaterialProperties {
pub render_method: OpaqueRendererMethod,
pub alpha_mode: AlphaMode,
pub mesh_pipeline_key_bits: MeshPipelineKey,
pub depth_bias: f32,
pub reads_view_transmission_texture: bool,
}
pub struct PreparedMaterial<T: Material> {
pub bindings: Vec<(u32, OwnedBindingResource)>,
pub bind_group: BindGroup,
pub key: T::Data,
pub properties: MaterialProperties,
}
impl<M: Material> RenderAsset for PreparedMaterial<M> {
type SourceAsset = M;
type Param = (
SRes<RenderDevice>,
SRes<RenderAssets<GpuImage>>,
SRes<FallbackImage>,
SRes<MaterialPipeline<M>>,
SRes<DefaultOpaqueRendererMethod>,
SRes<Msaa>,
);
fn prepare_asset(
material: Self::SourceAsset,
(render_device, images, fallback_image, pipeline, default_opaque_render_method, msaa): &mut SystemParamItem<Self::Param>,
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
match material.as_bind_group(
&pipeline.material_layout,
render_device,
images,
fallback_image,
) {
Ok(prepared) => {
let method = match material.opaque_render_method() {
OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
OpaqueRendererMethod::Auto => default_opaque_render_method.0,
};
let mut mesh_pipeline_key_bits = MeshPipelineKey::empty();
mesh_pipeline_key_bits.set(
MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
material.reads_view_transmission_texture(),
);
mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(material.alpha_mode(), msaa));
Ok(PreparedMaterial {
bindings: prepared.bindings,
bind_group: prepared.bind_group,
key: prepared.data,
properties: MaterialProperties {
alpha_mode: material.alpha_mode(),
depth_bias: material.depth_bias(),
reads_view_transmission_texture: mesh_pipeline_key_bits
.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE),
render_method: method,
mesh_pipeline_key_bits,
},
})
}
Err(AsBindGroupError::RetryNextUpdate) => {
Err(PrepareAssetError::RetryNextUpdate(material))
}
}
}
}
#[derive(Component, Clone, Copy, Default, PartialEq, Eq, Deref, DerefMut)]
pub struct MaterialBindGroupId(pub Option<BindGroupId>);
impl MaterialBindGroupId {
pub fn new(id: BindGroupId) -> Self {
Self(Some(id))
}
}
impl From<BindGroup> for MaterialBindGroupId {
fn from(value: BindGroup) -> Self {
Self::new(value.id())
}
}
#[derive(Default)]
pub struct AtomicMaterialBindGroupId(AtomicU32);
impl AtomicMaterialBindGroupId {
pub fn set(&self, id: MaterialBindGroupId) {
let id = if let Some(id) = id.0 {
NonZeroU32::from(id).get()
} else {
0
};
self.0.store(id, Ordering::Relaxed);
}
pub fn get(&self) -> MaterialBindGroupId {
MaterialBindGroupId(NonZeroU32::new(self.0.load(Ordering::Relaxed)).map(BindGroupId::from))
}
}
impl<T: Material> PreparedMaterial<T> {
pub fn get_bind_group_id(&self) -> MaterialBindGroupId {
MaterialBindGroupId(Some(self.bind_group.id()))
}
}