aura-gui 0.3.0

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.
//! Pipelines de render: uno por tipo de primitiva, todos con el mismo shader.

use crate::gpu::context::Error;

pub struct Pipelines {
    pub quad: wgpu::RenderPipeline,
    pub shadow: wgpu::RenderPipeline,
    pub underline: wgpu::RenderPipeline,
    pub monochrome: wgpu::RenderPipeline,
    pub polychrome: wgpu::RenderPipeline,
    pub globals_layout: wgpu::BindGroupLayout,
    pub instances_layout: wgpu::BindGroupLayout,
    pub atlas_layout: wgpu::BindGroupLayout,
    pub(crate) shader: wgpu::ShaderModule,
    pub(crate) layout: wgpu::PipelineLayout,
}

/// Tamaño de `Globals` en el shader: viewport + relleno + matriz + desplazamiento.
pub const GLOBALS_SIZE: u64 = 48;

fn buffer_entry(binding: u32, kind: wgpu::BufferBindingType, stage: wgpu::ShaderStages) -> wgpu::BindGroupLayoutEntry {
    wgpu::BindGroupLayoutEntry {
        binding,
        visibility: stage,
        ty: wgpu::BindingType::Buffer { ty: kind, has_dynamic_offset: false, min_binding_size: None },
        count: None,
    }
}

impl Pipelines {
    pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Result<Self, Error> {
        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
            label: Some("aura_uber_shader"),
            source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
        });

        let globals_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: Some("aura_globals_layout"),
            // Un `Globals` por transformación; cada lote elige el suyo con un desplazamiento dinámico.
            entries: &[wgpu::BindGroupLayoutEntry {
                ty: wgpu::BindingType::Buffer {
                    ty: wgpu::BufferBindingType::Uniform,
                    has_dynamic_offset: true,
                    min_binding_size: wgpu::BufferSize::new(GLOBALS_SIZE),
                },
                ..buffer_entry(
                    0,
                    wgpu::BufferBindingType::Uniform,
                    wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
                )
            }],
        });
        let instances_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: Some("aura_instances_layout"),
            entries: &[buffer_entry(
                0,
                wgpu::BufferBindingType::Storage { read_only: true },
                wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
            )],
        });
        let atlas_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: Some("aura_atlas_layout"),
            entries: &[
                wgpu::BindGroupLayoutEntry {
                    binding: 0,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Texture {
                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
                        view_dimension: wgpu::TextureViewDimension::D2,
                        multisampled: false,
                    },
                    count: None,
                },
                wgpu::BindGroupLayoutEntry {
                    binding: 1,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
                    count: None,
                },
            ],
        });

        let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
            label: Some("aura_pipeline_layout"),
            bind_group_layouts: &[Some(&globals_layout), Some(&instances_layout), Some(&atlas_layout)],
            immediate_size: 0,
        });

        let build = |label: &str, vertex: &str, fragment: &str| {
            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
                label: Some(label),
                layout: Some(&layout),
                vertex: wgpu::VertexState {
                    module: &shader,
                    entry_point: Some(vertex),
                    compilation_options: Default::default(),
                    buffers: &[],
                },
                fragment: Some(wgpu::FragmentState {
                    module: &shader,
                    entry_point: Some(fragment),
                    compilation_options: Default::default(),
                    targets: &[Some(wgpu::ColorTargetState {
                        format,
                        blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
                        write_mask: wgpu::ColorWrites::ALL,
                    })],
                }),
                primitive: wgpu::PrimitiveState {
                    topology: wgpu::PrimitiveTopology::TriangleStrip,
                    ..Default::default()
                },
                depth_stencil: None,
                multisample: wgpu::MultisampleState::default(),
                multiview_mask: None,
                cache: None,
            })
        };

        let quad = build("aura_quads", "vs_quad", "fs_quad");
        let shadow = build("aura_shadows", "vs_shadow", "fs_shadow");
        let underline = build("aura_underlines", "vs_underline", "fs_underline");
        let monochrome = build("aura_monochrome", "vs_sprite", "fs_monochrome");
        let polychrome = build("aura_polychrome", "vs_sprite", "fs_polychrome");
        Ok(Self {
            quad,
            shadow,
            underline,
            monochrome,
            polychrome,
            globals_layout,
            instances_layout,
            atlas_layout,
            shader,
            layout,
        })
    }
}