| name | titan-compute-pipeline-pattern |
| description | Canonical pattern for creating a new compute pipeline in Titan stratum-lighting — include_str WGSL, BGL entries, pipeline layout with Some(&bgl) and immediate_size:0, compute pipeline with cache:None |
| source | auto-skill |
| extracted_at | 2026-06-15T04:29:18.101Z |
Titan compute pipeline creation pattern
Every new compute pass in titan-stratum-lighting follows this canonical
pattern. Do not deviate — incorrect BGL entries, missing Some(), or wrong
pipeline layout fields produce compile errors that are hard to diagnose.
1. WGSL shader loading
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("my_pass.wgsl"),
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/my_pass.wgsl").into()),
});
Shaders live in crates/titan-stratum-lighting/src/shaders/. No imports —
each file is self-contained (see wgsl-no-imports skill).
2. Bind group layout entries
Use these helpers (define in the module if not already present):
fn bgl_uniform(binding: u32) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding, visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false, min_binding_size: None,
},
count: None,
}
}
fn bgl_texture_f32(binding: u32, filterable: bool) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding, visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable },
view_dimension: wgpu::TextureViewDimension::D2, multisampled: false,
},
count: None,
}
}
fn bgl_storage_rgba16f(binding: u32) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding, visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::StorageTexture {
access: wgpu::StorageTextureAccess::WriteOnly,
format: wgpu::TextureFormat::Rgba16Float,
view_dimension: wgpu::TextureViewDimension::D2,
},
count: None,
}
}
3. Pipeline layout — CRITICAL
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("my_pass"),
bind_group_layouts: &[Some(&io_bgl), Some(¶m_bgl)],
immediate_size: 0,
});
Wgpu 29 specifics:
bind_group_layouts elements are Option<&BindGroupLayout> — wrap each in Some()
immediate_size replaces the removed push_constant_ranges
4. Compute pipeline
let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("my_pass"),
layout: Some(&layout),
module: &shader,
entry_point: Some("main"),
compilation_options: wgpu::PipelineCompilationOptions::default(),
cache: None,
});
5. Dispatch
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: Some("my_pass"), timestamp_writes: None,
});
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &io_bg, &[]);
pass.set_bind_group(1, ¶m_bg, &[]);
pass.dispatch_workgroups(div_ceil(w, 8), div_ceil(h, 8), 1);
6. UBO structs
Use #[repr(C)] + bytemuck::Pod + Zeroable:
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct MyUbo { field: f32, _pad: [f32; 3] }
queue.write_buffer(&self.ubo, 0, bytemuck::bytes_of(&ubo));
Reference
See crates/titan-stratum-lighting/src/preblur.rs for the canonical
implementation that was used as the template.