diff --git a/src/plugin.rs b/src/plugin.rs index 6e25fb1..1415b7b 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -23,8 +23,8 @@ use crate::{ ExtractedAmbientLight2d, ExtractedLightOccluder2d, ExtractedPointLight2d, }, light_map::{ - prepare_light_map_texture, LightMapNode, LightMapPass, LightMapPipeline, - LIGHT_MAP_SHADER, + prepare_light_map_texture, prepare_point_light_count, LightMapNode, LightMapPass, + LightMapPipeline, PointLightMetaBuffer, LIGHT_MAP_SHADER, }, lighting::{ prepare_lighting_pipelines, LightingNode, LightingPass, LightingPipeline, @@ -83,6 +83,7 @@ impl Plugin for Light2dPlugin { render_app .init_resource::>() + .init_resource::() .add_systems( ExtractSchedule, ( @@ -95,6 +96,7 @@ impl Plugin for Light2dPlugin { Render, ( prepare_lighting_pipelines.in_set(RenderSet::Prepare), + prepare_point_light_count.in_set(RenderSet::Prepare), prepare_sdf_texture .after(prepare_view_targets) .in_set(RenderSet::ManageViews), diff --git a/src/render/light_map/light_map.wgsl b/src/render/light_map/light_map.wgsl index 1918d2b..86f709a 100644 --- a/src/render/light_map/light_map.wgsl +++ b/src/render/light_map/light_map.wgsl @@ -1,6 +1,6 @@ #import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput #import bevy_render::view::View -#import bevy_light_2d::types::{AmbientLight2d, PointLight2d}; +#import bevy_light_2d::types::{AmbientLight2d, PointLight2d, PointLightMeta}; #import bevy_light_2d::view_transformations::{ frag_coord_to_ndc, ndc_to_world, @@ -30,9 +30,12 @@ var ambient_light: AmbientLight2d; #endif @group(0) @binding(3) -var sdf: texture_2d; +var point_light_meta: PointLightMeta; @group(0) @binding(4) +var sdf: texture_2d; + +@group(0) @binding(5) var sdf_sampler: sampler; @fragment @@ -45,15 +48,7 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { var lighting_color = vec3(1.0); - // WebGL2 does not support storage buffers (or runtime sized arrays), so we - // need to use a fixed number of point lights. -#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 6 - let point_light_count = arrayLength(&point_lights); -#else - let point_light_count = MAX_POINT_LIGHTS; -#endif - - for (var i = 0u; i < point_light_count; i++) { + for (var i = 0u; i < point_light_meta.count; i++) { let light = point_lights[i]; let dist = distance(light.center, pos); diff --git a/src/render/light_map/mod.rs b/src/render/light_map/mod.rs index bde33f8..ce04209 100644 --- a/src/render/light_map/mod.rs +++ b/src/render/light_map/mod.rs @@ -4,13 +4,18 @@ mod prepare; use bevy::{ asset::Handle, - ecs::component::Component, - render::{render_graph::RenderLabel, render_resource::Shader, texture::CachedTexture}, + ecs::{component::Component, system::Resource}, + math::Vec3, + render::{ + render_graph::RenderLabel, + render_resource::{Shader, ShaderType, UniformBuffer}, + texture::CachedTexture, + }, }; pub use node::LightMapNode; pub use pipeline::LightMapPipeline; -pub use prepare::prepare_light_map_texture; +pub use prepare::{prepare_light_map_texture, prepare_point_light_count}; pub const LIGHT_MAP_SHADER: Handle = Handle::weak_from_u128(320609826414128764415270070474935914193); @@ -22,3 +27,24 @@ pub struct LightMapPass; pub struct LightMapTexture { pub light_map: CachedTexture, } + +#[derive(Resource, Default)] +pub struct PointLightMetaBuffer { + pub buffer: UniformBuffer, +} + +#[derive(Default, ShaderType)] +pub struct PointLightMeta { + pub count: u32, + // WebGL2 structs must be 16 byte aligned. + _padding: Vec3, +} + +impl PointLightMeta { + pub fn new(count: u32) -> Self { + Self { + count, + _padding: Vec3::ZERO, + } + } +} diff --git a/src/render/light_map/node.rs b/src/render/light_map/node.rs index fefb9fa..931e527 100644 --- a/src/render/light_map/node.rs +++ b/src/render/light_map/node.rs @@ -14,7 +14,7 @@ use smallvec::{smallvec, SmallVec}; use crate::render::extract::{ExtractedAmbientLight2d, ExtractedPointLight2d}; use crate::render::sdf::SdfTexture; -use super::{LightMapPipeline, LightMapTexture}; +use super::{LightMapPipeline, LightMapTexture, PointLightMetaBuffer}; const LIGHT_MAP_PASS: &str = "light_map_pass"; const LIGHT_MAP_BIND_GROUP: &str = "light_map_bind_group"; @@ -48,6 +48,7 @@ impl ViewNode for LightMapNode { Some(view_uniform_binding), Some(ambient_light_uniform), Some(point_light_binding), + Some(point_light_count_binding), ) = ( pipeline_cache.get_render_pipeline(light_map_pipeline.pipeline_id), world.resource::().uniforms.binding(), @@ -58,6 +59,7 @@ impl ViewNode for LightMapNode { world .resource::>() .binding(), + world.resource::().buffer.binding(), ) else { return Ok(()); @@ -70,6 +72,7 @@ impl ViewNode for LightMapNode { view_uniform_binding.clone(), ambient_light_uniform.clone(), point_light_binding.clone(), + point_light_count_binding.clone(), &sdf_texture.sdf.default_view, &light_map_pipeline.sdf_sampler, )), diff --git a/src/render/light_map/pipeline.rs b/src/render/light_map/pipeline.rs index 9cf0f02..3c65b09 100644 --- a/src/render/light_map/pipeline.rs +++ b/src/render/light_map/pipeline.rs @@ -13,7 +13,7 @@ use bevy::render::view::ViewUniform; use crate::render::extract::{ExtractedAmbientLight2d, ExtractedPointLight2d}; -use super::LIGHT_MAP_SHADER; +use super::{PointLightMeta, LIGHT_MAP_SHADER}; const LIGHT_MAP_BIND_GROUP_LAYOUT: &str = "light_map_group_layout"; const LIGHT_MAP_PIPELINE: &str = "light_map_pipeline"; @@ -37,6 +37,7 @@ impl FromWorld for LightMapPipeline { uniform_buffer::(true), uniform_buffer::(true), GpuArrayBuffer::::binding_layout(render_device), + uniform_buffer::(false), texture_2d(TextureSampleType::Float { filterable: true }), sampler(SamplerBindingType::Filtering), ), diff --git a/src/render/light_map/prepare.rs b/src/render/light_map/prepare.rs index 75584d3..2721154 100644 --- a/src/render/light_map/prepare.rs +++ b/src/render/light_map/prepare.rs @@ -5,13 +5,15 @@ use bevy::{ }, render::{ render_resource::{TextureDescriptor, TextureDimension, TextureFormat, TextureUsages}, - renderer::RenderDevice, + renderer::{RenderDevice, RenderQueue}, texture::TextureCache, view::ViewTarget, }, }; -use super::LightMapTexture; +use crate::render::extract::ExtractedPointLight2d; + +use super::{LightMapTexture, PointLightMeta, PointLightMetaBuffer}; const LIGHT_MAP_TEXTURE: &str = "light_map_texture"; @@ -41,3 +43,16 @@ pub fn prepare_light_map_texture( }); } } + +pub fn prepare_point_light_count( + render_device: Res, + render_queue: Res, + point_lights: Query<&ExtractedPointLight2d>, + mut point_light_count: ResMut, +) { + let meta = PointLightMeta::new(point_lights.iter().count() as u32); + point_light_count.buffer.set(meta); + point_light_count + .buffer + .write_buffer(&render_device, &render_queue); +} diff --git a/src/render/types.wgsl b/src/render/types.wgsl index d3b037a..33b332a 100644 --- a/src/render/types.wgsl +++ b/src/render/types.wgsl @@ -17,3 +17,9 @@ struct PointLight2d { falloff: f32, cast_shadows: u32 } + +struct PointLightMeta { + count: u32, + // WebGL2 structs must be 16 byte aligned. + _padding: vec3 +}