edgefirst-tensor 0.33.0

Zero-copy tensor memory management with DMA, shared memory, and heap backends
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
// SPDX-FileCopyrightText: Copyright 2026 Au-Zone Technologies
// SPDX-License-Identifier: Apache-2.0

//! `dynamic`'s `Tensor<T>` — a typed lens over [`TensorDyn`], not a second
//! storage-owning type.
//!
//! `static`'s `Tensor<T>` (in `crate::lib` proper) owns real per-backend
//! storage; `TensorDyn` there is an eleven-variant enum boxing one. Under
//! `dynamic` there is no such enum -- `TensorDyn` already *is* the one
//! handle -- so this `Tensor<T>` carries no storage of its own either. It
//! exists only to attach a compile-time-known element type to a `TensorDyn`
//! value, and every method here either forwards straight to the identical
//! method [`TensorDyn`] (`dynamic_backend.rs`) already implements, or drives
//! `edgefirst-tensor-ffi` directly for the handful of operations that need
//! `T::DTYPE` (allocation, import) or a typed view (`map_with`).
//!
//! # `#[repr(transparent)]` and why the cast in `lens.rs` is sound here
//!
//! This struct has exactly one field with nonzero size -- `inner:
//! TensorDyn` -- plus a zero-sized `PhantomData` marker, so
//! `#[repr(transparent)]` gives it the identical layout of `TensorDyn`
//! itself, whatever `TensorDyn`'s own fields happen to be (see
//! `dynamic_backend.rs`'s module docs: it is a handle plus two cached
//! facts, not a bare pointer). That is what makes `&TensorDyn as *const
//! Tensor<T>` sound in `lens.rs`'s `dynamic` `as_typed`/`as_typed_mut` --
//! contrast `static`, where `TensorDyn` is a tagged-union `enum` with
//! eleven differently-typed payloads and the same cast would be UB (see
//! `lens.rs`'s `static_lens` module docs, which uses `Any::downcast_ref`
//! instead for exactly that reason). Same source-level idea -- "borrow the
//! erased handle as a typed one" -- different validity argument per
//! backend, because the two `TensorDyn`s have different actual layouts.
use std::fmt;
use std::marker::PhantomData;

use half::f16;

use crate::lens::Element;
use crate::{
    BufferIdentity, Colorimetry, CpuAccess, DType, Error, IntegerType, PixelFormat, Quantization,
    Region, Result, TensorDyn, TensorMapTrait, TensorMemory, TensorTrait, ViewOrigin,
};

/// The dynamic backend's `Tensor<T>`. See the module docs.
#[repr(transparent)]
pub struct Tensor<T: Element> {
    inner: TensorDyn,
    // `fn() -> T`, not `T`: this tensor does not own a `T` (it owns a
    // type-erased handle `T::DTYPE` describes), so the marker should not
    // tie `Tensor<T>`'s variance or auto-trait derivation to `T`'s own --
    // `Element` already requires `Send + Sync + 'static` directly, so this
    // costs nothing and avoids implying a relationship that isn't there.
    _marker: PhantomData<fn() -> T>,
}

impl<T: Element> Tensor<T> {
    fn from_inner(inner: TensorDyn) -> Self {
        Tensor {
            inner,
            _marker: PhantomData,
        }
    }

    /// Create a new tensor with the given shape, optional memory backing,
    /// and optional name. Same signature as `static`'s inherent `new` (the
    /// wider constructor `TensorTrait::new` delegates to) -- kept identical
    /// so shared call sites like [`crate::is_dma_available`] need no
    /// backend-specific branch.
    pub fn new(shape: &[usize], memory: Option<TensorMemory>, name: Option<&str>) -> Result<Self> {
        TensorDyn::new(shape, T::DTYPE, memory, name).map(Self::from_inner)
    }

    /// Import an existing buffer as a tensor, taking ownership of its file
    /// descriptor. No bytes are copied.
    #[cfg(unix)]
    pub fn from_fd(fd: std::os::fd::OwnedFd, shape: &[usize], name: Option<&str>) -> Result<Self> {
        TensorDyn::from_fd(fd, shape, T::DTYPE, name).map(Self::from_inner)
    }

    /// Construct a tensor from a row-major element slice + shape. Allocates
    /// a new buffer (`TensorMemory::Mem`) and memcpys the contents --
    /// derived over [`Self::new`] plus [`TensorTrait::map`], identical
    /// logic to `static`'s `Tensor::from_slice` (`lib.rs`); no primitive
    /// needed beyond what construction and mapping already cover.
    ///
    /// # Errors
    ///
    /// - [`Error::InvalidShape`] if `values.len() != shape.iter().product()`.
    /// - Propagates any allocation error from [`Self::new`].
    pub fn from_slice(values: &[T], shape: &[usize]) -> Result<Self>
    where
        T: Copy,
    {
        let expected: usize = shape.iter().product();
        if values.len() != expected {
            return Err(Error::InvalidShape(format!(
                "from_slice: values.len()={} but shape product={expected} (shape={shape:?})",
                values.len()
            )));
        }
        let t = Self::new(shape, Some(TensorMemory::Mem), None)?;
        {
            let mut m = TensorTrait::map(&t)?;
            m.as_mut_slice().copy_from_slice(values);
        }
        Ok(t)
    }

