-
Notifications
You must be signed in to change notification settings - Fork 1k
/
Copy pathlib.rs
2548 lines (2342 loc) · 93.2 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//! A cross-platform unsafe graphics abstraction.
//!
//! This crate defines a set of traits abstracting over modern graphics APIs,
//! with implementations ("backends") for Vulkan, Metal, Direct3D, and GL.
//!
//! `wgpu-hal` is a spiritual successor to
//! [gfx-hal](https://github.com/gfx-rs/gfx), but with reduced scope, and
//! oriented towards WebGPU implementation goals. It has no overhead for
//! validation or tracking, and the API translation overhead is kept to the bare
//! minimum by the design of WebGPU. This API can be used for resource-demanding
//! applications and engines.
//!
//! The `wgpu-hal` crate's main design choices:
//!
//! - Our traits are meant to be *portable*: proper use
//! should get equivalent results regardless of the backend.
//!
//! - Our traits' contracts are *unsafe*: implementations perform minimal
//! validation, if any, and incorrect use will often cause undefined behavior.
//! This allows us to minimize the overhead we impose over the underlying
//! graphics system. If you need safety, the [`wgpu-core`] crate provides a
//! safe API for driving `wgpu-hal`, implementing all necessary validation,
//! resource state tracking, and so on. (Note that `wgpu-core` is designed for
//! use via FFI; the [`wgpu`] crate provides more idiomatic Rust bindings for
//! `wgpu-core`.) Or, you can do your own validation.
//!
//! - In the same vein, returned errors *only cover cases the user can't
//! anticipate*, like running out of memory or losing the device. Any errors
//! that the user could reasonably anticipate are their responsibility to
//! avoid. For example, `wgpu-hal` returns no error for mapping a buffer that's
//! not mappable: as the buffer creator, the user should already know if they
//! can map it.
//!
//! - We use *static dispatch*. The traits are not
//! generally object-safe. You must select a specific backend type
//! like [`vulkan::Api`] or [`metal::Api`], and then use that
//! according to the main traits, or call backend-specific methods.
//!
//! - We use *idiomatic Rust parameter passing*,
//! taking objects by reference, returning them by value, and so on,
//! unlike `wgpu-core`, which refers to objects by ID.
//!
//! - We map buffer contents *persistently*. This means that the buffer can
//! remain mapped on the CPU while the GPU reads or writes to it. You must
//! explicitly indicate when data might need to be transferred between CPU and
//! GPU, if [`Device::map_buffer`] indicates that this is necessary.
//!
//! - You must record *explicit barriers* between different usages of a
//! resource. For example, if a buffer is written to by a compute
//! shader, and then used as and index buffer to a draw call, you
//! must use [`CommandEncoder::transition_buffers`] between those two
//! operations.
//!
//! - Pipeline layouts are *explicitly specified* when setting bind groups.
//! Incompatible layouts disturb groups bound at higher indices.
//!
//! - The API *accepts collections as iterators*, to avoid forcing the user to
//! store data in particular containers. The implementation doesn't guarantee
//! that any of the iterators are drained, unless stated otherwise by the
//! function documentation. For this reason, we recommend that iterators don't
//! do any mutating work.
//!
//! Unfortunately, `wgpu-hal`'s safety requirements are not fully documented.
//! Ideally, all trait methods would have doc comments setting out the
//! requirements users must meet to ensure correct and portable behavior. If you
//! are aware of a specific requirement that a backend imposes that is not
//! ensured by the traits' documented rules, please file an issue. Or, if you are
//! a capable technical writer, please file a pull request!
//!
//! [`wgpu-core`]: https://crates.io/crates/wgpu-core
//! [`wgpu`]: https://crates.io/crates/wgpu
//! [`vulkan::Api`]: vulkan/struct.Api.html
//! [`metal::Api`]: metal/struct.Api.html
//!
//! ## Primary backends
//!
//! The `wgpu-hal` crate has full-featured backends implemented on the following
//! platform graphics APIs:
//!
//! - Vulkan, available on Linux, Android, and Windows, using the [`ash`] crate's
//! Vulkan bindings. It's also available on macOS, if you install [MoltenVK].
//!
//! - Metal on macOS, using the [`metal`] crate's bindings.
//!
//! - Direct3D 12 on Windows, using the [`windows`] crate's bindings.
//!
//! [`ash`]: https://crates.io/crates/ash
//! [MoltenVK]: https://github.com/KhronosGroup/MoltenVK
//! [`metal`]: https://crates.io/crates/metal
//! [`windows`]: https://crates.io/crates/windows
//!
//! ## Secondary backends
//!
//! The `wgpu-hal` crate has a partial implementation based on the following
//! platform graphics API:
//!
//! - The GL backend is available anywhere OpenGL, OpenGL ES, or WebGL are
//! available. See the [`gles`] module documentation for details.
//!
//! [`gles`]: gles/index.html
//!
//! You can see what capabilities an adapter is missing by checking the
//! [`DownlevelCapabilities`][tdc] in [`ExposedAdapter::capabilities`], available
//! from [`Instance::enumerate_adapters`].
//!
//! The API is generally designed to fit the primary backends better than the
//! secondary backends, so the latter may impose more overhead.
//!
//! [tdc]: wgt::DownlevelCapabilities
//!
//! ## Traits
//!
//! The `wgpu-hal` crate defines a handful of traits that together
//! represent a cross-platform abstraction for modern GPU APIs.
//!
//! - The [`Api`] trait represents a `wgpu-hal` backend. It has no methods of its
//! own, only a collection of associated types.
//!
//! - [`Api::Instance`] implements the [`Instance`] trait. [`Instance::init`]
//! creates an instance value, which you can use to enumerate the adapters
//! available on the system. For example, [`vulkan::Api::Instance::init`][Ii]
//! returns an instance that can enumerate the Vulkan physical devices on your
//! system.
//!
//! - [`Api::Adapter`] implements the [`Adapter`] trait, representing a
//! particular device from a particular backend. For example, a Vulkan instance
//! might have a Lavapipe software adapter and a GPU-based adapter.
//!
//! - [`Api::Device`] implements the [`Device`] trait, representing an active
//! link to a device. You get a device value by calling [`Adapter::open`], and
//! then use it to create buffers, textures, shader modules, and so on.
//!
//! - [`Api::Queue`] implements the [`Queue`] trait, which you use to submit
//! command buffers to a given device.
//!
//! - [`Api::CommandEncoder`] implements the [`CommandEncoder`] trait, which you
//! use to build buffers of commands to submit to a queue. This has all the
//! methods for drawing and running compute shaders, which is presumably what
//! you're here for.
//!
//! - [`Api::Surface`] implements the [`Surface`] trait, which represents a
//! swapchain for presenting images on the screen, via interaction with the
//! system's window manager.
//!
//! The [`Api`] trait has various other associated types like [`Api::Buffer`] and
//! [`Api::Texture`] that represent resources the rest of the interface can
//! operate on, but these generally do not have their own traits.
//!
//! [Ii]: Instance::init
//!
//! ## Validation is the calling code's responsibility, not `wgpu-hal`'s
//!
//! As much as possible, `wgpu-hal` traits place the burden of validation,
//! resource tracking, and state tracking on the caller, not on the trait
//! implementations themselves. Anything which can reasonably be handled in
//! backend-independent code should be. A `wgpu_hal` backend's sole obligation is
//! to provide portable behavior, and report conditions that the calling code
//! can't reasonably anticipate, like device loss or running out of memory.
//!
//! The `wgpu` crate collection is intended for use in security-sensitive
//! applications, like web browsers, where the API is available to untrusted
//! code. This means that `wgpu-core`'s validation is not simply a service to
//! developers, to be provided opportunistically when the performance costs are
//! acceptable and the necessary data is ready at hand. Rather, `wgpu-core`'s
//! validation must be exhaustive, to ensure that even malicious content cannot
//! provoke and exploit undefined behavior in the platform's graphics API.
//!
//! Because graphics APIs' requirements are complex, the only practical way for
//! `wgpu` to provide exhaustive validation is to comprehensively track the
//! lifetime and state of all the resources in the system. Implementing this
//! separately for each backend is infeasible; effort would be better spent
//! making the cross-platform validation in `wgpu-core` legible and trustworthy.
//! Fortunately, the requirements are largely similar across the various
//! platforms, so cross-platform validation is practical.
//!
//! Some backends have specific requirements that aren't practical to foist off
//! on the `wgpu-hal` user. For example, properly managing macOS Objective-C or
//! Microsoft COM reference counts is best handled by using appropriate pointer
//! types within the backend.
//!
//! A desire for "defense in depth" may suggest performing additional validation
//! in `wgpu-hal` when the opportunity arises, but this must be done with
//! caution. Even experienced contributors infer the expectations their changes
//! must meet by considering not just requirements made explicit in types, tests,
//! assertions, and comments, but also those implicit in the surrounding code.
//! When one sees validation or state-tracking code in `wgpu-hal`, it is tempting
//! to conclude, "Oh, `wgpu-hal` checks for this, so `wgpu-core` needn't worry
//! about it - that would be redundant!" The responsibility for exhaustive
//! validation always rests with `wgpu-core`, regardless of what may or may not
//! be checked in `wgpu-hal`.
//!
//! To this end, any "defense in depth" validation that does appear in `wgpu-hal`
//! for requirements that `wgpu-core` should have enforced should report failure
//! via the `unreachable!` macro, because problems detected at this stage always
//! indicate a bug in `wgpu-core`.
//!
//! ## Debugging
//!
//! Most of the information on the wiki [Debugging wgpu Applications][wiki-debug]
//! page still applies to this API, with the exception of API tracing/replay
//! functionality, which is only available in `wgpu-core`.
//!
//! [wiki-debug]: https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![allow(
// this happens on the GL backend, where it is both thread safe and non-thread safe in the same code.
clippy::arc_with_non_send_sync,
// We don't use syntax sugar where it's not necessary.
clippy::match_like_matches_macro,
// Redundant matching is more explicit.
clippy::redundant_pattern_matching,
// Explicit lifetimes are often easier to reason about.
clippy::needless_lifetimes,
// No need for defaults in the internal types.
clippy::new_without_default,
// Matches are good and extendable, no need to make an exception here.
clippy::single_match,
// Push commands are more regular than macros.
clippy::vec_init_then_push,
// We unsafe impl `Send` for a reason.
clippy::non_send_fields_in_send_ty,
// TODO!
clippy::missing_safety_doc,
// It gets in the way a lot and does not prevent bugs in practice.
clippy::pattern_type_mismatch,
)]
#![warn(
clippy::alloc_instead_of_core,
clippy::ptr_as_ptr,
clippy::std_instead_of_alloc,
clippy::std_instead_of_core,
trivial_casts,
trivial_numeric_casts,
unsafe_op_in_unsafe_fn,
unused_extern_crates,
unused_qualifications
)]
extern crate alloc;
extern crate wgpu_types as wgt;
// Each of these backends needs `std` in some fashion; usually `std::thread` functions.
#[cfg(any(dx12, gles_with_std, metal, vulkan))]
#[macro_use]
extern crate std;
/// DirectX12 API internals.
#[cfg(dx12)]
pub mod dx12;
/// GLES API internals.
#[cfg(gles)]
pub mod gles;
/// Metal API internals.
#[cfg(metal)]
pub mod metal;
/// A dummy API implementation.
// TODO(https://github.com/gfx-rs/wgpu/issues/7120): this should have a cfg
pub mod noop;
/// Vulkan API internals.
#[cfg(vulkan)]
pub mod vulkan;
pub mod auxil;
pub mod api {
#[cfg(dx12)]
pub use super::dx12::Api as Dx12;
#[cfg(gles)]
pub use super::gles::Api as Gles;
#[cfg(metal)]
pub use super::metal::Api as Metal;
pub use super::noop::Api as Noop;
#[cfg(vulkan)]
pub use super::vulkan::Api as Vulkan;
}
mod dynamic;
pub(crate) use dynamic::impl_dyn_resource;
pub use dynamic::{
DynAccelerationStructure, DynAcquiredSurfaceTexture, DynAdapter, DynBindGroup,
DynBindGroupLayout, DynBuffer, DynCommandBuffer, DynCommandEncoder, DynComputePipeline,
DynDevice, DynExposedAdapter, DynFence, DynInstance, DynOpenDevice, DynPipelineCache,
DynPipelineLayout, DynQuerySet, DynQueue, DynRenderPipeline, DynResource, DynSampler,
DynShaderModule, DynSurface, DynSurfaceTexture, DynTexture, DynTextureView,
};
#[allow(unused)]
use alloc::boxed::Box;
use alloc::{borrow::Cow, string::String, sync::Arc, vec::Vec};
use core::{
borrow::Borrow,
error::Error,
fmt,
num::NonZeroU32,
ops::{Range, RangeInclusive},
ptr::NonNull,
};
use bitflags::bitflags;
use parking_lot::Mutex;
use thiserror::Error;
use wgt::WasmNotSendSync;
// - Vertex + Fragment
// - Compute
// Task + Mesh + Fragment
pub const MAX_CONCURRENT_SHADER_STAGES: usize = 3;
pub const MAX_ANISOTROPY: u8 = 16;
pub const MAX_BIND_GROUPS: usize = 8;
pub const MAX_VERTEX_BUFFERS: usize = 16;
pub const MAX_COLOR_ATTACHMENTS: usize = 8;
pub const MAX_MIP_LEVELS: u32 = 16;
/// Size of a single occlusion/timestamp query, when copied into a buffer, in bytes.
/// cbindgen:ignore
pub const QUERY_SIZE: wgt::BufferAddress = 8;
pub type Label<'a> = Option<&'a str>;
pub type MemoryRange = Range<wgt::BufferAddress>;
pub type FenceValue = u64;
#[cfg(supports_64bit_atomics)]
pub type AtomicFenceValue = core::sync::atomic::AtomicU64;
#[cfg(not(supports_64bit_atomics))]
pub type AtomicFenceValue = portable_atomic::AtomicU64;
/// A callback to signal that wgpu is no longer using a resource.
#[cfg(any(gles, vulkan))]
pub type DropCallback = Box<dyn FnOnce() + Send + Sync + 'static>;
#[cfg(any(gles, vulkan))]
pub struct DropGuard {
callback: Option<DropCallback>,
}
#[cfg(all(any(gles, vulkan), any(native, Emscripten)))]
impl DropGuard {
fn from_option(callback: Option<DropCallback>) -> Option<Self> {
callback.map(|callback| Self {
callback: Some(callback),
})
}
}
#[cfg(any(gles, vulkan))]
impl Drop for DropGuard {
fn drop(&mut self) {
if let Some(cb) = self.callback.take() {
(cb)();
}
}
}
#[cfg(any(gles, vulkan))]
impl fmt::Debug for DropGuard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DropGuard").finish()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum DeviceError {
#[error("Out of memory")]
OutOfMemory,
#[error("Device is lost")]
Lost,
#[error("Creation of a resource failed for a reason other than running out of memory.")]
ResourceCreationFailed,
#[error("Unexpected error variant (driver implementation is at fault)")]
Unexpected,
}
#[allow(dead_code)] // may be unused on some platforms
#[cold]
fn hal_usage_error<T: fmt::Display>(txt: T) -> ! {
panic!("wgpu-hal invariant was violated (usage error): {txt}")
}
#[allow(dead_code)] // may be unused on some platforms
#[cold]
fn hal_internal_error<T: fmt::Display>(txt: T) -> ! {
panic!("wgpu-hal ran into a preventable internal error: {txt}")
}
#[derive(Clone, Debug, Eq, PartialEq, Error)]
pub enum ShaderError {
#[error("Compilation failed: {0:?}")]
Compilation(String),
#[error(transparent)]
Device(#[from] DeviceError),
}
#[derive(Clone, Debug, Eq, PartialEq, Error)]
pub enum PipelineError {
#[error("Linkage failed for stage {0:?}: {1}")]
Linkage(wgt::ShaderStages, String),
#[error("Entry point for stage {0:?} is invalid")]
EntryPoint(naga::ShaderStage),
#[error(transparent)]
Device(#[from] DeviceError),
#[error("Pipeline constant error for stage {0:?}: {1}")]
PipelineConstants(wgt::ShaderStages, String),
}
#[derive(Clone, Debug, Eq, PartialEq, Error)]
pub enum PipelineCacheError {
#[error(transparent)]
Device(#[from] DeviceError),
}
#[derive(Clone, Debug, Eq, PartialEq, Error)]
pub enum SurfaceError {
#[error("Surface is lost")]
Lost,
#[error("Surface is outdated, needs to be re-created")]
Outdated,
#[error(transparent)]
Device(#[from] DeviceError),
#[error("Other reason: {0}")]
Other(&'static str),
}
/// Error occurring while trying to create an instance, or create a surface from an instance;
/// typically relating to the state of the underlying graphics API or hardware.
#[derive(Clone, Debug, Error)]
#[error("{message}")]
pub struct InstanceError {
/// These errors are very platform specific, so do not attempt to encode them as an enum.
///
/// This message should describe the problem in sufficient detail to be useful for a
/// user-to-developer “why won't this work on my machine” bug report, and otherwise follow
/// <https://rust-lang.github.io/api-guidelines/interoperability.html#error-types-are-meaningful-and-well-behaved-c-good-err>.
message: String,
/// Underlying error value, if any is available.
#[source]
source: Option<Arc<dyn Error + Send + Sync + 'static>>,
}
impl InstanceError {
#[allow(dead_code)] // may be unused on some platforms
pub(crate) fn new(message: String) -> Self {
Self {
message,
source: None,
}
}
#[allow(dead_code)] // may be unused on some platforms
pub(crate) fn with_source(message: String, source: impl Error + Send + Sync + 'static) -> Self {
Self {
message,
source: Some(Arc::new(source)),
}
}
}
pub trait Api: Clone + fmt::Debug + Sized {
type Instance: DynInstance + Instance<A = Self>;
type Surface: DynSurface + Surface<A = Self>;
type Adapter: DynAdapter + Adapter<A = Self>;
type Device: DynDevice + Device<A = Self>;
type Queue: DynQueue + Queue<A = Self>;
type CommandEncoder: DynCommandEncoder + CommandEncoder<A = Self>;
/// This API's command buffer type.
///
/// The only thing you can do with `CommandBuffer`s is build them
/// with a [`CommandEncoder`] and then pass them to
/// [`Queue::submit`] for execution, or destroy them by passing
/// them to [`CommandEncoder::reset_all`].
///
/// [`CommandEncoder`]: Api::CommandEncoder
type CommandBuffer: DynCommandBuffer;
type Buffer: DynBuffer;
type Texture: DynTexture;
type SurfaceTexture: DynSurfaceTexture + Borrow<Self::Texture>;
type TextureView: DynTextureView;
type Sampler: DynSampler;
type QuerySet: DynQuerySet;
/// A value you can block on to wait for something to finish.
///
/// A `Fence` holds a monotonically increasing [`FenceValue`]. You can call
/// [`Device::wait`] to block until a fence reaches or passes a value you
/// choose. [`Queue::submit`] can take a `Fence` and a [`FenceValue`] to
/// store in it when the submitted work is complete.
///
/// Attempting to set a fence to a value less than its current value has no
/// effect.
///
/// Waiting on a fence returns as soon as the fence reaches *or passes* the
/// requested value. This implies that, in order to reliably determine when
/// an operation has completed, operations must finish in order of
/// increasing fence values: if a higher-valued operation were to finish
/// before a lower-valued operation, then waiting for the fence to reach the
/// lower value could return before the lower-valued operation has actually
/// finished.
type Fence: DynFence;
type BindGroupLayout: DynBindGroupLayout;
type BindGroup: DynBindGroup;
type PipelineLayout: DynPipelineLayout;
type ShaderModule: DynShaderModule;
type RenderPipeline: DynRenderPipeline;
type ComputePipeline: DynComputePipeline;
type PipelineCache: DynPipelineCache;
type AccelerationStructure: DynAccelerationStructure + 'static;
}
pub trait Instance: Sized + WasmNotSendSync {
type A: Api;
unsafe fn init(desc: &InstanceDescriptor) -> Result<Self, InstanceError>;
unsafe fn create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
) -> Result<<Self::A as Api>::Surface, InstanceError>;
/// `surface_hint` is only used by the GLES backend targeting WebGL2
unsafe fn enumerate_adapters(
&self,
surface_hint: Option<&<Self::A as Api>::Surface>,
) -> Vec<ExposedAdapter<Self::A>>;
}
pub trait Surface: WasmNotSendSync {
type A: Api;
/// Configure `self` to use `device`.
///
/// # Safety
///
/// - All GPU work using `self` must have been completed.
/// - All [`AcquiredSurfaceTexture`]s must have been destroyed.
/// - All [`Api::TextureView`]s derived from the [`AcquiredSurfaceTexture`]s must have been destroyed.
/// - The surface `self` must not currently be configured to use any other [`Device`].
unsafe fn configure(
&self,
device: &<Self::A as Api>::Device,
config: &SurfaceConfiguration,
) -> Result<(), SurfaceError>;
/// Unconfigure `self` on `device`.
///
/// # Safety
///
/// - All GPU work that uses `surface` must have been completed.
/// - All [`AcquiredSurfaceTexture`]s must have been destroyed.
/// - All [`Api::TextureView`]s derived from the [`AcquiredSurfaceTexture`]s must have been destroyed.
/// - The surface `self` must have been configured on `device`.
unsafe fn unconfigure(&self, device: &<Self::A as Api>::Device);
/// Return the next texture to be presented by `self`, for the caller to draw on.
///
/// On success, return an [`AcquiredSurfaceTexture`] representing the
/// texture into which the caller should draw the image to be displayed on
/// `self`.
///
/// If `timeout` elapses before `self` has a texture ready to be acquired,
/// return `Ok(None)`. If `timeout` is `None`, wait indefinitely, with no
/// timeout.
///
/// # Using an [`AcquiredSurfaceTexture`]
///
/// On success, this function returns an [`AcquiredSurfaceTexture`] whose
/// [`texture`] field is a [`SurfaceTexture`] from which the caller can
/// [`borrow`] a [`Texture`] to draw on. The [`AcquiredSurfaceTexture`] also
/// carries some metadata about that [`SurfaceTexture`].
///
/// All calls to [`Queue::submit`] that draw on that [`Texture`] must also
/// include the [`SurfaceTexture`] in the `surface_textures` argument.
///
/// When you are done drawing on the texture, you can display it on `self`
/// by passing the [`SurfaceTexture`] and `self` to [`Queue::present`].
///
/// If you do not wish to display the texture, you must pass the
/// [`SurfaceTexture`] to [`self.discard_texture`], so that it can be reused
/// by future acquisitions.
///
/// # Portability
///
/// Some backends can't support a timeout when acquiring a texture. On these
/// backends, `timeout` is ignored.
///
/// # Safety
///
/// - The surface `self` must currently be configured on some [`Device`].
///
/// - The `fence` argument must be the same [`Fence`] passed to all calls to
/// [`Queue::submit`] that used [`Texture`]s acquired from this surface.
///
/// - You may only have one texture acquired from `self` at a time. When
/// `acquire_texture` returns `Ok(Some(ast))`, you must pass the returned
/// [`SurfaceTexture`] `ast.texture` to either [`Queue::present`] or
/// [`Surface::discard_texture`] before calling `acquire_texture` again.
///
/// [`texture`]: AcquiredSurfaceTexture::texture
/// [`SurfaceTexture`]: Api::SurfaceTexture
/// [`borrow`]: alloc::borrow::Borrow::borrow
/// [`Texture`]: Api::Texture
/// [`Fence`]: Api::Fence
/// [`self.discard_texture`]: Surface::discard_texture
unsafe fn acquire_texture(
&self,
timeout: Option<core::time::Duration>,
fence: &<Self::A as Api>::Fence,
) -> Result<Option<AcquiredSurfaceTexture<Self::A>>, SurfaceError>;
/// Relinquish an acquired texture without presenting it.
///
/// After this call, the texture underlying [`SurfaceTexture`] may be
/// returned by subsequent calls to [`self.acquire_texture`].
///
/// # Safety
///
/// - The surface `self` must currently be configured on some [`Device`].
///
/// - `texture` must be a [`SurfaceTexture`] returned by a call to
/// [`self.acquire_texture`] that has not yet been passed to
/// [`Queue::present`].
///
/// [`SurfaceTexture`]: Api::SurfaceTexture
/// [`self.acquire_texture`]: Surface::acquire_texture
unsafe fn discard_texture(&self, texture: <Self::A as Api>::SurfaceTexture);
}
pub trait Adapter: WasmNotSendSync {
type A: Api;
unsafe fn open(
&self,
features: wgt::Features,
limits: &wgt::Limits,
memory_hints: &wgt::MemoryHints,
) -> Result<OpenDevice<Self::A>, DeviceError>;
/// Return the set of supported capabilities for a texture format.
unsafe fn texture_format_capabilities(
&self,
format: wgt::TextureFormat,
) -> TextureFormatCapabilities;
/// Returns the capabilities of working with a specified surface.
///
/// `None` means presentation is not supported for it.
unsafe fn surface_capabilities(
&self,
surface: &<Self::A as Api>::Surface,
) -> Option<SurfaceCapabilities>;
/// Creates a [`PresentationTimestamp`] using the adapter's WSI.
///
/// [`PresentationTimestamp`]: wgt::PresentationTimestamp
unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp;
}
/// A connection to a GPU and a pool of resources to use with it.
///
/// A `wgpu-hal` `Device` represents an open connection to a specific graphics
/// processor, controlled via the backend [`Device::A`]. A `Device` is mostly
/// used for creating resources. Each `Device` has an associated [`Queue`] used
/// for command submission.
///
/// On Vulkan a `Device` corresponds to a logical device ([`VkDevice`]). Other
/// backends don't have an exact analog: for example, [`ID3D12Device`]s and
/// [`MTLDevice`]s are owned by the backends' [`wgpu_hal::Adapter`]
/// implementations, and shared by all [`wgpu_hal::Device`]s created from that
/// `Adapter`.
///
/// A `Device`'s life cycle is generally:
///
/// 1) Obtain a `Device` and its associated [`Queue`] by calling
/// [`Adapter::open`].
///
/// Alternatively, the backend-specific types that implement [`Adapter`] often
/// have methods for creating a `wgpu-hal` `Device` from a platform-specific
/// handle. For example, [`vulkan::Adapter::device_from_raw`] can create a
/// [`vulkan::Device`] from an [`ash::Device`].
///
/// 1) Create resources to use on the device by calling methods like
/// [`Device::create_texture`] or [`Device::create_shader_module`].
///
/// 1) Call [`Device::create_command_encoder`] to obtain a [`CommandEncoder`],
/// which you can use to build [`CommandBuffer`]s holding commands to be
/// executed on the GPU.
///
/// 1) Call [`Queue::submit`] on the `Device`'s associated [`Queue`] to submit
/// [`CommandBuffer`]s for execution on the GPU. If needed, call
/// [`Device::wait`] to wait for them to finish execution.
///
/// 1) Free resources with methods like [`Device::destroy_texture`] or
/// [`Device::destroy_shader_module`].
///
/// 1) Drop the device.
///
/// [`vkDevice`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VkDevice
/// [`ID3D12Device`]: https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12device
/// [`MTLDevice`]: https://developer.apple.com/documentation/metal/mtldevice
/// [`wgpu_hal::Adapter`]: Adapter
/// [`wgpu_hal::Device`]: Device
/// [`vulkan::Adapter::device_from_raw`]: vulkan/struct.Adapter.html#method.device_from_raw
/// [`vulkan::Device`]: vulkan/struct.Device.html
/// [`ash::Device`]: https://docs.rs/ash/latest/ash/struct.Device.html
/// [`CommandBuffer`]: Api::CommandBuffer
///
/// # Safety
///
/// As with other `wgpu-hal` APIs, [validation] is the caller's
/// responsibility. Here are the general requirements for all `Device`
/// methods:
///
/// - Any resource passed to a `Device` method must have been created by that
/// `Device`. For example, a [`Texture`] passed to [`Device::destroy_texture`] must
/// have been created with the `Device` passed as `self`.
///
/// - Resources may not be destroyed if they are used by any submitted command
/// buffers that have not yet finished execution.
///
/// [validation]: index.html#validation-is-the-calling-codes-responsibility-not-wgpu-hals
/// [`Texture`]: Api::Texture
pub trait Device: WasmNotSendSync {
type A: Api;
/// Creates a new buffer.
///
/// The initial usage is `wgt::BufferUses::empty()`.
unsafe fn create_buffer(
&self,
desc: &BufferDescriptor,
) -> Result<<Self::A as Api>::Buffer, DeviceError>;
/// Free `buffer` and any GPU resources it owns.
///
/// Note that backends are allowed to allocate GPU memory for buffers from
/// allocation pools, and this call is permitted to simply return `buffer`'s
/// storage to that pool, without making it available to other applications.
///
/// # Safety
///
/// - The given `buffer` must not currently be mapped.
unsafe fn destroy_buffer(&self, buffer: <Self::A as Api>::Buffer);
/// A hook for when a wgpu-core buffer is created from a raw wgpu-hal buffer.
unsafe fn add_raw_buffer(&self, buffer: &<Self::A as Api>::Buffer);
/// Return a pointer to CPU memory mapping the contents of `buffer`.
///
/// Buffer mappings are persistent: the buffer may remain mapped on the CPU
/// while the GPU reads or writes to it. (Note that `wgpu_core` does not use
/// this feature: when a `wgpu_core::Buffer` is unmapped, the underlying
/// `wgpu_hal` buffer is also unmapped.)
///
/// If this function returns `Ok(mapping)`, then:
///
/// - `mapping.ptr` is the CPU address of the start of the mapped memory.
///
/// - If `mapping.is_coherent` is `true`, then CPU writes to the mapped
/// memory are immediately visible on the GPU, and vice versa.
///
/// # Safety
///
/// - The given `buffer` must have been created with the [`MAP_READ`] or
/// [`MAP_WRITE`] flags set in [`BufferDescriptor::usage`].
///
/// - The given `range` must fall within the size of `buffer`.
///
/// - The caller must avoid data races between the CPU and the GPU. A data
/// race is any pair of accesses to a particular byte, one of which is a
/// write, that are not ordered with respect to each other by some sort of
/// synchronization operation.
///
/// - If this function returns `Ok(mapping)` and `mapping.is_coherent` is
/// `false`, then:
///
/// - Every CPU write to a mapped byte followed by a GPU read of that byte
/// must have at least one call to [`Device::flush_mapped_ranges`]
/// covering that byte that occurs between those two accesses.
///
/// - Every GPU write to a mapped byte followed by a CPU read of that byte
/// must have at least one call to [`Device::invalidate_mapped_ranges`]
/// covering that byte that occurs between those two accesses.
///
/// Note that the data race rule above requires that all such access pairs
/// be ordered, so it is meaningful to talk about what must occur
/// "between" them.
///
/// - Zero-sized mappings are not allowed.
///
/// - The returned [`BufferMapping::ptr`] must not be used after a call to
/// [`Device::unmap_buffer`].
///
/// [`MAP_READ`]: wgt::BufferUses::MAP_READ
/// [`MAP_WRITE`]: wgt::BufferUses::MAP_WRITE
unsafe fn map_buffer(
&self,
buffer: &<Self::A as Api>::Buffer,
range: MemoryRange,
) -> Result<BufferMapping, DeviceError>;
/// Remove the mapping established by the last call to [`Device::map_buffer`].
///
/// # Safety
///
/// - The given `buffer` must be currently mapped.
unsafe fn unmap_buffer(&self, buffer: &<Self::A as Api>::Buffer);
/// Indicate that CPU writes to mapped buffer memory should be made visible to the GPU.
///
/// # Safety
///
/// - The given `buffer` must be currently mapped.
///
/// - All ranges produced by `ranges` must fall within `buffer`'s size.
unsafe fn flush_mapped_ranges<I>(&self, buffer: &<Self::A as Api>::Buffer, ranges: I)
where
I: Iterator<Item = MemoryRange>;
/// Indicate that GPU writes to mapped buffer memory should be made visible to the CPU.
///
/// # Safety
///
/// - The given `buffer` must be currently mapped.
///
/// - All ranges produced by `ranges` must fall within `buffer`'s size.
unsafe fn invalidate_mapped_ranges<I>(&self, buffer: &<Self::A as Api>::Buffer, ranges: I)
where
I: Iterator<Item = MemoryRange>;
/// Creates a new texture.
///
/// The initial usage for all subresources is `wgt::TextureUses::UNINITIALIZED`.
unsafe fn create_texture(
&self,
desc: &TextureDescriptor,
) -> Result<<Self::A as Api>::Texture, DeviceError>;
unsafe fn destroy_texture(&self, texture: <Self::A as Api>::Texture);
/// A hook for when a wgpu-core texture is created from a raw wgpu-hal texture.
unsafe fn add_raw_texture(&self, texture: &<Self::A as Api>::Texture);
unsafe fn create_texture_view(
&self,
texture: &<Self::A as Api>::Texture,
desc: &TextureViewDescriptor,
) -> Result<<Self::A as Api>::TextureView, DeviceError>;
unsafe fn destroy_texture_view(&self, view: <Self::A as Api>::TextureView);
unsafe fn create_sampler(
&self,
desc: &SamplerDescriptor,
) -> Result<<Self::A as Api>::Sampler, DeviceError>;
unsafe fn destroy_sampler(&self, sampler: <Self::A as Api>::Sampler);
/// Create a fresh [`CommandEncoder`].
///
/// The new `CommandEncoder` is in the "closed" state.
unsafe fn create_command_encoder(
&self,
desc: &CommandEncoderDescriptor<<Self::A as Api>::Queue>,
) -> Result<<Self::A as Api>::CommandEncoder, DeviceError>;
/// Creates a bind group layout.
unsafe fn create_bind_group_layout(
&self,
desc: &BindGroupLayoutDescriptor,
) -> Result<<Self::A as Api>::BindGroupLayout, DeviceError>;
unsafe fn destroy_bind_group_layout(&self, bg_layout: <Self::A as Api>::BindGroupLayout);
unsafe fn create_pipeline_layout(
&self,
desc: &PipelineLayoutDescriptor<<Self::A as Api>::BindGroupLayout>,
) -> Result<<Self::A as Api>::PipelineLayout, DeviceError>;
unsafe fn destroy_pipeline_layout(&self, pipeline_layout: <Self::A as Api>::PipelineLayout);
#[allow(clippy::type_complexity)]
unsafe fn create_bind_group(
&self,
desc: &BindGroupDescriptor<
<Self::A as Api>::BindGroupLayout,
<Self::A as Api>::Buffer,
<Self::A as Api>::Sampler,
<Self::A as Api>::TextureView,
<Self::A as Api>::AccelerationStructure,
>,
) -> Result<<Self::A as Api>::BindGroup, DeviceError>;
unsafe fn destroy_bind_group(&self, group: <Self::A as Api>::BindGroup);
unsafe fn create_shader_module(
&self,
desc: &ShaderModuleDescriptor,
shader: ShaderInput,
) -> Result<<Self::A as Api>::ShaderModule, ShaderError>;
unsafe fn destroy_shader_module(&self, module: <Self::A as Api>::ShaderModule);
#[allow(clippy::type_complexity)]
unsafe fn create_render_pipeline(
&self,
desc: &RenderPipelineDescriptor<
<Self::A as Api>::PipelineLayout,
<Self::A as Api>::ShaderModule,
<Self::A as Api>::PipelineCache,
>,
) -> Result<<Self::A as Api>::RenderPipeline, PipelineError>;
#[allow(clippy::type_complexity)]
unsafe fn create_mesh_pipeline(
&self,
desc: &MeshPipelineDescriptor<
<Self::A as Api>::PipelineLayout,
<Self::A as Api>::ShaderModule,
<Self::A as Api>::PipelineCache,
>,
) -> Result<<Self::A as Api>::RenderPipeline, PipelineError>;
unsafe fn destroy_render_pipeline(&self, pipeline: <Self::A as Api>::RenderPipeline);
#[allow(clippy::type_complexity)]
unsafe fn create_compute_pipeline(
&self,
desc: &ComputePipelineDescriptor<
<Self::A as Api>::PipelineLayout,
<Self::A as Api>::ShaderModule,
<Self::A as Api>::PipelineCache,
>,
) -> Result<<Self::A as Api>::ComputePipeline, PipelineError>;
unsafe fn destroy_compute_pipeline(&self, pipeline: <Self::A as Api>::ComputePipeline);
unsafe fn create_pipeline_cache(
&self,
desc: &PipelineCacheDescriptor<'_>,
) -> Result<<Self::A as Api>::PipelineCache, PipelineCacheError>;
fn pipeline_cache_validation_key(&self) -> Option<[u8; 16]> {
None
}
unsafe fn destroy_pipeline_cache(&self, cache: <Self::A as Api>::PipelineCache);
unsafe fn create_query_set(
&self,
desc: &wgt::QuerySetDescriptor<Label>,
) -> Result<<Self::A as Api>::QuerySet, DeviceError>;
unsafe fn destroy_query_set(&self, set: <Self::A as Api>::QuerySet);
unsafe fn create_fence(&self) -> Result<<Self::A as Api>::Fence, DeviceError>;
unsafe fn destroy_fence(&self, fence: <Self::A as Api>::Fence);
unsafe fn get_fence_value(
&self,
fence: &<Self::A as Api>::Fence,
) -> Result<FenceValue, DeviceError>;
/// Wait for `fence` to reach `value`.
///
/// Operations like [`Queue::submit`] can accept a [`Fence`] and a
/// [`FenceValue`] to store in it, so you can use this `wait` function
/// to wait for a given queue submission to finish execution.
///
/// The `value` argument must be a value that some actual operation you have
/// already presented to the device is going to store in `fence`. You cannot
/// wait for values yet to be submitted. (This restriction accommodates
/// implementations like the `vulkan` backend's [`FencePool`] that must
/// allocate a distinct synchronization object for each fence value one is
/// able to wait for.)
///
/// Calling `wait` with a lower [`FenceValue`] than `fence`'s current value
/// returns immediately.
///
/// Returns `Ok(true)` on success and `Ok(false)` on timeout.
///
/// [`Fence`]: Api::Fence
/// [`FencePool`]: vulkan/enum.Fence.html#variant.FencePool
unsafe fn wait(
&self,
fence: &<Self::A as Api>::Fence,
value: FenceValue,
timeout_ms: u32,
) -> Result<bool, DeviceError>;
unsafe fn start_capture(&self) -> bool;
unsafe fn stop_capture(&self);
#[allow(unused_variables)]
unsafe fn pipeline_cache_get_data(
&self,
cache: &<Self::A as Api>::PipelineCache,
) -> Option<Vec<u8>> {
None
}
unsafe fn create_acceleration_structure(
&self,
desc: &AccelerationStructureDescriptor,
) -> Result<<Self::A as Api>::AccelerationStructure, DeviceError>;
unsafe fn get_acceleration_structure_build_sizes(
&self,
desc: &GetAccelerationStructureBuildSizesDescriptor<<Self::A as Api>::Buffer>,
) -> AccelerationStructureBuildSizes;
unsafe fn get_acceleration_structure_device_address(
&self,
acceleration_structure: &<Self::A as Api>::AccelerationStructure,
) -> wgt::BufferAddress;
unsafe fn destroy_acceleration_structure(
&self,
acceleration_structure: <Self::A as Api>::AccelerationStructure,
);