Skip to main content

wgpu_hal/noop/
mod.rs

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