    /// Construct a tensor from a 3-D ndarray view. Respects strides -- one
    /// copy in all cases; contiguous views take a memcpy fast path. Derived
    /// the same way as [`Self::from_slice`] -- see its docs.
    ///
    /// Only available when the `ndarray` feature is enabled.
    #[cfg(feature = "ndarray")]
    pub fn from_arrayview3(view: ndarray::ArrayView3<'_, T>) -> Result<Self>
    where
        T: Copy,
    {
        let (h, w, c) = view.dim();
        let t = Self::new(&[h, w, c], Some(TensorMemory::Mem), None)?;
        {
            let mut m = TensorTrait::map(&t)?;
            let dst = m.as_mut_slice();
            if let Some(src) = view.as_slice() {
                dst.copy_from_slice(src);
            } else {
                for (d, &s) in dst.iter_mut().zip(view.iter()) {
                    *d = s;
                }
            }
        }
        Ok(t)
    }

    /// Runtime element type discriminant. Always `T::DTYPE` for a live
    /// lens, but sourced from the handle (`ef_tensor_dtype`) rather than
    /// the constant, so a lens opened by [`TensorDyn::as_typed`] reports
    /// what the handle itself carries.
    pub fn dtype(&self) -> DType {
        self.inner.dtype()
    }

    /// Whether this semi-planar tensor was assembled from separate
    /// luma/chroma allocations (`from_planes`), as opposed to one
    /// contiguous combined-plane buffer. Answered from the C handle
    /// (`ef_tensor_is_multiplane`), so a tensor wrapped via `from_raw`
    /// after `ef_tensor_from_planes` reports the same as this crate's
    /// own `from_planes` wrapper.
    pub fn is_multiplane(&self) -> bool {
        self.inner.is_multiplane()
    }

    /// The chroma plane, if this is a two-allocation semi-planar tensor.
    /// Borrowed from a cached lens over the retained `ef_tensor_chroma`
    /// handle, so native handles borrowed from it remain valid for this
    /// parent's lifetime.
    pub fn chroma(&self) -> Option<&Self> {
        self.inner.chroma()?.as_typed::<T>()
    }

    // `as_dma` is deliberately ABSENT on this backend, where the static one
    // has it (`lib.rs`). It used to exist here returning `None`
    // unconditionally, on the reasoning that `DmaTensor<T>` is
    // `static`-backend-internal storage this backend never constructs, that
    // the return type matched, and that callers already handle `None`.
    //
    // Every part of that was true and the conclusion was still wrong. The
    // callers handled `None` by *declining*: `crates/image`'s EGLImage
    // import, its Path-B R8 import, `import_buffer_packed`, and all four
    // G2D sites refused a genuinely DMA-backed tensor and fell back to a
    // slower path. Nothing failed, nothing logged, and the suite stayed
    // green because both backends still produced correct pixels -- so a
    // user on i.MX lost the zero-copy hardware path with nothing reporting
    // it. See task P2b's review, F3.
    //
    // Every one of those callers wanted exactly one thing from the
    // `DmaTensor`: its `.fd`. They now use [`Self::dmabuf`], which both
    // backends implement with the same signature. With no caller left, a
    // stub that always answers "not DMA-backed" is a trap for the next one,
    // so it is gone: a future `dynamic` caller of `as_dma` gets a compile
    // error naming the problem instead of a silent, plausible `None`.

    /// Create from separate Y and UV planes (multiplane NV12/NV16). Same
    /// signature as `static`'s `Tensor::from_planes` (`lib.rs`).
    ///
    /// Drives `ef_tensor_from_planes`, which resolves the combination
    /// through the producer side's real `static::Tensor::from_planes` --
    /// the NV12/NV16 combined-plane geometry lives there, not here.
    /// **Consumes `luma` and `chroma`** (both `TensorDyn::into_raw`'d
    /// before the call), matching `static`'s by-value signature exactly:
    /// neither is usable afterward, on any outcome.
    ///
    /// # Errors
    ///
    /// [`Error::InvalidArgument`] if the underlying call fails -- either
    /// `Tensor::from_planes`'s own validation (format/shape mismatch; see
    /// its doc comment in `lib.rs`), or one of `ef_tensor_from_planes`'s own
    /// preconditions (see its Doxygen). A dtype mismatch between `luma` and
    /// `chroma` is unreachable through this typed entry point -- both are
    /// already the same `T` at compile time, unlike the type-erased C ABI
    /// this drives.
    ///
    /// **Known residual risk, not silently hidden**: `ef_tensor_from_planes`
    /// does *not* consume `luma`/`chroma` when it rejects them before ever
    /// reaching `Tensor::from_planes` (an outstanding `ef_tensor_retain` or
    /// `ef_tensor_map` on either handle -- reachable from safe Rust here via
    /// e.g. `tensor.map_with(access)` leaving a live `'static` view while
    /// `tensor` itself is still moved into this call). This wrapper cannot
    /// distinguish that case from an ordinary consumed failure using only
    /// the `NULL` return (both look identical from here), so on **any**
    /// failure it does not attempt to reclaim `luma`/`chroma` itself --
    /// guessing wrong in either direction would be a double-free or a
    /// use-after-free. In the ordinary (consumed) failure case this is
    /// exactly correct (the C library already freed them). In the rare
    /// non-consuming rejection case, the two underlying allocations are
    /// intentionally leaked rather than risking UB -- a documented trade-off,
    /// not an oversight; leaking under a caller-created precondition
    /// violation is a bounded, honest cost next to a double-free.
    ///
    /// **Consumes `luma` and `chroma`**. Multiplane is then answered from
    /// the resulting C handle (`ef_tensor_is_multiplane` /
    /// `ef_tensor_chroma`), including after `from_raw`.
    pub fn from_planes(luma: Self, chroma: Self, format: PixelFormat) -> Result<Self> {
        let c_format = std::ffi::CString::new(format.as_str()).map_err(|e| {
            Error::InvalidArgument(format!("pixel format string contains a NUL: {e}"))
        })?;
        let luma_raw = TensorDyn::from(luma).into_raw();
        let chroma_raw = TensorDyn::from(chroma).into_raw();
        // SAFETY: both raw pointers are live, own-mint handles this
        // backend minted; ownership was just forgotten above, matching
        // `ef_tensor_from_planes`'s "consumes both inputs" contract.
        let handle = unsafe {
            edgefirst_tensor_ffi::ef_tensor_from_planes(luma_raw, chroma_raw, c_format.as_ptr())
        };
        match std::ptr::NonNull::new(handle) {
            Some(h) => {
                // SAFETY: `h` is a live handle `ef_tensor_from_planes`
                // returned as the caller-owned result.
                Ok(Self::from_inner(unsafe { TensorDyn::from_raw(h.as_ptr()) }))
            }
            None => Err(Error::InvalidArgument(format!(
                "ef_tensor_from_planes failed: {} -- see this method's doc comment for the \
                 known non-consuming-rejection case",
                crate::tensor_dyn::ffi_last_error()
            ))),
        }
    }

    /// Pixel format, if this tensor is an image (`None` otherwise).
    pub fn format(&self) -> Option<PixelFormat> {
        self.inner.format()
    }

    /// Image width in pixels (`None` if not an image tensor).
    pub fn width(&self) -> Option<usize> {
        self.inner.width()
    }

    /// Image height in pixels (`None` if not an image tensor).
    pub fn height(&self) -> Option<usize> {
        self.inner.height()
    }

    /// True if `self` and `other` reference the same underlying buffer.
    pub fn aliases(&self, other: &Self) -> bool {
        self.inner.aliases(&other.inner)
    }

    /// Colorimetry metadata (`None` = undefined; never auto-filled).
    pub fn colorimetry(&self) -> Option<Colorimetry> {
        self.inner.colorimetry()
    }

    /// Attach/clear colorimetry metadata.
    pub fn set_colorimetry(&mut self, c: Option<Colorimetry>) {
        self.inner.set_colorimetry(c)
    }

    /// Effective row stride: the padded byte pitch a stride-aware CPU
    /// reader must honor.
    pub fn effective_row_stride(&self) -> Option<usize> {
        self.inner.effective_row_stride()
    }

    /// The *recorded* row stride in bytes; `None` when the tensor is
    /// tightly packed. Deliberately distinct from
    /// [`Self::effective_row_stride`], which substitutes a computed pitch
    /// when nothing is recorded -- see [`TensorDyn::row_stride`]'s own doc
    /// comment for why the difference is load-bearing for the
    /// cross-package descriptor.
    pub fn row_stride(&self) -> Option<usize> {
        self.inner.row_stride()
    }

    /// The parent-image snapshot if this tensor is a `view`/`batch`
    /// sub-region; `None` for a whole tensor.
    pub fn view_origin(&self) -> Option<ViewOrigin> {
        self.inner.view_origin()
    }

    /// Byte offset within the DMA-BUF where image data starts (`None` = 0).
    /// Forwards to [`TensorDyn::plane_offset`], which drives the real
    /// `ef_tensor_plane_offset` primitive.
    ///
    /// **Not** plane 0's `ef_tensor_plane_at` offset -- that is
    /// `plane_table`'s intra-buffer layout, always 0 for plane 0 by
    /// construction, a different quantity from this field entirely; an
    /// earlier version of this method read that one by mistake, which
    /// always reported `Some(0)` or `None` regardless of what
    /// [`Self::set_plane_offset`] had actually been called with.
    pub fn plane_offset(&self) -> Option<usize> {
        self.inner.plane_offset()
    }

    /// Set the row stride, format-validated. Forwards to
    /// [`TensorDyn::set_row_stride`], which drives the real
    /// `ef_tensor_set_row_stride` primitive.
    pub fn set_row_stride(&mut self, stride: usize) -> Result<()> {
        self.inner.set_row_stride(stride)
    }

    /// Set the row stride without format validation. Same signature as
    /// `static`'s `Tensor::set_row_stride_unchecked` (`lib.rs`), which has
    /// no `TensorDyn`-level counterpart there either (it is a `Tensor<T>`-
    /// only raw escape hatch for sub-tensors without format metadata) --
    /// forwards to [`TensorDyn::set_row_stride_unchecked`], which drives
    /// the real `ef_tensor_set_row_stride_unchecked` primitive (task 17).
    /// This used to always panic, because no primitive backed it; that
    /// gap mattered in practice, not just in principle -- it is exactly
    /// what a multiplane chroma sub-tensor needs (see
    /// `from_planes`'s doc comments), and
    /// `edgefirst-image`'s `import_image` calls it on one.
    pub fn set_row_stride_unchecked(&mut self, stride: usize) {
        self.inner.set_row_stride_unchecked(stride)
    }

    /// Set the byte offset within the DMA-BUF where image data starts.
    /// Forwards to [`TensorDyn::set_plane_offset`], which drives the real
    /// `ef_tensor_set_plane_offset` primitive.
    pub fn set_plane_offset(&mut self, offset: usize) {
        self.inner.set_plane_offset(offset)
    }

    /// Borrow the DMA-BUF file descriptor backing this tensor.
    ///
    /// Derived from plane 0's native handle (`ef_tensor_plane_at`), the
    /// same primitive [`TensorTrait::clone_fd`] dup's -- this borrows
    /// instead of duplicating, matching `static`'s `dmabuf()` contract
    /// (`lib.rs`) of a borrow scoped to `&self`.
    ///
    /// # Errors
    ///
    /// [`Error::NotImplemented`] if this tensor is not DMA-backed, has no
    /// plane 0, or that plane carries no native fd.
    #[cfg(target_os = "linux")]
    pub fn dmabuf(&self) -> Result<std::os::fd::BorrowedFd<'_>> {
        if self.inner.memory() != TensorMemory::DmaBuf {
            return Err(Error::NotImplemented(format!(
                "dmabuf requires DMA-backed tensor, got {:?}",
                self.inner.memory()
            )));
        }
        let plane = self
            .inner
            .plane0()
            .ok_or_else(|| Error::NotImplemented("dmabuf: this tensor has no plane 0".into()))?;
        if plane.handle < 0 {
            return Err(Error::NotImplemented(
                "dmabuf: this tensor has no native fd (not DMA-backed)".into(),
            ));
        }
        // SAFETY: `plane.handle` is a valid fd owned by this tensor (via the
        // live handle behind `self.inner`) for at least as long as `self`
        // is borrowed.
        Ok(unsafe { std::os::fd::BorrowedFd::borrow_raw(plane.handle as i32) })
    }

