use alloc::string::String;
use core::{hash::Hash, str::FromStr};
#[cfg(any(feature = "serde", test))]
use serde::{Deserialize, Serialize};
use crate::link_to_wgpu_docs;
#[cfg(doc)]
use crate::InstanceDescriptor;
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Backend {
Noop = 0,
Vulkan = 1,
Metal = 2,
Dx12 = 3,
Gl = 4,
BrowserWebGpu = 5,
}
impl Backend {
pub const ALL: [Backend; Backends::all().bits().count_ones() as usize] = [
Self::Noop,
Self::Vulkan,
Self::Metal,
Self::Dx12,
Self::Gl,
Self::BrowserWebGpu,
];
#[must_use]
pub const fn to_str(self) -> &'static str {
match self {
Backend::Noop => "noop",
Backend::Vulkan => "vulkan",
Backend::Metal => "metal",
Backend::Dx12 => "dx12",
Backend::Gl => "gl",
Backend::BrowserWebGpu => "webgpu",
}
}
}
impl core::fmt::Display for Backend {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.to_str())
}
}
bitflags::bitflags! {
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Backends: u32 {
const NOOP = 1 << Backend::Noop as u32;
const VULKAN = 1 << Backend::Vulkan as u32;
const GL = 1 << Backend::Gl as u32;
const METAL = 1 << Backend::Metal as u32;
const DX12 = 1 << Backend::Dx12 as u32;
const BROWSER_WEBGPU = 1 << Backend::BrowserWebGpu as u32;
const PRIMARY = Self::VULKAN.bits()
| Self::METAL.bits()
| Self::DX12.bits()
| Self::BROWSER_WEBGPU.bits();
const SECONDARY = Self::GL.bits();
}
}
impl Default for Backends {
fn default() -> Self {
Self::all()
}
}
impl From<Backend> for Backends {
fn from(backend: Backend) -> Self {
Self::from_bits(1 << backend as u32).unwrap()
}
}
impl Backends {
pub fn from_env() -> Option<Self> {
let env = crate::env::var("WGPU_BACKEND")?;
Some(Self::from_comma_list(&env))
}
pub fn with_env(&self) -> Self {
if let Some(env) = Self::from_env() {
env
} else {
*self
}
}
pub fn from_comma_list(string: &str) -> Self {
let mut backends = Self::empty();
for backend in string.to_lowercase().split(',') {
backends |= match backend.trim() {
"vulkan" | "vk" => Self::VULKAN,
"dx12" | "d3d12" => Self::DX12,
"metal" | "mtl" => Self::METAL,
"opengl" | "gles" | "gl" => Self::GL,
"webgpu" => Self::BROWSER_WEBGPU,
"noop" => Self::NOOP,
b => {
log::warn!("unknown backend string '{b}'");
continue;
}
}
}
if backends.is_empty() {
log::warn!("no valid backend strings found!");
}
backends
}
}
#[derive(Clone, Debug, Default)]
pub struct BackendOptions {
pub gl: GlBackendOptions,
pub dx12: Dx12BackendOptions,
pub noop: NoopBackendOptions,
}
impl BackendOptions {
#[must_use]
pub fn from_env_or_default() -> Self {
Self {
gl: GlBackendOptions::from_env_or_default(),
dx12: Dx12BackendOptions::from_env_or_default(),
noop: NoopBackendOptions::from_env_or_default(),
}
}
#[must_use]
pub fn with_env(self) -> Self {
Self {
gl: self.gl.with_env(),
dx12: self.dx12.with_env(),
noop: self.noop.with_env(),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct GlBackendOptions {
pub gles_minor_version: Gles3MinorVersion,
pub fence_behavior: GlFenceBehavior,
pub debug_fns: GlDebugFns,
}
impl GlBackendOptions {
#[must_use]
pub fn from_env_or_default() -> Self {
let gles_minor_version = Gles3MinorVersion::from_env().unwrap_or_default();
let debug_fns = GlDebugFns::from_env().unwrap_or_default();
Self {
gles_minor_version,
fence_behavior: GlFenceBehavior::Normal,
debug_fns,
}
}
#[must_use]
pub fn with_env(self) -> Self {
let gles_minor_version = self.gles_minor_version.with_env();
let fence_behavior = self.fence_behavior.with_env();
let debug_fns = self.debug_fns.with_env();
Self {
gles_minor_version,
fence_behavior,
debug_fns,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum GlDebugFns {
#[default]
Auto,
ForceEnabled,
Disabled,
}
impl GlDebugFns {
#[must_use]
pub fn from_env() -> Option<Self> {
let value = crate::env::var("WGPU_GL_DEBUG_FNS")
.as_deref()?
.to_lowercase();
match value.as_str() {
"auto" => Some(Self::Auto),
"forceenabled" | "force_enabled" | "enabled" => Some(Self::ForceEnabled),
"disabled" => Some(Self::Disabled),
_ => None,
}
}
#[must_use]
pub fn with_env(self) -> Self {
if let Some(debug_fns) = Self::from_env() {
debug_fns
} else {
self
}
}
}
#[derive(Default, Clone, Debug)]
pub struct ForceShaderModelToken {
inner: Option<DxcShaderModel>,
}
impl ForceShaderModelToken {
pub unsafe fn with_shader_model(sm: DxcShaderModel) -> Self {
Self { inner: Some(sm) }
}
pub fn get(&self) -> Option<DxcShaderModel> {
self.inner.clone()
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Dx12AgilitySDKLoadFailure {
#[default]
Fallback,
Error,
}
impl Dx12AgilitySDKLoadFailure {
#[must_use]
pub fn from_env() -> Option<Self> {
let value = crate::env::var("WGPU_DX12_AGILITY_SDK_REQUIRE")?;
match value.as_str() {
"1" => Some(Self::Error),
"0" => Some(Self::Fallback),
_ => None,
}
}
#[must_use]
pub fn with_env(self) -> Self {
if let Some(v) = Self::from_env() {
v
} else {
self
}
}
}
#[derive(Clone, Debug)]
pub struct Dx12AgilitySDK {
pub sdk_version: u32,
pub sdk_path: String,
pub on_load_failure: Dx12AgilitySDKLoadFailure,
}
impl Dx12AgilitySDK {
#[must_use]
pub fn from_env() -> Option<Self> {
let sdk_path = crate::env::var("WGPU_DX12_AGILITY_SDK_PATH")?;
let sdk_version_str = crate::env::var("WGPU_DX12_AGILITY_SDK_VERSION")?;
let sdk_version = sdk_version_str.parse::<u32>().ok()?;
let on_load_failure = Dx12AgilitySDKLoadFailure::from_env().unwrap_or_default();
Some(Self {
sdk_version,
sdk_path,
on_load_failure,
})
}
#[must_use]
pub fn with_env(mut self) -> Self {
if let Some(sdk_path) = crate::env::var("WGPU_DX12_AGILITY_SDK_PATH") {
self.sdk_path = sdk_path;
}
if let Some(sdk_version_str) = crate::env::var("WGPU_DX12_AGILITY_SDK_VERSION") {
if let Ok(sdk_version) = sdk_version_str.parse::<u32>() {
self.sdk_version = sdk_version;
}
}
self.on_load_failure = self.on_load_failure.with_env();
self
}
}
#[derive(Clone, Debug, Default)]
pub struct Dx12BackendOptions {
pub shader_compiler: Dx12Compiler,
pub presentation_system: Dx12SwapchainKind,
pub latency_waitable_object: Dx12UseFrameLatencyWaitableObject,
pub force_shader_model: ForceShaderModelToken,
pub agility_sdk: Option<Dx12AgilitySDK>,
}
impl Dx12BackendOptions {
#[must_use]
pub fn from_env_or_default() -> Self {
let compiler = Dx12Compiler::from_env().unwrap_or_default();
let presentation_system = Dx12SwapchainKind::from_env().unwrap_or_default();
let latency_waitable_object =
Dx12UseFrameLatencyWaitableObject::from_env().unwrap_or_default();
let agility_sdk = Dx12AgilitySDK::from_env();
Self {
shader_compiler: compiler,
presentation_system,
latency_waitable_object,
force_shader_model: ForceShaderModelToken::default(),
agility_sdk,
}
}
#[must_use]
pub fn with_env(self) -> Self {
let shader_compiler = self.shader_compiler.with_env();
let presentation_system = self.presentation_system.with_env();
let latency_waitable_object = self.latency_waitable_object.with_env();
let agility_sdk = self
.agility_sdk
.map(|s| s.with_env())
.or_else(Dx12AgilitySDK::from_env);
Self {
shader_compiler,
presentation_system,
latency_waitable_object,
force_shader_model: ForceShaderModelToken::default(),
agility_sdk,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct NoopBackendOptions {
pub enable: bool,
}
impl NoopBackendOptions {
#[must_use]
pub fn from_env_or_default() -> Self {
Self {
enable: Self::enable_from_env().unwrap_or(false),
}
}
#[must_use]
pub fn with_env(self) -> Self {
Self {
enable: Self::enable_from_env().unwrap_or(self.enable),
}
}
fn enable_from_env() -> Option<bool> {
let value = crate::env::var("WGPU_NOOP_BACKEND")?;
match value.as_str() {
"1" => Some(true),
"0" => Some(false),
_ => None,
}
}
}
#[derive(Clone, Debug, Default, Copy, PartialEq, Eq)]
pub enum Dx12SwapchainKind {
#[default]
DxgiFromHwnd,
#[doc = link_to_wgpu_docs!(["CV"]: "struct.SurfaceTargetUnsafe.html#variant.CompositionVisual")]
DxgiFromVisual,
}
impl Dx12SwapchainKind {
#[must_use]
pub fn from_env() -> Option<Self> {
let value = crate::env::var("WGPU_DX12_PRESENTATION_SYSTEM")
.as_deref()?
.to_lowercase();
match value.as_str() {
"dxgifromvisual" | "visual" => Some(Self::DxgiFromVisual),
"dxgifromhwnd" | "hwnd" => Some(Self::DxgiFromHwnd),
_ => None,
}
}
#[must_use]
pub fn with_env(self) -> Self {
if let Some(presentation_system) = Self::from_env() {
presentation_system
} else {
self
}
}
}
#[derive(Clone, Debug)]
#[allow(missing_docs)]
pub enum DxcShaderModel {
V6_0,
V6_1,
V6_2,
V6_3,
V6_4,
V6_5,
V6_6,
V6_7,
V6_8,
V6_9,
}
impl DxcShaderModel {
pub fn from_dxc_version(major: u32, minor: u32) -> Self {
if major > 1 {
Self::V6_9
} else {
Self::from_parts(6, minor)
}
}
pub fn from_parts(major: u32, minor: u32) -> Self {
if major > 6 || minor > 8 {
Self::V6_9
} else {
match minor {
0 => DxcShaderModel::V6_0,
1 => DxcShaderModel::V6_1,
2 => DxcShaderModel::V6_2,
3 => DxcShaderModel::V6_3,
4 => DxcShaderModel::V6_4,
5 => DxcShaderModel::V6_5,
6 => DxcShaderModel::V6_6,
7 => DxcShaderModel::V6_7,
8 => DxcShaderModel::V6_8,
9 => DxcShaderModel::V6_9,
_ => DxcShaderModel::V6_9,
}
}
}
}
#[derive(Clone, Debug, Default)]
pub enum Dx12Compiler {
Fxc,
DynamicDxc {
dxc_path: String,
},
StaticDxc,
#[default]
Auto,
}
impl Dx12Compiler {
pub fn default_dynamic_dxc() -> Self {
Self::DynamicDxc {
dxc_path: String::from("dxcompiler.dll"),
}
}
#[must_use]
pub fn from_env() -> Option<Self> {
let env = crate::env::var("WGPU_DX12_COMPILER")?;
env.parse().map_err(|expected_msg| {
log::warn!(
"Unknown value `{env:?}` for `WGPU_DX12_COMPILER` environment variable. {expected_msg}"
)
})
.ok()
}
#[must_use]
pub fn with_env(self) -> Self {
if let Some(compiler) = Self::from_env() {
compiler
} else {
self
}
}
}
impl FromStr for Dx12Compiler {
type Err = &'static str;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Ok(match value.to_lowercase().as_str() {
"dxc" | "dynamicdxc" => Self::default_dynamic_dxc(),
"staticdxc" => Self::StaticDxc,
"fxc" => Self::Fxc,
"auto" => Self::Auto,
_ => return Err("Expected `dynamicdxc` (alias `dxc`), `staticdxc`, `fxc`, or `auto`."),
})
}
}
#[derive(Clone, Debug, Default)]
pub enum Dx12UseFrameLatencyWaitableObject {
None,
#[default]
Wait,
DontWait,
}
impl Dx12UseFrameLatencyWaitableObject {
#[must_use]
pub fn from_env() -> Option<Self> {
let value = crate::env::var("WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT")
.as_deref()?
.to_lowercase();
match value.as_str() {
"none" => Some(Self::None),
"wait" => Some(Self::Wait),
"dontwait" => Some(Self::DontWait),
_ => None,
}
}
#[must_use]
pub fn with_env(self) -> Self {
if let Some(compiler) = Self::from_env() {
compiler
} else {
self
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
pub enum Gles3MinorVersion {
#[default]
Automatic,
Version0,
Version1,
Version2,
}
impl Gles3MinorVersion {
#[must_use]
pub fn from_env() -> Option<Self> {
let value = crate::env::var("WGPU_GLES_MINOR_VERSION")
.as_deref()?
.to_lowercase();
match value.as_str() {
"automatic" => Some(Self::Automatic),
"0" => Some(Self::Version0),
"1" => Some(Self::Version1),
"2" => Some(Self::Version2),
_ => None,
}
}
#[must_use]
pub fn with_env(self) -> Self {
if let Some(compiler) = Self::from_env() {
compiler
} else {
self
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum GlFenceBehavior {
#[default]
Normal,
AutoFinish,
}
impl GlFenceBehavior {
pub fn is_auto_finish(&self) -> bool {
matches!(self, Self::AutoFinish)
}
pub fn is_normal(&self) -> bool {
matches!(self, Self::Normal)
}
#[must_use]
pub fn from_env() -> Option<Self> {
let value = crate::env::var("WGPU_GL_FENCE_BEHAVIOR")
.as_deref()?
.to_lowercase();
match value.as_str() {
"normal" => Some(Self::Normal),
"autofinish" => Some(Self::AutoFinish),
_ => None,
}
}
#[must_use]
pub fn with_env(self) -> Self {
if let Some(fence) = Self::from_env() {
fence
} else {
self
}
}
}