Path: blob/main/crates/bevy_gizmos_render/src/pipeline_2d.rs
7228 views
use crate::{1init_line_gizmo_uniform_bind_group_layout, line_gizmo_vertex_buffer_layouts,2line_joint_gizmo_vertex_buffer_layouts, DrawLineGizmo, DrawLineJointGizmo, GizmoRenderSystems,3GpuLineGizmo, LineGizmoUniformBindgroupLayout, SetLineGizmoBindGroup,4};5use bevy_app::{App, Plugin};6use bevy_asset::{load_embedded_asset, AssetServer, Handle};7use bevy_camera::visibility::RenderLayers;8use bevy_core_pipeline::core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT};9use bevy_gizmos::config::{GizmoLineJoint, GizmoLineStyle, GizmoMeshConfig};1011use bevy_ecs::{12prelude::Entity,13resource::Resource,14schedule::IntoScheduleConfigs,15system::{Commands, Query, Res, ResMut},16};17use bevy_image::BevyDefault as _;18use bevy_math::FloatOrd;19use bevy_render::{20render_asset::{prepare_assets, RenderAssets},21render_phase::{22AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline,23ViewSortedRenderPhases,24},25render_resource::*,26view::{ExtractedView, Msaa, ViewTarget},27Render, RenderApp, RenderSystems,28};29use bevy_render::{sync_world::MainEntity, RenderStartup};30use bevy_shader::Shader;31use bevy_sprite_render::{32init_mesh_2d_pipeline, Mesh2dPipeline, Mesh2dPipelineKey, SetMesh2dViewBindGroup,33};34use bevy_utils::default;35use tracing::error;3637pub struct LineGizmo2dPlugin;3839impl Plugin for LineGizmo2dPlugin {40fn build(&self, app: &mut App) {41let Some(render_app) = app.get_sub_app_mut(RenderApp) else {42return;43};4445render_app46.add_render_command::<Transparent2d, DrawLineGizmo2d>()47.add_render_command::<Transparent2d, DrawLineGizmo2dStrip>()48.add_render_command::<Transparent2d, DrawLineJointGizmo2d>()49.init_resource::<SpecializedRenderPipelines<LineGizmoPipeline>>()50.init_resource::<SpecializedRenderPipelines<LineJointGizmoPipeline>>()51.configure_sets(52Render,53GizmoRenderSystems::QueueLineGizmos2d54.in_set(RenderSystems::Queue)55.ambiguous_with(bevy_sprite_render::queue_sprites)56.ambiguous_with(57bevy_sprite_render::queue_material2d_meshes::<58bevy_sprite_render::ColorMaterial,59>,60),61)62.add_systems(63RenderStartup,64init_line_gizmo_pipelines65.after(init_line_gizmo_uniform_bind_group_layout)66.after(init_mesh_2d_pipeline),67)68.add_systems(69Render,70(queue_line_gizmos_2d, queue_line_joint_gizmos_2d)71.in_set(GizmoRenderSystems::QueueLineGizmos2d)72.after(prepare_assets::<GpuLineGizmo>),73);74}75}7677#[derive(Clone, Resource)]78struct LineGizmoPipeline {79mesh_pipeline: Mesh2dPipeline,80uniform_layout: BindGroupLayoutDescriptor,81shader: Handle<Shader>,82}8384fn init_line_gizmo_pipelines(85mut commands: Commands,86mesh_2d_pipeline: Res<Mesh2dPipeline>,87uniform_bind_group_layout: Res<LineGizmoUniformBindgroupLayout>,88asset_server: Res<AssetServer>,89) {90commands.insert_resource(LineGizmoPipeline {91mesh_pipeline: mesh_2d_pipeline.clone(),92uniform_layout: uniform_bind_group_layout.layout.clone(),93shader: load_embedded_asset!(asset_server.as_ref(), "lines.wgsl"),94});95commands.insert_resource(LineJointGizmoPipeline {96mesh_pipeline: mesh_2d_pipeline.clone(),97uniform_layout: uniform_bind_group_layout.layout.clone(),98shader: load_embedded_asset!(asset_server.as_ref(), "line_joints.wgsl"),99});100}101102#[derive(PartialEq, Eq, Hash, Clone)]103struct LineGizmoPipelineKey {104mesh_key: Mesh2dPipelineKey,105strip: bool,106line_style: GizmoLineStyle,107}108109impl SpecializedRenderPipeline for LineGizmoPipeline {110type Key = LineGizmoPipelineKey;111112fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {113let format = if key.mesh_key.contains(Mesh2dPipelineKey::HDR) {114ViewTarget::TEXTURE_FORMAT_HDR115} else {116TextureFormat::bevy_default()117};118119let shader_defs = vec![120#[cfg(feature = "webgl")]121"SIXTEEN_BYTE_ALIGNMENT".into(),122];123124let layout = vec![125self.mesh_pipeline.view_layout.clone(),126self.uniform_layout.clone(),127];128129let fragment_entry_point = match key.line_style {130GizmoLineStyle::Solid => "fragment_solid",131GizmoLineStyle::Dotted => "fragment_dotted",132GizmoLineStyle::Dashed { .. } => "fragment_dashed",133_ => unimplemented!(),134};135136RenderPipelineDescriptor {137vertex: VertexState {138shader: self.shader.clone(),139shader_defs: shader_defs.clone(),140buffers: line_gizmo_vertex_buffer_layouts(key.strip),141..default()142},143fragment: Some(FragmentState {144shader: self.shader.clone(),145shader_defs,146entry_point: Some(fragment_entry_point.into()),147targets: vec![Some(ColorTargetState {148format,149blend: Some(BlendState::ALPHA_BLENDING),150write_mask: ColorWrites::ALL,151})],152}),153layout,154depth_stencil: Some(DepthStencilState {155format: CORE_2D_DEPTH_FORMAT,156depth_write_enabled: false,157depth_compare: CompareFunction::Always,158stencil: StencilState {159front: StencilFaceState::IGNORE,160back: StencilFaceState::IGNORE,161read_mask: 0,162write_mask: 0,163},164bias: DepthBiasState {165constant: 0,166slope_scale: 0.0,167clamp: 0.0,168},169}),170multisample: MultisampleState {171count: key.mesh_key.msaa_samples(),172mask: !0,173alpha_to_coverage_enabled: false,174},175label: Some("LineGizmo Pipeline 2D".into()),176..default()177}178}179}180181#[derive(Clone, Resource)]182struct LineJointGizmoPipeline {183mesh_pipeline: Mesh2dPipeline,184uniform_layout: BindGroupLayoutDescriptor,185shader: Handle<Shader>,186}187188#[derive(PartialEq, Eq, Hash, Clone)]189struct LineJointGizmoPipelineKey {190mesh_key: Mesh2dPipelineKey,191joints: GizmoLineJoint,192}193194impl SpecializedRenderPipeline for LineJointGizmoPipeline {195type Key = LineJointGizmoPipelineKey;196197fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {198let format = if key.mesh_key.contains(Mesh2dPipelineKey::HDR) {199ViewTarget::TEXTURE_FORMAT_HDR200} else {201TextureFormat::bevy_default()202};203204let shader_defs = vec![205#[cfg(feature = "webgl")]206"SIXTEEN_BYTE_ALIGNMENT".into(),207];208209let layout = vec![210self.mesh_pipeline.view_layout.clone(),211self.uniform_layout.clone(),212];213214if key.joints == GizmoLineJoint::None {215error!("There is no entry point for line joints with GizmoLineJoints::None. Please consider aborting the drawing process before reaching this stage.");216};217218let entry_point = match key.joints {219GizmoLineJoint::Miter => "vertex_miter",220GizmoLineJoint::Round(_) => "vertex_round",221GizmoLineJoint::None | GizmoLineJoint::Bevel => "vertex_bevel",222};223224RenderPipelineDescriptor {225vertex: VertexState {226shader: self.shader.clone(),227entry_point: Some(entry_point.into()),228shader_defs: shader_defs.clone(),229buffers: line_joint_gizmo_vertex_buffer_layouts(),230},231fragment: Some(FragmentState {232shader: self.shader.clone(),233shader_defs,234targets: vec![Some(ColorTargetState {235format,236blend: Some(BlendState::ALPHA_BLENDING),237write_mask: ColorWrites::ALL,238})],239..default()240}),241layout,242primitive: PrimitiveState::default(),243depth_stencil: Some(DepthStencilState {244format: CORE_2D_DEPTH_FORMAT,245depth_write_enabled: false,246depth_compare: CompareFunction::Always,247stencil: StencilState {248front: StencilFaceState::IGNORE,249back: StencilFaceState::IGNORE,250read_mask: 0,251write_mask: 0,252},253bias: DepthBiasState {254constant: 0,255slope_scale: 0.0,256clamp: 0.0,257},258}),259multisample: MultisampleState {260count: key.mesh_key.msaa_samples(),261mask: !0,262alpha_to_coverage_enabled: false,263},264label: Some("LineJointGizmo Pipeline 2D".into()),265..default()266}267}268}269270type DrawLineGizmo2d = (271SetItemPipeline,272SetMesh2dViewBindGroup<0>,273SetLineGizmoBindGroup<1>,274DrawLineGizmo<false>,275);276type DrawLineGizmo2dStrip = (277SetItemPipeline,278SetMesh2dViewBindGroup<0>,279SetLineGizmoBindGroup<1>,280DrawLineGizmo<true>,281);282type DrawLineJointGizmo2d = (283SetItemPipeline,284SetMesh2dViewBindGroup<0>,285SetLineGizmoBindGroup<1>,286DrawLineJointGizmo,287);288289fn queue_line_gizmos_2d(290draw_functions: Res<DrawFunctions<Transparent2d>>,291pipeline: Res<LineGizmoPipeline>,292mut pipelines: ResMut<SpecializedRenderPipelines<LineGizmoPipeline>>,293pipeline_cache: Res<PipelineCache>,294line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,295line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,296mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,297mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>,298) {299let draw_function = draw_functions.read().get_id::<DrawLineGizmo2d>().unwrap();300let draw_function_strip = draw_functions301.read()302.get_id::<DrawLineGizmo2dStrip>()303.unwrap();304305for (view, msaa, render_layers) in &mut views {306let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)307else {308continue;309};310311let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())312| Mesh2dPipelineKey::from_hdr(view.hdr);313314let render_layers = render_layers.unwrap_or_default();315for (entity, main_entity, config) in &line_gizmos {316if !config.render_layers.intersects(render_layers) {317continue;318}319320let Some(line_gizmo) = line_gizmo_assets.get(&config.handle) else {321continue;322};323324if line_gizmo.list_vertex_count > 0 {325let pipeline = pipelines.specialize(326&pipeline_cache,327&pipeline,328LineGizmoPipelineKey {329mesh_key,330strip: false,331line_style: config.line_style,332},333);334transparent_phase.add(Transparent2d {335entity: (entity, *main_entity),336draw_function,337pipeline,338sort_key: FloatOrd(f32::INFINITY),339batch_range: 0..1,340extra_index: PhaseItemExtraIndex::None,341extracted_index: usize::MAX,342indexed: false,343});344}345346if line_gizmo.strip_vertex_count >= 2 {347let pipeline = pipelines.specialize(348&pipeline_cache,349&pipeline,350LineGizmoPipelineKey {351mesh_key,352strip: true,353line_style: config.line_style,354},355);356transparent_phase.add(Transparent2d {357entity: (entity, *main_entity),358draw_function: draw_function_strip,359pipeline,360sort_key: FloatOrd(f32::INFINITY),361batch_range: 0..1,362extra_index: PhaseItemExtraIndex::None,363extracted_index: usize::MAX,364indexed: false,365});366}367}368}369}370fn queue_line_joint_gizmos_2d(371draw_functions: Res<DrawFunctions<Transparent2d>>,372pipeline: Res<LineJointGizmoPipeline>,373mut pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>,374pipeline_cache: Res<PipelineCache>,375line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,376line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,377mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,378mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>,379) {380let draw_function = draw_functions381.read()382.get_id::<DrawLineJointGizmo2d>()383.unwrap();384385for (view, msaa, render_layers) in &mut views {386let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)387else {388continue;389};390391let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())392| Mesh2dPipelineKey::from_hdr(view.hdr);393394let render_layers = render_layers.unwrap_or_default();395for (entity, main_entity, config) in &line_gizmos {396if !config.render_layers.intersects(render_layers) {397continue;398}399400let Some(line_gizmo) = line_gizmo_assets.get(&config.handle) else {401continue;402};403404if line_gizmo.strip_vertex_count < 3 || config.line_joints == GizmoLineJoint::None {405continue;406}407408let pipeline = pipelines.specialize(409&pipeline_cache,410&pipeline,411LineJointGizmoPipelineKey {412mesh_key,413joints: config.line_joints,414},415);416transparent_phase.add(Transparent2d {417entity: (entity, *main_entity),418draw_function,419pipeline,420sort_key: FloatOrd(f32::INFINITY),421batch_range: 0..1,422extra_index: PhaseItemExtraIndex::None,423extracted_index: usize::MAX,424indexed: false,425});426}427}428}429430431