don't use bevy_pbr for base bevy_gizmos plugin (#17581)

# Objective

This PR enables `bevy_gizmos` to be used without `bevy_pbr`, for user
who want to create their custom mesh rendering logic.

It can also be useful for user who just want to use bevy for drawing
lines (why not).

This work is part of a bigger effort to make the bevy rendering pipeline
more modular. I would like to contribute an exemple to render custom
meshes without `bevy_pbr`. Something like
[this](https://github.com/rambip/plant-mesh/blob/main/src/shader/mod.rs)

## Solution

Now, `bevy_pbr` is an optional dependency, and used only to debug
lights.

I query the `ViewUniforms` manually, instead of using `bevy_pbr` to get
the heavy `MeshViewLayout`

## Testing

I'm not used to testing with bevy at all, but I was able to use
successfully in my project.
It might break for some different mesh pipelines, but I don't think so.

---

## Showcase


![image](https://github.com/user-attachments/assets/7fa22d1c-8b4f-456b-a74b-1a579449e9f5)
So nice ...

## Migration Guide

I don't think there is any breaking change


# Remaining work

Before merging it, it would be useful to:
- rewrite the `pipeline_2d.rs` logic to remove the `bevy_sprite`
depedency too
- move `view.rs` to `bevy_render`, so that it can be used in a more
modular way.
~~- include the most recent changes from 0.16~~

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
This commit is contained in:
Antonin Peronnet 2025-03-10 22:16:52 +01:00 committed by GitHub
parent cdef139710
commit b574599444
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 192 additions and 231 deletions

View File

@ -16,7 +16,6 @@ bevy_render = ["dep:bevy_render", "bevy_core_pipeline"]
[dependencies] [dependencies]
# Bevy # Bevy
bevy_pbr = { path = "../bevy_pbr", version = "0.16.0-dev", optional = true } bevy_pbr = { path = "../bevy_pbr", version = "0.16.0-dev", optional = true }
bevy_sprite = { path = "../bevy_sprite", version = "0.16.0-dev", optional = true }
bevy_app = { path = "../bevy_app", version = "0.16.0-dev" } bevy_app = { path = "../bevy_app", version = "0.16.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.16.0-dev" } bevy_color = { path = "../bevy_color", version = "0.16.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev" }

View File

@ -2,10 +2,7 @@
pub use bevy_gizmos_macros::GizmoConfigGroup; pub use bevy_gizmos_macros::GizmoConfigGroup;
#[cfg(all( #[cfg(feature = "bevy_render")]
feature = "bevy_render",
any(feature = "bevy_pbr", feature = "bevy_sprite")
))]
use {crate::GizmoAsset, bevy_asset::Handle, bevy_ecs::component::Component}; use {crate::GizmoAsset, bevy_asset::Handle, bevy_ecs::component::Component};
use bevy_ecs::{reflect::ReflectResource, resource::Resource}; use bevy_ecs::{reflect::ReflectResource, resource::Resource};
@ -238,10 +235,7 @@ impl Default for GizmoLineConfig {
} }
} }
#[cfg(all( #[cfg(feature = "bevy_render")]
feature = "bevy_render",
any(feature = "bevy_pbr", feature = "bevy_sprite")
))]
#[derive(Component)] #[derive(Component)]
pub(crate) struct GizmoMeshConfig { pub(crate) struct GizmoMeshConfig {
pub line_perspective: bool, pub line_perspective: bool,

View File

@ -26,10 +26,8 @@ extern crate self as bevy_gizmos;
#[derive(SystemSet, Clone, Debug, Hash, PartialEq, Eq)] #[derive(SystemSet, Clone, Debug, Hash, PartialEq, Eq)]
pub enum GizmoRenderSystem { pub enum GizmoRenderSystem {
/// Adds gizmos to the [`Transparent2d`](bevy_core_pipeline::core_2d::Transparent2d) render phase /// Adds gizmos to the [`Transparent2d`](bevy_core_pipeline::core_2d::Transparent2d) render phase
#[cfg(feature = "bevy_sprite")]
QueueLineGizmos2d, QueueLineGizmos2d,
/// Adds gizmos to the [`Transparent3d`](bevy_core_pipeline::core_3d::Transparent3d) render phase /// Adds gizmos to the [`Transparent3d`](bevy_core_pipeline::core_3d::Transparent3d) render phase
#[cfg(feature = "bevy_pbr")]
QueueLineGizmos3d, QueueLineGizmos3d,
} }
@ -46,13 +44,16 @@ pub mod grid;
pub mod primitives; pub mod primitives;
pub mod retained; pub mod retained;
pub mod rounded_box; pub mod rounded_box;
#[cfg(feature = "bevy_render")]
mod view;
#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))] #[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
pub mod light; pub mod light;
#[cfg(all(feature = "bevy_sprite", feature = "bevy_render"))] #[cfg(feature = "bevy_render")]
mod pipeline_2d; mod pipeline_2d;
#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
#[cfg(feature = "bevy_render")]
mod pipeline_3d; mod pipeline_3d;
/// The gizmos prelude. /// The gizmos prelude.
@ -85,13 +86,12 @@ use bevy_ecs::{
schedule::{IntoSystemConfigs, SystemSet}, schedule::{IntoSystemConfigs, SystemSet},
system::{Res, ResMut}, system::{Res, ResMut},
}; };
use bevy_math::{Vec3, Vec4}; use bevy_math::Vec4;
use bevy_reflect::TypePath; use bevy_reflect::TypePath;
#[cfg(feature = "bevy_render")]
use view::OnlyViewLayout;
#[cfg(all( #[cfg(feature = "bevy_render")]
feature = "bevy_render",
any(feature = "bevy_pbr", feature = "bevy_sprite")
))]
use crate::config::GizmoMeshConfig; use crate::config::GizmoMeshConfig;
use crate::{config::ErasedGizmoConfigGroup, gizmos::GizmoBuffer}; use crate::{config::ErasedGizmoConfigGroup, gizmos::GizmoBuffer};
@ -125,10 +125,7 @@ use {
bytemuck::cast_slice, bytemuck::cast_slice,
}; };
#[cfg(all( #[cfg(feature = "bevy_render")]
feature = "bevy_render",
any(feature = "bevy_pbr", feature = "bevy_sprite"),
))]
use bevy_render::render_resource::{VertexAttribute, VertexBufferLayout, VertexStepMode}; use bevy_render::render_resource::{VertexAttribute, VertexBufferLayout, VertexStepMode};
use bevy_time::Fixed; use bevy_time::Fixed;
use bevy_utils::TypeIdMap; use bevy_utils::TypeIdMap;
@ -146,9 +143,10 @@ const LINE_SHADER_HANDLE: Handle<Shader> = weak_handle!("15dc5869-ad30-4664-b35a
const LINE_JOINT_SHADER_HANDLE: Handle<Shader> = const LINE_JOINT_SHADER_HANDLE: Handle<Shader> =
weak_handle!("7b5bdda5-df81-4711-a6cf-e587700de6f2"); weak_handle!("7b5bdda5-df81-4711-a6cf-e587700de6f2");
/// A [`Plugin`] that provides an immediate mode drawing api for visual debugging. /// A [`Plugin`] for the [`RenderApp`] that provides an immediate mode drawing api for visual debugging.
/// ///
/// Requires to be loaded after [`PbrPlugin`](bevy_pbr::PbrPlugin) or [`SpritePlugin`](bevy_sprite::SpritePlugin). /// Additionally, it can support debugging light when the `bevy_pbr` feature is enabled,
/// see [`LightGizmoPlugin`]
#[derive(Default)] #[derive(Default)]
pub struct GizmoPlugin; pub struct GizmoPlugin;
@ -189,19 +187,8 @@ impl Plugin for GizmoPlugin {
); );
render_app.add_systems(ExtractSchedule, (extract_gizmo_data, extract_linegizmos)); render_app.add_systems(ExtractSchedule, (extract_gizmo_data, extract_linegizmos));
app.add_plugins(pipeline_3d::LineGizmo3dPlugin)
#[cfg(feature = "bevy_sprite")] .add_plugins(pipeline_2d::LineGizmo2dPlugin);
if app.is_plugin_added::<bevy_sprite::SpritePlugin>() {
app.add_plugins(pipeline_2d::LineGizmo2dPlugin);
} else {
tracing::warn!("bevy_sprite feature is enabled but bevy_sprite::SpritePlugin was not detected. Are you sure you loaded GizmoPlugin after SpritePlugin?");
}
#[cfg(feature = "bevy_pbr")]
if app.is_plugin_added::<bevy_pbr::PbrPlugin>() {
app.add_plugins(pipeline_3d::LineGizmo3dPlugin);
} else {
tracing::warn!("bevy_pbr feature is enabled but bevy_pbr::PbrPlugin was not detected. Are you sure you loaded GizmoPlugin after PbrPlugin?");
}
} else { } else {
tracing::warn!("bevy_render feature is enabled but RenderApp was not detected. Are you sure you loaded GizmoPlugin after RenderPlugin?"); tracing::warn!("bevy_render feature is enabled but RenderApp was not detected. Are you sure you loaded GizmoPlugin after RenderPlugin?");
} }
@ -222,7 +209,9 @@ impl Plugin for GizmoPlugin {
), ),
); );
render_app.insert_resource(LineGizmoUniformBindgroupLayout { render_app
.init_resource::<OnlyViewLayout>()
.insert_resource(LineGizmoUniformBindgroupLayout {
layout: line_layout, layout: line_layout,
}); });
} }
@ -474,7 +463,6 @@ fn extract_gizmo_data(
#[cfg(feature = "webgl")] #[cfg(feature = "webgl")]
_padding: Default::default(), _padding: Default::default(),
}, },
#[cfg(any(feature = "bevy_pbr", feature = "bevy_sprite"))]
GizmoMeshConfig { GizmoMeshConfig {
line_perspective: config.line.perspective, line_perspective: config.line.perspective,
line_style: config.line.style, line_style: config.line.style,
@ -503,7 +491,7 @@ struct LineGizmoUniform {
line_scale: f32, line_scale: f32,
/// WebGL2 structs must be 16 byte aligned. /// WebGL2 structs must be 16 byte aligned.
#[cfg(feature = "webgl")] #[cfg(feature = "webgl")]
_padding: Vec3, _padding: bevy_math::Vec3,
} }
/// A collection of gizmos. /// A collection of gizmos.
@ -634,7 +622,7 @@ impl<const I: usize, P: PhaseItem> RenderCommand<P> for SetLineGizmoBindGroup<I>
#[inline] #[inline]
fn render<'w>( fn render<'w>(
_item: &P, _item: &P,
_view: ROQueryItem<'w, Self::ViewQuery>, _views: ROQueryItem<'w, Self::ViewQuery>,
uniform_index: Option<ROQueryItem<'w, Self::ItemQuery>>, uniform_index: Option<ROQueryItem<'w, Self::ItemQuery>>,
bind_group: SystemParamItem<'w, '_, Self::Param>, bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
@ -653,10 +641,7 @@ impl<const I: usize, P: PhaseItem> RenderCommand<P> for SetLineGizmoBindGroup<I>
#[cfg(feature = "bevy_render")] #[cfg(feature = "bevy_render")]
struct DrawLineGizmo<const STRIP: bool>; struct DrawLineGizmo<const STRIP: bool>;
#[cfg(all( #[cfg(feature = "bevy_render")]
feature = "bevy_render",
any(feature = "bevy_pbr", feature = "bevy_sprite")
))]
impl<P: PhaseItem, const STRIP: bool> RenderCommand<P> for DrawLineGizmo<STRIP> { impl<P: PhaseItem, const STRIP: bool> RenderCommand<P> for DrawLineGizmo<STRIP> {
type Param = SRes<RenderAssets<GpuLineGizmo>>; type Param = SRes<RenderAssets<GpuLineGizmo>>;
type ViewQuery = (); type ViewQuery = ();
@ -716,10 +701,7 @@ impl<P: PhaseItem, const STRIP: bool> RenderCommand<P> for DrawLineGizmo<STRIP>
#[cfg(feature = "bevy_render")] #[cfg(feature = "bevy_render")]
struct DrawLineJointGizmo; struct DrawLineJointGizmo;
#[cfg(all( #[cfg(feature = "bevy_render")]
feature = "bevy_render",
any(feature = "bevy_pbr", feature = "bevy_sprite")
))]
impl<P: PhaseItem> RenderCommand<P> for DrawLineJointGizmo { impl<P: PhaseItem> RenderCommand<P> for DrawLineJointGizmo {
type Param = SRes<RenderAssets<GpuLineGizmo>>; type Param = SRes<RenderAssets<GpuLineGizmo>>;
type ViewQuery = (); type ViewQuery = ();
@ -789,10 +771,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawLineJointGizmo {
} }
} }
#[cfg(all( #[cfg(feature = "bevy_render")]
feature = "bevy_render",
any(feature = "bevy_pbr", feature = "bevy_sprite")
))]
fn line_gizmo_vertex_buffer_layouts(strip: bool) -> Vec<VertexBufferLayout> { fn line_gizmo_vertex_buffer_layouts(strip: bool) -> Vec<VertexBufferLayout> {
use VertexFormat::*; use VertexFormat::*;
let mut position_layout = VertexBufferLayout { let mut position_layout = VertexBufferLayout {
@ -847,10 +826,7 @@ fn line_gizmo_vertex_buffer_layouts(strip: bool) -> Vec<VertexBufferLayout> {
} }
} }
#[cfg(all( #[cfg(feature = "bevy_render")]
feature = "bevy_render",
any(feature = "bevy_pbr", feature = "bevy_sprite")
))]
fn line_joint_gizmo_vertex_buffer_layouts() -> Vec<VertexBufferLayout> { fn line_joint_gizmo_vertex_buffer_layouts() -> Vec<VertexBufferLayout> {
use VertexFormat::*; use VertexFormat::*;
let mut position_layout = VertexBufferLayout { let mut position_layout = VertexBufferLayout {

View File

@ -1,3 +1,4 @@
use crate::view::{OnlyViewLayout, SetViewBindGroup};
use crate::{ use crate::{
config::{GizmoLineJoint, GizmoLineStyle, GizmoMeshConfig}, config::{GizmoLineJoint, GizmoLineStyle, GizmoMeshConfig},
line_gizmo_vertex_buffer_layouts, line_joint_gizmo_vertex_buffer_layouts, DrawLineGizmo, line_gizmo_vertex_buffer_layouts, line_joint_gizmo_vertex_buffer_layouts, DrawLineGizmo,
@ -10,7 +11,7 @@ use bevy_core_pipeline::core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT};
use bevy_ecs::{ use bevy_ecs::{
prelude::Entity, prelude::Entity,
resource::Resource, resource::Resource,
schedule::{IntoSystemConfigs, IntoSystemSetConfigs}, schedule::IntoSystemConfigs,
system::{Query, Res, ResMut}, system::{Query, Res, ResMut},
world::{FromWorld, World}, world::{FromWorld, World},
}; };
@ -25,9 +26,8 @@ use bevy_render::{
}, },
render_resource::*, render_resource::*,
view::{ExtractedView, Msaa, RenderLayers, ViewTarget}, view::{ExtractedView, Msaa, RenderLayers, ViewTarget},
Render, RenderApp, RenderSet, Render, RenderApp,
}; };
use bevy_sprite::{Mesh2dPipeline, Mesh2dPipelineKey, SetMesh2dViewBindGroup};
use tracing::error; use tracing::error;
pub struct LineGizmo2dPlugin; pub struct LineGizmo2dPlugin;
@ -44,15 +44,6 @@ impl Plugin for LineGizmo2dPlugin {
.add_render_command::<Transparent2d, DrawLineJointGizmo2d>() .add_render_command::<Transparent2d, DrawLineJointGizmo2d>()
.init_resource::<SpecializedRenderPipelines<LineGizmoPipeline>>() .init_resource::<SpecializedRenderPipelines<LineGizmoPipeline>>()
.init_resource::<SpecializedRenderPipelines<LineJointGizmoPipeline>>() .init_resource::<SpecializedRenderPipelines<LineJointGizmoPipeline>>()
.configure_sets(
Render,
GizmoRenderSystem::QueueLineGizmos2d
.in_set(RenderSet::Queue)
.ambiguous_with(bevy_sprite::queue_sprites)
.ambiguous_with(
bevy_sprite::queue_material2d_meshes::<bevy_sprite::ColorMaterial>,
),
)
.add_systems( .add_systems(
Render, Render,
(queue_line_gizmos_2d, queue_line_joint_gizmos_2d) (queue_line_gizmos_2d, queue_line_joint_gizmos_2d)
@ -73,14 +64,15 @@ impl Plugin for LineGizmo2dPlugin {
#[derive(Clone, Resource)] #[derive(Clone, Resource)]
struct LineGizmoPipeline { struct LineGizmoPipeline {
mesh_pipeline: Mesh2dPipeline, view_layout: BindGroupLayout,
uniform_layout: BindGroupLayout, uniform_layout: BindGroupLayout,
} }
impl FromWorld for LineGizmoPipeline { impl FromWorld for LineGizmoPipeline {
fn from_world(render_world: &mut World) -> Self { fn from_world(render_world: &mut World) -> Self {
let view_layout = render_world.resource::<OnlyViewLayout>().0.clone();
LineGizmoPipeline { LineGizmoPipeline {
mesh_pipeline: render_world.resource::<Mesh2dPipeline>().clone(), view_layout,
uniform_layout: render_world uniform_layout: render_world
.resource::<LineGizmoUniformBindgroupLayout>() .resource::<LineGizmoUniformBindgroupLayout>()
.layout .layout
@ -91,7 +83,8 @@ impl FromWorld for LineGizmoPipeline {
#[derive(PartialEq, Eq, Hash, Clone)] #[derive(PartialEq, Eq, Hash, Clone)]
struct LineGizmoPipelineKey { struct LineGizmoPipelineKey {
mesh_key: Mesh2dPipelineKey, hdr: bool,
msaa: Msaa,
strip: bool, strip: bool,
line_style: GizmoLineStyle, line_style: GizmoLineStyle,
} }
@ -100,7 +93,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
type Key = LineGizmoPipelineKey; type Key = LineGizmoPipelineKey;
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let format = if key.mesh_key.contains(Mesh2dPipelineKey::HDR) { let format = if key.hdr {
ViewTarget::TEXTURE_FORMAT_HDR ViewTarget::TEXTURE_FORMAT_HDR
} else { } else {
TextureFormat::bevy_default() TextureFormat::bevy_default()
@ -111,10 +104,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
"SIXTEEN_BYTE_ALIGNMENT".into(), "SIXTEEN_BYTE_ALIGNMENT".into(),
]; ];
let layout = vec![ let layout = vec![self.view_layout.clone(), self.uniform_layout.clone()];
self.mesh_pipeline.view_layout.clone(),
self.uniform_layout.clone(),
];
let fragment_entry_point = match key.line_style { let fragment_entry_point = match key.line_style {
GizmoLineStyle::Solid => "fragment_solid", GizmoLineStyle::Solid => "fragment_solid",
@ -158,7 +148,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
}, },
}), }),
multisample: MultisampleState { multisample: MultisampleState {
count: key.mesh_key.msaa_samples(), count: key.msaa.samples(),
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
@ -171,14 +161,15 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
#[derive(Clone, Resource)] #[derive(Clone, Resource)]
struct LineJointGizmoPipeline { struct LineJointGizmoPipeline {
mesh_pipeline: Mesh2dPipeline, view_layout: BindGroupLayout,
uniform_layout: BindGroupLayout, uniform_layout: BindGroupLayout,
} }
impl FromWorld for LineJointGizmoPipeline { impl FromWorld for LineJointGizmoPipeline {
fn from_world(render_world: &mut World) -> Self { fn from_world(render_world: &mut World) -> Self {
let view_layout = render_world.resource::<OnlyViewLayout>().0.clone();
LineJointGizmoPipeline { LineJointGizmoPipeline {
mesh_pipeline: render_world.resource::<Mesh2dPipeline>().clone(), view_layout,
uniform_layout: render_world uniform_layout: render_world
.resource::<LineGizmoUniformBindgroupLayout>() .resource::<LineGizmoUniformBindgroupLayout>()
.layout .layout
@ -189,7 +180,8 @@ impl FromWorld for LineJointGizmoPipeline {
#[derive(PartialEq, Eq, Hash, Clone)] #[derive(PartialEq, Eq, Hash, Clone)]
struct LineJointGizmoPipelineKey { struct LineJointGizmoPipelineKey {
mesh_key: Mesh2dPipelineKey, hdr: bool,
msaa: Msaa,
joints: GizmoLineJoint, joints: GizmoLineJoint,
} }
@ -197,7 +189,7 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
type Key = LineJointGizmoPipelineKey; type Key = LineJointGizmoPipelineKey;
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let format = if key.mesh_key.contains(Mesh2dPipelineKey::HDR) { let format = if key.hdr {
ViewTarget::TEXTURE_FORMAT_HDR ViewTarget::TEXTURE_FORMAT_HDR
} else { } else {
TextureFormat::bevy_default() TextureFormat::bevy_default()
@ -208,10 +200,7 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
"SIXTEEN_BYTE_ALIGNMENT".into(), "SIXTEEN_BYTE_ALIGNMENT".into(),
]; ];
let layout = vec![ let layout = vec![self.view_layout.clone(), self.uniform_layout.clone()];
self.mesh_pipeline.view_layout.clone(),
self.uniform_layout.clone(),
];
if key.joints == GizmoLineJoint::None { if key.joints == GizmoLineJoint::None {
error!("There is no entry point for line joints with GizmoLineJoints::None. Please consider aborting the drawing process before reaching this stage."); error!("There is no entry point for line joints with GizmoLineJoints::None. Please consider aborting the drawing process before reaching this stage.");
@ -259,7 +248,7 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
}, },
}), }),
multisample: MultisampleState { multisample: MultisampleState {
count: key.mesh_key.msaa_samples(), count: key.msaa.samples(),
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
@ -272,19 +261,19 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
type DrawLineGizmo2d = ( type DrawLineGizmo2d = (
SetItemPipeline, SetItemPipeline,
SetMesh2dViewBindGroup<0>, SetViewBindGroup<0>,
SetLineGizmoBindGroup<1>, SetLineGizmoBindGroup<1>,
DrawLineGizmo<false>, DrawLineGizmo<false>,
); );
type DrawLineGizmo2dStrip = ( type DrawLineGizmo2dStrip = (
SetItemPipeline, SetItemPipeline,
SetMesh2dViewBindGroup<0>, SetViewBindGroup<0>,
SetLineGizmoBindGroup<1>, SetLineGizmoBindGroup<1>,
DrawLineGizmo<true>, DrawLineGizmo<true>,
); );
type DrawLineJointGizmo2d = ( type DrawLineJointGizmo2d = (
SetItemPipeline, SetItemPipeline,
SetMesh2dViewBindGroup<0>, SetViewBindGroup<0>,
SetLineGizmoBindGroup<1>, SetLineGizmoBindGroup<1>,
DrawLineJointGizmo, DrawLineJointGizmo,
); );
@ -311,9 +300,6 @@ fn queue_line_gizmos_2d(
continue; continue;
}; };
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())
| Mesh2dPipelineKey::from_hdr(view.hdr);
let render_layers = render_layers.unwrap_or_default(); let render_layers = render_layers.unwrap_or_default();
for (entity, main_entity, config) in &line_gizmos { for (entity, main_entity, config) in &line_gizmos {
if !config.render_layers.intersects(render_layers) { if !config.render_layers.intersects(render_layers) {
@ -329,7 +315,8 @@ fn queue_line_gizmos_2d(
&pipeline_cache, &pipeline_cache,
&pipeline, &pipeline,
LineGizmoPipelineKey { LineGizmoPipelineKey {
mesh_key, msaa: *msaa,
hdr: view.hdr,
strip: false, strip: false,
line_style: config.line_style, line_style: config.line_style,
}, },
@ -350,7 +337,8 @@ fn queue_line_gizmos_2d(
&pipeline_cache, &pipeline_cache,
&pipeline, &pipeline,
LineGizmoPipelineKey { LineGizmoPipelineKey {
mesh_key, msaa: *msaa,
hdr: view.hdr,
strip: true, strip: true,
line_style: config.line_style, line_style: config.line_style,
}, },
@ -389,9 +377,6 @@ fn queue_line_joint_gizmos_2d(
continue; continue;
}; };
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())
| Mesh2dPipelineKey::from_hdr(view.hdr);
let render_layers = render_layers.unwrap_or_default(); let render_layers = render_layers.unwrap_or_default();
for (entity, main_entity, config) in &line_gizmos { for (entity, main_entity, config) in &line_gizmos {
if !config.render_layers.intersects(render_layers) { if !config.render_layers.intersects(render_layers) {
@ -410,7 +395,8 @@ fn queue_line_joint_gizmos_2d(
&pipeline_cache, &pipeline_cache,
&pipeline, &pipeline,
LineJointGizmoPipelineKey { LineJointGizmoPipelineKey {
mesh_key, msaa: *msaa,
hdr: view.hdr,
joints: config.line_joints, joints: config.line_joints,
}, },
); );

View File

@ -1,26 +1,22 @@
use crate::{ use crate::{
config::{GizmoLineJoint, GizmoLineStyle, GizmoMeshConfig}, config::{GizmoLineJoint, GizmoLineStyle, GizmoMeshConfig},
line_gizmo_vertex_buffer_layouts, line_joint_gizmo_vertex_buffer_layouts, DrawLineGizmo, line_gizmo_vertex_buffer_layouts, line_joint_gizmo_vertex_buffer_layouts,
DrawLineJointGizmo, GizmoRenderSystem, GpuLineGizmo, LineGizmoUniformBindgroupLayout, view::{prepare_view_bind_groups, OnlyViewLayout, SetViewBindGroup},
SetLineGizmoBindGroup, LINE_JOINT_SHADER_HANDLE, LINE_SHADER_HANDLE, DrawLineGizmo, DrawLineJointGizmo, GizmoRenderSystem, GpuLineGizmo,
LineGizmoUniformBindgroupLayout, SetLineGizmoBindGroup, LINE_JOINT_SHADER_HANDLE,
LINE_SHADER_HANDLE,
}; };
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_core_pipeline::{ use bevy_core_pipeline::core_3d::{Transparent3d, CORE_3D_DEPTH_FORMAT};
core_3d::{Transparent3d, CORE_3D_DEPTH_FORMAT}, use bevy_ecs::schedule::IntoSystemConfigs;
oit::OrderIndependentTransparencySettings,
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
};
use bevy_ecs::{ use bevy_ecs::{
prelude::Entity, prelude::Entity,
query::Has,
resource::Resource, resource::Resource,
schedule::{IntoSystemConfigs, IntoSystemSetConfigs},
system::{Query, Res, ResMut}, system::{Query, Res, ResMut},
world::{FromWorld, World}, world::{FromWorld, World},
}; };
use bevy_image::BevyDefault as _; use bevy_image::BevyDefault as _;
use bevy_pbr::{MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup};
use bevy_render::sync_world::MainEntity; use bevy_render::sync_world::MainEntity;
use bevy_render::{ use bevy_render::{
render_asset::{prepare_assets, RenderAssets}, render_asset::{prepare_assets, RenderAssets},
@ -33,7 +29,6 @@ use bevy_render::{
Render, RenderApp, RenderSet, Render, RenderApp, RenderSet,
}; };
use tracing::error; use tracing::error;
pub struct LineGizmo3dPlugin; pub struct LineGizmo3dPlugin;
impl Plugin for LineGizmo3dPlugin { impl Plugin for LineGizmo3dPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
@ -47,15 +42,13 @@ impl Plugin for LineGizmo3dPlugin {
.add_render_command::<Transparent3d, DrawLineJointGizmo3d>() .add_render_command::<Transparent3d, DrawLineJointGizmo3d>()
.init_resource::<SpecializedRenderPipelines<LineGizmoPipeline>>() .init_resource::<SpecializedRenderPipelines<LineGizmoPipeline>>()
.init_resource::<SpecializedRenderPipelines<LineJointGizmoPipeline>>() .init_resource::<SpecializedRenderPipelines<LineJointGizmoPipeline>>()
.configure_sets(
Render,
GizmoRenderSystem::QueueLineGizmos3d
.in_set(RenderSet::Queue)
.ambiguous_with(bevy_pbr::queue_material_meshes::<bevy_pbr::StandardMaterial>),
)
.add_systems( .add_systems(
Render, Render,
(queue_line_gizmos_3d, queue_line_joint_gizmos_3d) (
queue_line_gizmos_3d,
queue_line_joint_gizmos_3d,
prepare_view_bind_groups.in_set(RenderSet::Prepare),
)
.in_set(GizmoRenderSystem::QueueLineGizmos3d) .in_set(GizmoRenderSystem::QueueLineGizmos3d)
.after(prepare_assets::<GpuLineGizmo>), .after(prepare_assets::<GpuLineGizmo>),
); );
@ -72,15 +65,17 @@ impl Plugin for LineGizmo3dPlugin {
} }
#[derive(Clone, Resource)] #[derive(Clone, Resource)]
struct LineGizmoPipeline { pub(crate) struct LineGizmoPipeline {
mesh_pipeline: MeshPipeline, view_layout: BindGroupLayout,
uniform_layout: BindGroupLayout, uniform_layout: BindGroupLayout,
} }
impl FromWorld for LineGizmoPipeline { impl FromWorld for LineGizmoPipeline {
fn from_world(render_world: &mut World) -> Self { fn from_world(render_world: &mut World) -> Self {
let view_layout = render_world.resource::<OnlyViewLayout>().0.clone();
LineGizmoPipeline { LineGizmoPipeline {
mesh_pipeline: render_world.resource::<MeshPipeline>().clone(), view_layout,
uniform_layout: render_world uniform_layout: render_world
.resource::<LineGizmoUniformBindgroupLayout>() .resource::<LineGizmoUniformBindgroupLayout>()
.layout .layout
@ -90,8 +85,9 @@ impl FromWorld for LineGizmoPipeline {
} }
#[derive(PartialEq, Eq, Hash, Clone)] #[derive(PartialEq, Eq, Hash, Clone)]
struct LineGizmoPipelineKey { pub(crate) struct LineGizmoPipelineKey {
view_key: MeshPipelineKey, msaa: Msaa,
hdr: bool,
strip: bool, strip: bool,
perspective: bool, perspective: bool,
line_style: GizmoLineStyle, line_style: GizmoLineStyle,
@ -110,18 +106,13 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
shader_defs.push("PERSPECTIVE".into()); shader_defs.push("PERSPECTIVE".into());
} }
let format = if key.view_key.contains(MeshPipelineKey::HDR) { let format = if key.hdr {
ViewTarget::TEXTURE_FORMAT_HDR ViewTarget::TEXTURE_FORMAT_HDR
} else { } else {
TextureFormat::bevy_default() TextureFormat::bevy_default()
}; };
let view_layout = self let layout = vec![self.view_layout.clone(), self.uniform_layout.clone()];
.mesh_pipeline
.get_view_layout(key.view_key.into())
.clone();
let layout = vec![view_layout, self.uniform_layout.clone()];
let fragment_entry_point = match key.line_style { let fragment_entry_point = match key.line_style {
GizmoLineStyle::Solid => "fragment_solid", GizmoLineStyle::Solid => "fragment_solid",
@ -156,7 +147,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
bias: DepthBiasState::default(), bias: DepthBiasState::default(),
}), }),
multisample: MultisampleState { multisample: MultisampleState {
count: key.view_key.msaa_samples(), count: key.msaa.samples(),
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
@ -169,14 +160,16 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
#[derive(Clone, Resource)] #[derive(Clone, Resource)]
struct LineJointGizmoPipeline { struct LineJointGizmoPipeline {
mesh_pipeline: MeshPipeline, view_layout: BindGroupLayout,
uniform_layout: BindGroupLayout, uniform_layout: BindGroupLayout,
} }
impl FromWorld for LineJointGizmoPipeline { impl FromWorld for LineJointGizmoPipeline {
fn from_world(render_world: &mut World) -> Self { fn from_world(render_world: &mut World) -> Self {
let view_layout = render_world.resource::<OnlyViewLayout>().0.clone();
LineJointGizmoPipeline { LineJointGizmoPipeline {
mesh_pipeline: render_world.resource::<MeshPipeline>().clone(), view_layout,
uniform_layout: render_world uniform_layout: render_world
.resource::<LineGizmoUniformBindgroupLayout>() .resource::<LineGizmoUniformBindgroupLayout>()
.layout .layout
@ -187,7 +180,8 @@ impl FromWorld for LineJointGizmoPipeline {
#[derive(PartialEq, Eq, Hash, Clone)] #[derive(PartialEq, Eq, Hash, Clone)]
struct LineJointGizmoPipelineKey { struct LineJointGizmoPipelineKey {
view_key: MeshPipelineKey, msaa: Msaa,
hdr: bool,
perspective: bool, perspective: bool,
joints: GizmoLineJoint, joints: GizmoLineJoint,
} }
@ -205,18 +199,13 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
shader_defs.push("PERSPECTIVE".into()); shader_defs.push("PERSPECTIVE".into());
} }
let format = if key.view_key.contains(MeshPipelineKey::HDR) { let format = if key.hdr {
ViewTarget::TEXTURE_FORMAT_HDR ViewTarget::TEXTURE_FORMAT_HDR
} else { } else {
TextureFormat::bevy_default() TextureFormat::bevy_default()
}; };
let view_layout = self let layout = vec![self.view_layout.clone(), self.uniform_layout.clone()];
.mesh_pipeline
.get_view_layout(key.view_key.into())
.clone();
let layout = vec![view_layout, self.uniform_layout.clone()];
if key.joints == GizmoLineJoint::None { if key.joints == GizmoLineJoint::None {
error!("There is no entry point for line joints with GizmoLineJoints::None. Please consider aborting the drawing process before reaching this stage."); error!("There is no entry point for line joints with GizmoLineJoints::None. Please consider aborting the drawing process before reaching this stage.");
@ -255,7 +244,7 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
bias: DepthBiasState::default(), bias: DepthBiasState::default(),
}), }),
multisample: MultisampleState { multisample: MultisampleState {
count: key.view_key.msaa_samples(), count: key.msaa.samples(),
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
@ -268,19 +257,19 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
type DrawLineGizmo3d = ( type DrawLineGizmo3d = (
SetItemPipeline, SetItemPipeline,
SetMeshViewBindGroup<0>, SetViewBindGroup<0>,
SetLineGizmoBindGroup<1>, SetLineGizmoBindGroup<1>,
DrawLineGizmo<false>, DrawLineGizmo<false>,
); );
type DrawLineGizmo3dStrip = ( type DrawLineGizmo3dStrip = (
SetItemPipeline, SetItemPipeline,
SetMeshViewBindGroup<0>, SetViewBindGroup<0>,
SetLineGizmoBindGroup<1>, SetLineGizmoBindGroup<1>,
DrawLineGizmo<true>, DrawLineGizmo<true>,
); );
type DrawLineJointGizmo3d = ( type DrawLineJointGizmo3d = (
SetItemPipeline, SetItemPipeline,
SetMeshViewBindGroup<0>, SetViewBindGroup<0>,
SetLineGizmoBindGroup<1>, SetLineGizmoBindGroup<1>,
DrawLineJointGizmo, DrawLineJointGizmo,
); );
@ -293,18 +282,7 @@ fn queue_line_gizmos_3d(
line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>, line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>, mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
views: Query<( views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>,
&ExtractedView,
&Msaa,
Option<&RenderLayers>,
(
Has<NormalPrepass>,
Has<DepthPrepass>,
Has<MotionVectorPrepass>,
Has<DeferredPrepass>,
Has<OrderIndependentTransparencySettings>,
),
)>,
) { ) {
let draw_function = draw_functions.read().get_id::<DrawLineGizmo3d>().unwrap(); let draw_function = draw_functions.read().get_id::<DrawLineGizmo3d>().unwrap();
let draw_function_strip = draw_functions let draw_function_strip = draw_functions
@ -312,13 +290,7 @@ fn queue_line_gizmos_3d(
.get_id::<DrawLineGizmo3dStrip>() .get_id::<DrawLineGizmo3dStrip>()
.unwrap(); .unwrap();
for ( for (view, msaa, render_layers) in &views {
view,
msaa,
render_layers,
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass, oit),
) in &views
{
let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)
else { else {
continue; continue;
@ -326,29 +298,6 @@ fn queue_line_gizmos_3d(
let render_layers = render_layers.unwrap_or_default(); let render_layers = render_layers.unwrap_or_default();
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
| MeshPipelineKey::from_hdr(view.hdr);
if normal_prepass {
view_key |= MeshPipelineKey::NORMAL_PREPASS;
}
if depth_prepass {
view_key |= MeshPipelineKey::DEPTH_PREPASS;
}
if motion_vector_prepass {
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
}
if deferred_prepass {
view_key |= MeshPipelineKey::DEFERRED_PREPASS;
}
if oit {
view_key |= MeshPipelineKey::OIT_ENABLED;
}
for (entity, main_entity, config) in &line_gizmos { for (entity, main_entity, config) in &line_gizmos {
if !config.render_layers.intersects(render_layers) { if !config.render_layers.intersects(render_layers) {
continue; continue;
@ -363,7 +312,8 @@ fn queue_line_gizmos_3d(
&pipeline_cache, &pipeline_cache,
&pipeline, &pipeline,
LineGizmoPipelineKey { LineGizmoPipelineKey {
view_key, msaa: *msaa,
hdr: view.hdr,
strip: false, strip: false,
perspective: config.line_perspective, perspective: config.line_perspective,
line_style: config.line_style, line_style: config.line_style,
@ -385,7 +335,8 @@ fn queue_line_gizmos_3d(
&pipeline_cache, &pipeline_cache,
&pipeline, &pipeline,
LineGizmoPipelineKey { LineGizmoPipelineKey {
view_key, msaa: *msaa,
hdr: view.hdr,
strip: true, strip: true,
perspective: config.line_perspective, perspective: config.line_perspective,
line_style: config.line_style, line_style: config.line_style,
@ -413,30 +364,14 @@ fn queue_line_joint_gizmos_3d(
line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>, line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>, mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
views: Query<( mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>,
&ExtractedView,
&Msaa,
Option<&RenderLayers>,
(
Has<NormalPrepass>,
Has<DepthPrepass>,
Has<MotionVectorPrepass>,
Has<DeferredPrepass>,
),
)>,
) { ) {
let draw_function = draw_functions let draw_function = draw_functions
.read() .read()
.get_id::<DrawLineJointGizmo3d>() .get_id::<DrawLineJointGizmo3d>()
.unwrap(); .unwrap();
for ( for (view, msaa, render_layers) in &mut views {
view,
msaa,
render_layers,
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
) in &views
{
let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)
else { else {
continue; continue;
@ -444,25 +379,6 @@ fn queue_line_joint_gizmos_3d(
let render_layers = render_layers.unwrap_or_default(); let render_layers = render_layers.unwrap_or_default();
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
| MeshPipelineKey::from_hdr(view.hdr);
if normal_prepass {
view_key |= MeshPipelineKey::NORMAL_PREPASS;
}
if depth_prepass {
view_key |= MeshPipelineKey::DEPTH_PREPASS;
}
if motion_vector_prepass {
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
}
if deferred_prepass {
view_key |= MeshPipelineKey::DEFERRED_PREPASS;
}
for (entity, main_entity, config) in &line_gizmos { for (entity, main_entity, config) in &line_gizmos {
if !config.render_layers.intersects(render_layers) { if !config.render_layers.intersects(render_layers) {
continue; continue;
@ -480,7 +396,8 @@ fn queue_line_joint_gizmos_3d(
&pipeline_cache, &pipeline_cache,
&pipeline, &pipeline,
LineJointGizmoPipelineKey { LineJointGizmoPipelineKey {
view_key, msaa: *msaa,
hdr: view.hdr,
perspective: config.line_perspective, perspective: config.line_perspective,
joints: config.line_joints, joints: config.line_joints,
}, },

View File

@ -143,7 +143,6 @@ pub(crate) fn extract_linegizmos(
#[cfg(feature = "webgl")] #[cfg(feature = "webgl")]
_padding: Default::default(), _padding: Default::default(),
}, },
#[cfg(any(feature = "bevy_pbr", feature = "bevy_sprite"))]
crate::config::GizmoMeshConfig { crate::config::GizmoMeshConfig {
line_perspective: gizmo.line_config.perspective, line_perspective: gizmo.line_config.perspective,
line_style: gizmo.line_config.style, line_style: gizmo.line_config.style,

View File

@ -0,0 +1,90 @@
use bevy_ecs::{
component::Component,
entity::Entity,
query::With,
resource::Resource,
system::{lifetimeless::Read, Commands, Query, Res},
world::FromWorld,
};
use bevy_render::{
render_phase::{PhaseItem, RenderCommand, RenderCommandResult},
render_resource::{
binding_types::uniform_buffer, BindGroup, BindGroupLayout, BindGroupLayoutEntry,
DynamicBindGroupEntries, DynamicBindGroupLayoutEntries, ShaderStages,
},
renderer::RenderDevice,
view::{ExtractedView, ViewUniform, ViewUniforms},
};
#[derive(Component)]
pub(crate) struct ViewBindGroup(BindGroup);
/// very common layout: just the view uniform
#[derive(Resource)]
pub(crate) struct OnlyViewLayout(pub BindGroupLayout);
impl FromWorld for OnlyViewLayout {
fn from_world(world: &mut bevy_ecs::world::World) -> Self {
let render_device = world.resource::<RenderDevice>();
let view_layout =
render_device.create_bind_group_layout("mesh_view_layout", &view_layout_entries());
Self(view_layout)
}
}
pub(crate) struct SetViewBindGroup<const I: usize>;
impl<const I: usize, P: PhaseItem> RenderCommand<P> for SetViewBindGroup<I> {
type Param = ();
type ViewQuery = Read<ViewBindGroup>;
type ItemQuery = ();
fn render<'w>(
_: &P,
view: bevy_ecs::query::ROQueryItem<'w, Self::ViewQuery>,
_: Option<bevy_ecs::query::ROQueryItem<'w, Self::ItemQuery>>,
_: bevy_ecs::system::SystemParamItem<'w, '_, Self::Param>,
pass: &mut bevy_render::render_phase::TrackedRenderPass<'w>,
) -> RenderCommandResult {
pass.set_bind_group(I, &view.0, &[0]);
RenderCommandResult::Success
}
}
pub(crate) fn prepare_view_bind_groups(
mut commands: Commands,
render_device: Res<RenderDevice>,
view_uniforms: Res<ViewUniforms>,
layout: Res<OnlyViewLayout>,
views: Query<Entity, With<ExtractedView>>,
) {
let Some(view_binding) = view_uniforms.uniforms.binding() else {
return;
};
let entries = DynamicBindGroupEntries::new_with_indices(((0, view_binding.clone()),));
for entity in &views {
commands
.entity(entity)
.insert(ViewBindGroup(render_device.create_bind_group(
"view_bind_group",
&layout.0,
&entries,
)));
}
}
pub(crate) fn view_layout_entries() -> Vec<BindGroupLayoutEntry> {
DynamicBindGroupLayoutEntries::new_with_indices(
ShaderStages::FRAGMENT,
((
0,
uniform_buffer::<ViewUniform>(true).visibility(ShaderStages::VERTEX_FRAGMENT),
),),
)
.to_vec()
}

View File

@ -172,7 +172,7 @@ bevy_ci_testing = ["bevy_dev_tools/bevy_ci_testing", "bevy_render?/ci_limits"]
# Enable animation support, and glTF animation loading # Enable animation support, and glTF animation loading
animation = ["bevy_animation", "bevy_gltf?/bevy_animation"] animation = ["bevy_animation", "bevy_gltf?/bevy_animation"]
bevy_sprite = ["dep:bevy_sprite", "bevy_gizmos?/bevy_sprite", "bevy_image"] bevy_sprite = ["dep:bevy_sprite", "bevy_gizmos", "bevy_image"]
bevy_pbr = ["dep:bevy_pbr", "bevy_gizmos?/bevy_pbr", "bevy_image"] bevy_pbr = ["dep:bevy_pbr", "bevy_gizmos?/bevy_pbr", "bevy_image"]
bevy_window = ["dep:bevy_window", "dep:bevy_a11y"] bevy_window = ["dep:bevy_window", "dep:bevy_a11y"]
bevy_core_pipeline = ["dep:bevy_core_pipeline", "bevy_image"] bevy_core_pipeline = ["dep:bevy_core_pipeline", "bevy_image"]