diff options
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/mod.rs | 2 | ||||
-rw-r--r-- | components/script/dom/vertexarrayobject.rs | 296 | ||||
-rw-r--r-- | components/script/dom/webgl2renderingcontext.rs | 42 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 290 | ||||
-rw-r--r-- | components/script/dom/webglvertexarrayobject.rs | 100 | ||||
-rw-r--r-- | components/script/dom/webglvertexarrayobjectoes.rs | 251 | ||||
-rw-r--r-- | components/script/dom/webidls/WebGL2RenderingContext.webidl | 8 | ||||
-rw-r--r-- | components/script/dom/webidls/WebGLVertexArrayObject.webidl | 11 |
8 files changed, 682 insertions, 318 deletions
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 0e1b4789ad4..3691ba5a299 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -527,6 +527,7 @@ pub mod userscripts; pub mod validation; pub mod validitystate; pub mod values; +pub mod vertexarrayobject; pub mod videotrack; pub mod videotracklist; pub mod virtualmethods; @@ -560,6 +561,7 @@ pub mod webglsync; pub mod webgltexture; pub mod webgltransformfeedback; pub mod webgluniformlocation; +pub mod webglvertexarrayobject; pub mod webglvertexarrayobjectoes; pub mod websocket; pub mod wheelevent; diff --git a/components/script/dom/vertexarrayobject.rs b/components/script/dom/vertexarrayobject.rs new file mode 100644 index 00000000000..025cff9194a --- /dev/null +++ b/components/script/dom/vertexarrayobject.rs @@ -0,0 +1,296 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::{ref_filter_map, DomRefCell, Ref}; +use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; +use crate::dom::bindings::root::{Dom, MutNullableDom}; +use crate::dom::webglbuffer::WebGLBuffer; +use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use canvas_traits::webgl::{ + ActiveAttribInfo, WebGLCommand, WebGLError, WebGLResult, WebGLVertexArrayId, +}; +use std::cell::Cell; + +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub struct VertexArrayObject { + context: Dom<WebGLRenderingContext>, + id: Option<WebGLVertexArrayId>, + ever_bound: Cell<bool>, + is_deleted: Cell<bool>, + vertex_attribs: DomRefCell<Box<[VertexAttribData]>>, + element_array_buffer: MutNullableDom<WebGLBuffer>, +} + +impl VertexArrayObject { + pub fn new(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self { + let max_vertex_attribs = context.limits().max_vertex_attribs as usize; + Self { + context: Dom::from_ref(context), + id, + ever_bound: Default::default(), + is_deleted: Default::default(), + vertex_attribs: DomRefCell::new(vec![Default::default(); max_vertex_attribs].into()), + element_array_buffer: Default::default(), + } + } + + pub fn id(&self) -> Option<WebGLVertexArrayId> { + self.id + } + + pub fn is_deleted(&self) -> bool { + self.is_deleted.get() + } + + pub fn delete(&self, fallible: bool) { + assert!(self.id.is_some()); + if self.is_deleted.get() { + return; + } + self.is_deleted.set(true); + let cmd = WebGLCommand::DeleteVertexArray(self.id.unwrap()); + if fallible { + self.context.send_command_ignored(cmd); + } else { + self.context.send_command(cmd); + } + + for attrib_data in &**self.vertex_attribs.borrow() { + if let Some(buffer) = attrib_data.buffer() { + buffer.decrement_attached_counter(); + } + } + if let Some(buffer) = self.element_array_buffer.get() { + buffer.decrement_attached_counter(); + } + } + + pub fn ever_bound(&self) -> bool { + return self.ever_bound.get(); + } + + pub fn set_ever_bound(&self) { + self.ever_bound.set(true); + } + + pub fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> { + &self.element_array_buffer + } + + pub fn get_vertex_attrib(&self, index: u32) -> Option<Ref<VertexAttribData>> { + ref_filter_map(self.vertex_attribs.borrow(), |attribs| { + attribs.get(index as usize) + }) + } + + pub fn vertex_attrib_pointer( + &self, + index: u32, + size: i32, + type_: u32, + normalized: bool, + stride: i32, + offset: i64, + ) -> WebGLResult<()> { + let mut attribs = self.vertex_attribs.borrow_mut(); + let data = attribs + .get_mut(index as usize) + .ok_or(WebGLError::InvalidValue)?; + + if size < 1 || size > 4 { + return Err(WebGLError::InvalidValue); + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#BUFFER_OFFSET_AND_STRIDE + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#VERTEX_STRIDE + if stride < 0 || stride > 255 || offset < 0 { + return Err(WebGLError::InvalidValue); + } + let bytes_per_component: i32 = match type_ { + constants::BYTE | constants::UNSIGNED_BYTE => 1, + constants::SHORT | constants::UNSIGNED_SHORT => 2, + constants::FLOAT => 4, + _ => return Err(WebGLError::InvalidEnum), + }; + if offset % bytes_per_component as i64 > 0 || stride % bytes_per_component > 0 { + return Err(WebGLError::InvalidOperation); + } + + let buffer = self.context.array_buffer(); + match buffer { + Some(ref buffer) => buffer.increment_attached_counter(), + None if offset != 0 => { + // https://github.com/KhronosGroup/WebGL/pull/2228 + return Err(WebGLError::InvalidOperation); + }, + _ => {}, + } + self.context.send_command(WebGLCommand::VertexAttribPointer( + index, + size, + type_, + normalized, + stride, + offset as u32, + )); + if let Some(old) = data.buffer() { + old.decrement_attached_counter(); + } + + *data = VertexAttribData { + enabled_as_array: data.enabled_as_array, + size: size as u8, + type_, + bytes_per_vertex: size as u8 * bytes_per_component as u8, + normalized, + stride: stride as u8, + offset: offset as u32, + buffer: buffer.map(|b| Dom::from_ref(&*b)), + divisor: data.divisor, + }; + + Ok(()) + } + + pub fn vertex_attrib_divisor(&self, index: u32, value: u32) { + self.vertex_attribs.borrow_mut()[index as usize].divisor = value; + } + + pub fn enabled_vertex_attrib_array(&self, index: u32, value: bool) { + self.vertex_attribs.borrow_mut()[index as usize].enabled_as_array = value; + } + + pub fn unbind_buffer(&self, buffer: &WebGLBuffer) { + for attrib in &mut **self.vertex_attribs.borrow_mut() { + if let Some(b) = attrib.buffer() { + if b.id() != buffer.id() { + continue; + } + b.decrement_attached_counter(); + } + attrib.buffer = None; + } + if self + .element_array_buffer + .get() + .map_or(false, |b| buffer == &*b) + { + buffer.decrement_attached_counter(); + self.element_array_buffer.set(None); + } + } + + pub fn validate_for_draw( + &self, + required_len: u32, + instance_count: u32, + active_attribs: &[ActiveAttribInfo], + ) -> WebGLResult<()> { + // TODO(nox): Cache limits per VAO. + let attribs = self.vertex_attribs.borrow(); + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.2 + if attribs + .iter() + .any(|data| data.enabled_as_array && data.buffer.is_none()) + { + return Err(WebGLError::InvalidOperation); + } + let mut has_active_attrib = false; + let mut has_divisor_0 = false; + for active_info in active_attribs { + if active_info.location < 0 { + continue; + } + has_active_attrib = true; + let attrib = &attribs[active_info.location as usize]; + if attrib.divisor == 0 { + has_divisor_0 = true; + } + if !attrib.enabled_as_array { + continue; + } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6 + if required_len > 0 && instance_count > 0 { + let max_vertices = attrib.max_vertices(); + if attrib.divisor == 0 { + if max_vertices < required_len { + return Err(WebGLError::InvalidOperation); + } + } else if max_vertices + .checked_mul(attrib.divisor) + .map_or(false, |v| v < instance_count) + { + return Err(WebGLError::InvalidOperation); + } + } + } + if has_active_attrib && !has_divisor_0 { + return Err(WebGLError::InvalidOperation); + } + Ok(()) + } +} + +impl Drop for VertexArrayObject { + fn drop(&mut self) { + if self.id.is_some() { + self.delete(true); + } + } +} + +#[derive(Clone, JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub struct VertexAttribData { + pub enabled_as_array: bool, + pub size: u8, + pub type_: u32, + bytes_per_vertex: u8, + pub normalized: bool, + pub stride: u8, + pub offset: u32, + pub buffer: Option<Dom<WebGLBuffer>>, + pub divisor: u32, +} + +impl Default for VertexAttribData { + #[allow(unrooted_must_root)] + fn default() -> Self { + Self { + enabled_as_array: false, + size: 4, + type_: constants::FLOAT, + bytes_per_vertex: 16, + normalized: false, + stride: 0, + offset: 0, + buffer: None, + divisor: 0, + } + } +} + +impl VertexAttribData { + pub fn buffer(&self) -> Option<&WebGLBuffer> { + self.buffer.as_ref().map(|b| &**b) + } + + pub fn max_vertices(&self) -> u32 { + let capacity = (self.buffer().unwrap().capacity() as u32).saturating_sub(self.offset); + if capacity < self.bytes_per_vertex as u32 { + 0 + } else if self.stride == 0 { + capacity / self.bytes_per_vertex as u32 + } else if self.stride < self.bytes_per_vertex { + (capacity - (self.bytes_per_vertex - self.stride) as u32) / self.stride as u32 + } else { + let mut max = capacity / self.stride as u32; + if capacity % self.stride as u32 >= self.bytes_per_vertex as u32 { + max += 1; + } + max + } + } +} diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index 1c458778a1c..699631de5d2 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -36,6 +36,7 @@ use crate::dom::webglsync::WebGLSync; use crate::dom::webgltexture::WebGLTexture; use crate::dom::webgltransformfeedback::WebGLTransformFeedback; use crate::dom::webgluniformlocation::WebGLUniformLocation; +use crate::dom::webglvertexarrayobject::WebGLVertexArrayObject; use crate::dom::window::Window; use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::JSContext; @@ -181,6 +182,10 @@ impl WebGL2RenderingContext { self.base.recreate(size) } + pub fn current_vao(&self) -> DomRoot<WebGLVertexArrayObject> { + self.base.current_vao_webgl2() + } + pub fn base_context(&self) -> DomRoot<WebGLRenderingContext> { DomRoot::from_ref(&*self.base) } @@ -193,6 +198,7 @@ impl WebGL2RenderingContext { constants::PIXEL_UNPACK_BUFFER => Ok(self.bound_pixel_unpack_buffer.get()), constants::TRANSFORM_FEEDBACK_BUFFER => Ok(self.bound_transform_feedback_buffer.get()), constants::UNIFORM_BUFFER => Ok(self.bound_uniform_buffer.get()), + constants::ELEMENT_ARRAY_BUFFER => Ok(self.current_vao().element_array_buffer().get()), _ => self.base.bound_buffer(target), } } @@ -777,6 +783,15 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.current_transform_feedback.get() ); }, + constants::ELEMENT_ARRAY_BUFFER_BINDING => unsafe { + let buffer = self.current_vao().element_array_buffer().get(); + return optional_root_object_to_js_or_null!(*cx, buffer); + }, + constants::VERTEX_ARRAY_BINDING => unsafe { + let vao = self.current_vao(); + let vao = vao.id().map(|_| &*vao); + return optional_root_object_to_js_or_null!(*cx, vao); + }, // NOTE: DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING, handled on the WebGL1 side constants::READ_FRAMEBUFFER_BINDING => unsafe { return optional_root_object_to_js_or_null!( @@ -942,6 +957,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2 fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) { + let current_vao; let slot = match target { constants::COPY_READ_BUFFER => &self.bound_copy_read_buffer, constants::COPY_WRITE_BUFFER => &self.bound_copy_write_buffer, @@ -949,6 +965,10 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { constants::PIXEL_UNPACK_BUFFER => &self.bound_pixel_unpack_buffer, constants::TRANSFORM_FEEDBACK_BUFFER => &self.bound_transform_feedback_buffer, constants::UNIFORM_BUFFER => &self.bound_uniform_buffer, + constants::ELEMENT_ARRAY_BUFFER => { + current_vao = self.current_vao(); + current_vao.element_array_buffer() + }, _ => return self.base.BindBuffer(target, buffer), }; self.base.bind_buffer_maybe(&slot, target, buffer); @@ -1409,6 +1429,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.CreateShader(shader_type) } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn CreateVertexArray(&self) -> Option<DomRoot<WebGLVertexArrayObject>> { + self.base.create_vertex_array_webgl2() + } + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) { let buffer = match buffer { @@ -1419,7 +1444,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { if buffer.is_marked_for_deletion() { return; } - self.base.current_vao().unbind_buffer(buffer); + self.current_vao().unbind_buffer(buffer); self.unbind_from(&self.base.array_buffer_slot(), &buffer); self.unbind_from(&self.bound_copy_read_buffer, &buffer); self.unbind_from(&self.bound_copy_write_buffer, &buffer); @@ -1463,6 +1488,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.DeleteShader(shader) } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn DeleteVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) { + self.base.delete_vertex_array_webgl2(vertex_array); + } + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 fn DrawArrays(&self, mode: u32, first: i32, count: i32) { self.base.DrawArrays(mode, first, count) @@ -1662,6 +1692,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.IsTexture(texture) } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn IsVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) -> bool { + self.base.is_vertex_array_webgl2(vertex_array) + } + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 fn LineWidth(&self, width: f32) { self.base.LineWidth(width) @@ -3042,6 +3077,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn BindVertexArray(&self, array: Option<&WebGLVertexArrayObject>) { + self.base.bind_vertex_array_webgl2(array); + } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13 fn SamplerParameteri(&self, sampler: &WebGLSampler, pname: u32, param: i32) { handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return); diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 65134a631e1..bda7c036d60 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::dom::bindings::cell::Ref; use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants; use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants; use crate::dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants; @@ -26,6 +27,7 @@ use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::htmliframeelement::HTMLIFrameElement; use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage}; use crate::dom::promise::Promise; +use crate::dom::vertexarrayobject::VertexAttribData; use crate::dom::webgl_extensions::WebGLExtensions; use crate::dom::webgl_validations::tex_image_2d::{ CommonCompressedTexImage2DValidatorResult, CommonTexImage2DValidator, @@ -47,6 +49,7 @@ use crate::dom::webglshader::WebGLShader; use crate::dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat; use crate::dom::webgltexture::{TexParameterValue, WebGLTexture}; use crate::dom::webgluniformlocation::WebGLUniformLocation; +use crate::dom::webglvertexarrayobject::WebGLVertexArrayObject; use crate::dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES; use crate::dom::window::Window; use crate::script_runtime::JSContext as SafeJSContext; @@ -183,6 +186,8 @@ pub struct WebGLRenderingContext { capabilities: Capabilities, default_vao: DomOnceCell<WebGLVertexArrayObjectOES>, current_vao: MutNullableDom<WebGLVertexArrayObjectOES>, + default_vao_webgl2: DomOnceCell<WebGLVertexArrayObject>, + current_vao_webgl2: MutNullableDom<WebGLVertexArrayObject>, textures: Textures, api_type: GlType, } @@ -242,6 +247,8 @@ impl WebGLRenderingContext { capabilities: Default::default(), default_vao: Default::default(), current_vao: Default::default(), + default_vao_webgl2: Default::default(), + current_vao_webgl2: Default::default(), textures: Textures::new(max_combined_texture_image_units), api_type: ctx_data.api_type, } @@ -294,6 +301,15 @@ impl WebGLRenderingContext { }) } + pub fn current_vao_webgl2(&self) -> DomRoot<WebGLVertexArrayObject> { + self.current_vao_webgl2.or_init(|| { + DomRoot::from_ref( + self.default_vao_webgl2 + .init_once(|| WebGLVertexArrayObject::new(self, None)), + ) + }) + } + pub fn recreate(&self, size: Size2D<u32>) { let (sender, receiver) = webgl_channel().unwrap(); self.webgl_sender.send_resize(size, sender).unwrap(); @@ -889,11 +905,18 @@ impl WebGLRenderingContext { 0 }; - self.current_vao().validate_for_draw( - required_len, - primcount as u32, - ¤t_program.active_attribs(), - )?; + match self.webgl_version() { + WebGLVersion::WebGL1 => self.current_vao().validate_for_draw( + required_len, + primcount as u32, + ¤t_program.active_attribs(), + )?, + WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw( + required_len, + primcount as u32, + ¤t_program.active_attribs(), + )?, + }; self.validate_framebuffer()?; @@ -950,11 +973,11 @@ impl WebGLRenderingContext { } let current_program = self.current_program.get().ok_or(InvalidOperation)?; - let array_buffer = self - .current_vao() - .element_array_buffer() - .get() - .ok_or(InvalidOperation)?; + let array_buffer = match self.webgl_version() { + WebGLVersion::WebGL1 => self.current_vao().element_array_buffer().get(), + WebGLVersion::WebGL2 => self.current_vao_webgl2().element_array_buffer().get(), + } + .ok_or(InvalidOperation)?; if count > 0 && primcount > 0 { // This operation cannot overflow in u64 and we know all those values are nonnegative. @@ -965,11 +988,18 @@ impl WebGLRenderingContext { } // TODO(nox): Pass the correct number of vertices required. - self.current_vao().validate_for_draw( - 0, - primcount as u32, - ¤t_program.active_attribs(), - )?; + match self.webgl_version() { + WebGLVersion::WebGL1 => self.current_vao().validate_for_draw( + 0, + primcount as u32, + ¤t_program.active_attribs(), + )?, + WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw( + 0, + primcount as u32, + ¤t_program.active_attribs(), + )?, + }; self.validate_framebuffer()?; @@ -1003,7 +1033,12 @@ impl WebGLRenderingContext { return self.webgl_error(InvalidValue); } - self.current_vao().vertex_attrib_divisor(index, divisor); + match self.webgl_version() { + WebGLVersion::WebGL1 => self.current_vao().vertex_attrib_divisor(index, divisor), + WebGLVersion::WebGL2 => self + .current_vao_webgl2() + .vertex_attrib_divisor(index, divisor), + }; self.send_command(WebGLCommand::VertexAttribDivisor { index, divisor }); } @@ -1066,6 +1101,15 @@ impl WebGLRenderingContext { .map(|id| WebGLVertexArrayObjectOES::new(self, Some(id))) } + pub fn create_vertex_array_webgl2(&self) -> Option<DomRoot<WebGLVertexArrayObject>> { + let (sender, receiver) = webgl_channel().unwrap(); + self.send_command(WebGLCommand::CreateVertexArray(sender)); + receiver + .recv() + .unwrap() + .map(|id| WebGLVertexArrayObject::new(self, Some(id))) + } + pub fn delete_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) { if let Some(vao) = vao { handle_potential_webgl_error!(self, self.validate_ownership(vao), return); @@ -1084,6 +1128,24 @@ impl WebGLRenderingContext { } } + pub fn delete_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) { + if let Some(vao) = vao { + handle_potential_webgl_error!(self, self.validate_ownership(vao), return); + // The default vertex array has no id and should never be passed around. + assert!(vao.id().is_some()); + if vao.is_deleted() { + return; + } + if vao == &*self.current_vao_webgl2() { + // Setting it to None will make self.current_vao() reset it to the default one + // next time it is called. + self.current_vao_webgl2.set(None); + self.send_command(WebGLCommand::BindVertexArray(None)); + } + vao.delete(false); + } + } + pub fn is_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) -> bool { vao.map_or(false, |vao| { // The default vertex array has no id and should never be passed around. @@ -1092,6 +1154,14 @@ impl WebGLRenderingContext { }) } + pub fn is_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) -> bool { + vao.map_or(false, |vao| { + // The default vertex array has no id and should never be passed around. + assert!(vao.id().is_some()); + self.validate_ownership(vao).is_ok() && vao.ever_bound() && !vao.is_deleted() + }) + } + pub fn bind_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) { if let Some(vao) = vao { // The default vertex array has no id and should never be passed around. @@ -1108,6 +1178,22 @@ impl WebGLRenderingContext { self.current_vao.set(vao); } + pub fn bind_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) { + if let Some(vao) = vao { + // The default vertex array has no id and should never be passed around. + assert!(vao.id().is_some()); + handle_potential_webgl_error!(self, self.validate_ownership(vao), return); + if vao.is_deleted() { + return self.webgl_error(InvalidOperation); + } + vao.set_ever_bound(); + } + self.send_command(WebGLCommand::BindVertexArray(vao.and_then(|vao| vao.id()))); + // Setting it to None will make self.current_vao() reset it to the default one + // next time it is called. + self.current_vao_webgl2.set(vao); + } + fn validate_blend_mode(&self, mode: u32) -> WebGLResult<()> { match mode { constants::FUNC_ADD | constants::FUNC_SUBTRACT | constants::FUNC_REVERSE_SUBTRACT => { @@ -2552,9 +2638,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { if attrib_id >= self.limits.max_vertex_attribs { return self.webgl_error(InvalidValue); } - - self.current_vao() - .enabled_vertex_attrib_array(attrib_id, true); + match self.webgl_version() { + WebGLVersion::WebGL1 => self + .current_vao() + .enabled_vertex_attrib_array(attrib_id, true), + WebGLVersion::WebGL2 => self + .current_vao_webgl2() + .enabled_vertex_attrib_array(attrib_id, true), + }; self.send_command(WebGLCommand::EnableVertexAttribArray(attrib_id)); } @@ -2563,9 +2654,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { if attrib_id >= self.limits.max_vertex_attribs { return self.webgl_error(InvalidValue); } - - self.current_vao() - .enabled_vertex_attrib_array(attrib_id, false); + match self.webgl_version() { + WebGLVersion::WebGL1 => self + .current_vao() + .enabled_vertex_attrib_array(attrib_id, false), + WebGLVersion::WebGL2 => self + .current_vao_webgl2() + .enabled_vertex_attrib_array(attrib_id, false), + }; self.send_command(WebGLCommand::DisableVertexAttribArray(attrib_id)); } @@ -2890,56 +2986,73 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 fn GetVertexAttrib(&self, cx: SafeJSContext, index: u32, param: u32) -> JSVal { - let current_vao = self.current_vao(); - let data = handle_potential_webgl_error!( - self, - current_vao.get_vertex_attrib(index).ok_or(InvalidValue), - return NullValue() - ); - if param == constants::CURRENT_VERTEX_ATTRIB { - let value = if index == 0 { - let (x, y, z, w) = self.current_vertex_attrib_0.get(); - [x, y, z, w] - } else { - let (sender, receiver) = webgl_channel().unwrap(); - self.send_command(WebGLCommand::GetCurrentVertexAttrib(index, sender)); - receiver.recv().unwrap() - }; - unsafe { - rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>()); - let _ = Float32Array::create(*cx, CreateWith::Slice(&value), result.handle_mut()) - .unwrap(); - return ObjectValue(result.get()); + let get_attrib = |data: Ref<VertexAttribData>| -> JSVal { + if param == constants::CURRENT_VERTEX_ATTRIB { + let value = if index == 0 { + let (x, y, z, w) = self.current_vertex_attrib_0.get(); + [x, y, z, w] + } else { + let (sender, receiver) = webgl_channel().unwrap(); + self.send_command(WebGLCommand::GetCurrentVertexAttrib(index, sender)); + receiver.recv().unwrap() + }; + unsafe { + rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>()); + let _ = + Float32Array::create(*cx, CreateWith::Slice(&value), result.handle_mut()) + .unwrap(); + return ObjectValue(result.get()); + } + } + if !self + .extension_manager + .is_get_vertex_attrib_name_enabled(param) + { + self.webgl_error(WebGLError::InvalidEnum); + return NullValue(); } - } - if !self - .extension_manager - .is_get_vertex_attrib_name_enabled(param) - { - self.webgl_error(WebGLError::InvalidEnum); - return NullValue(); - } + match param { + constants::VERTEX_ATTRIB_ARRAY_ENABLED => BooleanValue(data.enabled_as_array), + constants::VERTEX_ATTRIB_ARRAY_SIZE => Int32Value(data.size as i32), + constants::VERTEX_ATTRIB_ARRAY_TYPE => Int32Value(data.type_ as i32), + constants::VERTEX_ATTRIB_ARRAY_NORMALIZED => BooleanValue(data.normalized), + constants::VERTEX_ATTRIB_ARRAY_STRIDE => Int32Value(data.stride as i32), + constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING => unsafe { + rooted!(in(*cx) let mut jsval = NullValue()); + if let Some(buffer) = data.buffer() { + buffer.to_jsval(*cx, jsval.handle_mut()); + } + jsval.get() + }, + ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => { + UInt32Value(data.divisor) + }, + _ => { + self.webgl_error(InvalidEnum); + NullValue() + }, + } + }; - match param { - constants::VERTEX_ATTRIB_ARRAY_ENABLED => BooleanValue(data.enabled_as_array), - constants::VERTEX_ATTRIB_ARRAY_SIZE => Int32Value(data.size as i32), - constants::VERTEX_ATTRIB_ARRAY_TYPE => Int32Value(data.type_ as i32), - constants::VERTEX_ATTRIB_ARRAY_NORMALIZED => BooleanValue(data.normalized), - constants::VERTEX_ATTRIB_ARRAY_STRIDE => Int32Value(data.stride as i32), - constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING => unsafe { - rooted!(in(*cx) let mut jsval = NullValue()); - if let Some(buffer) = data.buffer() { - buffer.to_jsval(*cx, jsval.handle_mut()); - } - jsval.get() - }, - ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => { - UInt32Value(data.divisor) + match self.webgl_version() { + WebGLVersion::WebGL1 => { + let current_vao = self.current_vao(); + let data = handle_potential_webgl_error!( + self, + current_vao.get_vertex_attrib(index).ok_or(InvalidValue), + return NullValue() + ); + get_attrib(data) }, - _ => { - self.webgl_error(InvalidEnum); - NullValue() + WebGLVersion::WebGL2 => { + let current_vao = self.current_vao_webgl2(); + let data = handle_potential_webgl_error!( + self, + current_vao.get_vertex_attrib(index).ok_or(InvalidValue), + return NullValue() + ); + get_attrib(data) }, } } @@ -2950,13 +3063,26 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { self.webgl_error(InvalidEnum); return 0; } - let vao = self.current_vao(); - let data = handle_potential_webgl_error!( - self, - vao.get_vertex_attrib(index).ok_or(InvalidValue), - return 0 - ); - data.offset as i64 + match self.webgl_version() { + WebGLVersion::WebGL1 => { + let current_vao = self.current_vao(); + let data = handle_potential_webgl_error!( + self, + current_vao.get_vertex_attrib(index).ok_or(InvalidValue), + return 0 + ); + data.offset as i64 + }, + WebGLVersion::WebGL2 => { + let current_vao = self.current_vao_webgl2(); + let data = handle_potential_webgl_error!( + self, + current_vao.get_vertex_attrib(index).ok_or(InvalidValue), + return 0 + ); + data.offset as i64 + }, + } } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 @@ -3819,11 +3945,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { stride: i32, offset: i64, ) { - handle_potential_webgl_error!( - self, - self.current_vao() - .vertex_attrib_pointer(index, size, type_, normalized, stride, offset) - ); + let res = match self.webgl_version() { + WebGLVersion::WebGL1 => self + .current_vao() + .vertex_attrib_pointer(index, size, type_, normalized, stride, offset), + WebGLVersion::WebGL2 => self + .current_vao_webgl2() + .vertex_attrib_pointer(index, size, type_, normalized, stride, offset), + }; + handle_potential_webgl_error!(self, res); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4 diff --git a/components/script/dom/webglvertexarrayobject.rs b/components/script/dom/webglvertexarrayobject.rs new file mode 100644 index 00000000000..48355d05557 --- /dev/null +++ b/components/script/dom/webglvertexarrayobject.rs @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::Ref; +use crate::dom::bindings::codegen::Bindings::WebGLVertexArrayObjectBinding; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::vertexarrayobject::{VertexArrayObject, VertexAttribData}; +use crate::dom::webglbuffer::WebGLBuffer; +use crate::dom::webglobject::WebGLObject; +use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use canvas_traits::webgl::{ActiveAttribInfo, WebGLResult, WebGLVertexArrayId}; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct WebGLVertexArrayObject { + webgl_object_: WebGLObject, + array_object: VertexArrayObject, +} + +impl WebGLVertexArrayObject { + fn new_inherited(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self { + Self { + webgl_object_: WebGLObject::new_inherited(context), + array_object: VertexArrayObject::new(context, id), + } + } + + pub fn new(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> DomRoot<Self> { + reflect_dom_object( + Box::new(WebGLVertexArrayObject::new_inherited(context, id)), + &*context.global(), + WebGLVertexArrayObjectBinding::Wrap, + ) + } + + pub fn id(&self) -> Option<WebGLVertexArrayId> { + self.array_object.id() + } + + pub fn is_deleted(&self) -> bool { + self.array_object.is_deleted() + } + + pub fn delete(&self, fallible: bool) { + self.array_object.delete(fallible); + } + + pub fn ever_bound(&self) -> bool { + self.array_object.ever_bound() + } + + pub fn set_ever_bound(&self) { + self.array_object.set_ever_bound(); + } + + pub fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> { + self.array_object.element_array_buffer() + } + + pub fn get_vertex_attrib(&self, index: u32) -> Option<Ref<VertexAttribData>> { + self.array_object.get_vertex_attrib(index) + } + + pub fn vertex_attrib_pointer( + &self, + index: u32, + size: i32, + type_: u32, + normalized: bool, + stride: i32, + offset: i64, + ) -> WebGLResult<()> { + self.array_object + .vertex_attrib_pointer(index, size, type_, normalized, stride, offset) + } + + pub fn vertex_attrib_divisor(&self, index: u32, value: u32) { + self.array_object.vertex_attrib_divisor(index, value); + } + + pub fn enabled_vertex_attrib_array(&self, index: u32, value: bool) { + self.array_object.enabled_vertex_attrib_array(index, value); + } + + pub fn unbind_buffer(&self, buffer: &WebGLBuffer) { + self.array_object.unbind_buffer(buffer); + } + + pub fn validate_for_draw( + &self, + required_len: u32, + instance_count: u32, + active_attribs: &[ActiveAttribInfo], + ) -> WebGLResult<()> { + self.array_object + .validate_for_draw(required_len, instance_count, active_attribs) + } +} diff --git a/components/script/dom/webglvertexarrayobjectoes.rs b/components/script/dom/webglvertexarrayobjectoes.rs index 1d802968ae1..2c03f084031 100644 --- a/components/script/dom/webglvertexarrayobjectoes.rs +++ b/components/script/dom/webglvertexarrayobjectoes.rs @@ -2,41 +2,28 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::dom::bindings::cell::{ref_filter_map, DomRefCell, Ref}; -use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; +use crate::dom::bindings::cell::Ref; use crate::dom::bindings::codegen::Bindings::WebGLVertexArrayObjectOESBinding; -use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; -use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; +use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::vertexarrayobject::{VertexArrayObject, VertexAttribData}; use crate::dom::webglbuffer::WebGLBuffer; use crate::dom::webglobject::WebGLObject; use crate::dom::webglrenderingcontext::WebGLRenderingContext; -use canvas_traits::webgl::{ - ActiveAttribInfo, WebGLCommand, WebGLError, WebGLResult, WebGLVertexArrayId, -}; +use canvas_traits::webgl::{ActiveAttribInfo, WebGLResult, WebGLVertexArrayId}; use dom_struct::dom_struct; -use std::cell::Cell; #[dom_struct] pub struct WebGLVertexArrayObjectOES { webgl_object_: WebGLObject, - id: Option<WebGLVertexArrayId>, - ever_bound: Cell<bool>, - is_deleted: Cell<bool>, - vertex_attribs: DomRefCell<Box<[VertexAttribData]>>, - element_array_buffer: MutNullableDom<WebGLBuffer>, + array_object: VertexArrayObject, } impl WebGLVertexArrayObjectOES { fn new_inherited(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self { - let max_vertex_attribs = context.limits().max_vertex_attribs as usize; Self { webgl_object_: WebGLObject::new_inherited(context), - id, - ever_bound: Default::default(), - is_deleted: Default::default(), - vertex_attribs: DomRefCell::new(vec![Default::default(); max_vertex_attribs].into()), - element_array_buffer: Default::default(), + array_object: VertexArrayObject::new(context, id), } } @@ -49,54 +36,31 @@ impl WebGLVertexArrayObjectOES { } pub fn id(&self) -> Option<WebGLVertexArrayId> { - self.id + self.array_object.id() } pub fn is_deleted(&self) -> bool { - self.is_deleted.get() + self.array_object.is_deleted() } pub fn delete(&self, fallible: bool) { - assert!(self.id.is_some()); - if self.is_deleted.get() { - return; - } - self.is_deleted.set(true); - - let context = self.upcast::<WebGLObject>().context(); - let cmd = WebGLCommand::DeleteVertexArray(self.id.unwrap()); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); - } - - for attrib_data in &**self.vertex_attribs.borrow() { - if let Some(buffer) = attrib_data.buffer() { - buffer.decrement_attached_counter(); - } - } - if let Some(buffer) = self.element_array_buffer.get() { - buffer.decrement_attached_counter(); - } + self.array_object.delete(fallible); } pub fn ever_bound(&self) -> bool { - return self.ever_bound.get(); + self.array_object.ever_bound() } pub fn set_ever_bound(&self) { - self.ever_bound.set(true); + self.array_object.set_ever_bound(); } pub fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> { - &self.element_array_buffer + self.array_object.element_array_buffer() } pub fn get_vertex_attrib(&self, index: u32) -> Option<Ref<VertexAttribData>> { - ref_filter_map(self.vertex_attribs.borrow(), |attribs| { - attribs.get(index as usize) - }) + self.array_object.get_vertex_attrib(index) } pub fn vertex_attrib_pointer( @@ -108,93 +72,20 @@ impl WebGLVertexArrayObjectOES { stride: i32, offset: i64, ) -> WebGLResult<()> { - let mut attribs = self.vertex_attribs.borrow_mut(); - let data = attribs - .get_mut(index as usize) - .ok_or(WebGLError::InvalidValue)?; - - if size < 1 || size > 4 { - return Err(WebGLError::InvalidValue); - } - - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#BUFFER_OFFSET_AND_STRIDE - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#VERTEX_STRIDE - if stride < 0 || stride > 255 || offset < 0 { - return Err(WebGLError::InvalidValue); - } - let bytes_per_component: i32 = match type_ { - constants::BYTE | constants::UNSIGNED_BYTE => 1, - constants::SHORT | constants::UNSIGNED_SHORT => 2, - constants::FLOAT => 4, - _ => return Err(WebGLError::InvalidEnum), - }; - if offset % bytes_per_component as i64 > 0 || stride % bytes_per_component > 0 { - return Err(WebGLError::InvalidOperation); - } - - let context = self.upcast::<WebGLObject>().context(); - let buffer = context.array_buffer(); - match buffer { - Some(ref buffer) => buffer.increment_attached_counter(), - None if offset != 0 => { - // https://github.com/KhronosGroup/WebGL/pull/2228 - return Err(WebGLError::InvalidOperation); - }, - _ => {}, - } - context.send_command(WebGLCommand::VertexAttribPointer( - index, - size, - type_, - normalized, - stride, - offset as u32, - )); - if let Some(old) = data.buffer() { - old.decrement_attached_counter(); - } - - *data = VertexAttribData { - enabled_as_array: data.enabled_as_array, - size: size as u8, - type_, - bytes_per_vertex: size as u8 * bytes_per_component as u8, - normalized, - stride: stride as u8, - offset: offset as u32, - buffer: buffer.map(|b| Dom::from_ref(&*b)), - divisor: data.divisor, - }; - - Ok(()) + self.array_object + .vertex_attrib_pointer(index, size, type_, normalized, stride, offset) } pub fn vertex_attrib_divisor(&self, index: u32, value: u32) { - self.vertex_attribs.borrow_mut()[index as usize].divisor = value; + self.array_object.vertex_attrib_divisor(index, value); } pub fn enabled_vertex_attrib_array(&self, index: u32, value: bool) { - self.vertex_attribs.borrow_mut()[index as usize].enabled_as_array = value; + self.array_object.enabled_vertex_attrib_array(index, value); } pub fn unbind_buffer(&self, buffer: &WebGLBuffer) { - for attrib in &mut **self.vertex_attribs.borrow_mut() { - if let Some(b) = attrib.buffer() { - if b.id() != buffer.id() { - continue; - } - b.decrement_attached_counter(); - } - attrib.buffer = None; - } - if self - .element_array_buffer - .get() - .map_or(false, |b| buffer == &*b) - { - buffer.decrement_attached_counter(); - self.element_array_buffer.set(None); - } + self.array_object.unbind_buffer(buffer); } pub fn validate_for_draw( @@ -203,109 +94,7 @@ impl WebGLVertexArrayObjectOES { instance_count: u32, active_attribs: &[ActiveAttribInfo], ) -> WebGLResult<()> { - // TODO(nox): Cache limits per VAO. - let attribs = self.vertex_attribs.borrow(); - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.2 - if attribs - .iter() - .any(|data| data.enabled_as_array && data.buffer.is_none()) - { - return Err(WebGLError::InvalidOperation); - } - let mut has_active_attrib = false; - let mut has_divisor_0 = false; - for active_info in active_attribs { - if active_info.location < 0 { - continue; - } - has_active_attrib = true; - let attrib = &attribs[active_info.location as usize]; - if attrib.divisor == 0 { - has_divisor_0 = true; - } - if !attrib.enabled_as_array { - continue; - } - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6 - if required_len > 0 && instance_count > 0 { - let max_vertices = attrib.max_vertices(); - if attrib.divisor == 0 { - if max_vertices < required_len { - return Err(WebGLError::InvalidOperation); - } - } else if max_vertices - .checked_mul(attrib.divisor) - .map_or(false, |v| v < instance_count) - { - return Err(WebGLError::InvalidOperation); - } - } - } - if has_active_attrib && !has_divisor_0 { - return Err(WebGLError::InvalidOperation); - } - Ok(()) - } -} - -impl Drop for WebGLVertexArrayObjectOES { - fn drop(&mut self) { - if self.id.is_some() { - self.delete(true); - } - } -} - -#[derive(Clone, JSTraceable, MallocSizeOf)] -#[unrooted_must_root_lint::must_root] -pub struct VertexAttribData { - pub enabled_as_array: bool, - pub size: u8, - pub type_: u32, - bytes_per_vertex: u8, - pub normalized: bool, - pub stride: u8, - pub offset: u32, - pub buffer: Option<Dom<WebGLBuffer>>, - pub divisor: u32, -} - -impl Default for VertexAttribData { - #[allow(unrooted_must_root)] - fn default() -> Self { - Self { - enabled_as_array: false, - size: 4, - type_: constants::FLOAT, - bytes_per_vertex: 16, - normalized: false, - stride: 0, - offset: 0, - buffer: None, - divisor: 0, - } - } -} - -impl VertexAttribData { - pub fn buffer(&self) -> Option<&WebGLBuffer> { - self.buffer.as_ref().map(|b| &**b) - } - - fn max_vertices(&self) -> u32 { - let capacity = (self.buffer().unwrap().capacity() as u32).saturating_sub(self.offset); - if capacity < self.bytes_per_vertex as u32 { - 0 - } else if self.stride == 0 { - capacity / self.bytes_per_vertex as u32 - } else if self.stride < self.bytes_per_vertex { - (capacity - (self.bytes_per_vertex - self.stride) as u32) / self.stride as u32 - } else { - let mut max = capacity / self.stride as u32; - if capacity % self.stride as u32 >= self.bytes_per_vertex as u32 { - max += 1; - } - max - } + self.array_object + .validate_for_draw(required_len, instance_count, active_attribs) } } diff --git a/components/script/dom/webidls/WebGL2RenderingContext.webidl b/components/script/dom/webidls/WebGL2RenderingContext.webidl index e25174b858d..72bd68a9f8a 100644 --- a/components/script/dom/webidls/WebGL2RenderingContext.webidl +++ b/components/script/dom/webidls/WebGL2RenderingContext.webidl @@ -11,10 +11,6 @@ typedef long long GLint64; typedef unsigned long long GLuint64; - -// interface WebGLVertexArrayObject : WebGLObject { -// }; - typedef (/*[AllowShared]*/ Uint32Array or sequence<GLuint>) Uint32List; interface mixin WebGL2RenderingContextBase @@ -540,10 +536,10 @@ interface mixin WebGL2RenderingContextBase void uniformBlockBinding(WebGLProgram program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); /* Vertex Array Objects */ - /*WebGLVertexArrayObject? createVertexArray(); + WebGLVertexArrayObject? createVertexArray(); void deleteVertexArray(WebGLVertexArrayObject? vertexArray); [WebGLHandlesContextLoss] GLboolean isVertexArray(WebGLVertexArrayObject? vertexArray); - void bindVertexArray(WebGLVertexArrayObject? array);*/ + void bindVertexArray(WebGLVertexArrayObject? array); }; [Exposed=Window, Pref="dom.webgl2.enabled"] diff --git a/components/script/dom/webidls/WebGLVertexArrayObject.webidl b/components/script/dom/webidls/WebGLVertexArrayObject.webidl new file mode 100644 index 00000000000..a42d8cbe051 --- /dev/null +++ b/components/script/dom/webidls/WebGLVertexArrayObject.webidl @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +// +// WebGL IDL definitions scraped from the Khronos specification: +// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 +// + +[Exposed=(Window), Pref="dom.webgl2.enabled"] +interface WebGLVertexArrayObject : WebGLObject { +}; |