    /// Borrow the raw `IOSurfaceRef` backing this tensor (macOS/iOS).
    #[cfg(any(target_os = "macos", target_os = "ios"))]
    pub fn iosurface_ref(&self) -> Option<*mut std::ffi::c_void> {
        self.inner.iosurface_ref()
    }

    /// Wrap a live `IOSurfaceRef` as a typed tensor (macOS/iOS).
    ///
    /// Same signature as `static`'s `Tensor::from_iosurface` so identity
    /// wrap tests compile against both backends.
    ///
    /// # Safety
    ///
    /// `surface_ref` must be a valid live `IOSurfaceRef`. `shape` must
    /// match the IOSurface's pixel dimensions and chosen element type.
    #[cfg(any(target_os = "macos", target_os = "ios"))]
    pub unsafe fn from_iosurface(
        surface_ref: *mut std::ffi::c_void,
        shape: &[usize],
        name: Option<&str>,
    ) -> Result<Self> {
        // SAFETY: caller guarantees `surface_ref` is a live IOSurfaceRef.
        unsafe { TensorDyn::from_iosurface(surface_ref, shape, T::DTYPE, name) }
            .map(Self::from_inner)
    }

    /// Physical IOSurface dimensions in texels. `None` when not IOSurface-backed.
    #[cfg(any(target_os = "macos", target_os = "ios"))]
    pub fn iosurface_physical_dims(&self) -> Option<(usize, usize)> {
        self.inner.iosurface_physical_dims()
    }

