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        } = *desc;
102        if enable {
103            Ok(Context)
104        } else {
105            Err(crate::InstanceError::new(String::from(
106                "noop backend disabled because NoopBackendOptions::enable is false",
107            )))
108        }
109    }
110    unsafe fn create_surface(
111        &self,
112        _display_handle: raw_window_handle::RawDisplayHandle,
113        _window_handle: raw_window_handle::RawWindowHandle,
114    ) -> Result<Context, crate::InstanceError> {
115        Ok(Context)
116    }
117    unsafe fn enumerate_adapters(
118        &self,
119        _surface_hint: Option<&Context>,
120    ) -> Vec<crate::ExposedAdapter<Api>> {
121        vec![crate::ExposedAdapter {
122            adapter: Context,
123            info: adapter_info(),
124            features: wgt::Features::all(),
125            capabilities: CAPABILITIES,
126        }]
127    }
128}
129
130/// Returns the adapter info for the noop backend.
131///
132/// This is used in the test harness to construct info about
133/// the noop backend adapter without actually initializing wgpu.
134pub fn adapter_info() -> wgt::AdapterInfo {
135    wgt::AdapterInfo {
136        name: String::from("noop wgpu backend"),
137        vendor: 0,
138        device: 0,
139        device_type: wgt::DeviceType::Cpu,
140        driver: String::from("wgpu"),
141        driver_info: String::new(),
142        backend: wgt::Backend::Noop,
143    }
144}
145
146/// The capabilities of the noop backend.
147///
148/// This is used in the test harness to construct capabilities
149/// of the noop backend without actually initializing wgpu.
150pub const CAPABILITIES: crate::Capabilities = {
151    /// Guaranteed to be no bigger than isize::MAX which is the maximum size of an allocation,
152    /// except on 16-bit platforms which we certainly don’t fit in.
153    const ALLOC_MAX_U32: u32 = i32::MAX as u32;
154
155    crate::Capabilities {
156        limits: wgt::Limits {
157            // All maximally permissive
158            max_texture_dimension_1d: ALLOC_MAX_U32,
159            max_texture_dimension_2d: ALLOC_MAX_U32,
160            max_texture_dimension_3d: ALLOC_MAX_U32,
161            max_texture_array_layers: ALLOC_MAX_U32,
162            max_bind_groups: ALLOC_MAX_U32,
163            max_bindings_per_bind_group: ALLOC_MAX_U32,
164            max_dynamic_uniform_buffers_per_pipeline_layout: ALLOC_MAX_U32,
165            max_dynamic_storage_buffers_per_pipeline_layout: ALLOC_MAX_U32,
166            max_sampled_textures_per_shader_stage: ALLOC_MAX_U32,
167            max_samplers_per_shader_stage: ALLOC_MAX_U32,
168            max_storage_buffers_per_shader_stage: ALLOC_MAX_U32,
169            max_storage_textures_per_shader_stage: ALLOC_MAX_U32,
170            max_uniform_buffers_per_shader_stage: ALLOC_MAX_U32,
171            max_binding_array_elements_per_shader_stage: ALLOC_MAX_U32,
172            max_binding_array_sampler_elements_per_shader_stage: ALLOC_MAX_U32,
173            max_uniform_buffer_binding_size: ALLOC_MAX_U32,
174            max_storage_buffer_binding_size: ALLOC_MAX_U32,
175            max_vertex_buffers: ALLOC_MAX_U32,
176            max_buffer_size: ALLOC_MAX_U32 as u64,
177            max_vertex_attributes: ALLOC_MAX_U32,
178            max_vertex_buffer_array_stride: ALLOC_MAX_U32,
179            min_uniform_buffer_offset_alignment: 1,
180            min_storage_buffer_offset_alignment: 1,
181            max_inter_stage_shader_components: ALLOC_MAX_U32,
182            max_color_attachments: ALLOC_MAX_U32,
183            max_color_attachment_bytes_per_sample: ALLOC_MAX_U32,
184            max_compute_workgroup_storage_size: ALLOC_MAX_U32,
185            max_compute_invocations_per_workgroup: ALLOC_MAX_U32,
186            max_compute_workgroup_size_x: ALLOC_MAX_U32,
187            max_compute_workgroup_size_y: ALLOC_MAX_U32,
188            max_compute_workgroup_size_z: ALLOC_MAX_U32,
189            max_compute_workgroups_per_dimension: ALLOC_MAX_U32,
190            min_subgroup_size: 1,
191            max_subgroup_size: ALLOC_MAX_U32,
192            max_push_constant_size: ALLOC_MAX_U32,
193            max_non_sampler_bindings: ALLOC_MAX_U32,
194
195            max_task_workgroup_total_count: 0,
196            max_task_workgroups_per_dimension: 0,
197            max_mesh_multiview_count: 0,
198            max_mesh_output_layers: 0,
199
200            max_blas_primitive_count: ALLOC_MAX_U32,
201            max_blas_geometry_count: ALLOC_MAX_U32,
202            max_tlas_instance_count: ALLOC_MAX_U32,
203            max_acceleration_structures_per_shader_stage: ALLOC_MAX_U32,
204        },
205        alignments: crate::Alignments {
206            // All maximally permissive
207            buffer_copy_offset: wgt::BufferSize::MIN,
208            buffer_copy_pitch: wgt::BufferSize::MIN,
209            uniform_bounds_check_alignment: wgt::BufferSize::MIN,
210            raw_tlas_instance_size: 0,
211            ray_tracing_scratch_buffer_alignment: 1,
212        },
213        downlevel: wgt::DownlevelCapabilities {
214            flags: wgt::DownlevelFlags::all(),
215            limits: wgt::DownlevelLimits {},
216            shader_model: wgt::ShaderModel::Sm5,
217        },
218    }
219};
220
221impl crate::Surface for Context {
222    type A = Api;
223
224    unsafe fn configure(
225        &self,
226        device: &Context,
227        config: &crate::SurfaceConfiguration,
228    ) -> Result<(), crate::SurfaceError> {
229        Ok(())
230    }
231
232    unsafe fn unconfigure(&self, device: &Context) {}
233
234    unsafe fn acquire_texture(
235        &self,
236        timeout: Option<Duration>,
237        fence: &Fence,
238    ) -> Result<Option<crate::AcquiredSurfaceTexture<Api>>, crate::SurfaceError> {
239        Ok(None)
240    }
241    unsafe fn discard_texture(&self, texture: Resource) {}
242}
243
244impl crate::Adapter for Context {
245    type A = Api;
246
247    unsafe fn open(
248        &self,
249        features: wgt::Features,
250        _limits: &wgt::Limits,
251        _memory_hints: &wgt::MemoryHints,
252    ) -> DeviceResult<crate::OpenDevice<Api>> {
253        Ok(crate::OpenDevice {
254            device: Context,
255            queue: Context,
256        })
257    }
258    unsafe fn texture_format_capabilities(
259        &self,
260        format: wgt::TextureFormat,
261    ) -> crate::TextureFormatCapabilities {
262        crate::TextureFormatCapabilities::empty()
263    }
264
265    unsafe fn surface_capabilities(&self, surface: &Context) -> Option<crate::SurfaceCapabilities> {
266        None
267    }
268
269    unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
270        wgt::PresentationTimestamp::INVALID_TIMESTAMP
271    }
272}
273
274impl crate::Queue for Context {
275    type A = Api;
276
277    unsafe fn submit(
278        &self,
279        command_buffers: &[&CommandBuffer],
280        surface_textures: &[&Resource],
281        (fence, fence_value): (&mut Fence, crate::FenceValue),
282    ) -> DeviceResult<()> {
283        // All commands are executed synchronously.
284        for cb in command_buffers {
285            // SAFETY: Caller is responsible for ensuring synchronization between commands and
286            // other mutations.
287            unsafe {
288                cb.execute();
289            }
290        }
291        fence.value.store(fence_value, Ordering::Release);
292        Ok(())
293    }
294    unsafe fn present(
295        &self,
296        surface: &Context,
297        texture: Resource,
298    ) -> Result<(), crate::SurfaceError> {
299        Ok(())
300    }
301
302    unsafe fn get_timestamp_period(&self) -> f32 {
303        1.0
304    }
305}
306
307impl crate::Device for Context {
308    type A = Api;
309
310    unsafe fn create_buffer(&self, desc: &crate::BufferDescriptor) -> DeviceResult<Buffer> {
311        Buffer::new(desc)
312    }
313
314    unsafe fn destroy_buffer(&self, buffer: Buffer) {}
315    unsafe fn add_raw_buffer(&self, _buffer: &Buffer) {}
316
317    unsafe fn map_buffer(
318        &self,
319        buffer: &Buffer,
320        range: crate::MemoryRange,
321    ) -> DeviceResult<crate::BufferMapping> {
322        // Safety: the `wgpu-core` validation layer will prevent any user-accessible aliasing
323        // mappings from being created, so we don’t need to perform any checks here, except for
324        // bounds checks on the range which are built into `get_slice_ptr()`.
325        Ok(crate::BufferMapping {
326            ptr: ptr::NonNull::new(buffer.get_slice_ptr(range).cast::<u8>()).unwrap(),
327            is_coherent: true,
328        })
329    }
330    unsafe fn unmap_buffer(&self, buffer: &Buffer) {}
331    unsafe fn flush_mapped_ranges<I>(&self, buffer: &Buffer, ranges: I) {}
332    unsafe fn invalidate_mapped_ranges<I>(&self, buffer: &Buffer, ranges: I) {}
333
334    unsafe fn create_texture(&self, desc: &crate::TextureDescriptor) -> DeviceResult<Resource> {
335        Ok(Resource)
336    }
337    unsafe fn destroy_texture(&self, texture: Resource) {}
338    unsafe fn add_raw_texture(&self, _texture: &Resource) {}
339
340    unsafe fn create_texture_view(
341        &self,
342        texture: &Resource,
343        desc: &crate::TextureViewDescriptor,
344    ) -> DeviceResult<Resource> {
345        Ok(Resource)
346    }
347    unsafe fn destroy_texture_view(&self, view: Resource) {}
348    unsafe fn create_sampler(&self, desc: &crate::SamplerDescriptor) -> DeviceResult<Resource> {
349        Ok(Resource)
350    }
351    unsafe fn destroy_sampler(&self, sampler: Resource) {}
352
353    unsafe fn create_command_encoder(
354        &self,
355        desc: &crate::CommandEncoderDescriptor<Context>,
356    ) -> DeviceResult<CommandBuffer> {
357        Ok(CommandBuffer::new())
358    }
359
360    unsafe fn create_bind_group_layout(
361        &self,
362        desc: &crate::BindGroupLayoutDescriptor,
363    ) -> DeviceResult<Resource> {
364        Ok(Resource)
365    }
366    unsafe fn destroy_bind_group_layout(&self, bg_layout: Resource) {}
367    unsafe fn create_pipeline_layout(
368        &self,
369        desc: &crate::PipelineLayoutDescriptor<Resource>,
370    ) -> DeviceResult<Resource> {
371        Ok(Resource)
372    }
373    unsafe fn destroy_pipeline_layout(&self, pipeline_layout: Resource) {}
374    unsafe fn create_bind_group(
375        &self,
376        desc: &crate::BindGroupDescriptor<Resource, Buffer, Resource, Resource, Resource>,
377    ) -> DeviceResult<Resource> {
378        Ok(Resource)
379    }
380    unsafe fn destroy_bind_group(&self, group: Resource) {}
381
382    unsafe fn create_shader_module(
383        &self,
384        desc: &crate::ShaderModuleDescriptor,
385        shader: crate::ShaderInput,
386    ) -> Result<Resource, crate::ShaderError> {
387        Ok(Resource)
388    }
389    unsafe fn destroy_shader_module(&self, module: Resource) {}
390    unsafe fn create_render_pipeline(
391        &self,
392        desc: &crate::RenderPipelineDescriptor<Resource, Resource, Resource>,
393    ) -> Result<Resource, crate::PipelineError> {
394        Ok(Resource)
395    }
396    unsafe fn destroy_render_pipeline(&self, pipeline: Resource) {}
397    unsafe fn create_compute_pipeline(
398        &self,
399        desc: &crate::ComputePipelineDescriptor<Resource, Resource, Resource>,
400    ) -> Result<Resource, crate::PipelineError> {
401        Ok(Resource)
402    }
403    unsafe fn destroy_compute_pipeline(&self, pipeline: Resource) {}
404    unsafe fn create_pipeline_cache(
405        &self,
406        desc: &crate::PipelineCacheDescriptor<'_>,
407    ) -> Result<Resource, crate::PipelineCacheError> {
408        Ok(Resource)
409    }
410    unsafe fn destroy_pipeline_cache(&self, cache: Resource) {}
411
412    unsafe fn create_query_set(
413        &self,
414        desc: &wgt::QuerySetDescriptor<crate::Label>,
415    ) -> DeviceResult<Resource> {
416        Ok(Resource)
417    }
418    unsafe fn destroy_query_set(&self, set: Resource) {}
419    unsafe fn create_fence(&self) -> DeviceResult<Fence> {
420        Ok(Fence {
421            value: AtomicU64::new(0),
422        })
423    }
424    unsafe fn destroy_fence(&self, fence: Fence) {}
425    unsafe fn get_fence_value(&self, fence: &Fence) -> DeviceResult<crate::FenceValue> {
426        Ok(fence.value.load(Ordering::Acquire))
427    }
428    unsafe fn wait(
429        &self,
430        fence: &Fence,
431        value: crate::FenceValue,
432        timeout_ms: u32,
433    ) -> DeviceResult<bool> {
434        // The relevant commands must have already been submitted, and noop-backend commands are
435        // executed synchronously, so there is no waiting — either it is already done,
436        // or this method was called incorrectly.
437        assert!(
438            fence.value.load(Ordering::Acquire) >= value,
439            "submission must have already been done"
440        );
441        Ok(true)
442    }
443
444    unsafe fn start_graphics_debugger_capture(&self) -> bool {
445        false
446    }
447    unsafe fn stop_graphics_debugger_capture(&self) {}
448    unsafe fn create_acceleration_structure(
449        &self,
450        desc: &crate::AccelerationStructureDescriptor,
451    ) -> DeviceResult<Resource> {
452        Ok(Resource)
453    }
454    unsafe fn get_acceleration_structure_build_sizes<'a>(
455        &self,
456        _desc: &crate::GetAccelerationStructureBuildSizesDescriptor<'a, Buffer>,
457    ) -> crate::AccelerationStructureBuildSizes {
458        Default::default()
459    }
460    unsafe fn get_acceleration_structure_device_address(
461        &self,
462        _acceleration_structure: &Resource,
463    ) -> wgt::BufferAddress {
464        Default::default()
465    }
466    unsafe fn destroy_acceleration_structure(&self, _acceleration_structure: Resource) {}
467
468    fn tlas_instance_to_bytes(&self, instance: TlasInstance) -> Vec<u8> {
469        vec![]
470    }
471
472    fn get_internal_counters(&self) -> wgt::HalCounters {
473        Default::default()
474    }
475
476    fn check_if_oom(&self) -> DeviceResult<()> {
477        Ok(())
478    }
479}