tenferro_gpu/webgpu/exec_session.rs
1use tenferro_tensor::backend::{
2 BackendSession, BackendSessionHost, ElementwiseReadOp, SessionCachedDot, TensorAnalytic,
3 TensorBuffer, TensorDeviceTransfer, TensorDot, TensorElementwise, TensorFusion, TensorIndexing,
4 TensorReduction, TensorStructural,
5};
6use tenferro_tensor::config::{
7 CompareDir, DotGeneralConfig, GatherConfig, PadConfig, ScatterConfig, SliceConfig,
8};
9use tenferro_tensor::DType;
10use tenferro_tensor::{with_session_entry_guard, Tensor, TensorRead, TensorWrite};
11
12use super::{
13 gemm, structural, unsupported, unsupported_op, WebGpuBackend, WebGpuRuntime,
14 WebGpuRuntimeIdentity,
15};
16
17/// Native-session marker for [`WebGpuExecSession`]; private to this crate so no other
18/// crate can create a token that claims to be this session.
19pub(super) struct WebGpuExecSessionMarker;
20
21/// Borrowed WebGPU execution capability.
22#[doc(hidden)]
23#[derive(Debug)]
24pub struct WebGpuExecSession<'a> {
25 backend: &'a mut WebGpuBackend,
26}
27
28// Typed view canonicalization runs on the session, never on the backend
29// owner: the owner is not an execution surface (#1946 F6).
30impl tenferro_tensor::TensorViewCanonicalization<f32, tenferro_tensor::DynRank>
31 for WebGpuExecSession<'_>
32{
33 fn to_contiguous(
34 &mut self,
35 view: &tenferro_tensor::TypedTensorView<'_, f32>,
36 ) -> crate::Result<tenferro_tensor::TypedTensor<f32>> {
37 super::structural::to_contiguous_f32(self.backend, view)
38 }
39
40 fn copy_into(
41 &mut self,
42 _src: &tenferro_tensor::TypedTensorView<'_, f32>,
43 _dst: &mut tenferro_tensor::TypedTensorViewMut<'_, f32>,
44 ) -> crate::Result<()> {
45 super::unsupported!("WebGpuExecSession::copy_into")
46 }
47}
48
49impl WebGpuExecSession<'_> {
50 /// Borrow the provider runtime without exposing the owning backend.
51 #[doc(hidden)]
52 pub fn runtime(&self) -> &WebGpuRuntime {
53 self.backend.runtime()
54 }
55
56 /// Return the identity of the borrowed provider runtime.
57 #[doc(hidden)]
58 pub fn runtime_identity(&self) -> WebGpuRuntimeIdentity {
59 self.backend.runtime_identity()
60 }
61}
62
63/// Visit a WebGPU execution session through the erased backend-session surface.
64///
65/// The callback receives only the lifetime-bound session capability. The
66/// owning [`WebGpuBackend`] never crosses this boundary.
67#[doc(hidden)]
68pub fn with_webgpu_exec_session<B, R>(
69 session: &mut B,
70 f: impl for<'a> FnOnce(&'a mut WebGpuExecSession<'a>) -> R,
71) -> Option<R>
72where
73 B: BackendSession + ?Sized,
74{
75 let data = session
76 .native_session()?
77 .into_marked_ptr::<WebGpuExecSessionMarker>()?;
78 // SAFETY: only `WebGpuExecSession::native_session` creates a token with the
79 // crate-private `WebGpuExecSessionMarker`, and it points that token at a live
80 // `WebGpuExecSession`. The token borrowed `*session` exclusively, and this function
81 // keeps holding `session: &mut B` for the whole scoped visit.
82 Some(unsafe { f(data.cast::<WebGpuExecSession<'static>>().as_mut()) })
83}
84
85macro_rules! delegate {
86 ($trait:path {
87 $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) -> $ret:ty;)*
88 }) => {
89 impl $trait for WebGpuExecSession<'_> {
90 $(
91 fn $method(&mut self, $($arg: $arg_ty),*) -> $ret {
92 self.backend.$method($($arg),*)
93 }
94 )*
95 }
96 };
97}
98
99impl TensorElementwise for WebGpuExecSession<'_> {
100 fn elementwise_read_into(
101 &mut self,
102 _op: ElementwiseReadOp,
103 _inputs: &[TensorRead<'_>],
104 _out: TensorWrite<'_>,
105 ) -> crate::Result<()> {
106 unsupported!("webgpu_elementwise_read_into")
107 }
108
109 // The old chain was: owned input -> the one-shot method -> its unsupported
110 // error; view input -> the read-boundary error. WebGPU rejects the operation
111 // either way, so evaluate the read inputs first and then raise the same
112 // unsupported error. Written against the read inputs directly so the later
113 // removal of the one-shot methods does not need to revisit this.
114 fn add_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
115 let _ = tenferro_tensor::backend::read_owned_tensor("add", lhs)?;
116 let _ = tenferro_tensor::backend::read_owned_tensor("add", rhs)?;
117 unsupported!("webgpu_add")
118 }
119
120 // The old chain was: owned input -> the one-shot method -> its unsupported
121 // error; view input -> the read-boundary error. WebGPU rejects the operation
122 // either way, so evaluate the read inputs first and then raise the same
123 // unsupported error. Written against the read inputs directly so the later
124 // removal of the one-shot methods does not need to revisit this.
125 fn sub_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
126 let _ = tenferro_tensor::backend::read_owned_tensor("sub", lhs)?;
127 let _ = tenferro_tensor::backend::read_owned_tensor("sub", rhs)?;
128 unsupported!("webgpu_sub")
129 }
130
131 // The old chain was: owned input -> the one-shot method -> its unsupported
132 // error; view input -> the read-boundary error. WebGPU rejects the operation
133 // either way, so evaluate the read inputs first and then raise the same
134 // unsupported error. Written against the read inputs directly so the later
135 // removal of the one-shot methods does not need to revisit this.
136 fn mul_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
137 let _ = tenferro_tensor::backend::read_owned_tensor("mul", lhs)?;
138 let _ = tenferro_tensor::backend::read_owned_tensor("mul", rhs)?;
139 unsupported!("webgpu_mul")
140 }
141
142 // The old chain was: owned input -> the one-shot method -> its unsupported
143 // error; view input -> the read-boundary error. WebGPU rejects the operation
144 // either way, so evaluate the read inputs first and then raise the same
145 // unsupported error. Written against the read inputs directly so the later
146 // removal of the one-shot methods does not need to revisit this.
147 fn neg_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
148 let _ = tenferro_tensor::backend::read_owned_tensor("neg", input)?;
149 unsupported!("webgpu_neg")
150 }
151
152 // The old chain was: owned input -> the one-shot method -> its unsupported
153 // error; view input -> the read-boundary error. WebGPU rejects the operation
154 // either way, so evaluate the read inputs first and then raise the same
155 // unsupported error. Written against the read inputs directly so the later
156 // removal of the one-shot methods does not need to revisit this.
157 fn conj_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
158 let _ = tenferro_tensor::backend::read_owned_tensor("conj", input)?;
159 unsupported!("webgpu_conj")
160 }
161
162 // The old chain was: owned input -> the one-shot method -> its unsupported
163 // error; view input -> the read-boundary error. WebGPU rejects the operation
164 // either way, so evaluate the read inputs first and then raise the same
165 // unsupported error. Written against the read inputs directly so the later
166 // removal of the one-shot methods does not need to revisit this.
167 fn div_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
168 let _ = tenferro_tensor::backend::read_owned_tensor("div", lhs)?;
169 let _ = tenferro_tensor::backend::read_owned_tensor("div", rhs)?;
170 unsupported!("webgpu_div")
171 }
172
173 // The old chain was: owned input -> the one-shot method -> its unsupported
174 // error; view input -> the read-boundary error. WebGPU rejects the operation
175 // either way, so evaluate the read inputs first and then raise the same
176 // unsupported error. Written against the read inputs directly so the later
177 // removal of the one-shot methods does not need to revisit this.
178 fn abs_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
179 let _ = tenferro_tensor::backend::read_owned_tensor("abs", input)?;
180 unsupported!("webgpu_abs")
181 }
182
183 // The old chain was: owned input -> the one-shot method -> its unsupported
184 // error; view input -> the read-boundary error. WebGPU rejects the operation
185 // either way, so evaluate the read inputs first and then raise the same
186 // unsupported error. Written against the read inputs directly so the later
187 // removal of the one-shot methods does not need to revisit this.
188 fn sign_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
189 let _ = tenferro_tensor::backend::read_owned_tensor("sign", input)?;
190 unsupported!("webgpu_sign")
191 }
192
193 // The old chain was: owned input -> the one-shot method -> its unsupported
194 // error; view input -> the read-boundary error. WebGPU rejects the operation
195 // either way, so evaluate the read inputs first and then raise the same
196 // unsupported error. Written against the read inputs directly so the later
197 // removal of the one-shot methods does not need to revisit this.
198 fn maximum_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
199 let _ = tenferro_tensor::backend::read_owned_tensor("maximum", lhs)?;
200 let _ = tenferro_tensor::backend::read_owned_tensor("maximum", rhs)?;
201 unsupported!("webgpu_maximum")
202 }
203
204 // The old chain was: owned input -> the one-shot method -> its unsupported
205 // error; view input -> the read-boundary error. WebGPU rejects the operation
206 // either way, so evaluate the read inputs first and then raise the same
207 // unsupported error. Written against the read inputs directly so the later
208 // removal of the one-shot methods does not need to revisit this.
209 fn minimum_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
210 let _ = tenferro_tensor::backend::read_owned_tensor("minimum", lhs)?;
211 let _ = tenferro_tensor::backend::read_owned_tensor("minimum", rhs)?;
212 unsupported!("webgpu_minimum")
213 }
214
215 // The old chain was: owned input -> the one-shot method -> its unsupported
216 // error; view input -> the read-boundary error. WebGPU rejects the operation
217 // either way, so evaluate the read inputs first and then raise the same
218 // unsupported error. Written against the read inputs directly so the later
219 // removal of the one-shot methods does not need to revisit this.
220 fn compare_read(
221 &mut self,
222 lhs: TensorRead<'_>,
223 rhs: TensorRead<'_>,
224 _dir: &CompareDir,
225 ) -> crate::Result<Tensor> {
226 let _ = tenferro_tensor::backend::read_owned_tensor("compare", lhs)?;
227 let _ = tenferro_tensor::backend::read_owned_tensor("compare", rhs)?;
228 unsupported!("webgpu_compare")
229 }
230
231 // The old chain was: owned input -> the one-shot method -> its unsupported
232 // error; view input -> the read-boundary error. WebGPU rejects the operation
233 // either way, so evaluate the read inputs first and then raise the same
234 // unsupported error. Written against the read inputs directly so the later
235 // removal of the one-shot methods does not need to revisit this.
236 fn select_read(
237 &mut self,
238 pred: TensorRead<'_>,
239 on_true: TensorRead<'_>,
240 on_false: TensorRead<'_>,
241 ) -> crate::Result<Tensor> {
242 let _ = tenferro_tensor::backend::read_owned_tensor("select", pred)?;
243 let _ = tenferro_tensor::backend::read_owned_tensor("select", on_true)?;
244 let _ = tenferro_tensor::backend::read_owned_tensor("select", on_false)?;
245 unsupported!("webgpu_select")
246 }
247
248 // The old chain was: owned input -> the one-shot method -> its unsupported
249 // error; view input -> the read-boundary error. WebGPU rejects the operation
250 // either way, so evaluate the read inputs first and then raise the same
251 // unsupported error. Written against the read inputs directly so the later
252 // removal of the one-shot methods does not need to revisit this.
253 fn clamp_read(
254 &mut self,
255 input: TensorRead<'_>,
256 lower: TensorRead<'_>,
257 upper: TensorRead<'_>,
258 ) -> crate::Result<Tensor> {
259 let _ = tenferro_tensor::backend::read_owned_tensor("clamp", input)?;
260 let _ = tenferro_tensor::backend::read_owned_tensor("clamp", lower)?;
261 let _ = tenferro_tensor::backend::read_owned_tensor("clamp", upper)?;
262 unsupported!("webgpu_clamp")
263 }
264}
265
266impl TensorAnalytic for WebGpuExecSession<'_> {
267 // The old chain was: owned input -> the one-shot method -> its unsupported
268 // error; view input -> the read-boundary error. WebGPU rejects the operation
269 // either way, so evaluate the read input first and then raise the same
270 // unsupported error. Written against the read input directly so the later
271 // removal of the one-shot methods does not need to revisit this.
272 fn exp_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
273 let _ = tenferro_tensor::backend::read_owned_tensor("exp", input)?;
274 unsupported!("webgpu_exp")
275 }
276
277 // The old chain was: owned input -> the one-shot method -> its unsupported
278 // error; view input -> the read-boundary error. WebGPU rejects the operation
279 // either way, so evaluate the read input first and then raise the same
280 // unsupported error. Written against the read input directly so the later
281 // removal of the one-shot methods does not need to revisit this.
282 fn log_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
283 let _ = tenferro_tensor::backend::read_owned_tensor("log", input)?;
284 unsupported!("webgpu_log")
285 }
286
287 // The old chain was: owned input -> the one-shot method -> its unsupported
288 // error; view input -> the read-boundary error. WebGPU rejects the operation
289 // either way, so evaluate the read input first and then raise the same
290 // unsupported error. Written against the read input directly so the later
291 // removal of the one-shot methods does not need to revisit this.
292 fn sin_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
293 let _ = tenferro_tensor::backend::read_owned_tensor("sin", input)?;
294 unsupported!("webgpu_sin")
295 }
296
297 // The old chain was: owned input -> the one-shot method -> its unsupported
298 // error; view input -> the read-boundary error. WebGPU rejects the operation
299 // either way, so evaluate the read input first and then raise the same
300 // unsupported error. Written against the read input directly so the later
301 // removal of the one-shot methods does not need to revisit this.
302 fn cos_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
303 let _ = tenferro_tensor::backend::read_owned_tensor("cos", input)?;
304 unsupported!("webgpu_cos")
305 }
306
307 // The old chain was: owned input -> the one-shot method -> its unsupported
308 // error; view input -> the read-boundary error. WebGPU rejects the operation
309 // either way, so evaluate the read input first and then raise the same
310 // unsupported error. Written against the read input directly so the later
311 // removal of the one-shot methods does not need to revisit this.
312 fn tanh_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
313 let _ = tenferro_tensor::backend::read_owned_tensor("tanh", input)?;
314 unsupported!("webgpu_tanh")
315 }
316
317 // The old chain was: owned input -> the one-shot method -> its unsupported
318 // error; view input -> the read-boundary error. WebGPU rejects the operation
319 // either way, so evaluate the read input first and then raise the same
320 // unsupported error. Written against the read input directly so the later
321 // removal of the one-shot methods does not need to revisit this.
322 fn sqrt_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
323 let _ = tenferro_tensor::backend::read_owned_tensor("sqrt", input)?;
324 unsupported!("webgpu_sqrt")
325 }
326
327 // The old chain was: owned input -> the one-shot method -> its unsupported
328 // error; view input -> the read-boundary error. WebGPU rejects the operation
329 // either way, so evaluate the read input first and then raise the same
330 // unsupported error. Written against the read input directly so the later
331 // removal of the one-shot methods does not need to revisit this.
332 fn rsqrt_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
333 let _ = tenferro_tensor::backend::read_owned_tensor("rsqrt", input)?;
334 unsupported!("webgpu_rsqrt")
335 }
336
337 // See the unary analytic read halves above: evaluate the read inputs and then
338 // raise the same unsupported error.
339 fn pow_read(
340 &mut self,
341 lhs: tenferro_tensor::TensorRead<'_>,
342 rhs: tenferro_tensor::TensorRead<'_>,
343 ) -> crate::Result<Tensor> {
344 let _ = tenferro_tensor::backend::read_owned_tensor("pow", lhs)?;
345 let _ = tenferro_tensor::backend::read_owned_tensor("pow", rhs)?;
346 unsupported!("webgpu_pow")
347 }
348
349 // The old chain was: owned input -> the one-shot method -> its unsupported
350 // error; view input -> the read-boundary error. WebGPU rejects the operation
351 // either way, so evaluate the read input first and then raise the same
352 // unsupported error. Written against the read input directly so the later
353 // removal of the one-shot methods does not need to revisit this.
354 fn expm1_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
355 let _ = tenferro_tensor::backend::read_owned_tensor("expm1", input)?;
356 unsupported!("webgpu_expm1")
357 }
358
359 // The old chain was: owned input -> the one-shot method -> its unsupported
360 // error; view input -> the read-boundary error. WebGPU rejects the operation
361 // either way, so evaluate the read input first and then raise the same
362 // unsupported error. Written against the read input directly so the later
363 // removal of the one-shot methods does not need to revisit this.
364 fn log1p_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
365 let _ = tenferro_tensor::backend::read_owned_tensor("log1p", input)?;
366 unsupported!("webgpu_log1p")
367 }
368
369 fn erf_read(&mut self, input: tenferro_tensor::TensorRead<'_>) -> crate::Result<Tensor> {
370 let _ = tenferro_tensor::backend::read_owned_tensor("erf", input)?;
371 unsupported!("webgpu_erf")
372 }
373}
374
375impl TensorStructural for WebGpuExecSession<'_> {
376 fn to_contiguous_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
377 structural::to_contiguous_read(self.backend, input)
378 }
379
380 fn copy_read_into(&mut self, _src: TensorRead<'_>, _dst: TensorWrite<'_>) -> crate::Result<()> {
381 unsupported!("WebGpuBackend::copy_read_into")
382 }
383
384 fn cast(&mut self, _input: &Tensor, _to: DType) -> crate::Result<Tensor> {
385 unsupported!("webgpu_cast")
386 }
387
388 fn extract_diagonal(
389 &mut self,
390 _input: &Tensor,
391 _axis_a: usize,
392 _axis_b: usize,
393 ) -> crate::Result<Tensor> {
394 unsupported!("webgpu_extract_diagonal")
395 }
396
397 fn embed_diagonal(
398 &mut self,
399 _input: &Tensor,
400 _axis_a: usize,
401 _axis_b: usize,
402 ) -> crate::Result<Tensor> {
403 unsupported!("webgpu_embed_diagonal")
404 }
405
406 fn tril(&mut self, _input: &Tensor, _k: i64) -> crate::Result<Tensor> {
407 unsupported!("webgpu_tril")
408 }
409
410 fn triu(&mut self, _input: &Tensor, _k: i64) -> crate::Result<Tensor> {
411 unsupported!("webgpu_triu")
412 }
413
414 // The old chain was: owned input -> the one-shot method -> its unsupported
415 // error; view input -> the read-boundary error. WebGPU rejects the operation
416 // either way, so evaluate the read input first and then raise the same
417 // unsupported error. Written against the read input directly so the later
418 // removal of the one-shot methods does not need to revisit this.
419 fn transpose_read(&mut self, input: TensorRead<'_>, perm: &[usize]) -> crate::Result<Tensor> {
420 // The one-shot entry used to run the device transpose; the read half is
421 // now that entry, so it must keep the operation rather than report it
422 // unsupported.
423 let input = tenferro_tensor::backend::read_owned_tensor("transpose", input)?;
424 structural::transpose(self.backend, input, perm)
425 }
426
427 // The old chain was: owned input -> the one-shot method -> its unsupported
428 // error; view input -> the read-boundary error. WebGPU rejects the operation
429 // either way, so evaluate the read input first and then raise the same
430 // unsupported error. Written against the read input directly so the later
431 // removal of the one-shot methods does not need to revisit this.
432 fn reshape_read(&mut self, input: TensorRead<'_>, _shape: &[usize]) -> crate::Result<Tensor> {
433 let _ = tenferro_tensor::backend::read_owned_tensor("reshape", input)?;
434 unsupported!("webgpu_reshape")
435 }
436
437 // The old chain was: owned input -> the one-shot method -> its unsupported
438 // error; view input -> the read-boundary error. WebGPU rejects the operation
439 // either way, so evaluate the read input first and then raise the same
440 // unsupported error. Written against the read input directly so the later
441 // removal of the one-shot methods does not need to revisit this.
442 fn broadcast_in_dim_read(
443 &mut self,
444 input: TensorRead<'_>,
445 _shape: &[usize],
446 _dims: &[usize],
447 ) -> crate::Result<Tensor> {
448 let _ = tenferro_tensor::backend::read_owned_tensor("broadcast_in_dim", input)?;
449 unsupported!("webgpu_broadcast_in_dim")
450 }
451}
452
453impl TensorReduction for WebGpuExecSession<'_> {
454 // The old chain was: owned input -> the one-shot method -> its unsupported
455 // error; view input -> the read-boundary error. WebGPU rejects the reduction
456 // either way, so evaluate the read input first and then raise the same
457 // unsupported error. Written against the read input directly so the later
458 // removal of the one-shot methods does not need to revisit this.
459 fn reduce_sum_read(&mut self, input: TensorRead<'_>, _axes: &[usize]) -> crate::Result<Tensor> {
460 let _ = tenferro_tensor::backend::read_owned_tensor("reduce_sum", input)?;
461 unsupported!("webgpu_reduce_sum")
462 }
463 // The old chain was: owned input -> the one-shot method -> its unsupported
464 // error; view input -> the read-boundary error. WebGPU rejects the reduction
465 // either way, so evaluate the read input first and then raise the same
466 // unsupported error. Written against the read input directly so the later
467 // removal of the one-shot methods does not need to revisit this.
468 fn reduce_prod_read(
469 &mut self,
470 input: TensorRead<'_>,
471 _axes: &[usize],
472 ) -> crate::Result<Tensor> {
473 let _ = tenferro_tensor::backend::read_owned_tensor("reduce_prod", input)?;
474 unsupported!("webgpu_reduce_prod")
475 }
476 // The old chain was: owned input -> the one-shot method -> its unsupported
477 // error; view input -> the read-boundary error. WebGPU rejects the reduction
478 // either way, so evaluate the read input first and then raise the same
479 // unsupported error. Written against the read input directly so the later
480 // removal of the one-shot methods does not need to revisit this.
481 fn reduce_max_read(&mut self, input: TensorRead<'_>, _axes: &[usize]) -> crate::Result<Tensor> {
482 let _ = tenferro_tensor::backend::read_owned_tensor("reduce_max", input)?;
483 unsupported!("webgpu_reduce_max")
484 }
485 // The old chain was: owned input -> the one-shot method -> its unsupported
486 // error; view input -> the read-boundary error. WebGPU rejects the reduction
487 // either way, so evaluate the read input first and then raise the same
488 // unsupported error. Written against the read input directly so the later
489 // removal of the one-shot methods does not need to revisit this.
490 fn reduce_min_read(&mut self, input: TensorRead<'_>, _axes: &[usize]) -> crate::Result<Tensor> {
491 let _ = tenferro_tensor::backend::read_owned_tensor("reduce_min", input)?;
492 unsupported!("webgpu_reduce_min")
493 }
494}
495
496impl TensorDot for WebGpuExecSession<'_> {
497 // The previous read-half default delegated an owned pair to the one-shot
498 // method and materialized views through to_contiguous_read before
499 // contracting. That default is inlined here so the later removal of the
500 // one-shot methods does not need to revisit WebGPU.
501 fn dot_general_read(
502 &mut self,
503 lhs: TensorRead<'_>,
504 rhs: TensorRead<'_>,
505 config: &DotGeneralConfig,
506 ) -> crate::Result<Tensor> {
507 match (lhs.as_tensor(), rhs.as_tensor()) {
508 (Some(lhs), Some(rhs)) => gemm::dot_general(self.backend, lhs, rhs, config),
509 _ => {
510 let lhs = self.to_contiguous_read(lhs)?;
511 let rhs = self.to_contiguous_read(rhs)?;
512 gemm::dot_general(self.backend, &lhs, &rhs, config)
513 }
514 }
515 }
516
517 fn dot_general_with_conj(
518 &mut self,
519 lhs: &Tensor,
520 rhs: &Tensor,
521 config: &DotGeneralConfig,
522 lhs_conj: bool,
523 rhs_conj: bool,
524 ) -> crate::Result<Tensor> {
525 gemm::dot_general_with_conj(self.backend, lhs, rhs, config, lhs_conj, rhs_conj)
526 }
527}
528
529impl TensorIndexing for WebGpuExecSession<'_> {
530 fn gather(
531 &mut self,
532 _operand: &Tensor,
533 _start_indices: &Tensor,
534 _config: &GatherConfig,
535 ) -> crate::Result<Tensor> {
536 unsupported!("webgpu_gather")
537 }
538
539 fn scatter(
540 &mut self,
541 _operand: &Tensor,
542 _scatter_indices: &Tensor,
543 _updates: &Tensor,
544 _config: &ScatterConfig,
545 ) -> crate::Result<Tensor> {
546 unsupported!("webgpu_scatter")
547 }
548
549 fn slice(&mut self, _input: &Tensor, _config: &SliceConfig) -> crate::Result<Tensor> {
550 unsupported!("webgpu_slice")
551 }
552
553 fn dynamic_slice(
554 &mut self,
555 _input: &Tensor,
556 _starts: &Tensor,
557 _slice_sizes: &[usize],
558 ) -> crate::Result<Tensor> {
559 unsupported!("webgpu_dynamic_slice")
560 }
561
562 fn dynamic_update_slice(
563 &mut self,
564 _operand: &Tensor,
565 _update: &Tensor,
566 _starts: &Tensor,
567 ) -> crate::Result<Tensor> {
568 unsupported!("webgpu_dynamic_update_slice")
569 }
570
571 fn pad(&mut self, _input: &Tensor, _config: &PadConfig) -> crate::Result<Tensor> {
572 unsupported!("webgpu_pad")
573 }
574
575 fn concatenate(&mut self, _inputs: &[&Tensor], _axis: usize) -> crate::Result<Tensor> {
576 unsupported!("webgpu_concatenate")
577 }
578
579 fn reverse(&mut self, _input: &Tensor, _axes: &[usize]) -> crate::Result<Tensor> {
580 unsupported!("webgpu_reverse")
581 }
582}
583
584impl TensorFusion for WebGpuExecSession<'_> {}
585
586// WebGPU device buffers return to the runtime allocator on drop, so the
587// session keeps the trait's no-op reclaim; the owner is not a buffer surface.
588impl TensorBuffer for WebGpuExecSession<'_> {}
589
590delegate!(TensorDeviceTransfer {
591 fn download_to_host(tensor: TensorRead<'_>) -> crate::Result<Tensor>;
592 fn upload_host_tensor(tensor: TensorRead<'_>) -> crate::Result<Tensor>;
593});
594
595// The cached reads are the trait's provided defaults here: WebGPU owns no
596// runtime cache of its own, so the session implements them directly.
597impl SessionCachedDot for WebGpuExecSession<'_> {}
598
599impl BackendSession for WebGpuExecSession<'_> {
600 fn native_session(&mut self) -> Option<tenferro_tensor::NativeSessionRef<'_>> {
601 // SAFETY: `WebGpuExecSessionMarker` is private to this crate, and this is the only
602 // place a token carrying it is created; it always points to a
603 // `WebGpuExecSession`, exclusively borrowed for the token lifetime.
604 Some(unsafe { tenferro_tensor::NativeSessionRef::new::<WebGpuExecSessionMarker, _>(self) })
605 }
606}
607
608impl BackendSessionHost for WebGpuBackend {
609 fn with_backend_session<R: Send>(
610 &mut self,
611 f: impl FnOnce(&mut dyn BackendSession) -> R + Send,
612 ) -> Result<R, tenferro_tensor::SessionEntryError> {
613 let mut session = WebGpuExecSession { backend: self };
614 // The portable in-session guard rejects nested entry before `f` runs;
615 // the WebGPU runtime must never re-enter a session closure.
616 with_session_entry_guard("WebGpuBackend", || f(&mut session))
617 }
618}