Skip to content

Commit 7dc63b8

Browse files
committed
Merge branch 'master' of github.com:komadori/bevy_mod_outline into stencil-control
2 parents 6b799ca + 06a3c9e commit 7dc63b8

File tree

11 files changed

+604
-447
lines changed

11 files changed

+604
-447
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## bevy_mod_outline 0.10.3 (2025-08-24)
4+
5+
### Added
6+
- Added OutlineWarmUp for warming up pipelines.
7+
38
## bevy_mod_outline 0.10.2 (2025-06-19)
49

510
### Fixed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bevy_mod_outline"
3-
version = "0.10.2"
3+
version = "0.10.3"
44
edition = "2021"
55
license = "MIT OR Apache-2.0"
66
description = "A mesh outlining plugin for Bevy."

src/computed.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use bevy::{ecs::query::QueryItem, prelude::*, render::view::RenderLayers};
22

33
use crate::{
4+
pipeline_key::ComputedOutlineKey,
45
uniforms::{DepthMode, DrawMode},
56
InheritOutline, OutlineAlphaMask, OutlineMode, OutlinePlaneDepth, OutlineRenderLayers,
6-
OutlineStencil, OutlineStencilEnabled, OutlineVolume, TextureChannel,
7+
OutlineStencil, OutlineStencilEnabled, OutlineVolume, OutlineWarmUp,
78
};
89

910
#[derive(Clone)]
@@ -117,13 +118,6 @@ impl<T: Clone> Sourced<T> {
117118
}
118119
}
119120

120-
#[derive(Clone)]
121-
pub(crate) struct ComputedAlphaMask {
122-
pub(crate) texture: Option<Handle<Image>>,
123-
pub(crate) channel: TextureChannel,
124-
pub(crate) threshold: f32,
125-
}
126-
127121
#[derive(Clone)]
128122
pub(crate) struct ComputedInternal {
129123
pub(crate) inherited_from: Option<Entity>,
@@ -132,11 +126,13 @@ pub(crate) struct ComputedInternal {
132126
pub(crate) mode: Sourced<ComputedMode>,
133127
pub(crate) depth: Sourced<ComputedDepth>,
134128
pub(crate) layers: Sourced<RenderLayers>,
135-
pub(crate) alpha_mask: Sourced<ComputedAlphaMask>,
129+
pub(crate) alpha_mask: Sourced<OutlineAlphaMask>,
130+
pub(crate) warm_up: Sourced<OutlineWarmUp>,
136131
}
137132

138133
/// A component for storing the computed depth at which the outline lies.
139134
#[derive(Clone, Component, Default)]
135+
#[require(ComputedOutlineKey)]
140136
pub struct ComputedOutline(pub(crate) Option<ComputedInternal>);
141137

142138
type OutlineComponents<'a> = (
@@ -149,6 +145,7 @@ type OutlineComponents<'a> = (
149145
Option<Ref<'a, OutlineRenderLayers>>,
150146
Option<Ref<'a, RenderLayers>>,
151147
Option<Ref<'a, OutlineAlphaMask>>,
148+
Option<Ref<'a, OutlineWarmUp>>,
152149
);
153150

154151
#[allow(clippy::type_complexity)]
@@ -217,10 +214,18 @@ fn propagate_computed_outline(
217214

218215
fn update_computed_outline(
219216
computed: &mut Mut<'_, ComputedOutline>,
220-
(visibility, transform, volume, stencil, mode, depth, layers, fallback_layers, alpha_mask): QueryItem<
221-
'_,
222-
OutlineComponents,
223-
>,
217+
(
218+
visibility,
219+
transform,
220+
volume,
221+
stencil,
222+
mode,
223+
depth,
224+
layers,
225+
fallback_layers,
226+
alpha_mask,
227+
warm_up,
228+
): QueryItem<'_, OutlineComponents>,
224229
parent_computed: Option<&ComputedInternal>,
225230
parent_entity: Option<Entity>,
226231
force_update: bool,
@@ -326,11 +331,12 @@ fn update_computed_outline(
326331
alpha_mask: Sourced::set(
327332
alpha_mask,
328333
parent_computed.map(|p| p.alpha_mask.value.clone()),
329-
|mask| ComputedAlphaMask {
330-
texture: mask.texture.clone(),
331-
channel: mask.channel,
332-
threshold: mask.threshold,
333-
},
334+
|mask| mask.clone(),
335+
),
336+
warm_up: Sourced::set(
337+
warm_up,
338+
parent_computed.map(|p| p.warm_up.value.clone()),
339+
|warm_up| warm_up.clone(),
334340
),
335341
});
336342
}

src/flood/compose_output.rs

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@ use bevy::{
1515
view::{ExtractedView, ViewDepthTexture, ViewTarget},
1616
},
1717
};
18-
use bitfield::{bitfield_bitrange, bitfield_fields};
1918
use wgpu_types::{
2019
BlendState, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
2120
MultisampleState, PrimitiveState, SamplerBindingType, ShaderStages, StencilState,
2221
TextureFormat, TextureSampleType,
2322
};
2423