    /// The borrowed `ID3D11Texture2D*` backing this tensor, valid while the
    /// tensor lives. `None` when the tensor is not texture-backed.
    #[cfg(target_os = "windows")]
    pub fn d3d11_texture(&self) -> Option<*mut std::ffi::c_void> {
        self.inner.d3d11_texture()
    }

    /// The texture geometry the HAL chose for this image: DXGI format, texel
    /// dimensions and the matching GL internal format. `None` when the tensor
    /// is not texture-backed.
    #[cfg(target_os = "windows")]
    pub fn d3d11_layout(&self) -> Option<crate::d3d11_layout::D3d11ImageLayout> {
        self.inner.d3d11_layout()
    }

    /// A duplicated NT handle the caller owns: open it on a D3D12 device,
    /// another D3D11 device, or CUDA, or duplicate it into another process.
    /// Closing it does not affect this tensor.
    #[cfg(target_os = "windows")]
    pub fn d3d11_shared_handle(&self) -> Result<std::os::windows::io::OwnedHandle> {
        self.inner.d3d11_shared_handle()
    }

    /// The fence handle plus value a GPU consumer waits on before reading this
    /// texture, or `None` when no GPU write has been recorded on it.
    #[cfg(target_os = "windows")]
    pub fn gpu_completion(&self) -> Result<Option<crate::d3d11::GpuCompletion>> {
        self.inner.gpu_completion()
    }

    /// The fence value of the newest GPU write recorded on this tensor, or 0
    /// when there is none: the `value` of `gpu_completion` without the
    /// duplicated fence handle. 0 for a tensor that is not a D3D11 texture,
    /// and on every platform other than Windows.
    pub fn gpu_write_value(&self) -> u64 {
        self.inner.gpu_write_value()
    }

    /// Record that GPU work writing this texture completes at `value` of the
    /// process device's fence. Monotonic: an older value never displaces a
    /// newer one, so recording out of order is safe rather than merely
    /// tolerated.
    ///
    /// Takes `&self` for the reason `static`'s own `Tensor::set_gpu_write`
    /// does: the fence slot is an atomic, and a producer records a write on
    /// a tensor its consumers already hold.
    #[cfg(target_os = "windows")]
    pub fn set_gpu_write(&self, value: u64) -> Result<()> {
        self.inner.set_gpu_write(value)
    }

    /// Wrap an existing `ID3D11Texture2D` as a tensor. The texture is checked
    /// against the layout the HAL would have chosen for `format`/`width`/
    /// `height` and must live on the HAL's process device; ownership stays
    /// with the caller, since this takes its own reference.
    ///
    /// Same signature as `static`'s `Tensor::from_d3d11_texture`, so a
    /// caller compiles against either backend.
    ///
    /// # Safety
    ///
    /// `texture` must be null or a live `ID3D11Texture2D` created on the HAL
    /// device ([`crate::d3d11::device()`]).
    #[cfg(target_os = "windows")]
    pub unsafe fn from_d3d11_texture(
        texture: *mut std::ffi::c_void,
        width: usize,
        height: usize,
        format: PixelFormat,
        access: CpuAccess,
        name: Option<&str>,
    ) -> Result<Self> {
        // SAFETY: caller guarantees `texture` is null or a live texture on
        // the HAL device.
        unsafe {
            TensorDyn::from_d3d11_texture(texture, width, height, format, T::DTYPE, access, name)
        }
        .map(Self::from_inner)
    }

    /// Open a shared texture by its NT handle on the HAL's device. When
    /// `completion` is given, its fence value is waited for on the immediate
    /// context, so a same-device reader needs no further ordering. Both
    /// handles stay owned by the caller: this duplicates what it keeps.
    ///
    /// Same signature as `static`'s `Tensor::from_d3d11_shared_handle`, for
    /// the reason [`from_d3d11_texture`](Self::from_d3d11_texture) gives.
    ///
    /// # Safety
    ///
    /// `handle` must be an NT shared handle of a D3D11 texture, valid in this
    /// process, and `completion`'s handle a shared fence handle.
    #[cfg(target_os = "windows")]
    pub unsafe fn from_d3d11_shared_handle(
        handle: std::os::windows::io::RawHandle,
        width: usize,
        height: usize,
        format: PixelFormat,
        access: CpuAccess,
        completion: Option<(std::os::windows::io::RawHandle, u64)>,
        name: Option<&str>,
    ) -> Result<Self> {
        // SAFETY: caller guarantees the handles are valid in this process.
        unsafe {
            TensorDyn::from_d3d11_shared_handle(
                handle,
                width,
                height,
                format,
                T::DTYPE,
                access,
                completion,
                name,
            )
        }
        .map(Self::from_inner)
    }

