aura-gui 0.3.5

UI nativa acelerada por GPU para escritorio, Android e iOS, en un solo crate: estilo tipo Tailwind, componentes, sombras, animaciones y agentes de IA. Reposo a cero. Licencia AGPL-3.0 o comercial.
//! Frame con desenfoques: pinta en una textura intermedia, corta el pase en cada desenfoque y copia a la ventana.

use super::GpuRenderer;
use crate::core::Rgba;
use crate::gpu::blur::BlurState;
use crate::gpu::scene::{PrimitiveKind, Scene};

pub(super) fn clear_color(clear: Rgba) -> wgpu::Color {
    wgpu::Color { r: clear.r as f64, g: clear.g as f64, b: clear.b as f64, a: clear.a as f64 }
}

impl GpuRenderer {
    /// Crea (la primera vez) y prepara el estado del desenfoque, o cuenta un frame de reposo para soltarlo.
    pub(super) fn prepare_blur(&mut self, scene: &Scene, scale: f32) {
        if !scene.has_blur() {
            if let Some(blur) = &mut self.blur {
                blur.idle_frame();
            }
            return;
        }
        let (device, queue) = (&self.context.device, &self.context.queue);
        let format = self.context.config.format;
        let size = (self.context.config.width, self.context.config.height);
        let blur = self.blur.get_or_insert_with(|| BlurState::new(device, &self.pipelines, format));
        blur.prepare(device, queue, &self.pipelines, scene, size, scale);
    }

    /// Los lotes anteriores a cada desenfoque van en su propio pase sobre la textura intermedia; el desenfoque se
    /// aplica entre pases y la copia final lleva todo a `view`.
    pub(super) fn encode_blurred(
        &self,
        encoder: &mut wgpu::CommandEncoder,
        blur: &BlurState,
        view: &wgpu::TextureView,
        scene: &Scene,
        clear: Rgba,
    ) {
        let target = blur.scene_view();
        let mut load = wgpu::LoadOp::Clear(clear_color(clear));
        let mut segment = 0;
        let draw = |encoder: &mut wgpu::CommandEncoder,
                    load: wgpu::LoadOp<wgpu::Color>,
                    batches: &[crate::gpu::scene::Batch]| {
            let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: Some("aura_pass"),
                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                    view: target,
                    depth_slice: None,
                    resolve_target: None,
                    ops: wgpu::Operations { load, store: wgpu::StoreOp::Store },
                })],
                ..Default::default()
            });
            self.draw_batches(&mut pass, batches);
        };
        for (index, batch) in scene.batches.iter().enumerate() {
            if batch.kind != PrimitiveKind::Blur {
                continue;
            }
            if segment < index {
                draw(encoder, load, &scene.batches[segment..index]);
                load = wgpu::LoadOp::Load;
            }
            for instance in batch.range.clone() {
                blur.blur_passes(encoder, instance as usize);
                // El compuesto es un lote de un solo desenfoque, sobre lo que ya hay en la textura.
                let one =
                    crate::gpu::scene::Batch { kind: PrimitiveKind::Blur, range: instance..instance + 1, transform: 0 };
                draw(encoder, load, std::slice::from_ref(&one));
                load = wgpu::LoadOp::Load;
            }
            segment = index + 1;
        }
        if segment < scene.batches.len() || segment == 0 {
            draw(encoder, load, &scene.batches[segment..]);
        }
        blur.blit(encoder, view);
    }
}