25-
use crate::uniforms::ExtractedOutline;
24+
use crate::{pipeline_key::ViewPipelineKey, uniforms::ExtractedOutline};
2625

2726
use super::{DrawMode, OutlineViewUniform, COMPOSE_OUTPUT_SHADER_HANDLE};
2827

@@ -47,47 +46,11 @@ pub(crate) fn prepare_compose_output_uniform(
4746
}
4847
}
4948

50-
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
51-
pub(crate) struct ComposeOutputPipelineKey(u32);
52-
bitfield_bitrange! {struct ComposeOutputPipelineKey(u32)}
53-
54-
impl ComposeOutputPipelineKey {
55-
bitfield_fields! {
56-
u32;
57-
msaa_samples_minus_one, set_msaa_samples_minus_one: 5, 0;
58-
pub hdr_format, set_hdr_format: 6;
59-
}
60-
61-
pub(crate) fn new() -> Self {
62-
ComposeOutputPipelineKey(0)
63-
}
64-
65-
pub(crate) fn with_msaa(mut self, msaa: Msaa) -> Self {
66-
self.set_msaa_samples_minus_one(msaa as u32 - 1);
67-
self
68-
}
69-
70-
pub(crate) fn msaa(&self) -> Msaa {
71-
match self.msaa_samples_minus_one() + 1 {
72-
x if x == Msaa::Off as u32 => Msaa::Off,
73-
x if x == Msaa::Sample2 as u32 => Msaa::Sample2,
74-
x if x == Msaa::Sample4 as u32 => Msaa::Sample4,
75-
x if x == Msaa::Sample8 as u32 => Msaa::Sample8,
76-
x => panic!("Invalid value for Msaa: {}", x),
77-
}
78-
}
79-
80-
pub(crate) fn with_hdr_format(mut self, hdr_format: bool) -> Self {
81-
self.set_hdr_format(hdr_format);
82-
self
83-
}
84-
}
85-
8649
#[derive(Clone, Resource)]
8750
pub(crate) struct ComposeOutputPipeline {
8851
pub(crate) layout: BindGroupLayout,
8952
pub(crate) sampler: Sampler,
90-
pub(crate) pipeline_cache: HashMap<ComposeOutputPipelineKey, CachedRenderPipelineId>,
53+
pub(crate) pipeline_cache: HashMap<ViewPipelineKey, CachedRenderPipelineId>,
9154
}
9255

