Path: blob/main/crates/bevy_gizmos_render/src/pipeline_2d.rs
9416 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,70queue_line_and_joint_gizmos_2d71.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(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]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(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]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_and_joint_gizmos_2d(290draw_functions: Res<DrawFunctions<Transparent2d>>,291line_gizmo_pipeline: Res<LineGizmoPipeline>,292line_joint_gizmo_pipeline: Res<LineJointGizmoPipeline>,293mut line_gizmo_pipelines: ResMut<SpecializedRenderPipelines<LineGizmoPipeline>>,294mut line_joint_gizmo_pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>,295pipeline_cache: Res<PipelineCache>,296line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,297line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,298mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,299mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>,300) {301let draw_function = draw_functions.read().get_id::<DrawLineGizmo2d>().unwrap();302let draw_line_function_strip = draw_functions303.read()304.get_id::<DrawLineGizmo2dStrip>()305.unwrap();306let draw_line_joint_function = draw_functions307.read()308.get_id::<DrawLineJointGizmo2d>()309.unwrap();310311for (view, msaa, render_layers) in &mut views {312let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)313else {314continue;315};316317let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())318| Mesh2dPipelineKey::from_hdr(view.hdr);319320let render_layers = render_layers.unwrap_or_default();321for (entity, main_entity, config) in &line_gizmos {322if !config.render_layers.intersects(render_layers) {323continue;324}325326let Some(line_gizmo) = line_gizmo_assets.get(&config.handle) else {327continue;328};329330// Draw lines331if line_gizmo.list_vertex_count > 0 {332let pipeline = line_gizmo_pipelines.specialize(333&pipeline_cache,334&line_gizmo_pipeline,335LineGizmoPipelineKey {336mesh_key,337strip: false,338line_style: config.line_style,339},340);341transparent_phase.add(Transparent2d {342entity: (entity, *main_entity),343draw_function,344pipeline,345sort_key: FloatOrd(f32::INFINITY),346batch_range: 0..1,347extra_index: PhaseItemExtraIndex::None,348extracted_index: usize::MAX,349indexed: false,350});351}352353if line_gizmo.strip_vertex_count >= 2 {354let pipeline = line_gizmo_pipelines.specialize(355&pipeline_cache,356&line_gizmo_pipeline,357LineGizmoPipelineKey {358mesh_key,359strip: true,360line_style: config.line_style,361},362);363transparent_phase.add(Transparent2d {364entity: (entity, *main_entity),365draw_function: draw_line_function_strip,366pipeline,367sort_key: FloatOrd(f32::INFINITY),368batch_range: 0..1,369extra_index: PhaseItemExtraIndex::None,370extracted_index: usize::MAX,371indexed: false,372});373}374375// Draw line joints376if line_gizmo.strip_vertex_count < 3 || config.line_joints == GizmoLineJoint::None {377continue;378}379380let pipeline = line_joint_gizmo_pipelines.specialize(381&pipeline_cache,382&line_joint_gizmo_pipeline,383LineJointGizmoPipelineKey {384mesh_key,385joints: config.line_joints,386},387);388transparent_phase.add(Transparent2d {389entity: (entity, *main_entity),390draw_function: draw_line_joint_function,391pipeline,392sort_key: FloatOrd(f32::INFINITY),393batch_range: 0..1,394extra_index: PhaseItemExtraIndex::None,395extracted_index: usize::MAX,396indexed: false,397});398}399}400}401402403