    /// CUDA registration for this tensor, if any.
    ///
    /// On the dynamic lens the `CudaHandle` lives in the library, so this
    /// cannot borrow it and always returns `None` even after
    /// [`Self::set_cuda_handle`]. Use [`Self::is_cuda_attached`] or
    /// [`Self::cuda_map`] instead.
    #[deprecated(
        since = "0.32.0",
        note = "On the dynamic backend use is_cuda_attached() or cuda_map() — the CudaHandle is not borrowable through this lens."
    )]
    pub fn cuda(&self) -> Option<&crate::cuda::CudaHandle> {
        #[allow(deprecated)]
        {
            self.inner.cuda()
        }
    }

    /// Whether the library tensor carries a CUDA registration.
    pub fn is_cuda_attached(&self) -> bool {
        self.inner.is_cuda_attached()
    }

    /// See [`Self::cuda`]. `None` when the tensor carries no registration.
    pub fn cuda_map(&self) -> Option<crate::cuda::CudaMap<'_>> {
        self.inner.cuda_map()
    }

    /// See [`Self::cuda_map`]. Writable counterpart.
    pub fn cuda_map_mut(&self) -> Option<crate::cuda::CudaMap<'_>> {
        self.inner.cuda_map_mut()
    }

    /// Attach a CUDA handle via `ef_tensor_cuda_attach`.
    pub fn set_cuda_handle(&mut self, h: crate::cuda::CudaHandle) {
        self.inner.set_cuda_handle(h);
    }

    /// Construct a tensor from a PBO tensor (for GL backends that allocate
    /// PBOs).
    ///
    /// Drives `ef_tensor_wrap_pbo`: the GL buffer stays this process's, and
    /// what crosses into `libedgefirst_tensor.so` is the **callback
    /// channel** -- a frozen `EfClientState` plus the two op functions (see
    /// [`crate::PboTensor::into_client_parts`]). The library builds real
    /// `TensorStorage::Pbo` storage over it, so the returned tensor's
    /// `memory()`, `map()`, `view()` and `capacity_bytes()` are all answered
    /// by the handle rather than by a field on this side.
    ///
    /// **This allocates no host memory.** The previous implementation minted
    /// a companion `TensorMemory::Mem` handle sized to the PBO's whole shape
    /// -- ~63.3 MiB for a 4K RGBA16F surface -- purely to own something with
    /// the right metadata, and stashed the real `PboTensor` beside it. That
    /// second source of truth was dropped by every one of the thirteen
    /// `from_handle` sites, which is what made `view()` on a PBO come back
    /// as an all-zero host window (issue #162).
    ///
    /// # Errors
    /// Fails for a PBO sub-view (`view_offset != 0`), which
    /// `ef_tensor_wrap_pbo`'s argument list cannot express -- take the view
    /// on the far side instead -- and for anything the library refuses
    /// (unknown dtype, a `size` smaller than the shape needs).
    pub fn from_pbo(pbo: crate::PboTensor<T>) -> Result<Self> {
        let parts = pbo.into_client_parts()?;
        // SAFETY: `parts.state.ctx` borrows the `Arc<PboHandle>` `parts`
        // still holds, and `ef_tensor_wrap_pbo` retains it before returning,
        // so it stays live past the `drop(parts)` below. The op functions
        // are this crate's own and uphold `PboOps`'s contract.
        let inner = unsafe {
            TensorDyn::from_client_pbo(
                parts.state,
                parts.map_fn,
                parts.unmap_fn,
                parts.buffer_id,
                parts.size,
                &parts.shape,
                T::DTYPE,
            )
        }?;
        // Drops this side's own reference on the channel; the library holds
        // its own from here.
        drop(parts);
        Ok(Self::from_inner(inner))
    }

    /// The GL buffer name for a PBO-backed tensor; `None` otherwise.
    ///
    /// This and [`Self::pbo_is_mapped`] replace the borrowed
    /// `Option<&PboTensor<T>>` this lens used to hand out. There is nothing
    /// here to borrow: this backend's PBO storage lives inside
    /// `libedgefirst_tensor.so`. These two scalar facts are the entire
    /// surface `edgefirst-image`'s call sites ever read off that borrow.
    ///
    /// Note that `T` does not gate the answer. `edgefirst-image` allocates
    /// a PBO as `u8` and hands it back as an `i8` tensor, so the library's
    /// own `PboTensor`'s element type need not be this lens's `T`;
    /// `ef_tensor_pbo_id` reads the GL buffer id off the storage, which no
    /// dtype retag touches. That is what the borrow-shaped accessor needed a
    /// layout-asserted reinterpretation of `PboTensor<T>` to achieve, and
    /// what these two `Copy`-returning accessors get for free.
    pub fn pbo_id(&self) -> Option<u32> {
        self.inner.pbo_id()
    }

    /// Whether a PBO-backed tensor holds (or is establishing) a CPU
    /// mapping; `None` when the tensor is not PBO-backed. See
    /// [`Self::pbo_id`].
    pub fn pbo_is_mapped(&self) -> Option<bool> {
        self.inner.pbo_is_mapped()
    }

    /// The C-ABI `PboOpsVtable` address backing this tensor; `None` when the
    /// tensor is not PBO-backed. Every `view()` of one PBO reports the same
    /// address. See [`TensorDyn::pbo_vtable_ptr`].
    pub fn pbo_vtable_ptr(&self) -> Option<*const std::ffi::c_void> {
        self.inner.pbo_vtable_ptr()
    }

    /// Allocate an image tensor with the given geometry, memory backing,
    /// and CPU access declaration. Same signature as `static`'s
    /// `Tensor::image` (`lib.rs`), which real production code calls
    /// (`edgefirst-codec`'s V4L2 hardware JPEG decoder,
    /// `jpeg/v4l2/mod.rs`) to allocate its destination tensor.
    ///
    /// Forwards to [`TensorDyn::image`], which drives the real
    /// `ef_tensor_image_alloc` primitive -- the platform image geometry
    /// (macOS IOSurface tiling, Android AHardwareBuffer, Linux DMA-BUF
    /// 64-byte pitch alignment, odd-dimension chroma handling, `DmaBuf` →
    /// `Mem` fallback) lives in `libedgefirst_tensor.so`'s `static::Tensor::image`,
    /// not here; this does not reimplement or approximate any of it.
    pub fn image(
        width: usize,
        height: usize,
        format: PixelFormat,
        memory: Option<TensorMemory>,
        access: CpuAccess,
    ) -> Result<Self> {
        TensorDyn::image(width, height, format, T::DTYPE, memory, access).map(Self::from_inner)
    }

    /// See [`Self::image`]: same primitive, full-featured request form.
    /// Forwards to [`TensorDyn::image_desc`].
    pub fn image_desc(desc: &crate::ImageDesc) -> Result<Self> {
        TensorDyn::image_desc(desc).map(Self::from_inner)
    }

    /// See [`Self::image`]: same primitive, externally-strided form.
    /// Forwards to [`TensorDyn::image_with_stride`]. Argument order matches
    /// `static`'s `Tensor::image_with_stride` (`lib.rs`) exactly --
    /// `row_stride_bytes` before `memory`/`access` -- which real callers
    /// (`edgefirst-image`'s `gl/dma_import.rs`, `lib.rs`) already call
    /// positionally in that order.
    pub fn image_with_stride(
        width: usize,
        height: usize,
        format: PixelFormat,
        row_stride_bytes: usize,
        memory: Option<TensorMemory>,
        access: CpuAccess,
    ) -> Result<Self> {
        TensorDyn::image_with_stride(
            width,
            height,
            format,
            T::DTYPE,
            row_stride_bytes,
            memory,
            access,
        )
        .map(Self::from_inner)
    }

    /// Set this tensor's pixel format. Same signature as `static`'s
    /// `Tensor::set_format` (`lib.rs`), which real production code (e.g.
    /// `edgefirst-image`'s `gl/threaded.rs::create_pbo_image`, right after
    /// [`Self::from_pbo`]) calls directly on the typed `Tensor<T>` --
    /// missing here (only `TensorDyn::set_format`, `dynamic_backend.rs`,
    /// existed) until `image-capi` was first built against `dynamic` with
    /// `opengl` enabled surfaced the gap.
    ///
    /// Forwards to [`TensorDyn::set_format`], which drives the real
    /// `ef_tensor_set_format` primitive.
    pub fn set_format(&mut self, format: PixelFormat) -> Result<()> {
        self.inner.set_format(format)
    }

    /// Set this tensor's logical dimensions and pixel format to a decoded
    /// image, reusing the existing allocation. Same signature as `static`'s
    /// `Tensor::configure_image` (`lib.rs`), which real production code
    /// calls (`edgefirst-codec`'s JPEG decode-into-pool path) to reconfigure
    /// a pool-allocated destination tensor's shape/format/row-stride before
    /// writing into it.
    ///
    /// Forwards to [`TensorDyn::configure_image`], which drives the real
    /// `ef_tensor_configure_image` primitive -- the pool-reuse
    /// stride-preservation and alignment rules live in
    /// `libedgefirst_tensor.so`'s `static::Tensor::configure_image`, not
    /// here.
    pub fn configure_image(
        &mut self,
        width: usize,
        height: usize,
        format: PixelFormat,
    ) -> Result<()> {
        self.inner.configure_image(width, height, format)
    }

    /// Pin a stable host address for this tensor's data, type-erased to
    /// raw bytes -- same contract as [`TensorTrait::map_with`]'s pin, just
    /// without the element-typed view wrapped around it. `static`'s
    /// `Tensor<T>` exposes this as an inherent method too (`lib.rs`), for
    /// callers (e.g. `edgefirst-image`'s `lib.rs`) that want the address
    /// without going through `TensorMapTrait`.
    pub fn pin_host<'a>(&self, access: CpuAccess) -> Result<crate::pin::HostPin<'a>> {
        self.inner.pin_host(access)
    }

    /// Borrow a rectangular spatial sub-region as a zero-copy view. Same
    /// signature as `static`'s `Tensor::view` (`lib.rs`). Forwards to
    /// [`TensorDyn::view`], which drives the real `ef_tensor_view_region`
    /// primitive.
    pub fn view(&self, region: Region) -> Result<Self> {
        self.inner.view(region).map(Self::from_inner)
    }
}

