Skip to content

Commit

Permalink
Use separate node for SDF passo
Browse files Browse the repository at this point in the history
Not strictly necessary, but I do like having an explicit render graph,
especially if it's something someone else ever wanted to tweak.
  • Loading branch information
jgayfer committed Aug 1, 2024
1 parent d2d1142 commit f23311e
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 114 deletions.
8 changes: 5 additions & 3 deletions src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ use crate::{
},
lighting::{
prepare_lighting_auxiliary_textures, prepare_lighting_pipelines, LightMapPipeline,
LightingNode, LightingPass, LightingPipeline, SdfPipeline, LIGHTING_SHADER,
LIGHT_MAP_SHADER, SDF_SHADER, TYPES_SHADER,
LightingNode, LightingPass, LightingPipeline, LIGHTING_SHADER, LIGHT_MAP_SHADER,
TYPES_SHADER,
},
sdf::{SdfNode, SdfPass, SdfPipeline, SDF_SHADER},
},
};

Expand Down Expand Up @@ -99,7 +100,8 @@ impl Plugin for Light2dPlugin {
),
)
.add_render_graph_node::<ViewNodeRunner<LightingNode>>(Core2d, LightingPass)
.add_render_graph_edge(Core2d, Node2d::EndMainPass, LightingPass);
.add_render_graph_node::<ViewNodeRunner<SdfNode>>(Core2d, SdfPass)
.add_render_graph_edges(Core2d, (Node2d::EndMainPass, SdfPass, LightingPass));
}

