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
//! Ranks for `wgpu-core` locks, restricting acquisition order.
//!
//! See [`LockRank`].

/// The rank of a lock.
///
/// Each [`Mutex`], [`RwLock`], and [`SnatchLock`] in `wgpu-core` has been
/// assigned a *rank*: a node in the DAG defined at the bottom of
/// `wgpu-core/src/lock/rank.rs`. The rank of the most recently
/// acquired lock you are still holding determines which locks you may
/// attempt to acquire next.
///
/// When you create a lock in `wgpu-core`, you must specify its rank
/// by passing in a [`LockRank`] value. This module declares a
/// pre-defined set of ranks to cover everything in `wgpu-core`, named
/// after the type in which they occur, and the name of the type's
/// field that is a lock. For example, [`CommandBuffer::data`] is a
/// `Mutex`, and its rank here is the constant
/// [`COMMAND_BUFFER_DATA`].
///
/// [`Mutex`]: parking_lot::Mutex
/// [`RwLock`]: parking_lot::RwLock
/// [`SnatchLock`]: crate::snatch::SnatchLock
/// [`CommandBuffer::data`]: crate::command::CommandBuffer::data
#[derive(Debug, Copy, Clone)]
pub struct LockRank {
    /// The bit representing this lock.
    ///
    /// There should only be a single bit set in this value.
    pub(super) bit: LockRankSet,

    /// A bitmask of permitted successor ranks.
    ///
    /// If `rank` is the rank of the most recently acquired lock we
    /// are still holding, then `rank.followers` is the mask of
    /// locks we are allowed to acquire next.
    ///
    /// The `define_lock_ranks!` macro ensures that there are no
    /// cycles in the graph of lock ranks and their followers.
    pub(super) followers: LockRankSet,
}

/// Define a set of lock ranks, and each rank's permitted successors.
macro_rules! define_lock_ranks {
    {
        $(
            $( #[ $attr:meta ] )*
            rank $name:ident $member:literal followed by { $( $follower:ident ),* $(,)? }
        )*
    } => {
        // An enum that assigns a unique number to each rank.
        #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
        enum LockRankNumber { $( $name, )* }

        bitflags::bitflags! {
            #[derive(Debug, Copy, Clone, Eq, PartialEq)]
            /// A bitflags type representing a set of lock ranks.
            pub struct LockRankSet: u64 {
                $(
                    const $name = 1 << (LockRankNumber:: $name as u64);
                )*
            }
        }

        impl LockRankSet {
            pub fn member_name(self) -> &'static str {
                match self {
                    $(
                        LockRankSet:: $name => $member,
                    )*
                    _ => "<unrecognized LockRankSet bit>",
                }
            }

            #[cfg_attr(not(feature = "observe_locks"), allow(dead_code))]
            pub fn const_name(self) -> &'static str {
                match self {
                    $(
                        LockRankSet:: $name => stringify!($name),
                    )*
                    _ => "<unrecognized LockRankSet bit>",
                }
            }
        }

        $(
            // If there is any cycle in the ranking, the initializers
            // for `followers` will be cyclic, and rustc will give us
            // an error message explaining the cycle.
            $( #[ $attr ] )*
            pub const $name: LockRank = LockRank {
                bit: LockRankSet:: $name,
                followers: LockRankSet::empty() $( .union($follower.bit) )*,
            };
        )*
    }
}

define_lock_ranks! {
    rank COMMAND_BUFFER_DATA "CommandBuffer::data" followed by {
        DEVICE_SNATCHABLE_LOCK,
        DEVICE_USAGE_SCOPES,
        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,
        BUFFER_MAP_STATE,
    }
    rank DEVICE_SNATCHABLE_LOCK "Device::snatchable_lock" followed by {
        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,
        DEVICE_TRACE,
        BUFFER_MAP_STATE,
        // Uncomment this to see an interesting cycle.
        // COMMAND_BUFFER_DATA,
    }
    rank BUFFER_MAP_STATE "Buffer::map_state" followed by {
        QUEUE_PENDING_WRITES,
        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,
        DEVICE_TRACE,
    }
    rank QUEUE_PENDING_WRITES "Queue::pending_writes" followed by {
        COMMAND_ALLOCATOR_FREE_ENCODERS,
        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,
        QUEUE_LIFE_TRACKER,
    }
    rank QUEUE_LIFE_TRACKER "Queue::life_tracker" followed by {
        COMMAND_ALLOCATOR_FREE_ENCODERS,
        DEVICE_TRACE,
    }
    rank COMMAND_ALLOCATOR_FREE_ENCODERS "CommandAllocator::free_encoders" followed by {
        SHARED_TRACKER_INDEX_ALLOCATOR_INNER,
    }

    rank BUFFER_BIND_GROUPS "Buffer::bind_groups" followed by { }
    rank BUFFER_INITIALIZATION_STATUS "Buffer::initialization_status" followed by { }
    rank DEVICE_DEFERRED_DESTROY "Device::deferred_destroy" followed by { }
    rank DEVICE_FENCE "Device::fence" followed by { }
    #[allow(dead_code)]
    rank DEVICE_TRACE "Device::trace" followed by { }
    rank DEVICE_TRACKERS "Device::trackers" followed by { }
    rank DEVICE_LOST_CLOSURE "Device::device_lost_closure" followed by { }
    rank DEVICE_USAGE_SCOPES "Device::usage_scopes" followed by { }
    rank IDENTITY_MANAGER_VALUES "IdentityManager::values" followed by { }
    rank REGISTRY_STORAGE "Registry::storage" followed by { }
    rank RESOURCE_POOL_INNER "ResourcePool::inner" followed by { }
    rank SHARED_TRACKER_INDEX_ALLOCATOR_INNER "SharedTrackerIndexAllocator::inner" followed by { }
    rank SURFACE_PRESENTATION "Surface::presentation" followed by { }
    rank TEXTURE_BIND_GROUPS "Texture::bind_groups" followed by { }
    rank TEXTURE_INITIALIZATION_STATUS "Texture::initialization_status" followed by { }
    rank TEXTURE_VIEWS "Texture::views" followed by { }
    rank BLAS_BUILT_INDEX "Blas::built_index" followed by { }
    rank TLAS_BUILT_INDEX "Tlas::built_index" followed by { }
    rank TLAS_DEPENDENCIES "Tlas::dependencies" followed by { }
    rank TLAS_BIND_GROUPS "Tlas::bind_groups" followed by { }

    #[cfg(test)]
    rank PAWN "pawn" followed by { ROOK, BISHOP }
    #[cfg(test)]
    rank ROOK "rook" followed by { KNIGHT }
    #[cfg(test)]
    rank KNIGHT "knight" followed by { }
    #[cfg(test)]
    rank BISHOP "bishop" followed by { }
}