use bevy::{
camera::{MainPassResolutionOverride, Viewport},
ecs::{query::QueryItem, world::World},
log::error,
render::{
camera::ExtractedCamera,
extract_component::ComponentUniforms,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::ViewSortedRenderPhases,
render_resource::*,
renderer::{RenderContext, RenderDevice},
view::{ExtractedView, ViewTarget, ViewUniformOffset, ViewUniforms},
},
};
use smallvec::{SmallVec, smallvec};
use crate::render::{
extract::{ExtractedAmbientLight2d, ExtractedLight2dMeta, ExtractedPointLight2d},
phase::Light2dOccluderPhaseItem,
pipeline::{Light2dCompositePipeline, Light2dPipeline},
prepare::{Light2dOccluderTextures, Light2dTextures},
};
#[derive(Default)]
pub(super) struct Light2dOccluderNode;
impl ViewNode for Light2dOccluderNode {
type ViewQuery = (
&'static ViewTarget,
&'static ExtractedCamera,
&'static ExtractedView,
Option<&'static MainPassResolutionOverride>,
);
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
(_, extracted_camera, extracted_view, resolution_override): QueryItem<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.view_entity();
let occluder_phases = world.resource::<ViewSortedRenderPhases<Light2dOccluderPhaseItem>>();
let light_2d_occluder_textures = world.resource::<Light2dOccluderTextures>();
let (Some(occluder_phase), Some(light_2d_occluder_texture)) = (
occluder_phases.get(&extracted_view.retained_view_entity),
light_2d_occluder_textures
.0
.get(&extracted_view.retained_view_entity),
) else {
return Ok(());
};
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("light_2d_occluder_render_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view: &light_2d_occluder_texture.default_view,
depth_slice: None,
resolve_target: None,
ops: Operations::default(),
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
if let Some(viewport) = Viewport::from_viewport_and_override(
extracted_camera.viewport.as_ref(),
resolution_override,
) {
render_pass.set_camera_viewport(&viewport);
}
if let Err(err) = occluder_phase.render(&mut render_pass, world, view_entity) {
error!("Error encountered while rendering the stencil phase {err:?}");
}
Ok(())
}
}
#[derive(Default)]
pub(super) struct Light2dNode;
impl ViewNode for Light2dNode {
type ViewQuery = (
&'static ViewTarget,
&'static ExtractedView,
&'static ViewUniformOffset,
&'static ExtractedAmbientLight2d,
);
fn run(
&self,
_: &mut RenderGraphContext,
render_context: &mut RenderContext,
(_, extracted_view, view_offset, _): QueryItem<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
let view = world.resource::<ViewUniforms>();
let pipeline_cache = world.resource::<PipelineCache>();
let light_2d_pipeline = world.resource::<Light2dPipeline>();
let light_2d_occluder_textures = world.resource::<Light2dOccluderTextures>();
let light_2d_textures = world.resource::<Light2dTextures>();
let light_meta = world.resource::<ComponentUniforms<ExtractedLight2dMeta>>();
let point_lights = world.resource::<GpuArrayBuffer<ExtractedPointLight2d>>();
let (
Some(view),
Some(pipeline),
Some(light_2d_occluder_texture),
Some(light_2d_texture),
Some(light_meta),
Some(point_lights),
) = (
view.uniforms.binding(),
pipeline_cache.get_render_pipeline(light_2d_pipeline.pipeline_id),
light_2d_occluder_textures
.0
.get(&extracted_view.retained_view_entity),
light_2d_textures
.0
.get(&extracted_view.retained_view_entity),
light_meta.uniforms().binding(),
point_lights.binding(),
)
else {
return Ok(());
};
let vertex_bind_group = render_context.render_device().create_bind_group(
"light_2d_vertex_bind_group",
&pipeline_cache.get_bind_group_layout(&light_2d_pipeline.vertex_layout),
&BindGroupEntries::single(view),
);
let fragment_bind_group = render_context.render_device().create_bind_group(
"light_2d_fragment_bind_group",
&pipeline_cache.get_bind_group_layout(&light_2d_pipeline.fragment_layout),
&BindGroupEntries::sequential((
&light_2d_occluder_texture.default_view,
&light_2d_pipeline.light_2d_occluder_sampler,
light_meta,
point_lights,
)),
);
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("light_2d_render_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view: &light_2d_texture.default_view,
depth_slice: None,
resolve_target: None,
ops: Operations::default(),
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
let mut fragment_offsets: SmallVec<[u32; 1]> = smallvec![];
let limits = world.resource::<RenderDevice>().limits();
if limits.max_storage_buffers_per_shader_stage == 0 {
fragment_offsets.push(0); }
render_pass.set_render_pipeline(pipeline);
render_pass.set_bind_group(0, &vertex_bind_group, &[view_offset.offset]);
render_pass.set_bind_group(1, &fragment_bind_group, &fragment_offsets);
render_pass.draw(0..3, 0..1);
Ok(())
}
}
#[derive(Default)]
pub(super) struct Light2dCompositeNode;
impl ViewNode for Light2dCompositeNode {
type ViewQuery = (
&'static ViewTarget,
&'static ExtractedView,
&'static ExtractedAmbientLight2d,
);
fn run(
&self,
_: &mut RenderGraphContext,
render_context: &mut RenderContext,
(view_target, extracted_view, _): QueryItem<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>();
let light_2d_composite_pipeline = world.resource::<Light2dCompositePipeline>();
let light_2d_textures = world.resource::<Light2dTextures>();
let ambient = world.resource::<ComponentUniforms<ExtractedAmbientLight2d>>();
let (Some(pipeline), Some(light_2d_texture), Some(ambient)) = (
pipeline_cache.get_render_pipeline(light_2d_composite_pipeline.pipeline_id),
light_2d_textures
.0
.get(&extracted_view.retained_view_entity),
ambient.uniforms().binding(),
) else {
return Ok(());
};
let screen_texture = view_target.post_process_write();
let fragment_bind_group = render_context.render_device().create_bind_group(
"light_2d_composite_fragment_bind_group",
&pipeline_cache.get_bind_group_layout(&light_2d_composite_pipeline.fragment_layout),
&BindGroupEntries::sequential((
screen_texture.source,
&light_2d_composite_pipeline.screen_sampler,
&light_2d_texture.default_view,
&light_2d_composite_pipeline.light_2d_sampler,
ambient,
)),
);
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("light_2d_composite_render_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view: screen_texture.destination,
depth_slice: None,
resolve_target: None,
ops: Operations::default(),
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
render_pass.set_render_pipeline(pipeline);
render_pass.set_bind_group(0, &fragment_bind_group, &[]);
render_pass.draw(0..3, 0..1);
Ok(())
}
}