fn finish(&self, app: &mut App) {
Expand Down
2 changes: 0 additions & 2 deletions src/render/lighting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ pub use prepare::*;

pub const TYPES_SHADER: Handle<Shader> =
Handle::weak_from_u128(134542958402584092759402858489640143033);
pub const SDF_SHADER: Handle<Shader> =
Handle::weak_from_u128(231804371047309214783091483091843019281);
pub const LIGHTING_SHADER: Handle<Shader> =
Handle::weak_from_u128(111120241052143214281687226997564407636);
pub const LIGHT_MAP_SHADER: Handle<Shader> =
Expand Down
56 changes: 2 additions & 54 deletions src/render/lighting/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,10 @@ use bevy::render::renderer::RenderDevice;
use bevy::render::view::{ViewTarget, ViewUniformOffset, ViewUniforms};
use smallvec::{smallvec, SmallVec};

use crate::render::extract::{
ExtractedAmbientLight2d, ExtractedLightOccluder2d, ExtractedPointLight2d,
};
use crate::render::extract::{ExtractedAmbientLight2d, ExtractedPointLight2d};

use super::{
LightMapPipeline, Lighting2dAuxiliaryTextures, LightingPipeline, LightingPipelineId,
SdfPipeline,
};
use super::{LightMapPipeline, Lighting2dAuxiliaryTextures, LightingPipeline, LightingPipelineId};

const SDF_PASS: &str = "sdf_pass";
const SDF_BIND_GROUP: &str = "sdf_bind_group";
const LIGHT_MAP_PASS: &str = "light_map_pass";
const LIGHT_MAP_BIND_GROUP: &str = "light_map_bind_group";
const LIGHTING_PASS: &str = "lighting_pass";
Expand Down Expand Up @@ -49,21 +42,17 @@ impl ViewNode for LightingNode {
>,
world: &'w World,
) -> Result<(), bevy::render::render_graph::NodeRunError> {
let sdf_pipeline_resource = world.resource::<SdfPipeline>();
let light_map_pipeline_resource = world.resource::<LightMapPipeline>();
let pipeline = world.resource::<LightingPipeline>();
let pipeline_cache = world.resource::<PipelineCache>();

let (
Some(sdf_pipeline),
Some(lighting_pipeline),
Some(light_map_pipeline),
Some(view_uniform_binding),
Some(ambient_light_uniform),
Some(point_light_binding),
Some(light_occluders_binding),
) = (
pipeline_cache.get_render_pipeline(sdf_pipeline_resource.pipeline_id),
pipeline_cache.get_render_pipeline(pipeline_id.0),
pipeline_cache.get_render_pipeline(light_map_pipeline_resource.pipeline_id),
world.resource::<ViewUniforms>().uniforms.binding(),
Expand All @@ -74,52 +63,11 @@ impl ViewNode for LightingNode {
world
.resource::<GpuArrayBuffer<ExtractedPointLight2d>>()
.binding(),
world
.resource::<GpuArrayBuffer<ExtractedLightOccluder2d>>()
.binding(),
)
else {
return Ok(());
};

// SDF
let bind_group = render_context.render_device().create_bind_group(
SDF_BIND_GROUP,
&sdf_pipeline_resource.layout,
&BindGroupEntries::sequential((view_uniform_binding.clone(), light_occluders_binding)),
);

let mut sdf_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some(SDF_PASS),
color_attachments: &[Some(RenderPassColorAttachment {
view: &aux_textures.sdf.default_view,
resolve_target: None,
ops: Operations::default(),
})],
..default()
});

let mut dynamic_offsets: SmallVec<[u32; 3]> = smallvec![view_offset.offset];

// Storage buffers aren't available in WebGL2. We fall back to a
// dynamic uniform buffer, and therefore need to provide the offset.
// We're providing a value of 0 here as we're limiting the number of
// point lights to only those that can reasonably fit in a single binding.
if world
.resource::<RenderDevice>()
.limits()
.max_storage_buffers_per_shader_stage
== 0
{
dynamic_offsets.push(0);
}

sdf_pass.set_render_pipeline(sdf_pipeline);
sdf_pass.set_bind_group(0, &bind_group, &dynamic_offsets);
sdf_pass.draw(0..3, 0..1);

drop(sdf_pass);

// Light map
let light_map_bind_group = render_context.render_device().create_bind_group(
LIGHT_MAP_BIND_GROUP,
Expand Down
57 changes: 2 additions & 55 deletions src/render/lighting/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,68 +11,15 @@ use bevy::render::renderer::RenderDevice;
use bevy::render::texture::BevyDefault;
use bevy::render::view::{ViewTarget, ViewUniform};

use crate::render::extract::{
ExtractedAmbientLight2d, ExtractedLightOccluder2d, ExtractedPointLight2d,
};
use crate::render::extract::{ExtractedAmbientLight2d, ExtractedPointLight2d};

use super::{LightingPipelineKey, LIGHTING_SHADER, LIGHT_MAP_SHADER, SDF_SHADER};
use super::{LightingPipelineKey, LIGHTING_SHADER, LIGHT_MAP_SHADER};

const SDF_PIPELINE: &str = "sdf_pipeline";
const SDF_BIND_GROUP_LAYOUT: &str = "sdf_bind_group_layout";
const LIGHTING_PIPELINE: &str = "lighting_pipeline";
const LIGHTING_BIND_GROUP_LAYOUT: &str = "lighting_bind_group_layout";
const LIGHT_MAP_BIND_GROUP_LAYOUT: &str = "light_map_group_layout";
const LIGHT_MAP_PIPELINE: &str = "light_map_pipeline";

#[derive(Resource)]
pub struct SdfPipeline {
pub layout: BindGroupLayout,
pub pipeline_id: CachedRenderPipelineId,
}

impl FromWorld for SdfPipeline {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
let pipeline_cache = world.resource::<PipelineCache>();

let layout = render_device.create_bind_group_layout(
SDF_BIND_GROUP_LAYOUT,
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
uniform_buffer::<ViewUniform>(true),
GpuArrayBuffer::<ExtractedLightOccluder2d>::binding_layout(render_device),
),
),
);

let pipeline_id = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
label: Some(SDF_PIPELINE.into()),
layout: vec![layout.clone()],
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
shader: SDF_SHADER,
shader_defs: vec![],
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {
format: TextureFormat::Rgba16Float,
blend: None,
write_mask: ColorWrites::ALL,
})],
}),
primitive: PrimitiveState::default(),
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: vec![],
});

Self {
layout,
pipeline_id,
}
}
}

