concinnity_core/ecs/
access.rs1use crate::ecs::mask::{ComponentId, ComponentMask};
16
17#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
18pub struct Access {
20 component_reads: ComponentMask,
21 component_writes: ComponentMask,
22 resource_reads: ComponentMask,
23 resource_writes: ComponentMask,
24 exclusive: bool,
25}
26
27impl Access {
28 pub fn new() -> Access {
30 Access::default()
31 }
32
33 pub fn reads_components(mut self, components: ComponentMask) -> Access {
35 self.component_reads = components;
36 self
37 }
38
39 pub fn writes_components(mut self, components: ComponentMask) -> Access {
41 self.component_writes = components;
42 self
43 }
44
45 pub fn reads_resources(mut self, resources: ComponentMask) -> Access {
47 self.resource_reads = resources;
48 self
49 }
50
51 pub fn writes_resources(mut self, resources: ComponentMask) -> Access {
53 self.resource_writes = resources;
54 self
55 }
56
57 pub fn exclusive(mut self) -> Access {
61 self.exclusive = true;
62 self
63 }
64
65 pub fn is_exclusive(self) -> bool {
67 self.exclusive
68 }
69
70 pub fn union(self, other: Access) -> Access {
74 Access {
75 component_reads: self.component_reads.merged(other.component_reads),
76 component_writes: self.component_writes.merged(other.component_writes),
77 resource_reads: self.resource_reads.merged(other.resource_reads),
78 resource_writes: self.resource_writes.merged(other.resource_writes),
79 exclusive: self.exclusive || other.exclusive,
80 }
81 }
82
83 pub fn may_read_component(self, id: ComponentId) -> bool {
86 self.exclusive || self.component_reads.contains(id) || self.component_writes.contains(id)
87 }
88
89 pub fn may_write_component(self, id: ComponentId) -> bool {
91 self.exclusive || self.component_writes.contains(id)
92 }
93
94 pub fn may_read_resource(self, id: ComponentId) -> bool {
97 self.exclusive || self.resource_reads.contains(id) || self.resource_writes.contains(id)
98 }
99
100 pub fn may_write_resource(self, id: ComponentId) -> bool {
102 self.exclusive || self.resource_writes.contains(id)
103 }
104
105 pub fn conflicts_with(self, other: Access) -> bool {
107 self.exclusive
108 || other.exclusive
109 || overlaps(
110 self.component_reads,
111 self.component_writes,
112 other.component_reads,
113 other.component_writes,
114 )
115 || overlaps(
116 self.resource_reads,
117 self.resource_writes,
118 other.resource_reads,
119 other.resource_writes,
120 )
121 }
122}
123
124fn overlaps(
127 a_reads: ComponentMask,
128 a_writes: ComponentMask,
129 b_reads: ComponentMask,
130 b_writes: ComponentMask,
131) -> bool {
132 !a_writes.is_disjoint(b_reads)
133 || !a_writes.is_disjoint(b_writes)
134 || !a_reads.is_disjoint(b_writes)
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::ecs::mask::{ComponentId, ComponentMask};
141
142 fn mask(ids: &[u8]) -> ComponentMask {
143 let mut m = ComponentMask::EMPTY;
144 for &id in ids {
145 m.insert(ComponentId::new(id));
146 }
147 m
148 }
149
150 #[test]
151 fn two_readers_do_not_conflict() {
152 let a = Access::new().reads_components(mask(&[1, 2]));
153 let b = Access::new().reads_components(mask(&[2, 3]));
154 assert!(!a.conflicts_with(b));
155 assert!(!b.conflicts_with(a));
156 }
157
158 #[test]
159 fn read_write_overlap_conflicts() {
160 let reader = Access::new().reads_components(mask(&[2]));
161 let writer = Access::new().writes_components(mask(&[2]));
162 assert!(reader.conflicts_with(writer));
163 assert!(writer.conflicts_with(reader));
165 }
166
167 #[test]
168 fn write_write_overlap_conflicts() {
169 let a = Access::new().writes_components(mask(&[5]));
170 let b = Access::new().writes_components(mask(&[5, 6]));
171 assert!(a.conflicts_with(b));
172 }
173
174 #[test]
175 fn disjoint_access_does_not_conflict() {
176 let a = Access::new()
177 .reads_components(mask(&[1]))
178 .writes_components(mask(&[2]));
179 let b = Access::new()
180 .reads_components(mask(&[3]))
181 .writes_components(mask(&[4]));
182 assert!(!a.conflicts_with(b));
183 }
184
185 #[test]
186 fn exclusive_conflicts_with_everything() {
187 let solo = Access::new().exclusive();
188 let empty = Access::new();
189 assert!(solo.conflicts_with(empty));
190 assert!(empty.conflicts_with(solo));
191 assert!(solo.conflicts_with(Access::new().exclusive()));
193 }
194
195 #[test]
196 fn resource_and_component_spaces_are_independent() {
197 let writes_component_7 = Access::new().writes_components(mask(&[7]));
199 let reads_resource_7 = Access::new().reads_resources(mask(&[7]));
200 assert!(!writes_component_7.conflicts_with(reads_resource_7));
201
202 let writes_resource_7 = Access::new().writes_resources(mask(&[7]));
204 assert!(reads_resource_7.conflicts_with(writes_resource_7));
205 }
206
207 #[test]
208 fn component_conflict_holds_despite_disjoint_resources() {
209 let a = Access::new()
210 .writes_components(mask(&[1]))
211 .reads_resources(mask(&[10]));
212 let b = Access::new()
213 .reads_components(mask(&[1]))
214 .reads_resources(mask(&[11]));
215 assert!(a.conflicts_with(b));
216 }
217
218 #[test]
219 fn empty_access_never_conflicts() {
220 let a = Access::new();
221 let b = Access::new()
222 .reads_components(mask(&[1]))
223 .writes_components(mask(&[2]));
224 assert!(!a.conflicts_with(b));
225 assert!(!b.conflicts_with(a));
226 }
227}