Skip to main content

wgpu_hal/noop/
mod.rs

1#![allow(unused_variables)]
2
3use alloc::{string::String, sync::Arc, vec, vec::Vec};
4use core::{ptr, sync::atomic::Ordering, time::Duration};
5
6#[cfg(supports_64bit_atomics)]
7use core::sync::atomic::AtomicU64;
8#[cfg(not(supports_64bit_atomics))]
9use portable_atomic::AtomicU64;
10
11use crate::TlasInstance;
12
13mod buffer;
14pub use buffer::Buffer;
15mod command;
16pub use command::CommandBuffer;
17
18#[derive(Clone, Debug)]
19pub struct Api;
20
21#[derive(Debug)]
22pub struct Context {
23    options: Arc<wgt::NoopBackendOptions>,
24}
25#[derive(Debug)]
26pub struct Encoder;
27#[derive(Debug)]
28pub struct Resource;
29
30#[derive(Debug)]
31pub struct Fence {
32    value: AtomicU64,
33}
34
35type DeviceResult<T> = Result<T, crate::DeviceError>;
36
37impl crate::Api for Api {
38    const VARIANT: wgt::Backend = wgt::Backend::Noop;
39
40    type Instance = Context;
41    type Surface = Context;
42    type Adapter = Context;
43    type Device = Context;
44
45    type Queue = Context;
46    type CommandEncoder = CommandBuffer;
47    type CommandBuffer = CommandBuffer;
48
49    type Buffer = Buffer;
50    type Texture = Resource;
51    type SurfaceTexture = Resource;
52    type TextureView = Resource;
53    type Sampler = Resource;
54    type QuerySet = Resource;
55    type Fence = Fence;
56    type AccelerationStructure = Resource;
57    type PipelineCache = Resource;
58
59    type BindGroupLayout = Resource;
60    type BindGroup = Resource;
61    type PipelineLayout = Resource;
62    type ShaderModule = Resource;
63    type RenderPipeline = Resource;
64    type RayTracingPipeline = Resource;
65    type ComputePipeline = Resource;
66}
67
68crate::impl_dyn_resource!(Buffer, CommandBuffer, Context, Fence, Resource);
69
70impl crate::DynAccelerationStructure for Resource {}
71impl crate::DynBindGroup for Resource {}
72impl crate::DynBindGroupLayout for Resource {}
73impl crate::DynBuffer for Buffer {}
74impl crate::DynCommandBuffer for CommandBuffer {}
75impl crate::DynComputePipeline for Resource {}
76impl crate::DynFence for Fence {}
77impl crate::DynPipelineCache for Resource {}
78impl crate::DynPipelineLayout for Resource {}
79impl crate::DynQuerySet for Resource {}
80impl crate::DynRenderPipeline for Resource {}
81impl crate::DynRayTracingPipeline for Resource {}
82impl crate::DynSampler for Resource {}
83impl crate::DynShaderModule for Resource {}
84impl crate::DynSurfaceTexture for Resource {}
85impl crate::DynTexture for Resource {}
86impl crate::DynTextureView for Resource {}
87
88impl core::borrow::Borrow<dyn crate::DynTexture> for Resource {
89    fn borrow(&self) -> &dyn crate::DynTexture {
90        self
91    }
92}
93
94impl crate::Instance for Context {
95    type A = Api;
96
97    unsafe fn init(desc: &crate::InstanceDescriptor<'_>) -> Result<Self, crate::InstanceError> {
98        let options = Arc::new(desc.backend_options.noop.clone());
99        if options.enable {
100            Ok(Context { options })
101        } else {
102            Err(crate::InstanceError::new(String::from(
103                "noop backend disabled because NoopBackendOptions::enable is false",
104            )))
105        }
106    }
107    unsafe fn create_surface(
108        &self,
109        _display_handle: raw_window_handle::RawDisplayHandle,
110        _window_handle: raw_window_handle::RawWindowHandle,
111    ) -> Result<Context, crate::InstanceError> {
112        Ok(Context {
113            options: Arc::clone(&self.options),
114        })
115    }
116    unsafe fn enumerate_adapters(
117        &self,
118        _surface_hint: Option<&Context>,
119    ) -> Vec<crate::ExposedAdapter<Api>> {
120        let device_type = self.options.device_type.unwrap_or(wgt::DeviceType::Other);
121        let subgroup_min_size = self
122            .options
123            .subgroup_min_size
124            .unwrap_or(wgt::MINIMUM_SUBGROUP_MIN_SIZE);
125        let subgroup_max_size = self
126            .options
127            .subgroup_max_size
128            .unwrap_or(wgt::MAXIMUM_SUBGROUP_MAX_SIZE);
129        let features = self.options.features.unwrap_or(wgt::Features::all());
130        let limits = self
131            .options
132            .limits
133            .clone()
134            .unwrap_or(CAPABILITIES.limits.clone());
135
136        let info = wgt::AdapterInfo {
137            subgroup_min_size,
138            subgroup_max_size,
139            ..adapter_info()
140        };
141
142        let capabilities = crate::Capabilities {
143            limits,
144            ..CAPABILITIES
145        };
146
147        vec![crate::ExposedAdapter {
148            adapter: Context {
149                options: Arc::clone(&self.options),
150            },
151            info,
152            features,
153            capabilities,
154        }]
155    }
156}
157
158/// Returns the adapter info for the noop backend.
159///
160/// This is used in the test harness to construct info about
161/// the noop backend adapter without actually initializing wgpu.
162pub fn adapter_info() -> wgt::AdapterInfo {
163    wgt::AdapterInfo {
164        name: String::from("noop wgpu backend"),
165        driver: String::from("wgpu"),
166        ..wgt::AdapterInfo::new(wgt::DeviceType::Cpu, wgt::Backend::Noop)
167    }
168}
169
170/// The capabilities of the noop backend.
171///
172/// This is used in the test harness to construct capabilities
173/// of the noop backend without actually initializing wgpu.
174pub const CAPABILITIES: crate::Capabilities = {
175    crate::Capabilities {
176        limits: wgt::Limits::unlimited(),
177        alignments: crate::Alignments {
178            // All maximally permissive
179            buffer_copy_offset: wgt::BufferSize::MIN,
180            buffer_copy_pitch: wgt::BufferSize::MIN,
181            uniform_bounds_check_alignment: wgt::BufferSize::MIN,
182            raw_tlas_instance_size: 0,
183            ray_tracing_scratch_buffer_alignment: 1,
184            ray_tracing_pipeline_group_data_size: 1,
185            ray_tracing_pipeline_group_data_alignment: 1,
186            ray_tracing_pipeline_data_offset_alignment: 1,
187        },
188        downlevel: wgt::DownlevelCapabilities {
189            flags: wgt::DownlevelFlags::all(),
190            limits: wgt::DownlevelLimits {},
191            shader_model: wgt::ShaderModel::Sm5,
192        },
193        cooperative_matrix_properties: Vec::new(),
194    }
195};
196
197impl crate::Surface for Context {
198    type A = Api;
199
200    unsafe fn configure(
201        &self,
202        device: &Context,
203        config: &crate::SurfaceConfiguration,
204    ) -> Result<(), crate::SurfaceError> {
205        Ok(())
206    }
207
208    unsafe fn unconfigure(&self, device: &Context) {}
209
210    unsafe fn acquire_texture(
211        &self,
212        _timeout: Option<Duration>,
213        _fence: &Fence,
214    ) -> Result<crate::AcquiredSurfaceTexture<Api>, crate::SurfaceError> {
215        Err(crate::SurfaceError::Timeout)
216    }
217    unsafe fn discard_texture(&self, texture: Resource) {}
218}
219
220impl crate::Adapter for Context {
221    type A = Api;
222
223    unsafe fn open(
224        &self,
225        features: wgt::Features,
226        _limits: &wgt::Limits,
227        _memory_hints: &wgt::MemoryHints,
228    ) -> DeviceResult<crate::OpenDevice<Api>> {
229        Ok(crate::OpenDevice {
230            device: Context {
231                options: Arc::clone(&self.options),
232            },
233            queue: Context {
234                options: Arc::clone(&self.options),
235            },
236        })
237    }
238    unsafe fn texture_format_capabilities(
239        &self,
240        format: wgt::TextureFormat,
241    ) -> crate::TextureFormatCapabilities {
242        crate::TextureFormatCapabilities::empty()
243    }
244
245    unsafe fn surface_capabilities(&self, surface: &Context) -> Option<crate::SurfaceCapabilities> {
246        None
247    }
248
249    unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
250        wgt::PresentationTimestamp::INVALID_TIMESTAMP
251    }
252
253    fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
254        wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
255    }
256
257    fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
258        wgt::TextureUses::INCLUSIVE
259            | wgt::TextureUses::COLOR_TARGET
260            | wgt::TextureUses::DEPTH_STENCIL_WRITE
261    }
262}
263
264impl crate::Queue for Context {
265    type A = Api;
266
267    unsafe fn submit(
268        &self,
269        command_buffers: &[&CommandBuffer],
270        surface_textures: &[&Resource],
271        (fence, fence_value): (&Fence, crate::FenceValue),
272    ) -> DeviceResult<()> {
273        // All commands are executed synchronously.
274        for cb in command_buffers {
275            // SAFETY: Caller is responsible for ensuring synchronization between commands and
276            // other mutations.
277            unsafe {
278                cb.execute();
279            }
280        }
281        fence.value.store(fence_value, Ordering::Release);
282        Ok(())
283    }
284    unsafe fn present(
285        &self,
286        surface: &Context,
287        texture: Resource,
288    ) -> Result<(), crate::SurfaceError> {
289        Ok(())
290    }
291
292    unsafe fn get_timestamp_period(&self) -> f32 {
293        1.0
294    }
295
296    unsafe fn wait_for_idle(&self) -> Result<(), crate::DeviceError> {
297        Ok(())
298    }
299}
300
301impl crate::Device for Context {
302    type A = Api;
303
304    unsafe fn create_buffer(&self, desc: &crate::BufferDescriptor) -> DeviceResult<Buffer> {
305        Buffer::new(desc)
306    }
307
308    unsafe fn destroy_buffer(&self, buffer: Buffer) {}
309    unsafe fn add_raw_buffer(&self, _buffer: &Buffer) {}
310
311    unsafe fn map_buffer(
312        &self,
313        buffer: &Buffer,
314        range: crate::MemoryRange,
315    ) -> DeviceResult<crate::BufferMapping> {
316        // Safety: the `wgpu-core` validation layer will prevent any user-accessible aliasing
317        // mappings from being created, so we don’t need to perform any checks here, except for
318        // bounds checks on the range which are built into `get_slice_ptr()`.
319        Ok(crate::BufferMapping {
320            ptr: ptr::NonNull::new(buffer.get_slice_ptr(range).cast::<u8>()).unwrap(),
321            is_coherent: true,
322        })
323    }
324    unsafe fn unmap_buffer(&self, buffer: &Buffer) {}
325    unsafe fn flush_mapped_ranges<I>(&self, buffer: &Buffer, ranges: I) {}
326    unsafe fn invalidate_mapped_ranges<I>(&self, buffer: &Buffer, ranges: I) {}
327
328    unsafe fn create_texture(&self, desc: &crate::TextureDescriptor) -> DeviceResult<Resource> {
329        Ok(Resource)
330    }
331    unsafe fn destroy_texture(&self, texture: Resource) {}
332    unsafe fn add_raw_texture(&self, _texture: &Resource) {}
333
334    unsafe fn create_texture_view(
335        &self,
336        texture: &Resource,
337        desc: &crate::TextureViewDescriptor,
338    ) -> DeviceResult<Resource> {
339        Ok(Resource)
340    }
341    unsafe fn destroy_texture_view(&self, view: Resource) {}
342    unsafe fn create_sampler(&self, desc: &crate::SamplerDescriptor) -> DeviceResult<Resource> {
343        Ok(Resource)
344    }
345    unsafe fn destroy_sampler(&self, sampler: Resource) {}
346
347    unsafe fn create_command_encoder(
348        &self,
349        desc: &crate::CommandEncoderDescriptor<Context>,
350    ) -> DeviceResult<CommandBuffer> {
351        Ok(CommandBuffer::new())
352    }
353
354    unsafe fn create_bind_group_layout(
355        &self,
356        desc: &crate::BindGroupLayoutDescriptor,
357    ) -> DeviceResult<Resource> {
358        Ok(Resource)
359    }
360    unsafe fn destroy_bind_group_layout(&self, bg_layout: Resource) {}
361    unsafe fn create_pipeline_layout(
362        &self,
363        desc: &crate::PipelineLayoutDescriptor<Resource>,
364    ) -> DeviceResult<Resource> {
365        Ok(Resource)
366    }
367    unsafe fn destroy_pipeline_layout(&self, pipeline_layout: Resource) {}
368    unsafe fn create_bind_group(
369        &self,
370        desc: &crate::BindGroupDescriptor<Resource, Buffer, Resource, Resource, Resource>,
371    ) -> DeviceResult<Resource> {
372        Ok(Resource)
373    }
374    unsafe fn destroy_bind_group(&self, group: Resource) {}
375
376    unsafe fn create_shader_module(
377        &self,
378        desc: &crate::ShaderModuleDescriptor,
379        shader: crate::ShaderInput,
380    ) -> Result<Resource, crate::ShaderError> {
381        Ok(Resource)
382    }
383    unsafe fn destroy_shader_module(&self, module: Resource) {}
384    unsafe fn create_render_pipeline(
385        &self,
386        desc: &crate::RenderPipelineDescriptor<Resource, Resource, Resource>,
387    ) -> Result<Resource, crate::PipelineError> {
388        Ok(Resource)
389    }
390    unsafe fn destroy_render_pipeline(&self, pipeline: Resource) {}
391    unsafe fn create_compute_pipeline(
392        &self,
393        desc: &crate::ComputePipelineDescriptor<Resource, Resource, Resource>,
394    ) -> Result<Resource, crate::PipelineError> {
395        Ok(Resource)
396    }
397    unsafe fn destroy_compute_pipeline(&self, pipeline: Resource) {}
398    unsafe fn create_ray_tracing_pipeline(
399        &self,
400        desc: &crate::RayTracingPipelineDescriptor<Resource, Resource, Resource>,
401    ) -> Result<Resource, crate::PipelineError> {
402        Ok(Resource)
403    }
404    unsafe fn destroy_ray_tracing_pipeline(&self, pipeline: Resource) {}
405    unsafe fn get_raytracing_pipeline_group_data(
406        &self,
407        pipeline: &Resource,
408        groups: core::ops::Range<u32>,
409    ) -> Result<Vec<u8>, crate::DeviceError> {
410        Ok(vec![0; groups.count()])
411    }
412    unsafe fn create_pipeline_cache(
413        &self,
414        desc: &crate::PipelineCacheDescriptor<'_>,
415    ) -> Result<Resource, crate::PipelineCacheError> {
416        Ok(Resource)
417    }
418    unsafe fn destroy_pipeline_cache(&self, cache: Resource) {}
419
420    unsafe fn create_query_set(
421        &self,
422        desc: &wgt::QuerySetDescriptor<crate::Label>,
423    ) -> DeviceResult<Resource> {
424        Ok(Resource)
425    }
426    unsafe fn destroy_query_set(&self, set: Resource) {}
427    unsafe fn create_fence(&self) -> DeviceResult<Fence> {
428        Ok(Fence {
429            value: AtomicU64::new(0),
430        })
431    }
432    unsafe fn destroy_fence(&self, fence: Fence) {}
433    unsafe fn get_fence_value(&self, fence: &Fence) -> DeviceResult<crate::FenceValue> {
434        Ok(fence.value.load(Ordering::Acquire))
435    }
436    unsafe fn wait(
437        &self,
438        fence: &Fence,
439        value: crate::FenceValue,
440        timeout: Option<Duration>,
441    ) -> DeviceResult<bool> {
442        // The relevant commands must have already been submitted, and noop-backend commands are
443        // executed synchronously, so there is no waiting — either it is already done,
444        // or this method was called incorrectly.
445        assert!(
446            fence.value.load(Ordering::Acquire) >= value,
447            "submission must have already been done"
448        );
449        Ok(true)
450    }
451
452    unsafe fn start_graphics_debugger_capture(&self) -> bool {
453        false
454    }
455    unsafe fn stop_graphics_debugger_capture(&self) {}
456    unsafe fn create_acceleration_structure(
457        &self,
458        desc: &crate::AccelerationStructureDescriptor,
459    ) -> DeviceResult<Resource> {
460        Ok(Resource)
461    }
462    unsafe fn get_acceleration_structure_build_sizes<'a>(
463        &self,
464        _desc: &crate::GetAccelerationStructureBuildSizesDescriptor<'a, Buffer>,
465    ) -> crate::AccelerationStructureBuildSizes {
466        Default::default()
467    }
468    unsafe fn get_acceleration_structure_device_address(
469        &self,
470        _acceleration_structure: &Resource,
471    ) -> wgt::BufferAddress {
472        Default::default()
473    }
474    unsafe fn destroy_acceleration_structure(&self, _acceleration_structure: Resource) {}
475
476    fn tlas_instance_to_bytes(&self, instance: TlasInstance) -> Vec<u8> {
477        vec![]
478    }
479
480    fn get_internal_counters(&self) -> wgt::HalCounters {
481        Default::default()
482    }
483
484    fn check_if_oom(&self) -> DeviceResult<()> {
485        Ok(())
486    }
487}