Meshlet prep (#11442)

# Objective

- Prep for https://github.com/bevyengine/bevy/pull/10164
- Make deferred_lighting_pass_id a ColorAttachment
- Correctly extract shadow view frusta so that the view uniforms get
populated
- Make some needed things public
- Misc formatting
This commit is contained in:
JMS55 2024-01-22 07:28:33 -08:00 committed by GitHub
parent 2165793ff0
commit a796d53a05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 75 additions and 45 deletions

View file

@ -805,7 +805,7 @@ pub fn prepare_prepass_textures(
.clone()
});
let deferred_lighting_pass_id_texture = deferred_prepass.then(|| {
let cached_deferred_lighting_pass_id_texture = deferred_prepass.then(|| {
deferred_lighting_id_textures
.entry(camera.target.clone())
.or_insert_with(|| {
@ -836,7 +836,8 @@ pub fn prepare_prepass_textures(
motion_vectors: cached_motion_vectors_texture
.map(|t| ColorAttachment::new(t, None, Color::BLACK)),
deferred: cached_deferred_texture.map(|t| ColorAttachment::new(t, None, Color::BLACK)),
deferred_lighting_pass_id: deferred_lighting_pass_id_texture,
deferred_lighting_pass_id: cached_deferred_lighting_pass_id_texture
.map(|t| ColorAttachment::new(t, None, Color::BLACK)),
size,
});
}

View file

@ -94,7 +94,7 @@ impl ViewNode for CopyDeferredLightingIdNode {
let bind_group = render_context.render_device().create_bind_group(
"copy_deferred_lighting_id_bind_group",
&copy_deferred_lighting_id_pipeline.layout,
&BindGroupEntries::single(&deferred_lighting_pass_id_texture.default_view),
&BindGroupEntries::single(&deferred_lighting_pass_id_texture.texture.default_view),
);
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {

View file

@ -5,10 +5,9 @@ use bevy_render::render_graph::ViewNode;
use bevy_render::render_resource::StoreOp;
use bevy_render::{
camera::ExtractedCamera,
prelude::Color,
render_graph::{NodeRunError, RenderGraphContext},
render_phase::RenderPhase,
render_resource::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor},
render_resource::RenderPassDescriptor,
renderer::RenderContext,
view::ViewDepthTexture,
};
@ -83,11 +82,11 @@ impl ViewNode for DeferredGBufferPrepassNode {
.map(|deferred_texture| {
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
{
RenderPassColorAttachment {
bevy_render::render_resource::RenderPassColorAttachment {
view: &deferred_texture.texture.default_view,
resolve_target: None,
ops: Operations {
load: LoadOp::Load,
ops: bevy_render::render_resource::Operations {
load: bevy_render::render_resource::LoadOp::Load,
store: StoreOp::Store,
},
}
@ -101,14 +100,7 @@ impl ViewNode for DeferredGBufferPrepassNode {
view_prepass_textures
.deferred_lighting_pass_id
.as_ref()
.map(|deferred_lighting_pass_id| RenderPassColorAttachment {
view: &deferred_lighting_pass_id.default_view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::BLACK.into()),
store: StoreOp::Store,
},
}),
.map(|deferred_lighting_pass_id| deferred_lighting_pass_id.get_attachment()),
);
if color_attachments.iter().all(Option::is_none) {

View file

@ -34,7 +34,7 @@ use bevy_reflect::Reflect;
use bevy_render::{
render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
render_resource::{CachedRenderPipelineId, Extent3d, TextureFormat, TextureView},
texture::{CachedTexture, ColorAttachment},
texture::ColorAttachment,
};
use bevy_utils::{nonmax::NonMaxU32, FloatOrd};
@ -78,7 +78,7 @@ pub struct ViewPrepassTextures {
pub deferred: Option<ColorAttachment>,
/// A texture that specifies the deferred lighting pass id for a material.
/// Exists only if [`DeferredPrepass`] is added to the `ViewTarget`
pub deferred_lighting_pass_id: Option<CachedTexture>,
pub deferred_lighting_pass_id: Option<ColorAttachment>,
/// The size of the textures.
pub size: Extent3d,
}

View file

@ -18,7 +18,7 @@
// Creates the deferred gbuffer from a PbrInput.
fn deferred_gbuffer_from_pbr_input(in: PbrInput) -> vec4<u32> {
// Only monochrome occlusion supported. May not be worth including at all.
// Some models have baked occlusion, GLTF only supports monochrome.
// Some models have baked occlusion, GLTF only supports monochrome.
// Real time occlusion is applied in the deferred lighting pass.
// Deriving luminance via Rec. 709. coefficients
// https://en.wikipedia.org/wiki/Rec._709
@ -27,7 +27,7 @@ fn deferred_gbuffer_from_pbr_input(in: PbrInput) -> vec4<u32> {
var props = deferred_types::pack_unorm3x4_plus_unorm_20_(vec4(
in.material.reflectance,
in.material.metallic,
diffuse_occlusion,
diffuse_occlusion,
in.frag_coord.z));
#else
var props = deferred_types::pack_unorm4x8_(vec4(
@ -79,7 +79,7 @@ fn pbr_input_from_deferred_gbuffer(frag_coord: vec4<f32>, gbuffer: vec4<u32>) ->
#ifdef WEBGL2 // More crunched for webgl so we can also fit depth.
let props = deferred_types::unpack_unorm3x4_plus_unorm_20_(gbuffer.b);
// Bias to 0.5 since that's the value for almost all materials.
pbr.material.reflectance = saturate(props.r - 0.03333333333);
pbr.material.reflectance = saturate(props.r - 0.03333333333);
#else
let props = deferred_types::unpack_unorm4x8_(gbuffer.b);
pbr.material.reflectance = props.r;
@ -92,9 +92,9 @@ fn pbr_input_from_deferred_gbuffer(frag_coord: vec4<f32>, gbuffer: vec4<u32>) ->
let world_position = vec4(position_ndc_to_world(frag_coord_to_ndc(frag_coord)), 1.0);
let is_orthographic = view.projection[3].w == 1.0;
let V = pbr_functions::calculate_view(world_position, is_orthographic);
pbr.frag_coord = frag_coord;
pbr.world_normal = N;
pbr.world_normal = N;
pbr.world_position = world_position;
pbr.N = N;
pbr.V = V;

View file

@ -639,8 +639,8 @@ pub fn prepare_previous_view_projection_uniforms(
#[derive(Default, Resource)]
pub struct PrepassViewBindGroup {
motion_vectors: Option<BindGroup>,
no_motion_vectors: Option<BindGroup>,
pub motion_vectors: Option<BindGroup>,
pub no_motion_vectors: Option<BindGroup>,
}
pub fn prepare_prepass_view_bind_group<M: Material>(

View file

@ -149,7 +149,7 @@ fn fragment(in: VertexOutput) -> FragmentOutput {
#ifdef DEFERRED_PREPASS
// There isn't any material info available for this default prepass shader so we are just writing 
// emissive magenta out to the deferred gbuffer to be rendered by the first deferred lighting pass layer.
// The is here so if the default prepass fragment is used for deferred magenta will be rendered, and also
// This is here so if the default prepass fragment is used for deferred magenta will be rendered, and also
// as an example to show that a user could write to the deferred gbuffer if they were to start from this shader.
out.deferred = vec4(0u, bevy_pbr::rgb9e5::vec3_to_rgb9e5_(vec3(1.0, 0.0, 1.0)), 0u, 0u);
out.deferred_lighting_pass_id = 1u;

View file

@ -5,6 +5,7 @@ use bevy_render::{
camera::Camera,
color::Color,
mesh::Mesh,
primitives::{CascadesFrusta, CubemapFrusta, Frustum},
render_asset::RenderAssets,
render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::*,
@ -48,6 +49,7 @@ pub struct ExtractedDirectionalLight {
shadow_normal_bias: f32,
cascade_shadow_config: CascadeShadowConfig,
cascades: EntityHashMap<Entity, Vec<Cascade>>,
frusta: EntityHashMap<Entity, Vec<Frustum>>,
render_layers: RenderLayers,
}
@ -296,6 +298,7 @@ pub fn extract_lights(
&CubemapVisibleEntities,
&GlobalTransform,
&ViewVisibility,
&CubemapFrusta,
)>,
>,
spot_lights: Extract<
@ -304,6 +307,7 @@ pub fn extract_lights(
&VisibleEntities,
&GlobalTransform,
&ViewVisibility,
&Frustum,
)>,
>,
directional_lights: Extract<
@ -314,6 +318,7 @@ pub fn extract_lights(
&CascadesVisibleEntities,
&Cascades,
&CascadeShadowConfig,
&CascadesFrusta,
&GlobalTransform,
&ViewVisibility,
Option<&RenderLayers>,
@ -343,7 +348,7 @@ pub fn extract_lights(
let mut point_lights_values = Vec::with_capacity(*previous_point_lights_len);
for entity in global_point_lights.iter().copied() {
let Ok((point_light, cubemap_visible_entities, transform, view_visibility)) =
let Ok((point_light, cubemap_visible_entities, transform, view_visibility, frusta)) =
point_lights.get(entity)
else {
continue;
@ -373,7 +378,11 @@ pub fn extract_lights(
};
point_lights_values.push((
entity,
(extracted_point_light, render_cubemap_visible_entities),
(
extracted_point_light,
render_cubemap_visible_entities,
(*frusta).clone(),
),
));
}
*previous_point_lights_len = point_lights_values.len();
@ -381,7 +390,7 @@ pub fn extract_lights(
let mut spot_lights_values = Vec::with_capacity(*previous_spot_lights_len);
for entity in global_point_lights.iter().copied() {
if let Ok((spot_light, visible_entities, transform, view_visibility)) =
if let Ok((spot_light, visible_entities, transform, view_visibility, frustum)) =
spot_lights.get(entity)
{
if !view_visibility.get() {
@ -417,6 +426,7 @@ pub fn extract_lights(
spot_light_angles: Some((spot_light.inner_angle, spot_light.outer_angle)),
},
render_visible_entities,
*frustum,
),
));
}
@ -430,6 +440,7 @@ pub fn extract_lights(
visible_entities,
cascades,
cascade_config,
frusta,
transform,
view_visibility,
maybe_layers,
@ -452,6 +463,7 @@ pub fn extract_lights(
shadow_normal_bias: directional_light.shadow_normal_bias * std::f32::consts::SQRT_2,
cascade_shadow_config: cascade_config.clone(),
cascades: cascades.cascades.clone(),
frusta: frusta.frusta.clone(),
render_layers: maybe_layers.copied().unwrap_or_default(),
},
render_visible_entities,
@ -656,7 +668,11 @@ pub fn prepare_lights(
directional_light_shadow_map: Res<DirectionalLightShadowMap>,
mut max_directional_lights_warning_emitted: Local<bool>,
mut max_cascades_per_light_warning_emitted: Local<bool>,
point_lights: Query<(Entity, &ExtractedPointLight)>,
point_lights: Query<(
Entity,
&ExtractedPointLight,
AnyOf<(&CubemapFrusta, &Frustum)>,
)>,
directional_lights: Query<(Entity, &ExtractedDirectionalLight)>,
) {
let views_iter = views.iter();
@ -733,7 +749,7 @@ pub fn prepare_lights(
let spot_light_shadow_maps_count = point_lights
.iter()
.filter(|(_, light)| light.shadows_enabled && light.spot_light_angles.is_some())
.filter(|(_, light, _)| light.shadows_enabled && light.spot_light_angles.is_some())
.count()
.min(max_texture_array_layers - directional_shadow_enabled_count * MAX_CASCADES_PER_LIGHT);
@ -742,7 +758,7 @@ pub fn prepare_lights(
// - then those with shadows enabled first, so that the index can be used to render at most `point_light_shadow_maps_count`
// point light shadows and `spot_light_shadow_maps_count` spot light shadow maps,
// - then by entity as a stable key to ensure that a consistent set of lights are chosen if the light count limit is exceeded.
point_lights.sort_by(|(entity_1, light_1), (entity_2, light_2)| {
point_lights.sort_by(|(entity_1, light_1, _), (entity_2, light_2, _)| {
point_light_order(
(
entity_1,
@ -775,7 +791,7 @@ pub fn prepare_lights(
}
let mut gpu_point_lights = Vec::new();
for (index, &(entity, light)) in point_lights.iter().enumerate() {
for (index, &(entity, light, _)) in point_lights.iter().enumerate() {
let mut flags = PointLightFlags::NONE;
// Lights are sorted, shadow enabled lights are first
@ -952,11 +968,11 @@ pub fn prepare_lights(
};
// TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
for &(light_entity, light) in point_lights
for &(light_entity, light, (point_light_frusta, _)) in point_lights
.iter()
// Lights are sorted, shadow enabled lights are first
.take(point_light_shadow_maps_count)
.filter(|(_, light)| light.shadows_enabled)
.filter(|(_, light, _)| light.shadows_enabled)
{
let light_index = *global_light_meta
.entity_to_index
@ -967,7 +983,11 @@ pub fn prepare_lights(
// and ignore rotation because we want the shadow map projections to align with the axes
let view_translation = GlobalTransform::from_translation(light.transform.translation());
for (face_index, view_rotation) in cube_face_rotations.iter().enumerate() {
for (face_index, (view_rotation, frustum)) in cube_face_rotations
.iter()
.zip(&point_light_frusta.unwrap().frusta)
.enumerate()
{
let depth_texture_view =
point_light_depth_texture
.texture
@ -1005,6 +1025,7 @@ pub fn prepare_lights(
hdr: false,
color_grading: Default::default(),
},
*frustum,
RenderPhase::<Shadow>::default(),
LightEntity::Point {
light_entity,
@ -1017,7 +1038,7 @@ pub fn prepare_lights(
}
// spot lights
for (light_index, &(light_entity, light)) in point_lights
for (light_index, &(light_entity, light, (_, spot_light_frustum))) in point_lights
.iter()
.skip(point_light_count)
.take(spot_light_shadow_maps_count)
@ -1063,6 +1084,7 @@ pub fn prepare_lights(
hdr: false,
color_grading: Default::default(),
},
*spot_light_frustum.unwrap(),
RenderPhase::<Shadow>::default(),
LightEntity::Spot { light_entity },
))
@ -1078,12 +1100,20 @@ pub fn prepare_lights(
.enumerate()
.take(directional_shadow_enabled_count)
{
for (cascade_index, (cascade, bound)) in light
let cascades = light
.cascades
.get(&entity)
.unwrap()
.iter()
.take(MAX_CASCADES_PER_LIGHT)
.take(MAX_CASCADES_PER_LIGHT);
let frusta = light
.frusta
.get(&entity)
.unwrap()
.iter()
.take(MAX_CASCADES_PER_LIGHT);
for (cascade_index, ((cascade, frusta), bound)) in cascades
.zip(frusta)
.zip(&light.cascade_shadow_config.bounds)
.enumerate()
{
@ -1129,6 +1159,7 @@ pub fn prepare_lights(
hdr: false,
color_grading: Default::default(),
},
*frusta,
RenderPhase::<Shadow>::default(),
LightEntity::Directional {
light_entity,

View file

@ -678,6 +678,13 @@ pub struct InnerMeshVertexBufferLayout {
}
impl InnerMeshVertexBufferLayout {
pub fn new(attribute_ids: Vec<MeshVertexAttributeId>, layout: VertexBufferLayout) -> Self {
Self {
attribute_ids,
layout,
}
}
#[inline]
pub fn contains(&self, attribute_id: impl Into<MeshVertexAttributeId>) -> bool {
self.attribute_ids.contains(&attribute_id.into())

View file

@ -303,7 +303,7 @@ impl Frustum {
}
}
#[derive(Component, Debug, Default, Reflect)]
#[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component)]
pub struct CubemapFrusta {
#[reflect(ignore)]

View file

@ -32,7 +32,7 @@ pub use uniform_buffer::*;
// TODO: decide where re-exports should go
pub use wgpu::{
util::{BufferInitDescriptor, DrawIndexedIndirect},
util::{BufferInitDescriptor, DrawIndexedIndirect, DrawIndirect},
AdapterInfo as WgpuAdapterInfo, AddressMode, BindGroupDescriptor, BindGroupEntry,
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent,
BlendFactor, BlendOperation, BlendState, BufferAddress, BufferAsyncError, BufferBinding,

View file

@ -457,7 +457,7 @@ struct MainTargetTextures {
}
#[allow(clippy::too_many_arguments)]
fn prepare_view_targets(
pub fn prepare_view_targets(
mut commands: Commands,
windows: Res<ExtractedWindows>,
images: Res<RenderAssets<Image>>,

View file

@ -17,7 +17,6 @@ fn main() {
DefaultPlugins,
FrameTimeDiagnosticsPlugin,
LogDiagnosticsPlugin::default(),
bevy_internal::core_pipeline::experimental::taa::TemporalAntiAliasPlugin,
))
.add_systems(Startup, setup)
.add_systems(Update, (light_sway, movement))

View file

@ -1,11 +1,11 @@
//! Update a scene from a glTF file, either by spawning the scene as a child of another entity,
//! or by accessing the entities of the scene.
use bevy::prelude::*;
use bevy::{pbr::DirectionalLightShadowMap, prelude::*};
fn main() {
App::new()
.insert_resource(bevy_internal::pbr::DirectionalLightShadowMap { size: 4096 })
.insert_resource(DirectionalLightShadowMap { size: 4096 })
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, move_scene_entities)