1use cubecl_core::server::ServerStorage;
2use cubecl_server::kernel::BufferIOAttr;
3use cubecl_server::kernel::DebugInformation;
4use std::collections::HashMap;
5use std::marker::PhantomData;
6
7use super::graph::WgpuGraph;
8use super::storage::{WgpuResource, WgpuStorage};
9use crate::WgpuCompiler;
10use crate::backend::ModuleSource;
11use crate::schedule::{BindingsResource, ScheduleTask, ScheduledWgpuBackend};
12use alloc::sync::Arc;
13use cubecl_common::pool::LeasePool;
14use cubecl_common::{
15 bytes::Bytes,
16 profile::{ProfileDuration, TimingMethod},
17};
18use cubecl_core::server::{BufferBinding, KernelResource};
19use cubecl_core::zspace::Shape;
20use cubecl_core::{
21 MemoryConfiguration, WgpuCompilationOptions,
22 prelude::*,
23 server::{
24 CopyDescriptor, IoError, KernelArguments, LaunchError, ProfileError, ProfilingToken,
25 ServerCommunication, ServerError, ServerUtilities,
26 },
27 zspace::{Strides, strides},
28};
29use cubecl_environment::backtrace::BackTrace;
30use cubecl_environment::future::DynFut;
31#[cfg(feature = "spirv")]
32use cubecl_environment::persistence::Store;
33use cubecl_environment::stream::StreamId;
34use cubecl_ir::MemoryDeviceProperties;
35use cubecl_server::compiler::CompilationRecording;
36#[cfg(feature = "spirv")]
37use cubecl_server::compiler::{KernelCacheKey, compilation_store, store_compiled};
38use cubecl_server::memory_management::{
39 InstallMemoryPoolsError, ManagedMemoryHandle, MemoryReport, MemoryUsage, SharedMemoryBindings,
40};
41use cubecl_server::{
42 compiler::CompilationCache,
43 config::{CubeClRuntimeConfig, RuntimeConfig},
44 dry_run::LaunchMode,
45 id::GraphId,
46 kernel::CubeKernel,
47 logging::ServerLogger,
48 memory_management::MemoryAllocationMode,
49 server::Server,
50 storage::ManagedResource,
51 stream::scheduler::{
52 SchedulerMultiStream, SchedulerMultiStreamOptions, SchedulerStrategy,
53 SchedulerStreamBackend,
54 },
55 stream::{ExecuteScope, FailureStore, StreamCapture, WriteScoped, failed_writing},
56 validation::{validate_cube_dim, validate_units},
57};
58use wgpu::ComputePipeline;
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum ParamsTransfer {
62 Immediate,
63 Uniform,
64}
65
66pub type PipelineEntry = (
72 Arc<ComputePipeline>,
73 CompilerInfo,
74 Option<Arc<[BufferIOAttr]>>,
75);
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum CompilerInfo {
79 Vulkan { params_transfer: ParamsTransfer },
80 Metal,
81 WGSL,
82 None,
83}
84
85#[derive(Debug)]
87pub struct WgpuServer<C: WgpuCompiler> {
88 pub(crate) device: wgpu::Device,
89 streams_pool: Vec<StreamId>,
91 pipelines: CompilationCache<KernelId, PipelineEntry>,
94 scheduler: SchedulerMultiStream<ScheduledWgpuBackend>,
95 #[cfg(feature = "spirv")]
96 pub(crate) spirv_cache: Option<Store<(u64, KernelCacheKey), cubecl_spirv::SpirvCacheEntry>>,
97 #[cfg(feature = "spirv")]
98 pub(crate) build_id: cubecl_common::hash::StableHash,
99 pub compilation_options: WgpuCompilationOptions,
100 pub(crate) backend: wgpu::Backend,
101 pub(crate) utilities: Arc<ServerUtilities>,
102 shared_bindings_pool: LeasePool<SharedMemoryBindings>,
104 graphs: HashMap<GraphId, WgpuGraph>,
108 _compiler: PhantomData<C>,
109}
110
111impl<C: WgpuCompiler> ServerCommunication for WgpuServer<C> {}
112
113impl<C: WgpuCompiler> WriteScoped for WgpuServer<C> {
114 type Streams = SchedulerMultiStream<ScheduledWgpuBackend>;
115
116 fn write_streams(&mut self) -> &mut Self::Streams {
117 &mut self.scheduler
118 }
119
120 fn on_failure(&mut self, stream: StreamId, error: &ServerError) {
121 self.scheduler.stream(&stream).profile_failure(error);
124 }
125
126 fn capturing(&mut self, stream: StreamId) -> Option<&mut StreamCapture> {
127 Some(&mut self.scheduler.stream(&stream).capturing)
128 }
129}
130
131impl<C: WgpuCompiler> WgpuServer<C> {
132 #[allow(clippy::too_many_arguments)]
134 pub fn new(
135 memory_properties: MemoryDeviceProperties,
136 memory_config: MemoryConfiguration,
137 compilation_options: WgpuCompilationOptions,
138 device: wgpu::Device,
139 queue: wgpu::Queue,
140 tasks_max: usize,
141 backend: wgpu::Backend,
142 timing_method: TimingMethod,
143 utilities: ServerUtilities,
144 ) -> Self {
145 #[cfg(feature = "spirv")]
146 let adapter_info = device.adapter_info();
147 let backend_scheduler = ScheduledWgpuBackend::new(
148 device.clone(),
149 queue.clone(),
150 memory_properties,
151 memory_config,
152 timing_method,
153 backend,
154 tasks_max,
155 utilities.logger.clone(),
156 compilation_options.supports_vulkan_compiler,
157 );
158
159 let config = CubeClRuntimeConfig::get();
160 let max_streams = config.streaming.max_streams;
161
162 #[cfg(feature = "spirv")]
163 let spirv_cache = compilation_store(
164 "vulkan",
165 format!("spirv_{}_{}", adapter_info.vendor, adapter_info.device),
166 );
167
168 #[cfg(feature = "spirv")]
171 let pipelines = CompilationCache::mirroring(&spirv_cache);
172 #[cfg(not(feature = "spirv"))]
173 let pipelines = CompilationCache::unbound();
174
175 Self {
176 compilation_options,
177 streams_pool: Vec::new(),
178 device,
179 pipelines,
180 scheduler: SchedulerMultiStream::new(
181 utilities.logger.clone(),
182 backend_scheduler,
183 SchedulerMultiStreamOptions {
184 max_streams,
185 max_tasks: tasks_max,
186 strategy: SchedulerStrategy::Interleave,
187 },
188 ),
189 #[cfg(feature = "spirv")]
190 spirv_cache,
191 #[cfg(feature = "spirv")]
192 build_id: cubecl_server::compiler::build_id_hash(),
193 backend,
194 utilities: Arc::new(utilities),
195 shared_bindings_pool: LeasePool::with_capacity(tasks_max * max_streams as usize),
196 graphs: HashMap::new(),
197 _compiler: PhantomData,
198 }
199 }
200
201 fn prepare_bindings(
202 &mut self,
203 bindings: KernelArguments,
204 compiler_info: CompilerInfo,
205 ) -> Result<BindingsResource, IoError> {
206 let mut resources = Vec::with_capacity(bindings.resources.len());
209
210 for resource in bindings.resources.into_iter() {
211 match resource {
212 KernelResource::Buffer(b) => {
213 let stream = self.scheduler.stream(&b.stream);
214 let resource = stream.mem_manage.get_resource(b)?;
215 resources.push(resource);
216 }
217 KernelResource::TensorMap(_) => panic!("Tensor map not supported in wgpu"),
218 }
219 }
220
221 Ok(BindingsResource {
222 resources,
223 info: bindings.info,
224 compiler_info,
225 })
226 }
227
228 fn pipeline(
229 &mut self,
230 kernel: Box<dyn CubeKernel>,
231 bindings: &KernelArguments,
232 ) -> Result<PipelineEntry, LaunchError> {
233 let kernel_id = kernel.id();
234 let mode = kernel_id.mode;
235
236 if let Some(pipeline) = self.pipelines.get(&kernel_id) {
237 return Ok(pipeline.clone());
238 }
239
240 let mut recording = CompilationRecording::new(&kernel_id);
241 let cached = self.load_cached_pipeline(&kernel_id, bindings, mode)?;
242
243 if let Some(Ok(pipeline)) = cached {
244 self.pipelines.insert(kernel_id, pipeline.clone());
245 recording.loaded();
246 return Ok(pipeline);
247 }
248
249 validate_cube_dim(&self.utilities.properties, &kernel_id)?;
250 validate_units(&self.utilities.properties, &kernel_id)?;
251
252 let definition = kernel.define();
253 recording.defined(&definition);
254
255 let mut compiler = C::init(self.backend, &self.compilation_options);
256 let mut compiled = compiler.compile_kernel(self, kernel, definition)?;
257
258 if self.scheduler.logger.compilation_source_activated() {
259 compiled.debug_info = Some(DebugInformation::new(
260 compiler.lang_tag(),
261 kernel_id.clone(),
262 ));
263 }
264 self.scheduler.logger.log_compilation(&compiled);
265
266 compiler.validate_ir(&compiled.repr, &self.utilities.properties)?;
267 let io = compiled.io.take().map(Arc::from);
270 let (compiler_info, auto_repr) = compiler.normalize_repr(compiled.repr);
271 let repr = auto_repr.as_ref().map(|r| r.as_ref());
272
273 let module = self.create_module(
300 &compiled.entrypoint_name,
301 kernel_id.cube_dim.into(),
302 ModuleSource::resolve(repr, compiler.lang_tag(), &compiled.source)?,
303 mode,
304 )?;
305 let pipeline = self.create_pipeline(&compiled.entrypoint_name, repr, module, bindings);
306 self.pipelines.insert(
307 kernel_id.clone(),
308 (pipeline.clone(), compiler_info, io.clone()),
309 );
310
311 recording.source(&compiled.source);
312
313 let stored = false;
315 #[cfg(feature = "spirv")]
316 let stored = match (cached, auto_repr) {
317 (Some(Err(key)), Some(crate::AutoRepresentation::SpirV(kernel))) => {
318 let cache = self.spirv_cache.as_mut().unwrap();
319 store_compiled(
320 cache,
321 key,
322 cubecl_spirv::SpirvCacheEntry::new(compiled.entrypoint_name, kernel),
323 )
324 }
325 _ => stored,
326 };
327 recording.compiled(stored);
328
329 Ok((pipeline, compiler_info, io))
330 }
331}
332
333impl<C: WgpuCompiler> Server for WgpuServer<C> {
334 fn logger(&self) -> Arc<ServerLogger> {
335 self.scheduler.logger.clone()
336 }
337
338 fn utilities(&self) -> Arc<ServerUtilities> {
339 self.utilities.clone()
340 }
341
342 fn staging(
343 &mut self,
344 _sizes: &[usize],
345 _stream_id: StreamId,
346 ) -> Result<Vec<Bytes>, ServerError> {
347 Err(IoError::UnsupportedIoOperation {
349 backtrace: BackTrace::capture(),
350 }
351 .into())
352 }
353
354 fn initialize_memory(&mut self, memory: ManagedMemoryHandle, size: u64, stream_id: StreamId) {
355 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
356 let reserved = stream
357 .empty(size, failures)
358 .unwrap_or_else(|err| panic!("failed to reserve {size} bytes of device memory: {err}"));
359 stream.mem_manage.bind(reserved, memory, failures);
360 }
361
362 fn read(
363 &mut self,
364 descriptors: Vec<CopyDescriptor>,
365 stream_id: StreamId,
366 ) -> DynFut<Result<Vec<Bytes>, ServerError>> {
367 if let Err(err) = self
370 .scheduler
371 .stream(&stream_id)
372 .reject_while_recording("read")
373 {
374 return Box::pin(async move { Err(err) });
375 }
376
377 if let Err(err) = self
381 .scheduler
382 .ensure_written(descriptors.iter().map(|d| &d.handle))
383 {
384 return Box::pin(async move { Err(err) });
385 }
386
387 let mut streams = vec![stream_id];
388 let mut resources = Vec::with_capacity(descriptors.len());
389 for desc in descriptors {
390 if contiguous_strides(&desc.shape) != desc.strides {
391 return Box::pin(async {
392 Err(IoError::UnsupportedStrides {
393 backtrace: BackTrace::capture(),
394 }
395 .into())
396 });
397 }
398 if !streams.contains(&desc.handle.stream) {
399 streams.push(desc.handle.stream);
400 }
401 let stream = self.scheduler.stream(&desc.handle.stream);
402 let resource = match stream.mem_manage.get_resource(desc.handle) {
403 Ok(val) => val,
404 Err(err) => return Box::pin(async move { Err(err.into()) }),
405 };
406 resources.push((resource, desc.shape, desc.elem_size));
407 }
408
409 self.scheduler.execute_streams(streams);
410
411 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
412 stream.read_resources(resources, stream_id, failures)
413 }
414
415 fn write(&mut self, descriptors: Vec<(CopyDescriptor, Bytes)>, stream_id: StreamId) {
416 {
426 let recording = self
427 .scheduler
428 .stream(&stream_id)
429 .reject_while_recording("write");
430 if let Err(err) = recording {
431 self.scheduler.taint(
436 err.clone(),
437 descriptors.iter().map(|(desc, _)| &desc.handle),
438 );
439 let stream = self.scheduler.stream(&stream_id);
444 if stream.capturing.owner() == Some(stream_id) {
445 stream.capturing.fail(err);
446 }
447 return;
448 }
449 }
450 for (desc, data) in descriptors {
451 let mut written = self.write_set();
460 written.push(desc.handle.clone());
461 ExecuteScope::over(self, stream_id, written).execute(|server| {
462 if contiguous_strides(&desc.shape) != desc.strides {
463 return Err(ServerError::Io(IoError::UnsupportedStrides {
464 backtrace: BackTrace::capture(),
465 }));
466 }
467
468 let owner = desc.handle.stream;
473 let handle = desc.handle.clone();
474 let stream = server.scheduler.stream(&owner);
475 let resource = stream
476 .mem_manage
477 .get_resource(desc.handle)
478 .map_err(ServerError::Io)?;
479 let task = ScheduleTask::Write {
480 data,
481 buffer: resource,
482 handle,
483 };
484
485 server.scheduler.register(stream_id, task, &[owner]);
486 Ok(())
487 });
488 }
489 }
490
491 fn check(
492 &mut self,
493 handles: Vec<BufferBinding>,
494 _stream_id: StreamId,
495 ) -> Result<(), ServerError> {
496 self.scheduler.ensure_written(handles.iter())
497 }
498
499 unsafe fn launch(
500 &mut self,
501 kernel: Box<dyn CubeKernel>,
502 count: CubeCount,
503 args: KernelArguments,
504 stream_id: StreamId,
505 launch_mode: LaunchMode,
506 ) {
507 let kernel_id = kernel.id();
520 let (pipeline, compiler_info, io) = match self.pipeline(kernel, &args) {
521 Ok(val) => val,
522 Err(err) => {
523 let error = ServerError::Launch(err);
524 self.scheduler.stream(&stream_id).profile_failure(&error);
525 if !launch_mode.is_skipped() {
526 let mut written = self.write_set();
527 written.extend(args.buffers_written(None).cloned());
528 failed_writing(self, stream_id, written, error);
529 }
530 return;
531 }
532 };
533 if launch_mode.is_skipped() {
534 return;
535 }
536
537 let mut written = self.write_set();
555 written.extend(args.buffers_written(io.as_deref()).cloned());
556 let count_read = match &count {
561 CubeCount::Dynamic(binding) => Some(binding),
562 CubeCount::Static(..) => None,
563 };
564 ExecuteScope::launching(
565 self,
566 kernel_id,
567 stream_id,
568 args.buffers_read(io.as_deref()).chain(count_read),
569 written,
570 )
571 .execute(|server| {
572 server.streams_pool.clear();
573 let mut shared_inputs = server.shared_bindings_pool.acquire();
576 args.resources.iter().for_each(|resource| match resource {
578 KernelResource::Buffer(b) => {
579 server.streams_pool.push(b.stream);
580 if b.stream != stream_id {
581 shared_inputs.push(b.memory.clone());
582 }
583 }
584 KernelResource::TensorMap(_) => {
585 panic!("Tensor maps not supported in WGPU")
586 }
587 });
588
589 let resources = server
590 .prepare_bindings(args, compiler_info)
591 .map_err(ServerError::Io)?;
592
593 let task = ScheduleTask::Execute {
594 pipeline,
595 count,
596 resources,
597 shared_inputs,
598 };
599
600 server
601 .scheduler
602 .register(stream_id, task, &server.streams_pool);
603 Ok(())
604 });
605 }
606
607 fn flush(&mut self, stream_id: StreamId) -> Result<(), ServerError> {
608 self.scheduler.execute_streams(vec![stream_id]);
609
610 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
611
612 stream.flush(stream_id, failures)
613 }
614
615 fn sync(
617 &mut self,
618 handles: Vec<BufferBinding>,
619 stream_id: StreamId,
620 ) -> DynFut<Result<(), ServerError>> {
621 if let Err(err) = self
622 .scheduler
623 .stream(&stream_id)
624 .reject_while_recording("sync")
625 {
626 return Box::pin(async move { Err(err) });
627 }
628 if let Err(err) = self.scheduler.ensure_written(handles.iter()) {
631 return Box::pin(async move { Err(err) });
632 }
633 self.scheduler.execute_streams(vec![stream_id]);
634 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
635
636 stream.sync(stream_id, failures)
637 }
638
639 fn start_profile(&mut self, stream_id: StreamId) -> Result<ProfilingToken, ServerError> {
640 self.scheduler
643 .stream(&stream_id)
644 .reject_while_recording("start_profile")?;
645 self.scheduler.execute_streams(vec![stream_id]);
646 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
647 stream.start_profile(stream_id, failures)
648 }
649
650 fn end_profile(
651 &mut self,
652 stream_id: StreamId,
653 token: ProfilingToken,
654 ) -> Result<ProfileDuration, ProfileError> {
655 self.scheduler.execute_streams(vec![stream_id]);
656 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
657
658 stream.end_profile(token, stream_id, failures)
659 }
660
661 fn abandon_profile(&mut self, stream_id: StreamId, token: ProfilingToken) {
662 self.scheduler.stream(&stream_id).abandon_profile(token);
666 }
667
668 fn memory_usage(&mut self, stream_id: StreamId) -> MemoryUsage {
669 self.scheduler.execute_streams(vec![stream_id]);
670 self.scheduler.stream(&stream_id).mem_manage.memory_usage()
671 }
672
673 fn memory_report(&mut self, stream_id: StreamId) -> MemoryReport {
674 self.scheduler.execute_streams(vec![stream_id]);
675 self.scheduler.stream(&stream_id).mem_manage.memory_report()
676 }
677
678 fn stream_ids(&self) -> Vec<StreamId> {
679 self.scheduler.stream_ids().collect()
680 }
681
682 fn memory_cleanup(&mut self, stream_id: StreamId) {
683 self.scheduler.execute_streams(vec![stream_id]);
684 let stream = self.scheduler.stream(&stream_id);
685 stream.info_cache.clear_unpinned();
690 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
691 stream.mem_manage.memory_cleanup(true, failures);
692 }
693
694 fn allocation_mode(&mut self, mode: MemoryAllocationMode, stream_id: StreamId) {
695 self.scheduler.execute_streams(vec![stream_id]);
696 let stream = self.scheduler.stream(&stream_id);
697 stream.mem_manage.mode(mode);
698 }
699
700 fn install_memory_pools(
701 &mut self,
702 config: MemoryConfiguration,
703 stream_id: StreamId,
704 ) -> Result<(), InstallMemoryPoolsError> {
705 self.scheduler
708 .backend_mut()
709 .factory()
710 .set_gpu_pools(config.clone());
711 let (_, props) = self.scheduler.backend_mut().factory().gpu_pools();
712
713 self.scheduler.execute_streams(vec![stream_id]);
716 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
717 stream
718 .mem_manage
719 .install_memory_pools(config, &props, failures)
720 }
721
722 fn graph_prepare(&mut self, stream_id: StreamId) -> Result<(), ServerError> {
723 self.scheduler.execute_streams(vec![stream_id]);
726 let stream = self.scheduler.stream(&stream_id);
727
728 stream.capturing.prepare(stream_id)?;
729
730 stream.mem_manage.capture_begin();
737 Ok(())
738 }
739
740 fn begin_capture(&mut self, stream_id: StreamId) -> Result<(), ServerError> {
741 self.scheduler.execute_streams(vec![stream_id]);
744 let stream = self.scheduler.stream(&stream_id);
745
746 stream.capturing.begin()?;
747
748 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
752 if let Err(err) = stream.flush(stream_id, failures) {
753 stream.mem_manage.capture_end();
757 stream.info_cache.capture_discard();
758 stream.capturing.abort();
759 return Err(err);
760 }
761
762 stream.mem_manage.capture_priming_end();
765 Ok(())
766 }
767
768 fn end_capture(&mut self, stream_id: StreamId) -> Result<GraphId, ServerError> {
769 self.scheduler.execute_streams(vec![stream_id]);
771 let stream = self.scheduler.stream(&stream_id);
772
773 let outcome = match stream.capturing.end(stream_id) {
780 Ok(outcome) => outcome,
781 Err(err) => {
782 if stream.capturing.is_active() {
790 stream.mem_manage.capture_end();
791 stream.info_cache.capture_discard();
792 stream.capturing.abort();
793 }
794 return Err(err);
795 }
796 };
797 let recording = stream.take_recording();
798 let written = stream.capturing.take_recorded();
802 let mut retained = stream.mem_manage.capture_end();
803
804 let doomed = stream.capturing.take_failure().map(|reason| {
810 ServerError::graph_state(format!(
811 "an operation inside the capture window failed, so the recording is missing \
812 an operation and cannot seal: {reason}"
813 ))
814 });
815 let discarded = match outcome.is_abandoned() {
816 true => Some(outcome.abandoned_error(stream_id, doomed)),
817 false => doomed,
818 };
819 if let Some(err) = discarded {
820 stream.info_cache.capture_discard();
821 self.scheduler.taint(err.clone(), written.iter());
826 return Err(err);
827 }
828
829 let id = GraphId::new();
830 stream.info_cache.capture_commit(id);
833 retained.extend(recording.uniform_pins);
834 self.graphs.insert(
835 id,
836 WgpuGraph {
837 tasks: recording.tasks,
838 _retained: retained,
839 _shared: recording.shared,
840 written,
841 },
842 );
843 Ok(id)
844 }
845
846 fn replay(&mut self, graph: GraphId, stream_id: StreamId) -> Result<(), ServerError> {
847 self.scheduler.execute_streams(vec![stream_id]);
849
850 let Some(wgpu_graph) = self.graphs.get(&graph) else {
855 return Err(ServerError::graph_state(
856 "replay was given an unknown or already-destroyed graph",
857 ));
858 };
859
860 let recorded = wgpu_graph.written.clone();
868 let mut written = self.write_set();
869 written.extend(recorded);
870 ExecuteScope::over(self, stream_id, written)
871 .execute(|server| {
872 server
873 .scheduler
874 .stream(&stream_id)
875 .reject_while_recording("replay")?;
876 let wgpu_graph = server
877 .graphs
878 .get(&graph)
879 .expect("checked above; nothing in the scope removes graphs");
880 let (stream, failures) = server.scheduler.stream_and_failures(&stream_id);
881 stream.replay_graph(wgpu_graph, failures);
882 Ok(())
883 })
884 .into_result()
885 }
886
887 fn graph_destroy(&mut self, graph: GraphId, stream_id: StreamId) {
888 let Some(wgpu_graph) = self.graphs.remove(&graph) else {
891 return;
892 };
893 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
894 stream.submit(failures);
903 stream.info_cache.graph_release(graph);
906 drop(wgpu_graph);
907 }
908}
909
910pub(crate) fn contiguous_strides(shape: &Shape) -> Strides {
911 let rank = shape.len();
912 let mut strides = strides![1; rank];
913 for i in (0..rank - 1).rev() {
914 strides[i] = strides[i + 1] * shape[i + 1];
915 }
916 strides
917}
918
919impl<C: WgpuCompiler> ServerStorage for WgpuServer<C> {
920 type Storage = WgpuStorage;
921
922 fn get_resource(
923 &mut self,
924 binding: BufferBinding,
925 stream_id: StreamId,
926 ) -> Result<ManagedResource<WgpuResource>, ServerError> {
927 self.scheduler.ensure_written([&binding].into_iter())?;
931 let mut streams = vec![stream_id];
932 if binding.stream != stream_id {
933 streams.push(binding.stream);
934 }
935 self.scheduler.execute_streams(streams);
936 let stream = self.scheduler.stream(&binding.stream);
937 let memory = binding.memory.clone();
938 let resource = stream.mem_manage.get_resource(binding)?;
939
940 Ok(ManagedResource::new(memory, resource))
941 }
942}