ruda_driver_wgpu/backend/base/
pipeline.rs1use super::*;
2
3impl WgpuServer {
4 #[allow(
8 clippy::type_complexity,
9 reason = "required because of error propagation"
10 )]
11 #[allow(unused_variables)]
12 pub fn load_cached_pipeline(
13 &self,
14 kernel_id: &KernelId,
15 bindings: &KernelArguments,
16 mode: ExecutionMode,
17 ) -> Result<Option<Result<Arc<ComputePipeline>, (u64, StableHash)>>, CompilationError> {
18 #[cfg(not(feature = "spirv"))]
19 let res = Ok(None);
20 #[cfg(feature = "spirv")]
21 let res = if let Some(cache) = &self.spirv_cache {
22 let key = (self.utilities.properties_hash, kernel_id.stable_hash());
23 if let Some(entry) = cache.get(&key) {
24 log::trace!("Using SPIR-V cache");
25
26 let repr = AutoRepresentationRef::SpirV(&entry.kernel);
27 let module = self.create_module(&entry.entrypoint_name, Some(repr), "", mode)?;
28 let pipeline =
29 self.create_pipeline(&entry.entrypoint_name, Some(repr), module, bindings);
30 Ok(Some(Ok(pipeline)))
31 } else {
32 Ok(Some(Err(key)))
33 }
34 } else {
35 Ok(None)
36 };
37
38 res
39 }
40
41 pub fn create_module(
42 &self,
43 entrypoint_name: &str,
44 repr: Option<AutoRepresentationRef<'_>>,
45 source: &str,
46 mode: ExecutionMode,
47 ) -> Result<ShaderModule, CompilationError> {
48 match repr {
49 #[cfg(feature = "spirv")]
50 Some(AutoRepresentationRef::SpirV(repr)) => unsafe {
51 Ok(self.device.create_shader_module_passthrough(
52 wgpu::ShaderModuleDescriptorPassthrough {
53 label: Some(entrypoint_name),
54 spirv: Some(Cow::Borrowed(&repr.assembled_module)),
55 ..Default::default()
56 },
57 ))
58 },
59 #[cfg(all(feature = "msl", target_os = "macos"))]
60 Some(AutoRepresentationRef::Msl(repr)) => unsafe {
61 Ok(self.device.create_shader_module_passthrough(
62 wgpu::ShaderModuleDescriptorPassthrough {
63 label: Some(entrypoint_name),
64 msl: Some(Cow::Borrowed(source)),
65 num_workgroups: (repr.ruda_dim.x, repr.ruda_dim.y, repr.ruda_dim.z),
66 ..Default::default()
67 },
68 ))
69 },
70 _ => {
71 let checks = wgpu::ShaderRuntimeChecks {
72 bounds_checks: false,
76 force_loop_bounding: mode == ExecutionMode::Checked,
78 ..wgpu::ShaderRuntimeChecks::unchecked()
79 };
80
81 log::trace!("[ruda-driver-wgpu] compiling WGSL module `{entrypoint_name}`\n{source}");
82
83 let error_scope = self.device.push_error_scope(wgpu::ErrorFilter::Validation);
84
85 let module = unsafe {
88 self.device.create_shader_module_trusted(
89 ShaderModuleDescriptor {
90 label: Some(entrypoint_name),
91 source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(source)),
92 },
93 checks,
94 )
95 };
96
97 let err_future = error_scope.pop();
100
101 #[cfg(not(target_family = "wasm"))]
102 if let Some(err) = ruda_core::future::block_on(err_future) {
103 log::error!(
104 "[ruda-driver-wgpu] WGSL compilation failed for kernel `{entrypoint_name}`:\n{err}\n--- shader source ({} bytes) ---\n{source}\n--- end shader ---",
105 source.len()
106 );
107 return Err(CompilationError::Generic {
108 reason: format!(
109 "WGSL compilation failed for kernel `{entrypoint_name}`: {err}"
110 ),
111 backtrace: ruda_core::backtrace::BackTrace::capture(),
112 });
113 }
114
115 #[cfg(target_family = "wasm")]
118 {
119 let entrypoint_name = entrypoint_name.to_string();
120 let source = source.to_string();
121 wasm_bindgen_futures::spawn_local(async move {
122 if let Some(err) = err_future.await {
123 log::error!(
124 "[ruda-driver-wgpu] WGSL compilation failed for kernel `{entrypoint_name}`:\n{err}\n--- shader source ({} bytes) ---\n{source}\n--- end shader ---",
125 source.len()
126 );
127 }
128 });
129 }
130
131 Ok(module)
132 }
133 }
134 }
135
136 #[allow(unused_variables)]
137 pub fn create_pipeline(
138 &self,
139 entrypoint_name: &str,
140 repr: Option<AutoRepresentationRef<'_>>,
141 module: ShaderModule,
142 bindings: &KernelArguments,
143 ) -> Arc<ComputePipeline> {
144 let bindings_info = match repr {
145 Some(AutoRepresentationRef::Wgsl(repr)) => Some(wgsl::bindings(repr, bindings)),
146 #[cfg(all(feature = "msl", target_os = "macos"))]
147 Some(AutoRepresentationRef::Msl(repr)) => Some(cpp_metal::bindings(repr, bindings)),
148 #[cfg(feature = "spirv")]
149 Some(AutoRepresentationRef::SpirV(repr)) => Some(vulkan::bindings(repr, bindings)),
150 _ => None,
151 };
152
153 let layout = bindings_info.map(|bindings| {
154 let (mut bindings, info, uniform_info) = bindings;
155 if !cfg!(exclusive_memory_only) {
158 bindings.fill(ruda::runtime::kernel::Visibility::ReadWrite);
159 }
160
161 let info = info.map(|_| match uniform_info {
162 true => BufferBindingType::Uniform,
163 false => BufferBindingType::Storage { read_only: true },
164 });
165
166 let bindings = bindings
167 .into_iter()
168 .map(|visibility| BufferBindingType::Storage {
169 read_only: matches!(visibility, ruda::runtime::kernel::Visibility::Read),
170 })
171 .chain(info)
172 .enumerate()
173 .map(|(i, ty)| BindGroupLayoutEntry {
174 binding: i as u32,
175 visibility: ShaderStages::COMPUTE,
176 ty: BindingType::Buffer {
177 ty,
178 has_dynamic_offset: false,
179 min_binding_size: None,
180 },
181 count: None,
182 })
183 .collect::<Vec<_>>();
184 let layout = self
185 .device
186 .create_bind_group_layout(&BindGroupLayoutDescriptor {
187 label: None,
188 entries: &bindings,
189 });
190 self.device
191 .create_pipeline_layout(&PipelineLayoutDescriptor {
192 label: None,
193 bind_group_layouts: &[Some(&layout)],
194 immediate_size: 0,
195 })
196 });
197
198 let pipeline = self
199 .device
200 .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
201 label: Some(entrypoint_name),
202 layout: layout.as_ref(),
203 module: &module,
204 entry_point: Some(entrypoint_name),
205 compilation_options: wgpu::PipelineCompilationOptions {
206 zero_initialize_workgroup_memory: false,
207 ..Default::default()
208 },
209 cache: None,
210 });
211 Arc::new(pipeline)
212 }
213}