mod auto_array;
mod bindings;
mod compute;
mod cpu_buffer;
mod extra_bind_group;
mod gpu_buffer;
mod node;
mod pipeline;
mod readback;
use bevy::{
core_pipeline::{Core3d, Core3dSystems},
prelude::*,
render::{
Render, RenderApp, RenderStartup, RenderSystems,
render_resource::{
BindGroup, BindGroupLayoutDescriptor, BindGroupLayoutEntries, ShaderSize, ShaderStages,
ShaderType,
binding_types::{storage_buffer_read_only_sized, storage_buffer_sized},
encase::internal::{CreateFrom, WriteInto},
},
},
};
pub use auto_array::{ArrayBufferChanges, ArrayBufferState};
pub use bindings::{
AutoBufferBindGroups, AutoBufferCompiledLayouts, AutoBufferKind, AutoBufferLayoutDescriptors,
AutoBufferLayouts, BindingTable, BoundResource,
};
pub use compute::{ComputePasses, ComputeShaderPlugin, Workgroups};
pub use extra_bind_group::FragmentBindGroupBuilder;
pub use gpu_buffer::GpuBuffer;
pub use node::fullscreen_pass;
pub use pipeline::{FullscreenPipeline, FullscreenPipelineConfig};
pub use readback::{GpuReadback, ReadbackMode, StorageReadback};
pub mod prelude {
pub use crate::{
ArrayBufferChanges, ComputePasses, ComputeShaderPlugin, FullscreenFragmentPlugin,
GpuBuffer, GpuReadback, ReadbackMode, ShaderAppExt, StorageReadback, Workgroups,
};
pub use bevy::render::render_resource::ShaderType;
pub use bevy::window::PrimaryWindow;
}
#[doc(hidden)]
pub mod __private {
pub use bevy::render::render_resource::{
BindGroupLayoutDescriptor, BindGroupLayoutEntries, ShaderStages,
};
}
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
pub enum ShaderSystems {
CompileLayouts,
PrepareBindings,
FinalizeBindGroups,
}
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
pub enum FragmentSystems {
InitPipeline,
}
#[derive(Resource, Default)]
pub struct FragmentExtraLayouts(pub Vec<BindGroupLayoutDescriptor>);
impl FragmentExtraLayouts {
pub fn push(&mut self, desc: BindGroupLayoutDescriptor) -> &mut Self {
self.0.push(desc);
self
}
pub fn storage_buffer_read_only(&mut self, label: &'static str) -> &mut Self {
self.0.push(BindGroupLayoutDescriptor::new(
label,
&BindGroupLayoutEntries::single(
ShaderStages::FRAGMENT,
storage_buffer_read_only_sized(false, None),
),
));
self
}
pub fn storage_buffer_read_write(&mut self, label: &'static str) -> &mut Self {
self.0.push(BindGroupLayoutDescriptor::new(
label,
&BindGroupLayoutEntries::single(
ShaderStages::FRAGMENT,
storage_buffer_sized(false, None),
),
));
self
}
}
#[derive(Resource, Default)]
pub struct FragmentExtraBindGroups(pub Vec<BindGroup>);
impl FragmentExtraBindGroups {
pub fn push(&mut self, bind_group: BindGroup) -> &mut Self {
self.0.push(bind_group);
self
}
pub fn clear(&mut self) -> &mut Self {
self.0.clear();
self
}
}
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
pub enum ShaderPassSystems {
Compute,
Fullscreen,
ReadbackCopy,
}
pub trait ShaderAppExt {
fn register_uniform_buffer<U>(&mut self, group_index: u32, binding_index: u32) -> &mut Self
where
U: ShaderType + WriteInto + Default + Resource + Clone + Send + Sync + 'static;
fn register_storage_buffer<S>(
&mut self,
group_index: u32,
binding_index: u32,
read_write: bool,
) -> &mut Self
where
S: ShaderType + WriteInto + Default + Resource + Clone + Send + Sync + 'static;
fn register_array_buffer<Tag, T, const N: usize>(
&mut self,
group_index: u32,
binding_index: u32,
read_write: bool,
) -> &mut Self
where
Tag: Send + Sync + 'static,
T: ShaderSize + WriteInto + Default + Send + Sync + 'static;
fn register_gpu_buffer<Tag, T>(
&mut self,
group_index: u32,
binding_index: u32,
len: u32,
) -> &mut Self
where
Tag: Send + Sync + 'static,
T: ShaderSize + WriteInto + Default + Send + Sync + 'static;
fn read_back_gpu_buffer<Tag, T>(&mut self, mode: ReadbackMode) -> &mut Self
where
Tag: Send + Sync + 'static,
T: ShaderSize + CreateFrom + Send + Sync + 'static;
fn read_back_array_buffer<Tag, T>(&mut self, mode: ReadbackMode) -> &mut Self
where
Tag: Send + Sync + 'static,
T: ShaderSize + CreateFrom + Send + Sync + 'static;
fn read_back_storage_buffer<S>(&mut self, mode: ReadbackMode) -> &mut Self
where
S: ShaderType + WriteInto + CreateFrom + Default + Resource + Clone + Send + Sync + 'static;
}
impl ShaderAppExt for App {
fn register_uniform_buffer<U>(&mut self, group_index: u32, binding_index: u32) -> &mut Self
where
U: ShaderType + WriteInto + Default + Resource + Clone + Send + Sync + 'static,
{
cpu_buffer::register::<U>(self, group_index, binding_index, AutoBufferKind::Uniform);
self
}
fn register_storage_buffer<S>(
&mut self,
group_index: u32,
binding_index: u32,
read_write: bool,
) -> &mut Self
where
S: ShaderType + WriteInto + Default + Resource + Clone + Send + Sync + 'static,
{
cpu_buffer::register::<S>(
self,
group_index,
binding_index,
AutoBufferKind::Storage {
read_only: !read_write,
},
);
self
}
fn register_array_buffer<Tag, T, const N: usize>(
&mut self,
group_index: u32,
binding_index: u32,
read_write: bool,
) -> &mut Self
where
Tag: Send + Sync + 'static,
T: ShaderSize + WriteInto + Default + Send + Sync + 'static,
{
auto_array::register::<Tag, T, N>(self, group_index, binding_index, read_write);
self
}
fn register_gpu_buffer<Tag, T>(
&mut self,
group_index: u32,
binding_index: u32,
len: u32,
) -> &mut Self
where
Tag: Send + Sync + 'static,
T: ShaderSize + WriteInto + Default + Send + Sync + 'static,
{
gpu_buffer::register::<Tag, T>(self, group_index, binding_index, len);
self
}
fn read_back_gpu_buffer<Tag, T>(&mut self, mode: ReadbackMode) -> &mut Self
where
Tag: Send + Sync + 'static,
T: ShaderSize + CreateFrom + Send + Sync + 'static,
{
readback::register_gpu::<Tag, T>(self, mode);
self
}
fn read_back_array_buffer<Tag, T>(&mut self, mode: ReadbackMode) -> &mut Self
where
Tag: Send + Sync + 'static,
T: ShaderSize + CreateFrom + Send + Sync + 'static,
{
readback::register_array::<Tag, T>(self, mode);
self
}
fn read_back_storage_buffer<S>(&mut self, mode: ReadbackMode) -> &mut Self
where
S: ShaderType + WriteInto + CreateFrom + Default + Resource + Clone + Send + Sync + 'static,
{
readback::register_storage::<S>(self, mode);
self
}
}
#[macro_export]
macro_rules! fragment_layout {
($label:expr, $($entry:expr),+ $(,)?) => {
$crate::__private::BindGroupLayoutDescriptor::new(
$label,
&$crate::__private::BindGroupLayoutEntries::sequential(
$crate::__private::ShaderStages::FRAGMENT,
($($entry,)+),
),
)
};
}
struct ShaderCorePlugin;
impl Plugin for ShaderCorePlugin {
fn build(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
bindings::init_resources(render_app);
render_app
.configure_sets(
Render,
(
ShaderSystems::PrepareBindings,
ShaderSystems::FinalizeBindGroups,
)
.chain()
.in_set(RenderSystems::PrepareBindGroups),
)
.add_systems(
Render,
bindings::finalize_bind_groups.in_set(ShaderSystems::FinalizeBindGroups),
)
.add_systems(
RenderStartup,
bindings::compile_layouts.in_set(ShaderSystems::CompileLayouts),
);
}
}
fn add_core_plugin(app: &mut App) {
if !app.is_plugin_added::<ShaderCorePlugin>() {
app.add_plugins(ShaderCorePlugin);
}
}
pub struct FullscreenFragmentPlugin {
pub shader_path: &'static str,
pub entry_point: Option<&'static str>,
}
impl FullscreenFragmentPlugin {
pub fn new(shader_path: &'static str) -> Self {
Self {
shader_path,
entry_point: None,
}
}
pub fn with_entry_point(mut self, entry_point: &'static str) -> Self {
self.entry_point = Some(entry_point);
self
}
}
impl Plugin for FullscreenFragmentPlugin {
fn build(&self, app: &mut App) {
add_core_plugin(app);
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.insert_resource(FullscreenPipelineConfig {
shader_path: self.shader_path,
entry_point: self.entry_point,
});
render_app.init_resource::<FragmentExtraLayouts>();
render_app.init_resource::<FragmentExtraBindGroups>();
render_app
.configure_sets(
RenderStartup,
FragmentSystems::InitPipeline.after(ShaderSystems::CompileLayouts),
)
.add_systems(
RenderStartup,
pipeline::init_pipeline.in_set(FragmentSystems::InitPipeline),
)
.add_systems(
Render,
pipeline::queue_fullscreen_pipelines.in_set(RenderSystems::Prepare),
);
render_app.add_systems(
Core3d,
fullscreen_pass
.in_set(ShaderPassSystems::Fullscreen)
.after(Core3dSystems::Prepass)
.before(Core3dSystems::MainPass),
);
}
}