Skip to content

Commit

Permalink
Add HDR support (#11)
Browse files Browse the repository at this point in the history
* 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.

* Update CHANGELOG.md
  • Loading branch information
jgayfer authored Jun 15, 2024
1 parent d4c837c commit aee087c
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 40 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 11 additions & 2 deletions src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};

Expand All @@ -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,
},
},
};

Expand Down Expand Up @@ -47,10 +51,15 @@ impl Plugin for Light2dPlugin {
};

render_app
.init_resource::<SpecializedRenderPipelines<LightingPipeline>>()
.add_systems(
ExtractSchedule,
(extract_point_lights, extract_ambient_lights),
)
.add_systems(
Render,
prepare_lighting_pipelines.in_set(RenderSet::Prepare),
)
.add_render_graph_node::<ViewNodeRunner<LightingNode>>(Core2d, LightingPass)
.add_render_graph_edge(Core2d, Node2d::MainPass, LightingPass);
}
Expand Down
16 changes: 15 additions & 1 deletion src/render/lighting/mod.rs
Original file line number Diff line number Diff line change
@@ -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<Shader> =
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);
11 changes: 7 additions & 4 deletions src/render/lighting/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -25,21 +25,24 @@ impl ViewNode for LightingNode {
&'static ViewTarget,
&'static DynamicUniformIndex<ExtractedAmbientLight2d>,
&'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::<LightingPipeline>();

let pipeline_cache = world.resource::<PipelineCache>();

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(());
};

Expand Down
68 changes: 35 additions & 33 deletions src/render/lighting/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 {
Expand All @@ -45,33 +44,36 @@ impl FromWorld for LightingPipeline {

let sampler = render_device.create_sampler(&SamplerDescriptor::default());

let pipeline_id =
world
.resource_mut::<PipelineCache>()
.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![],
}
}
}
35 changes: 35 additions & 0 deletions src/render/lighting/prepare.rs
Original file line number Diff line number Diff line change
@@ -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<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<LightingPipeline>>,
lighting_pipeline: Res<LightingPipeline>,
view_targets: Query<(Entity, &ExtractedView), With<ExtractedAmbientLight2d>>,
) {
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));
}
}

0 comments on commit aee087c

Please sign in to comment.