9356
impl FromWorld for ComposeOutputPipeline {
@@ -121,7 +84,7 @@ impl ComposeOutputPipeline {
12184
pub(crate) fn get_pipeline(
12285
&mut self,
12386
pipeline_cache: &PipelineCache,
124-
key: ComposeOutputPipelineKey,
87+
key: ViewPipelineKey,
12588
) -> CachedRenderPipelineId {
12689
*self.pipeline_cache.entry(key).or_insert_with(|| {
12790
pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
@@ -176,7 +139,7 @@ pub(crate) fn prepare_compose_output_pass(
176139
for (entity, view, msaa) in query.iter() {
177140
let pipeline_id = compose_output_pipeline.get_pipeline(
178141
&pipeline_cache,
179-
ComposeOutputPipelineKey::new()
142+
ViewPipelineKey::new()
180143
.with_msaa(*msaa)
181144
.with_hdr_format(view.hdr),
182145
);

src/flood/flood_init.rs

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ use std::ops::Range;
33
use bevy::prelude::*;
44
use bevy::render::camera::{ExtractedCamera, Viewport};
55
use bevy::render::mesh::allocator::MeshAllocator;
6-
use bevy::render::mesh::RenderMesh;
7-
use bevy::render::render_asset::RenderAssets;
86
use bevy::render::render_phase::{DrawFunctions, PhaseItemExtraIndex, ViewSortedRenderPhases};
9-
use bevy::render::render_resource::{PipelineCache, SpecializedMeshPipelines};
107
use bevy::render::sync_world::MainEntity;
118
use bevy::render::view::{ExtractedView, RenderLayers};
129
use bevy::render::{
@@ -19,15 +16,13 @@ use bevy::render::{
1916
};
2017
use wgpu_types::ImageSubresourceRange;
2118

22-
use crate::queue::OutlineRangefinder;
19+
use crate::queue::{OutlineCache, OutlineCacheEntry, OutlineRangefinder};
2320
use crate::uniforms::ExtractedOutline;
2421
use crate::view_uniforms::OutlineQueueStatus;
2522

2623
use super::bounds::FloodMeshBounds;
2724
use super::node::FloodOutline;
28-
use super::{
29-
DepthMode, DrawMode, DrawOutline, OutlinePipeline, OutlineViewUniform, PassType, PipelineKey,
30-
};
25+
use super::{DrawMode, DrawOutline, OutlineViewUniform};
3126

3227
pub(crate) fn prepare_flood_phases(
3328
query: Query<&ExtractedView, With<OutlineViewUniform>>,
@@ -41,11 +36,8 @@ pub(crate) fn prepare_flood_phases(
4136
#[allow(clippy::too_many_arguments)]
4237
pub(crate) fn queue_flood_meshes(
4338
flood_draw_functions: Res<DrawFunctions<FloodOutline>>,
44-
outline_pipeline: Res<OutlinePipeline>,
45-
mut pipelines: ResMut<SpecializedMeshPipelines<OutlinePipeline>>,
46-
pipeline_cache: Res<PipelineCache>,
47-
render_meshes: Res<RenderAssets<RenderMesh>>,
4839
mesh_allocator: Res<MeshAllocator>,
40+
outline_cache: Res<OutlineCache>,
4941
material_meshes: Query<(Entity, &MainEntity, &ExtractedOutline, &FloodMeshBounds)>,
5042
mut flood_phases: ResMut<ViewSortedRenderPhases<FloodOutline>>,
5143
mut views: Query<(
@@ -78,6 +70,11 @@ pub(crate) fn queue_flood_meshes(
7870

7971
let rangefinder = OutlineRangefinder::new(view);
8072

73+
let outline_view_cache = outline_cache
74+
.view_map
75+
.get(&view.retained_view_entity)
76+
.unwrap();
77+
8178
for (entity, main_entity, outline, mesh_bounds) in material_meshes.iter() {
8279
if !outline.volume {
8380
continue;
@@ -91,10 +88,6 @@ pub(crate) fn queue_flood_meshes(
9188
continue;
9289
}
9390

94-
let Some(mesh) = render_meshes.get(outline.mesh_id) else {
95-
continue;
96-
};
97-
9891
// Calculate screen-space bounds of outline
9992
let border = (view_uniform.scale_physical_from_logical
10093
* outline.instance_data.volume_offset)
@@ -107,36 +100,30 @@ pub(crate) fn queue_flood_meshes(
107100

108101
let (_vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&outline.mesh_id);
109102

110-
let flood_key = PipelineKey::new()
111-
.with_primitive_topology(mesh.primitive_topology())
112-
.with_depth_mode(DepthMode::Flat)
113-
.with_morph_targets(mesh.morph_targets.is_some())
114-
.with_vertex_offset_zero(true)
115-
.with_plane_offset_zero(true)
116-
.with_pass_type(PassType::FloodInit)
117-
.with_double_sided(outline.double_sided)
118-
.with_alpha_mask_texture(outline.alpha_mask_id.is_some())
119-
.with_alpha_mask_channel(outline.alpha_mask_channel);
103+
let Some(OutlineCacheEntry {
104+
changed_tick: _,
105+
stencil_pipeline_id: _,
106+
volume_pipeline_id,
107+
}) = outline_view_cache.entity_map.get(main_entity)
108+
else {
109+
continue;
110+
};
120111

121112
queue_status.has_volume = true;
122113

123-
if let Ok(pipeline) =
124-
pipelines.specialize(&pipeline_cache, &outline_pipeline, flood_key, &mesh.layout)
125-
{
126-
flood_phase.add(FloodOutline {
127-
distance: rangefinder.distance_of(outline),
128-
entity,
129-
main_entity: *main_entity,
130-
pipeline,
131-
draw_function: draw_flood,
132-
batch_range: 0..0,
133-
extra_index: PhaseItemExtraIndex::None,
134-
indexed: index_slab.is_some(),
135-
volume_offset: outline.instance_data.volume_offset,
136-
volume_colour: outline.instance_data.volume_colour,
137-
screen_space_bounds,
138-
});
139-
}
114+
flood_phase.add(FloodOutline {
115+
distance: rangefinder.distance_of(outline),
116+
entity,
117+
main_entity: *main_entity,
118+
pipeline: *volume_pipeline_id,
119+
draw_function: draw_flood,
120+
batch_range: 0..0,
121+
extra_index: PhaseItemExtraIndex::None,
122+
indexed: index_slab.is_some(),
123+
volume_offset: outline.instance_data.volume_offset,
124+
volume_colour: outline.instance_data.volume_colour,
125+
screen_space_bounds,
126+
});
140127
}
141128
}
142129
}

src/flood/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ use flood_init::{prepare_flood_phases, queue_flood_meshes};
2828
use jump_flood::JumpFloodPipeline;
2929
use node::{FloodNode, FloodOutline};
3030

31-
use crate::pipeline::{OutlinePipeline, PassType, PipelineKey};
31+
use crate::pipeline::OutlinePipeline;
3232
use crate::render::DrawOutline;
33-
use crate::uniforms::{DepthMode, DrawMode};
33+
use crate::uniforms::DrawMode;
3434
use crate::view_uniforms::OutlineViewUniform;
3535
use crate::{add_dummy_phase_buffer, NodeOutline};
3636

0 commit comments

Comments
 (0)