tessera_ui_basic_components/pipelines/
mean.rs

1//! # Mean Luminance Compute Pipeline
2//!
3//! This module implements a GPU-based compute pipeline for calculating the mean luminance of a texture.
4//! It provides both the pipeline and command abstractions for integrating mean luminance computation into Tessera UI rendering flows.
5//!
6//! ## Functionality
7//! - Calculates the average (mean) luminance of a given texture using a compute shader.
8//! - Exposes a [`MeanCommand`](MeanCommand) for issuing the operation and a [`MeanPipeline`](MeanPipeline) for dispatching the compute workload.
9//!
10//! ## Typical Use Cases
11//! - Image processing and analysis
12//! - Tone mapping and exposure adaptation in UI or graphics applications
13//! - Adaptive UI rendering based on scene brightness
14//!
15//! ## Integration
16//! Register and use this pipeline when average brightness information is required for further rendering or UI logic.
17//! See [`MeanCommand`] and [`MeanPipeline`] for usage examples.
18
19use tessera_ui::{
20    compute::{ComputeResourceRef, resource::ComputeResourceManager},
21    renderer::compute::{ComputablePipeline, command::ComputeCommand},
22    wgpu,
23};
24
25// --- Command ---
26
27/// A command to calculate the mean luminance of the input texture.
28#[derive(Debug, Clone, Copy)]
29/// Command to calculate the mean luminance of the input texture.
30///
31/// # Example
32/// ```rust,ignore
33/// use tessera_ui_basic_components::pipelines::mean::MeanCommand;
34/// let command = MeanCommand::new(&device, &mut resource_manager);
35/// ```
36pub struct MeanCommand {
37    result_buffer_ref: ComputeResourceRef,
38}
39
40impl MeanCommand {
41    /// Creates a new `MeanCommand` and allocates a result buffer.
42    ///
43    /// # Parameters
44    /// - `gpu`: The wgpu device.
45    /// - `compute_resource_manager`: Resource manager for compute buffers.
46    pub fn new(gpu: &wgpu::Device, compute_resource_manager: &mut ComputeResourceManager) -> Self {
47        let result_buffer = gpu.create_buffer(&wgpu::BufferDescriptor {
48            label: Some("Mean Result Buffer"),
49            size: 8, // two u32s: total_luminance, total_pixels
50            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
51            mapped_at_creation: false,
52        });
53
54        let result_buffer_ref = compute_resource_manager.push(result_buffer);
55        MeanCommand { result_buffer_ref }
56    }
57
58    /// Returns the reference to the result buffer.
59    pub fn result_buffer_ref(&self) -> ComputeResourceRef {
60        self.result_buffer_ref
61    }
62}
63
64impl ComputeCommand for MeanCommand {}
65
66// --- Pipeline ---
67
68/// Pipeline for calculating mean luminance using a compute shader.
69///
70/// # Example
71/// ```rust,ignore
72/// use tessera_ui_basic_components::pipelines::mean::MeanPipeline;
73/// let pipeline = MeanPipeline::new(&device);
74/// ```
75pub struct MeanPipeline {
76    pipeline: wgpu::ComputePipeline,
77    bind_group_layout: wgpu::BindGroupLayout,
78}
79
80impl MeanPipeline {
81    pub fn new(device: &wgpu::Device) -> Self {
82        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
83            label: Some("Mean Shader"),
84            source: wgpu::ShaderSource::Wgsl(include_str!("mean/mean.wgsl").into()),
85        });
86
87        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
88            entries: &[
89                // 0: Source Texture
90                wgpu::BindGroupLayoutEntry {
91                    binding: 0,
92                    visibility: wgpu::ShaderStages::COMPUTE,
93                    ty: wgpu::BindingType::Texture {
94                        sample_type: wgpu::TextureSampleType::Float { filterable: false },
95                        view_dimension: wgpu::TextureViewDimension::D2,
96                        multisampled: false,
97                    },
98                    count: None,
99                },
100                // 1: Result Buffer (Storage)
101                wgpu::BindGroupLayoutEntry {
102                    binding: 1,
103                    visibility: wgpu::ShaderStages::COMPUTE,
104                    ty: wgpu::BindingType::Buffer {
105                        ty: wgpu::BufferBindingType::Storage { read_only: false },
106                        has_dynamic_offset: false,
107                        min_binding_size: Some(std::num::NonZeroU64::new(8).unwrap()),
108                    },
109                    count: None,
110                },
111                // 2: Destination Texture (Storage)
112                wgpu::BindGroupLayoutEntry {
113                    binding: 2,
114                    visibility: wgpu::ShaderStages::COMPUTE,
115                    ty: wgpu::BindingType::StorageTexture {
116                        access: wgpu::StorageTextureAccess::WriteOnly,
117                        format: wgpu::TextureFormat::Rgba8Unorm,
118                        view_dimension: wgpu::TextureViewDimension::D2,
119                    },
120                    count: None,
121                },
122            ],
123            label: Some("mean_bind_group_layout"),
124        });
125
126        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
127            label: Some("Mean Pipeline Layout"),
128            bind_group_layouts: &[&bind_group_layout],
129            push_constant_ranges: &[],
130        });
131
132        let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
133            label: Some("Mean Pipeline"),
134            layout: Some(&pipeline_layout),
135            module: &shader,
136            entry_point: Some("main"),
137            compilation_options: Default::default(),
138            cache: None,
139        });
140
141        Self {
142            pipeline,
143            bind_group_layout,
144        }
145    }
146}
147
148impl ComputablePipeline<MeanCommand> for MeanPipeline {
149    /// Dispatches the compute shader to calculate mean luminance.
150    ///
151    /// # Parameters
152    /// - `device`: The wgpu device.
153    /// - `config`: Surface configuration.
154    /// - `compute_pass`: The compute pass to encode commands.
155    /// - `command`: The mean command with buffer reference.
156    /// - `resource_manager`: Resource manager for compute buffers.
157    /// - `input_view`: Source texture view.
158    /// - `output_view`: Destination texture view.
159    fn dispatch(
160        &mut self,
161        device: &wgpu::Device,
162        _queue: &wgpu::Queue,
163        config: &wgpu::SurfaceConfiguration,
164        compute_pass: &mut wgpu::ComputePass<'_>,
165        command: &MeanCommand,
166        resource_manager: &mut ComputeResourceManager,
167        input_view: &wgpu::TextureView,
168        output_view: &wgpu::TextureView,
169    ) {
170        let result_buffer = resource_manager.get(&command.result_buffer_ref).unwrap();
171        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
172            layout: &self.bind_group_layout,
173            entries: &[
174                wgpu::BindGroupEntry {
175                    binding: 0,
176                    resource: wgpu::BindingResource::TextureView(input_view),
177                },
178                wgpu::BindGroupEntry {
179                    binding: 1,
180                    resource: result_buffer.as_entire_binding(),
181                },
182                wgpu::BindGroupEntry {
183                    binding: 2,
184                    resource: wgpu::BindingResource::TextureView(output_view),
185                },
186            ],
187            label: Some("mean_bind_group"),
188        });
189
190        compute_pass.set_pipeline(&self.pipeline);
191        compute_pass.set_bind_group(0, &bind_group, &[]);
192        compute_pass.dispatch_workgroups(config.width.div_ceil(8), config.height.div_ceil(8), 1);
193    }
194}