1use cubecl_core::server::{DeviceCaptures, 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 ManagedMemoryHandle, SharedMemoryBindings, StreamMemoryReport,
40 relocation::{RelocatingStreams, RelocationReason},
41};
42use cubecl_server::{
43 compiler::CompilationCache,
44 config::{CubeClRuntimeConfig, RuntimeConfig},
45 dry_run::LaunchMode,
46 id::GraphId,
47 kernel::CubeKernel,
48 logging::ServerLogger,
49 memory_management::MemoryAllocationMode,
50 server::Server,
51 storage::ManagedResource,
52 stream::scheduler::{SchedulerMultiStream, SchedulerMultiStreamOptions, SchedulerStrategy},
53 stream::{ExecuteScope, FailureStore, StreamCapture, WriteScoped, failed_writing},
54 validation::{validate_cube_dim, validate_units},
55};
56use wgpu::ComputePipeline;
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum ParamsTransfer {
60 Immediate,
61 Uniform,
62}
63
64pub type PipelineEntry = (
70 Arc<ComputePipeline>,
71 CompilerInfo,
72 Option<Arc<[BufferIOAttr]>>,
73);
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum CompilerInfo {
77 Vulkan { params_transfer: ParamsTransfer },
78 Metal,
79 WGSL,
80 None,
81}
82
83#[derive(Debug)]
85pub struct WgpuServer<C: WgpuCompiler> {
86 pub(crate) device: wgpu::Device,
87 streams_pool: Vec<StreamId>,
89 pipelines: CompilationCache<KernelId, PipelineEntry>,
92 scheduler: SchedulerMultiStream<ScheduledWgpuBackend>,
93 #[cfg(feature = "spirv")]
94 pub(crate) spirv_cache: Option<Store<(u64, KernelCacheKey), cubecl_spirv::SpirvCacheEntry>>,
95 #[cfg(feature = "spirv")]
96 pub(crate) build_id: cubecl_common::hash::StableHash,
97 pub compilation_options: WgpuCompilationOptions,
98 pub(crate) backend: wgpu::Backend,
99 pub(crate) utilities: Arc<ServerUtilities>,
100 shared_bindings_pool: LeasePool<SharedMemoryBindings>,
102 graphs: HashMap<GraphId, WgpuGraph>,
106 _compiler: PhantomData<C>,
107}
108
109impl<C: WgpuCompiler> ServerCommunication for WgpuServer<C> {}
110
111impl<C: WgpuCompiler> WriteScoped for WgpuServer<C> {
112 type Streams = SchedulerMultiStream<ScheduledWgpuBackend>;
113
114 fn write_streams(&mut self) -> &mut Self::Streams {
115 &mut self.scheduler
116 }
117
118 fn on_failure(&mut self, stream: StreamId, error: &ServerError) {
119 self.scheduler.stream(&stream).profile_failure(error);
122 }
123
124 fn capturing(&mut self, stream: StreamId) -> Option<&mut StreamCapture> {
125 Some(&mut self.scheduler.stream(&stream).capturing)
126 }
127}
128
129impl<C: WgpuCompiler> WgpuServer<C> {
130 #[allow(clippy::too_many_arguments)]
132 pub fn new(
133 memory_properties: MemoryDeviceProperties,
134 memory_config: MemoryConfiguration,
135 compilation_options: WgpuCompilationOptions,
136 device: wgpu::Device,
137 queue: wgpu::Queue,
138 tasks_max: usize,
139 backend: wgpu::Backend,
140 timing_method: TimingMethod,
141 utilities: ServerUtilities,
142 captures: DeviceCaptures,
143 ) -> Self {
144 #[cfg(feature = "spirv")]
145 let adapter_info = device.adapter_info();
146 let backend_scheduler = ScheduledWgpuBackend::new(
147 device.clone(),
148 queue.clone(),
149 memory_properties,
150 memory_config,
151 timing_method,
152 backend,
153 tasks_max,
154 utilities.logger.clone(),
155 compilation_options.supports_vulkan_compiler,
156 captures,
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 stream_id: StreamId,
204 bindings: KernelArguments,
205 compiler_info: CompilerInfo,
206 ) -> Result<BindingsResource, IoError> {
207 let mut resources = Vec::with_capacity(bindings.resources.len());
210
211 for resource in bindings.resources.into_iter() {
212 match resource {
213 KernelResource::Buffer(b) => {
214 self.scheduler
216 .stream(&stream_id)
217 .capturing
218 .touch(b.stream, b.memory.descriptor().location());
219 let stream = self.scheduler.stream(&b.stream);
220 let resource = stream.resource(b)?;
221 resources.push(resource);
222 }
223 KernelResource::TensorMap(_) => panic!("Tensor map not supported in wgpu"),
224 }
225 }
226
227 Ok(BindingsResource {
228 resources,
229 info: bindings.info,
230 compiler_info,
231 })
232 }
233
234 fn pipeline(
235 &mut self,
236 kernel: Box<dyn CubeKernel>,
237 bindings: &KernelArguments,
238 ) -> Result<PipelineEntry, LaunchError> {
239 let kernel_id = kernel.id();
240 let mode = kernel_id.mode;
241
242 if let Some(pipeline) = self.pipelines.get(&kernel_id) {
243 return Ok(pipeline.clone());
244 }
245
246 let mut recording = CompilationRecording::new(&kernel_id);
247 let cached = self.load_cached_pipeline(&kernel_id, bindings, mode)?;
248
249 if let Some(Ok(pipeline)) = cached {
250 self.pipelines.insert(kernel_id, pipeline.clone());
251 recording.loaded();
252 return Ok(pipeline);
253 }
254
255 validate_cube_dim(&self.utilities.properties, &kernel_id)?;
256 validate_units(&self.utilities.properties, &kernel_id)?;
257
258 let definition = cubecl_core::define_kernel(&*kernel)?;
259 recording.defined(&definition);
260
261 let mut compiler = C::init(self.backend, &self.compilation_options);
262 let mut compiled = compiler.compile_kernel(self, kernel, definition)?;
263
264 if self.scheduler.logger.compilation_source_activated() {
265 compiled.debug_info = Some(DebugInformation::new(
266 compiler.lang_tag(),
267 kernel_id.clone(),
268 ));
269 }
270 self.scheduler.logger.log_compilation(&compiled);
271
272 compiler.validate_ir(&compiled.repr, &self.utilities.properties)?;
273 let io = compiled.io.take().map(Arc::from);
276 let (compiler_info, auto_repr) = compiler.normalize_repr(compiled.repr);
277 let repr = auto_repr.as_ref().map(|r| r.as_ref());
278
279 let module = self.create_module(
306 &compiled.entrypoint_name,
307 kernel_id.cube_dim.into(),
308 ModuleSource::resolve(repr, compiler.lang_tag(), &compiled.source)?,
309 mode,
310 )?;
311 let pipeline = self.create_pipeline(&compiled.entrypoint_name, repr, module, bindings)?;
312 self.pipelines.insert(
313 kernel_id.clone(),
314 (pipeline.clone(), compiler_info, io.clone()),
315 );
316
317 recording.source(&compiled.source);
318
319 let stored = false;
321 #[cfg(feature = "spirv")]
322 let stored = match (cached, auto_repr) {
323 (Some(Err(key)), Some(crate::AutoRepresentation::SpirV(kernel))) => {
324 let cache = self.spirv_cache.as_mut().unwrap();
325 store_compiled(
326 cache,
327 key,
328 cubecl_spirv::SpirvCacheEntry::new(compiled.entrypoint_name, kernel),
329 )
330 }
331 _ => stored,
332 };
333 recording.compiled(stored);
334
335 Ok((pipeline, compiler_info, io))
336 }
337}
338
339impl<C: WgpuCompiler> Server for WgpuServer<C> {
340 fn logger(&self) -> Arc<ServerLogger> {
341 self.scheduler.logger.clone()
342 }
343
344 fn utilities(&self) -> Arc<ServerUtilities> {
345 self.utilities.clone()
346 }
347
348 fn staging(
349 &mut self,
350 _sizes: &[usize],
351 _stream_id: StreamId,
352 ) -> Result<Vec<Bytes>, ServerError> {
353 Err(IoError::UnsupportedIoOperation {
355 backtrace: BackTrace::capture(),
356 }
357 .into())
358 }
359
360 fn initialize_memory(
361 &mut self,
362 memory: ManagedMemoryHandle,
363 size: u64,
364 stream_id: StreamId,
365 ) -> Result<(), ServerError> {
366 let mut relocating = self.scheduler.relocating(stream_id);
367 relocating.relocate_when_wanted();
368 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
369 let update = stream.capturing.page_update();
370 let reserved = match stream.memory.reserve(size, update, failures) {
371 Ok(reserved) => reserved,
372 Err(err @ IoError::PageUpdateForbidden { .. }) => {
376 stream.capturing.fail(err.into());
377 return Ok(());
378 }
379 Err(err) => {
380 let err = ServerError::from(err);
382 failures.fail_unallocated(&memory, err.clone());
383 return Err(err);
384 }
385 };
386 if let Err(err) = stream.memory.bind(reserved, memory.clone(), 0, failures) {
387 let err = ServerError::from(err);
388 failures.fail_unallocated(&memory, err.clone());
389 return Err(err);
390 }
391 Ok(())
392 }
393
394 fn read(
395 &mut self,
396 descriptors: Vec<CopyDescriptor>,
397 stream_id: StreamId,
398 ) -> DynFut<Result<Vec<Bytes>, ServerError>> {
399 if let Err(err) = self
402 .scheduler
403 .stream(&stream_id)
404 .reject_while_recording("read")
405 {
406 return Box::pin(async move { Err(err) });
407 }
408
409 if let Err(err) = self
413 .scheduler
414 .ensure_written(descriptors.iter().map(|d| &d.handle))
415 {
416 return Box::pin(async move { Err(err) });
417 }
418
419 let mut streams = vec![stream_id];
420 let mut resources = Vec::with_capacity(descriptors.len());
421 for desc in descriptors {
422 if contiguous_strides(&desc.shape) != desc.strides {
423 return Box::pin(async {
424 Err(IoError::UnsupportedStrides {
425 backtrace: BackTrace::capture(),
426 }
427 .into())
428 });
429 }
430 if !streams.contains(&desc.handle.stream) {
431 streams.push(desc.handle.stream);
432 }
433 let stream = self.scheduler.stream(&desc.handle.stream);
434 let resource = match stream.resource(desc.handle) {
435 Ok(val) => val,
436 Err(err) => return Box::pin(async move { Err(err.into()) }),
437 };
438 resources.push((resource, desc.shape, desc.elem_size));
439 }
440
441 self.scheduler.execute_streams(streams);
442
443 let stream = self.scheduler.stream(&stream_id);
444 stream.read_resources(resources, stream_id)
445 }
446
447 fn write(&mut self, descriptors: Vec<(CopyDescriptor, Bytes)>, stream_id: StreamId) {
448 {
458 let recording = self
459 .scheduler
460 .stream(&stream_id)
461 .reject_while_recording("write");
462 if let Err(err) = recording {
463 self.scheduler.taint(
468 err.clone(),
469 descriptors.iter().map(|(desc, _)| &desc.handle),
470 );
471 let stream = self.scheduler.stream(&stream_id);
476 if stream.capturing.owner() == Some(stream_id) {
477 stream.capturing.fail(err);
478 }
479 return;
480 }
481 }
482 for (desc, data) in descriptors {
483 let mut written = self.write_set();
492 written.push(desc.handle.clone());
493 ExecuteScope::over(self, stream_id, written).execute(|server| {
494 if contiguous_strides(&desc.shape) != desc.strides {
495 return Err(ServerError::Io(IoError::UnsupportedStrides {
496 backtrace: BackTrace::capture(),
497 }));
498 }
499
500 let owner = desc.handle.stream;
505 let handle = desc.handle.clone();
506 let stream = server.scheduler.stream(&owner);
507 let resource = stream.resource(desc.handle).map_err(ServerError::Io)?;
508 let task = ScheduleTask::Write {
509 data,
510 buffer: resource,
511 handle,
512 };
513
514 server.scheduler.register(stream_id, task, &[owner]);
515 Ok(())
516 });
517 }
518 }
519
520 fn check(
521 &mut self,
522 handles: Vec<BufferBinding>,
523 _stream_id: StreamId,
524 ) -> Result<(), ServerError> {
525 self.scheduler.ensure_written(handles.iter())
526 }
527
528 unsafe fn launch(
529 &mut self,
530 kernel: Box<dyn CubeKernel>,
531 count: CubeCount,
532 args: KernelArguments,
533 stream_id: StreamId,
534 launch_mode: LaunchMode,
535 ) {
536 let kernel_id = kernel.id();
549 let (pipeline, compiler_info, io) = match self.pipeline(kernel, &args) {
550 Ok(val) => val,
551 Err(err) => {
552 let error = ServerError::Launch(err);
553 self.scheduler.stream(&stream_id).profile_failure(&error);
554 if !launch_mode.is_skipped() {
555 let mut written = self.write_set();
556 written.extend(args.buffers_written(None).cloned());
557 failed_writing(self, stream_id, written, error);
558 }
559 return;
560 }
561 };
562 if launch_mode.is_skipped() {
563 return;
564 }
565
566 let mut written = self.write_set();
584 written.extend(args.buffers_written(io.as_deref()).cloned());
585 let count_read = match &count {
590 CubeCount::Dynamic(binding) => Some(binding),
591 CubeCount::Static(..) => None,
592 };
593 ExecuteScope::launching(
594 self,
595 kernel_id,
596 stream_id,
597 args.buffers_read(io.as_deref()).chain(count_read),
598 written,
599 )
600 .execute(|server| {
601 server.streams_pool.clear();
602 let mut shared_inputs = server.shared_bindings_pool.acquire();
605 args.resources.iter().for_each(|resource| match resource {
607 KernelResource::Buffer(b) => {
608 server.streams_pool.push(b.stream);
609 if b.stream != stream_id {
610 shared_inputs.push(b.memory.clone());
611 }
612 }
613 KernelResource::TensorMap(_) => {
614 panic!("Tensor maps not supported in WGPU")
615 }
616 });
617
618 let resources = server
619 .prepare_bindings(stream_id, args, compiler_info)
620 .map_err(ServerError::Io)?;
621
622 let task = ScheduleTask::Execute {
623 pipeline,
624 count,
625 resources,
626 shared_inputs,
627 };
628
629 server
630 .scheduler
631 .register(stream_id, task, &server.streams_pool);
632 Ok(())
633 });
634 }
635
636 fn flush(&mut self, stream_id: StreamId) -> Result<(), ServerError> {
637 self.scheduler.execute_streams(vec![stream_id]);
638
639 let stream = self.scheduler.stream(&stream_id);
640
641 stream.flush(stream_id)
642 }
643
644 fn sync(
646 &mut self,
647 handles: Vec<BufferBinding>,
648 stream_id: StreamId,
649 ) -> DynFut<Result<(), ServerError>> {
650 if let Err(err) = self
651 .scheduler
652 .stream(&stream_id)
653 .reject_while_recording("sync")
654 {
655 return Box::pin(async move { Err(err) });
656 }
657 if let Err(err) = self.scheduler.ensure_written(handles.iter()) {
660 return Box::pin(async move { Err(err) });
661 }
662 self.scheduler.execute_streams(vec![stream_id]);
663 let stream = self.scheduler.stream(&stream_id);
664
665 stream.sync(stream_id)
666 }
667
668 fn start_profile(&mut self, stream_id: StreamId) -> Result<ProfilingToken, ServerError> {
669 self.scheduler
672 .stream(&stream_id)
673 .reject_while_recording("start_profile")?;
674 self.scheduler.execute_streams(vec![stream_id]);
675 let stream = self.scheduler.stream(&stream_id);
676 stream.start_profile(stream_id)
677 }
678
679 fn end_profile(
680 &mut self,
681 stream_id: StreamId,
682 token: ProfilingToken,
683 ) -> Result<ProfileDuration, ProfileError> {
684 self.scheduler.execute_streams(vec![stream_id]);
685 let stream = self.scheduler.stream(&stream_id);
686
687 stream.end_profile(token, stream_id)
688 }
689
690 fn abandon_profile(&mut self, stream_id: StreamId, token: ProfilingToken) {
691 self.scheduler.stream(&stream_id).abandon_profile(token);
695 }
696
697 fn memory_report(&mut self, stream_id: StreamId) -> Option<StreamMemoryReport> {
698 self.scheduler.execute_streams(vec![stream_id]);
699 let stream = self.scheduler.stream(&stream_id);
700 Some(StreamMemoryReport {
701 stream: stream_id,
702 pools: stream.memory.memory_report(),
703 auxiliary: stream.auxiliary.report(),
704 })
705 }
706
707 fn stream_ids(&self) -> Vec<StreamId> {
708 self.scheduler.stream_ids().collect()
709 }
710
711 fn memory_cleanup(&mut self, stream_id: StreamId) -> Result<(), ServerError> {
712 self.scheduler.execute_streams(vec![stream_id]);
713 self.scheduler
714 .relocating(stream_id)
715 .refuse_while_recording()?;
716 let stream = self.scheduler.stream(&stream_id);
717 stream.info_cache.clear_unpinned();
722 self.scheduler.relocating(stream_id).reclaim()
723 }
724
725 fn allocation_mode(&mut self, mode: MemoryAllocationMode, stream_id: StreamId) {
726 self.scheduler.execute_streams(vec![stream_id]);
727 let stream = self.scheduler.stream(&stream_id);
728 stream.memory.mode(mode);
729 }
730
731 fn graph_prepare(&mut self, stream_id: StreamId) -> Result<(), ServerError> {
732 self.scheduler.execute_streams(vec![stream_id]);
735 let stream = self.scheduler.stream(&stream_id);
736
737 stream.capturing.prepare(stream_id)?;
740
741 self.scheduler
745 .relocating(stream_id)
746 .relocate_device(RelocationReason::Capture);
747 Ok(())
748 }
749
750 fn begin_capture(&mut self, stream_id: StreamId) -> Result<(), ServerError> {
751 self.scheduler.execute_streams(vec![stream_id]);
754 let stream = self.scheduler.stream(&stream_id);
755
756 stream.capturing.begin()?;
757
758 let stream = self.scheduler.stream(&stream_id);
762 if let Err(err) = stream.flush(stream_id) {
763 stream.info_cache.capture_discard();
767 stream.capturing.abort();
768 return Err(err);
769 }
770 Ok(())
771 }
772
773 fn end_capture(&mut self, stream_id: StreamId) -> Result<GraphId, ServerError> {
774 self.scheduler.execute_streams(vec![stream_id]);
776 let stream = self.scheduler.stream(&stream_id);
777
778 let outcome = match stream.capturing.end(stream_id) {
785 Ok(outcome) => outcome,
786 Err(err) => {
787 if stream.capturing.is_active() {
794 stream.info_cache.capture_discard();
795 stream.capturing.abort();
796 }
797 return Err(err);
798 }
799 };
800 let recording = stream.take_recording();
801 let written = stream.capturing.take_recorded();
805 let touched = stream.capturing.take_touched();
806
807 let doomed = stream.capturing.take_failure().map(|reason| {
813 ServerError::graph_state(format!(
814 "an operation inside the capture window failed, so the recording is missing \
815 an operation and cannot seal: {reason}"
816 ))
817 });
818 let discarded = match outcome.is_abandoned() {
819 true => Some(outcome.abandoned_error(stream_id, doomed)),
820 false => doomed,
821 };
822 if let Some(err) = discarded {
823 stream.info_cache.capture_discard();
824 self.scheduler.taint(err.clone(), written.iter());
829 return Err(err);
830 }
831
832 let id = GraphId::new();
833 stream.info_cache.capture_commit(id);
836 let guards = touched
839 .iter()
840 .filter_map(|page| {
841 self.scheduler
842 .stream(&page.stream)
843 .memory
844 .guard(page.location)
845 })
846 .collect();
847 self.graphs.insert(
848 id,
849 WgpuGraph {
850 tasks: recording.tasks,
851 _guards: guards,
852 _uniforms: recording.uniform_pins,
853 _shared: recording.shared,
854 written,
855 },
856 );
857 Ok(id)
858 }
859
860 fn replay(&mut self, graph: GraphId, stream_id: StreamId) -> Result<(), ServerError> {
861 self.scheduler.execute_streams(vec![stream_id]);
863
864 let Some(wgpu_graph) = self.graphs.get(&graph) else {
869 return Err(ServerError::graph_state(
870 "replay was given an unknown or already-destroyed graph",
871 ));
872 };
873
874 let recorded = wgpu_graph.written.clone();
882 let mut written = self.write_set();
883 written.extend(recorded);
884 ExecuteScope::over(self, stream_id, written)
885 .execute(|server| {
886 server
887 .scheduler
888 .stream(&stream_id)
889 .reject_while_recording("replay")?;
890 let wgpu_graph = server
891 .graphs
892 .get(&graph)
893 .expect("checked above; nothing in the scope removes graphs");
894 let stream = server.scheduler.stream(&stream_id);
895 stream.replay_graph(wgpu_graph);
896 Ok(())
897 })
898 .into_result()
899 }
900
901 fn graph_destroy(&mut self, graph: GraphId, stream_id: StreamId) {
902 let Some(wgpu_graph) = self.graphs.remove(&graph) else {
905 return;
906 };
907 let stream = self.scheduler.stream(&stream_id);
908 stream.submit();
917 stream.info_cache.graph_release(graph);
920 drop(wgpu_graph);
921 }
922}
923
924pub(crate) fn contiguous_strides(shape: &Shape) -> Strides {
925 let rank = shape.len();
926 let mut strides = strides![1; rank];
927 for i in (0..rank - 1).rev() {
928 strides[i] = strides[i + 1] * shape[i + 1];
929 }
930 strides
931}
932
933impl<C: WgpuCompiler> ServerStorage for WgpuServer<C> {
934 type Storage = WgpuStorage;
935
936 fn get_resource(
937 &mut self,
938 binding: BufferBinding,
939 stream_id: StreamId,
940 ) -> Result<ManagedResource<WgpuResource>, ServerError> {
941 self.scheduler.ensure_written([&binding].into_iter())?;
945 let mut streams = vec![stream_id];
946 if binding.stream != stream_id {
947 streams.push(binding.stream);
948 }
949 self.scheduler.execute_streams(streams);
950 let stream = self.scheduler.stream(&binding.stream);
951 Ok(stream.memory.managed_resource(
952 binding.memory,
953 binding.offset_start,
954 binding.offset_end,
955 )?)
956 }
957}