use super::*;
static GPU_CTX: OnceLock<GpuContext> = OnceLock::new();
pub struct Gpu;
impl Deref for Gpu
{
type Target = GpuContext;
fn deref(&self) -> &Self::Target { GPU_CTX.get().expect("gpu not init") }
}
impl Gpu
{
pub fn is_init() -> bool { GPU_CTX.get().is_some() }
pub fn is_not_init() -> bool { !Self::is_init() }
}
impl<F> AsyncRunner<F, GpuParam> for Gpu
where
F: AsyncFnOnce(GpuInitOutput),
{
type Output = GpuResult;
async fn run_with_param(f: F, param: GpuParam) -> Self::Output
{
let output = Self::new(param).await?;
f(output);
Ok(())
}
}
impl Gpu
{
pub async fn new(param: GpuParam) -> GpuResult<GpuInitOutput>
{
if Gpu::is_init()
{
return Err(GpuError::GpuAlreadyInit);
}
Self::from_init(GpuInit::new(param).await?).await
}
pub async fn from_init(gpu: GpuInit) -> GpuResult<GpuInitOutput>
{
let GpuInit { gpu, output } = gpu;
match GPU_CTX.try_insert(gpu)
{
Ok(_) => Ok(output),
Err(_) => Err(GpuError::GpuAlreadyInit),
}
}
}
#[derive(Default)]
pub struct GpuParam
{
pub instance: InstanceDescriptor,
pub wgpu_instance: WgpuInstanceDescriptor,
pub power_preference: PowerPreference,
pub compatible_surface: Option<wgpu::SurfaceTarget<'static>>,
}
impl Debug for GpuParam
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
{
f.debug_struct("GpuParam")
.field("instance", &self.instance)
.field("power_preference", &self.power_preference)
.field("compatible_surface", &self.compatible_surface.is_some())
.finish()
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PowerPreference
{
#[default]
None = 0,
LowPower = 1,
HighPerformance = 2,
}
impl Into<wgpu::PowerPreference> for PowerPreference
{
fn into(self) -> wgpu::PowerPreference
{
match self
{
PowerPreference::None => wgpu::PowerPreference::None,
PowerPreference::LowPower => wgpu::PowerPreference::LowPower,
PowerPreference::HighPerformance => wgpu::PowerPreference::HighPerformance,
}
}
}
#[derive(Debug)]
pub struct GpuInit
{
gpu: GpuContext,
output: GpuInitOutput,
}
impl GpuInit
{
pub async fn from_instance_and_surface(
instance: GpuInstance,
surface: Option<GpuSurface<'static>>,
param: GpuParam,
) -> GpuResult<Self>
{
let adapter = instance
.wgpu
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: param.power_preference.into(),
force_fallback_adapter: false,
compatible_surface: surface.as_ref().map(|s| &s.wgpu),
})
.await?;
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
label: None,
required_features: wgpu::Features::empty(),
required_limits: if cfg!(target_arch = "wasm32")
{
wgpu::Limits::downlevel_webgl2_defaults()
}
else
{
wgpu::Limits::default()
},
memory_hints: Default::default(),
trace: wgpu::Trace::Off,
})
.await?;
Ok(GpuInit {
gpu: GpuContext {
wgpu: WgpuContext {
instance,
adapter,
device,
queue,
},
},
output: GpuInitOutput {
surface: surface.map(|wgpu| wgpu.into()),
},
})
}
pub async fn new(mut param: GpuParam) -> GpuResult<Self>
{
let instance = GpuInstance::new(¶m.instance);
let surface = match param.compatible_surface.take()
{
Some(s) => Some(instance.wgpu.create_surface(s)?),
None => None,
};
Self::from_instance_and_surface(instance, surface.map(|v| v.into()), param).await
}
}
#[derive(Debug)]
pub struct GpuInitOutput
{
pub surface: Option<GpuSurface<'static>>,
}
#[bit_index]
#[repr(u8)]
pub enum Backend
{
Noop,
Vulkan,
Gl,
Metal,
Dx12,
BrowserWebgpu,
Primary = Self::Vulkan | Self::Metal | Self::Dx12 | Self::BrowserWebgpu,
Secondary = Self::Gl,
Debug = Self::Gl,
}
impl From<Backend> for wgpu::Backends
{
fn from(value: Backend) -> Self
{
use wgpu::Backends as B;
match value
{
Backend::Noop => B::NOOP,
Backend::Vulkan => B::VULKAN,
Backend::Gl => B::GL,
Backend::Metal => B::METAL,
Backend::Dx12 => B::DX12,
Backend::BrowserWebgpu => B::BROWSER_WEBGPU,
}
}
}
impl From<BackendFlags> for wgpu::Backends
{
fn from(backends: BackendFlags) -> Self
{
let mut flags = wgpu::Backends::empty();
for backend in backends
{
flags |= backend.into();
}
flags
}
}
impl Default for BackendFlags
{
fn default() -> Self
{
if cfg!(debug_assertions)
{
BackendFlags::Debug
}
else
{
BackendFlags::Primary | BackendFlags::Secondary
}
}
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct GpuContext
{
pub wgpu: WgpuContext,
}
#[derive(Debug, Clone)]
pub struct WgpuContext
{
pub instance: GpuInstance,
pub adapter: wgpu::Adapter,
pub device: wgpu::Device,
pub queue: wgpu::Queue,
}
impl WgpuContext
{
pub fn wgpu_instance(&self) -> &wgpu::Instance { &self.instance.wgpu }
pub fn wgpu_adapter(&self) -> &wgpu::Adapter { &self.adapter }
pub fn wgpu_device(&self) -> &wgpu::Device { &self.device }
pub fn wgpu_queue(&self) -> &wgpu::Queue { &self.queue }
}
pub fn instance() -> impl Deref<Target = GpuInstance> { &Gpu.wgpu.instance }
pub fn wgpu_instance() -> impl Deref<Target = wgpu::Instance> { &Gpu.wgpu.instance.wgpu }
pub fn wgpu_adapter() -> impl Deref<Target = wgpu::Adapter> { &Gpu.wgpu.adapter }
pub fn wgpu_device() -> impl Deref<Target = wgpu::Device> { &Gpu.wgpu.device }
pub fn wgpu_queue() -> impl Deref<Target = wgpu::Queue> { &Gpu.wgpu.queue }