#[derive(Resource)]
pub struct LightMapPipeline {
pub layout: BindGroupLayout,
Expand Down
1 change: 1 addition & 0 deletions src/render/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod extract;
pub mod lighting;
pub mod sdf;
16 changes: 16 additions & 0 deletions src/render/sdf/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mod node;
mod pipeline;

use bevy::{
asset::Handle,
render::{render_graph::RenderLabel, render_resource::Shader},
};

pub use node::SdfNode;
pub use pipeline::SdfPipeline;

pub const SDF_SHADER: Handle<Shader> =
Handle::weak_from_u128(231804371047309214783091483091843019281);

#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
pub struct SdfPass;
85 changes: 85 additions & 0 deletions src/render/sdf/node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use bevy::ecs::system::lifetimeless::Read;
use bevy::prelude::*;
use bevy::render::render_graph::ViewNode;

use bevy::render::render_resource::{
BindGroupEntries, GpuArrayBuffer, Operations, PipelineCache, RenderPassColorAttachment,
RenderPassDescriptor,
};
use bevy::render::renderer::RenderDevice;
use bevy::render::view::{ViewUniformOffset, ViewUniforms};
use smallvec::{smallvec, SmallVec};

use crate::render::extract::ExtractedLightOccluder2d;

use crate::render::lighting::Lighting2dAuxiliaryTextures;

use super::pipeline::SdfPipeline;

const SDF_PASS: &str = "sdf_pass";
const SDF_BIND_GROUP: &str = "sdf_bind_group";

#[derive(Default)]
pub struct SdfNode;

impl ViewNode for SdfNode {
type ViewQuery = (Read<ViewUniformOffset>, Read<Lighting2dAuxiliaryTextures>);

fn run<'w>(
&self,
_graph: &mut bevy::render::render_graph::RenderGraphContext,
render_context: &mut bevy::render::renderer::RenderContext<'w>,
(view_offset, aux_textures): bevy::ecs::query::QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), bevy::render::render_graph::NodeRunError> {
let sdf_pipeline = world.resource::<SdfPipeline>();
let pipeline_cache = world.resource::<PipelineCache>();

let (Some(pipeline), Some(view_uniform_binding), Some(light_occluders_binding)) = (
pipeline_cache.get_render_pipeline(sdf_pipeline.pipeline_id),
world.resource::<ViewUniforms>().uniforms.binding(),
world
.resource::<GpuArrayBuffer<ExtractedLightOccluder2d>>()
.binding(),
) else {
return Ok(());
};

let bind_group = render_context.render_device().create_bind_group(
SDF_BIND_GROUP,
&sdf_pipeline.layout,
&BindGroupEntries::sequential((view_uniform_binding.clone(), light_occluders_binding)),
);

let mut sdf_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some(SDF_PASS),
color_attachments: &[Some(RenderPassColorAttachment {
view: &aux_textures.sdf.default_view,
resolve_target: None,
ops: Operations::default(),
})],
..default()
});

let mut dynamic_offsets: SmallVec<[u32; 3]> = smallvec![view_offset.offset];

// Storage buffers aren't available in WebGL2. We fall back to a
// dynamic uniform buffer, and therefore need to provide the offset.
// We're providing a value of 0 here as we're limiting the number of
// point lights to only those that can reasonably fit in a single binding.
if world
.resource::<RenderDevice>()
.limits()
.max_storage_buffers_per_shader_stage
== 0
{
dynamic_offsets.push(0);
}

sdf_pass.set_render_pipeline(pipeline);
sdf_pass.set_bind_group(0, &bind_group, &dynamic_offsets);
sdf_pass.draw(0..3, 0..1);

Ok(())
}
}
66 changes: 66 additions & 0 deletions src/render/sdf/pipeline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use bevy::core_pipeline::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
use bevy::prelude::*;
use bevy::render::render_resource::binding_types::uniform_buffer;
use bevy::render::render_resource::{
BindGroupLayout, BindGroupLayoutEntries, CachedRenderPipelineId, ColorTargetState, ColorWrites,
FragmentState, GpuArrayBuffer, MultisampleState, PipelineCache, PrimitiveState,
RenderPipelineDescriptor, ShaderStages, TextureFormat,
};
use bevy::render::renderer::RenderDevice;
use bevy::render::view::ViewUniform;

use crate::render::extract::ExtractedLightOccluder2d;

use super::SDF_SHADER;

const SDF_PIPELINE: &str = "sdf_pipeline";
const SDF_BIND_GROUP_LAYOUT: &str = "sdf_bind_group_layout";

#[derive(Resource)]
pub struct SdfPipeline {
pub layout: BindGroupLayout,
pub pipeline_id: CachedRenderPipelineId,
}

impl FromWorld for SdfPipeline {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
let pipeline_cache = world.resource::<PipelineCache>();

let layout = render_device.create_bind_group_layout(
SDF_BIND_GROUP_LAYOUT,
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
uniform_buffer::<ViewUniform>(true),
GpuArrayBuffer::<ExtractedLightOccluder2d>::binding_layout(render_device),
),
),
);

let pipeline_id = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
label: Some(SDF_PIPELINE.into()),
layout: vec![layout.clone()],
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
shader: SDF_SHADER,
shader_defs: vec![],
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {
format: TextureFormat::Rgba16Float,
blend: None,
write_mask: ColorWrites::ALL,
})],
}),
primitive: PrimitiveState::default(),
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: vec![],
});

Self {
layout,
pipeline_id,
}
}
}

0 comments on commit f23311e

Please sign in to comment.