Modular Rendering (#2831)
This changes how render logic is composed to make it much more modular. Previously, all extraction logic was centralized for a given "type" of rendered thing. For example, we extracted meshes into a vector of ExtractedMesh, which contained the mesh and material asset handles, the transform, etc. We looked up bindings for "drawn things" using their index in the `Vec<ExtractedMesh>`. This worked fine for built in rendering, but made it hard to reuse logic for "custom" rendering. It also prevented us from reusing things like "extracted transforms" across contexts. To make rendering more modular, I made a number of changes: * Entities now drive rendering: * We extract "render components" from "app components" and store them _on_ entities. No more centralized uber lists! We now have true "ECS-driven rendering" * To make this perform well, I implemented #2673 in upstream Bevy for fast batch insertions into specific entities. This was merged into the `pipelined-rendering` branch here: #2815 * Reworked the `Draw` abstraction: * Generic `PhaseItems`: each draw phase can define its own type of "rendered thing", which can define its own "sort key" * Ported the 2d, 3d, and shadow phases to the new PhaseItem impl (currently Transparent2d, Transparent3d, and Shadow PhaseItems) * `Draw` trait and and `DrawFunctions` are now generic on PhaseItem * Modular / Ergonomic `DrawFunctions` via `RenderCommands` * RenderCommand is a trait that runs an ECS query and produces one or more RenderPass calls. Types implementing this trait can be composed to create a final DrawFunction. For example the DrawPbr DrawFunction is created from the following DrawCommand tuple. Const generics are used to set specific bind group locations: ```rust pub type DrawPbr = ( SetPbrPipeline, SetMeshViewBindGroup<0>, SetStandardMaterialBindGroup<1>, SetTransformBindGroup<2>, DrawMesh, ); ``` * The new `custom_shader_pipelined` example illustrates how the commands above can be reused to create a custom draw function: ```rust type DrawCustom = ( SetCustomMaterialPipeline, SetMeshViewBindGroup<0>, SetTransformBindGroup<2>, DrawMesh, ); ``` * ExtractComponentPlugin and UniformComponentPlugin: * Simple, standardized ways to easily extract individual components and write them to GPU buffers * Ported PBR and Sprite rendering to the new primitives above. * Removed staging buffer from UniformVec in favor of direct Queue usage * Makes UniformVec much easier to use and more ergonomic. Completely removes the need for custom render graph nodes in these contexts (see the PbrNode and view Node removals and the much simpler call patterns in the relevant Prepare systems). * Added a many_cubes_pipelined example to benchmark baseline 3d rendering performance and ensure there were no major regressions during this port. Avoiding regressions was challenging given that the old approach of extracting into centralized vectors is basically the "optimal" approach. However thanks to a various ECS optimizations and render logic rephrasing, we pretty much break even on this benchmark! * Lifetimeless SystemParams: this will be a bit divisive, but as we continue to embrace "trait driven systems" (ex: ExtractComponentPlugin, UniformComponentPlugin, DrawCommand), the ergonomics of `(Query<'static, 'static, (&'static A, &'static B, &'static)>, Res<'static, C>)` were getting very hard to bear. As a compromise, I added "static type aliases" for the relevant SystemParams. The previous example can now be expressed like this: `(SQuery<(Read<A>, Read<B>)>, SRes<C>)`. If anyone has better ideas / conflicting opinions, please let me know! * RunSystem trait: a way to define Systems via a trait with a SystemParam associated type. This is used to implement the various plugins mentioned above. I also added SystemParamItem and QueryItem type aliases to make "trait stye" ecs interactions nicer on the eyes (and fingers). * RenderAsset retrying: ensures that render assets are only created when they are "ready" and allows us to create bind groups directly inside render assets (which significantly simplified the StandardMaterial code). I think ultimately we should swap this out on "asset dependency" events to wait for dependencies to load, but this will require significant asset system changes. * Updated some built in shaders to account for missing MeshUniform fields
This commit is contained in:
parent
1e03a97e73
commit
08969a24b8
@ -108,6 +108,7 @@ ron = "0.6.2"
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
# Needed to poll Task examples
|
# Needed to poll Task examples
|
||||||
futures-lite = "1.11.3"
|
futures-lite = "1.11.3"
|
||||||
|
crevice = {path = "crates/crevice"}
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "hello_world"
|
name = "hello_world"
|
||||||
@ -159,6 +160,10 @@ path = "examples/3d/3d_scene.rs"
|
|||||||
name = "3d_scene_pipelined"
|
name = "3d_scene_pipelined"
|
||||||
path = "examples/3d/3d_scene_pipelined.rs"
|
path = "examples/3d/3d_scene_pipelined.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "many_cubes_pipelined"
|
||||||
|
path = "examples/3d/many_cubes_pipelined.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "cornell_box_pipelined"
|
name = "cornell_box_pipelined"
|
||||||
path = "examples/3d/cornell_box_pipelined.rs"
|
path = "examples/3d/cornell_box_pipelined.rs"
|
||||||
@ -462,6 +467,10 @@ path = "examples/shader/shader_custom_material.rs"
|
|||||||
name = "shader_defs"
|
name = "shader_defs"
|
||||||
path = "examples/shader/shader_defs.rs"
|
path = "examples/shader/shader_defs.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "custom_shader_pipelined"
|
||||||
|
path = "examples/shader/custom_shader_pipelined.rs"
|
||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "bevymark"
|
name = "bevymark"
|
||||||
|
|||||||
46
assets/shaders/custom.wgsl
Normal file
46
assets/shaders/custom.wgsl
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
[[block]]
|
||||||
|
struct View {
|
||||||
|
view_proj: mat4x4<f32>;
|
||||||
|
projection: mat4x4<f32>;
|
||||||
|
world_position: vec3<f32>;
|
||||||
|
};
|
||||||
|
[[group(0), binding(0)]]
|
||||||
|
var view: View;
|
||||||
|
|
||||||
|
[[block]]
|
||||||
|
struct Mesh {
|
||||||
|
transform: mat4x4<f32>;
|
||||||
|
};
|
||||||
|
[[group(2), binding(0)]]
|
||||||
|
var 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[block]]
|
||||||
|
struct CustomMaterial {
|
||||||
|
color: vec4<f32>;
|
||||||
|
};
|
||||||
|
[[group(1), binding(0)]]
|
||||||
|
var material: CustomMaterial;
|
||||||
|
|
||||||
|
[[stage(fragment)]]
|
||||||
|
fn fragment() -> [[location(0)]] vec4<f32> {
|
||||||
|
return material.color;
|
||||||
|
}
|
||||||
@ -96,6 +96,7 @@ impl<T: Asset> Handle<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn weak(id: HandleId) -> Self {
|
pub fn weak(id: HandleId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
@ -129,6 +130,7 @@ impl<T: Asset> Handle<T> {
|
|||||||
self.handle_type = HandleType::Strong(sender);
|
self.handle_type = HandleType::Strong(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn clone_weak(&self) -> Self {
|
pub fn clone_weak(&self) -> Self {
|
||||||
Handle::weak(self.id)
|
Handle::weak(self.id)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,8 @@ pub mod prelude {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use bevy_ecs_macros::all_tuples;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate as bevy_ecs;
|
use crate as bevy_ecs;
|
||||||
|
|||||||
@ -45,6 +45,8 @@ pub trait WorldQuery {
|
|||||||
type State: FetchState;
|
type State: FetchState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type QueryItem<'w, 's, Q> = <<Q as WorldQuery>::Fetch as Fetch<'w, 's>>::Item;
|
||||||
|
|
||||||
pub trait Fetch<'world, 'state>: Sized {
|
pub trait Fetch<'world, 'state>: Sized {
|
||||||
type Item;
|
type Item;
|
||||||
type State: FetchState;
|
type State: FetchState;
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::{
|
|||||||
query::{Access, FilteredAccessSet},
|
query::{Access, FilteredAccessSet},
|
||||||
system::{
|
system::{
|
||||||
check_system_change_tick, ReadOnlySystemParamFetch, System, SystemId, SystemParam,
|
check_system_change_tick, ReadOnlySystemParamFetch, System, SystemId, SystemParam,
|
||||||
SystemParamFetch, SystemParamState,
|
SystemParamFetch, SystemParamItem, SystemParamState,
|
||||||
},
|
},
|
||||||
world::{World, WorldId},
|
world::{World, WorldId},
|
||||||
};
|
};
|
||||||
@ -48,6 +48,11 @@ impl SystemMeta {
|
|||||||
pub fn set_non_send(&mut self) {
|
pub fn set_non_send(&mut self) {
|
||||||
self.is_send = false;
|
self.is_send = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn check_change_tick(&mut self, change_tick: u32) {
|
||||||
|
check_system_change_tick(&mut self.last_change_tick, change_tick, self.name.as_ref());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Actually use this in FunctionSystem. We should probably only do this once Systems are constructed using a World reference
|
// TODO: Actually use this in FunctionSystem. We should probably only do this once Systems are constructed using a World reference
|
||||||
@ -123,6 +128,10 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||||||
self.world_id == world.id()
|
self.world_id == world.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_archetype(&mut self, archetype: &Archetype) {
|
||||||
|
self.param_state.new_archetype(archetype, &mut self.meta);
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_world_and_update_archetypes(&mut self, world: &World) {
|
fn validate_world_and_update_archetypes(&mut self, world: &World) {
|
||||||
assert!(self.matches_world(world), "Encountered a mismatched World. A SystemState cannot be used with Worlds other than the one it was created with.");
|
assert!(self.matches_world(world), "Encountered a mismatched World. A SystemState cannot be used with Worlds other than the one it was created with.");
|
||||||
let archetypes = world.archetypes();
|
let archetypes = world.archetypes();
|
||||||
@ -161,6 +170,69 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait RunSystem: Send + Sync + 'static {
|
||||||
|
type Param: SystemParam;
|
||||||
|
fn run(param: SystemParamItem<Self::Param>);
|
||||||
|
fn system(world: &mut World) -> ParamSystem<Self::Param> {
|
||||||
|
ParamSystem {
|
||||||
|
run: Self::run,
|
||||||
|
state: SystemState::new(world),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ParamSystem<P: SystemParam> {
|
||||||
|
state: SystemState<P>,
|
||||||
|
run: fn(SystemParamItem<P>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: SystemParam + 'static> System for ParamSystem<P> {
|
||||||
|
type In = ();
|
||||||
|
|
||||||
|
type Out = ();
|
||||||
|
|
||||||
|
fn name(&self) -> Cow<'static, str> {
|
||||||
|
self.state.meta().name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> SystemId {
|
||||||
|
self.state.meta().id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_archetype(&mut self, archetype: &Archetype) {
|
||||||
|
self.state.new_archetype(archetype);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn component_access(&self) -> &Access<ComponentId> {
|
||||||
|
self.state.meta().component_access_set.combined_access()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
|
||||||
|
&self.state.meta().archetype_component_access
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_send(&self) -> bool {
|
||||||
|
self.state.meta().is_send()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn run_unsafe(&mut self, _input: Self::In, world: &World) -> Self::Out {
|
||||||
|
let param = self.state.get_unchecked_manual(world);
|
||||||
|
(self.run)(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_buffers(&mut self, world: &mut World) {
|
||||||
|
self.state.apply(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize(&mut self, _world: &mut World) {
|
||||||
|
// already initialized by nature of the SystemState being constructed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_change_tick(&mut self, change_tick: u32) {
|
||||||
|
self.state.meta.check_change_tick(change_tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Conversion trait to turn something into a [`System`].
|
/// Conversion trait to turn something into a [`System`].
|
||||||
///
|
///
|
||||||
/// Use this to get a system from a function. Also note that every system implements this trait as
|
/// Use this to get a system from a function. Also note that every system implements this trait as
|
||||||
|
|||||||
@ -47,6 +47,8 @@ pub trait SystemParam: Sized {
|
|||||||
type Fetch: for<'w, 's> SystemParamFetch<'w, 's>;
|
type Fetch: for<'w, 's> SystemParamFetch<'w, 's>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type SystemParamItem<'w, 's, P> = <<P as SystemParam>::Fetch as SystemParamFetch<'w, 's>>::Item;
|
||||||
|
|
||||||
/// The state of a [`SystemParam`].
|
/// The state of a [`SystemParam`].
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
@ -1220,3 +1222,12 @@ macro_rules! impl_system_param_tuple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
all_tuples!(impl_system_param_tuple, 0, 16, P);
|
all_tuples!(impl_system_param_tuple, 0, 16, P);
|
||||||
|
|
||||||
|
pub mod lifetimeless {
|
||||||
|
pub type SQuery<Q, F = ()> = super::Query<'static, 'static, Q, F>;
|
||||||
|
pub type Read<T> = &'static T;
|
||||||
|
pub type Write<T> = &'static mut T;
|
||||||
|
pub type SRes<T> = super::Res<'static, T>;
|
||||||
|
pub type SResMut<T> = super::ResMut<'static, T>;
|
||||||
|
pub type SCommands = crate::system::Commands<'static, 'static>;
|
||||||
|
}
|
||||||
|
|||||||
50
examples/3d/many_cubes_pipelined.rs
Normal file
50
examples/3d/many_cubes_pipelined.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use bevy::{
|
||||||
|
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||||
|
ecs::prelude::*,
|
||||||
|
pbr2::{PbrBundle, StandardMaterial},
|
||||||
|
prelude::{App, Assets, Transform},
|
||||||
|
render2::{
|
||||||
|
camera::PerspectiveCameraBundle,
|
||||||
|
color::Color,
|
||||||
|
mesh::{shape, Mesh},
|
||||||
|
},
|
||||||
|
PipelinedDefaultPlugins,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(PipelinedDefaultPlugins)
|
||||||
|
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||||
|
.add_plugin(LogDiagnosticsPlugin::default())
|
||||||
|
.add_startup_system(setup.system())
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
const WIDTH: usize = 100;
|
||||||
|
const HEIGHT: usize = 100;
|
||||||
|
for x in 0..WIDTH {
|
||||||
|
for y in 0..HEIGHT {
|
||||||
|
// cube
|
||||||
|
commands.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
|
material: materials.add(StandardMaterial {
|
||||||
|
base_color: Color::PINK,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
transform: Transform::from_xyz((x as f32) * 2.0, (y as f32) * 2.0, 0.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// camera
|
||||||
|
commands.spawn_bundle(PerspectiveCameraBundle {
|
||||||
|
transform: Transform::from_xyz(80.0, 80.0, 300.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -101,6 +101,7 @@ Example | File | Description
|
|||||||
`cornell_box_pipelined` | [`3d/cornell_box_pipelined.rs`](./3d/cornell_box_pipelined.rs) | Re-production of the cornell box
|
`cornell_box_pipelined` | [`3d/cornell_box_pipelined.rs`](./3d/cornell_box_pipelined.rs) | Re-production of the cornell box
|
||||||
`load_gltf` | [`3d/load_gltf.rs`](./3d/load_gltf.rs) | Loads and renders a gltf file as a scene
|
`load_gltf` | [`3d/load_gltf.rs`](./3d/load_gltf.rs) | Loads and renders a gltf file as a scene
|
||||||
`load_gltf_pipelined` | [`3d/load_gltf_pipelined.rs`](./3d/load_gltf_pipelined.rs) | Loads and renders a gltf file as a scene
|
`load_gltf_pipelined` | [`3d/load_gltf_pipelined.rs`](./3d/load_gltf_pipelined.rs) | Loads and renders a gltf file as a scene
|
||||||
|
`many_cubes_pipelined` | [`3d/many_cubes_pipelined.rs`](./3d/many_cubes_pipelined.rs) | Simple benchmark to test per-entity draw overhead
|
||||||
`msaa` | [`3d/msaa.rs`](./3d/msaa.rs) | Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges
|
`msaa` | [`3d/msaa.rs`](./3d/msaa.rs) | Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges
|
||||||
`orthographic` | [`3d/orthographic.rs`](./3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications)
|
`orthographic` | [`3d/orthographic.rs`](./3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications)
|
||||||
`orthographic_pipelined` | [`3d/orthographic_pipelined.rs`](./3d/orthographic_pipelined.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications)
|
`orthographic_pipelined` | [`3d/orthographic_pipelined.rs`](./3d/orthographic_pipelined.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications)
|
||||||
@ -222,6 +223,7 @@ Example | File | Description
|
|||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
`animate_shader` | [`shader/animate_shader.rs`](./shader/animate_shader.rs) | Shows how to animate a shader by accessing a time uniform variable
|
`animate_shader` | [`shader/animate_shader.rs`](./shader/animate_shader.rs) | Shows how to animate a shader by accessing a time uniform variable
|
||||||
`array_texture` | [`shader/array_texture.rs`](./shader/array_texture.rs) | Illustrates how to create a texture for use with a texture2DArray shader uniform variable
|
`array_texture` | [`shader/array_texture.rs`](./shader/array_texture.rs) | Illustrates how to create a texture for use with a texture2DArray shader uniform variable
|
||||||
|
`custom_shader_pipelined` | [`shader/custom_shader_pipelined.rs`](./shader/custom_shader_pipelined.rs) | Illustrates how to create custom shaders
|
||||||
`hot_shader_reloading` | [`shader/hot_shader_reloading.rs`](./shader/hot_shader_reloading.rs) | Illustrates how to load shaders such that they can be edited while the example is still running
|
`hot_shader_reloading` | [`shader/hot_shader_reloading.rs`](./shader/hot_shader_reloading.rs) | Illustrates how to load shaders such that they can be edited while the example is still running
|
||||||
`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
|
`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_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it
|
||||||
|
|||||||
297
examples/shader/custom_shader_pipelined.rs
Normal file
297
examples/shader/custom_shader_pipelined.rs
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
use bevy::{
|
||||||
|
core_pipeline::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},
|
||||||
|
reflect::TypeUuid,
|
||||||
|
render2::{
|
||||||
|
camera::PerspectiveCameraBundle,
|
||||||
|
color::Color,
|
||||||
|
mesh::{shape, Mesh},
|
||||||
|
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
|
||||||
|
render_component::ExtractComponentPlugin,
|
||||||
|
render_phase::{
|
||||||
|
AddRenderCommand, DrawFunctions, RenderCommand, RenderPhase, TrackedRenderPass,
|
||||||
|
},
|
||||||
|
render_resource::*,
|
||||||
|
renderer::RenderDevice,
|
||||||
|
shader::Shader,
|
||||||
|
texture::BevyDefault,
|
||||||
|
view::ExtractedView,
|
||||||
|
RenderApp, RenderStage,
|
||||||
|
},
|
||||||
|
PipelinedDefaultPlugins,
|
||||||
|
};
|
||||||
|
use crevice::std140::{AsStd140, Std140};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TypeUuid)]
|
||||||
|
#[uuid = "4ee9c363-1124-4113-890e-199d81b00281"]
|
||||||
|
pub struct CustomMaterial {
|
||||||
|
color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GpuCustomMaterial {
|
||||||
|
_buffer: Buffer,
|
||||||
|
bind_group: BindGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderAsset for CustomMaterial {
|
||||||
|
type ExtractedAsset = CustomMaterial;
|
||||||
|
type PreparedAsset = GpuCustomMaterial;
|
||||||
|
type Param = (SRes<RenderDevice>, SRes<CustomPipeline>);
|
||||||
|
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_asset(
|
||||||
|
extracted_asset: Self::ExtractedAsset,
|
||||||
|
(render_device, custom_pipeline): &mut SystemParamItem<Self::Param>,
|
||||||
|
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
|
||||||
|
let color: Vec4 = extracted_asset.color.as_rgba_linear().into();
|
||||||
|
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||||
|
contents: color.as_std140().as_bytes(),
|
||||||
|
label: None,
|
||||||
|
usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
|
||||||
|
});
|
||||||
|
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
|
entries: &[BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: buffer.as_entire_binding(),
|
||||||
|
}],
|
||||||
|
label: None,
|
||||||
|
layout: &custom_pipeline.material_layout,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(GpuCustomMaterial {
|
||||||
|
_buffer: buffer,
|
||||||
|
bind_group,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct CustomMaterialPlugin;
|
||||||
|
|
||||||
|
impl Plugin for CustomMaterialPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_asset::<CustomMaterial>()
|
||||||
|
.add_plugin(ExtractComponentPlugin::<Handle<CustomMaterial>>::default())
|
||||||
|
.add_plugin(RenderAssetPlugin::<CustomMaterial>::default());
|
||||||
|
app.sub_app(RenderApp)
|
||||||
|
.add_render_command::<Transparent3d, DrawCustom>()
|
||||||
|
.init_resource::<CustomPipeline>()
|
||||||
|
.add_system_to_stage(RenderStage::Queue, queue_custom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
|
entries: &[BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: ShaderStage::FRAGMENT,
|
||||||
|
ty: BindingType::Buffer {
|
||||||
|
ty: BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: BufferSize::new(Vec4::std140_size_static() as u64),
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}],
|
||||||
|
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: InputStepMode::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: ColorWrite::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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
CustomPipeline {
|
||||||
|
pipeline,
|
||||||
|
material_layout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_custom(
|
||||||
|
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
|
||||||
|
materials: Res<RenderAssets<CustomMaterial>>,
|
||||||
|
material_meshes: Query<(Entity, &Handle<CustomMaterial>, &MeshUniform), With<Handle<Mesh>>>,
|
||||||
|
mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>,
|
||||||
|
) {
|
||||||
|
let draw_custom = transparent_3d_draw_functions
|
||||||
|
.read()
|
||||||
|
.get_id::<DrawCustom>()
|
||||||
|
.unwrap();
|
||||||
|
for (view, mut transparent_phase) in views.iter_mut() {
|
||||||
|
let view_matrix = view.transform.compute_matrix();
|
||||||
|
let view_row_2 = view_matrix.row(2);
|
||||||
|
for (entity, material_handle, mesh_uniform) in material_meshes.iter() {
|
||||||
|
if materials.contains_key(material_handle) {
|
||||||
|
transparent_phase.add(Transparent3d {
|
||||||
|
entity,
|
||||||
|
draw_function: draw_custom,
|
||||||
|
distance: view_row_2.dot(mesh_uniform.transform.col(3)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrawCustom = (
|
||||||
|
SetCustomMaterialPipeline,
|
||||||
|
SetMeshViewBindGroup<0>,
|
||||||
|
SetTransformBindGroup<2>,
|
||||||
|
DrawMesh,
|
||||||
|
);
|
||||||
|
|
||||||
|
struct SetCustomMaterialPipeline;
|
||||||
|
impl RenderCommand<Transparent3d> for SetCustomMaterialPipeline {
|
||||||
|
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>,
|
||||||
|
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, &[]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -66,11 +66,23 @@ struct BirdTexture(Handle<Image>);
|
|||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
_window: Res<WindowDescriptor>,
|
window: Res<WindowDescriptor>,
|
||||||
_counter: ResMut<BevyCounter>,
|
mut counter: ResMut<BevyCounter>,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
) {
|
) {
|
||||||
// spawn_birds(&mut commands, &window, &mut counter, 10);
|
let texture = asset_server.load("branding/icon.png");
|
||||||
|
if let Some(initial_count) = std::env::args()
|
||||||
|
.nth(1)
|
||||||
|
.and_then(|arg| arg.parse::<u128>().ok())
|
||||||
|
{
|
||||||
|
spawn_birds(
|
||||||
|
&mut commands,
|
||||||
|
&window,
|
||||||
|
&mut counter,
|
||||||
|
initial_count,
|
||||||
|
texture.clone_weak(),
|
||||||
|
);
|
||||||
|
}
|
||||||
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
|
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
|
||||||
// commands.spawn_bundle(UiCameraBundle::default());
|
// commands.spawn_bundle(UiCameraBundle::default());
|
||||||
// commands.spawn_bundle(TextBundle {
|
// commands.spawn_bundle(TextBundle {
|
||||||
@ -123,7 +135,7 @@ fn setup(
|
|||||||
// ..Default::default()
|
// ..Default::default()
|
||||||
// });
|
// });
|
||||||
|
|
||||||
commands.insert_resource(BirdTexture(asset_server.load("branding/icon.png")));
|
commands.insert_resource(BirdTexture(texture));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
|||||||
@ -15,6 +15,8 @@ keywords = ["bevy"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# bevy
|
# bevy
|
||||||
bevy_app = { path = "../../crates/bevy_app", version = "0.5.0" }
|
bevy_app = { path = "../../crates/bevy_app", version = "0.5.0" }
|
||||||
|
bevy_asset = { path = "../../crates/bevy_asset", version = "0.5.0" }
|
||||||
|
bevy_core = { path = "../../crates/bevy_core", version = "0.5.0" }
|
||||||
bevy_ecs = { path = "../../crates/bevy_ecs", version = "0.5.0" }
|
bevy_ecs = { path = "../../crates/bevy_ecs", version = "0.5.0" }
|
||||||
bevy_render2 = { path = "../bevy_render2", version = "0.5.0" }
|
bevy_render2 = { path = "../bevy_render2", version = "0.5.0" }
|
||||||
|
|
||||||
|
|||||||
@ -7,19 +7,18 @@ pub use main_pass_3d::*;
|
|||||||
pub use main_pass_driver::*;
|
pub use main_pass_driver::*;
|
||||||
|
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
|
use bevy_asset::Handle;
|
||||||
|
use bevy_core::FloatOrd;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
camera::{ActiveCameras, CameraPlugin},
|
camera::{ActiveCameras, CameraPlugin},
|
||||||
color::Color,
|
color::Color,
|
||||||
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
|
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
|
||||||
render_phase::{sort_phase_system, RenderPhase},
|
render_phase::{sort_phase_system, DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase},
|
||||||
render_resource::{
|
render_resource::*,
|
||||||
Extent3d, Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage,
|
|
||||||
TextureView,
|
|
||||||
},
|
|
||||||
renderer::RenderDevice,
|
renderer::RenderDevice,
|
||||||
texture::TextureCache,
|
texture::{Image, TextureCache},
|
||||||
view::{ExtractedView, ViewPlugin},
|
view::ExtractedView,
|
||||||
RenderApp, RenderStage, RenderWorld,
|
RenderApp, RenderStage, RenderWorld,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,17 +75,13 @@ impl Plugin for CorePipelinePlugin {
|
|||||||
|
|
||||||
let render_app = app.sub_app(RenderApp);
|
let render_app = app.sub_app(RenderApp);
|
||||||
render_app
|
render_app
|
||||||
|
.init_resource::<DrawFunctions<Transparent2d>>()
|
||||||
|
.init_resource::<DrawFunctions<Transparent3d>>()
|
||||||
.add_system_to_stage(RenderStage::Extract, extract_clear_color)
|
.add_system_to_stage(RenderStage::Extract, extract_clear_color)
|
||||||
.add_system_to_stage(RenderStage::Extract, extract_core_pipeline_camera_phases)
|
.add_system_to_stage(RenderStage::Extract, extract_core_pipeline_camera_phases)
|
||||||
.add_system_to_stage(RenderStage::Prepare, prepare_core_views_system)
|
.add_system_to_stage(RenderStage::Prepare, prepare_core_views_system)
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Transparent2d>)
|
||||||
RenderStage::PhaseSort,
|
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Transparent3d>);
|
||||||
sort_phase_system::<Transparent2dPhase>,
|
|
||||||
)
|
|
||||||
.add_system_to_stage(
|
|
||||||
RenderStage::PhaseSort,
|
|
||||||
sort_phase_system::<Transparent3dPhase>,
|
|
||||||
);
|
|
||||||
|
|
||||||
let pass_node_2d = MainPass2dNode::new(&mut render_app.world);
|
let pass_node_2d = MainPass2dNode::new(&mut render_app.world);
|
||||||
let pass_node_3d = MainPass3dNode::new(&mut render_app.world);
|
let pass_node_3d = MainPass3dNode::new(&mut render_app.world);
|
||||||
@ -151,17 +146,51 @@ impl Plugin for CorePipelinePlugin {
|
|||||||
|
|
||||||
graph.add_node(node::MAIN_PASS_DEPENDENCIES, EmptyNode);
|
graph.add_node(node::MAIN_PASS_DEPENDENCIES, EmptyNode);
|
||||||
graph.add_node(node::MAIN_PASS_DRIVER, MainPassDriverNode);
|
graph.add_node(node::MAIN_PASS_DRIVER, MainPassDriverNode);
|
||||||
graph
|
|
||||||
.add_node_edge(ViewPlugin::VIEW_NODE, node::MAIN_PASS_DEPENDENCIES)
|
|
||||||
.unwrap();
|
|
||||||
graph
|
graph
|
||||||
.add_node_edge(node::MAIN_PASS_DEPENDENCIES, node::MAIN_PASS_DRIVER)
|
.add_node_edge(node::MAIN_PASS_DEPENDENCIES, node::MAIN_PASS_DRIVER)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Transparent3dPhase;
|
pub struct Transparent2d {
|
||||||
pub struct Transparent2dPhase;
|
pub sort_key: Handle<Image>,
|
||||||
|
pub entity: Entity,
|
||||||
|
pub draw_function: DrawFunctionId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhaseItem for Transparent2d {
|
||||||
|
type SortKey = Handle<Image>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sort_key(&self) -> Self::SortKey {
|
||||||
|
self.sort_key.clone_weak()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn draw_function(&self) -> DrawFunctionId {
|
||||||
|
self.draw_function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Transparent3d {
|
||||||
|
pub distance: f32,
|
||||||
|
pub entity: Entity,
|
||||||
|
pub draw_function: DrawFunctionId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhaseItem for Transparent3d {
|
||||||
|
type SortKey = FloatOrd;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sort_key(&self) -> Self::SortKey {
|
||||||
|
FloatOrd(self.distance)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn draw_function(&self) -> DrawFunctionId {
|
||||||
|
self.draw_function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ViewDepthTexture {
|
pub struct ViewDepthTexture {
|
||||||
pub texture: Texture,
|
pub texture: Texture,
|
||||||
@ -184,14 +213,14 @@ pub fn extract_core_pipeline_camera_phases(
|
|||||||
if let Some(entity) = camera_2d.entity {
|
if let Some(entity) = camera_2d.entity {
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(entity)
|
.get_or_spawn(entity)
|
||||||
.insert(RenderPhase::<Transparent2dPhase>::default());
|
.insert(RenderPhase::<Transparent2d>::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(camera_3d) = active_cameras.get(CameraPlugin::CAMERA_3D) {
|
if let Some(camera_3d) = active_cameras.get(CameraPlugin::CAMERA_3D) {
|
||||||
if let Some(entity) = camera_3d.entity {
|
if let Some(entity) = camera_3d.entity {
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(entity)
|
.get_or_spawn(entity)
|
||||||
.insert(RenderPhase::<Transparent3dPhase>::default());
|
.insert(RenderPhase::<Transparent3d>::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,7 +229,7 @@ pub fn prepare_core_views_system(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut texture_cache: ResMut<TextureCache>,
|
mut texture_cache: ResMut<TextureCache>,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
views: Query<(Entity, &ExtractedView), With<RenderPhase<Transparent3dPhase>>>,
|
views: Query<(Entity, &ExtractedView), With<RenderPhase<Transparent3d>>>,
|
||||||
) {
|
) {
|
||||||
for (entity, view) in views.iter() {
|
for (entity, view) in views.iter() {
|
||||||
let cached_texture = texture_cache.get(
|
let cached_texture = texture_cache.get(
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::{ClearColor, Transparent2dPhase};
|
use crate::{ClearColor, Transparent2d};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
||||||
@ -9,7 +9,7 @@ use bevy_render2::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub struct MainPass2dNode {
|
pub struct MainPass2dNode {
|
||||||
query: QueryState<&'static RenderPhase<Transparent2dPhase>, With<ExtractedView>>,
|
query: QueryState<&'static RenderPhase<Transparent2d>, With<ExtractedView>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MainPass2dNode {
|
impl MainPass2dNode {
|
||||||
@ -57,7 +57,9 @@ impl Node for MainPass2dNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||||
let draw_functions = world.get_resource::<DrawFunctions>().unwrap();
|
let draw_functions = world
|
||||||
|
.get_resource::<DrawFunctions<Transparent2d>>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let transparent_phase = self
|
let transparent_phase = self
|
||||||
.query
|
.query
|
||||||
@ -70,15 +72,9 @@ impl Node for MainPass2dNode {
|
|||||||
|
|
||||||
let mut draw_functions = draw_functions.write();
|
let mut draw_functions = draw_functions.write();
|
||||||
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
||||||
for drawable in transparent_phase.drawn_things.iter() {
|
for item in transparent_phase.items.iter() {
|
||||||
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
|
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
|
||||||
draw_function.draw(
|
draw_function.draw(world, &mut tracked_pass, view_entity, item);
|
||||||
world,
|
|
||||||
&mut tracked_pass,
|
|
||||||
view_entity,
|
|
||||||
drawable.draw_key,
|
|
||||||
drawable.sort_key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::{ClearColor, Transparent3dPhase};
|
use crate::{ClearColor, Transparent3d};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
||||||
@ -12,7 +12,7 @@ use bevy_render2::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub struct MainPass3dNode {
|
pub struct MainPass3dNode {
|
||||||
query: QueryState<&'static RenderPhase<Transparent3dPhase>, With<ExtractedView>>,
|
query: QueryState<&'static RenderPhase<Transparent3d>, With<ExtractedView>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MainPass3dNode {
|
impl MainPass3dNode {
|
||||||
@ -70,7 +70,9 @@ impl Node for MainPass3dNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||||
let draw_functions = world.get_resource::<DrawFunctions>().unwrap();
|
let draw_functions = world
|
||||||
|
.get_resource::<DrawFunctions<Transparent3d>>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let transparent_phase = self
|
let transparent_phase = self
|
||||||
.query
|
.query
|
||||||
@ -82,15 +84,9 @@ impl Node for MainPass3dNode {
|
|||||||
.begin_render_pass(&pass_descriptor);
|
.begin_render_pass(&pass_descriptor);
|
||||||
let mut draw_functions = draw_functions.write();
|
let mut draw_functions = draw_functions.write();
|
||||||
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
||||||
for drawable in transparent_phase.drawn_things.iter() {
|
for item in transparent_phase.items.iter() {
|
||||||
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
|
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
|
||||||
draw_function.draw(
|
draw_function.draw(world, &mut tracked_pass, view_entity, item);
|
||||||
world,
|
|
||||||
&mut tracked_pass,
|
|
||||||
view_entity,
|
|
||||||
drawable.draw_key,
|
|
||||||
drawable.sort_key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,10 +9,13 @@ pub use material::*;
|
|||||||
pub use render::*;
|
pub use render::*;
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
|
use bevy_asset::Handle;
|
||||||
|
use bevy_core_pipeline::Transparent3d;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
|
render_component::{ExtractComponentPlugin, UniformComponentPlugin},
|
||||||
render_graph::RenderGraph,
|
render_graph::RenderGraph,
|
||||||
render_phase::{sort_phase_system, DrawFunctions},
|
render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions},
|
||||||
RenderApp, RenderStage,
|
RenderApp, RenderStage,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -28,15 +31,17 @@ pub struct PbrPlugin;
|
|||||||
impl Plugin for PbrPlugin {
|
impl Plugin for PbrPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugin(StandardMaterialPlugin)
|
app.add_plugin(StandardMaterialPlugin)
|
||||||
|
.add_plugin(ExtractComponentPlugin::<Handle<StandardMaterial>>::default())
|
||||||
|
.add_plugin(UniformComponentPlugin::<MeshUniform>::default())
|
||||||
.init_resource::<AmbientLight>()
|
.init_resource::<AmbientLight>()
|
||||||
.init_resource::<DirectionalLightShadowMap>()
|
.init_resource::<DirectionalLightShadowMap>()
|
||||||
.init_resource::<PointLightShadowMap>();
|
.init_resource::<PointLightShadowMap>()
|
||||||
|
.init_resource::<AmbientLight>();
|
||||||
|
|
||||||
let render_app = app.sub_app(RenderApp);
|
let render_app = app.sub_app(RenderApp);
|
||||||
render_app
|
render_app
|
||||||
.add_system_to_stage(RenderStage::Extract, render::extract_meshes)
|
.add_system_to_stage(RenderStage::Extract, render::extract_meshes)
|
||||||
.add_system_to_stage(RenderStage::Extract, render::extract_lights)
|
.add_system_to_stage(RenderStage::Extract, render::extract_lights)
|
||||||
.add_system_to_stage(RenderStage::Prepare, render::prepare_meshes)
|
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
RenderStage::Prepare,
|
RenderStage::Prepare,
|
||||||
// this is added as an exclusive system because it contributes new views. it must run (and have Commands applied)
|
// this is added as an exclusive system because it contributes new views. it must run (and have Commands applied)
|
||||||
@ -44,27 +49,24 @@ impl Plugin for PbrPlugin {
|
|||||||
render::prepare_lights.exclusive_system(),
|
render::prepare_lights.exclusive_system(),
|
||||||
)
|
)
|
||||||
.add_system_to_stage(RenderStage::Queue, render::queue_meshes)
|
.add_system_to_stage(RenderStage::Queue, render::queue_meshes)
|
||||||
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<ShadowPhase>)
|
.add_system_to_stage(RenderStage::Queue, render::queue_shadows)
|
||||||
// FIXME: Hack to ensure RenderCommandQueue is initialized when PbrShaders is being initialized
|
.add_system_to_stage(RenderStage::Queue, render::queue_shadow_view_bind_group)
|
||||||
// .init_resource::<RenderCommandQueue>()
|
.add_system_to_stage(RenderStage::Queue, render::queue_transform_bind_group)
|
||||||
|
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Shadow>)
|
||||||
.init_resource::<PbrShaders>()
|
.init_resource::<PbrShaders>()
|
||||||
.init_resource::<ShadowShaders>()
|
.init_resource::<ShadowShaders>()
|
||||||
.init_resource::<MeshMeta>()
|
.init_resource::<DrawFunctions<Shadow>>()
|
||||||
.init_resource::<LightMeta>();
|
.init_resource::<LightMeta>();
|
||||||
|
|
||||||
let draw_pbr = DrawPbr::new(&mut render_app.world);
|
|
||||||
let draw_shadow_mesh = DrawShadowMesh::new(&mut render_app.world);
|
let draw_shadow_mesh = DrawShadowMesh::new(&mut render_app.world);
|
||||||
let shadow_pass_node = ShadowPassNode::new(&mut render_app.world);
|
let shadow_pass_node = ShadowPassNode::new(&mut render_app.world);
|
||||||
|
render_app.add_render_command::<Transparent3d, DrawPbr>();
|
||||||
let render_world = render_app.world.cell();
|
let render_world = render_app.world.cell();
|
||||||
let draw_functions = render_world.get_resource::<DrawFunctions>().unwrap();
|
let draw_functions = render_world
|
||||||
draw_functions.write().add(draw_pbr);
|
.get_resource::<DrawFunctions<Shadow>>()
|
||||||
|
.unwrap();
|
||||||
draw_functions.write().add(draw_shadow_mesh);
|
draw_functions.write().add(draw_shadow_mesh);
|
||||||
let mut graph = render_world.get_resource_mut::<RenderGraph>().unwrap();
|
let mut graph = render_world.get_resource_mut::<RenderGraph>().unwrap();
|
||||||
graph.add_node("pbr", PbrNode);
|
|
||||||
graph
|
|
||||||
.add_node_edge("pbr", bevy_core_pipeline::node::MAIN_PASS_DEPENDENCIES)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let draw_3d_graph = graph
|
let draw_3d_graph = graph
|
||||||
.get_sub_graph_mut(bevy_core_pipeline::draw_3d_graph::NAME)
|
.get_sub_graph_mut(bevy_core_pipeline::draw_3d_graph::NAME)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@ -1,15 +1,19 @@
|
|||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{AddAsset, Handle};
|
use bevy_asset::{AddAsset, Handle};
|
||||||
|
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
|
||||||
use bevy_math::Vec4;
|
use bevy_math::Vec4;
|
||||||
use bevy_reflect::TypeUuid;
|
use bevy_reflect::TypeUuid;
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
color::Color,
|
color::Color,
|
||||||
render_asset::{RenderAsset, RenderAssetPlugin},
|
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
|
||||||
render_resource::{Buffer, BufferInitDescriptor, BufferUsage},
|
render_resource::{BindGroup, Buffer, BufferInitDescriptor, BufferUsage, Sampler, TextureView},
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::RenderDevice,
|
||||||
texture::Image,
|
texture::Image,
|
||||||
};
|
};
|
||||||
use crevice::std140::{AsStd140, Std140};
|
use crevice::std140::{AsStd140, Std140};
|
||||||
|
use wgpu::{BindGroupDescriptor, BindGroupEntry, BindingResource};
|
||||||
|
|
||||||
|
use crate::PbrShaders;
|
||||||
|
|
||||||
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.frag!
|
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.frag!
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
@ -134,16 +138,17 @@ impl Plugin for StandardMaterialPlugin {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GpuStandardMaterial {
|
pub struct GpuStandardMaterial {
|
||||||
pub buffer: Buffer,
|
pub buffer: Buffer,
|
||||||
// FIXME: image handles feel unnecessary here but the extracted asset is discarded
|
pub bind_group: BindGroup,
|
||||||
pub base_color_texture: Option<Handle<Image>>,
|
|
||||||
pub emissive_texture: Option<Handle<Image>>,
|
|
||||||
pub metallic_roughness_texture: Option<Handle<Image>>,
|
|
||||||
pub occlusion_texture: Option<Handle<Image>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderAsset for StandardMaterial {
|
impl RenderAsset for StandardMaterial {
|
||||||
type ExtractedAsset = StandardMaterial;
|
type ExtractedAsset = StandardMaterial;
|
||||||
type PreparedAsset = GpuStandardMaterial;
|
type PreparedAsset = GpuStandardMaterial;
|
||||||
|
type Param = (
|
||||||
|
SRes<RenderDevice>,
|
||||||
|
SRes<PbrShaders>,
|
||||||
|
SRes<RenderAssets<Image>>,
|
||||||
|
);
|
||||||
|
|
||||||
fn extract_asset(&self) -> Self::ExtractedAsset {
|
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||||
self.clone()
|
self.clone()
|
||||||
@ -151,9 +156,41 @@ impl RenderAsset for StandardMaterial {
|
|||||||
|
|
||||||
fn prepare_asset(
|
fn prepare_asset(
|
||||||
material: Self::ExtractedAsset,
|
material: Self::ExtractedAsset,
|
||||||
render_device: &RenderDevice,
|
(render_device, pbr_shaders, gpu_images): &mut SystemParamItem<Self::Param>,
|
||||||
_render_queue: &RenderQueue,
|
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
|
||||||
) -> Self::PreparedAsset {
|
let (base_color_texture_view, base_color_sampler) = if let Some(result) =
|
||||||
|
image_handle_to_view_sampler(pbr_shaders, gpu_images, &material.base_color_texture)
|
||||||
|
{
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
return Err(PrepareAssetError::RetryNextUpdate(material));
|
||||||
|
};
|
||||||
|
|
||||||
|
let (emissive_texture_view, emissive_sampler) = if let Some(result) =
|
||||||
|
image_handle_to_view_sampler(pbr_shaders, gpu_images, &material.emissive_texture)
|
||||||
|
{
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
return Err(PrepareAssetError::RetryNextUpdate(material));
|
||||||
|
};
|
||||||
|
|
||||||
|
let (metallic_roughness_texture_view, metallic_roughness_sampler) = if let Some(result) =
|
||||||
|
image_handle_to_view_sampler(
|
||||||
|
pbr_shaders,
|
||||||
|
gpu_images,
|
||||||
|
&material.metallic_roughness_texture,
|
||||||
|
) {
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
return Err(PrepareAssetError::RetryNextUpdate(material));
|
||||||
|
};
|
||||||
|
let (occlusion_texture_view, occlusion_sampler) = if let Some(result) =
|
||||||
|
image_handle_to_view_sampler(pbr_shaders, gpu_images, &material.occlusion_texture)
|
||||||
|
{
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
return Err(PrepareAssetError::RetryNextUpdate(material));
|
||||||
|
};
|
||||||
let mut flags = StandardMaterialFlags::NONE;
|
let mut flags = StandardMaterialFlags::NONE;
|
||||||
if material.base_color_texture.is_some() {
|
if material.base_color_texture.is_some() {
|
||||||
flags |= StandardMaterialFlags::BASE_COLOR_TEXTURE;
|
flags |= StandardMaterialFlags::BASE_COLOR_TEXTURE;
|
||||||
@ -188,12 +225,65 @@ impl RenderAsset for StandardMaterial {
|
|||||||
usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
|
usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
|
||||||
contents: value_std140.as_bytes(),
|
contents: value_std140.as_bytes(),
|
||||||
});
|
});
|
||||||
GpuStandardMaterial {
|
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
buffer,
|
entries: &[
|
||||||
base_color_texture: material.base_color_texture,
|
BindGroupEntry {
|
||||||
emissive_texture: material.emissive_texture,
|
binding: 0,
|
||||||
metallic_roughness_texture: material.metallic_roughness_texture,
|
resource: buffer.as_entire_binding(),
|
||||||
occlusion_texture: material.occlusion_texture,
|
},
|
||||||
}
|
BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: BindingResource::TextureView(base_color_texture_view),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 2,
|
||||||
|
resource: BindingResource::Sampler(base_color_sampler),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 3,
|
||||||
|
resource: BindingResource::TextureView(emissive_texture_view),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 4,
|
||||||
|
resource: BindingResource::Sampler(emissive_sampler),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 5,
|
||||||
|
resource: BindingResource::TextureView(metallic_roughness_texture_view),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 6,
|
||||||
|
resource: BindingResource::Sampler(metallic_roughness_sampler),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 7,
|
||||||
|
resource: BindingResource::TextureView(occlusion_texture_view),
|
||||||
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 8,
|
||||||
|
resource: BindingResource::Sampler(occlusion_sampler),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
layout: &pbr_shaders.material_layout,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(GpuStandardMaterial { buffer, bind_group })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn image_handle_to_view_sampler<'a>(
|
||||||
|
pbr_pipeline: &'a PbrShaders,
|
||||||
|
gpu_images: &'a RenderAssets<Image>,
|
||||||
|
handle_option: &Option<Handle<Image>>,
|
||||||
|
) -> Option<(&'a TextureView, &'a Sampler)> {
|
||||||
|
if let Some(handle) = handle_option {
|
||||||
|
let gpu_image = gpu_images.get(handle)?;
|
||||||
|
Some((&gpu_image.texture_view, &gpu_image.sampler))
|
||||||
|
} else {
|
||||||
|
Some((
|
||||||
|
&pbr_pipeline.dummy_white_gpu_image.texture_view,
|
||||||
|
&pbr_pipeline.dummy_white_gpu_image.sampler,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,11 @@ var view: View;
|
|||||||
[[block]]
|
[[block]]
|
||||||
struct Mesh {
|
struct Mesh {
|
||||||
model: mat4x4<f32>;
|
model: mat4x4<f32>;
|
||||||
|
inverse_transpose_model: mat4x4<f32>;
|
||||||
|
// 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options.
|
||||||
|
flags: u32;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[group(1), binding(0)]]
|
[[group(1), binding(0)]]
|
||||||
var mesh: Mesh;
|
var mesh: Mesh;
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,30 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
AmbientLight, DirectionalLight, DirectionalLightShadowMap, ExtractedMeshes, MeshMeta,
|
AmbientLight, DirectionalLight, DirectionalLightShadowMap, MeshUniform, NotShadowCaster,
|
||||||
PbrShaders, PointLight, PointLightShadowMap,
|
PbrShaders, PointLight, PointLightShadowMap, TransformBindGroup,
|
||||||
|
};
|
||||||
|
use bevy_asset::Handle;
|
||||||
|
use bevy_core::FloatOrd;
|
||||||
|
use bevy_core_pipeline::Transparent3d;
|
||||||
|
use bevy_ecs::{
|
||||||
|
prelude::*,
|
||||||
|
system::{lifetimeless::*, SystemState},
|
||||||
};
|
};
|
||||||
use bevy_core_pipeline::Transparent3dPhase;
|
|
||||||
use bevy_ecs::{prelude::*, system::SystemState};
|
|
||||||
use bevy_math::{const_vec3, Mat4, Vec3, Vec4};
|
use bevy_math::{const_vec3, Mat4, Vec3, Vec4};
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
camera::CameraProjection,
|
camera::CameraProjection,
|
||||||
color::Color,
|
color::Color,
|
||||||
mesh::Mesh,
|
mesh::Mesh,
|
||||||
render_asset::RenderAssets,
|
render_asset::RenderAssets,
|
||||||
|
render_component::DynamicUniformIndex,
|
||||||
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
||||||
render_phase::{Draw, DrawFunctions, RenderPhase, TrackedRenderPass},
|
render_phase::{
|
||||||
|
Draw, DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase, TrackedRenderPass,
|
||||||
|
},
|
||||||
render_resource::*,
|
render_resource::*,
|
||||||
renderer::{RenderContext, RenderDevice},
|
renderer::{RenderContext, RenderDevice, RenderQueue},
|
||||||
shader::Shader,
|
shader::Shader,
|
||||||
texture::*,
|
texture::*,
|
||||||
view::{ExtractedView, ViewUniformOffset},
|
view::{ExtractedView, ViewUniformOffset, ViewUniforms},
|
||||||
};
|
};
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
use crevice::std140::AsStd140;
|
use crevice::std140::AsStd140;
|
||||||
@ -374,8 +382,9 @@ pub fn prepare_lights(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut texture_cache: ResMut<TextureCache>,
|
mut texture_cache: ResMut<TextureCache>,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
|
render_queue: Res<RenderQueue>,
|
||||||
mut light_meta: ResMut<LightMeta>,
|
mut light_meta: ResMut<LightMeta>,
|
||||||
views: Query<Entity, With<RenderPhase<Transparent3dPhase>>>,
|
views: Query<Entity, With<RenderPhase<Transparent3d>>>,
|
||||||
ambient_light: Res<ExtractedAmbientLight>,
|
ambient_light: Res<ExtractedAmbientLight>,
|
||||||
point_light_shadow_map: Res<ExtractedPointLightShadowMap>,
|
point_light_shadow_map: Res<ExtractedPointLightShadowMap>,
|
||||||
directional_light_shadow_map: Res<ExtractedDirectionalLightShadowMap>,
|
directional_light_shadow_map: Res<ExtractedDirectionalLightShadowMap>,
|
||||||
@ -477,7 +486,7 @@ pub fn prepare_lights(
|
|||||||
transform: view_translation * view_rotation,
|
transform: view_translation * view_rotation,
|
||||||
projection,
|
projection,
|
||||||
},
|
},
|
||||||
RenderPhase::<ShadowPhase>::default(),
|
RenderPhase::<Shadow>::default(),
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
view_lights.push(view_light_entity);
|
view_lights.push(view_light_entity);
|
||||||
@ -563,7 +572,7 @@ pub fn prepare_lights(
|
|||||||
transform: GlobalTransform::from_matrix(view.inverse()),
|
transform: GlobalTransform::from_matrix(view.inverse()),
|
||||||
projection,
|
projection,
|
||||||
},
|
},
|
||||||
RenderPhase::<ShadowPhase>::default(),
|
RenderPhase::<Shadow>::default(),
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
view_lights.push(view_light_entity);
|
view_lights.push(view_light_entity);
|
||||||
@ -604,16 +613,77 @@ pub fn prepare_lights(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
light_meta
|
light_meta.view_gpu_lights.write_buffer(&render_queue);
|
||||||
.view_gpu_lights
|
|
||||||
.write_to_staging_buffer(&render_device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ShadowPhase;
|
pub fn queue_shadow_view_bind_group(
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
shadow_shaders: Res<ShadowShaders>,
|
||||||
|
mut light_meta: ResMut<LightMeta>,
|
||||||
|
view_uniforms: Res<ViewUniforms>,
|
||||||
|
) {
|
||||||
|
if let Some(view_binding) = view_uniforms.uniforms.binding() {
|
||||||
|
light_meta.shadow_view_bind_group =
|
||||||
|
Some(render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
|
entries: &[BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: view_binding,
|
||||||
|
}],
|
||||||
|
label: None,
|
||||||
|
layout: &shadow_shaders.view_layout,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_shadows(
|
||||||
|
shadow_draw_functions: Res<DrawFunctions<Shadow>>,
|
||||||
|
casting_meshes: Query<Entity, (With<Handle<Mesh>>, Without<NotShadowCaster>)>,
|
||||||
|
mut view_lights: Query<&ViewLights>,
|
||||||
|
mut view_light_shadow_phases: Query<&mut RenderPhase<Shadow>>,
|
||||||
|
) {
|
||||||
|
for view_lights in view_lights.iter_mut() {
|
||||||
|
// ultimately lights should check meshes for relevancy (ex: light views can "see" different meshes than the main view can)
|
||||||
|
let draw_shadow_mesh = shadow_draw_functions
|
||||||
|
.read()
|
||||||
|
.get_id::<DrawShadowMesh>()
|
||||||
|
.unwrap();
|
||||||
|
for view_light_entity in view_lights.lights.iter().copied() {
|
||||||
|
let mut shadow_phase = view_light_shadow_phases.get_mut(view_light_entity).unwrap();
|
||||||
|
// TODO: this should only queue up meshes that are actually visible by each "light view"
|
||||||
|
for entity in casting_meshes.iter() {
|
||||||
|
shadow_phase.add(Shadow {
|
||||||
|
draw_function: draw_shadow_mesh,
|
||||||
|
entity,
|
||||||
|
distance: 0.0, // TODO: sort back-to-front
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Shadow {
|
||||||
|
pub distance: f32,
|
||||||
|
pub entity: Entity,
|
||||||
|
pub draw_function: DrawFunctionId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhaseItem for Shadow {
|
||||||
|
type SortKey = FloatOrd;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sort_key(&self) -> Self::SortKey {
|
||||||
|
FloatOrd(self.distance)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn draw_function(&self) -> DrawFunctionId {
|
||||||
|
self.draw_function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ShadowPassNode {
|
pub struct ShadowPassNode {
|
||||||
main_view_query: QueryState<&'static ViewLights>,
|
main_view_query: QueryState<&'static ViewLights>,
|
||||||
view_light_query: QueryState<(&'static ViewLight, &'static RenderPhase<ShadowPhase>)>,
|
view_light_query: QueryState<(&'static ViewLight, &'static RenderPhase<Shadow>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShadowPassNode {
|
impl ShadowPassNode {
|
||||||
@ -663,22 +733,16 @@ impl Node for ShadowPassNode {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
let draw_functions = world.get_resource::<DrawFunctions>().unwrap();
|
let draw_functions = world.get_resource::<DrawFunctions<Shadow>>().unwrap();
|
||||||
|
|
||||||
let render_pass = render_context
|
let render_pass = render_context
|
||||||
.command_encoder
|
.command_encoder
|
||||||
.begin_render_pass(&pass_descriptor);
|
.begin_render_pass(&pass_descriptor);
|
||||||
let mut draw_functions = draw_functions.write();
|
let mut draw_functions = draw_functions.write();
|
||||||
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
||||||
for drawable in shadow_phase.drawn_things.iter() {
|
for item in shadow_phase.items.iter() {
|
||||||
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
|
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
|
||||||
draw_function.draw(
|
draw_function.draw(world, &mut tracked_pass, view_light_entity, item);
|
||||||
world,
|
|
||||||
&mut tracked_pass,
|
|
||||||
view_light_entity,
|
|
||||||
drawable.draw_key,
|
|
||||||
drawable.sort_key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -687,16 +751,15 @@ impl Node for ShadowPassNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type DrawShadowMeshParams<'s, 'w> = (
|
|
||||||
Res<'w, ShadowShaders>,
|
|
||||||
Res<'w, ExtractedMeshes>,
|
|
||||||
Res<'w, LightMeta>,
|
|
||||||
Res<'w, MeshMeta>,
|
|
||||||
Res<'w, RenderAssets<Mesh>>,
|
|
||||||
Query<'w, 's, &'w ViewUniformOffset>,
|
|
||||||
);
|
|
||||||
pub struct DrawShadowMesh {
|
pub struct DrawShadowMesh {
|
||||||
params: SystemState<DrawShadowMeshParams<'static, 'static>>,
|
params: SystemState<(
|
||||||
|
SRes<ShadowShaders>,
|
||||||
|
SRes<LightMeta>,
|
||||||
|
SRes<TransformBindGroup>,
|
||||||
|
SRes<RenderAssets<Mesh>>,
|
||||||
|
SQuery<(Read<DynamicUniformIndex<MeshUniform>>, Read<Handle<Mesh>>)>,
|
||||||
|
SQuery<Read<ViewUniformOffset>>,
|
||||||
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawShadowMesh {
|
impl DrawShadowMesh {
|
||||||
@ -707,21 +770,19 @@ impl DrawShadowMesh {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Draw for DrawShadowMesh {
|
impl Draw<Shadow> for DrawShadowMesh {
|
||||||
fn draw<'w>(
|
fn draw<'w>(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: &'w World,
|
world: &'w World,
|
||||||
pass: &mut TrackedRenderPass<'w>,
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
view: Entity,
|
view: Entity,
|
||||||
draw_key: usize,
|
item: &Shadow,
|
||||||
_sort_key: usize,
|
|
||||||
) {
|
) {
|
||||||
let (shadow_shaders, extracted_meshes, light_meta, mesh_meta, meshes, views) =
|
let (shadow_shaders, light_meta, transform_bind_group, meshes, items, views) =
|
||||||
self.params.get(world);
|
self.params.get(world);
|
||||||
|
let (transform_index, mesh_handle) = items.get(item.entity).unwrap();
|
||||||
let view_uniform_offset = views.get(view).unwrap();
|
let view_uniform_offset = views.get(view).unwrap();
|
||||||
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
|
pass.set_render_pipeline(&shadow_shaders.into_inner().pipeline);
|
||||||
let shadow_shaders = shadow_shaders.into_inner();
|
|
||||||
pass.set_render_pipeline(&shadow_shaders.pipeline);
|
|
||||||
pass.set_bind_group(
|
pass.set_bind_group(
|
||||||
0,
|
0,
|
||||||
light_meta
|
light_meta
|
||||||
@ -732,18 +793,13 @@ impl Draw for DrawShadowMesh {
|
|||||||
&[view_uniform_offset.offset],
|
&[view_uniform_offset.offset],
|
||||||
);
|
);
|
||||||
|
|
||||||
let transform_bindgroup_key = mesh_meta.mesh_transform_bind_group_key.unwrap();
|
|
||||||
pass.set_bind_group(
|
pass.set_bind_group(
|
||||||
1,
|
1,
|
||||||
mesh_meta
|
&transform_bind_group.into_inner().value,
|
||||||
.into_inner()
|
&[transform_index.index()],
|
||||||
.mesh_transform_bind_group
|
|
||||||
.get_value(transform_bindgroup_key)
|
|
||||||
.unwrap(),
|
|
||||||
&[extracted_mesh.transform_binding_offset],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let gpu_mesh = meshes.into_inner().get(&extracted_mesh.mesh).unwrap();
|
let gpu_mesh = meshes.into_inner().get(mesh_handle).unwrap();
|
||||||
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
|
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
|
||||||
if let Some(index_info) = &gpu_mesh.index_info {
|
if let Some(index_info) = &gpu_mesh.index_info {
|
||||||
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
|
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
|
||||||
|
|||||||
@ -1,37 +1,127 @@
|
|||||||
mod light;
|
mod light;
|
||||||
|
|
||||||
pub use light::*;
|
pub use light::*;
|
||||||
|
|
||||||
use crate::{NotShadowCaster, NotShadowReceiver, StandardMaterial, StandardMaterialUniformData};
|
use crate::{NotShadowCaster, NotShadowReceiver, StandardMaterial, StandardMaterialUniformData};
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::Handle;
|
||||||
use bevy_core_pipeline::Transparent3dPhase;
|
use bevy_core_pipeline::Transparent3d;
|
||||||
use bevy_ecs::{prelude::*, system::SystemState};
|
use bevy_ecs::{
|
||||||
|
prelude::*,
|
||||||
|
system::{lifetimeless::*, SystemParamItem},
|
||||||
|
};
|
||||||
use bevy_math::Mat4;
|
use bevy_math::Mat4;
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
mesh::Mesh,
|
mesh::Mesh,
|
||||||
render_asset::RenderAssets,
|
render_asset::RenderAssets,
|
||||||
render_graph::{Node, NodeRunError, RenderGraphContext},
|
render_component::{ComponentUniforms, DynamicUniformIndex},
|
||||||
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
|
render_phase::{DrawFunctions, RenderCommand, RenderPhase, TrackedRenderPass},
|
||||||
render_resource::*,
|
render_resource::*,
|
||||||
renderer::{RenderContext, RenderDevice, RenderQueue},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
shader::Shader,
|
shader::Shader,
|
||||||
texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo},
|
texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo},
|
||||||
view::{ExtractedView, ViewMeta, ViewUniformOffset},
|
view::{ExtractedView, ViewUniformOffset, ViewUniforms},
|
||||||
};
|
};
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
use bevy_utils::slab::{FrameSlabMap, FrameSlabMapKey};
|
|
||||||
use crevice::std140::AsStd140;
|
use crevice::std140::AsStd140;
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
Extent3d, ImageCopyTexture, ImageDataLayout, Origin3d, TextureDimension, TextureFormat,
|
Extent3d, ImageCopyTexture, ImageDataLayout, Origin3d, TextureDimension, TextureFormat,
|
||||||
TextureViewDescriptor,
|
TextureViewDescriptor,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(AsStd140, Clone)]
|
||||||
|
pub struct MeshUniform {
|
||||||
|
pub transform: Mat4,
|
||||||
|
pub inverse_transpose_model: Mat4,
|
||||||
|
pub flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.wgsl!
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct MeshFlags: u32 {
|
||||||
|
const SHADOW_RECEIVER = (1 << 0);
|
||||||
|
const NONE = 0;
|
||||||
|
const UNINITIALIZED = 0xFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_meshes(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut previous_caster_len: Local<usize>,
|
||||||
|
mut previous_not_caster_len: Local<usize>,
|
||||||
|
caster_query: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
&GlobalTransform,
|
||||||
|
&Handle<Mesh>,
|
||||||
|
Option<&NotShadowReceiver>,
|
||||||
|
),
|
||||||
|
Without<NotShadowCaster>,
|
||||||
|
>,
|
||||||
|
not_caster_query: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
&GlobalTransform,
|
||||||
|
&Handle<Mesh>,
|
||||||
|
Option<&NotShadowReceiver>,
|
||||||
|
),
|
||||||
|
With<NotShadowCaster>,
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
let mut caster_values = Vec::with_capacity(*previous_caster_len);
|
||||||
|
for (entity, transform, handle, not_receiver) in caster_query.iter() {
|
||||||
|
let transform = transform.compute_matrix();
|
||||||
|
caster_values.push((
|
||||||
|
entity,
|
||||||
|
(
|
||||||
|
handle.clone_weak(),
|
||||||
|
MeshUniform {
|
||||||
|
flags: if not_receiver.is_some() {
|
||||||
|
MeshFlags::empty().bits
|
||||||
|
} else {
|
||||||
|
MeshFlags::SHADOW_RECEIVER.bits
|
||||||
|
},
|
||||||
|
transform,
|
||||||
|
inverse_transpose_model: transform.inverse().transpose(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
*previous_caster_len = caster_values.len();
|
||||||
|
commands.insert_or_spawn_batch(caster_values);
|
||||||
|
|
||||||
|
let mut not_caster_values = Vec::with_capacity(*previous_not_caster_len);
|
||||||
|
for (entity, transform, handle, not_receiver) in not_caster_query.iter() {
|
||||||
|
let transform = transform.compute_matrix();
|
||||||
|
not_caster_values.push((
|
||||||
|
entity,
|
||||||
|
(
|
||||||
|
handle.clone_weak(),
|
||||||
|
MeshUniform {
|
||||||
|
flags: if not_receiver.is_some() {
|
||||||
|
MeshFlags::empty().bits
|
||||||
|
} else {
|
||||||
|
MeshFlags::SHADOW_RECEIVER.bits
|
||||||
|
},
|
||||||
|
transform,
|
||||||
|
inverse_transpose_model: transform.inverse().transpose(),
|
||||||
|
},
|
||||||
|
NotShadowCaster,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
*previous_not_caster_len = not_caster_values.len();
|
||||||
|
commands.insert_or_spawn_batch(not_caster_values);
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PbrShaders {
|
pub struct PbrShaders {
|
||||||
pipeline: RenderPipeline,
|
pub pipeline: RenderPipeline,
|
||||||
view_layout: BindGroupLayout,
|
pub shader_module: ShaderModule,
|
||||||
material_layout: BindGroupLayout,
|
pub view_layout: BindGroupLayout,
|
||||||
mesh_layout: BindGroupLayout,
|
pub material_layout: BindGroupLayout,
|
||||||
|
pub mesh_layout: BindGroupLayout,
|
||||||
// This dummy white texture is to be used in place of optional StandardMaterial textures
|
// This dummy white texture is to be used in place of optional StandardMaterial textures
|
||||||
dummy_white_gpu_image: GpuImage,
|
pub dummy_white_gpu_image: GpuImage,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
|
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
|
||||||
@ -41,7 +131,6 @@ impl FromWorld for PbrShaders {
|
|||||||
let shader = Shader::from_wgsl(include_str!("pbr.wgsl"));
|
let shader = Shader::from_wgsl(include_str!("pbr.wgsl"));
|
||||||
let shader_module = render_device.create_shader_module(&shader);
|
let shader_module = render_device.create_shader_module(&shader);
|
||||||
|
|
||||||
// TODO: move this into ViewMeta?
|
|
||||||
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
entries: &[
|
entries: &[
|
||||||
// View
|
// View
|
||||||
@ -361,6 +450,7 @@ impl FromWorld for PbrShaders {
|
|||||||
};
|
};
|
||||||
PbrShaders {
|
PbrShaders {
|
||||||
pipeline,
|
pipeline,
|
||||||
|
shader_module,
|
||||||
view_layout,
|
view_layout,
|
||||||
material_layout,
|
material_layout,
|
||||||
mesh_layout,
|
mesh_layout,
|
||||||
@ -369,236 +459,69 @@ impl FromWorld for PbrShaders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExtractedMesh {
|
pub struct TransformBindGroup {
|
||||||
transform: Mat4,
|
pub value: BindGroup,
|
||||||
mesh: Handle<Mesh>,
|
|
||||||
transform_binding_offset: u32,
|
|
||||||
material_handle: Handle<StandardMaterial>,
|
|
||||||
casts_shadows: bool,
|
|
||||||
receives_shadows: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExtractedMeshes {
|
pub fn queue_transform_bind_group(
|
||||||
meshes: Vec<ExtractedMesh>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extract_meshes(
|
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
meshes: Res<Assets<Mesh>>,
|
pbr_shaders: Res<PbrShaders>,
|
||||||
materials: Res<Assets<StandardMaterial>>,
|
|
||||||
images: Res<Assets<Image>>,
|
|
||||||
query: Query<(
|
|
||||||
&GlobalTransform,
|
|
||||||
&Handle<Mesh>,
|
|
||||||
&Handle<StandardMaterial>,
|
|
||||||
Option<&NotShadowCaster>,
|
|
||||||
Option<&NotShadowReceiver>,
|
|
||||||
)>,
|
|
||||||
) {
|
|
||||||
let mut extracted_meshes = Vec::new();
|
|
||||||
for (
|
|
||||||
transform,
|
|
||||||
mesh_handle,
|
|
||||||
material_handle,
|
|
||||||
maybe_not_shadow_caster,
|
|
||||||
maybe_not_shadow_receiver,
|
|
||||||
) in query.iter()
|
|
||||||
{
|
|
||||||
if !meshes.contains(mesh_handle) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(material) = materials.get(material_handle) {
|
|
||||||
if let Some(ref image) = material.base_color_texture {
|
|
||||||
if !images.contains(image) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(ref image) = material.emissive_texture {
|
|
||||||
if !images.contains(image) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(ref image) = material.metallic_roughness_texture {
|
|
||||||
if !images.contains(image) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(ref image) = material.occlusion_texture {
|
|
||||||
if !images.contains(image) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
extracted_meshes.push(ExtractedMesh {
|
|
||||||
transform: transform.compute_matrix(),
|
|
||||||
mesh: mesh_handle.clone_weak(),
|
|
||||||
transform_binding_offset: 0,
|
|
||||||
material_handle: material_handle.clone_weak(),
|
|
||||||
// NOTE: Double-negative is so that meshes cast and receive shadows by default
|
|
||||||
// Not not shadow caster means that this mesh is a shadow caster
|
|
||||||
casts_shadows: maybe_not_shadow_caster.is_none(),
|
|
||||||
receives_shadows: maybe_not_shadow_receiver.is_none(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commands.insert_resource(ExtractedMeshes {
|
|
||||||
meshes: extracted_meshes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MeshDrawInfo {
|
|
||||||
// TODO: compare cost of doing this vs cloning the BindGroup?
|
|
||||||
material_bind_group_key: FrameSlabMapKey<BufferId, BindGroup>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, AsStd140)]
|
|
||||||
pub struct MeshUniform {
|
|
||||||
model: Mat4,
|
|
||||||
inverse_transpose_model: Mat4,
|
|
||||||
flags: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.wgsl!
|
|
||||||
bitflags::bitflags! {
|
|
||||||
#[repr(transparent)]
|
|
||||||
struct MeshFlags: u32 {
|
|
||||||
const SHADOW_RECEIVER = (1 << 0);
|
|
||||||
const NONE = 0;
|
|
||||||
const UNINITIALIZED = 0xFFFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MeshMeta {
|
|
||||||
transform_uniforms: DynamicUniformVec<MeshUniform>,
|
|
||||||
material_bind_groups: FrameSlabMap<BufferId, BindGroup>,
|
|
||||||
mesh_transform_bind_group: FrameSlabMap<BufferId, BindGroup>,
|
|
||||||
mesh_transform_bind_group_key: Option<FrameSlabMapKey<BufferId, BindGroup>>,
|
|
||||||
mesh_draw_info: Vec<MeshDrawInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepare_meshes(
|
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
mut mesh_meta: ResMut<MeshMeta>,
|
transform_uniforms: Res<ComponentUniforms<MeshUniform>>,
|
||||||
mut extracted_meshes: ResMut<ExtractedMeshes>,
|
|
||||||
) {
|
) {
|
||||||
mesh_meta
|
if let Some(binding) = transform_uniforms.uniforms().binding() {
|
||||||
.transform_uniforms
|
commands.insert_resource(TransformBindGroup {
|
||||||
.reserve_and_clear(extracted_meshes.meshes.len(), &render_device);
|
value: render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
for extracted_mesh in extracted_meshes.meshes.iter_mut() {
|
entries: &[BindGroupEntry {
|
||||||
let model = extracted_mesh.transform;
|
binding: 0,
|
||||||
let inverse_transpose_model = model.inverse().transpose();
|
resource: binding,
|
||||||
let flags = if extracted_mesh.receives_shadows {
|
}],
|
||||||
MeshFlags::SHADOW_RECEIVER
|
label: None,
|
||||||
} else {
|
layout: &pbr_shaders.mesh_layout,
|
||||||
MeshFlags::NONE
|
}),
|
||||||
};
|
|
||||||
extracted_mesh.transform_binding_offset = mesh_meta.transform_uniforms.push(MeshUniform {
|
|
||||||
model,
|
|
||||||
inverse_transpose_model,
|
|
||||||
flags: flags.bits,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh_meta
|
|
||||||
.transform_uniforms
|
|
||||||
.write_to_staging_buffer(&render_device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MeshViewBindGroups {
|
pub struct PbrViewBindGroup {
|
||||||
view: BindGroup,
|
pub value: BindGroup,
|
||||||
}
|
|
||||||
|
|
||||||
fn image_handle_to_view_sampler<'a>(
|
|
||||||
pbr_shaders: &'a PbrShaders,
|
|
||||||
gpu_images: &'a RenderAssets<Image>,
|
|
||||||
image_option: &Option<Handle<Image>>,
|
|
||||||
) -> (&'a TextureView, &'a Sampler) {
|
|
||||||
image_option.as_ref().map_or(
|
|
||||||
(
|
|
||||||
&pbr_shaders.dummy_white_gpu_image.texture_view,
|
|
||||||
&pbr_shaders.dummy_white_gpu_image.sampler,
|
|
||||||
),
|
|
||||||
|image_handle| {
|
|
||||||
let gpu_image = gpu_images
|
|
||||||
.get(image_handle)
|
|
||||||
.expect("only materials with valid textures should be drawn");
|
|
||||||
(&gpu_image.texture_view, &gpu_image.sampler)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn queue_meshes(
|
pub fn queue_meshes(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
draw_functions: Res<DrawFunctions>,
|
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
pbr_shaders: Res<PbrShaders>,
|
pbr_shaders: Res<PbrShaders>,
|
||||||
shadow_shaders: Res<ShadowShaders>,
|
shadow_shaders: Res<ShadowShaders>,
|
||||||
mesh_meta: ResMut<MeshMeta>,
|
light_meta: Res<LightMeta>,
|
||||||
mut light_meta: ResMut<LightMeta>,
|
view_uniforms: Res<ViewUniforms>,
|
||||||
view_meta: Res<ViewMeta>,
|
|
||||||
mut extracted_meshes: ResMut<ExtractedMeshes>,
|
|
||||||
gpu_images: Res<RenderAssets<Image>>,
|
|
||||||
render_materials: Res<RenderAssets<StandardMaterial>>,
|
render_materials: Res<RenderAssets<StandardMaterial>>,
|
||||||
|
standard_material_meshes: Query<
|
||||||
|
(Entity, &Handle<StandardMaterial>, &MeshUniform),
|
||||||
|
With<Handle<Mesh>>,
|
||||||
|
>,
|
||||||
mut views: Query<(
|
mut views: Query<(
|
||||||
Entity,
|
Entity,
|
||||||
&ExtractedView,
|
&ExtractedView,
|
||||||
&ViewLights,
|
&ViewLights,
|
||||||
&mut RenderPhase<Transparent3dPhase>,
|
&mut RenderPhase<Transparent3d>,
|
||||||
)>,
|
)>,
|
||||||
mut view_light_shadow_phases: Query<&mut RenderPhase<ShadowPhase>>,
|
|
||||||
) {
|
) {
|
||||||
let mesh_meta = mesh_meta.into_inner();
|
if let (Some(view_binding), Some(light_binding)) = (
|
||||||
|
view_uniforms.uniforms.binding(),
|
||||||
if view_meta.uniforms.is_empty() {
|
light_meta.view_gpu_lights.binding(),
|
||||||
return;
|
) {
|
||||||
}
|
|
||||||
|
|
||||||
light_meta.shadow_view_bind_group.get_or_insert_with(|| {
|
|
||||||
render_device.create_bind_group(&BindGroupDescriptor {
|
|
||||||
entries: &[BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: view_meta.uniforms.binding(),
|
|
||||||
}],
|
|
||||||
label: None,
|
|
||||||
layout: &shadow_shaders.view_layout,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
if extracted_meshes.meshes.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let transform_uniforms = &mesh_meta.transform_uniforms;
|
|
||||||
mesh_meta.mesh_transform_bind_group.next_frame();
|
|
||||||
mesh_meta.mesh_transform_bind_group_key =
|
|
||||||
Some(mesh_meta.mesh_transform_bind_group.get_or_insert_with(
|
|
||||||
transform_uniforms.uniform_buffer().unwrap().id(),
|
|
||||||
|| {
|
|
||||||
render_device.create_bind_group(&BindGroupDescriptor {
|
|
||||||
entries: &[BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: transform_uniforms.binding(),
|
|
||||||
}],
|
|
||||||
label: None,
|
|
||||||
layout: &pbr_shaders.mesh_layout,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
));
|
|
||||||
for (entity, view, view_lights, mut transparent_phase) in views.iter_mut() {
|
for (entity, view, view_lights, mut transparent_phase) in views.iter_mut() {
|
||||||
// TODO: cache this?
|
|
||||||
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
entries: &[
|
entries: &[
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
resource: view_meta.uniforms.binding(),
|
resource: view_binding.clone(),
|
||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 1,
|
binding: 1,
|
||||||
resource: light_meta.view_gpu_lights.binding(),
|
resource: light_binding.clone(),
|
||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 2,
|
binding: 2,
|
||||||
@ -618,227 +541,145 @@ pub fn queue_meshes(
|
|||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 5,
|
binding: 5,
|
||||||
resource: BindingResource::Sampler(&shadow_shaders.directional_light_sampler),
|
resource: BindingResource::Sampler(
|
||||||
|
&shadow_shaders.directional_light_sampler,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
label: None,
|
label: None,
|
||||||
layout: &pbr_shaders.view_layout,
|
layout: &pbr_shaders.view_layout,
|
||||||
});
|
});
|
||||||
|
|
||||||
commands.entity(entity).insert(MeshViewBindGroups {
|
commands.entity(entity).insert(PbrViewBindGroup {
|
||||||
view: view_bind_group,
|
value: view_bind_group,
|
||||||
});
|
});
|
||||||
|
|
||||||
let draw_pbr = draw_functions.read().get_id::<DrawPbr>().unwrap();
|
let draw_pbr = transparent_3d_draw_functions
|
||||||
mesh_meta.mesh_draw_info.clear();
|
.read()
|
||||||
mesh_meta.material_bind_groups.next_frame();
|
.get_id::<DrawPbr>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let view_matrix = view.transform.compute_matrix();
|
let view_matrix = view.transform.compute_matrix();
|
||||||
let view_row_2 = view_matrix.row(2);
|
let view_row_2 = view_matrix.row(2);
|
||||||
for (i, mesh) in extracted_meshes.meshes.iter_mut().enumerate() {
|
|
||||||
let gpu_material = &render_materials
|
|
||||||
.get(&mesh.material_handle)
|
|
||||||
.expect("Failed to get StandardMaterial PreparedAsset");
|
|
||||||
let material_bind_group_key =
|
|
||||||
mesh_meta
|
|
||||||
.material_bind_groups
|
|
||||||
.get_or_insert_with(gpu_material.buffer.id(), || {
|
|
||||||
let (base_color_texture_view, base_color_sampler) =
|
|
||||||
image_handle_to_view_sampler(
|
|
||||||
&pbr_shaders,
|
|
||||||
&gpu_images,
|
|
||||||
&gpu_material.base_color_texture,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (emissive_texture_view, emissive_sampler) =
|
|
||||||
image_handle_to_view_sampler(
|
|
||||||
&pbr_shaders,
|
|
||||||
&gpu_images,
|
|
||||||
&gpu_material.emissive_texture,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (metallic_roughness_texture_view, metallic_roughness_sampler) =
|
|
||||||
image_handle_to_view_sampler(
|
|
||||||
&pbr_shaders,
|
|
||||||
&gpu_images,
|
|
||||||
&gpu_material.metallic_roughness_texture,
|
|
||||||
);
|
|
||||||
let (occlusion_texture_view, occlusion_sampler) =
|
|
||||||
image_handle_to_view_sampler(
|
|
||||||
&pbr_shaders,
|
|
||||||
&gpu_images,
|
|
||||||
&gpu_material.occlusion_texture,
|
|
||||||
);
|
|
||||||
render_device.create_bind_group(&BindGroupDescriptor {
|
|
||||||
entries: &[
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: gpu_material.buffer.as_entire_binding(),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: BindingResource::TextureView(base_color_texture_view),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 2,
|
|
||||||
resource: BindingResource::Sampler(base_color_sampler),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 3,
|
|
||||||
resource: BindingResource::TextureView(emissive_texture_view),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 4,
|
|
||||||
resource: BindingResource::Sampler(emissive_sampler),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 5,
|
|
||||||
resource: BindingResource::TextureView(
|
|
||||||
metallic_roughness_texture_view,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 6,
|
|
||||||
resource: BindingResource::Sampler(metallic_roughness_sampler),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 7,
|
|
||||||
resource: BindingResource::TextureView(occlusion_texture_view),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 8,
|
|
||||||
resource: BindingResource::Sampler(occlusion_sampler),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
label: None,
|
|
||||||
layout: &pbr_shaders.material_layout,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
mesh_meta.mesh_draw_info.push(MeshDrawInfo {
|
|
||||||
material_bind_group_key,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
for (entity, material_handle, mesh_uniform) in standard_material_meshes.iter() {
|
||||||
|
if !render_materials.contains_key(material_handle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// NOTE: row 2 of the view matrix dotted with column 3 of the model matrix
|
// NOTE: row 2 of the view matrix dotted with column 3 of the model matrix
|
||||||
// gives the z component of translation of the mesh in view space
|
// gives the z component of translation of the mesh in view space
|
||||||
let mesh_z = view_row_2.dot(mesh.transform.col(3));
|
let mesh_z = view_row_2.dot(mesh_uniform.transform.col(3));
|
||||||
// FIXME: Switch from usize to u64 for portability and use sort key encoding
|
|
||||||
// similar to https://realtimecollisiondetection.net/blog/?p=86 as appropriate
|
|
||||||
// FIXME: What is the best way to map from view space z to a number of bits of unsigned integer?
|
|
||||||
let sort_key = (((mesh_z * 1000.0) as usize) << 10)
|
|
||||||
| (material_bind_group_key.index() & ((1 << 10) - 1));
|
|
||||||
// TODO: currently there is only "transparent phase". this should pick transparent vs opaque according to the mesh material
|
// TODO: currently there is only "transparent phase". this should pick transparent vs opaque according to the mesh material
|
||||||
transparent_phase.add(Drawable {
|
transparent_phase.add(Transparent3d {
|
||||||
|
entity,
|
||||||
draw_function: draw_pbr,
|
draw_function: draw_pbr,
|
||||||
draw_key: i,
|
distance: mesh_z,
|
||||||
sort_key,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ultimately lights should check meshes for relevancy (ex: light views can "see" different meshes than the main view can)
|
|
||||||
let draw_shadow_mesh = draw_functions.read().get_id::<DrawShadowMesh>().unwrap();
|
|
||||||
for view_light_entity in view_lights.lights.iter().copied() {
|
|
||||||
let mut shadow_phase = view_light_shadow_phases.get_mut(view_light_entity).unwrap();
|
|
||||||
// TODO: this should only queue up meshes that are actually visible by each "light view"
|
|
||||||
for (i, mesh) in extracted_meshes.meshes.iter().enumerate() {
|
|
||||||
if mesh.casts_shadows {
|
|
||||||
shadow_phase.add(Drawable {
|
|
||||||
draw_function: draw_shadow_mesh,
|
|
||||||
draw_key: i,
|
|
||||||
sort_key: 0, // TODO: sort back-to-front
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this logic can be moved to prepare_meshes once wgpu::Queue is exposed directly
|
pub type DrawPbr = (
|
||||||
pub struct PbrNode;
|
SetPbrPipeline,
|
||||||
|
SetMeshViewBindGroup<0>,
|
||||||
impl Node for PbrNode {
|
SetStandardMaterialBindGroup<1>,
|
||||||
fn run(
|
SetTransformBindGroup<2>,
|
||||||
&self,
|
DrawMesh,
|
||||||
_graph: &mut RenderGraphContext,
|
|
||||||
render_context: &mut RenderContext,
|
|
||||||
world: &World,
|
|
||||||
) -> Result<(), NodeRunError> {
|
|
||||||
let mesh_meta = world.get_resource::<MeshMeta>().unwrap();
|
|
||||||
let light_meta = world.get_resource::<LightMeta>().unwrap();
|
|
||||||
mesh_meta
|
|
||||||
.transform_uniforms
|
|
||||||
.write_to_uniform_buffer(&mut render_context.command_encoder);
|
|
||||||
light_meta
|
|
||||||
.view_gpu_lights
|
|
||||||
.write_to_uniform_buffer(&mut render_context.command_encoder);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type DrawPbrParams<'s, 'w> = (
|
|
||||||
Res<'w, PbrShaders>,
|
|
||||||
Res<'w, MeshMeta>,
|
|
||||||
Res<'w, ExtractedMeshes>,
|
|
||||||
Res<'w, RenderAssets<Mesh>>,
|
|
||||||
Query<
|
|
||||||
'w,
|
|
||||||
's,
|
|
||||||
(
|
|
||||||
&'w ViewUniformOffset,
|
|
||||||
&'w ViewLights,
|
|
||||||
&'w MeshViewBindGroups,
|
|
||||||
),
|
|
||||||
>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
pub struct DrawPbr {
|
pub struct SetPbrPipeline;
|
||||||
params: SystemState<DrawPbrParams<'static, 'static>>,
|
impl RenderCommand<Transparent3d> for SetPbrPipeline {
|
||||||
}
|
type Param = SRes<PbrShaders>;
|
||||||
|
#[inline]
|
||||||
impl DrawPbr {
|
fn render<'w>(
|
||||||
pub fn new(world: &mut World) -> Self {
|
_view: Entity,
|
||||||
Self {
|
_item: &Transparent3d,
|
||||||
params: SystemState::new(world),
|
pbr_shaders: SystemParamItem<'w, '_, Self::Param>,
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Draw for DrawPbr {
|
|
||||||
fn draw<'w, 's>(
|
|
||||||
&'s mut self,
|
|
||||||
world: &'w World,
|
|
||||||
pass: &mut TrackedRenderPass<'w>,
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
view: Entity,
|
|
||||||
draw_key: usize,
|
|
||||||
_sort_key: usize,
|
|
||||||
) {
|
) {
|
||||||
let (pbr_shaders, mesh_meta, extracted_meshes, meshes, views) = self.params.get(world);
|
|
||||||
let (view_uniforms, view_lights, mesh_view_bind_groups) = views.get(view).unwrap();
|
|
||||||
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
|
|
||||||
let mesh_meta = mesh_meta.into_inner();
|
|
||||||
pass.set_render_pipeline(&pbr_shaders.into_inner().pipeline);
|
pass.set_render_pipeline(&pbr_shaders.into_inner().pipeline);
|
||||||
pass.set_bind_group(
|
}
|
||||||
0,
|
}
|
||||||
&mesh_view_bind_groups.view,
|
|
||||||
&[view_uniforms.offset, view_lights.gpu_light_binding_index],
|
|
||||||
);
|
|
||||||
let mesh_draw_info = &mesh_meta.mesh_draw_info[draw_key];
|
|
||||||
pass.set_bind_group(
|
|
||||||
1,
|
|
||||||
// &mesh_meta.material_bind_groups[sort_key & ((1 << 10) - 1)],
|
|
||||||
&mesh_meta.material_bind_groups[mesh_draw_info.material_bind_group_key],
|
|
||||||
&[],
|
|
||||||
);
|
|
||||||
pass.set_bind_group(
|
|
||||||
2,
|
|
||||||
mesh_meta
|
|
||||||
.mesh_transform_bind_group
|
|
||||||
.get_value(mesh_meta.mesh_transform_bind_group_key.unwrap())
|
|
||||||
.unwrap(),
|
|
||||||
&[extracted_mesh.transform_binding_offset],
|
|
||||||
);
|
|
||||||
|
|
||||||
let gpu_mesh = meshes.into_inner().get(&extracted_mesh.mesh).unwrap();
|
pub struct SetMeshViewBindGroup<const I: usize>;
|
||||||
|
impl<const I: usize> RenderCommand<Transparent3d> for SetMeshViewBindGroup<I> {
|
||||||
|
type Param = SQuery<(
|
||||||
|
Read<ViewUniformOffset>,
|
||||||
|
Read<ViewLights>,
|
||||||
|
Read<PbrViewBindGroup>,
|
||||||
|
)>;
|
||||||
|
#[inline]
|
||||||
|
fn render<'w>(
|
||||||
|
view: Entity,
|
||||||
|
_item: &Transparent3d,
|
||||||
|
view_query: SystemParamItem<'w, '_, Self::Param>,
|
||||||
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
|
) {
|
||||||
|
let (view_uniform, view_lights, pbr_view_bind_group) = view_query.get(view).unwrap();
|
||||||
|
pass.set_bind_group(
|
||||||
|
I,
|
||||||
|
&pbr_view_bind_group.value,
|
||||||
|
&[view_uniform.offset, view_lights.gpu_light_binding_index],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SetTransformBindGroup<const I: usize>;
|
||||||
|
impl<const I: usize> RenderCommand<Transparent3d> for SetTransformBindGroup<I> {
|
||||||
|
type Param = (
|
||||||
|
SRes<TransformBindGroup>,
|
||||||
|
SQuery<Read<DynamicUniformIndex<MeshUniform>>>,
|
||||||
|
);
|
||||||
|
#[inline]
|
||||||
|
fn render<'w>(
|
||||||
|
_view: Entity,
|
||||||
|
item: &Transparent3d,
|
||||||
|
(transform_bind_group, mesh_query): SystemParamItem<'w, '_, Self::Param>,
|
||||||
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
|
) {
|
||||||
|
let transform_index = mesh_query.get(item.entity).unwrap();
|
||||||
|
pass.set_bind_group(
|
||||||
|
I,
|
||||||
|
&transform_bind_group.into_inner().value,
|
||||||
|
&[transform_index.index()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SetStandardMaterialBindGroup<const I: usize>;
|
||||||
|
impl<const I: usize> RenderCommand<Transparent3d> for SetStandardMaterialBindGroup<I> {
|
||||||
|
type Param = (
|
||||||
|
SRes<RenderAssets<StandardMaterial>>,
|
||||||
|
SQuery<Read<Handle<StandardMaterial>>>,
|
||||||
|
);
|
||||||
|
#[inline]
|
||||||
|
fn render<'w>(
|
||||||
|
_view: Entity,
|
||||||
|
item: &Transparent3d,
|
||||||
|
(materials, handle_query): SystemParamItem<'w, '_, Self::Param>,
|
||||||
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
|
) {
|
||||||
|
let handle = handle_query.get(item.entity).unwrap();
|
||||||
|
let materials = materials.into_inner();
|
||||||
|
let material = materials.get(handle).unwrap();
|
||||||
|
|
||||||
|
pass.set_bind_group(I, &material.bind_group, &[]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DrawMesh;
|
||||||
|
impl RenderCommand<Transparent3d> for DrawMesh {
|
||||||
|
type Param = (SRes<RenderAssets<Mesh>>, SQuery<Read<Handle<Mesh>>>);
|
||||||
|
#[inline]
|
||||||
|
fn render<'w>(
|
||||||
|
_view: Entity,
|
||||||
|
item: &Transparent3d,
|
||||||
|
(meshes, mesh_query): SystemParamItem<'w, '_, Self::Param>,
|
||||||
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
|
) {
|
||||||
|
let mesh_handle = mesh_query.get(item.entity).unwrap();
|
||||||
|
let gpu_mesh = meshes.into_inner().get(mesh_handle).unwrap();
|
||||||
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
|
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
|
||||||
if let Some(index_info) = &gpu_mesh.index_info {
|
if let Some(index_info) = &gpu_mesh.index_info {
|
||||||
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
|
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
// TODO: try merging this block with the binding?
|
|
||||||
// NOTE: Keep in sync with depth.wgsl
|
// NOTE: Keep in sync with depth.wgsl
|
||||||
[[block]]
|
[[block]]
|
||||||
struct View {
|
struct View {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ pub mod camera;
|
|||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub mod render_asset;
|
pub mod render_asset;
|
||||||
|
pub mod render_component;
|
||||||
pub mod render_graph;
|
pub mod render_graph;
|
||||||
pub mod render_phase;
|
pub mod render_phase;
|
||||||
pub mod render_resource;
|
pub mod render_resource;
|
||||||
@ -10,22 +11,21 @@ pub mod shader;
|
|||||||
pub mod texture;
|
pub mod texture;
|
||||||
pub mod view;
|
pub mod view;
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
pub use once_cell;
|
pub use once_cell;
|
||||||
use wgpu::BackendBit;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
camera::CameraPlugin,
|
camera::CameraPlugin,
|
||||||
mesh::MeshPlugin,
|
mesh::MeshPlugin,
|
||||||
render_graph::RenderGraph,
|
render_graph::RenderGraph,
|
||||||
render_phase::DrawFunctions,
|
|
||||||
renderer::render_system,
|
renderer::render_system,
|
||||||
texture::ImagePlugin,
|
texture::ImagePlugin,
|
||||||
view::{ViewPlugin, WindowRenderPlugin},
|
view::{ViewPlugin, WindowRenderPlugin},
|
||||||
};
|
};
|
||||||
use bevy_app::{App, AppLabel, Plugin};
|
use bevy_app::{App, AppLabel, Plugin};
|
||||||
|
use bevy_asset::AssetServer;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use wgpu::BackendBit;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RenderPlugin;
|
pub struct RenderPlugin;
|
||||||
@ -96,6 +96,7 @@ impl Plugin for RenderPlugin {
|
|||||||
app.insert_resource(device.clone())
|
app.insert_resource(device.clone())
|
||||||
.insert_resource(queue.clone())
|
.insert_resource(queue.clone())
|
||||||
.init_resource::<ScratchRenderWorld>();
|
.init_resource::<ScratchRenderWorld>();
|
||||||
|
let asset_server = app.world.get_resource::<AssetServer>().unwrap().clone();
|
||||||
|
|
||||||
let mut render_app = App::empty();
|
let mut render_app = App::empty();
|
||||||
let mut extract_stage = SystemStage::parallel();
|
let mut extract_stage = SystemStage::parallel();
|
||||||
@ -115,8 +116,8 @@ impl Plugin for RenderPlugin {
|
|||||||
.insert_resource(instance)
|
.insert_resource(instance)
|
||||||
.insert_resource(device)
|
.insert_resource(device)
|
||||||
.insert_resource(queue)
|
.insert_resource(queue)
|
||||||
.init_resource::<RenderGraph>()
|
.insert_resource(asset_server)
|
||||||
.init_resource::<DrawFunctions>();
|
.init_resource::<RenderGraph>();
|
||||||
|
|
||||||
app.add_sub_app(RenderApp, render_app, move |app_world, render_app| {
|
app.add_sub_app(RenderApp, render_app, move |app_world, render_app| {
|
||||||
// reserve all existing app entities for use in render_app
|
// reserve all existing app entities for use in render_app
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
mod conversions;
|
mod conversions;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render_asset::RenderAsset,
|
render_asset::{PrepareAssetError, RenderAsset},
|
||||||
render_resource::Buffer,
|
render_resource::Buffer,
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::RenderDevice,
|
||||||
};
|
};
|
||||||
use bevy_core::cast_slice;
|
use bevy_core::cast_slice;
|
||||||
|
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
|
||||||
use bevy_math::*;
|
use bevy_math::*;
|
||||||
use bevy_reflect::TypeUuid;
|
use bevy_reflect::TypeUuid;
|
||||||
use bevy_utils::EnumVariantMeta;
|
use bevy_utils::EnumVariantMeta;
|
||||||
@ -540,6 +541,7 @@ pub struct GpuIndexInfo {
|
|||||||
impl RenderAsset for Mesh {
|
impl RenderAsset for Mesh {
|
||||||
type ExtractedAsset = Mesh;
|
type ExtractedAsset = Mesh;
|
||||||
type PreparedAsset = GpuMesh;
|
type PreparedAsset = GpuMesh;
|
||||||
|
type Param = SRes<RenderDevice>;
|
||||||
|
|
||||||
fn extract_asset(&self) -> Self::ExtractedAsset {
|
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||||
self.clone()
|
self.clone()
|
||||||
@ -547,9 +549,8 @@ impl RenderAsset for Mesh {
|
|||||||
|
|
||||||
fn prepare_asset(
|
fn prepare_asset(
|
||||||
mesh: Self::ExtractedAsset,
|
mesh: Self::ExtractedAsset,
|
||||||
render_device: &RenderDevice,
|
render_device: &mut SystemParamItem<Self::Param>,
|
||||||
_render_queue: &RenderQueue,
|
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
|
||||||
) -> Self::PreparedAsset {
|
|
||||||
let vertex_buffer_data = mesh.get_vertex_buffer_data();
|
let vertex_buffer_data = mesh.get_vertex_buffer_data();
|
||||||
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||||
usage: BufferUsage::VERTEX,
|
usage: BufferUsage::VERTEX,
|
||||||
@ -566,9 +567,9 @@ impl RenderAsset for Mesh {
|
|||||||
count: mesh.indices().unwrap().len() as u32,
|
count: mesh.indices().unwrap().len() as u32,
|
||||||
});
|
});
|
||||||
|
|
||||||
GpuMesh {
|
Ok(GpuMesh {
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
index_info,
|
index_info,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,26 @@
|
|||||||
use std::marker::PhantomData;
|
use crate::{RenderApp, RenderStage};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
renderer::{RenderDevice, RenderQueue},
|
|
||||||
RenderApp, RenderStage,
|
|
||||||
};
|
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{Asset, AssetEvent, Assets, Handle};
|
use bevy_asset::{Asset, AssetEvent, Assets, Handle};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::{
|
||||||
|
prelude::*,
|
||||||
|
system::{lifetimeless::*, RunSystem, SystemParam, SystemParamItem},
|
||||||
|
};
|
||||||
use bevy_utils::{HashMap, HashSet};
|
use bevy_utils::{HashMap, HashSet};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub enum PrepareAssetError<E: Send + Sync + 'static> {
|
||||||
|
RetryNextUpdate(E),
|
||||||
|
}
|
||||||
|
|
||||||
pub trait RenderAsset: Asset {
|
pub trait RenderAsset: Asset {
|
||||||
type ExtractedAsset: Send + Sync + 'static;
|
type ExtractedAsset: Send + Sync + 'static;
|
||||||
type PreparedAsset: Send + Sync + 'static;
|
type PreparedAsset: Send + Sync + 'static;
|
||||||
|
type Param: SystemParam;
|
||||||
fn extract_asset(&self) -> Self::ExtractedAsset;
|
fn extract_asset(&self) -> Self::ExtractedAsset;
|
||||||
fn prepare_asset(
|
fn prepare_asset(
|
||||||
extracted_asset: Self::ExtractedAsset,
|
extracted_asset: Self::ExtractedAsset,
|
||||||
render_device: &RenderDevice,
|
param: &mut SystemParamItem<Self::Param>,
|
||||||
render_queue: &RenderQueue,
|
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>>;
|
||||||
) -> Self::PreparedAsset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts assets into gpu-usable data
|
/// Extracts assets into gpu-usable data
|
||||||
@ -31,15 +34,18 @@ impl<A: RenderAsset> Default for RenderAssetPlugin<A> {
|
|||||||
|
|
||||||
impl<A: RenderAsset> Plugin for RenderAssetPlugin<A> {
|
impl<A: RenderAsset> Plugin for RenderAssetPlugin<A> {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.sub_app(RenderApp)
|
let render_app = app.sub_app(RenderApp);
|
||||||
|
let prepare_asset_system = PrepareAssetSystem::<A>::system(&mut render_app.world);
|
||||||
|
render_app
|
||||||
.init_resource::<ExtractedAssets<A>>()
|
.init_resource::<ExtractedAssets<A>>()
|
||||||
.init_resource::<RenderAssets<A>>()
|
.init_resource::<RenderAssets<A>>()
|
||||||
|
.init_resource::<PrepareNextFrameAssets<A>>()
|
||||||
.add_system_to_stage(RenderStage::Extract, extract_render_asset::<A>)
|
.add_system_to_stage(RenderStage::Extract, extract_render_asset::<A>)
|
||||||
.add_system_to_stage(RenderStage::Prepare, prepare_render_asset::<A>);
|
.add_system_to_stage(RenderStage::Prepare, prepare_asset_system);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExtractedAssets<A: RenderAsset> {
|
pub struct ExtractedAssets<A: RenderAsset> {
|
||||||
extracted: Vec<(Handle<A>, A::ExtractedAsset)>,
|
extracted: Vec<(Handle<A>, A::ExtractedAsset)>,
|
||||||
removed: Vec<Handle<A>>,
|
removed: Vec<Handle<A>>,
|
||||||
}
|
}
|
||||||
@ -91,18 +97,58 @@ fn extract_render_asset<A: RenderAsset>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_render_asset<R: RenderAsset>(
|
pub type RenderAssetParams<R> = (
|
||||||
mut extracted_assets: ResMut<ExtractedAssets<R>>,
|
SResMut<ExtractedAssets<R>>,
|
||||||
mut render_assets: ResMut<RenderAssets<R>>,
|
SResMut<RenderAssets<R>>,
|
||||||
render_device: Res<RenderDevice>,
|
SResMut<PrepareNextFrameAssets<R>>,
|
||||||
render_queue: Res<RenderQueue>,
|
<R as RenderAsset>::Param,
|
||||||
) {
|
);
|
||||||
for removed in extracted_assets.removed.iter() {
|
|
||||||
render_assets.remove(removed);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (handle, extracted_asset) in extracted_assets.extracted.drain(..) {
|
// TODO: consider storing inside system?
|
||||||
let prepared_asset = R::prepare_asset(extracted_asset, &render_device, &render_queue);
|
pub struct PrepareNextFrameAssets<A: RenderAsset> {
|
||||||
render_assets.insert(handle, prepared_asset);
|
assets: Vec<(Handle<A>, A::ExtractedAsset)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: RenderAsset> Default for PrepareNextFrameAssets<A> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
assets: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PrepareAssetSystem<R: RenderAsset>(PhantomData<R>);
|
||||||
|
|
||||||
|
impl<R: RenderAsset> RunSystem for PrepareAssetSystem<R> {
|
||||||
|
type Param = RenderAssetParams<R>;
|
||||||
|
fn run(
|
||||||
|
(mut extracted_assets, mut render_assets, mut prepare_next_frame, mut param): SystemParamItem<Self::Param>,
|
||||||
|
) {
|
||||||
|
let mut queued_assets = std::mem::take(&mut prepare_next_frame.assets);
|
||||||
|
for (handle, extracted_asset) in queued_assets.drain(..) {
|
||||||
|
match R::prepare_asset(extracted_asset, &mut param) {
|
||||||
|
Ok(prepared_asset) => {
|
||||||
|
render_assets.insert(handle, prepared_asset);
|
||||||
|
}
|
||||||
|
Err(PrepareAssetError::RetryNextUpdate(extracted_asset)) => {
|
||||||
|
prepare_next_frame.assets.push((handle, extracted_asset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for removed in std::mem::take(&mut extracted_assets.removed) {
|
||||||
|
render_assets.remove(&removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (handle, extracted_asset) in std::mem::take(&mut extracted_assets.extracted) {
|
||||||
|
match R::prepare_asset(extracted_asset, &mut param) {
|
||||||
|
Ok(prepared_asset) => {
|
||||||
|
render_assets.insert(handle, prepared_asset);
|
||||||
|
}
|
||||||
|
Err(PrepareAssetError::RetryNextUpdate(extracted_asset)) => {
|
||||||
|
prepare_next_frame.assets.push((handle, extracted_asset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
162
pipelined/bevy_render2/src/render_component.rs
Normal file
162
pipelined/bevy_render2/src/render_component.rs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
use crate::{
|
||||||
|
render_resource::DynamicUniformVec,
|
||||||
|
renderer::{RenderDevice, RenderQueue},
|
||||||
|
RenderApp, RenderStage,
|
||||||
|
};
|
||||||
|
use bevy_app::{App, Plugin};
|
||||||
|
use bevy_asset::{Asset, Handle};
|
||||||
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
|
prelude::*,
|
||||||
|
query::{FilterFetch, QueryItem, ReadOnlyFetch, WorldQuery},
|
||||||
|
system::{
|
||||||
|
lifetimeless::{Read, SCommands, SQuery},
|
||||||
|
RunSystem, SystemParamItem,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use crevice::std140::AsStd140;
|
||||||
|
use std::{marker::PhantomData, ops::Deref};
|
||||||
|
|
||||||
|
pub struct DynamicUniformIndex<C: Component> {
|
||||||
|
index: u32,
|
||||||
|
marker: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Component> DynamicUniformIndex<C> {
|
||||||
|
#[inline]
|
||||||
|
pub fn index(&self) -> u32 {
|
||||||
|
self.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ExtractComponent: Component {
|
||||||
|
type Query: WorldQuery;
|
||||||
|
type Filter: WorldQuery;
|
||||||
|
fn extract_component(item: QueryItem<Self::Query>) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts assets into gpu-usable data
|
||||||
|
pub struct UniformComponentPlugin<C>(PhantomData<fn() -> C>);
|
||||||
|
|
||||||
|
impl<C> Default for UniformComponentPlugin<C> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Component + AsStd140 + Clone> Plugin for UniformComponentPlugin<C> {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.sub_app(RenderApp)
|
||||||
|
.insert_resource(ComponentUniforms::<C>::default())
|
||||||
|
.add_system_to_stage(
|
||||||
|
RenderStage::Prepare,
|
||||||
|
prepare_uniform_components::<C>.system(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ComponentUniforms<C: Component + AsStd140> {
|
||||||
|
uniforms: DynamicUniformVec<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Component + AsStd140> Deref for ComponentUniforms<C> {
|
||||||
|
type Target = DynamicUniformVec<C>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.uniforms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Component + AsStd140> ComponentUniforms<C> {
|
||||||
|
#[inline]
|
||||||
|
pub fn uniforms(&self) -> &DynamicUniformVec<C> {
|
||||||
|
&self.uniforms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Component + AsStd140> Default for ComponentUniforms<C> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
uniforms: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_uniform_components<C: Component>(
|
||||||
|
mut commands: Commands,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
render_queue: Res<RenderQueue>,
|
||||||
|
mut component_uniforms: ResMut<ComponentUniforms<C>>,
|
||||||
|
components: Query<(Entity, &C)>,
|
||||||
|
) where
|
||||||
|
C: AsStd140 + Clone,
|
||||||
|
{
|
||||||
|
let len = components.iter().len();
|
||||||
|
component_uniforms
|
||||||
|
.uniforms
|
||||||
|
.reserve_and_clear(len, &render_device);
|
||||||
|
for (entity, component) in components.iter() {
|
||||||
|
commands
|
||||||
|
.get_or_spawn(entity)
|
||||||
|
.insert(DynamicUniformIndex::<C> {
|
||||||
|
index: component_uniforms.uniforms.push(component.clone()),
|
||||||
|
marker: PhantomData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
component_uniforms.uniforms.write_buffer(&render_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExtractComponentPlugin<C, F = ()>(PhantomData<fn() -> (C, F)>);
|
||||||
|
|
||||||
|
impl<C, F> Default for ExtractComponentPlugin<C, F> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ExtractComponent> Plugin for ExtractComponentPlugin<C>
|
||||||
|
where
|
||||||
|
<C::Query as WorldQuery>::Fetch: ReadOnlyFetch,
|
||||||
|
<C::Filter as WorldQuery>::Fetch: FilterFetch,
|
||||||
|
{
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
let system = ExtractComponentSystem::<C>::system(&mut app.world);
|
||||||
|
let render_app = app.sub_app(RenderApp);
|
||||||
|
render_app.add_system_to_stage(RenderStage::Extract, system);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Asset> ExtractComponent for Handle<T> {
|
||||||
|
type Query = Read<Handle<T>>;
|
||||||
|
type Filter = ();
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn extract_component(handle: QueryItem<Self::Query>) -> Self {
|
||||||
|
handle.clone_weak()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExtractComponentSystem<C: ExtractComponent>(PhantomData<C>);
|
||||||
|
|
||||||
|
impl<C: ExtractComponent> RunSystem for ExtractComponentSystem<C>
|
||||||
|
where
|
||||||
|
<C::Filter as WorldQuery>::Fetch: FilterFetch,
|
||||||
|
<C::Query as WorldQuery>::Fetch: ReadOnlyFetch,
|
||||||
|
{
|
||||||
|
type Param = (
|
||||||
|
SCommands,
|
||||||
|
Local<'static, usize>,
|
||||||
|
SQuery<(Entity, C::Query), C::Filter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn run((mut commands, mut previous_len, query): SystemParamItem<Self::Param>) {
|
||||||
|
let mut values = Vec::with_capacity(*previous_len);
|
||||||
|
for (entity, query_item) in query.iter() {
|
||||||
|
values.push((entity, (C::extract_component(query_item),)));
|
||||||
|
}
|
||||||
|
*previous_len = values.len();
|
||||||
|
commands.insert_or_spawn_batch(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,59 +1,161 @@
|
|||||||
use crate::render_phase::TrackedRenderPass;
|
use crate::render_phase::TrackedRenderPass;
|
||||||
use bevy_ecs::{entity::Entity, world::World};
|
use bevy_app::App;
|
||||||
|
use bevy_ecs::{
|
||||||
|
all_tuples,
|
||||||
|
entity::Entity,
|
||||||
|
system::{ReadOnlySystemParamFetch, SystemParam, SystemParamItem, SystemState},
|
||||||
|
world::World,
|
||||||
|
};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use std::{any::TypeId, fmt::Debug, hash::Hash};
|
use std::{any::TypeId, fmt::Debug, hash::Hash};
|
||||||
|
|
||||||
// TODO: should this be generic on "drawn thing"? would provide more flexibility and explicitness
|
pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
|
||||||
// instead of hard coded draw key and sort key
|
fn draw<'w>(
|
||||||
pub trait Draw: Send + Sync + 'static {
|
&mut self,
|
||||||
fn draw<'w, 's>(
|
|
||||||
&'s mut self,
|
|
||||||
world: &'w World,
|
world: &'w World,
|
||||||
pass: &mut TrackedRenderPass<'w>,
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
view: Entity,
|
view: Entity,
|
||||||
draw_key: usize,
|
item: &P,
|
||||||
sort_key: usize,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait PhaseItem: Send + Sync + 'static {
|
||||||
|
type SortKey: Ord;
|
||||||
|
fn sort_key(&self) -> Self::SortKey;
|
||||||
|
fn draw_function(&self) -> DrawFunctionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make this generic?
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub struct DrawFunctionId(usize);
|
pub struct DrawFunctionId(usize);
|
||||||
|
|
||||||
#[derive(Default)]
|
pub struct DrawFunctionsInternal<P: PhaseItem> {
|
||||||
pub struct DrawFunctionsInternal {
|
pub draw_functions: Vec<Box<dyn Draw<P>>>,
|
||||||
pub draw_functions: Vec<Box<dyn Draw>>,
|
|
||||||
pub indices: HashMap<TypeId, DrawFunctionId>,
|
pub indices: HashMap<TypeId, DrawFunctionId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawFunctionsInternal {
|
impl<P: PhaseItem> DrawFunctionsInternal<P> {
|
||||||
pub fn add<D: Draw>(&mut self, draw_function: D) -> DrawFunctionId {
|
pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
|
||||||
|
self.add_with::<T, T>(draw_function)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_with<T: 'static, D: Draw<P>>(&mut self, draw_function: D) -> DrawFunctionId {
|
||||||
self.draw_functions.push(Box::new(draw_function));
|
self.draw_functions.push(Box::new(draw_function));
|
||||||
let id = DrawFunctionId(self.draw_functions.len() - 1);
|
let id = DrawFunctionId(self.draw_functions.len() - 1);
|
||||||
self.indices.insert(TypeId::of::<D>(), id);
|
self.indices.insert(TypeId::of::<T>(), id);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, id: DrawFunctionId) -> Option<&mut dyn Draw> {
|
pub fn get_mut(&mut self, id: DrawFunctionId) -> Option<&mut dyn Draw<P>> {
|
||||||
self.draw_functions.get_mut(id.0).map(|f| &mut **f)
|
self.draw_functions.get_mut(id.0).map(|f| &mut **f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_id<D: Draw>(&self) -> Option<DrawFunctionId> {
|
pub fn get_id<T: 'static>(&self) -> Option<DrawFunctionId> {
|
||||||
self.indices.get(&TypeId::of::<D>()).copied()
|
self.indices.get(&TypeId::of::<T>()).copied()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
pub struct DrawFunctions<P: PhaseItem> {
|
||||||
pub struct DrawFunctions {
|
internal: RwLock<DrawFunctionsInternal<P>>,
|
||||||
internal: RwLock<DrawFunctionsInternal>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawFunctions {
|
impl<P: PhaseItem> Default for DrawFunctions<P> {
|
||||||
pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal> {
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
internal: RwLock::new(DrawFunctionsInternal {
|
||||||
|
draw_functions: Vec::new(),
|
||||||
|
indices: HashMap::default(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: PhaseItem> DrawFunctions<P> {
|
||||||
|
pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal<P>> {
|
||||||
self.internal.read()
|
self.internal.read()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal> {
|
pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal<P>> {
|
||||||
self.internal.write()
|
self.internal.write()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub trait RenderCommand<P: PhaseItem> {
|
||||||
|
type Param: SystemParam;
|
||||||
|
fn render<'w>(
|
||||||
|
view: Entity,
|
||||||
|
item: &P,
|
||||||
|
param: SystemParamItem<'w, '_, Self::Param>,
|
||||||
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! render_command_tuple_impl {
|
||||||
|
($($name: ident),*) => {
|
||||||
|
impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
|
||||||
|
type Param = ($($name::Param,)*);
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn render<'w>(
|
||||||
|
_view: Entity,
|
||||||
|
_item: &P,
|
||||||
|
($($name,)*): SystemParamItem<'w, '_, Self::Param>,
|
||||||
|
_pass: &mut TrackedRenderPass<'w>,
|
||||||
|
) {
|
||||||
|
$($name::render(_view, _item, $name, _pass);)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
all_tuples!(render_command_tuple_impl, 0, 15, C);
|
||||||
|
|
||||||
|
pub struct RenderCommandState<P: PhaseItem, C: RenderCommand<P>> {
|
||||||
|
state: SystemState<C::Param>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: PhaseItem, C: RenderCommand<P>> RenderCommandState<P, C> {
|
||||||
|
pub fn new(world: &mut World) -> Self {
|
||||||
|
Self {
|
||||||
|
state: SystemState::new(world),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for RenderCommandState<P, C>
|
||||||
|
where
|
||||||
|
<C::Param as SystemParam>::Fetch: ReadOnlySystemParamFetch,
|
||||||
|
{
|
||||||
|
fn draw<'w>(
|
||||||
|
&mut self,
|
||||||
|
world: &'w World,
|
||||||
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
|
view: Entity,
|
||||||
|
item: &P,
|
||||||
|
) {
|
||||||
|
let param = self.state.get(world);
|
||||||
|
C::render(view, item, param, pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AddRenderCommand {
|
||||||
|
fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
|
||||||
|
&mut self,
|
||||||
|
) -> &mut Self
|
||||||
|
where
|
||||||
|
<C::Param as SystemParam>::Fetch: ReadOnlySystemParamFetch;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddRenderCommand for App {
|
||||||
|
fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
|
||||||
|
&mut self,
|
||||||
|
) -> &mut Self
|
||||||
|
where
|
||||||
|
<C::Param as SystemParam>::Fetch: ReadOnlySystemParamFetch,
|
||||||
|
{
|
||||||
|
let draw_function = RenderCommandState::<P, C>::new(&mut self.world);
|
||||||
|
let draw_functions = self.world.get_resource::<DrawFunctions<P>>().unwrap();
|
||||||
|
draw_functions.write().add_with::<C, _>(draw_function);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,41 +5,29 @@ pub use draw::*;
|
|||||||
pub use draw_state::*;
|
pub use draw_state::*;
|
||||||
|
|
||||||
use bevy_ecs::prelude::Query;
|
use bevy_ecs::prelude::Query;
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
// TODO: make this configurable per phase?
|
pub struct RenderPhase<I: PhaseItem> {
|
||||||
pub struct Drawable {
|
pub items: Vec<I>,
|
||||||
pub draw_function: DrawFunctionId,
|
|
||||||
pub draw_key: usize,
|
|
||||||
pub sort_key: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RenderPhase<T> {
|
impl<I: PhaseItem> Default for RenderPhase<I> {
|
||||||
pub drawn_things: Vec<Drawable>,
|
|
||||||
marker: PhantomData<fn() -> T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for RenderPhase<T> {
|
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self { items: Vec::new() }
|
||||||
drawn_things: Vec::new(),
|
|
||||||
marker: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> RenderPhase<T> {
|
impl<I: PhaseItem> RenderPhase<I> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add(&mut self, drawable: Drawable) {
|
pub fn add(&mut self, item: I) {
|
||||||
self.drawn_things.push(drawable);
|
self.items.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sort(&mut self) {
|
pub fn sort(&mut self) {
|
||||||
self.drawn_things.sort_by_key(|d| d.sort_key);
|
self.items.sort_by_key(|d| d.sort_key());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sort_phase_system<T: 'static>(mut render_phases: Query<&mut RenderPhase<T>>) {
|
pub fn sort_phase_system<I: PhaseItem>(mut render_phases: Query<&mut RenderPhase<I>>) {
|
||||||
for mut phase in render_phases.iter_mut() {
|
for mut phase in render_phases.iter_mut() {
|
||||||
phase.sort();
|
phase.sort();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,15 +15,14 @@ pub use texture::*;
|
|||||||
pub use uniform_vec::*;
|
pub use uniform_vec::*;
|
||||||
|
|
||||||
// TODO: decide where re-exports should go
|
// TODO: decide where re-exports should go
|
||||||
pub use wgpu::util::BufferInitDescriptor;
|
|
||||||
pub use wgpu::{
|
pub use wgpu::{
|
||||||
AddressMode, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
|
util::BufferInitDescriptor, AddressMode, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
|
||||||
BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent, BlendFactor,
|
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent,
|
||||||
BlendOperation, BlendState, BufferAddress, BufferBindingType, BufferSize, BufferUsage,
|
BlendFactor, BlendOperation, BlendState, BufferAddress, BufferBindingType, BufferSize,
|
||||||
ColorTargetState, ColorWrite, CompareFunction, ComputePassDescriptor,
|
BufferUsage, ColorTargetState, ColorWrite, CompareFunction, ComputePassDescriptor,
|
||||||
ComputePipelineDescriptor, DepthBiasState, DepthStencilState, Extent3d, Face, FilterMode,
|
ComputePipelineDescriptor, DepthBiasState, DepthStencilState, Extent3d, Face, FilterMode,
|
||||||
FragmentState, FrontFace, IndexFormat, InputStepMode, LoadOp, MultisampleState, Operations,
|
FragmentState, FrontFace, IndexFormat, InputStepMode, LoadOp, MultisampleState, Operations,
|
||||||
PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology,
|
PipelineLayout, PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology,
|
||||||
RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor,
|
RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor,
|
||||||
RenderPipelineDescriptor, SamplerDescriptor, ShaderFlags, ShaderModule, ShaderModuleDescriptor,
|
RenderPipelineDescriptor, SamplerDescriptor, ShaderFlags, ShaderModule, ShaderModuleDescriptor,
|
||||||
ShaderSource, ShaderStage, StencilFaceState, StencilOperation, StencilState,
|
ShaderSource, ShaderStage, StencilFaceState, StencilOperation, StencilState,
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
use crate::{render_resource::Buffer, renderer::RenderDevice};
|
use crate::{
|
||||||
|
render_resource::Buffer,
|
||||||
|
renderer::{RenderDevice, RenderQueue},
|
||||||
|
};
|
||||||
use crevice::std140::{self, AsStd140, DynamicUniform, Std140};
|
use crevice::std140::{self, AsStd140, DynamicUniform, Std140};
|
||||||
use std::{num::NonZeroU64, ops::DerefMut};
|
use std::num::NonZeroU64;
|
||||||
use wgpu::{BindingResource, BufferBinding, BufferDescriptor, BufferUsage, CommandEncoder};
|
use wgpu::{BindingResource, BufferBinding, BufferDescriptor, BufferUsage};
|
||||||
|
|
||||||
pub struct UniformVec<T: AsStd140> {
|
pub struct UniformVec<T: AsStd140> {
|
||||||
values: Vec<T>,
|
values: Vec<T>,
|
||||||
staging_buffer: Option<Buffer>,
|
scratch: Vec<u8>,
|
||||||
uniform_buffer: Option<Buffer>,
|
uniform_buffer: Option<Buffer>,
|
||||||
capacity: usize,
|
capacity: usize,
|
||||||
item_size: usize,
|
item_size: usize,
|
||||||
@ -15,7 +18,7 @@ impl<T: AsStd140> Default for UniformVec<T> {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
values: Vec::new(),
|
values: Vec::new(),
|
||||||
staging_buffer: None,
|
scratch: Vec::new(),
|
||||||
uniform_buffer: None,
|
uniform_buffer: None,
|
||||||
capacity: 0,
|
capacity: 0,
|
||||||
item_size: (T::std140_size_static() + <T as AsStd140>::Std140Type::ALIGNMENT - 1)
|
item_size: (T::std140_size_static() + <T as AsStd140>::Std140Type::ALIGNMENT - 1)
|
||||||
@ -25,23 +28,18 @@ impl<T: AsStd140> Default for UniformVec<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsStd140> UniformVec<T> {
|
impl<T: AsStd140> UniformVec<T> {
|
||||||
#[inline]
|
|
||||||
pub fn staging_buffer(&self) -> Option<&Buffer> {
|
|
||||||
self.staging_buffer.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn uniform_buffer(&self) -> Option<&Buffer> {
|
pub fn uniform_buffer(&self) -> Option<&Buffer> {
|
||||||
self.uniform_buffer.as_ref()
|
self.uniform_buffer.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn binding(&self) -> BindingResource {
|
pub fn binding(&self) -> Option<BindingResource> {
|
||||||
BindingResource::Buffer(BufferBinding {
|
Some(BindingResource::Buffer(BufferBinding {
|
||||||
buffer: self.uniform_buffer().expect("uniform buffer should exist"),
|
buffer: self.uniform_buffer()?,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: Some(NonZeroU64::new(self.item_size as u64).unwrap()),
|
size: Some(NonZeroU64::new(self.item_size as u64).unwrap()),
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -75,16 +73,11 @@ impl<T: AsStd140> UniformVec<T> {
|
|||||||
pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
|
pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
|
||||||
if capacity > self.capacity {
|
if capacity > self.capacity {
|
||||||
self.capacity = capacity;
|
self.capacity = capacity;
|
||||||
let size = (self.item_size * capacity) as wgpu::BufferAddress;
|
let size = self.item_size * capacity;
|
||||||
self.staging_buffer = Some(device.create_buffer(&BufferDescriptor {
|
self.scratch.resize(size, 0);
|
||||||
label: None,
|
|
||||||
size,
|
|
||||||
usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
}));
|
|
||||||
self.uniform_buffer = Some(device.create_buffer(&BufferDescriptor {
|
self.uniform_buffer = Some(device.create_buffer(&BufferDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
size,
|
size: size as wgpu::BufferAddress,
|
||||||
usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
|
usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
|
||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
}));
|
}));
|
||||||
@ -96,29 +89,12 @@ impl<T: AsStd140> UniformVec<T> {
|
|||||||
self.reserve(capacity, device);
|
self.reserve(capacity, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to_staging_buffer(&self, device: &RenderDevice) {
|
pub fn write_buffer(&mut self, queue: &RenderQueue) {
|
||||||
if let Some(staging_buffer) = &self.staging_buffer {
|
if let Some(uniform_buffer) = &self.uniform_buffer {
|
||||||
let slice = staging_buffer.slice(..);
|
let range = 0..self.item_size * self.values.len();
|
||||||
device.map_buffer(&slice, wgpu::MapMode::Write);
|
let mut writer = std140::Writer::new(&mut self.scratch[range.clone()]);
|
||||||
{
|
|
||||||
let mut data = slice.get_mapped_range_mut();
|
|
||||||
let mut writer = std140::Writer::new(data.deref_mut());
|
|
||||||
writer.write(self.values.as_slice()).unwrap();
|
writer.write(self.values.as_slice()).unwrap();
|
||||||
}
|
queue.write_buffer(uniform_buffer, 0, &self.scratch[range]);
|
||||||
staging_buffer.unmap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn write_to_uniform_buffer(&self, command_encoder: &mut CommandEncoder) {
|
|
||||||
if let (Some(staging_buffer), Some(uniform_buffer)) =
|
|
||||||
(&self.staging_buffer, &self.uniform_buffer)
|
|
||||||
{
|
|
||||||
command_encoder.copy_buffer_to_buffer(
|
|
||||||
staging_buffer,
|
|
||||||
0,
|
|
||||||
uniform_buffer,
|
|
||||||
0,
|
|
||||||
(self.values.len() * self.item_size) as u64,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,18 +116,13 @@ impl<T: AsStd140> Default for DynamicUniformVec<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsStd140> DynamicUniformVec<T> {
|
impl<T: AsStd140> DynamicUniformVec<T> {
|
||||||
#[inline]
|
|
||||||
pub fn staging_buffer(&self) -> Option<&Buffer> {
|
|
||||||
self.uniform_vec.staging_buffer()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn uniform_buffer(&self) -> Option<&Buffer> {
|
pub fn uniform_buffer(&self) -> Option<&Buffer> {
|
||||||
self.uniform_vec.uniform_buffer()
|
self.uniform_vec.uniform_buffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn binding(&self) -> BindingResource {
|
pub fn binding(&self) -> Option<BindingResource> {
|
||||||
self.uniform_vec.binding()
|
self.uniform_vec.binding()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,13 +157,8 @@ impl<T: AsStd140> DynamicUniformVec<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn write_to_staging_buffer(&self, device: &RenderDevice) {
|
pub fn write_buffer(&mut self, queue: &RenderQueue) {
|
||||||
self.uniform_vec.write_to_staging_buffer(device);
|
self.uniform_vec.write_buffer(queue);
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn write_to_uniform_buffer(&self, command_encoder: &mut CommandEncoder) {
|
|
||||||
self.uniform_vec.write_to_uniform_buffer(command_encoder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
use super::image_texture_conversion::image_to_texture;
|
use super::image_texture_conversion::image_to_texture;
|
||||||
use crate::{
|
use crate::{
|
||||||
render_asset::RenderAsset,
|
render_asset::{PrepareAssetError, RenderAsset},
|
||||||
render_resource::{Sampler, Texture, TextureView},
|
render_resource::{Sampler, Texture, TextureView},
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
texture::BevyDefault,
|
texture::BevyDefault,
|
||||||
};
|
};
|
||||||
|
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
|
||||||
use bevy_reflect::TypeUuid;
|
use bevy_reflect::TypeUuid;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
@ -349,6 +350,7 @@ pub struct GpuImage {
|
|||||||
impl RenderAsset for Image {
|
impl RenderAsset for Image {
|
||||||
type ExtractedAsset = Image;
|
type ExtractedAsset = Image;
|
||||||
type PreparedAsset = GpuImage;
|
type PreparedAsset = GpuImage;
|
||||||
|
type Param = (SRes<RenderDevice>, SRes<RenderQueue>);
|
||||||
|
|
||||||
fn extract_asset(&self) -> Self::ExtractedAsset {
|
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||||
self.clone()
|
self.clone()
|
||||||
@ -356,9 +358,8 @@ impl RenderAsset for Image {
|
|||||||
|
|
||||||
fn prepare_asset(
|
fn prepare_asset(
|
||||||
image: Self::ExtractedAsset,
|
image: Self::ExtractedAsset,
|
||||||
render_device: &RenderDevice,
|
(render_device, render_queue): &mut SystemParamItem<Self::Param>,
|
||||||
render_queue: &RenderQueue,
|
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
|
||||||
) -> Self::PreparedAsset {
|
|
||||||
let texture = render_device.create_texture(&image.texture_descriptor);
|
let texture = render_device.create_texture(&image.texture_descriptor);
|
||||||
let sampler = render_device.create_sampler(&image.sampler_descriptor);
|
let sampler = render_device.create_sampler(&image.sampler_descriptor);
|
||||||
|
|
||||||
@ -388,10 +389,10 @@ impl RenderAsset for Image {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let texture_view = texture.create_view(&TextureViewDescriptor::default());
|
let texture_view = texture.create_view(&TextureViewDescriptor::default());
|
||||||
GpuImage {
|
Ok(GpuImage {
|
||||||
texture,
|
texture,
|
||||||
texture_view,
|
texture_view,
|
||||||
sampler,
|
sampler,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,34 +1,25 @@
|
|||||||
pub mod window;
|
pub mod window;
|
||||||
|
|
||||||
use bevy_transform::components::GlobalTransform;
|
|
||||||
pub use window::*;
|
pub use window::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
|
|
||||||
render_resource::DynamicUniformVec,
|
render_resource::DynamicUniformVec,
|
||||||
renderer::{RenderContext, RenderDevice},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
RenderApp, RenderStage,
|
RenderApp, RenderStage,
|
||||||
};
|
};
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_math::{Mat4, Vec3};
|
use bevy_math::{Mat4, Vec3};
|
||||||
|
use bevy_transform::components::GlobalTransform;
|
||||||
use crevice::std140::AsStd140;
|
use crevice::std140::AsStd140;
|
||||||
|
|
||||||
pub struct ViewPlugin;
|
pub struct ViewPlugin;
|
||||||
|
|
||||||
impl ViewPlugin {
|
|
||||||
pub const VIEW_NODE: &'static str = "view";
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Plugin for ViewPlugin {
|
impl Plugin for ViewPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
let render_app = app.sub_app(RenderApp);
|
app.sub_app(RenderApp)
|
||||||
render_app
|
.init_resource::<ViewUniforms>()
|
||||||
.init_resource::<ViewMeta>()
|
|
||||||
.add_system_to_stage(RenderStage::Prepare, prepare_views);
|
.add_system_to_stage(RenderStage::Prepare, prepare_views);
|
||||||
|
|
||||||
let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
|
|
||||||
graph.add_node(ViewPlugin::VIEW_NODE, ViewNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +38,7 @@ pub struct ViewUniform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ViewMeta {
|
pub struct ViewUniforms {
|
||||||
pub uniforms: DynamicUniformVec<ViewUniform>,
|
pub uniforms: DynamicUniformVec<ViewUniform>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,17 +48,18 @@ pub struct ViewUniformOffset {
|
|||||||
|
|
||||||
fn prepare_views(
|
fn prepare_views(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
render_resources: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
mut view_meta: ResMut<ViewMeta>,
|
render_queue: Res<RenderQueue>,
|
||||||
|
mut view_uniforms: ResMut<ViewUniforms>,
|
||||||
mut extracted_views: Query<(Entity, &ExtractedView)>,
|
mut extracted_views: Query<(Entity, &ExtractedView)>,
|
||||||
) {
|
) {
|
||||||
view_meta
|
view_uniforms
|
||||||
.uniforms
|
.uniforms
|
||||||
.reserve_and_clear(extracted_views.iter_mut().len(), &render_resources);
|
.reserve_and_clear(extracted_views.iter_mut().len(), &render_device);
|
||||||
for (entity, camera) in extracted_views.iter() {
|
for (entity, camera) in extracted_views.iter() {
|
||||||
let projection = camera.projection;
|
let projection = camera.projection;
|
||||||
let view_uniforms = ViewUniformOffset {
|
let view_uniforms = ViewUniformOffset {
|
||||||
offset: view_meta.uniforms.push(ViewUniform {
|
offset: view_uniforms.uniforms.push(ViewUniform {
|
||||||
view_proj: projection * camera.transform.compute_matrix().inverse(),
|
view_proj: projection * camera.transform.compute_matrix().inverse(),
|
||||||
projection,
|
projection,
|
||||||
world_position: camera.transform.translation,
|
world_position: camera.transform.translation,
|
||||||
@ -77,24 +69,5 @@ fn prepare_views(
|
|||||||
commands.entity(entity).insert(view_uniforms);
|
commands.entity(entity).insert(view_uniforms);
|
||||||
}
|
}
|
||||||
|
|
||||||
view_meta
|
view_uniforms.uniforms.write_buffer(&render_queue);
|
||||||
.uniforms
|
|
||||||
.write_to_staging_buffer(&render_resources);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ViewNode;
|
|
||||||
|
|
||||||
impl Node for ViewNode {
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
_graph: &mut RenderGraphContext,
|
|
||||||
render_context: &mut RenderContext,
|
|
||||||
world: &World,
|
|
||||||
) -> Result<(), NodeRunError> {
|
|
||||||
let view_meta = world.get_resource::<ViewMeta>().unwrap();
|
|
||||||
view_meta
|
|
||||||
.uniforms
|
|
||||||
.write_to_uniform_buffer(&mut render_context.command_encoder);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ mod sprite;
|
|||||||
mod texture_atlas;
|
mod texture_atlas;
|
||||||
mod texture_atlas_builder;
|
mod texture_atlas_builder;
|
||||||
|
|
||||||
use bevy_asset::AddAsset;
|
|
||||||
pub use bundle::*;
|
pub use bundle::*;
|
||||||
pub use dynamic_texture_atlas_builder::*;
|
pub use dynamic_texture_atlas_builder::*;
|
||||||
pub use rect::*;
|
pub use rect::*;
|
||||||
@ -16,6 +15,8 @@ pub use texture_atlas::*;
|
|||||||
pub use texture_atlas_builder::*;
|
pub use texture_atlas_builder::*;
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
|
use bevy_asset::AddAsset;
|
||||||
|
use bevy_core_pipeline::Transparent2d;
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
render_graph::RenderGraph, render_phase::DrawFunctions, RenderApp, RenderStage,
|
render_graph::RenderGraph, render_phase::DrawFunctions, RenderApp, RenderStage,
|
||||||
};
|
};
|
||||||
@ -28,17 +29,18 @@ impl Plugin for SpritePlugin {
|
|||||||
app.add_asset::<TextureAtlas>().register_type::<Sprite>();
|
app.add_asset::<TextureAtlas>().register_type::<Sprite>();
|
||||||
let render_app = app.sub_app(RenderApp);
|
let render_app = app.sub_app(RenderApp);
|
||||||
render_app
|
render_app
|
||||||
.init_resource::<ExtractedSprites>()
|
.init_resource::<ImageBindGroups>()
|
||||||
|
.init_resource::<SpriteShaders>()
|
||||||
|
.init_resource::<SpriteMeta>()
|
||||||
.add_system_to_stage(RenderStage::Extract, render::extract_atlases)
|
.add_system_to_stage(RenderStage::Extract, render::extract_atlases)
|
||||||
.add_system_to_stage(RenderStage::Extract, render::extract_sprites)
|
.add_system_to_stage(RenderStage::Extract, render::extract_sprites)
|
||||||
.add_system_to_stage(RenderStage::Prepare, render::prepare_sprites)
|
.add_system_to_stage(RenderStage::Prepare, render::prepare_sprites)
|
||||||
.add_system_to_stage(RenderStage::Queue, queue_sprites)
|
.add_system_to_stage(RenderStage::Queue, queue_sprites);
|
||||||
.init_resource::<SpriteShaders>()
|
|
||||||
.init_resource::<SpriteMeta>();
|
|
||||||
let draw_sprite = DrawSprite::new(&mut render_app.world);
|
let draw_sprite = DrawSprite::new(&mut render_app.world);
|
||||||
render_app
|
render_app
|
||||||
.world
|
.world
|
||||||
.get_resource::<DrawFunctions>()
|
.get_resource::<DrawFunctions<Transparent2d>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write()
|
.write()
|
||||||
.add(draw_sprite);
|
.add(draw_sprite);
|
||||||
|
|||||||
@ -3,23 +3,25 @@ use crate::{
|
|||||||
Rect, Sprite,
|
Rect, Sprite,
|
||||||
};
|
};
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_core_pipeline::Transparent2dPhase;
|
use bevy_core_pipeline::Transparent2d;
|
||||||
use bevy_ecs::{prelude::*, system::SystemState};
|
use bevy_ecs::{
|
||||||
|
prelude::*,
|
||||||
|
system::{lifetimeless::*, SystemState},
|
||||||
|
};
|
||||||
use bevy_math::{Mat4, Vec2, Vec3, Vec4Swizzles};
|
use bevy_math::{Mat4, Vec2, Vec3, Vec4Swizzles};
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
mesh::{shape::Quad, Indices, Mesh, VertexAttributeValues},
|
mesh::{shape::Quad, Indices, Mesh, VertexAttributeValues},
|
||||||
render_asset::RenderAssets,
|
render_asset::RenderAssets,
|
||||||
render_graph::{Node, NodeRunError, RenderGraphContext},
|
render_graph::{Node, NodeRunError, RenderGraphContext},
|
||||||
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
|
render_phase::{Draw, DrawFunctions, RenderPhase, TrackedRenderPass},
|
||||||
render_resource::*,
|
render_resource::*,
|
||||||
renderer::{RenderContext, RenderDevice},
|
renderer::{RenderContext, RenderDevice},
|
||||||
shader::Shader,
|
shader::Shader,
|
||||||
texture::{BevyDefault, Image},
|
texture::{BevyDefault, Image},
|
||||||
view::{ViewMeta, ViewUniformOffset},
|
view::{ViewUniformOffset, ViewUniforms},
|
||||||
RenderWorld,
|
|
||||||
};
|
};
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
use bevy_utils::slab::{FrameSlabMap, FrameSlabMapKey};
|
use bevy_utils::HashMap;
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
pub struct SpriteShaders {
|
pub struct SpriteShaders {
|
||||||
@ -146,60 +148,56 @@ impl FromWorld for SpriteShaders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExtractedSprite {
|
pub struct ExtractedSprite {
|
||||||
transform: Mat4,
|
transform: Mat4,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
handle: Handle<Image>,
|
handle: Handle<Image>,
|
||||||
atlas_size: Option<Vec2>,
|
atlas_size: Option<Vec2>,
|
||||||
}
|
vertex_index: usize,
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ExtractedSprites {
|
|
||||||
sprites: Vec<ExtractedSprite>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_atlases(
|
pub fn extract_atlases(
|
||||||
|
mut commands: Commands,
|
||||||
texture_atlases: Res<Assets<TextureAtlas>>,
|
texture_atlases: Res<Assets<TextureAtlas>>,
|
||||||
atlas_query: Query<(&TextureAtlasSprite, &GlobalTransform, &Handle<TextureAtlas>)>,
|
atlas_query: Query<(
|
||||||
mut render_world: ResMut<RenderWorld>,
|
Entity,
|
||||||
|
&TextureAtlasSprite,
|
||||||
|
&GlobalTransform,
|
||||||
|
&Handle<TextureAtlas>,
|
||||||
|
)>,
|
||||||
) {
|
) {
|
||||||
let mut extracted_sprites = Vec::new();
|
let mut sprites = Vec::new();
|
||||||
for (atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() {
|
for (entity, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() {
|
||||||
if !texture_atlases.contains(texture_atlas_handle) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) {
|
if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) {
|
||||||
let rect = texture_atlas.textures[atlas_sprite.index as usize];
|
let rect = texture_atlas.textures[atlas_sprite.index as usize];
|
||||||
extracted_sprites.push(ExtractedSprite {
|
sprites.push((
|
||||||
|
entity,
|
||||||
|
(ExtractedSprite {
|
||||||
atlas_size: Some(texture_atlas.size),
|
atlas_size: Some(texture_atlas.size),
|
||||||
transform: transform.compute_matrix(),
|
transform: transform.compute_matrix(),
|
||||||
rect,
|
rect,
|
||||||
handle: texture_atlas.texture.clone_weak(),
|
handle: texture_atlas.texture.clone_weak(),
|
||||||
});
|
vertex_index: 0,
|
||||||
|
},),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
commands.insert_or_spawn_batch(sprites);
|
||||||
if let Some(mut extracted_sprites_res) = render_world.get_resource_mut::<ExtractedSprites>() {
|
|
||||||
extracted_sprites_res.sprites.extend(extracted_sprites);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_sprites(
|
pub fn extract_sprites(
|
||||||
|
mut commands: Commands,
|
||||||
images: Res<Assets<Image>>,
|
images: Res<Assets<Image>>,
|
||||||
sprite_query: Query<(&Sprite, &GlobalTransform, &Handle<Image>)>,
|
sprite_query: Query<(Entity, &Sprite, &GlobalTransform, &Handle<Image>)>,
|
||||||
mut render_world: ResMut<RenderWorld>,
|
|
||||||
) {
|
) {
|
||||||
let mut extracted_sprites = Vec::new();
|
let mut sprites = Vec::new();
|
||||||
for (sprite, transform, handle) in sprite_query.iter() {
|
for (entity, sprite, transform, handle) in sprite_query.iter() {
|
||||||
let image = if let Some(image) = images.get(handle) {
|
if let Some(image) = images.get(handle) {
|
||||||
image
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let size = image.texture_descriptor.size;
|
let size = image.texture_descriptor.size;
|
||||||
|
|
||||||
extracted_sprites.push(ExtractedSprite {
|
sprites.push((
|
||||||
|
entity,
|
||||||
|
(ExtractedSprite {
|
||||||
atlas_size: None,
|
atlas_size: None,
|
||||||
transform: transform.compute_matrix(),
|
transform: transform.compute_matrix(),
|
||||||
rect: Rect {
|
rect: Rect {
|
||||||
@ -209,12 +207,12 @@ pub fn extract_sprites(
|
|||||||
.unwrap_or_else(|| Vec2::new(size.width as f32, size.height as f32)),
|
.unwrap_or_else(|| Vec2::new(size.width as f32, size.height as f32)),
|
||||||
},
|
},
|
||||||
handle: handle.clone_weak(),
|
handle: handle.clone_weak(),
|
||||||
});
|
vertex_index: 0,
|
||||||
}
|
},),
|
||||||
|
));
|
||||||
if let Some(mut extracted_sprites_res) = render_world.get_resource_mut::<ExtractedSprites>() {
|
};
|
||||||
extracted_sprites_res.sprites.extend(extracted_sprites);
|
|
||||||
}
|
}
|
||||||
|
commands.insert_or_spawn_batch(sprites);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -229,8 +227,6 @@ pub struct SpriteMeta {
|
|||||||
indices: BufferVec<u32>,
|
indices: BufferVec<u32>,
|
||||||
quad: Mesh,
|
quad: Mesh,
|
||||||
view_bind_group: Option<BindGroup>,
|
view_bind_group: Option<BindGroup>,
|
||||||
texture_bind_group_keys: Vec<FrameSlabMapKey<Handle<Image>, BindGroup>>,
|
|
||||||
texture_bind_groups: FrameSlabMap<Handle<Image>, BindGroup>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SpriteMeta {
|
impl Default for SpriteMeta {
|
||||||
@ -238,8 +234,6 @@ impl Default for SpriteMeta {
|
|||||||
Self {
|
Self {
|
||||||
vertices: BufferVec::new(BufferUsage::VERTEX),
|
vertices: BufferVec::new(BufferUsage::VERTEX),
|
||||||
indices: BufferVec::new(BufferUsage::INDEX),
|
indices: BufferVec::new(BufferUsage::INDEX),
|
||||||
texture_bind_groups: Default::default(),
|
|
||||||
texture_bind_group_keys: Default::default(),
|
|
||||||
view_bind_group: None,
|
view_bind_group: None,
|
||||||
quad: Quad {
|
quad: Quad {
|
||||||
size: Vec2::new(1.0, 1.0),
|
size: Vec2::new(1.0, 1.0),
|
||||||
@ -253,10 +247,11 @@ impl Default for SpriteMeta {
|
|||||||
pub fn prepare_sprites(
|
pub fn prepare_sprites(
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
mut sprite_meta: ResMut<SpriteMeta>,
|
mut sprite_meta: ResMut<SpriteMeta>,
|
||||||
extracted_sprites: Res<ExtractedSprites>,
|
mut extracted_sprites: Query<&mut ExtractedSprite>,
|
||||||
) {
|
) {
|
||||||
|
let extracted_sprite_len = extracted_sprites.iter_mut().len();
|
||||||
// dont create buffers when there are no sprites
|
// dont create buffers when there are no sprites
|
||||||
if extracted_sprites.sprites.is_empty() {
|
if extracted_sprite_len == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,15 +274,14 @@ pub fn prepare_sprites(
|
|||||||
};
|
};
|
||||||
|
|
||||||
sprite_meta.vertices.reserve_and_clear(
|
sprite_meta.vertices.reserve_and_clear(
|
||||||
extracted_sprites.sprites.len() * quad_vertex_positions.len(),
|
extracted_sprite_len * quad_vertex_positions.len(),
|
||||||
&render_device,
|
|
||||||
);
|
|
||||||
sprite_meta.indices.reserve_and_clear(
|
|
||||||
extracted_sprites.sprites.len() * quad_indices.len(),
|
|
||||||
&render_device,
|
&render_device,
|
||||||
);
|
);
|
||||||
|
sprite_meta
|
||||||
|
.indices
|
||||||
|
.reserve_and_clear(extracted_sprite_len * quad_indices.len(), &render_device);
|
||||||
|
|
||||||
for (i, extracted_sprite) in extracted_sprites.sprites.iter().enumerate() {
|
for (i, mut extracted_sprite) in extracted_sprites.iter_mut().enumerate() {
|
||||||
let sprite_rect = extracted_sprite.rect;
|
let sprite_rect = extracted_sprite.rect;
|
||||||
|
|
||||||
// Specify the corners of the sprite
|
// Specify the corners of the sprite
|
||||||
@ -298,6 +292,7 @@ pub fn prepare_sprites(
|
|||||||
|
|
||||||
let atlas_positions: [Vec2; 4] = [bottom_left, top_left, top_right, bottom_right];
|
let atlas_positions: [Vec2; 4] = [bottom_left, top_left, top_right, bottom_right];
|
||||||
|
|
||||||
|
extracted_sprite.vertex_index = i;
|
||||||
for (index, vertex_position) in quad_vertex_positions.iter().enumerate() {
|
for (index, vertex_position) in quad_vertex_positions.iter().enumerate() {
|
||||||
let mut final_position =
|
let mut final_position =
|
||||||
Vec3::from(*vertex_position) * extracted_sprite.rect.size().extend(1.0);
|
Vec3::from(*vertex_position) * extracted_sprite.rect.size().extend(1.0);
|
||||||
@ -321,41 +316,39 @@ pub fn prepare_sprites(
|
|||||||
sprite_meta.indices.write_to_staging_buffer(&render_device);
|
sprite_meta.indices.write_to_staging_buffer(&render_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ImageBindGroups {
|
||||||
|
values: HashMap<Handle<Image>, BindGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn queue_sprites(
|
pub fn queue_sprites(
|
||||||
draw_functions: Res<DrawFunctions>,
|
draw_functions: Res<DrawFunctions<Transparent2d>>,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
mut sprite_meta: ResMut<SpriteMeta>,
|
mut sprite_meta: ResMut<SpriteMeta>,
|
||||||
view_meta: Res<ViewMeta>,
|
view_uniforms: Res<ViewUniforms>,
|
||||||
sprite_shaders: Res<SpriteShaders>,
|
sprite_shaders: Res<SpriteShaders>,
|
||||||
mut extracted_sprites: ResMut<ExtractedSprites>,
|
mut image_bind_groups: ResMut<ImageBindGroups>,
|
||||||
gpu_images: Res<RenderAssets<Image>>,
|
gpu_images: Res<RenderAssets<Image>>,
|
||||||
mut views: Query<&mut RenderPhase<Transparent2dPhase>>,
|
mut extracted_sprites: Query<(Entity, &ExtractedSprite)>,
|
||||||
|
mut views: Query<&mut RenderPhase<Transparent2d>>,
|
||||||
) {
|
) {
|
||||||
if view_meta.uniforms.is_empty() {
|
if let Some(view_binding) = view_uniforms.uniforms.binding() {
|
||||||
return;
|
sprite_meta.view_bind_group = Some(render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: define this without needing to check every frame
|
|
||||||
sprite_meta.view_bind_group.get_or_insert_with(|| {
|
|
||||||
render_device.create_bind_group(&BindGroupDescriptor {
|
|
||||||
entries: &[BindGroupEntry {
|
entries: &[BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
resource: view_meta.uniforms.binding(),
|
resource: view_binding,
|
||||||
}],
|
}],
|
||||||
label: None,
|
label: None,
|
||||||
layout: &sprite_shaders.view_layout,
|
layout: &sprite_shaders.view_layout,
|
||||||
})
|
}));
|
||||||
});
|
|
||||||
let sprite_meta = &mut *sprite_meta;
|
|
||||||
let draw_sprite_function = draw_functions.read().get_id::<DrawSprite>().unwrap();
|
let draw_sprite_function = draw_functions.read().get_id::<DrawSprite>().unwrap();
|
||||||
sprite_meta.texture_bind_groups.next_frame();
|
|
||||||
sprite_meta.texture_bind_group_keys.clear();
|
|
||||||
for mut transparent_phase in views.iter_mut() {
|
for mut transparent_phase in views.iter_mut() {
|
||||||
for (i, sprite) in extracted_sprites.sprites.iter().enumerate() {
|
for (entity, sprite) in extracted_sprites.iter_mut() {
|
||||||
let texture_bind_group_key = sprite_meta.texture_bind_groups.get_or_insert_with(
|
image_bind_groups
|
||||||
sprite.handle.clone_weak(),
|
.values
|
||||||
|| {
|
.entry(sprite.handle.clone_weak())
|
||||||
|
.or_insert_with(|| {
|
||||||
let gpu_image = gpu_images.get(&sprite.handle).unwrap();
|
let gpu_image = gpu_images.get(&sprite.handle).unwrap();
|
||||||
render_device.create_bind_group(&BindGroupDescriptor {
|
render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
entries: &[
|
entries: &[
|
||||||
@ -371,21 +364,15 @@ pub fn queue_sprites(
|
|||||||
label: None,
|
label: None,
|
||||||
layout: &sprite_shaders.material_layout,
|
layout: &sprite_shaders.material_layout,
|
||||||
})
|
})
|
||||||
},
|
});
|
||||||
);
|
transparent_phase.add(Transparent2d {
|
||||||
sprite_meta
|
|
||||||
.texture_bind_group_keys
|
|
||||||
.push(texture_bind_group_key);
|
|
||||||
|
|
||||||
transparent_phase.add(Drawable {
|
|
||||||
draw_function: draw_sprite_function,
|
draw_function: draw_sprite_function,
|
||||||
draw_key: i,
|
entity,
|
||||||
sort_key: texture_bind_group_key.index(),
|
sort_key: sprite.handle.clone_weak(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
extracted_sprites.sprites.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this logic can be moved to prepare_sprites once wgpu::Queue is exposed directly
|
// TODO: this logic can be moved to prepare_sprites once wgpu::Queue is exposed directly
|
||||||
@ -409,13 +396,14 @@ impl Node for SpriteNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type DrawSpriteQuery<'s, 'w> = (
|
|
||||||
Res<'w, SpriteShaders>,
|
|
||||||
Res<'w, SpriteMeta>,
|
|
||||||
Query<'w, 's, &'w ViewUniformOffset>,
|
|
||||||
);
|
|
||||||
pub struct DrawSprite {
|
pub struct DrawSprite {
|
||||||
params: SystemState<DrawSpriteQuery<'static, 'static>>,
|
params: SystemState<(
|
||||||
|
SRes<SpriteShaders>,
|
||||||
|
SRes<SpriteMeta>,
|
||||||
|
SRes<ImageBindGroups>,
|
||||||
|
SQuery<Read<ViewUniformOffset>>,
|
||||||
|
SQuery<Read<ExtractedSprite>>,
|
||||||
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawSprite {
|
impl DrawSprite {
|
||||||
@ -426,19 +414,21 @@ impl DrawSprite {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Draw for DrawSprite {
|
impl Draw<Transparent2d> for DrawSprite {
|
||||||
fn draw<'w>(
|
fn draw<'w>(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: &'w World,
|
world: &'w World,
|
||||||
pass: &mut TrackedRenderPass<'w>,
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
view: Entity,
|
view: Entity,
|
||||||
draw_key: usize,
|
item: &Transparent2d,
|
||||||
_sort_key: usize,
|
|
||||||
) {
|
) {
|
||||||
const INDICES: usize = 6;
|
const INDICES: usize = 6;
|
||||||
let (sprite_shaders, sprite_meta, views) = self.params.get(world);
|
let (sprite_shaders, sprite_meta, image_bind_groups, views, sprites) =
|
||||||
|
self.params.get(world);
|
||||||
let view_uniform = views.get(view).unwrap();
|
let view_uniform = views.get(view).unwrap();
|
||||||
let sprite_meta = sprite_meta.into_inner();
|
let sprite_meta = sprite_meta.into_inner();
|
||||||
|
let image_bind_groups = image_bind_groups.into_inner();
|
||||||
|
let extracted_sprite = sprites.get(item.entity).unwrap();
|
||||||
pass.set_render_pipeline(&sprite_shaders.into_inner().pipeline);
|
pass.set_render_pipeline(&sprite_shaders.into_inner().pipeline);
|
||||||
pass.set_vertex_buffer(0, sprite_meta.vertices.buffer().unwrap().slice(..));
|
pass.set_vertex_buffer(0, sprite_meta.vertices.buffer().unwrap().slice(..));
|
||||||
pass.set_index_buffer(
|
pass.set_index_buffer(
|
||||||
@ -453,12 +443,16 @@ impl Draw for DrawSprite {
|
|||||||
);
|
);
|
||||||
pass.set_bind_group(
|
pass.set_bind_group(
|
||||||
1,
|
1,
|
||||||
&sprite_meta.texture_bind_groups[sprite_meta.texture_bind_group_keys[draw_key]],
|
image_bind_groups
|
||||||
|
.values
|
||||||
|
.get(&extracted_sprite.handle)
|
||||||
|
.unwrap(),
|
||||||
&[],
|
&[],
|
||||||
);
|
);
|
||||||
|
|
||||||
pass.draw_indexed(
|
pass.draw_indexed(
|
||||||
(draw_key * INDICES) as u32..(draw_key * INDICES + INDICES) as u32,
|
(extracted_sprite.vertex_index * INDICES) as u32
|
||||||
|
..(extracted_sprite.vertex_index * INDICES + INDICES) as u32,
|
||||||
0,
|
0,
|
||||||
0..1,
|
0..1,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 v_Uv;
|
|
||||||
layout(location = 0) out vec4 o_Target;
|
|
||||||
|
|
||||||
layout(set = 1, binding = 0) uniform texture2D sprite_texture;
|
|
||||||
layout(set = 1, binding = 1) uniform sampler sprite_sampler;
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
o_Target = texture(sampler2D(sprite_texture, sprite_sampler), v_Uv);
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 Vertex_Position;
|
|
||||||
layout(location = 1) in vec2 Vertex_Uv;
|
|
||||||
|
|
||||||
layout(location = 0) out vec2 v_Uv;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform View {
|
|
||||||
mat4 ViewProj;
|
|
||||||
vec4 ViewWorldPosition;
|
|
||||||
// vec3 ViewWorldPosition;
|
|
||||||
};
|
|
||||||
|
|
||||||
void vertex() {
|
|
||||||
v_Uv = Vertex_Uv;
|
|
||||||
gl_Position = ViewProj * vec4(Vertex_Position, 1.0);
|
|
||||||
}
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
// TODO: try merging this block with the binding?
|
|
||||||
[[block]]
|
[[block]]
|
||||||
struct View {
|
struct View {
|
||||||
view_proj: mat4x4<f32>;
|
view_proj: mat4x4<f32>;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user