From d4943ce55c3751719cc70de65ef010992b008acc Mon Sep 17 00:00:00 2001 From: James Gayfer <10660608+jgayfer@users.noreply.github.com> Date: Sat, 8 Jun 2024 13:43:25 -0700 Subject: [PATCH] Add support for WebGL2 Pretty sure we're taking some shortcuts for supporting large numbers of point lights, but I believe this gets us to a point where we can run on WebGL2. --- src/plugin.rs | 23 ++++++++------- src/render/extract.rs | 18 ++++-------- src/render/gpu.rs | 48 ------------------------------- src/render/lighting/lighting.wgsl | 17 +++++++++-- src/render/lighting/node.rs | 38 ++++++++++++++++++------ src/render/lighting/pipeline.rs | 14 ++++----- src/render/mod.rs | 1 - 7 files changed, 67 insertions(+), 92 deletions(-) delete mode 100644 src/render/gpu.rs diff --git a/src/plugin.rs b/src/plugin.rs index 8a9cd78..1d627a8 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -6,16 +6,19 @@ use bevy::{ prelude::*, render::{ extract_component::UniformComponentPlugin, + gpu_component_array_buffer::GpuComponentArrayBufferPlugin, render_graph::{RenderGraphApp, ViewNodeRunner}, - Render, RenderApp, RenderSet, + RenderApp, }, }; use crate::{ light::{AmbientLight2d, PointLight2d}, render::{ - extract::{extract_ambient_lights, extract_point_lights, ExtractedAmbientLight2d}, - gpu::{prepare_point_lights, GpuPointLights}, + extract::{ + extract_ambient_lights, extract_point_lights, ExtractedAmbientLight2d, + ExtractedPointLight2d, + }, lighting::{LightingNode, LightingPass, LightingPipeline, LIGHTING_SHADER}, }, }; @@ -32,9 +35,12 @@ impl Plugin for Light2dPlugin { Shader::from_wgsl ); - app.add_plugins(UniformComponentPlugin::::default()) - .register_type::() - .register_type::(); + app.add_plugins(( + UniformComponentPlugin::::default(), + GpuComponentArrayBufferPlugin::::default(), + )) + .register_type::() + .register_type::(); let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { return; @@ -45,7 +51,6 @@ impl Plugin for Light2dPlugin { ExtractSchedule, (extract_point_lights, extract_ambient_lights), ) - .add_systems(Render, (prepare_point_lights).in_set(RenderSet::Prepare)) .add_render_graph_node::>(Core2d, LightingPass) .add_render_graph_edge(Core2d, Node2d::MainPass, LightingPass); } @@ -55,8 +60,6 @@ impl Plugin for Light2dPlugin { return; }; - render_app - .init_resource::() - .init_resource::(); + render_app.init_resource::(); } } diff --git a/src/render/extract.rs b/src/render/extract.rs index ac57844..eb79abc 100644 --- a/src/render/extract.rs +++ b/src/render/extract.rs @@ -1,21 +1,13 @@ use bevy::{ - core_pipeline::core_2d::Camera2d, - ecs::{ - component::Component, - entity::Entity, - query::{With, Without}, - system::{Commands, Query}, - }, - math::Vec4, - render::{render_resource::ShaderType, view::ViewVisibility, Extract}, - transform::components::GlobalTransform, + prelude::*, + render::{render_resource::ShaderType, Extract}, }; use crate::light::{AmbientLight2d, PointLight2d}; -#[derive(Component, Default, Clone)] +#[derive(Component, Default, Clone, ShaderType)] pub struct ExtractedPointLight2d { - pub transform: GlobalTransform, + pub transform: Vec2, pub radius: f32, pub color: Vec4, pub intensity: f32, @@ -37,7 +29,7 @@ pub fn extract_point_lights( } commands.get_or_spawn(entity).insert(ExtractedPointLight2d { color: point_light.color.rgba_linear_to_vec4(), - transform: *global_transform, + transform: global_transform.translation().xy(), radius: point_light.radius, intensity: point_light.intensity, falloff: point_light.falloff, diff --git a/src/render/gpu.rs b/src/render/gpu.rs deleted file mode 100644 index c8c27ad..0000000 --- a/src/render/gpu.rs +++ /dev/null @@ -1,48 +0,0 @@ -use super::extract::ExtractedPointLight2d; -use bevy::{ - prelude::*, - render::{ - render_resource::{ShaderType, StorageBuffer}, - renderer::{RenderDevice, RenderQueue}, - }, -}; - -#[derive(Default, Resource)] -pub struct GpuPointLights { - pub buffer: StorageBuffer>, -} - -#[derive(Default, Clone, ShaderType)] -pub struct GpuPointLight2d { - pub center: Vec2, - pub radius: f32, - pub color: Vec4, - pub intensity: f32, - pub falloff: f32, -} - -pub fn prepare_point_lights( - render_device: Res, - render_queue: Res, - point_light_query: Query<&ExtractedPointLight2d>, - mut gpu_point_lights: ResMut, -) { - let point_light_buffer = gpu_point_lights.buffer.get_mut(); - - // Resources are global state, so we need to clear the data from the previous frame. - point_light_buffer.clear(); - - for point_light in &point_light_query { - point_light_buffer.push(GpuPointLight2d { - center: point_light.transform.translation().xy(), - radius: point_light.radius, - color: point_light.color, - intensity: point_light.intensity, - falloff: point_light.falloff, - }); - } - - gpu_point_lights - .buffer - .write_buffer(&render_device, &render_queue); -} diff --git a/src/render/lighting/lighting.wgsl b/src/render/lighting/lighting.wgsl index 9d46c5e..988a91a 100644 --- a/src/render/lighting/lighting.wgsl +++ b/src/render/lighting/lighting.wgsl @@ -45,8 +45,13 @@ var texture_sampler: sampler; @group(0) @binding(2) var view: View; -@group(0) @binding(3) -var point_lights: array; +#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 6 + @group(0) @binding(3) + var point_lights: array; +#else + @group(0) @binding(3) + var point_lights: array; +#endif @group(0) @binding(4) var ambient_light: AmbientLight2d; @@ -56,8 +61,14 @@ fn fragment(vo: FullscreenVertexOutput) -> @location(0) vec4 { // Setup aggregate color from light sources to multiply the main texture by. var light_color = vec3(1.0); +#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 6 + let point_light_count = arrayLength(&point_lights); +#else + let point_light_count = 64u; +#endif + // For each light, determine its illumination if we're within range of it. - for (var i = 0u; i < arrayLength(&point_lights); i++) { + for (var i = 0u; i < point_light_count; i++) { let point_light = point_lights[i]; diff --git a/src/render/lighting/node.rs b/src/render/lighting/node.rs index df6ba82..ec579c0 100644 --- a/src/render/lighting/node.rs +++ b/src/render/lighting/node.rs @@ -3,12 +3,14 @@ use bevy::render::extract_component::{ComponentUniforms, DynamicUniformIndex}; use bevy::render::render_graph::ViewNode; use bevy::render::render_resource::{ - BindGroupEntries, Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, + BindGroupEntries, GpuArrayBuffer, Operations, PipelineCache, RenderPassColorAttachment, + RenderPassDescriptor, }; +use bevy::render::renderer::RenderDevice; use bevy::render::view::{ViewTarget, ViewUniformOffset, ViewUniforms}; +use bevy::utils::smallvec::{smallvec, SmallVec}; -use crate::render::extract::ExtractedAmbientLight2d; -use crate::render::gpu::GpuPointLights; +use crate::render::extract::{ExtractedAmbientLight2d, ExtractedPointLight2d}; use super::LightingPipeline; @@ -45,10 +47,6 @@ impl ViewNode for LightingNode { return Ok(()); }; - let Some(point_light_buffer) = world.resource::().buffer.binding() else { - return Ok(()); - }; - let Some(ambient_light_uniform) = world .resource::>() .uniforms() @@ -57,6 +55,13 @@ impl ViewNode for LightingNode { return Ok(()); }; + let Some(point_light_binding) = world + .resource::>() + .binding() + else { + return Ok(()); + }; + let post_process = view_target.post_process_write(); let bind_group = render_context.render_device().create_bind_group( @@ -66,7 +71,7 @@ impl ViewNode for LightingNode { post_process.source, &lighting_pipeline.sampler, view_uniform_binding, - point_light_buffer, + point_light_binding, ambient_light_uniform, )), ); @@ -83,8 +88,23 @@ impl ViewNode for LightingNode { occlusion_query_set: None, }); + let mut dynamic_offsets: SmallVec<[u32; 3]> = smallvec![]; + + dynamic_offsets.push(view_offset.offset); + + if world + .resource::() + .limits() + .max_storage_buffers_per_shader_stage + == 0 + { + dynamic_offsets.push(0); + } + + dynamic_offsets.push(ambient_index.index()); + render_pass.set_render_pipeline(pipeline); - render_pass.set_bind_group(0, &bind_group, &[view_offset.offset, ambient_index.index()]); + render_pass.set_bind_group(0, &bind_group, &dynamic_offsets); render_pass.draw(0..3, 0..1); Ok(()) diff --git a/src/render/lighting/pipeline.rs b/src/render/lighting/pipeline.rs index 7908dd9..eef4e33 100644 --- a/src/render/lighting/pipeline.rs +++ b/src/render/lighting/pipeline.rs @@ -1,19 +1,17 @@ use bevy::core_pipeline::fullscreen_vertex_shader::fullscreen_shader_vertex_state; use bevy::prelude::*; -use bevy::render::render_resource::binding_types::{ - sampler, storage_buffer_read_only, texture_2d, uniform_buffer, -}; +use bevy::render::render_resource::binding_types::{sampler, texture_2d, uniform_buffer}; use bevy::render::render_resource::{ BindGroupLayout, BindGroupLayoutEntries, CachedRenderPipelineId, ColorTargetState, ColorWrites, - FragmentState, MultisampleState, PipelineCache, PrimitiveState, RenderPipelineDescriptor, - Sampler, SamplerBindingType, SamplerDescriptor, ShaderStages, TextureFormat, TextureSampleType, + FragmentState, GpuArrayBuffer, MultisampleState, PipelineCache, PrimitiveState, + RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, ShaderStages, + TextureFormat, TextureSampleType, }; use bevy::render::renderer::RenderDevice; use bevy::render::texture::BevyDefault; use bevy::render::view::ViewUniform; -use crate::render::extract::ExtractedAmbientLight2d; -use crate::render::gpu::GpuPointLight2d; +use crate::render::extract::{ExtractedAmbientLight2d, ExtractedPointLight2d}; use super::LIGHTING_SHADER; @@ -39,7 +37,7 @@ impl FromWorld for LightingPipeline { texture_2d(TextureSampleType::Float { filterable: true }), sampler(SamplerBindingType::Filtering), uniform_buffer::(true), - storage_buffer_read_only::>(false), + GpuArrayBuffer::::binding_layout(render_device), uniform_buffer::(true), ), ), diff --git a/src/render/mod.rs b/src/render/mod.rs index 1339c21..aec3230 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,3 +1,2 @@ pub mod extract; -pub mod gpu; pub mod lighting;