// Quantization accessors — type-gated to integer element types via the
// sealed `IntegerType` trait, same as `static`'s equivalent block in
// `lib.rs` (see `[[feedback_tensor_quantization_type_gated]]`: this must
// stay a compile-time rejection for `Tensor<f32>`, not a runtime one).
// Each method here forwards straight to `TensorDyn`'s real implementation
// (`dynamic_backend.rs`), which drives the `ef_tensor_quantization_*`
// primitives and owns the cache `quantization()`'s borrow is served from --
// see that struct's `quantization_cache` field doc comment for why the
// cache is sound under concurrent access, the question this block's
// previous stub-era comment (before the primitive existed) had no answer
// for.
impl<T: Element + IntegerType> Tensor<T> {
    /// Quantization metadata for this tensor.
    pub fn quantization(&self) -> Option<&Quantization> {
        self.inner.quantization()
    }

    /// Attach quantization metadata to this tensor.
    pub fn set_quantization(&mut self, q: Quantization) -> Result<()> {
        self.inner.set_quantization(q)
    }

    /// Builder-style variant of [`Self::set_quantization`].
    pub fn with_quantization(mut self, q: Quantization) -> Result<Self> {
        self.set_quantization(q)?;
        Ok(self)
    }

    /// Clear any quantization metadata on this tensor.
    pub fn clear_quantization(&mut self) {
        self.inner.clear_quantization()
    }
}

