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

[Merged by Bors] - Pipeline Specialization, Shader Assets, and Shader Preprocessing #3031

Closed
Closed
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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,10 @@ path = "examples/shader/shader_defs.rs"
name = "custom_shader_pipelined"
path = "examples/shader/custom_shader_pipelined.rs"

[[example]]
name = "shader_defs_pipelined"
path = "examples/shader/shader_defs_pipelined.rs"

# Tools
[[example]]
name = "bevymark"
Expand Down
43 changes: 43 additions & 0 deletions assets/shaders/shader_defs.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[[block]]
struct View {
view_proj: mat4x4<f32>;
projection: mat4x4<f32>;
world_position: vec3<f32>;
};
[[group(0), binding(0)]]
var<uniform> view: View;

[[block]]
struct Mesh {
transform: mat4x4<f32>;
};
[[group(1), binding(0)]]
var<uniform> mesh: Mesh;

struct Vertex {
[[location(0)]] position: vec3<f32>;
[[location(1)]] normal: vec3<f32>;
[[location(2)]] uv: vec2<f32>;
};

struct VertexOutput {
[[builtin(position)]] clip_position: vec4<f32>;
};

[[stage(vertex)]]
fn vertex(vertex: Vertex) -> VertexOutput {
let world_position = mesh.transform * vec4<f32>(vertex.position, 1.0);

var out: VertexOutput;
out.clip_position = view.view_proj * world_position;
return out;
}

[[stage(fragment)]]
fn fragment() -> [[location(0)]] vec4<f32> {
var color = vec4<f32>(0.0, 0.0, 1.0, 1.0);
# ifdef IS_RED
color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
# endif
return color;
}
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ Example | File | Description
`mesh_custom_attribute` | [`shader/mesh_custom_attribute.rs`](./shader/mesh_custom_attribute.rs) | Illustrates how to add a custom attribute to a mesh and use it in a custom shader
`shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it
`shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader)
`shader_defs_pipelined` | [`shader/shader_defs_pipelined.rs`](./shader/shader_defs_pipelined.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader)

## Tests

Expand Down
203 changes: 63 additions & 140 deletions examples/shader/custom_shader_pipelined.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use bevy::{
core_pipeline::Transparent3d,
core_pipeline::{SetItemPipeline, Transparent3d},
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
ecs::{
prelude::*,
system::{lifetimeless::*, SystemParamItem},
},
math::{Vec3, Vec4},
pbr2::{DrawMesh, MeshUniform, PbrShaders, SetMeshViewBindGroup, SetTransformBindGroup},
prelude::{AddAsset, App, Assets, GlobalTransform, Handle, Plugin, Transform},
pbr2::{
DrawMesh, MeshUniform, PbrPipeline, PbrPipelineKey, SetMeshViewBindGroup,
SetTransformBindGroup,
},
prelude::{AddAsset, App, AssetServer, Assets, GlobalTransform, Handle, Plugin, Transform},
reflect::TypeUuid,
render2::{
camera::PerspectiveCameraBundle,
Expand All @@ -20,15 +23,46 @@ use bevy::{
},
render_resource::*,
renderer::RenderDevice,
shader::Shader,
texture::BevyDefault,
view::ExtractedView,
RenderApp, RenderStage,
},
PipelinedDefaultPlugins,
};
use crevice::std140::{AsStd140, Std140};

fn main() {
App::new()
.add_plugins(PipelinedDefaultPlugins)
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(CustomMaterialPlugin)
.add_startup_system(setup)
.run();
}

/// set up a simple 3D scene
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<CustomMaterial>>,
) {
// cube
commands.spawn().insert_bundle((
meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
Transform::from_xyz(0.0, 0.5, 0.0),
GlobalTransform::default(),
materials.add(CustomMaterial {
color: Color::GREEN,
}),
));

// camera
commands.spawn_bundle(PerspectiveCameraBundle {
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});
}

#[derive(Debug, Clone, TypeUuid)]
#[uuid = "4ee9c363-1124-4113-890e-199d81b00281"]
pub struct CustomMaterial {
Expand Down Expand Up @@ -88,51 +122,18 @@ impl Plugin for CustomMaterialPlugin {
}
}

fn main() {
App::new()
.add_plugins(PipelinedDefaultPlugins)
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(CustomMaterialPlugin)
.add_startup_system(setup)
.run();
}

/// set up a simple 3D scene
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<CustomMaterial>>,
) {
// cube
commands.spawn().insert_bundle((
meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
Transform::from_xyz(0.0, 0.5, 0.0),
GlobalTransform::default(),
materials.add(CustomMaterial {
color: Color::GREEN,
}),
));

// camera
commands.spawn_bundle(PerspectiveCameraBundle {
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});
}

pub struct CustomPipeline {
material_layout: BindGroupLayout,
pipeline: RenderPipeline,
pipeline: CachedPipelineId,
}

// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
impl FromWorld for CustomPipeline {
fn from_world(world: &mut World) -> Self {
let render_device = world.get_resource::<RenderDevice>().unwrap();
let shader = Shader::from_wgsl(include_str!("../../assets/shaders/custom.wgsl"));
let shader_module = render_device.create_shader_module(&shader);
let world = world.cell();
let asset_server = world.get_resource::<AssetServer>().unwrap();
let shader = asset_server.load("shaders/custom.wgsl");

let render_device = world.get_resource::<RenderDevice>().unwrap();
let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
Expand All @@ -146,99 +147,20 @@ impl FromWorld for CustomPipeline {
}],
label: None,
});
let pbr_pipeline = world.get_resource::<PbrShaders>().unwrap();

let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
push_constant_ranges: &[],
bind_group_layouts: &[
&pbr_pipeline.view_layout,
&material_layout,
&pbr_pipeline.mesh_layout,
],
});

let pipeline = render_device.create_render_pipeline(&RenderPipelineDescriptor {
label: None,
vertex: VertexState {
buffers: &[VertexBufferLayout {
array_stride: 32,
step_mode: VertexStepMode::Vertex,
attributes: &[
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
},
],
}],
module: &shader_module,
entry_point: "vertex",
},
fragment: Some(FragmentState {
module: &shader_module,
entry_point: "fragment",
targets: &[ColorTargetState {
format: TextureFormat::bevy_default(),
blend: Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
}),
write_mask: ColorWrites::ALL,
}],
}),
depth_stencil: Some(DepthStencilState {
format: TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: CompareFunction::Greater,
stencil: StencilState {
front: StencilFaceState::IGNORE,
back: StencilFaceState::IGNORE,
read_mask: 0,
write_mask: 0,
},
bias: DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
layout: Some(&pipeline_layout),
multisample: MultisampleState::default(),
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: Some(Face::Back),
polygon_mode: PolygonMode::Fill,
clamp_depth: false,
conservative: false,
},
});

let pbr_pipeline = world.get_resource::<PbrPipeline>().unwrap();
let mut descriptor = pbr_pipeline.specialize(PbrPipelineKey::empty());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let mut descriptor = pbr_pipeline.specialize(PbrPipelineKey::empty());
let mut descriptor = pbr_pipeline.specialize(PbrPipelineKey::empty());
descriptor.label = Some("custom_pipeline".into());

descriptor.vertex.shader = shader.clone();
descriptor.fragment.as_mut().unwrap().shader = shader;
descriptor.layout = Some(vec![
pbr_pipeline.view_layout.clone(),
material_layout.clone(),
pbr_pipeline.mesh_layout.clone(),
]);

let mut pipeline_cache = world.get_resource_mut::<RenderPipelineCache>().unwrap();
CustomPipeline {
pipeline,
pipeline: pipeline_cache.queue(descriptor),
material_layout,
}
}
Expand All @@ -247,6 +169,7 @@ impl FromWorld for CustomPipeline {
pub fn queue_custom(
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
materials: Res<RenderAssets<CustomMaterial>>,
custom_pipeline: Res<CustomPipeline>,
material_meshes: Query<(Entity, &Handle<CustomMaterial>, &MeshUniform), With<Handle<Mesh>>>,
mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>,
) {
Expand All @@ -261,6 +184,7 @@ pub fn queue_custom(
if materials.contains_key(material_handle) {
transparent_phase.add(Transparent3d {
entity,
pipeline: custom_pipeline.pipeline,
draw_function: draw_custom,
distance: view_row_2.dot(mesh_uniform.transform.col(3)),
});
Expand All @@ -270,28 +194,27 @@ pub fn queue_custom(
}

type DrawCustom = (
SetCustomMaterialPipeline,
SetItemPipeline,
SetMeshViewBindGroup<0>,
SetCustomMaterialBindGroup,
SetTransformBindGroup<2>,
DrawMesh,
);

struct SetCustomMaterialPipeline;
impl RenderCommand<Transparent3d> for SetCustomMaterialPipeline {
struct SetCustomMaterialBindGroup;
impl RenderCommand<Transparent3d> for SetCustomMaterialBindGroup {
type Param = (
SRes<RenderAssets<CustomMaterial>>,
SRes<CustomPipeline>,
SQuery<Read<Handle<CustomMaterial>>>,
);
fn render<'w>(
_view: Entity,
item: &Transparent3d,
(materials, custom_pipeline, query): SystemParamItem<'w, '_, Self::Param>,
(materials, query): SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) {
let material_handle = query.get(item.entity).unwrap();
let material = materials.into_inner().get(material_handle).unwrap();
pass.set_render_pipeline(&custom_pipeline.into_inner().pipeline);
pass.set_bind_group(1, &material.bind_group, &[]);
}
}
Loading