Module pipeline

Source
Expand description

GPU compute pipeline system for Tessera UI framework.

This module provides the infrastructure for GPU compute operations in Tessera, enabling advanced visual effects and post-processing operations that would be inefficient or impossible to achieve with traditional CPU-based approaches.

§Architecture Overview

The compute pipeline system is designed to work seamlessly with the rendering pipeline, using a ping-pong buffer approach for efficient multi-pass operations. Each compute pipeline processes a specific type of compute command and operates on texture data using GPU compute shaders.

§Key Components

§Design Philosophy

The compute pipeline system embraces WGPU’s compute shader capabilities to enable:

  • Advanced Post-Processing: Blur, contrast adjustment, color grading, and other image effects
  • Parallel Processing: Leverage GPU parallelism for computationally intensive operations
  • Real-Time Effects: Achieve complex visual effects at interactive frame rates
  • Memory Efficiency: Use GPU memory directly without CPU roundtrips

§Ping-Pong Rendering

The system uses a ping-pong approach where:

  1. Input Texture: Contains the result from previous rendering or compute pass
  2. Output Texture: Receives the processed result from the current compute operation
  3. Format Convention: All textures use wgpu::TextureFormat::Rgba8Unorm for compatibility

This approach enables efficient chaining of multiple compute operations without intermediate CPU involvement.

§Implementation Guide

§Creating a Custom Compute Pipeline

To create a custom compute pipeline:

  1. Define your compute command struct implementing ComputeCommand
  2. Create a pipeline struct implementing ComputablePipeline<YourCommand>
  3. Write a compute shader in WGSL
  4. Register the pipeline with ComputePipelineRegistry::register

§Example: Simple Brightness Adjustment Pipeline

use tessera_ui::{ComputeCommand, ComputablePipeline, compute::resource::ComputeResourceManager};
use wgpu;

// 1. Define the compute command
#[derive(Debug)]
struct BrightnessCommand {
    brightness: f32,
}

impl ComputeCommand for BrightnessCommand {}

// 2. Implement the pipeline
struct BrightnessPipeline {
    compute_pipeline: wgpu::ComputePipeline,
    bind_group_layout: wgpu::BindGroupLayout,
}

impl BrightnessPipeline {
    fn new(device: &wgpu::Device) -> Self {
        // Create compute shader and pipeline
        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
            label: Some("Brightness Shader"),
            source: wgpu::ShaderSource::Wgsl(include_str!("brightness.wgsl").into()),
        });
         
        // ... setup bind group layout and pipeline ...
    }
}

impl ComputablePipeline<BrightnessCommand> for BrightnessPipeline {
    fn dispatch(
        &mut self,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        config: &wgpu::SurfaceConfiguration,
        compute_pass: &mut wgpu::ComputePass<'_>,
        command: &BrightnessCommand,
        resource_manager: &mut ComputeResourceManager,
        input_view: &wgpu::TextureView,
        output_view: &wgpu::TextureView,
    ) {
        // Create uniforms buffer with brightness value
        let uniforms = [command.brightness];
        let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
            label: Some("Brightness Uniforms"),
            contents: bytemuck::cast_slice(&uniforms),
            usage: wgpu::BufferUsages::UNIFORM,
        });
         
        // Create bind group with input/output textures and uniforms
        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            layout: &self.bind_group_layout,
            entries: &[
                wgpu::BindGroupEntry { binding: 0, resource: uniform_buffer.as_entire_binding() },
                wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(input_view) },
                wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(output_view) },
            ],
            label: Some("brightness_bind_group"),
        });
         
        // Dispatch compute shader
        compute_pass.set_pipeline(&self.compute_pipeline);
        compute_pass.set_bind_group(0, &bind_group, &[]);
        compute_pass.dispatch_workgroups(
            (config.width + 7) / 8,
            (config.height + 7) / 8,
            1
        );
    }
}

// 3. Register the pipeline
let mut registry = ComputePipelineRegistry::new();
let brightness_pipeline = BrightnessPipeline::new(&device);
registry.register(brightness_pipeline);

§Example WGSL Compute Shader

@group(0) @binding(0) var<uniform> brightness: f32;
@group(0) @binding(1) var input_texture: texture_2d<f32>;
@group(0) @binding(2) var output_texture: texture_storage_2d<rgba8unorm, write>;

@compute @workgroup_size(8, 8)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
    let coords = vec2<i32>(global_id.xy);
    let input_color = textureLoad(input_texture, coords, 0);
    let output_color = vec4<f32>(input_color.rgb * brightness, input_color.a);
    textureStore(output_texture, coords, output_color);
}

§Integration with Basic Components

The tessera_basic_components crate provides several compute pipeline implementations:

  • BlurPipeline: Gaussian blur effects for backgrounds and UI elements
  • MeanPipeline: Average color calculation for adaptive UI themes
  • ContrastPipeline: Contrast and saturation adjustments

These pipelines demonstrate real-world usage patterns and can serve as references for implementing custom compute operations.

§Performance Considerations

  • Workgroup Size: Choose workgroup sizes that align with GPU architecture (typically 8x8 or 16x16)
  • Memory Access: Optimize memory access patterns in shaders for better cache utilization
  • Resource Reuse: Use the ComputeResourceManager to reuse buffers across frames
  • Batch Operations: Combine multiple similar operations when possible

§Texture Format Requirements

Due to WGPU limitations, compute shaders require specific texture formats:

  • Input Textures: Can be any readable format, typically from render passes
  • Output Textures: Must use wgpu::TextureFormat::Rgba8Unorm for storage binding
  • sRGB Limitation: sRGB formats cannot be used as storage textures

The framework automatically handles format conversions when necessary.

Structs§

ComputePipelineRegistry
Registry for managing and dispatching compute pipelines.

Traits§

ComputablePipeline
Core trait for implementing GPU compute pipelines.