/// Generate the three downcast methods (ref, mut ref, owned) for one
/// element type on [`TensorDyn`] -- the `dynamic`-backend counterpart to
/// `static_backend.rs`'s own `downcast_methods!` macro (same names, same
/// signatures, so a caller compiled against either backend needs no
/// backend-specific branch). `static`'s version matches an enum variant;
/// this one is built entirely on [`TensorDyn::as_typed`]/`as_typed_mut`
/// (`lens.rs`), the existing dtype-checked `#[repr(transparent)]` lens
/// `dynamic` already uses for exactly this "reinterpret the erased handle
/// as a typed one" operation -- so unlike `PboTensor`/`CudaHandle` above,
/// this family needed no new `TensorDyn` state and no new `ef_tensor_*`
/// primitive, only these convenience wrappers. Found missing (`no method
/// named as_u8/as_f32/... found for struct TensorDyn`) when `image-capi`
/// was first built against `dynamic` with `opengl` enabled -- task 15/17's
/// own `--no-default-features` verification never compiled `opengl`, so
/// this gap was as invisible to them as the PBO/CUDA one (see task 9's
/// report, "The verification hole").
macro_rules! downcast_methods {
    ($ty:ty, $as_name:ident, $as_mut_name:ident, $into_name:ident) => {
        /// Returns a shared reference to the inner tensor if the type matches.
        pub fn $as_name(&self) -> Option<&Tensor<$ty>> {
            self.as_typed::<$ty>()
        }

        /// Returns a mutable reference to the inner tensor if the type matches.
        pub fn $as_mut_name(&mut self) -> Option<&mut Tensor<$ty>> {
            self.as_typed_mut::<$ty>()
        }

        /// Unwraps the inner tensor if the type matches, otherwise returns `self` as `Err`.
        /// The Err variant is necessarily large (returns the unconsumed TensorDyn).
        #[allow(clippy::result_large_err)]
        pub fn $into_name(self) -> std::result::Result<Tensor<$ty>, Self> {
            if self.dtype() == <$ty as Element>::DTYPE {
                Ok(Tensor::from_inner(self))
            } else {
                Err(self)
            }
        }
    };
}

impl TensorDyn {
    downcast_methods!(u8, as_u8, as_u8_mut, into_u8);
    downcast_methods!(i8, as_i8, as_i8_mut, into_i8);
    downcast_methods!(u16, as_u16, as_u16_mut, into_u16);
    downcast_methods!(i16, as_i16, as_i16_mut, into_i16);
    downcast_methods!(u32, as_u32, as_u32_mut, into_u32);
    downcast_methods!(i32, as_i32, as_i32_mut, into_i32);
    downcast_methods!(u64, as_u64, as_u64_mut, into_u64);
    downcast_methods!(i64, as_i64, as_i64_mut, into_i64);
    downcast_methods!(f16, as_f16, as_f16_mut, into_f16);
    downcast_methods!(f32, as_f32, as_f32_mut, into_f32);
    downcast_methods!(f64, as_f64, as_f64_mut, into_f64);
}

impl<T: Element> From<Tensor<T>> for TensorDyn {
    /// Erase the compile-time element type, re-asserting `T`'s dtype on the
    /// handle as it goes.
    ///
    /// `Tensor<T>` already *is* a `TensorDyn` plus a zero-sized marker (see
    /// the module docs), so the unwrap itself is free. The retag is not
    /// decoration: it repairs the invariant `Tensor<T>` exists to carry.
    ///
    /// Under `static`, `TensorDyn` is an enum over eleven `Tensor<T>`, so
    /// the dtype **is** the Rust type — `TensorDyn::from(t)` picks the
    /// variant from `T` and a layout-identical `transmute` between
    /// same-width `Tensor`s changes the reported dtype for free. Under
    /// `dynamic` the dtype lives in the C handle instead, so that same
    /// transmute changes a `PhantomData` and nothing else, and the erased
    /// tensor keeps reporting whatever dtype the handle was *minted* with.
    ///
    /// That is not hypothetical. `edgefirst-image` allocates a PBO or DMA
    /// buffer as `u8` and hands it back as `Tensor<i8>` by exactly that
    /// transmute (twice, in `crates/image/src/lib.rs`), because the int8
    /// shader applies an XOR 0x80 bias over the same bytes. Without the
    /// retag, `create_image(dtype="int8")` returned a tensor reporting
    /// `uint8` — a wrong answer rather than a refusal, and one that
    /// propagates silently into quantization and inference rather than
    /// stopping anything.
    ///
    /// Cost is one `ef_tensor_dtype` read on the common path, where the
    /// handle already agrees and [`TensorDyn::set_dtype`] returns early.
    ///
    /// A failure here can only mean `T` is a different **width** than the
    /// handle's dtype, which no layout-identical transmute can produce —
    /// `Tensor<u8>` to `Tensor<u32>` is not layout-identical in the sense
    /// this relies on. `From` cannot return an error, so that case is
    /// logged rather than swallowed, and the dtype mismatch it leaves
    /// behind is then caught by [`crate::TensorDyn::as_typed`], which
    /// checks the handle's dtype against `T` before handing back a lens.
    fn from(t: Tensor<T>) -> TensorDyn {
        let mut inner = t.inner;
        if let Err(e) = inner.set_dtype(T::DTYPE) {
            log::error!(
                "erasing a Tensor<{}> whose handle reports {:?}: {e}. The typed lens and its \
                 handle disagree on element width, which no layout-identical transmute can \
                 produce; the erased tensor keeps the handle's dtype.",
                std::any::type_name::<T>(),
                inner.dtype()
            );
        }
        inner
    }
}

