1use std::cell::Cell;
6
7use script_bindings::cell::{DomRefCell, Ref};
8use script_bindings::weakref::WeakRef;
9use servo_canvas_traits::webgl::{
10 ActiveAttribInfo, WebGLCommand, WebGLError, WebGLResult, WebGLVersion, WebGLVertexArrayId,
11};
12
13use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants2;
14use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
15use crate::dom::bindings::root::{Dom, MutNullableDom};
16use crate::dom::webgl::webglbuffer::WebGLBuffer;
17use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
18
19#[derive(JSTraceable, MallocSizeOf)]
20#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
21pub(crate) struct VertexArrayObject {
22 context: WeakRef<WebGLRenderingContext>,
23 #[no_trace]
24 id: Option<WebGLVertexArrayId>,
25 ever_bound: Cell<bool>,
26 is_deleted: Cell<bool>,
27 vertex_attribs: DomRefCell<Box<[VertexAttribData]>>,
28 element_array_buffer: MutNullableDom<WebGLBuffer>,
29}
30
31impl VertexArrayObject {
32 pub(crate) fn new(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self {
33 let max_vertex_attribs = context.limits().max_vertex_attribs as usize;
34 Self {
35 context: WeakRef::new(context),
36 id,
37 ever_bound: Default::default(),
38 is_deleted: Default::default(),
39 vertex_attribs: DomRefCell::new(vec![Default::default(); max_vertex_attribs].into()),
40 element_array_buffer: Default::default(),
41 }
42 }
43
44 pub(crate) fn id(&self) -> Option<WebGLVertexArrayId> {
45 self.id
46 }
47
48 pub(crate) fn is_deleted(&self) -> bool {
49 self.is_deleted.get()
50 }
51
52 pub(crate) fn delete(&self, operation_fallibility: Operation) {
53 assert!(self.id.is_some());
54 if self.is_deleted.get() {
55 return;
56 }
57 self.is_deleted.set(true);
58
59 if let Some(context) = self.context.root() {
60 context.send_with_fallibility(
61 WebGLCommand::DeleteVertexArray(self.id.unwrap()),
62 operation_fallibility,
63 );
64 }
65
66 for attrib_data in &**self.vertex_attribs.borrow() {
67 if let Some(buffer) = attrib_data.buffer() {
68 buffer.decrement_attached_counter(operation_fallibility);
69 }
70 }
71 if let Some(buffer) = self.element_array_buffer.get() {
72 buffer.decrement_attached_counter(operation_fallibility);
73 }
74 }
75
76 pub(crate) fn ever_bound(&self) -> bool {
77 self.ever_bound.get()
78 }
79
80 pub(crate) fn set_ever_bound(&self) {
81 self.ever_bound.set(true);
82 }
83
84 pub(crate) fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> {
85 &self.element_array_buffer
86 }
87
88 pub(crate) fn get_vertex_attrib(&self, index: u32) -> Option<Ref<'_, VertexAttribData>> {
89 Ref::filter_map(self.vertex_attribs.borrow(), |attribs| {
90 attribs.get(index as usize)
91 })
92 .ok()
93 }
94
95 pub(crate) fn vertex_attrib_pointer(
96 &self,
97 index: u32,
98 size: i32,
99 type_: u32,
100 stride: i32,
101 offset: i64,
102 kind: VertexAttribPointerKind,
103 ) -> WebGLResult<()> {
104 let mut attribs = self.vertex_attribs.borrow_mut();
105 let data = attribs
106 .get_mut(index as usize)
107 .ok_or(WebGLError::InvalidValue)?;
108
109 if !(1..=4).contains(&size) {
110 return Err(WebGLError::InvalidValue);
111 }
112
113 if !(0..=255).contains(&stride) || offset < 0 {
116 return Err(WebGLError::InvalidValue);
117 }
118
119 let Some(context) = self.context.root() else {
120 return Err(WebGLError::ContextLost);
121 };
122
123 let is_webgl2 = matches!(context.webgl_version(), WebGLVersion::WebGL2);
124 let bytes_per_component: i32 = match type_ {
125 constants::BYTE | constants::UNSIGNED_BYTE => 1,
126 constants::SHORT | constants::UNSIGNED_SHORT => 2,
127 constants::FLOAT => 4,
128 constants::INT | constants::UNSIGNED_INT if is_webgl2 => 4,
129 constants2::HALF_FLOAT if is_webgl2 => 2,
130 glow::FIXED if is_webgl2 => 4,
131 constants2::INT_2_10_10_10_REV | constants2::UNSIGNED_INT_2_10_10_10_REV
132 if is_webgl2 && size == 4 =>
133 {
134 4
135 },
136 _ => return Err(WebGLError::InvalidEnum),
137 };
138
139 if offset % bytes_per_component as i64 > 0 || stride % bytes_per_component > 0 {
140 return Err(WebGLError::InvalidOperation);
141 }
142
143 let buffer = context.array_buffer();
144 match buffer {
145 Some(ref buffer) => buffer.increment_attached_counter(),
146 None if offset != 0 => {
147 return Err(WebGLError::InvalidOperation);
149 },
150 _ => {},
151 }
152 context.send_command(match kind {
153 VertexAttribPointerKind::Float { normalized } => WebGLCommand::VertexAttribPointer(
154 index,
155 size,
156 type_,
157 normalized,
158 stride,
159 offset as u32,
160 ),
161 VertexAttribPointerKind::Integer => {
162 WebGLCommand::VertexAttribIPointer(index, size, type_, stride, offset as u32)
163 },
164 });
165 if let Some(old) = data.buffer() {
166 old.decrement_attached_counter(Operation::Infallible);
167 }
168
169 *data = VertexAttribData {
170 enabled_as_array: data.enabled_as_array,
171 size: size as u8,
172 type_,
173 bytes_per_vertex: size as u8 * bytes_per_component as u8,
174 kind,
175 stride: stride as u8,
176 offset: offset as u32,
177 buffer: buffer.map(|b| Dom::from_ref(&*b)),
178 divisor: data.divisor,
179 };
180
181 Ok(())
182 }
183
184 pub(crate) fn vertex_attrib_divisor(&self, index: u32, value: u32) {
185 self.vertex_attribs.borrow_mut()[index as usize].divisor = value;
186 }
187
188 pub(crate) fn enabled_vertex_attrib_array(&self, index: u32, value: bool) {
189 self.vertex_attribs.borrow_mut()[index as usize].enabled_as_array = value;
190 }
191
192 pub(crate) fn unbind_buffer(&self, buffer: &WebGLBuffer) {
193 for attrib in &mut **self.vertex_attribs.borrow_mut() {
194 if let Some(b) = attrib.buffer() {
195 if b.id() != buffer.id() {
196 continue;
197 }
198 b.decrement_attached_counter(Operation::Infallible);
199 }
200 attrib.buffer = None;
201 }
202 if self
203 .element_array_buffer
204 .get()
205 .is_some_and(|b| buffer == &*b)
206 {
207 buffer.decrement_attached_counter(Operation::Infallible);
208 self.element_array_buffer.set(None);
209 }
210 }
211
212 pub(crate) fn validate_for_draw(
213 &self,
214 required_len: u32,
215 instance_count: u32,
216 active_attribs: &[ActiveAttribInfo],
217 ) -> WebGLResult<()> {
218 let attribs = self.vertex_attribs.borrow();
220 if attribs
222 .iter()
223 .any(|data| data.enabled_as_array && data.buffer.is_none())
224 {
225 return Err(WebGLError::InvalidOperation);
226 }
227 let mut has_active_attrib = false;
228 let mut has_divisor_0 = false;
229 for active_info in active_attribs {
230 let Some(location) = active_info.location else {
231 continue;
232 };
233 has_active_attrib = true;
234 let attrib = &attribs[location as usize];
235 if attrib.divisor == 0 {
236 has_divisor_0 = true;
237 }
238 if !attrib.enabled_as_array {
239 continue;
240 }
241 if required_len > 0 && instance_count > 0 {
243 let max_vertices = attrib.max_vertices();
244 if attrib.divisor == 0 {
245 if max_vertices < required_len {
246 return Err(WebGLError::InvalidOperation);
247 }
248 } else if max_vertices
249 .checked_mul(attrib.divisor)
250 .is_some_and(|v| v < instance_count)
251 {
252 return Err(WebGLError::InvalidOperation);
253 }
254 }
255 }
256 if has_active_attrib && !has_divisor_0 {
257 return Err(WebGLError::InvalidOperation);
258 }
259 Ok(())
260 }
261}
262
263impl Drop for VertexArrayObject {
264 fn drop(&mut self) {
265 if self.id.is_some() {
266 self.delete(Operation::Fallible);
267 }
268 }
269}
270
271#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
273pub(crate) enum VertexAttribPointerKind {
274 Float { normalized: bool },
276 Integer,
278}
279
280impl VertexAttribPointerKind {
281 pub(crate) fn is_normalized(self) -> bool {
282 matches!(self, Self::Float { normalized: true })
283 }
284
285 pub(crate) fn is_integer(self) -> bool {
286 matches!(self, Self::Integer)
287 }
288}
289
290#[derive(Clone, JSTraceable, MallocSizeOf)]
291#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
292pub(crate) struct VertexAttribData {
293 pub(crate) enabled_as_array: bool,
294 pub(crate) size: u8,
295 pub(crate) type_: u32,
296 bytes_per_vertex: u8,
297 pub(crate) kind: VertexAttribPointerKind,
298 pub(crate) stride: u8,
299 pub(crate) offset: u32,
300 pub(crate) buffer: Option<Dom<WebGLBuffer>>,
301 pub(crate) divisor: u32,
302}
303
304impl Default for VertexAttribData {
305 fn default() -> Self {
306 Self {
307 enabled_as_array: false,
308 size: 4,
309 type_: constants::FLOAT,
310 bytes_per_vertex: 16,
311 kind: VertexAttribPointerKind::Float { normalized: false },
312 stride: 0,
313 offset: 0,
314 buffer: None,
315 divisor: 0,
316 }
317 }
318}
319
320impl VertexAttribData {
321 pub(crate) fn buffer(&self) -> Option<&WebGLBuffer> {
322 self.buffer.as_deref()
323 }
324
325 pub(crate) fn max_vertices(&self) -> u32 {
326 let capacity = (self.buffer().unwrap().capacity() as u32).saturating_sub(self.offset);
327 if capacity < self.bytes_per_vertex as u32 {
328 0
329 } else if self.stride == 0 {
330 capacity / self.bytes_per_vertex as u32
331 } else if self.stride < self.bytes_per_vertex {
332 (capacity - (self.bytes_per_vertex - self.stride) as u32) / self.stride as u32
333 } else {
334 let mut max = capacity / self.stride as u32;
335 if capacity % self.stride as u32 >= self.bytes_per_vertex as u32 {
336 max += 1;
337 }
338 max
339 }
340 }
341}