keyhog_scanner/gpu/
policy.rs1use std::{
9 fmt,
10 sync::atomic::{AtomicU8, Ordering},
11};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14#[repr(u8)]
15pub enum GpuRuntimePolicy {
16 Auto = 0,
17 Disabled = 1,
18 Required = 2,
19}
20
21impl GpuRuntimePolicy {
22 #[must_use]
23 pub(crate) fn label(self) -> &'static str {
24 match self {
25 Self::Auto => "auto",
26 Self::Disabled => "off",
27 Self::Required => "required",
28 }
29 }
30
31 #[must_use]
32 pub const fn is_disabled(self) -> bool {
33 matches!(self, Self::Disabled)
34 }
35
36 #[must_use]
37 pub const fn is_required(self) -> bool {
38 matches!(self, Self::Required)
39 }
40
41 fn from_u8(value: u8) -> Self {
42 match value {
43 1 => Self::Disabled,
44 2 => Self::Required,
45 _ => Self::Auto,
46 }
47 }
48}
49
50impl fmt::Display for GpuRuntimePolicy {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 f.write_str((*self).label())
53 }
54}
55
56static GPU_RUNTIME_POLICY: AtomicU8 = AtomicU8::new(GpuRuntimePolicy::Auto as u8);
57
58#[derive(Debug, Clone, Default)]
59pub(crate) struct GpuRuntimeProbe {
60 pub(crate) available: bool,
61 pub(crate) name: Option<String>,
62 pub(crate) buffer_limit_mb: Option<u64>,
63 pub(crate) runtime_identity: Option<String>,
64 pub(crate) is_software: bool,
65}
66
67pub fn set_gpu_runtime_policy(policy: GpuRuntimePolicy) {
70 GPU_RUNTIME_POLICY.store(policy as u8, Ordering::SeqCst);
71}
72
73#[must_use]
75pub fn gpu_runtime_policy() -> GpuRuntimePolicy {
76 GpuRuntimePolicy::from_u8(GPU_RUNTIME_POLICY.load(Ordering::SeqCst))
77}
78
79#[must_use]
87pub(crate) fn gpu_probe() -> GpuRuntimeProbe {
88 if gpu_disabled_by_policy() {
89 return GpuRuntimeProbe::default();
90 }
91 #[cfg(feature = "gpu")]
92 if let Some(gpu) = super::gpu_adapter_probe() {
93 return GpuRuntimeProbe {
94 available: !gpu.is_software,
95 name: Some(gpu.name.clone()),
96 buffer_limit_mb: Some(gpu.buffer_limit_mb),
97 runtime_identity: Some(gpu.runtime_identity.clone()),
98 is_software: gpu.is_software,
99 };
100 }
101 GpuRuntimeProbe::default()
102}
103
104#[must_use]
107pub fn gpu_required_by_policy() -> bool {
108 gpu_runtime_policy().is_required()
109}
110
111pub fn require_gpu_preflight() -> Result<(), String> {
125 require_gpu_preflight_with_policy(gpu_runtime_policy())
126}
127
128pub(crate) fn require_gpu_preflight_with_policy(policy: GpuRuntimePolicy) -> Result<(), String> {
129 if !policy.is_required() {
130 return Ok(());
131 }
132
133 if let Err(reason) = super::gpu_region_presence_self_test() {
134 return Err(format!(
135 "--require-gpu requested but no complete production GPU peer set passed region-presence parity ({reason}); \
136 refusing to run on CPU. Fix the GPU stack or run without \
137 --require-gpu."
138 ));
139 }
140
141 Ok(())
142}
143
144pub(crate) fn gpu_disabled_by_policy() -> bool {
145 gpu_runtime_policy().is_disabled()
146}