1use bevy::asset::{AssetServer, Handle};
26use bevy::prelude::*;
27use bevy::render::render_resource::binding_types::{sampler, texture_2d};
28use bevy::render::render_resource::{
29 BindGroup, BindGroupEntries, BindGroupLayoutDescriptor, BindGroupLayoutEntries,
30 CachedRenderPipelineId, ColorTargetState, ColorWrites, FilterMode, FragmentState,
31 PipelineCache, RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor,
32 ShaderStages, SpecializedRenderPipeline, SpecializedRenderPipelines, Texture, TextureFormat,
33 TextureSampleType, TextureView, TextureViewDescriptor, VertexState,
34};
35use bevy::render::renderer::RenderDevice;
36use bevy::shader::Shader;
37
38use super::{ExtractedUiLayers, FilterSlot, LayerTextureStore};
39
40pub struct MipChain {
45 pub full_view: TextureView,
47 pub level_views: Vec<TextureView>,
50 pub bind_groups: Vec<BindGroup>,
52}
53
54pub fn mip_level_count(size: UVec2) -> u32 {
56 size.max(UVec2::ONE).max_element().ilog2() + 1
57}
58
59pub fn build_mip_chain(texture: &Texture, levels: u32) -> MipChain {
62 let full_view = texture.create_view(&TextureViewDescriptor {
63 label: Some("ui_layer_mips_full"),
64 ..Default::default()
65 });
66 let level_views = (0..levels)
67 .map(|level| {
68 texture.create_view(&TextureViewDescriptor {
69 label: Some("ui_layer_mip_level"),
70 base_mip_level: level,
71 mip_level_count: Some(1),
72 ..Default::default()
73 })
74 })
75 .collect();
76 MipChain {
77 full_view,
78 level_views,
79 bind_groups: Vec::new(),
80 }
81}
82
83#[derive(Resource)]
87pub struct LayerBlitPipeline {
88 pub layout: BindGroupLayoutDescriptor,
89 pub sampler: Sampler,
92 pub shader: Handle<Shader>,
93}
94
95pub fn init_layer_blit_pipeline(
96 mut commands: Commands,
97 render_device: Res<RenderDevice>,
98 asset_server: Res<AssetServer>,
99) {
100 let layout = BindGroupLayoutDescriptor::new(
101 "ui_layer_blit_layout",
102 &BindGroupLayoutEntries::sequential(
103 ShaderStages::FRAGMENT,
104 (
105 texture_2d(TextureSampleType::Float { filterable: true }),
106 sampler(SamplerBindingType::Filtering),
107 ),
108 ),
109 );
110 commands.insert_resource(LayerBlitPipeline {
111 layout,
112 sampler: render_device.create_sampler(&SamplerDescriptor {
113 label: Some("ui_layer_blit_sampler"),
114 mag_filter: FilterMode::Linear,
115 min_filter: FilterMode::Linear,
116 ..Default::default()
117 }),
118 shader: bevy::asset::load_embedded_asset!(asset_server.as_ref(), "mip_blit.wgsl"),
119 });
120}
121
122#[derive(Clone, Copy, Hash, PartialEq, Eq)]
123pub struct LayerBlitPipelineKey {
124 pub target_format: TextureFormat,
125}
126
127impl SpecializedRenderPipeline for LayerBlitPipeline {
128 type Key = LayerBlitPipelineKey;
129
130 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
131 RenderPipelineDescriptor {
132 vertex: VertexState {
133 shader: self.shader.clone(),
134 entry_point: Some("vertex".into()),
135 ..Default::default()
136 },
137 fragment: Some(FragmentState {
138 shader: self.shader.clone(),
139 entry_point: Some("fragment".into()),
140 targets: vec![Some(ColorTargetState {
141 format: key.target_format,
142 blend: None,
144 write_mask: ColorWrites::ALL,
145 })],
146 ..Default::default()
147 }),
148 layout: vec![self.layout.clone()],
149 label: Some("ui_layer_blit_pipeline".into()),
150 ..Default::default()
151 }
152 }
153}
154
155pub struct MipLevel {
158 pub bind_group: BindGroup,
159 pub target: TextureView,
160}
161
162pub struct MipRun {
164 pub pipeline: CachedRenderPipelineId,
165 pub levels: Vec<MipLevel>,
166}
167
168#[derive(Resource, Default)]
173pub struct LayerMipMeta {
174 pub runs: Vec<Option<MipRun>>,
175}
176
177pub fn prepare_layer_mips(
187 extracted: Res<ExtractedUiLayers>,
188 mut store: ResMut<LayerTextureStore>,
189 pipeline: Option<Res<LayerBlitPipeline>>,
190 mut specialized: ResMut<SpecializedRenderPipelines<LayerBlitPipeline>>,
191 pipeline_cache: Res<PipelineCache>,
192 render_device: Res<RenderDevice>,
193 mut meta: ResMut<LayerMipMeta>,
194) {
195 meta.runs.clear();
196 meta.runs.resize_with(extracted.layers.len(), || None);
197 let Some(pipeline) = pipeline else {
198 return;
199 };
200 for (idx, layer) in extracted.layers.iter().enumerate() {
201 if !layer.wants_mips {
202 continue;
203 }
204 let Some(slot) = store.slots.get_mut(&layer.main_entity) else {
205 continue;
206 };
207 let pipeline_id = specialized.specialize(
208 &pipeline_cache,
209 &pipeline,
210 LayerBlitPipelineKey {
211 target_format: layer.target_format,
212 },
213 );
214 let (chain, mips_valid, source_valid) = if layer.chain.is_some() {
218 let Some(filter) = slot.filter.as_mut() else {
219 continue;
220 };
221 let FilterSlot {
222 mips,
223 mips_valid,
224 output_index,
225 output_valid,
226 ..
227 } = filter;
228 let Some(chain) = mips[*output_index].as_mut() else {
229 continue;
230 };
231 (chain, mips_valid, *output_valid)
232 } else {
233 let Some(chain) = slot.mips.as_mut() else {
234 continue;
235 };
236 (chain, &mut slot.mips_valid, slot.content_valid)
237 };
238 if *mips_valid {
239 continue; }
241 if !source_valid || pipeline_cache.get_render_pipeline(pipeline_id).is_none() {
244 continue;
245 }
246 if chain.bind_groups.is_empty() {
247 let layout = pipeline_cache.get_bind_group_layout(&pipeline.layout);
248 chain.bind_groups = chain
249 .level_views
250 .iter()
251 .map(|view| {
252 render_device.create_bind_group(
253 "ui_layer_mip_source",
254 &layout,
255 &BindGroupEntries::sequential((view, &pipeline.sampler)),
256 )
257 })
258 .collect();
259 }
260 let levels = (0..chain.level_views.len().saturating_sub(1))
261 .map(|level| MipLevel {
262 bind_group: chain.bind_groups[level].clone(),
263 target: chain.level_views[level + 1].clone(),
264 })
265 .collect();
266 meta.runs[idx] = Some(MipRun {
267 pipeline: pipeline_id,
268 levels,
269 });
270 *mips_valid = true;
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn mip_level_count_covers_full_chain() {
280 assert_eq!(mip_level_count(UVec2::new(1, 1)), 1);
281 assert_eq!(mip_level_count(UVec2::new(2, 2)), 2);
282 assert_eq!(mip_level_count(UVec2::new(256, 64)), 9);
283 assert_eq!(mip_level_count(UVec2::new(300, 20)), 9);
286 assert_eq!(mip_level_count(UVec2::ZERO), 1);
288 }
289}