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
ComputablePipeline<C>
: The main trait for implementing custom compute pipelinesComputePipelineRegistry
: Manages and dispatches commands to registered compute pipelinesComputeResourceManager
: Manages GPU buffers and resources for compute operations
§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:
- Input Texture: Contains the result from previous rendering or compute pass
- Output Texture: Receives the processed result from the current compute operation
- 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:
- Define your compute command struct implementing
ComputeCommand
- Create a pipeline struct implementing
ComputablePipeline<YourCommand>
- Write a compute shader in WGSL
- 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§
- Compute
Pipeline Registry - Registry for managing and dispatching compute pipelines.
Traits§
- Computable
Pipeline - Core trait for implementing GPU compute pipelines.