alpha2_gpu/
capabilities.rs1#[derive(Copy, Clone, Debug, Eq, PartialEq)]
3#[non_exhaustive]
4pub enum CapabilityTier {
5 TierA,
7 TierB,
9}
10
11#[derive(Copy, Clone, Debug, Eq, PartialEq)]
13#[non_exhaustive]
14pub enum IntermediateFormat {
15 Rgba16Float,
17 Rgba8Unorm,
19}
20
21#[derive(Clone, Debug)]
23#[non_exhaustive]
24pub struct Capabilities {
25 pub tier: CapabilityTier,
27 pub intermediate_format: IntermediateFormat,
29 pub adapter_name: String,
31 pub max_texture_dimension_2d: u32,
33 pub timestamp_queries: bool,
35 pub backend: String,
37 pub driver: String,
39 pub driver_info: String,
41 pub software_rasterizer: bool,
45 pub half_float_blendable: bool,
48}
49
50impl Capabilities {
51 #[must_use]
53 #[allow(clippy::too_many_arguments)]
54 pub fn new(
55 tier: CapabilityTier,
56 intermediate_format: IntermediateFormat,
57 adapter_name: impl Into<String>,
58 max_texture_dimension_2d: u32,
59 timestamp_queries: bool,
60 backend: impl Into<String>,
61 driver: impl Into<String>,
62 driver_info: impl Into<String>,
63 ) -> Self {
64 Self {
65 tier,
66 intermediate_format,
67 adapter_name: adapter_name.into(),
68 max_texture_dimension_2d,
69 timestamp_queries,
70 backend: backend.into(),
71 driver: driver.into(),
72 driver_info: driver_info.into(),
73 software_rasterizer: false,
74 half_float_blendable: intermediate_format == IntermediateFormat::Rgba16Float,
75 }
76 }
77
78 pub(crate) fn from_adapter(adapter: &wgpu::Adapter) -> Self {
79 let info = adapter.get_info();
80 let half_float = adapter.get_texture_format_features(wgpu::TextureFormat::Rgba16Float);
81 Self {
82 tier: tier_for(&adapter.get_downlevel_capabilities()),
83 intermediate_format: intermediate_format_for(half_float),
84 half_float_blendable: half_float
85 .allowed_usages
86 .contains(wgpu::TextureUsages::RENDER_ATTACHMENT)
87 && half_float
88 .flags
89 .contains(wgpu::TextureFormatFeatureFlags::BLENDABLE),
90 adapter_name: info.name,
91 max_texture_dimension_2d: adapter.limits().max_texture_dimension_2d,
92 timestamp_queries: adapter.features().contains(wgpu::Features::TIMESTAMP_QUERY),
93 backend: format!("{:?}", info.backend),
94 driver: info.driver,
95 driver_info: info.driver_info,
96 software_rasterizer: info.device_type == wgpu::DeviceType::Cpu,
97 }
98 }
99}
100
101fn intermediate_format_for(half_float: wgpu::TextureFormatFeatures) -> IntermediateFormat {
108 let required = wgpu::TextureUsages::RENDER_ATTACHMENT
109 | wgpu::TextureUsages::TEXTURE_BINDING
110 | wgpu::TextureUsages::COPY_SRC;
111 if half_float.allowed_usages.contains(required)
112 && half_float
113 .flags
114 .contains(wgpu::TextureFormatFeatureFlags::FILTERABLE)
115 {
116 IntermediateFormat::Rgba16Float
117 } else {
118 IntermediateFormat::Rgba8Unorm
119 }
120}
121
122fn tier_for(downlevel: &wgpu::DownlevelCapabilities) -> CapabilityTier {
125 if downlevel.is_webgpu_compliant() {
126 CapabilityTier::TierA
127 } else {
128 CapabilityTier::TierB
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use wgpu::{TextureFormatFeatureFlags as Flags, TextureUsages as Usages};
136
137 fn half_float(allowed_usages: Usages, flags: Flags) -> wgpu::TextureFormatFeatures {
138 wgpu::TextureFormatFeatures {
139 allowed_usages,
140 flags,
141 }
142 }
143
144 #[test]
145 fn half_float_is_the_intermediate_only_where_it_renders_filters_and_copies() {
146 let required = Usages::RENDER_ATTACHMENT | Usages::TEXTURE_BINDING | Usages::COPY_SRC;
147 assert_eq!(
148 intermediate_format_for(half_float(required | Usages::COPY_DST, Flags::FILTERABLE)),
149 IntermediateFormat::Rgba16Float
150 );
151 for missing in [
152 Usages::RENDER_ATTACHMENT,
153 Usages::TEXTURE_BINDING,
154 Usages::COPY_SRC,
155 ] {
156 assert_eq!(
157 intermediate_format_for(half_float(required - missing, Flags::FILTERABLE)),
158 IntermediateFormat::Rgba8Unorm,
159 "without {missing:?}"
160 );
161 }
162 assert_eq!(
163 intermediate_format_for(half_float(required, Flags::empty())),
164 IntermediateFormat::Rgba8Unorm,
165 "without filtering"
166 );
167 assert_eq!(
169 intermediate_format_for(half_float(Usages::empty(), Flags::empty())),
170 IntermediateFormat::Rgba8Unorm
171 );
172 }
173
174 #[test]
175 fn the_tier_follows_webgpu_compliance_alone() {
176 assert_eq!(
177 tier_for(&wgpu::DownlevelCapabilities::default()),
178 CapabilityTier::TierA
179 );
180 let mut downlevel = wgpu::DownlevelCapabilities::default();
181 downlevel
182 .flags
183 .remove(wgpu::DownlevelFlags::COMPUTE_SHADERS);
184 assert_eq!(tier_for(&downlevel), CapabilityTier::TierB);
185 let shader_model = wgpu::DownlevelCapabilities {
186 shader_model: wgpu::ShaderModel::Sm4,
187 ..Default::default()
188 };
189 assert_eq!(tier_for(&shader_model), CapabilityTier::TierB);
190 }
191}