impl<T: Element> fmt::Debug for Tensor<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Forward to `TensorDyn`'s own `Debug` rather than deriving: the
        // lens carries no fields of its own to print, and this keeps one
        // `Debug` implementation instead of two that could drift.
        fmt::Debug::fmt(&self.inner, f)
    }
}

impl<T: Element> TensorTrait<T> for Tensor<T> {
    fn new(shape: &[usize], name: Option<&str>) -> Result<Self>
    where
        Self: Sized,
    {
        Self::new(shape, None, name)
    }

    #[cfg(unix)]
    fn from_fd(fd: std::os::fd::OwnedFd, shape: &[usize], name: Option<&str>) -> Result<Self>
    where
        Self: Sized,
    {
        Self::from_fd(fd, shape, name)
    }

    #[cfg(unix)]
    fn clone_fd(&self) -> Result<std::os::fd::OwnedFd> {
        self.inner.clone_fd()
    }

    fn memory(&self) -> TensorMemory {
        self.inner.memory()
    }

    fn name(&self) -> String {
        self.inner.name()
    }

    fn shape(&self) -> &[usize] {
        self.inner.shape()
    }

    fn reshape(&mut self, shape: &[usize]) -> Result<()> {
        self.inner.reshape(shape)
    }

    /// Forwarded rather than left on `TensorTrait`'s default, whose body is
    /// `self.reshape(shape)` -- the strict rule under a name promising the
    /// capacity-based one. See the static backend's own override
    /// (`impl TensorTrait for Tensor<T>`, `lib.rs`) and task P2e.
    fn set_logical_shape(&mut self, shape: &[usize]) -> Result<()> {
        self.inner.set_logical_shape(shape)
    }

    /// The allocation's real byte count, forwarded to
    /// [`TensorDyn::capacity_bytes`] rather than left on
    /// [`TensorTrait::capacity_bytes`]'s "the logical size" default.
    ///
    /// The default is wrong for this backend, not merely imprecise: a
    /// pitch-aligned or pool-sized tensor really does have headroom past
    /// its shape, and reporting the tight size discards it. Concretely, a
    /// `Mem`-backed NV12 image at an odd width pads its rows to a 64-byte
    /// pitch -- so a `HOST` descriptor minted from it carried a `capacity`
    /// equal to its shape, and a consumer importing that alias could not
    /// `configure_image` back into memory the producer actually has. That
    /// is `host_import_inherits_the_producers_capacity_headroom`
    /// (`tests/protocol_roundtrip.rs`), which fails on its own
    /// *precondition* without this -- it was never run against this
    /// backend before task P2a, because `descriptor_pinned` did not exist
    /// here to run it with. There was also no way to override the default
    /// before P2a: no `ef_tensor_*` primitive reported an allocation's
    /// byte count until `ef_tensor_capacity_bytes`.
    fn capacity_bytes(&self) -> usize {
        self.inner.capacity_bytes()
    }

    /// One `ef_tensor_map` call for the pointer (the same
    /// `TensorDyn::map_pin` core `TensorDyn::map_bytes` shares), then an
    /// ordinary typed [`crate::view::HostView`] over the resulting window
    /// -- the slice operations a caller does with the mapped view
    /// (`chunks_mut`, `split_at_mut`, `iter`, indexing, ...) are plain
    /// `[T]` methods after that, not additional ABI calls.
    ///
    /// `byte_size_override` is `Some(pin.len())`, not `None`: `pin.len()`
    /// is the real, already stride-aware extent `ef_tensor_map` mapped
    /// (`self.inner.map_pin` -> `ef_tensor_plane_at`'s own geometry, task
    /// 17's `vt_plane_at` fix included), which for a padded row (e.g. a
    /// semi-planar image whose width is not a multiple of the 64-byte
    /// alignment `configure_image` picks) is *larger* than
    /// `self.inner.shape().iter().product()`, the tight logical size.
    /// `None` here made [`HostView::len_elems`] fall back to that tight
    /// product, silently truncating the mapped view to less than what the
    /// real handle actually maps -- exactly the "full padded buffer" the
    /// `static` backend's own `Tensor<T>::map_with` (`crate::lib`) exposes
    /// for the identical reason (see that function's own doc comment). Not
    /// reached by anything before task 9: no caller mapped a `dynamic`
    /// `Tensor<T>` over a semi-planar image whose stride exceeds its tight
    /// width until G3's host-memory JPEG decode exercised it for the first
    /// time.
    fn map_with<'a>(&self, access: CpuAccess) -> Result<crate::view::HostView<'a, T>>
    where
        T: 'a,
    {
        let pin = self.inner.map_pin(access)?;
        let byte_len = pin.len();
        Ok(crate::view::HostView::new(
            pin,
            self.inner.shape().to_vec(),
            Some(byte_len),
            access,
        ))
    }

    /// The non-blocking counterpart of [`map_with`](Self::map_with), routed
    /// through `ef_tensor_try_map` rather than left on `TensorTrait`'s
    /// `map_with` default.
    ///
    /// The default would be wrong here, not merely imprecise: it would stall
    /// on the Windows D3D11 texture's staging refresh under a name whose
    /// whole contract is that it does not, so a caller polling this would
    /// block instead of making progress elsewhere. Same body as `map_with`
    /// otherwise -- including the `byte_size_override`, for the padded-row
    /// reason that method's own doc comment gives.
    fn try_map_with<'a>(&self, access: CpuAccess) -> Result<crate::view::HostView<'a, T>>
    where
        T: 'a,
    {
        let pin = self.inner.map_pin_with(access, true)?;
        let byte_len = pin.len();
        Ok(crate::view::HostView::new(
            pin,
            self.inner.shape().to_vec(),
            Some(byte_len),
            access,
        ))
    }

    fn buffer_identity(&self) -> &BufferIdentity {
        self.inner.buffer_identity()
    }
}