From d4521e0c6c5f11f4d8ec5abaa0c0c851d5937c40 Mon Sep 17 00:00:00 2001 From: James Gayfer <10660608+jgayfer@users.noreply.github.com> Date: Fri, 14 Jun 2024 21:02:54 -0700 Subject: [PATCH 1/2] Add HDR support As HDR uses a different texture format, we need to set it on a per view basis. As the pipeline is currently more of a "one and done" construct, we need to make use of a specialized pipeline. It allows us to dynamically create the render pipeline descriptor, giving us a place to hook into if each view has HDR enabled. On the view node side of things, we need to ensure we're grabbing the specialized pipeline ID from the view entity, as we are no longer constructing a "static" variant of the pipeline. --- src/plugin.rs | 13 ++++++- src/render/lighting/mod.rs | 16 +++++++- src/render/lighting/node.rs | 11 ++++-- src/render/lighting/pipeline.rs | 68 +++++++++++++++++---------------- src/render/lighting/prepare.rs | 35 +++++++++++++++++ 5 files changed, 103 insertions(+), 40 deletions(-) create mode 100644 src/render/lighting/prepare.rs diff --git a/src/plugin.rs b/src/plugin.rs index 1d627a8..f830dc5 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -8,7 +8,8 @@ use bevy::{ extract_component::UniformComponentPlugin, gpu_component_array_buffer::GpuComponentArrayBufferPlugin, render_graph::{RenderGraphApp, ViewNodeRunner}, - RenderApp, + render_resource::SpecializedRenderPipelines, + Render, RenderApp, RenderSet, }, }; @@ -19,7 +20,10 @@ use crate::{ extract_ambient_lights, extract_point_lights, ExtractedAmbientLight2d, ExtractedPointLight2d, }, - lighting::{LightingNode, LightingPass, LightingPipeline, LIGHTING_SHADER}, + lighting::{ + prepare_lighting_pipelines, LightingNode, LightingPass, LightingPipeline, + LIGHTING_SHADER, + }, }, }; @@ -47,10 +51,15 @@ impl Plugin for Light2dPlugin { }; render_app + .init_resource::>() .add_systems( ExtractSchedule, (extract_point_lights, extract_ambient_lights), ) + .add_systems( + Render, + prepare_lighting_pipelines.in_set(RenderSet::Prepare), + ) .add_render_graph_node::>(Core2d, LightingPass) .add_render_graph_edge(Core2d, Node2d::MainPass, LightingPass); } diff --git a/src/render/lighting/mod.rs b/src/render/lighting/mod.rs index b083046..d09a4b0 100644 --- a/src/render/lighting/mod.rs +++ b/src/render/lighting/mod.rs @@ -1,16 +1,30 @@ mod node; mod pipeline; +mod prepare; use bevy::{ asset::Handle, - render::{render_graph::RenderLabel, render_resource::Shader}, + ecs::component::Component, + render::{ + render_graph::RenderLabel, + render_resource::{CachedRenderPipelineId, Shader}, + }, }; pub use node::LightingNode; pub use pipeline::LightingPipeline; +pub use prepare::prepare_lighting_pipelines; pub const LIGHTING_SHADER: Handle = Handle::weak_from_u128(111120241052143214281687226997564407636); #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] pub struct LightingPass; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct LightingPipelineKey { + pub hdr: bool, +} + +#[derive(Component)] +pub struct LightingPipelineId(pub CachedRenderPipelineId); diff --git a/src/render/lighting/node.rs b/src/render/lighting/node.rs index 06f47fc..35a5a3b 100644 --- a/src/render/lighting/node.rs +++ b/src/render/lighting/node.rs @@ -12,7 +12,7 @@ use bevy::utils::smallvec::{smallvec, SmallVec}; use crate::render::extract::{ExtractedAmbientLight2d, ExtractedPointLight2d}; -use super::LightingPipeline; +use super::{LightingPipeline, LightingPipelineId}; const LIGHTING_PASS: &str = "lighting_pass"; const LIGHTING_BIND_GROUP: &str = "lighting_bind_group"; @@ -25,21 +25,24 @@ impl ViewNode for LightingNode { &'static ViewTarget, &'static DynamicUniformIndex, &'static ViewUniformOffset, + &'static LightingPipelineId, ); fn run<'w>( &self, _graph: &mut bevy::render::render_graph::RenderGraphContext, render_context: &mut bevy::render::renderer::RenderContext<'w>, - (view_target, ambient_index, view_offset): bevy::ecs::query::QueryItem<'w, Self::ViewQuery>, + (view_target, ambient_index, view_offset, pipeline_id): bevy::ecs::query::QueryItem< + 'w, + Self::ViewQuery, + >, world: &'w World, ) -> Result<(), bevy::render::render_graph::NodeRunError> { let lighting_pipeline = world.resource::(); let pipeline_cache = world.resource::(); - let Some(pipeline) = pipeline_cache.get_render_pipeline(lighting_pipeline.pipeline_id) - else { + let Some(pipeline) = pipeline_cache.get_render_pipeline(pipeline_id.0) else { return Ok(()); }; diff --git a/src/render/lighting/pipeline.rs b/src/render/lighting/pipeline.rs index 394adb9..d7d3123 100644 --- a/src/render/lighting/pipeline.rs +++ b/src/render/lighting/pipeline.rs @@ -2,18 +2,18 @@ use bevy::core_pipeline::fullscreen_vertex_shader::fullscreen_shader_vertex_stat use bevy::prelude::*; use bevy::render::render_resource::binding_types::{sampler, texture_2d, uniform_buffer}; use bevy::render::render_resource::{ - BindGroupLayout, BindGroupLayoutEntries, CachedRenderPipelineId, ColorTargetState, ColorWrites, - FragmentState, GpuArrayBuffer, MultisampleState, PipelineCache, PrimitiveState, - RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, ShaderStages, - TextureFormat, TextureSampleType, + BindGroupLayout, BindGroupLayoutEntries, ColorTargetState, ColorWrites, FragmentState, + GpuArrayBuffer, MultisampleState, PrimitiveState, RenderPipelineDescriptor, Sampler, + SamplerBindingType, SamplerDescriptor, ShaderStages, SpecializedRenderPipeline, TextureFormat, + TextureSampleType, }; use bevy::render::renderer::RenderDevice; use bevy::render::texture::BevyDefault; -use bevy::render::view::ViewUniform; +use bevy::render::view::{ViewTarget, ViewUniform}; use crate::render::extract::{ExtractedAmbientLight2d, ExtractedPointLight2d}; -use super::LIGHTING_SHADER; +use super::{LightingPipelineKey, LIGHTING_SHADER}; const LIGHTING_PIPELINE: &str = "lighting_pipeline"; const LIGHTING_BIND_GROUP_LAYOUT: &str = "lighting_bind_group_layout"; @@ -22,7 +22,6 @@ const LIGHTING_BIND_GROUP_LAYOUT: &str = "lighting_bind_group_layout"; pub struct LightingPipeline { pub layout: BindGroupLayout, pub sampler: Sampler, - pub pipeline_id: CachedRenderPipelineId, } impl FromWorld for LightingPipeline { @@ -45,33 +44,36 @@ impl FromWorld for LightingPipeline { let sampler = render_device.create_sampler(&SamplerDescriptor::default()); - let pipeline_id = - world - .resource_mut::() - .queue_render_pipeline(RenderPipelineDescriptor { - label: Some(LIGHTING_PIPELINE.into()), - layout: vec![layout.clone()], - vertex: fullscreen_shader_vertex_state(), - fragment: Some(FragmentState { - shader: LIGHTING_SHADER, - shader_defs: vec![], - entry_point: "fragment".into(), - targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), - blend: None, - write_mask: ColorWrites::ALL, - })], - }), - primitive: PrimitiveState::default(), - depth_stencil: None, - multisample: MultisampleState::default(), - push_constant_ranges: vec![], - }); + Self { layout, sampler } + } +} + +impl SpecializedRenderPipeline for LightingPipeline { + type Key = LightingPipelineKey; - Self { - layout, - sampler, - pipeline_id, + fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { + RenderPipelineDescriptor { + label: Some(LIGHTING_PIPELINE.into()), + layout: vec![self.layout.clone()], + vertex: fullscreen_shader_vertex_state(), + fragment: Some(FragmentState { + shader: LIGHTING_SHADER, + shader_defs: vec![], + entry_point: "fragment".into(), + targets: vec![Some(ColorTargetState { + format: if key.hdr { + ViewTarget::TEXTURE_FORMAT_HDR + } else { + TextureFormat::bevy_default() + }, + blend: None, + write_mask: ColorWrites::ALL, + })], + }), + primitive: PrimitiveState::default(), + depth_stencil: None, + multisample: MultisampleState::default(), + push_constant_ranges: vec![], } } } diff --git a/src/render/lighting/prepare.rs b/src/render/lighting/prepare.rs new file mode 100644 index 0000000..595e41d --- /dev/null +++ b/src/render/lighting/prepare.rs @@ -0,0 +1,35 @@ +use bevy::{ + ecs::{ + entity::Entity, + query::With, + system::{Commands, Query, Res, ResMut}, + }, + render::{ + render_resource::{PipelineCache, SpecializedRenderPipelines}, + view::ExtractedView, + }, +}; + +use crate::render::extract::ExtractedAmbientLight2d; + +use super::{LightingPipeline, LightingPipelineId, LightingPipelineKey}; + +pub fn prepare_lighting_pipelines( + mut commands: Commands, + pipeline_cache: Res, + mut pipelines: ResMut>, + lighting_pipeline: Res, + view_targets: Query<(Entity, &ExtractedView), With>, +) { + for (entity, view) in view_targets.iter() { + let pipeline_id = pipelines.specialize( + &pipeline_cache, + &lighting_pipeline, + LightingPipelineKey { hdr: view.hdr }, + ); + + commands + .entity(entity) + .insert(LightingPipelineId(pipeline_id)); + } +} From dcfa3f0672e9e310068a4af380beed9df32d89fe Mon Sep 17 00:00:00 2001 From: James Gayfer <10660608+jgayfer@users.noreply.github.com> Date: Fri, 14 Jun 2024 21:18:41 -0700 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9eaa60..2dfc0ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - WebGL2 support (#7). +### Fixed + +- Crash when HDR was enabled (#11). + ## [0.1.3] - 2024-06-02 ### Fixed