#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DeviceTier {
Gles = 0,
FullWebGpuSupport = 1,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WgpuBackendType {
WgpuCore,
#[cfg(web)]
WebGpu,
}
#[derive(thiserror::Error, Debug)]
pub enum InsufficientDeviceCapabilities {
#[error("Adapter does not support the minimum shader model required. Supported is {actual:?} but required is {required:?}")]
TooLowShaderModel {
required: wgpu::ShaderModel,
actual: wgpu::ShaderModel,
},
#[error("Adapter does not have all the required capability flags required. Supported are {actual:?} but required are {required:?}")]
MissingCapabilitiesFlags {
required: wgpu::DownlevelFlags,
actual: wgpu::DownlevelFlags,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DeviceCaps {
pub tier: DeviceTier,
pub max_texture_dimension2d: u32,
pub max_buffer_size: u64,
pub backend_type: WgpuBackendType,
}
impl DeviceCaps {
pub fn support_sampling_msaa_texture(&self) -> bool {
match self.tier {
DeviceTier::Gles => false,
DeviceTier::FullWebGpuSupport => true,
}
}
pub fn support_depth_readback(&self) -> bool {
match self.tier {
DeviceTier::Gles => false,
DeviceTier::FullWebGpuSupport => true,
}
}
pub fn from_adapter(adapter: &wgpu::Adapter) -> Self {
let backend = adapter.get_info().backend;
let tier = match backend {
wgpu::Backend::Vulkan
| wgpu::Backend::Metal
| wgpu::Backend::Dx12
| wgpu::Backend::BrowserWebGpu => DeviceTier::FullWebGpuSupport,
wgpu::Backend::Gl | wgpu::Backend::Empty => DeviceTier::Gles,
};
let backend_type = match backend {
wgpu::Backend::Empty
| wgpu::Backend::Vulkan
| wgpu::Backend::Metal
| wgpu::Backend::Dx12
| wgpu::Backend::Gl => WgpuBackendType::WgpuCore,
wgpu::Backend::BrowserWebGpu => {
#[cfg(web)]
{
WgpuBackendType::WebGpu
}
#[cfg(not(web))]
{
unreachable!("WebGPU backend is not supported on native platforms.")
}
}
};
Self {
tier,
max_texture_dimension2d: adapter.limits().max_texture_dimension_2d,
max_buffer_size: adapter.limits().max_buffer_size,
backend_type,
}
}
pub fn limits(&self) -> wgpu::Limits {
wgpu::Limits {
max_texture_dimension_2d: self.max_texture_dimension2d,
max_buffer_size: self.max_buffer_size,
..wgpu::Limits::downlevel_webgl2_defaults()
}
}
#[allow(clippy::unused_self)]
pub fn features(&self) -> wgpu::Features {
wgpu::Features::empty()
}
pub fn device_descriptor(&self) -> wgpu::DeviceDescriptor<'static> {
wgpu::DeviceDescriptor {
label: Some("re_renderer device"),
required_features: self.features(),
required_limits: self.limits(),
}
}
pub fn required_downlevel_capabilities(&self) -> wgpu::DownlevelCapabilities {
wgpu::DownlevelCapabilities {
flags: match self.tier {
DeviceTier::Gles => wgpu::DownlevelFlags::empty(),
DeviceTier::FullWebGpuSupport => wgpu::DownlevelFlags::all(),
},
limits: Default::default(), shader_model: wgpu::ShaderModel::Sm4,
}
}
pub fn check_downlevel_capabilities(
&self,
capabilities: &wgpu::DownlevelCapabilities,
) -> Result<(), InsufficientDeviceCapabilities> {
let wgpu::DownlevelCapabilities {
flags,
limits: _,
shader_model,
} = self.required_downlevel_capabilities();
if capabilities.shader_model < shader_model {
Err(InsufficientDeviceCapabilities::TooLowShaderModel {
required: shader_model,
actual: capabilities.shader_model,
})
} else if !capabilities.flags.contains(flags) {
Err(InsufficientDeviceCapabilities::MissingCapabilitiesFlags {
required: flags,
actual: capabilities.flags,
})
} else {
Ok(())
}
}
}
pub struct RenderContextConfig {
pub output_format_color: wgpu::TextureFormat,
pub device_caps: DeviceCaps,
}
pub fn supported_backends() -> wgpu::Backends {
if cfg!(native) {
wgpu::util::backend_bits_from_env()
.unwrap_or(wgpu::Backends::VULKAN | wgpu::Backends::METAL)
} else {
wgpu::Backends::GL | wgpu::Backends::BROWSER_WEBGPU
}
}
pub fn parse_graphics_backend(backend: &str) -> Option<wgpu::Backend> {
match backend.to_lowercase().as_str() {
"vulcan" | "vulkan" | "vk" => Some(wgpu::Backend::Vulkan),
"metal" | "apple" | "mtl" => Some(wgpu::Backend::Metal),
"dx12" | "dx" | "d3d" | "d3d12" | "directx" => Some(wgpu::Backend::Dx12),
"webgl2" | "webgl" | "opengl" | "gles" | "gles3" | "gl" => Some(wgpu::Backend::Gl),
"browserwebgpu" | "webgpu" => Some(wgpu::Backend::BrowserWebGpu),
_ => None,
}
}
pub fn validate_graphics_backend_applicability(backend: wgpu::Backend) -> Result<(), &'static str> {
match backend {
wgpu::Backend::Empty => {
return Err("Cannot run with empty backend.");
}
wgpu::Backend::Vulkan => {
if cfg!(target_arch = "wasm32") {
return Err("Can only run with WebGL or WebGPU on the web.");
}
}
wgpu::Backend::Metal => {
if cfg!(target_arch = "wasm32") {
return Err("Can only run with WebGL or WebGPU on the web.");
}
if cfg!(target_os = "linux") || cfg!(target_os = "windows") {
return Err("Cannot run with DX12 backend on Linux & Windows.");
}
}
wgpu::Backend::Dx12 => {
return Err("DX12 backend is currently not supported.");
}
wgpu::Backend::Gl => {
if cfg!(target_os = "macos") {
return Err("Cannot run with GL backend on Mac.");
}
}
wgpu::Backend::BrowserWebGpu => {
if !cfg!(target_arch = "wasm32") {
return Err("Cannot run with WebGPU backend on native application.");
}
}
}
Ok(())
}