wgpu的前身的gfx-rs,这个项目是将所有api封装成一套unsafe的vulkan like api。这个项目如果使用不当的话,可能会造成的麻烦的是,后端的类型作为泛型参数,如果不加控制,会一路pop到最上层的用户接口,比如camera。这是非常不合理的。简而言之,将这个类型进行擦除,wgpu的做法是通过cargo feature对指定的类型别名进行条件编译,除了类型别名,大量direct后端实现会直接调用接口会通过gfx_select!宏来转发,而宏展开的内部也会应用相同的feature逻辑
/// Type system for enforcing the lock order on shared HUB structures. /// If type A implements `Access<B>`, that means we are allowed to proceed /// with locking resource `B` after we lock `A`. /// /// The implenentations basically describe the edges in a directed graph /// of lock transitions. As long as it doesn't have loops, we can have /// multiple concurrent paths on this graph (from multiple threads) without /// deadlocks, i.e. there is always a path whose next resource is not locked /// by some other path, at any time. pubtraitAccess<A> {}
pubenumRoot {} //TODO: establish an order instead of declaring all the pairs. impl Access<Instance> for Root {} impl Access<Surface> for Root {} impl Access<Surface> for Instance {} impl<A: hal::Api> Access<Adapter<A>> for Root {} impl<A: hal::Api> Access<Adapter<A>> for Surface {} impl<A: hal::Api> Access<Device<A>> for Root {} impl<A: hal::Api> Access<Device<A>> for Surface {} impl<A: hal::Api> Access<Device<A>> for Adapter<A> {} impl<A: hal::Api> Access<PipelineLayout<A>> for Root {} impl<A: hal::Api> Access<PipelineLayout<A>> for Device<A> {} impl<A: hal::Api> Access<PipelineLayout<A>> for RenderBundle {} impl<A: hal::Api> Access<BindGroupLayout<A>> for Root {} impl<A: hal::Api> Access<BindGroupLayout<A>> for Device<A> {} ......
/// A struct responsible for tracking resource lifetimes. /// /// Here is how host mapping is handled: /// 1. When mapping is requested we add the buffer to the life_tracker list of `mapped` buffers. /// 2. When `triage_suspected` is called, it checks the last submission index associated with each of the mapped buffer, /// and register the buffer with either a submission in flight, or straight into `ready_to_map` vector. /// 3. When `ActiveSubmission` is retired, the mapped buffers associated with it are moved to `ready_to_map` vector. /// 4. Finally, `handle_mapping` issues all the callbacks. pub(super) structLifetimeTracker<A: hal::Api> { /// Resources that the user has requested be mapped, but are still in use. /// 用户请求异步map的buffer会添加至此 mapped: Vec<Stored<id::BufferId>>, /// Buffers can be used in a submission that is yet to be made, by the /// means of `write_buffer()`, so we have a special place for them. pub future_suspected_buffers: Vec<Stored<id::BufferId>>, /// Textures can be used in the upcoming submission by `write_texture`. pub future_suspected_textures: Vec<Stored<id::TextureId>>, /// Resources that are suspected for destruction. // 任何资源在用户侧被删除,会添加至此集合 pub suspected_resources: SuspectedResources, /// Resources that are not referenced any more but still used by GPU. /// Grouped by submissions associated with a fence and a submission index. /// The active submissions have to be stored in FIFO order: oldest come first. active: Vec<ActiveSubmission<A>>, /// Resources that are neither referenced or used, just life_tracker /// actual deletion. free_resources: NonReferencedResources<A>, ready_to_map: Vec<id::Valid<id::BufferId>>, }
sub resource:这个主要是针对texture的处理,因为显然,某个texture可能是有多个layer和level的,某个pass是可以读一个level写另一个level。所以对于一个texture不能使用一个usage来进行区分。简单来说,对于buffer,sub resource就是buffer,对于texture,sub resource是其下的某个level,layer的组合。
/// The main trait that abstracts away the tracking logic of /// a particular resource type, like a buffer or a texture. pub(crate) traitResourceState: Clone + Default { /// Corresponding `HUB` identifier. typeId: Copy + fmt::Debug + TypedId; /// A type specifying the sub-resources. typeSelector: fmt::Debug; /// Usage type for a `Unit` of a sub-resource. typeUsage: fmt::Debug;
/// Check if all the selected sub-resources have the same /// usage, and return it. /// /// Returns `None` if no sub-resources /// are intersecting with the selector, or their usage /// isn't consistent. fnquery(&self, selector: Self::Selector) -> Option<Self::Usage>;
/// Change the last usage of the selected sub-resources. /// /// If `output` is specified, it's filled with the /// `PendingTransition` objects corresponding to smaller /// sub-resource transitions. The old usage is replaced by /// the new one. /// /// If `output` is `None`, the old usage is extended with /// the new usage. The error is returned if it's not possible, /// specifying the conflicting transition. Extension can only /// be done for read-only usages. fnchange( &mutself, id: Valid<Self::Id>, selector: Self::Selector, usage: Self::Usage, output: Option<&mutVec<PendingTransition<Self>>>, ) -> Result<(), PendingTransition<Self>>;
/// Merge the state of this resource tracked by a different instance /// with the current one. /// /// Same rules for `output` apply as with `change()`: last usage state /// is either replaced (when `output` is provided) with a /// `PendingTransition` pushed to this vector, or extended with the /// other read-only usage, unless there is a usage conflict, and /// the error is generated (returning the conflict). fnmerge( &mutself, id: Valid<Self::Id>, other: &Self, output: Option<&mutVec<PendingTransition<Self>>>, ) -> Result<(), PendingTransition<Self>>;
/// Try to optimize the internal representation. fnoptimize(&mutself); }
// tracking 同一类型多个资源的引用关系 pub(crate) structResourceTracker<S: ResourceState> { /// An association of known resource indices with their tracked states. map: FastHashMap<Index, Resource<S>>, /// Temporary storage for collecting transitions. temp: Vec<PendingTransition<S>>, /// The backend variant for all the tracked resources. backend: wgt::Backend, }
// Most of the time a resource is either fully uninitialized (one element) or initialized (zero elements). typeUninitializedRangeVec<Idx> = SmallVec<[Range<Idx>; 1]>;
/// Tracks initialization status of a linear range from 0..size // 记录需要初始化的范围 pub(crate) structInitTracker<Idx: Ord + Copy + Default> { // Ordered, non overlapping list of all uninitialized ranges. uninitialized_ranges: UninitializedRangeVec<Idx>, }
pub(crate) enumMemoryInitKind { // The memory range is going to be written by an already initialized source, thus doesn't need extra attention other than marking as initialized. ImplicitlyInitialized, // The memory range is going to be read, therefore needs to ensure prior initialization. NeedsInitializedMemory, }