Skip to main content

ruda_driver_wgpu/
runtime.rs

1use crate::{
2    AutoCompiler, AutoGraphicsApi, GraphicsApi, WgpuDevice, backend, execution::WgpuServer,
3    contiguous_strides,
4};
5use ruda_core::device::{Device, DeviceService};
6use ruda_core::{future, profile::TimingMethod};
7use ruda_kernel::dsl::device::{DeviceId, ServerUtilitiesHandle};
8use ruda_kernel::dsl::server::ServerUtilities;
9use ruda_kernel::dsl::zspace::{Shape, Strides};
10use ruda_kernel::dsl::{Runtime, ir::TargetProperties};
11use ruda_core::ir::{DeviceProperties, HardwareProperties, MemoryDeviceProperties};
12use ruda::runtime::allocator::ContiguousMemoryLayoutPolicy;
13#[cfg(not(feature = "vulkan-validate"))]
14use ruda::runtime::logging::ProfileLevel;
15pub use ruda::runtime::memory_management::MemoryConfiguration;
16use ruda::runtime::{client::ComputeClient, logging::ServerLogger};
17use wgpu::{InstanceFlags, RequestAdapterOptions};
18
19/// Runtime that uses the [wgpu] crate with the wgsl compiler. This is used in the Wgpu backend.
20/// For advanced configuration, use [`init_setup`] to pass in runtime options or to select a
21/// specific graphics API.
22#[derive(Debug, Clone)]
23pub struct WgpuRuntime;
24
25impl Runtime for WgpuRuntime {
26    type Compiler = AutoCompiler;
27    type Server = WgpuServer;
28    type Device = WgpuDevice;
29
30    fn client(device: &Self::Device) -> ComputeClient<Self> {
31        ComputeClient::load(device)
32    }
33
34    fn name(client: &ComputeClient<Self>) -> &'static str {
35        match client.info() {
36            wgpu::Backend::Vulkan => {
37                #[cfg(feature = "spirv")]
38                return "wgpu<spirv>";
39
40                #[cfg(not(feature = "spirv"))]
41                return "wgpu<wgsl>";
42            }
43            wgpu::Backend::Metal => {
44                #[cfg(feature = "msl")]
45                return "wgpu<msl>";
46
47                #[cfg(not(feature = "msl"))]
48                return "wgpu<wgsl>";
49            }
50            _ => "wgpu<wgsl>",
51        }
52    }
53
54    fn max_ruda_count() -> (u32, u32, u32) {
55        let max_dim = u16::MAX as u32;
56        (max_dim, max_dim, max_dim)
57    }
58
59    fn can_read_tensor(shape: &Shape, strides: &Strides) -> bool {
60        if shape.is_empty() {
61            return true;
62        }
63
64        for (&expected, &stride) in contiguous_strides(shape).iter().zip(strides.iter()) {
65            if expected != stride {
66                return false;
67            }
68        }
69
70        true
71    }
72
73    fn target_properties() -> TargetProperties {
74        TargetProperties {
75            // Values are irrelevant, since no wgsl backends currently support manual mma
76            mma: Default::default(),
77        }
78    }
79
80    fn enumerate_devices(type_id: u16, info: &wgpu::Backend) -> Vec<DeviceId> {
81        #[cfg(target_family = "wasm")]
82        {
83            let _ = type_id;
84            let _ = info;
85            // WebGPU only supports a single device currently.
86            vec![DeviceId::new(0, 0)]
87        }
88
89        #[cfg(not(target_family = "wasm"))]
90        {
91            let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
92                backends: wgpu::Backends::all(),
93                ..wgpu::InstanceDescriptor::new_without_display_handle()
94            });
95
96            let adapters = enumerate_all_adapters(instance, *info);
97            adapters
98                .into_iter()
99                .filter(|adapter| {
100                    // Default doesn't filter device types.
101                    if type_id == 4 {
102                        return true;
103                    }
104
105                    let device_type = adapter.get_info().device_type;
106
107                    let adapter_type_id = match device_type {
108                        wgpu::DeviceType::Other => 4,
109                        wgpu::DeviceType::IntegratedGpu => 1,
110                        wgpu::DeviceType::DiscreteGpu => 0,
111                        wgpu::DeviceType::VirtualGpu => 2,
112                        wgpu::DeviceType::Cpu => 3,
113                    };
114
115                    adapter_type_id == type_id
116                })
117                .enumerate()
118                .map(|(index, adapter)| match adapter.get_info().device_type {
119                    wgpu::DeviceType::DiscreteGpu => DeviceId::new(0, index as u16),
120                    wgpu::DeviceType::IntegratedGpu => DeviceId::new(1, index as u16),
121                    wgpu::DeviceType::VirtualGpu => DeviceId::new(2, index as u16),
122                    wgpu::DeviceType::Cpu => DeviceId::new(3, 0),
123                    wgpu::DeviceType::Other => DeviceId::new(4, 0),
124                })
125                .collect()
126        }
127    }
128
129    fn enumerate_all_devices(info: &wgpu::Backend) -> Vec<DeviceId> {
130        #[cfg(target_family = "wasm")]
131        {
132            let _ = info;
133            // WebGPU only supports a single device currently.
134            vec![DeviceId::new(0, 0)]
135        }
136
137        #[cfg(not(target_family = "wasm"))]
138        {
139            let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
140                backends: wgpu::Backends::all(),
141                ..wgpu::InstanceDescriptor::new_without_display_handle()
142            });
143            let adapters = enumerate_all_adapters(instance, *info);
144            adapters
145                .into_iter()
146                .enumerate()
147                .map(|(index, adapter)| match adapter.get_info().device_type {
148                    wgpu::DeviceType::DiscreteGpu => DeviceId::new(0, index as u16),
149                    wgpu::DeviceType::IntegratedGpu => DeviceId::new(1, index as u16),
150                    wgpu::DeviceType::VirtualGpu => DeviceId::new(2, index as u16),
151                    wgpu::DeviceType::Cpu => DeviceId::new(3, 0),
152                    wgpu::DeviceType::Other => DeviceId::new(4, 0),
153                })
154                .collect()
155        }
156    }
157}
158
159#[cfg(not(target_family = "wasm"))]
160fn enumerate_all_adapters(instance: wgpu::Instance, backend: wgpu::Backend) -> Vec<wgpu::Adapter> {
161    // `enumerate_adapters` is now async & available on WebGPU
162    ruda_core::future::block_on(instance.enumerate_adapters(backend.into()))
163}
164
165/// The values that control how a WGPU Runtime will perform its calculations.
166pub struct RuntimeOptions {
167    /// Control the amount of compute tasks to be aggregated into a single GPU command.
168    pub tasks_max: usize,
169    /// Configures the memory management.
170    pub memory_config: MemoryConfiguration,
171}
172
173impl Default for RuntimeOptions {
174    fn default() -> Self {
175        #[cfg(test)]
176        const DEFAULT_MAX_TASKS: usize = 32;
177        #[cfg(not(test))]
178        const DEFAULT_MAX_TASKS: usize = 32;
179
180        let tasks_max = match std::env::var("RUDA_WGPU_MAX_TASKS") {
181            Ok(value) => value
182                .parse::<usize>()
183                .expect("RUDA_WGPU_MAX_TASKS should be a positive integer."),
184            Err(_) => DEFAULT_MAX_TASKS,
185        };
186
187        Self {
188            tasks_max,
189            memory_config: MemoryConfiguration::default(),
190        }
191    }
192}
193
194mod adapters;
195mod device_service;
196mod setup;
197
198pub use setup::{WgpuSetup, init_device, init_setup, init_setup_async};
199pub(crate) use setup::create_setup_for_device;
200pub(crate) use device_service::create_server;