1use std::collections::BTreeMap;
5
6use alpha2_gpu::CapabilityTier;
7use alpha2_render::Renderer;
8use serde::Serialize;
9
10use crate::PlaybackPolicy;
11
12pub const CAPABILITY_SCHEMA: &str = "alpha2-capabilities";
14pub const CAPABILITY_VERSION: u32 = 1;
16
17#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
19#[serde(rename_all = "snake_case")]
20pub enum Runtime {
21 Native,
23 Browser,
25}
26
27#[derive(Clone, Debug, PartialEq, Serialize)]
29#[serde(rename_all = "camelCase")]
30pub struct CapabilityReport {
31 pub schema: &'static str,
33 pub version: u32,
35 pub runtime: Runtime,
37 pub gpu: GpuReport,
39 pub color: ColorReport,
41 pub playback: PlaybackReport,
43 pub decode: serde_json::Value,
47 pub encode: serde_json::Value,
50 pub features: BTreeMap<String, Feature>,
52}
53
54#[derive(Clone, Debug, PartialEq, Serialize)]
56#[serde(rename_all = "camelCase")]
57pub struct GpuReport {
58 pub adapter: String,
60 pub backend: String,
62 pub driver: String,
64 pub driver_info: String,
66 pub tier: &'static str,
68 pub intermediate_format: &'static str,
72 pub max_texture_dimension_2d: u32,
74 pub timestamp_queries: bool,
76 pub texture_budget_bytes: u64,
78}
79
80#[derive(Clone, Debug, PartialEq, Serialize)]
82#[serde(rename_all = "camelCase")]
83pub struct ColorReport {
84 pub working_space: &'static str,
86 pub hdr_sources: bool,
88 pub hdr_output: bool,
90 pub extended_range_sources: bool,
93}
94
95#[derive(Clone, Debug, PartialEq, Serialize)]
97#[serde(rename_all = "camelCase")]
98pub struct PlaybackReport {
99 pub policies: Vec<PlaybackPolicy>,
101 pub default_policy: PlaybackPolicy,
103 pub audio_at_non_unit_rate: bool,
105}
106
107#[derive(Clone, Debug, PartialEq, Serialize)]
109pub struct Feature {
110 pub available: bool,
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub reason: Option<String>,
115}
116
117impl Feature {
118 #[must_use]
120 pub const fn available() -> Self {
121 Self {
122 available: true,
123 reason: None,
124 }
125 }
126
127 #[must_use]
129 pub fn unavailable(reason: impl Into<String>) -> Self {
130 Self {
131 available: false,
132 reason: Some(reason.into()),
133 }
134 }
135}
136
137impl CapabilityReport {
138 #[must_use]
141 pub fn new(runtime: Runtime, renderer: &Renderer) -> Self {
142 let capabilities = renderer.gpu_capabilities();
143 let half = matches!(
144 capabilities.intermediate_format,
145 alpha2_gpu::IntermediateFormat::Rgba16Float
146 );
147 Self {
148 schema: CAPABILITY_SCHEMA,
149 version: CAPABILITY_VERSION,
150 runtime,
151 gpu: GpuReport {
152 adapter: capabilities.adapter_name.clone(),
153 backend: capabilities.backend.clone(),
154 driver: capabilities.driver.clone(),
155 driver_info: capabilities.driver_info.clone(),
156 tier: match capabilities.tier {
157 CapabilityTier::TierA => "a",
158 CapabilityTier::TierB => "b",
159 _ => "unknown",
160 },
161 intermediate_format: if half { "rgba16float" } else { "rgba8unorm" },
162 max_texture_dimension_2d: capabilities.max_texture_dimension_2d,
163 timestamp_queries: capabilities.timestamp_queries,
164 texture_budget_bytes: renderer.diagnostics().textures.budget_bytes,
165 },
166 color: ColorReport {
167 working_space: "linear_bt709_sdr",
168 hdr_sources: true,
169 hdr_output: false,
170 extended_range_sources: half,
171 },
172 playback: PlaybackReport {
173 policies: PlaybackPolicy::ALL.to_vec(),
174 default_policy: PlaybackPolicy::default(),
175 audio_at_non_unit_rate: false,
176 },
177 decode: serde_json::Value::Null,
178 encode: serde_json::Value::Null,
179 features: BTreeMap::new(),
180 }
181 }
182
183 #[must_use]
185 pub fn with_feature(mut self, name: &str, feature: Feature) -> Self {
186 self.features.insert(name.to_owned(), feature);
187 self
188 }
189}