diff --git a/bevy_render/src/lib.rs b/bevy_render/src/lib.rs index 479841520b..cc3a478f4d 100644 --- a/bevy_render/src/lib.rs +++ b/bevy_render/src/lib.rs @@ -39,8 +39,7 @@ use self::{ }, render_graph::RenderGraph, render_resource::{ - entity_render_resource_assignments_system, - resource_providers::{LightResourceProvider, UniformResourceProvider}, + entity_render_resource_assignments_system, resource_providers::UniformResourceProvider, AssetBatchers, EntityRenderResourceAssignments, RenderResourceAssignments, }, shader::{uniforms::StandardMaterial, Shader}, @@ -54,7 +53,10 @@ use bevy_window::{WindowCreated, WindowReference, WindowResized}; use pass::PassDescriptor; use pipeline::pipelines::build_forward_pipeline; use render_graph_2::{ - nodes::{Camera2dNode, CameraNode, PassNode, WindowSwapChainNode, WindowTextureNode, UniformNode}, + nodes::{ + Camera2dNode, CameraNode, LightsNode, PassNode, UniformNode, WindowSwapChainNode, + WindowTextureNode, + }, RenderGraph2, }; use render_resource::resource_providers::mesh_resource_provider_system; @@ -77,7 +79,6 @@ impl RenderPlugin { let mut render_graph = resources.get_mut::().unwrap(); render_graph .build(&mut pipelines, &mut shaders) - .add_resource_provider(LightResourceProvider::new(10)) .add_resource_provider(UniformResourceProvider::::new(true)); } } @@ -123,7 +124,12 @@ impl AppPlugin for RenderPlugin { let resources = app.resources_mut(); render_graph.add_system_node_named("camera", CameraNode::default(), resources); render_graph.add_system_node_named("camera2d", Camera2dNode::default(), resources); - render_graph.add_system_node_named("standard_material", UniformNode::::new(true), resources); + render_graph.add_system_node_named( + "standard_material", + UniformNode::::new(true), + resources, + ); + render_graph.add_system_node_named("lights", LightsNode::new(10), resources); render_graph.add_node_named( "swapchain", WindowSwapChainNode::new( @@ -185,7 +191,10 @@ impl AppPlugin for RenderPlugin { // TODO: replace these with "autowire" groups render_graph.add_node_edge("camera", "main_pass").unwrap(); render_graph.add_node_edge("camera2d", "main_pass").unwrap(); - render_graph.add_node_edge("standard_material", "main_pass").unwrap(); + render_graph + .add_node_edge("standard_material", "main_pass") + .unwrap(); + render_graph.add_node_edge("lights", "main_pass").unwrap(); render_graph .add_slot_edge( "swapchain", diff --git a/bevy_render/src/render_graph_2/nodes.rs b/bevy_render/src/render_graph_2/nodes.rs index c577591857..fe0d4d58ec 100644 --- a/bevy_render/src/render_graph_2/nodes.rs +++ b/bevy_render/src/render_graph_2/nodes.rs @@ -4,10 +4,12 @@ mod window_texture_node; mod window_swapchain_node; mod pass_node; mod uniform_node; +mod lights_node; pub use camera_node::*; pub use camera2d_node::*; pub use window_texture_node::*; pub use window_swapchain_node::*; pub use pass_node::*; -pub use uniform_node::*; \ No newline at end of file +pub use uniform_node::*; +pub use lights_node::*; \ No newline at end of file diff --git a/bevy_render/src/render_graph_2/nodes/lights_node.rs b/bevy_render/src/render_graph_2/nodes/lights_node.rs new file mode 100644 index 0000000000..ce2f235530 --- /dev/null +++ b/bevy_render/src/render_graph_2/nodes/lights_node.rs @@ -0,0 +1,150 @@ +use crate::{ + render_graph_2::{CommandQueue, Node, ResourceSlots, SystemNode}, + render_resource::{resource_name, BufferInfo, BufferUsage, RenderResourceAssignments}, + renderer_2::{GlobalRenderResourceContext, RenderContext}, + Light, LightRaw, +}; + +use bevy_transform::prelude::*; +use legion::prelude::*; +use zerocopy::AsBytes; + +#[derive(Default)] +pub struct LightsNode { + command_queue: CommandQueue, + max_lights: usize, +} + +impl LightsNode { + pub fn new(max_lights: usize) -> Self { + LightsNode { + max_lights, + command_queue: CommandQueue::default(), + } + } +} + +impl Node for LightsNode { + fn update( + &mut self, + _world: &World, + _resources: &Resources, + render_context: &mut dyn RenderContext, + _input: &ResourceSlots, + _output: &mut ResourceSlots, + ) { + self.command_queue.execute(render_context); + } +} + +#[repr(C)] +#[derive(Clone, Copy, AsBytes)] +pub struct LightCount { + pub num_lights: [u32; 4], +} + +impl SystemNode for LightsNode { + fn get_system(&self, resources: &mut Resources) -> Box { + let mut light_buffer = None; + let mut lights_are_dirty = true; + // TODO: merge these + let mut tmp_count_buffer = None; + let mut tmp_light_buffer = None; + let mut command_queue = self.command_queue.clone(); + let max_lights = self.max_lights; + SystemBuilder::new("light_node") + .read_resource::() + // TODO: this write on RenderResourceAssignments will prevent this system from running in parallel with other systems that do the same + .write_resource::() + .with_query(<(Read, Read, Read)>::query()) + .build( + move |_, + world, + (render_resource_context, ref mut render_resource_assignments), + query| { + if !lights_are_dirty { + return; + } + + let render_resources = &render_resource_context.context; + if light_buffer.is_none() { + let light_uniform_size = std::mem::size_of::() + + max_lights * std::mem::size_of::(); + + let buffer = render_resources.create_buffer(BufferInfo { + size: light_uniform_size, + buffer_usage: BufferUsage::UNIFORM + | BufferUsage::COPY_SRC + | BufferUsage::COPY_DST, + ..Default::default() + }); + render_resource_assignments.set(resource_name::uniform::LIGHTS, buffer); + light_buffer = Some(buffer); + } + + let light_count = query.iter(world).count(); + + if light_count == 0 { + return; + } + + lights_are_dirty = false; + let size = std::mem::size_of::(); + let total_size = size * light_count; + let light_count_size = std::mem::size_of::(); + + if let Some(old_tmp_light_buffer) = tmp_light_buffer { + render_resources.remove_buffer(old_tmp_light_buffer); + } + + if let Some(old_tmp_count_buffer) = tmp_count_buffer { + render_resources.remove_buffer(old_tmp_count_buffer); + } + + tmp_light_buffer = Some(render_resources.create_buffer_mapped( + BufferInfo { + size: total_size, + buffer_usage: BufferUsage::COPY_SRC, + ..Default::default() + }, + &mut |data, _renderer| { + for ((light, local_to_world, translation), slot) in + query.iter(world).zip(data.chunks_exact_mut(size)) + { + slot.copy_from_slice( + LightRaw::from(&light, &local_to_world.0, &translation) + .as_bytes(), + ); + } + }, + )); + tmp_count_buffer = Some(render_resources.create_buffer_mapped( + BufferInfo { + size: light_count_size, + buffer_usage: BufferUsage::COPY_SRC, + ..Default::default() + }, + &mut |data, _renderer| { + data.copy_from_slice([light_count as u32, 0, 0, 0].as_bytes()); + }, + )); + + command_queue.copy_buffer_to_buffer( + tmp_count_buffer.unwrap(), + 0, + light_buffer.unwrap(), + 0, + light_count_size as u64, + ); + + command_queue.copy_buffer_to_buffer( + tmp_light_buffer.unwrap(), + 0, + light_buffer.unwrap(), + light_count_size as u64, + total_size as u64, + ); + }, + ) + } +}