Replace FULLSCREEN_SHADER_HANDLE with a FullscreenShader resource. (#19426)

# Objective

- Related to #19024.

## Solution

- Remove the `FULLSCREEN_SHADER_HANDLE` `weak_handle` with a resource
holding the shader handle.
- This also changes us from using `load_internal_asset` to
`embedded_asset`/`load_embedded_asset`.
- All uses have been migrated to clone the `FullscreenShader` resource
and use its `to_vertex_state` method.

## Testing

- `anti_aliasing` example still works.
- `bloom_3d` example still works.

---------

Co-authored-by: charlotte 🌸 <charlotte.c.mcelwain@gmail.com>
This commit is contained in:
andriyDev 2025-06-23 17:02:23 -07:00 committed by GitHub
parent 9f376e2537
commit a7fdd6fc6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 255 additions and 120 deletions

View File

@ -3,7 +3,7 @@ use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
FullscreenShader,
};
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_image::BevyDefault as _;
@ -163,7 +163,8 @@ impl Plugin for CasPlugin {
pub struct CasPipeline {
texture_bind_group: BindGroupLayout,
sampler: Sampler,
shader: Handle<Shader>,
fullscreen_shader: FullscreenShader,
fragment_shader: Handle<Shader>,
}
impl FromWorld for CasPipeline {
@ -187,7 +188,11 @@ impl FromWorld for CasPipeline {
CasPipeline {
texture_bind_group,
sampler,
shader: load_embedded_asset!(render_world, "robust_contrast_adaptive_sharpening.wgsl"),
fullscreen_shader: render_world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(
render_world,
"robust_contrast_adaptive_sharpening.wgsl"
),
}
}
}
@ -209,9 +214,9 @@ impl SpecializedRenderPipeline for CasPipeline {
RenderPipelineDescriptor {
label: Some("contrast_adaptive_sharpening".into()),
layout: vec![self.texture_bind_group.clone()],
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs,
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {

View File

@ -3,7 +3,7 @@ use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
FullscreenShader,
};
use bevy_ecs::prelude::*;
use bevy_image::BevyDefault as _;
@ -130,7 +130,8 @@ impl Plugin for FxaaPlugin {
pub struct FxaaPipeline {
texture_bind_group: BindGroupLayout,
sampler: Sampler,
shader: Handle<Shader>,
fullscreen_shader: FullscreenShader,
fragment_shader: Handle<Shader>,
}
impl FromWorld for FxaaPipeline {
@ -157,7 +158,8 @@ impl FromWorld for FxaaPipeline {
FxaaPipeline {
texture_bind_group,
sampler,
shader: load_embedded_asset!(render_world, "fxaa.wgsl"),
fullscreen_shader: render_world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(render_world, "fxaa.wgsl"),
}
}
}
@ -181,9 +183,9 @@ impl SpecializedRenderPipeline for FxaaPipeline {
RenderPipelineDescriptor {
label: Some("fxaa".into()),
layout: vec![self.texture_bind_group.clone()],
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs: vec![
format!("EDGE_THRESH_{}", key.edge_threshold.get_str()).into(),
format!("EDGE_THRESH_MIN_{}", key.edge_threshold_min.get_str()).into(),

View File

@ -2,9 +2,9 @@ use bevy_app::{App, Plugin};
use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
prelude::Camera3d,
prepass::{DepthPrepass, MotionVectorPrepass, ViewPrepassTextures},
FullscreenShader,
};
use bevy_diagnostic::FrameCount;
use bevy_ecs::{
@ -238,7 +238,8 @@ struct TaaPipeline {
taa_bind_group_layout: BindGroupLayout,
nearest_sampler: Sampler,
linear_sampler: Sampler,
shader: Handle<Shader>,
fullscreen_shader: FullscreenShader,
fragment_shader: Handle<Shader>,
}
impl FromWorld for TaaPipeline {
@ -283,7 +284,8 @@ impl FromWorld for TaaPipeline {
taa_bind_group_layout,
nearest_sampler,
linear_sampler,
shader: load_embedded_asset!(world, "taa.wgsl"),
fullscreen_shader: world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(world, "taa.wgsl"),
}
}
}
@ -314,9 +316,9 @@ impl SpecializedRenderPipeline for TaaPipeline {
RenderPipelineDescriptor {
label: Some("taa_pipeline".into()),
layout: vec![self.taa_bind_group_layout.clone()],
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs,
entry_point: "taa".into(),
targets: vec![

View File

@ -10,7 +10,7 @@ use bevy_render::{
RenderApp,
};
use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
use crate::FullscreenShader;
/// Adds support for specialized "blit pipelines", which can be used to write one texture to another.
pub struct BlitPlugin;
@ -38,7 +38,8 @@ impl Plugin for BlitPlugin {
pub struct BlitPipeline {
pub texture_bind_group: BindGroupLayout,
pub sampler: Sampler,
pub shader: Handle<Shader>,
pub fullscreen_shader: FullscreenShader,
pub fragment_shader: Handle<Shader>,
}
impl FromWorld for BlitPipeline {
@ -61,7 +62,8 @@ impl FromWorld for BlitPipeline {
BlitPipeline {
texture_bind_group,
sampler,
shader: load_embedded_asset!(render_world, "blit.wgsl"),
fullscreen_shader: render_world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(render_world, "blit.wgsl"),
}
}
}
@ -80,9 +82,9 @@ impl SpecializedRenderPipeline for BlitPipeline {
RenderPipelineDescriptor {
label: Some("blit pipeline".into()),
layout: vec![self.texture_bind_group.clone()],
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs: vec![],
entry_point: "fs_main".into(),
targets: vec![Some(ColorTargetState {

View File

@ -1,5 +1,6 @@
use crate::FullscreenShader;
use super::{Bloom, BLOOM_TEXTURE_FORMAT};
use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
use bevy_asset::{load_embedded_asset, Handle};
use bevy_ecs::{
prelude::{Component, Entity},
@ -27,8 +28,10 @@ pub struct BloomDownsamplingPipeline {
/// Layout with a texture, a sampler, and uniforms
pub bind_group_layout: BindGroupLayout,
pub sampler: Sampler,
/// The shader asset handle.
pub shader: Handle<Shader>,
/// The asset handle for the fullscreen vertex shader.
pub fullscreen_shader: FullscreenShader,
/// The fragment shader asset handle.
pub fragment_shader: Handle<Shader>,
}
#[derive(PartialEq, Eq, Hash, Clone)]
@ -81,7 +84,8 @@ impl FromWorld for BloomDownsamplingPipeline {
BloomDownsamplingPipeline {
bind_group_layout,
sampler,
shader: load_embedded_asset!(world, "bloom.wgsl"),
fullscreen_shader: world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(world, "bloom.wgsl"),
}
}
}
@ -122,9 +126,9 @@ impl SpecializedRenderPipeline for BloomDownsamplingPipeline {
.into(),
),
layout,
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs,
entry_point,
targets: vec![Some(ColorTargetState {

View File

@ -1,7 +1,8 @@
use crate::FullscreenShader;
use super::{
downsampling_pipeline::BloomUniforms, Bloom, BloomCompositeMode, BLOOM_TEXTURE_FORMAT,
};
use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
use bevy_asset::{load_embedded_asset, Handle};
use bevy_ecs::{
prelude::{Component, Entity},
@ -27,8 +28,10 @@ pub struct UpsamplingPipelineIds {
#[derive(Resource)]
pub struct BloomUpsamplingPipeline {
pub bind_group_layout: BindGroupLayout,
/// The shader asset handle.
pub shader: Handle<Shader>,
/// The asset handle for the fullscreen vertex shader.
pub fullscreen_shader: FullscreenShader,
/// The fragment shader asset handle.
pub fragment_shader: Handle<Shader>,
}
#[derive(PartialEq, Eq, Hash, Clone)]
@ -58,7 +61,8 @@ impl FromWorld for BloomUpsamplingPipeline {
BloomUpsamplingPipeline {
bind_group_layout,
shader: load_embedded_asset!(world, "bloom.wgsl"),
fullscreen_shader: world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(world, "bloom.wgsl"),
}
}
}
@ -108,9 +112,9 @@ impl SpecializedRenderPipeline for BloomUpsamplingPipeline {
RenderPipelineDescriptor {
label: Some("bloom_upsampling_pipeline".into()),
layout: vec![self.bind_group_layout.clone()],
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs: vec![],
entry_point: "upsample".into(),
targets: vec![Some(ColorTargetState {

View File

@ -1,6 +1,6 @@
use crate::{
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
prepass::{DeferredPrepass, ViewPrepassTextures},
FullscreenShader,
};
use bevy_app::prelude::*;
use bevy_asset::{embedded_asset, load_embedded_asset};
@ -130,6 +130,7 @@ impl FromWorld for CopyDeferredLightingIdPipeline {
),
);
let vertex_state = world.resource::<FullscreenShader>().to_vertex_state();
let shader = load_embedded_asset!(world, "copy_deferred_lighting_id.wgsl");
let pipeline_id =
@ -138,7 +139,7 @@ impl FromWorld for CopyDeferredLightingIdPipeline {
.queue_render_pipeline(RenderPipelineDescriptor {
label: Some("copy_deferred_lighting_id_pipeline".into()),
layout: vec![layout.clone()],
vertex: fullscreen_shader_vertex_state(),
vertex: vertex_state,
fragment: Some(FragmentState {
shader,
shader_defs: vec![],

View File

@ -66,7 +66,7 @@ use crate::{
graph::{Core3d, Node3d},
Camera3d, DEPTH_TEXTURE_SAMPLING_SUPPORTED,
},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
FullscreenShader,
};
/// A plugin that adds support for the depth of field effect to Bevy.
@ -325,8 +325,10 @@ pub struct DepthOfFieldPipeline {
/// The bind group layout shared among all invocations of the depth of field
/// shader.
global_bind_group_layout: BindGroupLayout,
/// The shader asset handle.
shader: Handle<Shader>,
/// The asset handle for the fullscreen vertex shader.
fullscreen_shader: FullscreenShader,
/// The fragment shader asset handle.
fragment_shader: Handle<Shader>,
}
impl ViewNode for DepthOfFieldNode {
@ -678,13 +680,15 @@ pub fn prepare_depth_of_field_pipelines(
&ViewDepthOfFieldBindGroupLayouts,
&Msaa,
)>,
fullscreen_shader: Res<FullscreenShader>,
asset_server: Res<AssetServer>,
) {
for (entity, view, depth_of_field, view_bind_group_layouts, msaa) in view_targets.iter() {
let dof_pipeline = DepthOfFieldPipeline {
view_bind_group_layouts: view_bind_group_layouts.clone(),
global_bind_group_layout: global_bind_group_layout.layout.clone(),
shader: load_embedded_asset!(asset_server.as_ref(), "dof.wgsl"),
fullscreen_shader: fullscreen_shader.clone(),
fragment_shader: load_embedded_asset!(asset_server.as_ref(), "dof.wgsl"),
};
// We'll need these two flags to create the `DepthOfFieldPipelineKey`s.
@ -797,12 +801,12 @@ impl SpecializedRenderPipeline for DepthOfFieldPipeline {
label: Some("depth of field pipeline".into()),
layout,
push_constant_ranges: vec![],
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
primitive: default(),
depth_stencil: None,
multisample: default(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs,
entry_point: match key.pass {
DofPass::GaussianHorizontal => "gaussian_horizontal".into(),

View File

@ -1,25 +1,40 @@
use bevy_asset::{weak_handle, Handle};
use bevy_asset::{load_embedded_asset, Handle};
use bevy_ecs::{resource::Resource, world::FromWorld};
use bevy_render::{prelude::Shader, render_resource::VertexState};
pub const FULLSCREEN_SHADER_HANDLE: Handle<Shader> =
weak_handle!("481fb759-d0b1-4175-8319-c439acde30a2");
/// A shader that renders to the whole screen. Useful for post-processing.
#[derive(Resource, Clone)]
pub struct FullscreenShader(Handle<Shader>);
/// uses the [`FULLSCREEN_SHADER_HANDLE`] to output a
/// ```wgsl
/// struct FullscreenVertexOutput {
/// [[builtin(position)]]
/// position: vec4<f32>;
/// [[location(0)]]
/// uv: vec2<f32>;
/// };
/// ```
/// from the vertex shader.
/// The draw call should render one triangle: `render_pass.draw(0..3, 0..1);`
pub fn fullscreen_shader_vertex_state() -> VertexState {
VertexState {
shader: FULLSCREEN_SHADER_HANDLE,
shader_defs: Vec::new(),
entry_point: "fullscreen_vertex_shader".into(),
buffers: Vec::new(),
impl FromWorld for FullscreenShader {
fn from_world(world: &mut bevy_ecs::world::World) -> Self {
Self(load_embedded_asset!(world, "fullscreen.wgsl"))
}
}
impl FullscreenShader {
/// Gets the raw shader handle.
pub fn shader(&self) -> Handle<Shader> {
self.0.clone()
}
/// Creates a [`VertexState`] that uses the [`FullscreenShader`] to output a
/// ```wgsl
/// struct FullscreenVertexOutput {
/// @builtin(position)
/// position: vec4<f32>;
/// @location(0)
/// uv: vec2<f32>;
/// };
/// ```
/// from the vertex shader.
/// The draw call should render one triangle: `render_pass.draw(0..3, 0..1);`
pub fn to_vertex_state(&self) -> VertexState {
VertexState {
shader: self.0.clone(),
shader_defs: Vec::new(),
entry_point: "fullscreen_vertex_shader".into(),
buffers: Vec::new(),
}
}
}

View File

@ -14,18 +14,20 @@ pub mod core_3d;
pub mod deferred;
pub mod dof;
pub mod experimental;
pub mod fullscreen_vertex_shader;
pub mod motion_blur;
pub mod msaa_writeback;
pub mod oit;
pub mod post_process;
pub mod prepass;
mod skybox;
pub mod tonemapping;
pub mod upscaling;
pub use fullscreen_vertex_shader::FullscreenShader;
pub use skybox::Skybox;
mod fullscreen_vertex_shader;
mod skybox;
/// The core pipeline prelude.
///
/// This includes the most common types in this crate, re-exported for your convenience.
@ -42,7 +44,6 @@ use crate::{
deferred::copy_lighting_id::CopyDeferredLightingIdPlugin,
dof::DepthOfFieldPlugin,
experimental::mip_generation::MipGenerationPlugin,
fullscreen_vertex_shader::FULLSCREEN_SHADER_HANDLE,
motion_blur::MotionBlurPlugin,
msaa_writeback::MsaaWritebackPlugin,
post_process::PostProcessingPlugin,
@ -51,8 +52,8 @@ use crate::{
upscaling::UpscalingPlugin,
};
use bevy_app::{App, Plugin};
use bevy_asset::load_internal_asset;
use bevy_render::prelude::Shader;
use bevy_asset::embedded_asset;
use bevy_render::RenderApp;
use oit::OrderIndependentTransparencyPlugin;
#[derive(Default)]
@ -60,17 +61,13 @@ pub struct CorePipelinePlugin;
impl Plugin for CorePipelinePlugin {
fn build(&self, app: &mut App) {
load_internal_asset!(
app,
FULLSCREEN_SHADER_HANDLE,
"fullscreen_vertex_shader/fullscreen.wgsl",
Shader::from_wgsl
);
embedded_asset!(app, "fullscreen_vertex_shader/fullscreen.wgsl");
app.register_type::<DepthPrepass>()
.register_type::<NormalPrepass>()
.register_type::<MotionVectorPrepass>()
.register_type::<DeferredPrepass>()
.init_resource::<FullscreenShader>()
.add_plugins((Core2dPlugin, Core3dPlugin, CopyDeferredLightingIdPlugin))
.add_plugins((
BlitPlugin,
@ -85,4 +82,11 @@ impl Plugin for CorePipelinePlugin {
MipGenerationPlugin,
));
}
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.init_resource::<FullscreenShader>();
}
}

View File

@ -25,7 +25,7 @@ use bevy_render::{
view::{ExtractedView, Msaa, ViewTarget},
};
use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
use crate::FullscreenShader;
use super::MotionBlurUniform;
@ -34,11 +34,16 @@ pub struct MotionBlurPipeline {
pub(crate) sampler: Sampler,
pub(crate) layout: BindGroupLayout,
pub(crate) layout_msaa: BindGroupLayout,
pub(crate) shader: Handle<Shader>,
pub(crate) fullscreen_shader: FullscreenShader,
pub(crate) fragment_shader: Handle<Shader>,
}
impl MotionBlurPipeline {
pub(crate) fn new(render_device: &RenderDevice, shader: Handle<Shader>) -> Self {
pub(crate) fn new(
render_device: &RenderDevice,
fullscreen_shader: FullscreenShader,
fragment_shader: Handle<Shader>,
) -> Self {
let mb_layout = &BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
@ -84,7 +89,8 @@ impl MotionBlurPipeline {
sampler,
layout,
layout_msaa,
shader,
fullscreen_shader,
fragment_shader,
}
}
}
@ -93,8 +99,9 @@ impl FromWorld for MotionBlurPipeline {
fn from_world(render_world: &mut bevy_ecs::world::World) -> Self {
let render_device = render_world.resource::<RenderDevice>().clone();
let shader = load_embedded_asset!(render_world, "motion_blur.wgsl");
MotionBlurPipeline::new(&render_device, shader)
let fullscreen_shader = render_world.resource::<FullscreenShader>().clone();
let fragment_shader = load_embedded_asset!(render_world, "motion_blur.wgsl");
MotionBlurPipeline::new(&render_device, fullscreen_shader, fragment_shader)
}
}
@ -128,9 +135,9 @@ impl SpecializedRenderPipeline for MotionBlurPipeline {
RenderPipelineDescriptor {
label: Some("motion_blur_pipeline".into()),
layout,
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs,
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {

View File

@ -1,7 +1,4 @@
use crate::{
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
oit::OrderIndependentTransparencySettings,
};
use crate::{oit::OrderIndependentTransparencySettings, FullscreenShader};
use bevy_app::Plugin;
use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer};
use bevy_derive::Deref;
@ -156,6 +153,7 @@ pub fn queue_oit_resolve_pipeline(
),
With<OrderIndependentTransparencySettings>,
>,
fullscreen_shader: Res<FullscreenShader>,
asset_server: Res<AssetServer>,
// Store the key with the id to make the clean up logic easier.
// This also means it will always replace the entry if the key changes so nothing to clean up.
@ -176,7 +174,12 @@ pub fn queue_oit_resolve_pipeline(
}
}
let desc = specialize_oit_resolve_pipeline(key, &resolve_pipeline, &asset_server);
let desc = specialize_oit_resolve_pipeline(
key,
&resolve_pipeline,
&fullscreen_shader,
&asset_server,
);
let pipeline_id = pipeline_cache.queue_render_pipeline(desc);
commands.entity(e).insert(OitResolvePipelineId(pipeline_id));
@ -194,6 +197,7 @@ pub fn queue_oit_resolve_pipeline(
fn specialize_oit_resolve_pipeline(
key: OitResolvePipelineKey,
resolve_pipeline: &OitResolvePipeline,
fullscreen_shader: &FullscreenShader,
asset_server: &AssetServer,
) -> RenderPipelineDescriptor {
let format = if key.hdr {
@ -224,7 +228,7 @@ fn specialize_oit_resolve_pipeline(
write_mask: ColorWrites::ALL,
})],
}),
vertex: fullscreen_shader_vertex_state(),
vertex: fullscreen_shader.to_vertex_state(),
primitive: PrimitiveState::default(),
depth_stencil: None,
multisample: MultisampleState::default(),

View File

@ -44,7 +44,7 @@ use bevy_utils::prelude::default;
use crate::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader,
FullscreenShader,
};
/// The handle to the default chromatic aberration lookup texture.
@ -130,8 +130,10 @@ pub struct PostProcessingPipeline {
source_sampler: Sampler,
/// Specifies how to sample the chromatic aberration gradient.
chromatic_aberration_lut_sampler: Sampler,
/// The shader asset handle.
shader: Handle<Shader>,
/// The asset handle for the fullscreen vertex shader.
fullscreen_shader: FullscreenShader,
/// The fragment shader asset handle.
fragment_shader: Handle<Shader>,
}
/// A key that uniquely identifies a built-in postprocessing pipeline.
@ -308,7 +310,8 @@ impl FromWorld for PostProcessingPipeline {
bind_group_layout,
source_sampler,
chromatic_aberration_lut_sampler,
shader: load_embedded_asset!(world, "post_process.wgsl"),
fullscreen_shader: world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(world, "post_process.wgsl"),
}
}
}
@ -320,9 +323,9 @@ impl SpecializedRenderPipeline for PostProcessingPipeline {
RenderPipelineDescriptor {
label: Some("postprocessing".into()),
layout: vec![self.bind_group_layout.clone()],
vertex: fullscreen_vertex_shader::fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs: vec![],
entry_point: "fragment_main".into(),
targets: vec![Some(ColorTargetState {

View File

@ -27,7 +27,7 @@ use crate::{
prepass_target_descriptors, MotionVectorPrepass, NormalPrepass, PreviousViewData,
PreviousViewUniforms,
},
Skybox,
FullscreenShader, Skybox,
};
/// This pipeline writes motion vectors to the prepass for all [`Skybox`]es.
@ -38,7 +38,8 @@ use crate::{
#[derive(Resource)]
pub struct SkyboxPrepassPipeline {
bind_group_layout: BindGroupLayout,
shader: Handle<Shader>,
fullscreen_shader: FullscreenShader,
fragment_shader: Handle<Shader>,
}
/// Used to specialize the [`SkyboxPrepassPipeline`].
@ -73,7 +74,8 @@ impl FromWorld for SkyboxPrepassPipeline {
),
),
),
shader: load_embedded_asset!(world, "skybox_prepass.wgsl"),
fullscreen_shader: world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(world, "skybox_prepass.wgsl"),
}
}
}
@ -86,7 +88,7 @@ impl SpecializedRenderPipeline for SkyboxPrepassPipeline {
label: Some("skybox_prepass_pipeline".into()),
layout: vec![self.bind_group_layout.clone()],
push_constant_ranges: vec![],
vertex: crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
primitive: default(),
depth_stencil: Some(DepthStencilState {
format: CORE_3D_DEPTH_FORMAT,
@ -101,7 +103,7 @@ impl SpecializedRenderPipeline for SkyboxPrepassPipeline {
alpha_to_coverage_enabled: false,
},
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs: vec![],
entry_point: "fragment".into(),
targets: prepass_target_descriptors(key.normal_prepass, true, false),

View File

@ -1,4 +1,3 @@
use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
use bevy_app::prelude::*;
use bevy_asset::{embedded_asset, load_embedded_asset, Assets, Handle};
use bevy_ecs::prelude::*;
@ -28,6 +27,8 @@ mod node;
use bevy_utils::default;
pub use node::TonemappingNode;
use crate::FullscreenShader;
/// 3D LUT (look up table) textures used for tonemapping
#[derive(Resource, Clone, ExtractResource)]
pub struct TonemappingLuts {
@ -112,7 +113,8 @@ impl Plugin for TonemappingPlugin {
pub struct TonemappingPipeline {
texture_bind_group: BindGroupLayout,
sampler: Sampler,
shader: Handle<Shader>,
fullscreen_shader: FullscreenShader,
fragment_shader: Handle<Shader>,
}
/// Optionally enables a tonemapping shader that attempts to map linear input stimulus into a perceptually uniform image for a given [`Camera`] entity.
@ -273,9 +275,9 @@ impl SpecializedRenderPipeline for TonemappingPipeline {
RenderPipelineDescriptor {
label: Some("tonemapping pipeline".into()),
layout: vec![self.texture_bind_group.clone()],
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs,
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {
@ -319,7 +321,8 @@ impl FromWorld for TonemappingPipeline {
TonemappingPipeline {
texture_bind_group: tonemap_texture_bind_group,
sampler,
shader: load_embedded_asset!(render_world, "tonemapping.wgsl"),
fullscreen_shader: render_world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(render_world, "tonemapping.wgsl"),
}
}
}

View File

@ -1,7 +1,5 @@
use bevy_asset::{load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_3d::Camera3d, fullscreen_vertex_shader::fullscreen_shader_vertex_state,
};
use bevy_core_pipeline::{core_3d::Camera3d, FullscreenShader};
use bevy_ecs::{
component::Component,
entity::Entity,
@ -36,7 +34,8 @@ pub(crate) struct AtmosphereBindGroupLayouts {
pub(crate) struct RenderSkyBindGroupLayouts {
pub render_sky: BindGroupLayout,
pub render_sky_msaa: BindGroupLayout,
pub shader: Handle<Shader>,
pub fullscreen_shader: FullscreenShader,
pub fragment_shader: Handle<Shader>,
}
impl FromWorld for AtmosphereBindGroupLayouts {
@ -205,7 +204,8 @@ impl FromWorld for RenderSkyBindGroupLayouts {
Self {
render_sky,
render_sky_msaa,
shader: load_embedded_asset!(world, "render_sky.wgsl"),
fullscreen_shader: world.resource::<FullscreenShader>().clone(),
fragment_shader: load_embedded_asset!(world, "render_sky.wgsl"),
}
}
}
@ -358,7 +358,7 @@ impl SpecializedRenderPipeline for RenderSkyBindGroupLayouts {
self.render_sky_msaa.clone()
}],
push_constant_ranges: vec![],
vertex: fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
primitive: PrimitiveState::default(),
depth_stencil: None,
multisample: MultisampleState {
@ -368,7 +368,7 @@ impl SpecializedRenderPipeline for RenderSkyBindGroupLayouts {
},
zero_initialize_workgroup_memory: false,
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs,
entry_point: "main".into(),
targets: vec![Some(ColorTargetState {

View File

@ -2,7 +2,7 @@ use super::resource_manager::ResourceManager;
use bevy_asset::{weak_handle, Handle};
use bevy_core_pipeline::{
core_3d::CORE_3D_DEPTH_FORMAT, experimental::mip_generation::DOWNSAMPLE_DEPTH_SHADER_HANDLE,
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
FullscreenShader,
};
use bevy_ecs::{
resource::Resource,
@ -80,6 +80,8 @@ impl FromWorld for MeshletPipelines {
let remap_1d_to_2d_dispatch_layout = resource_manager
.remap_1d_to_2d_dispatch_bind_group_layout
.clone();
let vertex_state = world.resource::<FullscreenShader>().to_vertex_state();
let pipeline_cache = world.resource_mut::<PipelineCache>();
Self {
@ -400,7 +402,7 @@ impl FromWorld for MeshletPipelines {
label: Some("meshlet_resolve_depth_pipeline".into()),
layout: vec![resolve_depth_layout],
push_constant_ranges: vec![],
vertex: fullscreen_shader_vertex_state(),
vertex: vertex_state.clone(),
primitive: PrimitiveState::default(),
depth_stencil: Some(DepthStencilState {
format: CORE_3D_DEPTH_FORMAT,
@ -424,7 +426,7 @@ impl FromWorld for MeshletPipelines {
label: Some("meshlet_resolve_depth_pipeline".into()),
layout: vec![resolve_depth_shadow_view_layout],
push_constant_ranges: vec![],
vertex: fullscreen_shader_vertex_state(),
vertex: vertex_state.clone(),
primitive: PrimitiveState::default(),
depth_stencil: Some(DepthStencilState {
format: CORE_3D_DEPTH_FORMAT,
@ -449,7 +451,7 @@ impl FromWorld for MeshletPipelines {
label: Some("meshlet_resolve_material_depth_pipeline".into()),
layout: vec![resolve_material_depth_layout],
push_constant_ranges: vec![],
vertex: fullscreen_shader_vertex_state(),
vertex: vertex_state,
primitive: PrimitiveState::default(),
depth_stencil: Some(DepthStencilState {
format: TextureFormat::Depth16Unorm,

View File

@ -7,8 +7,8 @@ use bevy_core_pipeline::{
graph::{Core3d, Node3d},
DEPTH_TEXTURE_SAMPLING_SUPPORTED,
},
fullscreen_vertex_shader,
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
FullscreenShader,
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
@ -155,7 +155,8 @@ pub struct ScreenSpaceReflectionsPipeline {
depth_nearest_sampler: Sampler,
bind_group_layout: BindGroupLayout,
binding_arrays_are_usable: bool,
shader: Handle<Shader>,
fullscreen_shader: FullscreenShader,
fragment_shader: Handle<Shader>,
}
/// A GPU buffer that stores the screen space reflection settings for each view.
@ -397,9 +398,10 @@ impl FromWorld for ScreenSpaceReflectionsPipeline {
depth_nearest_sampler,
bind_group_layout,
binding_arrays_are_usable: binding_arrays_are_usable(render_device, render_adapter),
fullscreen_shader: world.resource::<FullscreenShader>().clone(),
// Even though ssr was loaded using load_shader_library, we can still access it like a
// normal embedded asset (so we can use it as both a library or a kernel).
shader: load_embedded_asset!(world, "ssr.wgsl"),
fragment_shader: load_embedded_asset!(world, "ssr.wgsl"),
}
}
}
@ -536,9 +538,9 @@ impl SpecializedRenderPipeline for ScreenSpaceReflectionsPipeline {
RenderPipelineDescriptor {
label: Some("SSR pipeline".into()),
layout: vec![mesh_view_layout.clone(), self.bind_group_layout.clone()],
vertex: fullscreen_vertex_shader::fullscreen_shader_vertex_state(),
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.shader.clone(),
shader: self.fragment_shader.clone(),
shader_defs,
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {

View File

@ -9,7 +9,7 @@
use bevy::{
core_pipeline::{
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
FullscreenShader,
},
ecs::query::QueryItem,
prelude::*,
@ -259,6 +259,8 @@ impl FromWorld for PostProcessPipeline {
// Get the shader handle
let shader = world.load_asset(SHADER_ASSET_PATH);
// This will setup a fullscreen triangle for the vertex state.
let vertex_state = world.resource::<FullscreenShader>().to_vertex_state();
let pipeline_id = world
.resource_mut::<PipelineCache>()
@ -266,8 +268,7 @@ impl FromWorld for PostProcessPipeline {
.queue_render_pipeline(RenderPipelineDescriptor {
label: Some("post_process_pipeline".into()),
layout: vec![layout.clone()],
// This will setup a fullscreen triangle for the vertex state
vertex: fullscreen_shader_vertex_state(),
vertex: vertex_state,
fragment: Some(FragmentState {
shader,
shader_defs: vec![],

View File

@ -0,0 +1,68 @@
---
title: `FULLSCREEN_SHADER_HANDLE` replaced with `FullscreenShader`
pull_requests: [19426]
---
`FULLSCREEN_SHADER_HANDLE` and `fullscreen_shader_vertex_state` have been replaced by the
`FullscreenShader` resource. Users of either of these will need to call `FullscreenShader::shader`
or `FullscreenShader::to_vertex_state` respectively. You may need to clone `FullscreenShader` out of
the render world to store an instance that you can use later (e.g., if you are attempting to use the
fullscreen shader inside a `SpecializedRenderPipeline` implementation).
For example, if your previous code looked like this:
```rust
struct MyPipeline {
some_bind_group: BindGroupLayout,
}
impl FromWorld for MyPipeline {
fn from_world(render_world: &mut World) -> Self {
let some_bind_group = /* ... RenderDevice stuff */;
Self {
some_bind_group,
}
}
}
impl SpecializedRenderPipeline for MyPipeline {
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
RenderPipelineDescriptor {
vertex: fullscreen_shader_vertex_state(),
// ... other stuff
}
}
}
```
You can migrate your code to:
```rust
struct MyPipeline {
some_bind_group: BindGroupLayout,
fullscreen_shader: FullscreenShader,
}
impl FromWorld for MyPipeline {
fn from_world(render_world: &mut World) -> Self {
let some_bind_group = /* ... RenderDevice stuff */;
Self {
some_bind_group,
fullscreen_shader: render_world.resource::<FullscreenShader>().clone(),
}
}
}
impl SpecializedRenderPipeline for MyPipeline {
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
RenderPipelineDescriptor {
vertex: self.fullscreen_shader.to_vertex_state(),
// ... other stuff
}
}
}
```
This is just one example. Pipelines may be initialized in different ways, but the primary strategy
is clone out the `FullscreenShader` resource from the render world, and call `to_vertex_state` to
use it as the vertex shader.