1use std::collections::BTreeMap;
4
5use alpha2_gpu::CapabilityTier;
6use alpha2_render::Renderer;
7use serde::Serialize;
8
9use crate::PlaybackPolicy;
10
11pub const CAPABILITY_SCHEMA: &str = "alpha2-capabilities";
13pub const CAPABILITY_VERSION: u32 = 2;
15
16#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
18#[serde(rename_all = "snake_case")]
19pub enum Runtime {
20 Native,
22 Browser,
24}
25
26#[derive(Clone, Debug, PartialEq, Serialize)]
28#[serde(rename_all = "camelCase")]
29pub struct CapabilityReport {
30 pub schema: &'static str,
32 pub version: u32,
34 pub runtime: Runtime,
36 pub gpu: GpuReport,
38 pub color: ColorReport,
40 pub playback: PlaybackReport,
42 pub decode: serde_json::Value,
44 pub encode: serde_json::Value,
46 pub features: BTreeMap<String, Feature>,
48 pub adapters: serde_json::Value,
51 pub memory: serde_json::Value,
53}
54
55#[derive(Clone, Debug, PartialEq, Serialize)]
57#[serde(rename_all = "camelCase")]
58pub struct GpuReport {
59 pub adapter: String,
61 pub backend: String,
63 pub driver: String,
65 pub driver_info: String,
67 pub shader_compiler: String,
69 pub tier: &'static str,
71 pub intermediate_format: &'static str,
74 pub max_texture_dimension_2d: u32,
76 pub timestamp_queries: bool,
78 pub texture_budget_bytes: u64,
80 pub memory: GpuMemoryReport,
82}
83
84#[derive(Clone, Debug, Default, PartialEq, Serialize)]
86#[serde(rename_all = "camelCase")]
87pub struct GpuMemoryReport {
88 pub budget_bytes: Option<u64>,
91}
92
93#[derive(Clone, Debug, PartialEq, Serialize)]
95#[serde(rename_all = "camelCase")]
96pub struct ColorReport {
97 pub working_space: &'static str,
99 pub hdr_sources: bool,
101 pub hdr_output: bool,
103 pub extended_range_sources: bool,
106}
107
108#[derive(Clone, Debug, PartialEq, Serialize)]
110#[serde(rename_all = "camelCase")]
111pub struct PlaybackReport {
112 pub policies: Vec<PlaybackPolicy>,
114 pub default_policy: PlaybackPolicy,
116 pub audio_at_non_unit_rate: bool,
118}
119
120#[derive(Clone, Debug, PartialEq, Serialize)]
122pub struct Feature {
123 pub available: bool,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub reason: Option<String>,
128}
129
130impl Feature {
131 #[must_use]
133 pub const fn available() -> Self {
134 Self {
135 available: true,
136 reason: None,
137 }
138 }
139
140 #[must_use]
142 pub fn unavailable(reason: impl Into<String>) -> Self {
143 Self {
144 available: false,
145 reason: Some(reason.into()),
146 }
147 }
148}
149
150impl CapabilityReport {
151 #[must_use]
153 pub fn new(runtime: Runtime, renderer: &Renderer) -> Self {
154 let capabilities = renderer.gpu_capabilities();
155 let half = matches!(
156 capabilities.intermediate_format,
157 alpha2_gpu::IntermediateFormat::Rgba16Float
158 );
159 Self {
160 schema: CAPABILITY_SCHEMA,
161 version: CAPABILITY_VERSION,
162 runtime,
163 gpu: GpuReport {
164 adapter: capabilities.adapter_name.clone(),
165 backend: capabilities.backend.clone(),
166 driver: capabilities.driver.clone(),
167 driver_info: capabilities.driver_info.clone(),
168 shader_compiler: capabilities.shader_compiler.clone(),
169 tier: match capabilities.tier {
170 CapabilityTier::TierA => "a",
171 CapabilityTier::TierB => "b",
172 _ => "unknown",
173 },
174 intermediate_format: if half { "rgba16float" } else { "rgba8unorm" },
175 max_texture_dimension_2d: capabilities.max_texture_dimension_2d,
176 timestamp_queries: capabilities.timestamp_queries,
177 texture_budget_bytes: renderer.diagnostics().textures.budget_bytes,
178 memory: GpuMemoryReport {
179 budget_bytes: renderer.gpu_memory_budget(),
180 },
181 },
182 color: ColorReport {
183 working_space: "linear_bt709_sdr",
184 hdr_sources: true,
185 hdr_output: false,
186 extended_range_sources: half,
187 },
188 playback: PlaybackReport {
189 policies: PlaybackPolicy::ALL.to_vec(),
190 default_policy: PlaybackPolicy::default(),
191 audio_at_non_unit_rate: false,
192 },
193 decode: serde_json::Value::Null,
194 encode: serde_json::Value::Null,
195 features: BTreeMap::new(),
196 adapters: serde_json::Value::Null,
197 memory: serde_json::Value::Null,
198 }
199 }
200
201 #[must_use]
203 pub fn cpu(runtime: Runtime, renderer: &alpha2_render::CpuRenderer) -> Self {
204 Self {
205 schema: CAPABILITY_SCHEMA,
206 version: CAPABILITY_VERSION,
207 runtime,
208 gpu: GpuReport {
209 adapter: format!("alpha2 CPU renderer ({} threads)", renderer.threads()),
210 backend: "cpu".to_owned(),
211 driver: "alpha2".to_owned(),
212 driver_info: env!("CARGO_PKG_VERSION").to_owned(),
213 shader_compiler: String::new(),
214 tier: "a",
215 intermediate_format: "rgba16float",
216 max_texture_dimension_2d: alpha2_render::RenderSize::MAX_DIMENSION,
217 timestamp_queries: false,
218 texture_budget_bytes: 0,
219 memory: GpuMemoryReport::default(),
220 },
221 color: ColorReport {
222 working_space: "linear_bt709_sdr",
223 hdr_sources: true,
224 hdr_output: false,
225 extended_range_sources: true,
226 },
227 playback: PlaybackReport {
228 policies: PlaybackPolicy::ALL.to_vec(),
229 default_policy: PlaybackPolicy::default(),
230 audio_at_non_unit_rate: false,
231 },
232 decode: serde_json::Value::Null,
233 encode: serde_json::Value::Null,
234 features: BTreeMap::new(),
235 adapters: serde_json::Value::Null,
236 memory: serde_json::Value::Null,
237 }
238 }
239
240 #[must_use]
242 pub fn with_feature(mut self, name: &str, feature: Feature) -> Self {
243 self.features.insert(name.to_owned(), feature);
244 self
245 }
246}