Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HDR support #11

Merged
merged 2 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
}
}
Loading