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
//! Framing + validation for a persisted `wgpu::PipelineCache` blob.
//!
//! A `wgpu::PipelineCache` lets a driver reuse the machine code it compiled for
//! a device's render pipelines across process launches, cutting warm-start
//! shader/pipeline compilation to near zero. wgpu only implements it on Vulkan
//! (any Vulkan adapter — Android, Linux, Windows-on-Vulkan); Metal/DX12 drivers
//! manage their own caches, so this whole path is silently inert there (the
//! feature is absent → no cache is ever created, and
//! [`crate::pipeline::PipelineCache`] is simply built with `None`).
//!
//! The shell owns file I/O and hands the framework an opaque `Vec<u8>` blob (no
//! `serde`/file dependency lives here). Before that blob is ever fed to the
//! **unsafe** [`wgpu::Device::create_pipeline_cache`], it is wrapped in a small
//! self-describing header — a magic tag plus the adapter fingerprint the data
//! was produced on — checked here ([`unframe`]). A mismatch (wrong magic,
//! different adapter/driver, truncated blob) is treated as "no cache" and the
//! caller starts from an empty cache instead of handing the driver bytes from a
//! foreign device. This is belt-and-braces on top of wgpu's own
//! `create_pipeline_cache(fallback: true)`, which already falls back to an empty
//! cache for mismatched data rather than misbehaving.
//!
//! This is the **only** copy of the framing: `frust-render` used to carry an
//! identical one (kept in lockstep by a drift-guard test) and now re-exports
//! this module instead, so the on-disk layout has exactly one definition. A
//! blob persisted by any earlier frust build still validates here unchanged —
//! bump this module's `MAGIC` if the layout ever does change, so an old blob
//! is rejected
//! rather than misparsed.
/// Magic tag prefixing every framed blob: `Frust PipeLine Cache wgpu v1`.
/// Bumped if the framing layout below ever changes so an old on-disk blob is
/// rejected rather than misparsed.
const MAGIC: = *b"FKPLCwg1";
/// A compact identity string for the adapter a cache blob was produced on.
///
/// A `wgpu::PipelineCache` is only ever valid for the same adapter+driver that
/// produced it (wgpu validates this internally too, but framing the key lets us
/// reject a foreign blob before the unsafe API is ever called). The `driver` /
/// `driver_info` fields are included so a driver update — which can invalidate
/// the compiled machine code — changes the key and discards the stale blob.
/// Wraps a raw `PipelineCache::get_data()` payload in the validated header
/// [`unframe`] checks, tagging it with the adapter it was produced on.
///
/// Layout: `MAGIC (8)` · `key_len: u32 LE` · `key bytes` · `payload_len: u32 LE`
/// · `payload bytes`.
/// Validates a framed blob against `adapter_key` and returns the inner payload,
/// or `None` if the blob is not a well-formed frame for *this* adapter.
///
/// Rejects (returns `None`) on: a truncated/malformed frame, a wrong magic tag,
/// an adapter-key mismatch (foreign device or a post-driver-update key), or a
/// declared payload length that disagrees with the trailing bytes. A `None`
/// here means "start from an empty cache", never a panic.
/// Splits `n` bytes off the front of `*buf`, advancing it; `None` if short.
/// Reads a little-endian `u32` off the front of `*buf`, advancing it.