use crate::{
init_mesh_2d_pipeline, DrawMesh2d, Mesh2dPipeline, Mesh2dPipelineKey, RenderMesh2dInstances,
SetMesh2dBindGroup, SetMesh2dViewBindGroup, ViewKeyCache,
};
use bevy_app::{App, Plugin, PostUpdate, Startup, Update};
use bevy_asset::{
embedded_asset, load_embedded_asset, prelude::AssetChanged, AsAssetId, Asset, AssetApp,
AssetEventSystems, AssetId, AssetServer, Assets, Handle, UntypedAssetId,
};
use bevy_camera::{visibility::ViewVisibility, Camera, Camera2d};
use bevy_color::{Color, ColorToComponents};
use bevy_core_pipeline::schedule::{Core2d, Core2dSystems};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
change_detection::Tick,
prelude::*,
system::{lifetimeless::SRes, SystemChangeTick, SystemParamItem},
};
use bevy_mesh::{Mesh2d, MeshVertexBufferLayoutRef};
use bevy_platform::{
collections::{HashMap, HashSet},
hash::FixedHasher,
};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
batching::gpu_preprocessing::GpuPreprocessingMode,
camera::{
extract_cameras, DirtySpecializationSystems, DirtyWireframeSpecializations,
ExtractedCamera, PendingQueues,
},
extract_resource::ExtractResource,
mesh::{
allocator::{MeshAllocator, MeshSlabId, MeshSlabs},
RenderMesh,
},
prelude::*,
render_asset::{
prepare_assets, PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets,
},
render_phase::{
AddRenderCommand, BinnedPhaseItem, BinnedRenderPhasePlugin, BinnedRenderPhaseType,
CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, InputUniformIndex, PhaseItem,
PhaseItemBatchSetKey, PhaseItemExtraIndex, RenderCommand, RenderCommandResult,
SetItemPipeline, TrackedRenderPass, ViewBinnedRenderPhases,
},
render_resource::*,
renderer::{RenderContext, ViewQuery},
sync_world::{MainEntity, MainEntityHashMap},
view::{
ExtractedView, RenderVisibleEntities, RetainedViewEntity, ViewDepthTexture, ViewTarget,
},
Extract, GpuResourceAppExt, Render, RenderApp, RenderDebugFlags, RenderStartup, RenderSystems,
};
use bevy_shader::Shader;
use core::{hash::Hash, ops::Range};
use tracing::error;
#[derive(Debug, Default)]
pub struct Wireframe2dPlugin {
pub debug_flags: RenderDebugFlags,
}
impl Wireframe2dPlugin {
pub fn new(debug_flags: RenderDebugFlags) -> Self {
Self { debug_flags }
}
}
impl Plugin for Wireframe2dPlugin {
fn build(&self, app: &mut App) {
embedded_asset!(app, "wireframe2d.wgsl");
app.add_plugins((
BinnedRenderPhasePlugin::<Wireframe2dPhaseItem, Mesh2dPipeline>::new(self.debug_flags),
RenderAssetPlugin::<RenderWireframeMaterial>::default(),
))
.init_asset::<Wireframe2dMaterial>()
.init_resource::<SpecializedMeshPipelines<Wireframe2dPipeline>>()
.init_resource::<Wireframe2dConfig>()
.init_resource::<WireframeEntitiesNeedingSpecialization>()
.add_systems(Startup, setup_global_wireframe_material)
.add_systems(
Update,
(
global_color_changed.run_if(resource_changed::<Wireframe2dConfig>),
wireframe_color_changed,
(apply_wireframe_material, apply_global_wireframe_material).chain(),
),
)
.add_systems(
PostUpdate,
check_wireframe_entities_needing_specialization
.after(AssetEventSystems)
.run_if(resource_exists::<Wireframe2dConfig>),
);
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_gpu_resource::<SpecializedWireframePipelineCache>()
.init_resource::<DrawFunctions<Wireframe2dPhaseItem>>()
.add_render_command::<Wireframe2dPhaseItem, DrawWireframe2d>()
.init_resource::<RenderWireframeInstances>()
.init_gpu_resource::<SpecializedMeshPipelines<Wireframe2dPipeline>>()
.init_resource::<PendingWireframe2dQueues>()
.add_systems(
Core2d,
wireframe_2d
.after(Core2dSystems::MainPass)
.before(Core2dSystems::PostProcess),
)
.add_systems(
RenderStartup,
init_wireframe_2d_pipeline.after(init_mesh_2d_pipeline),
)
.add_systems(
ExtractSchedule,
(
extract_wireframe_2d_camera,
extract_wireframe_materials,
extract_wireframe_2d_entities_needing_specialization
.after(extract_cameras)
.in_set(DirtySpecializationSystems::CheckForChanges),
extract_wireframe_2d_entities_that_need_specializations_removed
.after(extract_cameras)
.in_set(DirtySpecializationSystems::CheckForRemovals),
),
)
.add_systems(
Render,
(
specialize_wireframes
.in_set(RenderSystems::PrepareMeshes)
.after(prepare_assets::<RenderWireframeMaterial>)
.after(prepare_assets::<RenderMesh>),
queue_wireframes
.in_set(RenderSystems::QueueMeshes)
.after(prepare_assets::<RenderWireframeMaterial>),
),
);
}
}
#[derive(Component, Debug, Clone, Default, Reflect, Eq, PartialEq)]
#[reflect(Component, Default, Debug, PartialEq)]
pub struct Wireframe2d;
pub struct Wireframe2dPhaseItem {
pub batch_set_key: Wireframe2dBatchSetKey,
pub bin_key: Wireframe2dBinKey,
pub representative_entity: (Entity, MainEntity),
pub batch_range: Range<u32>,
pub extra_index: PhaseItemExtraIndex,
}
impl PhaseItem for Wireframe2dPhaseItem {
fn entity(&self) -> Entity {
self.representative_entity.0
}
fn main_entity(&self) -> MainEntity {
self.representative_entity.1
}
fn draw_function(&self) -> DrawFunctionId {
self.batch_set_key.draw_function
}
fn batch_range(&self) -> &Range<u32> {
&self.batch_range
}
fn batch_range_mut(&mut self) -> &mut Range<u32> {
&mut self.batch_range
}
fn extra_index(&self) -> PhaseItemExtraIndex {
self.extra_index.clone()
}
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
(&mut self.batch_range, &mut self.extra_index)
}
}
impl CachedRenderPipelinePhaseItem for Wireframe2dPhaseItem {
fn cached_pipeline(&self) -> CachedRenderPipelineId {
self.batch_set_key.pipeline
}
}
impl BinnedPhaseItem for Wireframe2dPhaseItem {
type BinKey = Wireframe2dBinKey;
type BatchSetKey = Wireframe2dBatchSetKey;
fn new(
batch_set_key: Self::BatchSetKey,
bin_key: Self::BinKey,
representative_entity: (Entity, MainEntity),
batch_range: Range<u32>,
extra_index: PhaseItemExtraIndex,
) -> Self {
Self {
batch_set_key,
bin_key,
representative_entity,
batch_range,
extra_index,
}
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Wireframe2dBatchSetKey {
pub pipeline: CachedRenderPipelineId,
pub asset_id: UntypedAssetId,
pub draw_function: DrawFunctionId,
pub vertex_slab: MeshSlabId,
pub index_slab: Option<MeshSlabId>,
}
impl PhaseItemBatchSetKey for Wireframe2dBatchSetKey {
fn indexed(&self) -> bool {
self.index_slab.is_some()
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Wireframe2dBinKey {
pub asset_id: UntypedAssetId,
}
pub struct SetWireframe2dImmediates;
impl<P: PhaseItem> RenderCommand<P> for SetWireframe2dImmediates {
type Param = (
SRes<RenderWireframeInstances>,
SRes<RenderAssets<RenderWireframeMaterial>>,
);
type ViewQuery = ();
type ItemQuery = ();
#[inline]
fn render<'w>(
item: &P,
_view: (),
_item_query: Option<()>,
(wireframe_instances, wireframe_assets): SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let Some(wireframe_material) = wireframe_instances.get(&item.main_entity()) else {
return RenderCommandResult::Failure("No wireframe material found for entity");
};
let Some(wireframe_material) = wireframe_assets.get(*wireframe_material) else {
return RenderCommandResult::Failure("No wireframe material found for entity");
};
pass.set_immediates(0, bytemuck::bytes_of(&wireframe_material.color));
RenderCommandResult::Success
}
}
pub type DrawWireframe2d = (
SetItemPipeline,
SetMesh2dViewBindGroup<0>,
SetMesh2dBindGroup<1>,
SetWireframe2dImmediates,
DrawMesh2d,
);
#[derive(Resource, Clone)]
pub struct Wireframe2dPipeline {
mesh_pipeline: Mesh2dPipeline,
shader: Handle<Shader>,
}
pub fn init_wireframe_2d_pipeline(
mut commands: Commands,
mesh_2d_pipeline: Res<Mesh2dPipeline>,
asset_server: Res<AssetServer>,
) {
commands.insert_resource(Wireframe2dPipeline {
mesh_pipeline: mesh_2d_pipeline.clone(),
shader: load_embedded_asset!(asset_server.as_ref(), "wireframe2d.wgsl"),
});
}
impl SpecializedMeshPipeline for Wireframe2dPipeline {
type Key = Mesh2dPipelineKey;
fn specialize(
&self,
key: Self::Key,
layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
descriptor.label = Some("wireframe_2d_pipeline".into());
descriptor.immediate_size = 16;
let fragment = descriptor.fragment.as_mut().unwrap();
fragment.shader = self.shader.clone();
descriptor.primitive.polygon_mode = PolygonMode::Line;
if descriptor.primitive.topology.is_triangles() {
descriptor.depth_stencil.as_mut().unwrap().bias.slope_scale = 1.0;
}
Ok(descriptor)
}
}
pub(crate) fn wireframe_2d(
world: &World,
view: ViewQuery<(
&ExtractedCamera,
&ExtractedView,
&ViewTarget,
&ViewDepthTexture,
)>,
wireframe_phases: Res<ViewBinnedRenderPhases<Wireframe2dPhaseItem>>,
mut ctx: RenderContext,
) {
let view_entity = view.entity();
let (camera, extracted_view, target, depth) = view.into_inner();
let Some(wireframe_phase) = wireframe_phases.get(&extracted_view.retained_view_entity) else {
return;
};
if wireframe_phase.is_empty() {
return;
}
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("wireframe_2d"),
color_attachments: &[Some(target.get_color_attachment())],
depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(viewport);
}
if let Err(err) = wireframe_phase.render(&mut render_pass, world, view_entity) {
error!("Error encountered while rendering the wireframe phase {err:?}");
}
}
#[derive(Component, Debug, Clone, Default, Reflect)]
#[reflect(Component, Default, Debug)]
pub struct Wireframe2dColor {
pub color: Color,
}
#[derive(Component, Debug, Clone, Default)]
pub struct ExtractedWireframeColor {
pub color: [f32; 4],
}
#[derive(Component, Debug, Clone, Default, Reflect, Eq, PartialEq)]
#[reflect(Component, Default, Debug, PartialEq)]
pub struct NoWireframe2d;
#[derive(Resource, Debug, Clone, Default, ExtractResource, Reflect)]
#[reflect(Resource, Debug, Default)]
pub struct Wireframe2dConfig {
pub global: bool,
pub default_color: Color,
}
#[derive(Asset, Reflect, Clone, Debug, Default)]
#[reflect(Clone, Default)]
pub struct Wireframe2dMaterial {
pub color: Color,
}
pub struct RenderWireframeMaterial {
pub color: [f32; 4],
}
#[derive(
Component, FromTemplate, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq,
)]
#[reflect(Component, Default, Clone, PartialEq)]
pub struct Mesh2dWireframe(pub Handle<Wireframe2dMaterial>);
impl AsAssetId for Mesh2dWireframe {
type Asset = Wireframe2dMaterial;
fn as_asset_id(&self) -> AssetId<Self::Asset> {
self.0.id()
}
}
impl RenderAsset for RenderWireframeMaterial {
type SourceAsset = Wireframe2dMaterial;
type Param = ();
fn prepare_asset(
source_asset: Self::SourceAsset,
_asset_id: AssetId<Self::SourceAsset>,
_param: &mut SystemParamItem<Self::Param>,
_previous_asset: Option<&Self>,
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
Ok(RenderWireframeMaterial {
color: source_asset.color.to_linear().to_f32_array(),
})
}
}
#[derive(Resource, Deref, DerefMut, Default)]
pub struct RenderWireframeInstances(MainEntityHashMap<AssetId<Wireframe2dMaterial>>);
#[derive(Clone, Resource, Debug, Default)]
pub struct WireframeEntitiesNeedingSpecialization {
pub changed: Vec<Entity>,
pub removed: Vec<Entity>,
}
#[derive(Resource, Deref, DerefMut, Default)]
pub struct SpecializedWireframePipelineCache {
#[deref]
map: HashMap<RetainedViewEntity, SpecializedWireframeViewPipelineCache>,
}
#[derive(Deref, DerefMut, Default)]
pub struct SpecializedWireframeViewPipelineCache {
#[deref]
map: MainEntityHashMap<(Tick, CachedRenderPipelineId)>,
}
#[derive(Resource)]
struct GlobalWireframeMaterial {
handle: Handle<Wireframe2dMaterial>,
}
pub fn extract_wireframe_materials(
mut material_instances: ResMut<RenderWireframeInstances>,
changed_meshes_query: Extract<
Query<
(Entity, &ViewVisibility, &Mesh2dWireframe),
Or<(Changed<ViewVisibility>, Changed<Mesh2dWireframe>)>,
>,
>,
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
mut removed_materials_query: Extract<RemovedComponents<Mesh2dWireframe>>,
) {
for (entity, view_visibility, material) in &changed_meshes_query {
if view_visibility.get() {
material_instances.insert(entity.into(), material.id());
} else {
material_instances.remove(&MainEntity::from(entity));
}
}
for entity in removed_visibilities_query
.read()
.chain(removed_materials_query.read())
{
if !changed_meshes_query.contains(entity) {
material_instances.remove(&MainEntity::from(entity));
}
}
}
fn setup_global_wireframe_material(
mut commands: Commands,
mut materials: ResMut<Assets<Wireframe2dMaterial>>,
config: Res<Wireframe2dConfig>,
) {
commands.insert_resource(GlobalWireframeMaterial {
handle: materials.add(Wireframe2dMaterial {
color: config.default_color,
}),
});
}
fn global_color_changed(
config: Res<Wireframe2dConfig>,
mut materials: ResMut<Assets<Wireframe2dMaterial>>,
global_material: Res<GlobalWireframeMaterial>,
) {
if let Some(mut global_material) = materials.get_mut(&global_material.handle) {
global_material.color = config.default_color;
}
}
fn wireframe_color_changed(
mut materials: ResMut<Assets<Wireframe2dMaterial>>,
mut colors_changed: Query<
(&mut Mesh2dWireframe, &Wireframe2dColor),
(With<Wireframe2d>, Changed<Wireframe2dColor>),
>,
) {
for (mut handle, wireframe_color) in &mut colors_changed {
handle.0 = materials.add(Wireframe2dMaterial {
color: wireframe_color.color,
});
}
}
fn apply_wireframe_material(
mut commands: Commands,
mut materials: ResMut<Assets<Wireframe2dMaterial>>,
wireframes: Query<
(Entity, Option<&Wireframe2dColor>),
(With<Wireframe2d>, Without<Mesh2dWireframe>),
>,
no_wireframes: Query<Entity, (With<NoWireframe2d>, With<Mesh2dWireframe>)>,
mut removed_wireframes: RemovedComponents<Wireframe2d>,
global_material: Res<GlobalWireframeMaterial>,
) {
for e in removed_wireframes.read().chain(no_wireframes.iter()) {
if let Ok(mut commands) = commands.get_entity(e) {
commands.remove::<Mesh2dWireframe>();
}
}
let mut material_to_spawn = vec![];
for (e, maybe_color) in &wireframes {
let material = get_wireframe_material(maybe_color, &mut materials, &global_material);
material_to_spawn.push((e, Mesh2dWireframe(material)));
}
commands.try_insert_batch(material_to_spawn);
}
type WireframeFilter = (With<Mesh2d>, Without<Wireframe2d>, Without<NoWireframe2d>);
fn apply_global_wireframe_material(
mut commands: Commands,
config: Res<Wireframe2dConfig>,
meshes_without_material: Query<
(Entity, Option<&Wireframe2dColor>),
(WireframeFilter, Without<Mesh2dWireframe>),
>,
meshes_with_global_material: Query<Entity, (WireframeFilter, With<Mesh2dWireframe>)>,
global_material: Res<GlobalWireframeMaterial>,
mut materials: ResMut<Assets<Wireframe2dMaterial>>,
) {
if config.global {
let mut material_to_spawn = vec![];
for (e, maybe_color) in &meshes_without_material {
let material = get_wireframe_material(maybe_color, &mut materials, &global_material);
material_to_spawn.push((e, Mesh2dWireframe(material)));
}
commands.try_insert_batch(material_to_spawn);
} else {
for e in &meshes_with_global_material {
commands.entity(e).remove::<Mesh2dWireframe>();
}
}
}
fn get_wireframe_material(
maybe_color: Option<&Wireframe2dColor>,
wireframe_materials: &mut Assets<Wireframe2dMaterial>,
global_material: &GlobalWireframeMaterial,
) -> Handle<Wireframe2dMaterial> {
if let Some(wireframe_color) = maybe_color {
wireframe_materials.add(Wireframe2dMaterial {
color: wireframe_color.color,
})
} else {
global_material.handle.clone()
}
}
fn extract_wireframe_2d_camera(
mut wireframe_2d_phases: ResMut<ViewBinnedRenderPhases<Wireframe2dPhaseItem>>,
cameras: Extract<Query<(Entity, &Camera), With<Camera2d>>>,
mut live_entities: Local<HashSet<RetainedViewEntity>>,
) {
live_entities.clear();
for (main_entity, camera) in &cameras {
if !camera.is_active {
continue;
}
let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);
wireframe_2d_phases.prepare_for_new_frame(retained_view_entity, GpuPreprocessingMode::None);
live_entities.insert(retained_view_entity);
}
wireframe_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
}
pub fn extract_wireframe_2d_entities_needing_specialization(
entities_needing_specialization: Extract<Res<WireframeEntitiesNeedingSpecialization>>,
mut dirty_specializations: ResMut<DirtyWireframeSpecializations>,
) {
for entity in entities_needing_specialization.changed.iter() {
dirty_specializations
.changed_renderables
.insert(MainEntity::from(*entity));
}
}
pub fn extract_wireframe_2d_entities_that_need_specializations_removed(
entities_needing_specialization: Extract<Res<WireframeEntitiesNeedingSpecialization>>,
mut dirty_specializations: ResMut<DirtyWireframeSpecializations>,
) {
for entity in entities_needing_specialization.removed.iter() {
dirty_specializations
.removed_renderables
.insert(MainEntity::from(*entity));
}
}
pub fn check_wireframe_entities_needing_specialization(
needs_specialization: Query<
Entity,
Or<(
Changed<Mesh2d>,
AssetChanged<Mesh2d>,
Changed<Mesh2dWireframe>,
AssetChanged<Mesh2dWireframe>,
)>,
>,
mut entities_needing_specialization: ResMut<WireframeEntitiesNeedingSpecialization>,
mut removed_mesh_2d_components: RemovedComponents<Mesh2d>,
mut removed_mesh_2d_wireframe_components: RemovedComponents<Mesh2dWireframe>,
) {
for entity in &needs_specialization {
entities_needing_specialization.changed.push(entity);
}
for entity in removed_mesh_2d_components
.read()
.chain(removed_mesh_2d_wireframe_components.read())
{
entities_needing_specialization.removed.push(entity);
}
}
#[derive(Default, Deref, DerefMut, Resource)]
pub struct PendingWireframe2dQueues(pub PendingQueues);
pub fn specialize_wireframes(
render_meshes: Res<RenderAssets<RenderMesh>>,
render_mesh_instances: Res<RenderMesh2dInstances>,
render_wireframe_instances: Res<RenderWireframeInstances>,
wireframe_phases: Res<ViewBinnedRenderPhases<Wireframe2dPhaseItem>>,
views: Query<(&ExtractedView, &RenderVisibleEntities)>,
view_key_cache: Res<ViewKeyCache>,
dirty_wireframe_specializations: Res<DirtyWireframeSpecializations>,
mut specialized_material_pipeline_cache: ResMut<SpecializedWireframePipelineCache>,
mut pending_wireframe2d_queues: ResMut<PendingWireframe2dQueues>,
mut pipelines: ResMut<SpecializedMeshPipelines<Wireframe2dPipeline>>,
pipeline: Res<Wireframe2dPipeline>,
pipeline_cache: Res<PipelineCache>,
ticks: SystemChangeTick,
) {
let mut all_views: HashSet<RetainedViewEntity, FixedHasher> = HashSet::default();
for (view, visible_entities) in &views {
all_views.insert(view.retained_view_entity);
if !wireframe_phases.contains_key(&view.retained_view_entity) {
continue;
}
let Some(view_key) = view_key_cache.get(&view.retained_view_entity.main_entity) else {
continue;
};
let view_specialized_material_pipeline_cache = specialized_material_pipeline_cache
.entry(view.retained_view_entity)
.or_default();
let Some(visible_entities) = visible_entities.get::<Mesh2d>() else {
continue;
};
let view_pending_wireframe2d_queues =
pending_wireframe2d_queues.prepare_for_new_frame(view.retained_view_entity);
if dirty_wireframe_specializations
.must_wipe_specializations_for_view(view.retained_view_entity)
{
view_specialized_material_pipeline_cache.clear();
} else {
for &renderable_entity in dirty_wireframe_specializations.iter_to_despecialize() {
view_specialized_material_pipeline_cache.remove(&renderable_entity);
}
}
for (_, visible_entity) in dirty_wireframe_specializations.iter_to_specialize(
view.retained_view_entity,
visible_entities,
&view_pending_wireframe2d_queues.prev_frame,
) {
if view_specialized_material_pipeline_cache.contains_key(visible_entity) {
continue;
}
if !render_wireframe_instances.contains_key(visible_entity) {
continue;
};
let Some(mesh_instance) = render_mesh_instances.get(visible_entity) else {
continue;
};
let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
continue;
};
let mut mesh_key = *view_key;
mesh_key |= Mesh2dPipelineKey::from_primitive_topology_and_strip_index(
mesh.primitive_topology(),
mesh.index_format(),
);
let pipeline_id =
pipelines.specialize(&pipeline_cache, &pipeline, mesh_key, &mesh.layout);
let pipeline_id = match pipeline_id {
Ok(id) => id,
Err(err) => {
error!("{}", err);
continue;
}
};
view_specialized_material_pipeline_cache
.insert(*visible_entity, (ticks.this_run(), pipeline_id));
}
}
pending_wireframe2d_queues.expire_stale_views(&all_views);
specialized_material_pipeline_cache
.retain(|retained_view_entity, _| all_views.contains(retained_view_entity));
}
fn queue_wireframes(
custom_draw_functions: Res<DrawFunctions<Wireframe2dPhaseItem>>,
render_mesh_instances: Res<RenderMesh2dInstances>,
mesh_allocator: Res<MeshAllocator>,
specialized_wireframe_pipeline_cache: Res<SpecializedWireframePipelineCache>,
render_wireframe_instances: Res<RenderWireframeInstances>,
dirty_wireframe_specializations: Res<DirtyWireframeSpecializations>,
mut wireframe_2d_phases: ResMut<ViewBinnedRenderPhases<Wireframe2dPhaseItem>>,
mut pending_wireframe2d_queues: ResMut<PendingWireframe2dQueues>,
mut views: Query<(&ExtractedView, &RenderVisibleEntities)>,
) {
for (view, visible_entities) in &mut views {
let Some(wireframe_phase) = wireframe_2d_phases.get_mut(&view.retained_view_entity) else {
continue;
};
let draw_wireframe = custom_draw_functions.read().id::<DrawWireframe2d>();
let Some(view_specialized_material_pipeline_cache) =
specialized_wireframe_pipeline_cache.get(&view.retained_view_entity)
else {
continue;
};
let Some(visible_entities) = visible_entities.get::<Mesh2d>() else {
continue;
};
let view_pending_wireframe2d_queues = pending_wireframe2d_queues
.get_mut(&view.retained_view_entity)
.expect(
"View pending 2D wireframe queues should have been created in \
`specialize_wireframes`",
);
for &main_entity in dirty_wireframe_specializations
.iter_to_dequeue(view.retained_view_entity, visible_entities)
{
wireframe_phase.remove(main_entity);
}
for (render_entity, visible_entity) in dirty_wireframe_specializations.iter_to_queue(
view.retained_view_entity,
visible_entities,
&view_pending_wireframe2d_queues.prev_frame,
) {
let Some(wireframe_instance) = render_wireframe_instances.get(visible_entity) else {
continue;
};
let Some(pipeline_id) = view_specialized_material_pipeline_cache
.get(visible_entity)
.map(|(_, pipeline_id)| *pipeline_id)
else {
view_pending_wireframe2d_queues
.current_frame
.insert((*render_entity, *visible_entity));
continue;
};
let Some(mesh_instance) = render_mesh_instances.get(visible_entity) else {
continue;
};
let Some(MeshSlabs {
vertex_slab_id: vertex_slab,
index_slab_id: index_slab,
..
}) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id)
else {
continue;
};
let bin_key = Wireframe2dBinKey {
asset_id: mesh_instance.mesh_asset_id.untyped(),
};
let batch_set_key = Wireframe2dBatchSetKey {
pipeline: pipeline_id,
asset_id: wireframe_instance.untyped(),
draw_function: draw_wireframe,
vertex_slab,
index_slab,
};
wireframe_phase.add(
batch_set_key,
bin_key,
(*render_entity, *visible_entity),
InputUniformIndex::default(),
if mesh_instance.automatic_batching {
BinnedRenderPhaseType::BatchableMesh
} else {
BinnedRenderPhaseType::UnbatchableMesh
},
);
}
}
}