cubecl_wgpu/compute/
storage.rs1use cubecl_core::server::IoError;
2use cubecl_environment::backtrace::BackTrace;
3use cubecl_environment::collections::HashMap;
4use cubecl_server::storage::{ComputeStorage, StorageHandle, StorageId, StorageUtilization};
5use std::{num::NonZeroU64, ptr::NonNull};
6use wgpu::BufferUsages;
7
8const MIN_BUFFER_SIZE: u64 = 32;
12
13pub struct WgpuStorage {
15 memory: HashMap<StorageId, WgpuMemory>,
16 device: wgpu::Device,
17 buffer_usages: BufferUsages,
18 mem_alignment: usize,
19 #[allow(unused, reason = "keep it simple")]
20 vk_storage: bool,
21}
22
23impl core::fmt::Debug for WgpuStorage {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 f.write_str(format!("WgpuStorage {{ device: {:?} }}", self.device).as_str())
26 }
27}
28
29#[derive(Debug, Clone, Copy)]
31pub(crate) struct HostPtr(pub(crate) NonNull<u8>);
32
33unsafe impl Send for HostPtr {}
35unsafe impl Sync for HostPtr {}
36
37#[derive(new, Debug, Clone)]
39pub struct WgpuResource {
40 pub buffer: wgpu::Buffer,
42 pub address: Option<NonZeroU64>,
44 #[new(default)]
46 pub(crate) host_ptr: Option<HostPtr>,
47 pub offset: u64,
49 pub size: u64,
55}
56
57#[derive(new, Debug)]
59pub struct WgpuMemory {
60 pub buffer: wgpu::Buffer,
62 pub address: Option<NonZeroU64>,
64 #[new(default)]
66 pub(crate) host_ptr: Option<HostPtr>,
67}
68
69impl WgpuResource {
70 pub fn as_wgpu_bind_resource(&self) -> wgpu::BindingResource<'_> {
72 let size = NonZeroU64::new(self.size.next_multiple_of(4));
82
83 let binding = wgpu::BufferBinding {
84 buffer: &self.buffer,
85 offset: self.offset,
86 size,
87 };
88 wgpu::BindingResource::Buffer(binding)
89 }
90}
91
92impl WgpuStorage {
94 pub fn new(
96 mem_alignment: usize,
97 device: wgpu::Device,
98 usages: BufferUsages,
99 vk_storage: bool,
100 ) -> Self {
101 Self {
102 memory: HashMap::new(),
103 device,
104 buffer_usages: usages,
105 mem_alignment,
106 vk_storage,
107 }
108 }
109}
110
111impl ComputeStorage for WgpuStorage {
112 type Resource = WgpuResource;
113
114 fn alignment(&self) -> usize {
115 self.mem_alignment
116 }
117
118 fn get(&mut self, handle: &StorageHandle) -> Result<Self::Resource, IoError> {
119 let memory = self
120 .memory
121 .get(&handle.id)
122 .ok_or_else(|| IoError::StorageHandleNotFound {
123 reason: format!("{} in the wgpu buffer storage", handle.id).into(),
124 backtrace: BackTrace::capture(),
125 })?;
126 Ok(WgpuResource {
127 host_ptr: memory.host_ptr,
128 ..WgpuResource::new(
129 memory.buffer.clone(),
130 memory.address,
131 handle.offset(),
132 handle.size(),
133 )
134 })
135 }
136
137 #[cfg_attr(
138 feature = "tracing",
139 tracing::instrument(level = "trace", skip(self, size))
140 )]
141 fn alloc(&mut self, size: u64) -> Result<StorageHandle, IoError> {
142 let id = StorageId::new();
143
144 let alloc_size = size.max(MIN_BUFFER_SIZE);
145
146 let memory = self.create_buffer(&wgpu::BufferDescriptor {
147 label: None,
148 size: alloc_size,
149 usage: self.buffer_usages,
150 mapped_at_creation: false,
151 })?;
152
153 self.memory.insert(id, memory);
154 Ok(StorageHandle::new(
155 id,
156 StorageUtilization { offset: 0, size },
157 ))
158 }
159
160 #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self)))]
161 fn dealloc(&mut self, id: StorageId) {
162 self.memory.remove(&id);
163 }
164
165 fn flush(&mut self) {
166 }
168}
169
170impl WgpuStorage {
171 #[cfg(feature = "spirv")]
172 fn create_buffer(&self, desc: &wgpu::BufferDescriptor<'_>) -> Result<WgpuMemory, IoError> {
173 if self.vk_storage {
174 crate::backend::vulkan::create_storage_buffer(&self.device, desc)
180 } else {
181 Ok(WgpuMemory::new(self.device.create_buffer(desc), None))
182 }
183 }
184
185 #[cfg(not(feature = "spirv"))]
186 fn create_buffer(&self, desc: &wgpu::BufferDescriptor<'_>) -> Result<WgpuMemory, IoError> {
187 Ok(WgpuMemory::new(self.device.create_buffer(desc), None))
188 }
189}