diff options
-rw-r--r-- | components/script/dom/webgl_extensions/ext/oesvertexarrayobject.rs | 104 | ||||
-rw-r--r-- | components/script/dom/webgl_extensions/extensions.rs | 49 | ||||
-rw-r--r-- | components/script/dom/webgl_extensions/wrapper.rs | 10 | ||||
-rw-r--r-- | components/script/dom/webglbuffer.rs | 111 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 464 | ||||
-rw-r--r-- | components/script/dom/webglvertexarrayobjectoes.rs | 259 |
6 files changed, 439 insertions, 558 deletions
diff --git a/components/script/dom/webgl_extensions/ext/oesvertexarrayobject.rs b/components/script/dom/webgl_extensions/ext/oesvertexarrayobject.rs index 6ae83c401ba..aae50c1effe 100644 --- a/components/script/dom/webgl_extensions/ext/oesvertexarrayobject.rs +++ b/components/script/dom/webgl_extensions/ext/oesvertexarrayobject.rs @@ -2,24 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use canvas_traits::webgl::{webgl_channel, WebGLCommand, WebGLError, WebGLVersion}; +use canvas_traits::webgl::WebGLVersion; use dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::{self, OESVertexArrayObjectMethods}; use dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants; use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; -use dom::bindings::root::{Dom, DomRoot, MutNullableDom}; +use dom::bindings::root::{Dom, DomRoot}; use dom::webglrenderingcontext::WebGLRenderingContext; use dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES; use dom_struct::dom_struct; -use js::conversions::ToJSValConvertible; -use js::jsapi::JSContext; -use js::jsval::{JSVal, NullValue}; use super::{WebGLExtension, WebGLExtensions, WebGLExtensionSpec}; #[dom_struct] pub struct OESVertexArrayObject { reflector_: Reflector, ctx: Dom<WebGLRenderingContext>, - bound_vao: MutNullableDom<WebGLVertexArrayObjectOES>, } impl OESVertexArrayObject { @@ -27,104 +23,29 @@ impl OESVertexArrayObject { Self { reflector_: Reflector::new(), ctx: Dom::from_ref(ctx), - bound_vao: MutNullableDom::new(None) } } - - #[allow(unsafe_code)] - fn get_current_binding(&self, cx:*mut JSContext) -> JSVal { - rooted!(in(cx) let mut rval = NullValue()); - if let Some(bound_vao) = self.bound_vao.get() { - unsafe { - bound_vao.to_jsval(cx, rval.handle_mut()); - } - } - rval.get() - } } impl OESVertexArrayObjectMethods for OESVertexArrayObject { // https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/ fn CreateVertexArrayOES(&self) -> Option<DomRoot<WebGLVertexArrayObjectOES>> { - let (sender, receiver) = webgl_channel().unwrap(); - self.ctx.send_command(WebGLCommand::CreateVertexArray(sender)); - receiver.recv().unwrap().map(|id| WebGLVertexArrayObjectOES::new(&self.ctx, id)) + self.ctx.create_vertex_array() } // https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/ fn DeleteVertexArrayOES(&self, vao: Option<&WebGLVertexArrayObjectOES>) { - if let Some(vao) = vao { - if vao.is_deleted() { - return; - } - - // Unbind deleted VAO if currently bound - if let Some(bound_vao) = self.bound_vao.get() { - if bound_vao.id() == vao.id() { - self.bound_vao.set(None); - self.ctx.send_command(WebGLCommand::BindVertexArray(None)); - } - } - - // Remove VAO references from buffers - for attrib_data in &*vao.vertex_attribs().borrow() { - if let Some(buffer) = attrib_data.buffer() { - buffer.remove_vao_reference(vao.id()); - } - } - if let Some(buffer) = vao.bound_buffer_element_array() { - buffer.remove_vao_reference(vao.id()); - } - - // Delete the vao - self.ctx.send_command(WebGLCommand::DeleteVertexArray(vao.id())); - vao.set_deleted(); - } + self.ctx.delete_vertex_array(vao); } // https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/ fn IsVertexArrayOES(&self, vao: Option<&WebGLVertexArrayObjectOES>) -> bool { - // Conformance tests expect false if vao never bound - vao.map_or(false, |vao| !vao.is_deleted() && vao.ever_bound()) + self.ctx.is_vertex_array(vao) } // https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/ fn BindVertexArrayOES(&self, vao: Option<&WebGLVertexArrayObjectOES>) { - if let Some(bound_vao) = self.bound_vao.get() { - // Store buffers attached to attrib pointers - bound_vao.vertex_attribs().clone_from(&self.ctx.vertex_attribs()); - for attrib_data in &*bound_vao.vertex_attribs().borrow() { - if let Some(buffer) = attrib_data.buffer() { - buffer.add_vao_reference(bound_vao.id()); - } - } - // Store element array buffer - let element_array = self.ctx.bound_buffer_element_array(); - bound_vao.set_bound_buffer_element_array(element_array.as_ref().map(|buffer| { - buffer.add_vao_reference(bound_vao.id()); - &**buffer - })); - } - - if let Some(vao) = vao { - if vao.is_deleted() { - self.ctx.webgl_error(WebGLError::InvalidOperation); - return; - } - - self.ctx.send_command(WebGLCommand::BindVertexArray(Some(vao.id()))); - vao.set_ever_bound(); - self.bound_vao.set(Some(&vao)); - - // Restore WebGLRenderingContext current bindings - self.ctx.vertex_attribs().clone_from(&vao.vertex_attribs()); - let element_array = vao.bound_buffer_element_array(); - self.ctx.set_bound_buffer_element_array(element_array.as_ref().map(|buffer| &**buffer)); - } else { - self.ctx.send_command(WebGLCommand::BindVertexArray(None)); - self.bound_vao.set(None); - self.ctx.vertex_attribs().clear(); - } + self.ctx.bind_vertex_array(vao); } } @@ -147,18 +68,7 @@ impl WebGLExtension for OESVertexArrayObject { } fn enable(ext: &WebGLExtensions) { - let query = OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES; - ext.add_query_parameter_handler(query, Box::new(|cx, webgl_ctx| { - match webgl_ctx.get_extension_manager().get_dom_object::<OESVertexArrayObject>() { - Some(dom_object) => { - Ok(dom_object.get_current_binding(cx)) - }, - None => { - // Extension instance not found! - Err(WebGLError::InvalidOperation) - } - } - })); + ext.enable_get_parameter_name(OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES); } fn name() -> &'static str { diff --git a/components/script/dom/webgl_extensions/extensions.rs b/components/script/dom/webgl_extensions/extensions.rs index b3812f51cf5..51d8de44efc 100644 --- a/components/script/dom/webgl_extensions/extensions.rs +++ b/components/script/dom/webgl_extensions/extensions.rs @@ -2,24 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use canvas_traits::webgl::{WebGLError, WebGLVersion}; +use canvas_traits::webgl::WebGLVersion; use dom::bindings::cell::DomRefCell; use dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants; use dom::bindings::codegen::Bindings::EXTTextureFilterAnisotropicBinding::EXTTextureFilterAnisotropicConstants; use dom::bindings::codegen::Bindings::OESStandardDerivativesBinding::OESStandardDerivativesConstants; use dom::bindings::codegen::Bindings::OESTextureHalfFloatBinding::OESTextureHalfFloatConstants; +use dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; -use dom::bindings::root::DomRoot; use dom::bindings::trace::JSTraceable; use dom::webglrenderingcontext::WebGLRenderingContext; use fnv::{FnvHashMap, FnvHashSet}; use gleam::gl::GLenum; -use js::jsapi::JSContext; use js::jsapi::JSObject; -use js::jsval::JSVal; use malloc_size_of::MallocSizeOf; -use ref_filter_map::ref_filter_map; -use std::cell::Ref; use std::collections::HashMap; use std::iter::FromIterator; use std::ptr::NonNull; @@ -43,9 +39,10 @@ const DEFAULT_NOT_FILTERABLE_TEX_TYPES: [GLenum; 2] = [ // Param names that are implemented for glGetParameter in a WebGL 1.0 context // but must trigger a InvalidEnum error until the related WebGL Extensions are enabled. // Example: https://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/ -const DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL1: [GLenum; 2] = [ +const DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL1: [GLenum; 3] = [ EXTTextureFilterAnisotropicConstants::MAX_TEXTURE_MAX_ANISOTROPY_EXT, OESStandardDerivativesConstants::FRAGMENT_SHADER_DERIVATIVE_HINT_OES, + OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES, ]; // Param names that are implemented for glGetTexParameter in a WebGL 1.0 context @@ -69,7 +66,6 @@ struct WebGLExtensionFeatures { disabled_tex_types: FnvHashSet<GLenum>, not_filterable_tex_types: FnvHashSet<GLenum>, effective_tex_internal_formats: FnvHashMap<TexFormatType, u32>, - query_parameter_handlers: FnvHashMap<GLenum, WebGLQueryParameterHandler>, /// WebGL Hint() targets enabled by extensions. hint_targets: FnvHashSet<GLenum>, /// WebGL GetParameter() names enabled by extensions. @@ -120,7 +116,6 @@ impl WebGLExtensionFeatures { disabled_tex_types, not_filterable_tex_types: DEFAULT_NOT_FILTERABLE_TEX_TYPES.iter().cloned().collect(), effective_tex_internal_formats: Default::default(), - query_parameter_handlers: Default::default(), hint_targets: Default::default(), disabled_get_parameter_names, disabled_get_tex_parameter_names, @@ -196,18 +191,6 @@ impl WebGLExtensions { self.extensions.borrow().get(&name).map_or(false, |ext| { ext.is_enabled() }) } - pub fn get_dom_object<T>(&self) -> Option<DomRoot<T::Extension>> - where - T: 'static + WebGLExtension + JSTraceable + MallocSizeOf - { - let name = T::name().to_uppercase(); - self.extensions.borrow().get(&name).and_then(|extension| { - extension.as_any().downcast_ref::<TypedWebGLExtensionWrapper<T>>().and_then(|extension| { - extension.dom_object() - }) - }) - } - pub fn supports_gl_extension(&self, name: &str) -> bool { self.features.borrow().gl_extensions.contains(name) } @@ -252,19 +235,6 @@ impl WebGLExtensions { self.features.borrow().not_filterable_tex_types.get(&text_data_type).is_none() } - pub fn add_query_parameter_handler(&self, name: GLenum, f: Box<WebGLQueryParameterFunc>) { - let handler = WebGLQueryParameterHandler { - func: f - }; - self.features.borrow_mut().query_parameter_handlers.insert(name, handler); - } - - pub fn get_query_parameter_handler(&self, name: GLenum) -> Option<Ref<Box<WebGLQueryParameterFunc>>> { - ref_filter_map(self.features.borrow(), |features| { - features.query_parameter_handlers.get(&name).map(|item| &item.func) - }) - } - pub fn enable_hint_target(&self, name: GLenum) { self.features.borrow_mut().hint_targets.insert(name); } @@ -331,14 +301,3 @@ impl WebGLExtensions { // Helper structs #[derive(Eq, Hash, JSTraceable, MallocSizeOf, PartialEq)] struct TexFormatType(u32, u32); - -type WebGLQueryParameterFunc = Fn(*mut JSContext, &WebGLRenderingContext) - -> Result<JSVal, WebGLError>; - -#[derive(MallocSizeOf)] -struct WebGLQueryParameterHandler { - #[ignore_malloc_size_of = "Closures are hard"] - func: Box<WebGLQueryParameterFunc> -} - -unsafe_no_jsmanaged_fields!(WebGLQueryParameterHandler); diff --git a/components/script/dom/webgl_extensions/wrapper.rs b/components/script/dom/webgl_extensions/wrapper.rs index 05fb375ffbd..b5166327381 100644 --- a/components/script/dom/webgl_extensions/wrapper.rs +++ b/components/script/dom/webgl_extensions/wrapper.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::reflector::DomObject; -use dom::bindings::root::{DomRoot, MutNullableDom}; +use dom::bindings::root::MutNullableDom; use dom::bindings::trace::JSTraceable; use dom::webglrenderingcontext::WebGLRenderingContext; use js::jsapi::JSObject; @@ -87,11 +87,3 @@ impl<T> WebGLExtensionWrapper for TypedWebGLExtensionWrapper<T> self } } - -impl<T> TypedWebGLExtensionWrapper<T> - where T: WebGLExtension + JSTraceable + MallocSizeOf + 'static -{ - pub fn dom_object(&self) -> Option<DomRoot<T::Extension>> { - self.extension.get() - } -} diff --git a/components/script/dom/webglbuffer.rs b/components/script/dom/webglbuffer.rs index 27fe415e8f8..66a3ce0af40 100644 --- a/components/script/dom/webglbuffer.rs +++ b/components/script/dom/webglbuffer.rs @@ -3,9 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl -use canvas_traits::webgl::{WebGLBufferId, WebGLCommand, WebGLError, WebGLResult, WebGLVertexArrayId}; +use canvas_traits::webgl::{WebGLBufferId, WebGLCommand, WebGLError, WebGLResult}; use canvas_traits::webgl::webgl_channel; -use dom::bindings::cell::DomRefCell; use dom::bindings::codegen::Bindings::WebGLBufferBinding; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants; use dom::bindings::inheritance::Castable; @@ -15,8 +14,6 @@ use dom::webglobject::WebGLObject; use dom::webglrenderingcontext::WebGLRenderingContext; use dom_struct::dom_struct; use std::cell::Cell; -use std::collections::HashSet; - #[dom_struct] pub struct WebGLBuffer { @@ -25,10 +22,8 @@ pub struct WebGLBuffer { /// The target to which this buffer was bound the first time target: Cell<Option<u32>>, capacity: Cell<usize>, - is_deleted: Cell<bool>, - // The Vertex Array Objects that are referencing this buffer - vao_references: DomRefCell<Option<HashSet<WebGLVertexArrayId>>>, - pending_delete: Cell<bool>, + marked_for_deletion: Cell<bool>, + attached_counter: Cell<u32>, /// https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetBufferParameteriv.xml usage: Cell<u32>, } @@ -37,12 +32,11 @@ impl WebGLBuffer { fn new_inherited(context: &WebGLRenderingContext, id: WebGLBufferId) -> Self { Self { webgl_object: WebGLObject::new_inherited(context), - id: id, - target: Cell::new(None), - capacity: Cell::new(0), - is_deleted: Cell::new(false), - vao_references: DomRefCell::new(None), - pending_delete: Cell::new(false), + id, + target: Default::default(), + capacity: Default::default(), + marked_for_deletion: Default::default(), + attached_counter: Default::default(), usage: Cell::new(WebGLRenderingContextConstants::STATIC_DRAW), } } @@ -68,24 +62,6 @@ impl WebGLBuffer { self.id } - // NB: Only valid buffer targets come here - pub fn bind(&self, target: u32) -> WebGLResult<()> { - if self.is_deleted() || self.is_pending_delete() { - return Err(WebGLError::InvalidOperation); - } - if let Some(previous_target) = self.target.get() { - if target != previous_target { - return Err(WebGLError::InvalidOperation); - } - } else { - self.target.set(Some(target)); - } - self.upcast::<WebGLObject>() - .context() - .send_command(WebGLCommand::BindBuffer(target, Some(self.id))); - Ok(()) - } - pub fn buffer_data<T>(&self, target: u32, data: T, usage: u32) -> WebGLResult<()> where T: Into<Vec<u8>>, @@ -115,56 +91,59 @@ impl WebGLBuffer { self.capacity.get() } - pub fn delete(&self) { - if !self.is_deleted.get() { - self.is_deleted.set(true); - self.upcast::<WebGLObject>() - .context() - .send_command(WebGLCommand::DeleteBuffer(self.id)); + pub fn mark_for_deletion(&self) { + if self.marked_for_deletion.get() { + return; + } + self.marked_for_deletion.set(true); + if self.is_deleted() { + self.delete(); } } - pub fn is_deleted(&self) -> bool { - self.is_deleted.get() + fn delete(&self) { + assert!(self.is_deleted()); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::DeleteBuffer(self.id)); } - pub fn target(&self) -> Option<u32> { - self.target.get() + pub fn is_marked_for_deletion(&self) -> bool { + self.marked_for_deletion.get() } - pub fn is_attached_to_vao(&self) -> bool { - self.vao_references.borrow().as_ref().map_or(false, |vaos| !vaos.is_empty()) + pub fn is_deleted(&self) -> bool { + self.marked_for_deletion.get() && !self.is_attached() } - pub fn set_pending_delete(&self) { - self.pending_delete.set(true); + pub fn target(&self) -> Option<u32> { + self.target.get() } - pub fn is_pending_delete(&self) -> bool { - self.pending_delete.get() + pub fn set_target(&self, target: u32) -> WebGLResult<()> { + if self.target.get().map_or(false, |t| t != target) { + return Err(WebGLError::InvalidOperation); + } + self.target.set(Some(target)); + Ok(()) } - pub fn add_vao_reference(&self, id: WebGLVertexArrayId) { - let mut vao_refs = self.vao_references.borrow_mut(); - if let Some(ref mut vao_refs) = *vao_refs { - vao_refs.insert(id); - return; - } + pub fn is_attached(&self) -> bool { + self.attached_counter.get() != 0 + } - let mut map = HashSet::new(); - map.insert(id); - *vao_refs = Some(map); + pub fn increment_attached_counter(&self) { + self.attached_counter.set( + self.attached_counter.get().checked_add(1).expect("refcount overflowed"), + ); } - pub fn remove_vao_reference(&self, id: WebGLVertexArrayId) { - if let Some(ref mut vao_refs) = *self.vao_references.borrow_mut() { - if vao_refs.take(&id).is_some() && self.pending_delete.get() { - // WebGL spec: The deleted buffers should no longer be valid when the VAOs are deleted - self.upcast::<WebGLObject>() - .context() - .send_command(WebGLCommand::DeleteBuffer(self.id)); - self.is_deleted.set(true); - } + pub fn decrement_attached_counter(&self) { + self.attached_counter.set( + self.attached_counter.get().checked_sub(1).expect("refcount underflowed"), + ); + if self.is_deleted() { + self.delete(); } } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index ec0d5238555..afdbda09dfe 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -4,7 +4,7 @@ use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; use canvas_traits::canvas::{byte_swap, multiply_u8_pixel}; -use canvas_traits::webgl::{ActiveAttribInfo, DOMToTextureCommand, Parameter}; +use canvas_traits::webgl::{DOMToTextureCommand, Parameter}; use canvas_traits::webgl::{TexParameter, WebGLCommand, WebGLContextShareMode, WebGLError}; use canvas_traits::webgl::{WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender}; use canvas_traits::webgl::{WebGLProgramId, WebGLResult, WebGLSLVersion, WebGLSender}; @@ -13,6 +13,7 @@ use canvas_traits::webgl::WebGLError::*; use dom::bindings::cell::DomRefCell; use dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants; use dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants; +use dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes}; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods; @@ -24,7 +25,7 @@ use dom::bindings::conversions::{DerivedFrom, ToJSValConvertible}; use dom::bindings::error::{Error, ErrorResult}; use dom::bindings::inheritance::Castable; use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; -use dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom}; +use dom::bindings::root::{Dom, DomOnceCell, DomRoot, LayoutDom, MutNullableDom}; use dom::bindings::str::DOMString; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::htmlcanvaselement::HTMLCanvasElement; @@ -47,6 +48,7 @@ use dom::webglshader::WebGLShader; use dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat; use dom::webgltexture::{TexParameterValue, WebGLTexture}; use dom::webgluniformlocation::WebGLUniformLocation; +use dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES; use dom::window::Window; use dom_struct::dom_struct; use euclid::Size2D; @@ -61,11 +63,10 @@ use js::typedarray::{TypedArray, TypedArrayElementCreator}; use net_traits::image::base::PixelFormat; use net_traits::image_cache::ImageResponse; use offscreen_gl_context::{GLContextAttributes, GLLimits}; -use ref_filter_map::ref_filter_map; use script_layout_interface::HTMLCanvasDataSource; use serde::{Deserialize, Serialize}; use servo_config::prefs::PREFS; -use std::cell::{Cell, Ref}; +use std::cell::Cell; use std::cmp; use std::ptr::{self, NonNull}; use webrender_api; @@ -95,17 +96,6 @@ macro_rules! handle_object_deletion { }; } -macro_rules! object_binding_to_js_or_null { - ($cx: expr, $binding:expr) => { - { - rooted!(in($cx) let mut rval = NullValue()); - if let Some(bound_object) = $binding.get() { - bound_object.to_jsval($cx, rval.handle_mut()); - } - rval.get() - } - }; -} macro_rules! optional_root_object_to_js_or_null { ($cx: expr, $binding:expr) => { @@ -196,9 +186,8 @@ pub struct WebGLRenderingContext { bound_textures: DomRefCell<FnvHashMap<u32, TextureUnitBindings>>, bound_texture_unit: Cell<u32>, bound_buffer_array: MutNullableDom<WebGLBuffer>, - bound_buffer_element_array: MutNullableDom<WebGLBuffer>, - vertex_attribs: VertexAttribs, current_program: MutNullableDom<WebGLProgram>, + /// https://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Vertex_Attribute_0 #[ignore_malloc_size_of = "Because it's small"] current_vertex_attrib_0: Cell<(f32, f32, f32, f32)>, #[ignore_malloc_size_of = "Because it's small"] @@ -207,6 +196,8 @@ pub struct WebGLRenderingContext { current_clear_color: Cell<(f32, f32, f32, f32)>, extension_manager: WebGLExtensions, capabilities: Capabilities, + default_vao: DomOnceCell<WebGLVertexArrayObjectOES>, + current_vao: MutNullableDom<WebGLVertexArrayObjectOES>, } impl WebGLRenderingContext { @@ -239,7 +230,6 @@ impl WebGLRenderingContext { share_mode: ctx_data.share_mode, webgl_version, glsl_version: ctx_data.glsl_version, - vertex_attribs: VertexAttribs::new(ctx_data.limits.max_vertex_attribs), limits: ctx_data.limits, canvas: Dom::from_ref(canvas), last_error: Cell::new(None), @@ -249,7 +239,6 @@ impl WebGLRenderingContext { bound_textures: DomRefCell::new(Default::default()), bound_texture_unit: Cell::new(constants::TEXTURE0), bound_buffer_array: MutNullableDom::new(None), - bound_buffer_element_array: MutNullableDom::new(None), bound_renderbuffer: MutNullableDom::new(None), current_program: MutNullableDom::new(None), current_vertex_attrib_0: Cell::new((0f32, 0f32, 0f32, 1f32)), @@ -257,6 +246,8 @@ impl WebGLRenderingContext { current_clear_color: Cell::new((0.0, 0.0, 0.0, 0.0)), extension_manager: WebGLExtensions::new(webgl_version), capabilities: Default::default(), + default_vao: Default::default(), + current_vao: Default::default(), } }) } @@ -288,6 +279,14 @@ impl WebGLRenderingContext { &self.limits } + fn current_vao(&self) -> DomRoot<WebGLVertexArrayObjectOES> { + self.current_vao.or_init(|| { + DomRoot::from_ref( + self.default_vao.init_once(|| WebGLVertexArrayObjectOES::new(self, None)), + ) + }) + } + fn bound_texture(&self, target: u32) -> Option<DomRoot<WebGLTexture>> { match target { constants::TEXTURE_2D => { @@ -318,18 +317,6 @@ impl WebGLRenderingContext { }) } - pub fn vertex_attribs(&self) -> &VertexAttribs { - &self.vertex_attribs - } - - pub fn bound_buffer_element_array(&self) -> Option<DomRoot<WebGLBuffer>> { - self.bound_buffer_element_array.get() - } - - pub fn set_bound_buffer_element_array(&self, buffer: Option<&WebGLBuffer>) { - self.bound_buffer_element_array.set(buffer); - } - pub fn recreate(&self, size: Size2D<i32>) { let (sender, receiver) = webgl_channel().unwrap(); self.webgl_sender.send_resize(size, sender).unwrap(); @@ -382,10 +369,6 @@ impl WebGLRenderingContext { self.webgl_sender.send_vr(command).unwrap(); } - pub fn get_extension_manager<'a>(&'a self) -> &'a WebGLExtensions { - &self.extension_manager - } - pub fn webgl_error(&self, err: WebGLError) { // TODO(emilio): Add useful debug messages to this warn!("WebGL error: {:?}, previous error was {:?}", err, self.last_error.get()); @@ -1183,7 +1166,7 @@ impl WebGLRenderingContext { handle_potential_webgl_error!( self, - self.vertex_attribs.validate_for_draw(required_len, primcount as u32, ¤t_program.active_attribs()), + self.current_vao().validate_for_draw(required_len, primcount as u32, ¤t_program.active_attribs()), return ); @@ -1234,7 +1217,7 @@ impl WebGLRenderingContext { ); if count > 0 && primcount > 0 { - if let Some(array_buffer) = self.bound_buffer_element_array.get() { + if let Some(array_buffer) = self.current_vao().element_array_buffer().get() { // WebGL Spec: check buffer overflows, must be a valid multiple of the size. let val = offset as u64 + (count as u64 * type_size as u64); if val > array_buffer.capacity() as u64 { @@ -1253,7 +1236,7 @@ impl WebGLRenderingContext { // TODO(nox): Pass the correct number of vertices required. handle_potential_webgl_error!( self, - self.vertex_attribs.validate_for_draw(0, primcount as u32, ¤t_program.active_attribs()), + self.current_vao().validate_for_draw(0, primcount as u32, ¤t_program.active_attribs()), return ); @@ -1276,7 +1259,7 @@ impl WebGLRenderingContext { return self.webgl_error(InvalidValue); } - self.vertex_attribs.set_divisor(index, divisor); + self.current_vao().vertex_attrib_divisor(index, divisor); self.send_command(WebGLCommand::VertexAttribDivisor { index, divisor }); } @@ -1312,14 +1295,66 @@ impl WebGLRenderingContext { Some(receiver.recv().unwrap().into()) } + pub fn array_buffer(&self) -> Option<DomRoot<WebGLBuffer>> { + self.bound_buffer_array.get() + } + pub fn bound_buffer(&self, target: u32) -> WebGLResult<Option<DomRoot<WebGLBuffer>>> { match target { constants::ARRAY_BUFFER => Ok(self.bound_buffer_array.get()), - constants::ELEMENT_ARRAY_BUFFER => Ok(self.bound_buffer_element_array.get()), + constants::ELEMENT_ARRAY_BUFFER => Ok(self.current_vao().element_array_buffer().get()), _ => Err(WebGLError::InvalidEnum), } } + pub fn create_vertex_array(&self) -> Option<DomRoot<WebGLVertexArrayObjectOES>> { + let (sender, receiver) = webgl_channel().unwrap(); + self.send_command(WebGLCommand::CreateVertexArray(sender)); + receiver.recv().unwrap().map(|id| WebGLVertexArrayObjectOES::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); + // 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() { + // Setting it to None will make self.current_vao() reset it to the default one + // next time it is called. + self.current_vao.set(None); + self.send_command(WebGLCommand::BindVertexArray(None)); + } + vao.delete(); + } + } + + 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. + 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. + 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.set(vao); + } + fn validate_blend_mode(&self, mode: u32) -> WebGLResult<()> { match mode { constants::FUNC_ADD | @@ -1414,29 +1449,40 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 unsafe fn GetParameter(&self, cx: *mut JSContext, parameter: u32) -> JSVal { - // Handle the GL_*_BINDING without going all the way - // to the GL, since we would just need to map back from GL's - // returned ID to the WebGL* object we're tracking. + if !self.extension_manager.is_get_parameter_name_enabled(parameter) { + self.webgl_error(WebGLError::InvalidEnum); + return NullValue(); + } + match parameter { - constants::ARRAY_BUFFER_BINDING => - return object_binding_to_js_or_null!(cx, &self.bound_buffer_array), + constants::ARRAY_BUFFER_BINDING => { + return optional_root_object_to_js_or_null!(cx, &self.bound_buffer_array.get()); + } constants::CURRENT_PROGRAM => { - return object_binding_to_js_or_null!(cx, &self.current_program); - } - constants::ELEMENT_ARRAY_BUFFER_BINDING => - return object_binding_to_js_or_null!(cx, &self.bound_buffer_element_array), - constants::FRAMEBUFFER_BINDING => - return object_binding_to_js_or_null!(cx, &self.bound_framebuffer), - constants::RENDERBUFFER_BINDING => - return object_binding_to_js_or_null!(cx, &self.bound_renderbuffer), + return optional_root_object_to_js_or_null!(cx, &self.current_program.get()); + } + constants::ELEMENT_ARRAY_BUFFER_BINDING => { + let buffer = self.current_vao().element_array_buffer().get(); + return optional_root_object_to_js_or_null!(cx, buffer); + } + constants::FRAMEBUFFER_BINDING => { + return optional_root_object_to_js_or_null!(cx, &self.bound_framebuffer.get()); + } + constants::RENDERBUFFER_BINDING => { + return optional_root_object_to_js_or_null!(cx, &self.bound_renderbuffer.get()); + } constants::TEXTURE_BINDING_2D => { let texture = self.bound_texture(constants::TEXTURE_2D); - return optional_root_object_to_js_or_null!(cx, texture) - }, + return optional_root_object_to_js_or_null!(cx, texture); + } constants::TEXTURE_BINDING_CUBE_MAP => { let texture = self.bound_texture(constants::TEXTURE_CUBE_MAP); - return optional_root_object_to_js_or_null!(cx, texture) - }, + return optional_root_object_to_js_or_null!(cx, texture); + } + OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES => { + let vao = self.current_vao.get().filter(|vao| vao.id().is_some()); + return optional_root_object_to_js_or_null!(cx, vao); + } // In readPixels we currently support RGBA/UBYTE only. If // we wanted to support other formats, we could ask the // driver, but we would need to check for @@ -1525,24 +1571,6 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return BooleanValue(value); } - if !self.extension_manager.is_get_parameter_name_enabled(parameter) { - self.webgl_error(WebGLError::InvalidEnum); - return NullValue(); - } - - // Handle GetParameter getters injected via WebGL extensions - if let Some(query_handler) = self.extension_manager.get_query_parameter_handler(parameter) { - match query_handler(cx, &self) { - Ok(value) => { - return value; - }, - Err(error) => { - self.webgl_error(error); - return NullValue(); - } - } - } - match handle_potential_webgl_error!(self, Parameter::from_u32(parameter), return NullValue()) { Parameter::Bool(param) => { let (sender, receiver) = webgl_channel().unwrap(); @@ -1793,23 +1821,30 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { handle_potential_webgl_error!(self, self.validate_ownership(buffer), return); } + let current_vao; let slot = match target { - constants::ARRAY_BUFFER => &self.bound_buffer_array, - constants::ELEMENT_ARRAY_BUFFER => &self.bound_buffer_element_array, - + constants::ARRAY_BUFFER => { + &self.bound_buffer_array + } + constants::ELEMENT_ARRAY_BUFFER => { + current_vao = self.current_vao(); + current_vao.element_array_buffer() + } _ => return self.webgl_error(InvalidEnum), }; if let Some(buffer) = buffer { - match buffer.bind(target) { - Ok(_) => slot.set(Some(buffer)), - Err(e) => return self.webgl_error(e), + if buffer.is_marked_for_deletion() { + return self.webgl_error(InvalidOperation); } - } else { - slot.set(None); - // Unbind the current buffer - self.send_command(WebGLCommand::BindBuffer(target, None)); + handle_potential_webgl_error!(self, buffer.set_target(target), return); + buffer.increment_attached_counter(); } + self.send_command(WebGLCommand::BindBuffer(target, buffer.map(|b| b.id()))); + if let Some(old) = slot.get() { + old.decrement_attached_counter(); + } + slot.set(buffer); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 @@ -2248,26 +2283,20 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) { - if let Some(buffer) = buffer { - handle_potential_webgl_error!(self, self.validate_ownership(buffer), return); - - if buffer.is_attached_to_vao() { - // WebGL spec: The buffers attached to VAOs should still not be deleted. - // They are deleted after the VAO is deleted. - buffer.set_pending_delete(); - return; - } - - // Remove deleted buffer from bound attrib buffers. - self.vertex_attribs.delete_buffer(buffer); - - // Delete buffer. - handle_object_deletion!(self, self.bound_buffer_array, buffer, - Some(WebGLCommand::BindBuffer(constants::ARRAY_BUFFER, None))); - handle_object_deletion!(self, self.bound_buffer_element_array, buffer, - Some(WebGLCommand::BindBuffer(constants::ELEMENT_ARRAY_BUFFER, None))); - buffer.delete() + let buffer = match buffer { + Some(buffer) => buffer, + None => return, + }; + handle_potential_webgl_error!(self, self.validate_ownership(buffer), return); + if buffer.is_marked_for_deletion() { + return; + } + self.current_vao().unbind_buffer(buffer); + if self.bound_buffer_array.get().map_or(false, |b| buffer == &*b) { + self.bound_buffer_array.set(None); + buffer.decrement_attached_counter(); } + buffer.mark_for_deletion(); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 @@ -2398,7 +2427,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { handle_potential_webgl_error!( self, - self.vertex_attribs.validate_for_draw(required_len, 1, ¤t_program.active_attribs()), + self.current_vao().validate_for_draw(required_len, 1, ¤t_program.active_attribs()), return ); @@ -2449,7 +2478,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { ); if count > 0 { - if let Some(array_buffer) = self.bound_buffer_element_array.get() { + if let Some(array_buffer) = self.current_vao().element_array_buffer().get() { // WebGL Spec: check buffer overflows, must be a valid multiple of the size. let val = offset as u64 + (count as u64 * type_size as u64); if val > array_buffer.capacity() as u64 { @@ -2468,7 +2497,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // TODO(nox): Pass the correct number of vertices required. handle_potential_webgl_error!( self, - self.vertex_attribs.validate_for_draw(0, 1, ¤t_program.active_attribs()), + self.current_vao().validate_for_draw(0, 1, ¤t_program.active_attribs()), return ); @@ -2486,7 +2515,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return self.webgl_error(InvalidValue); } - self.vertex_attribs.enabled_as_array(attrib_id, true); + self.current_vao().enabled_vertex_attrib_array(attrib_id, true); self.send_command(WebGLCommand::EnableVertexAttribArray(attrib_id)); } @@ -2496,7 +2525,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return self.webgl_error(InvalidValue); } - self.vertex_attribs.enabled_as_array(attrib_id, false); + self.current_vao().enabled_vertex_attrib_array(attrib_id, false); self.send_command(WebGLCommand::DisableVertexAttribArray(attrib_id)); } @@ -2788,9 +2817,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 unsafe fn GetVertexAttrib(&self, cx: *mut JSContext, index: u32, param: u32) -> JSVal { + let current_vao = self.current_vao(); let data = handle_potential_webgl_error!( self, - self.vertex_attribs.get(index).ok_or(InvalidValue), + current_vao.get_vertex_attrib(index).ok_or(InvalidValue), return NullValue() ); if param == constants::CURRENT_VERTEX_ATTRIB { @@ -2824,15 +2854,13 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { constants::VERTEX_ATTRIB_ARRAY_STRIDE => Int32Value(data.stride as i32), constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING => { rooted!(in(cx) let mut jsval = NullValue()); - if let Some(data) = self.vertex_attribs.get(index) { - if let Some(buffer) = data.buffer() { - buffer.to_jsval(cx, jsval.handle_mut()); - } + if let Some(buffer) = data.buffer() { + buffer.to_jsval(cx, jsval.handle_mut()); } jsval.get() } ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => { - Int32Value(data.divisor as i32) + UInt32Value(data.divisor) } _ => { self.webgl_error(InvalidEnum); @@ -2847,9 +2875,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { self.webgl_error(InvalidEnum); return 0; } + let vao = self.current_vao(); let data = handle_potential_webgl_error!( self, - self.vertex_attribs.get(index).ok_or(InvalidValue), + vao.get_vertex_attrib(index).ok_or(InvalidValue), return 0 ); data.offset as i64 @@ -2875,7 +2904,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool { buffer.map_or(false, |buf| { - self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_deleted() + self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_marked_for_deletion() }) } @@ -3829,19 +3858,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { ) { handle_potential_webgl_error!( self, - self.vertex_attribs.set_pointer( + self.current_vao().vertex_attrib_pointer( index, size, type_, normalized, stride, offset, - self.bound_buffer_array.get().as_ref().map(|buffer| &**buffer), - ), - return + ) ); - - self.send_command(WebGLCommand::VertexAttribPointer(index, size, type_, normalized, stride, offset as u32)); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4 @@ -4245,199 +4270,6 @@ impl LayoutCanvasWebGLRenderingContextHelpers for LayoutDom<WebGLRenderingContex } } -#[derive(JSTraceable, MallocSizeOf)] -#[must_root] -pub struct VertexAttribs { - attribs: DomRefCell<Box<[VertexAttribData]>>, -} - -impl VertexAttribs { - pub fn new(max: u32) -> Self { - // High-end GPUs have 16 of those, let's just use a boxed slice. - Self { attribs: DomRefCell::new(vec![Default::default(); max as usize].into()) } - } - - pub fn clear(&self) { - for attrib in &mut **self.attribs.borrow_mut() { - *attrib = Default::default(); - } - } - - pub fn clone_from(&self, other: &Self) { - self.attribs.borrow_mut().clone_from_slice(&other.attribs.borrow()); - } - - pub fn set_pointer( - &self, - index: u32, - size: i32, - type_: u32, - normalized: bool, - stride: i32, - offset: i64, - buffer: Option<&WebGLBuffer>, - ) -> WebGLResult<()> { - let mut attribs = self.attribs.borrow_mut(); - let data = attribs.get_mut(index as usize).ok_or(InvalidValue)?; - - if size < 1 || size > 4 { - return Err(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(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(InvalidEnum), - }; - if offset % bytes_per_component as i64 > 0 || stride % bytes_per_component > 0 { - return Err(InvalidOperation); - } - - let buffer = buffer.ok_or(InvalidOperation)?; - - *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: Some(Dom::from_ref(buffer)), - divisor: data.divisor, - }; - Ok(()) - } - - pub fn borrow(&self) -> Ref<[VertexAttribData]> { - Ref::map(self.attribs.borrow(), |attribs| &**attribs) - } - - fn delete_buffer(&self, buffer: &WebGLBuffer) { - for attrib in &mut **self.attribs.borrow_mut() { - if attrib.buffer().map_or(false, |b| b.id() == buffer.id()) { - attrib.buffer = None; - } - } - } - - fn get(&self, index: u32) -> Option<Ref<VertexAttribData>> { - ref_filter_map(self.attribs.borrow(), |attribs| attribs.get(index as usize)) - } - - fn enabled_as_array(&self, index: u32, value: bool) { - self.attribs.borrow_mut()[index as usize].enabled_as_array = value; - } - - fn set_divisor(&self, index: u32, value: u32) { - self.attribs.borrow_mut()[index as usize].divisor = value; - } - - fn validate_for_draw( - &self, - required_len: u32, - instance_count: u32, - active_attribs: &[ActiveAttribInfo], - ) -> WebGLResult<()> { - // TODO(nox): Cache limits per VAO. - let attribs = self.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(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(InvalidOperation); - } - } else if max_vertices.checked_mul(attrib.divisor).map_or(false, |v| v < instance_count) { - return Err(InvalidOperation); - } - } - } - if has_active_attrib && !has_divisor_0 { - return Err(InvalidOperation); - } - Ok(()) - } -} - -#[derive(Clone, JSTraceable, MallocSizeOf)] -#[must_root] -pub struct VertexAttribData { - enabled_as_array: bool, - size: u8, - type_: u32, - bytes_per_vertex: u8, - normalized: bool, - stride: u8, - offset: u32, - buffer: Option<Dom<WebGLBuffer>>, - 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 - } - } -} - #[derive(Default, JSTraceable, MallocSizeOf)] struct Capabilities { value: Cell<CapFlags>, diff --git a/components/script/dom/webglvertexarrayobjectoes.rs b/components/script/dom/webglvertexarrayobjectoes.rs index 029cb30bea5..c717108b2aa 100644 --- a/components/script/dom/webglvertexarrayobjectoes.rs +++ b/components/script/dom/webglvertexarrayobjectoes.rs @@ -2,39 +2,44 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use canvas_traits::webgl::WebGLVertexArrayId; +use canvas_traits::webgl::{ActiveAttribInfo, WebGLCommand, WebGLError, WebGLResult, WebGLVertexArrayId}; +use dom::bindings::cell::DomRefCell; +use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use dom::bindings::codegen::Bindings::WebGLVertexArrayObjectOESBinding; +use dom::bindings::inheritance::Castable; use dom::bindings::reflector::{DomObject, reflect_dom_object}; -use dom::bindings::root::{DomRoot, MutNullableDom}; +use dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use dom::webglbuffer::WebGLBuffer; use dom::webglobject::WebGLObject; -use dom::webglrenderingcontext::{VertexAttribs, WebGLRenderingContext}; +use dom::webglrenderingcontext::WebGLRenderingContext; use dom_struct::dom_struct; -use std::cell::Cell; +use ref_filter_map::ref_filter_map; +use std::cell::{Cell, Ref}; #[dom_struct] pub struct WebGLVertexArrayObjectOES { webgl_object_: WebGLObject, - id: WebGLVertexArrayId, + id: Option<WebGLVertexArrayId>, ever_bound: Cell<bool>, is_deleted: Cell<bool>, - vertex_attribs: VertexAttribs, - bound_buffer_element_array: MutNullableDom<WebGLBuffer>, + vertex_attribs: DomRefCell<Box<[VertexAttribData]>>, + element_array_buffer: MutNullableDom<WebGLBuffer>, } impl WebGLVertexArrayObjectOES { - fn new_inherited(context: &WebGLRenderingContext, id: WebGLVertexArrayId) -> Self { + 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: id, - ever_bound: Cell::new(false), - is_deleted: Cell::new(false), - vertex_attribs: VertexAttribs::new(context.limits().max_vertex_attribs), - bound_buffer_element_array: MutNullableDom::new(None), + 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 new(context: &WebGLRenderingContext, id: WebGLVertexArrayId) -> DomRoot<Self> { + pub fn new(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> DomRoot<Self> { reflect_dom_object( Box::new(WebGLVertexArrayObjectOES::new_inherited(context, id)), &*context.global(), @@ -42,11 +47,7 @@ impl WebGLVertexArrayObjectOES { ) } - pub fn vertex_attribs(&self) -> &VertexAttribs { - &self.vertex_attribs - } - - pub fn id(&self) -> WebGLVertexArrayId { + pub fn id(&self) -> Option<WebGLVertexArrayId> { self.id } @@ -54,8 +55,25 @@ impl WebGLVertexArrayObjectOES { self.is_deleted.get() } - pub fn set_deleted(&self) { - self.is_deleted.set(true) + pub fn delete(&self) { + assert!(self.id.is_some()); + if self.is_deleted.get() { + return; + } + self.is_deleted.set(true); + + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::DeleteVertexArray(self.id.unwrap())); + + 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 { @@ -66,11 +84,202 @@ impl WebGLVertexArrayObjectOES { self.ever_bound.set(true); } - pub fn bound_buffer_element_array(&self) -> Option<DomRoot<WebGLBuffer>> { - self.bound_buffer_element_array.get() + 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 context = self.upcast::<WebGLObject>().context(); + let buffer = context.array_buffer().ok_or(WebGLError::InvalidOperation)?; + buffer.increment_attached_counter(); + 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: Some(Dom::from_ref(&*buffer)), + 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 set_bound_buffer_element_array(&self, buffer: Option<&WebGLBuffer>) { - self.bound_buffer_element_array.set(buffer); + 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 WebGLVertexArrayObjectOES { + fn drop(&mut self) { + if self.id.is_some() { + self.delete(); + } + } +} + +#[derive(Clone, JSTraceable, MallocSizeOf)] +#[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 + } } } |