diff options
Diffstat (limited to 'components/script/dom/webglprogram.rs')
-rw-r--r-- | components/script/dom/webglprogram.rs | 665 |
1 files changed, 524 insertions, 141 deletions
diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs index 3b2710a5d91..88cecf0296f 100644 --- a/components/script/dom/webglprogram.rs +++ b/components/script/dom/webglprogram.rs @@ -1,98 +1,137 @@ /* 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 http://mozilla.org/MPL/2.0/. */ + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl -use canvas_traits::CanvasMsg; -use dom::bindings::codegen::Bindings::WebGLProgramBinding; -use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; -use dom::bindings::js::{MutNullableJS, Root}; -use dom::bindings::reflector::{DomObject, reflect_dom_object}; -use dom::bindings::str::DOMString; -use dom::webglactiveinfo::WebGLActiveInfo; -use dom::webglobject::WebGLObject; -use dom::webglrenderingcontext::MAX_UNIFORM_AND_ATTRIBUTE_LEN; -use dom::webglshader::WebGLShader; -use dom::window::Window; +use crate::dom::bindings::cell::{DomRefCell, Ref}; +use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants2; +use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::bindings::str::DOMString; +use crate::dom::webglactiveinfo::WebGLActiveInfo; +use crate::dom::webglobject::WebGLObject; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; +use crate::dom::webglshader::WebGLShader; +use crate::dom::webgluniformlocation::WebGLUniformLocation; +use canvas_traits::webgl::{webgl_channel, WebGLProgramId, WebGLResult}; +use canvas_traits::webgl::{ + ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, WebGLCommand, WebGLError, +}; use dom_struct::dom_struct; -use ipc_channel::ipc::IpcSender; +use fnv::FnvHashSet; use std::cell::Cell; -use webrender_traits; -use webrender_traits::{WebGLCommand, WebGLError, WebGLParameter}; -use webrender_traits::{WebGLProgramId, WebGLResult}; #[dom_struct] pub struct WebGLProgram { webgl_object: WebGLObject, id: WebGLProgramId, - is_deleted: Cell<bool>, + is_in_use: Cell<bool>, + marked_for_deletion: Cell<bool>, link_called: Cell<bool>, linked: Cell<bool>, - fragment_shader: MutNullableJS<WebGLShader>, - vertex_shader: MutNullableJS<WebGLShader>, - #[ignore_heap_size_of = "Defined in ipc-channel"] - renderer: IpcSender<CanvasMsg>, + link_generation: Cell<u64>, + fragment_shader: MutNullableDom<WebGLShader>, + vertex_shader: MutNullableDom<WebGLShader>, + active_attribs: DomRefCell<Box<[ActiveAttribInfo]>>, + active_uniforms: DomRefCell<Box<[ActiveUniformInfo]>>, + active_uniform_blocks: DomRefCell<Box<[ActiveUniformBlockInfo]>>, + transform_feedback_varyings_length: Cell<i32>, + transform_feedback_mode: Cell<i32>, } impl WebGLProgram { - fn new_inherited(renderer: IpcSender<CanvasMsg>, - id: WebGLProgramId) - -> WebGLProgram { - WebGLProgram { - webgl_object: WebGLObject::new_inherited(), + fn new_inherited(context: &WebGLRenderingContext, id: WebGLProgramId) -> Self { + Self { + webgl_object: WebGLObject::new_inherited(context), id: id, - is_deleted: Cell::new(false), - link_called: Cell::new(false), - linked: Cell::new(false), + is_in_use: Default::default(), + marked_for_deletion: Default::default(), + link_called: Default::default(), + linked: Default::default(), + link_generation: Default::default(), fragment_shader: Default::default(), vertex_shader: Default::default(), - renderer: renderer, + active_attribs: DomRefCell::new(vec![].into()), + active_uniforms: DomRefCell::new(vec![].into()), + active_uniform_blocks: DomRefCell::new(vec![].into()), + transform_feedback_varyings_length: Default::default(), + transform_feedback_mode: Default::default(), } } - pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>) - -> Option<Root<WebGLProgram>> { - let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); - renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateProgram(sender))).unwrap(); - - let result = receiver.recv().unwrap(); - result.map(|program_id| WebGLProgram::new(window, renderer, program_id)) + pub fn maybe_new(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> { + let (sender, receiver) = webgl_channel().unwrap(); + context.send_command(WebGLCommand::CreateProgram(sender)); + receiver + .recv() + .unwrap() + .map(|id| WebGLProgram::new(context, id)) } - pub fn new(window: &Window, - renderer: IpcSender<CanvasMsg>, - id: WebGLProgramId) - -> Root<WebGLProgram> { - reflect_dom_object(box WebGLProgram::new_inherited(renderer, id), - window, - WebGLProgramBinding::Wrap) + pub fn new(context: &WebGLRenderingContext, id: WebGLProgramId) -> DomRoot<Self> { + reflect_dom_object( + Box::new(WebGLProgram::new_inherited(context, id)), + &*context.global(), + ) } } - impl WebGLProgram { pub fn id(&self) -> WebGLProgramId { self.id } /// glDeleteProgram - pub fn delete(&self) { - if !self.is_deleted.get() { - self.is_deleted.set(true); - let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteProgram(self.id))); + pub fn mark_for_deletion(&self, operation_fallibility: Operation) { + if self.marked_for_deletion.get() { + return; + } + self.marked_for_deletion.set(true); + let cmd = WebGLCommand::DeleteProgram(self.id); + let context = self.upcast::<WebGLObject>().context(); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(cmd), + Operation::Infallible => context.send_command(cmd), + } + if self.is_deleted() { + self.detach_shaders(); + } + } - if let Some(shader) = self.fragment_shader.get() { - shader.decrement_attached_counter(); - } + pub fn in_use(&self, value: bool) { + if self.is_in_use.get() == value { + return; + } + self.is_in_use.set(value); + if self.is_deleted() { + self.detach_shaders(); + } + } - if let Some(shader) = self.vertex_shader.get() { - shader.decrement_attached_counter(); - } + fn detach_shaders(&self) { + assert!(self.is_deleted()); + if let Some(shader) = self.fragment_shader.get() { + shader.decrement_attached_counter(); + self.fragment_shader.set(None); + } + if let Some(shader) = self.vertex_shader.get() { + shader.decrement_attached_counter(); + self.vertex_shader.set(None); } } + pub fn is_in_use(&self) -> bool { + self.is_in_use.get() + } + + pub fn is_marked_for_deletion(&self) -> bool { + self.marked_for_deletion.get() + } + pub fn is_deleted(&self) -> bool { - self.is_deleted.get() + self.marked_for_deletion.get() && !self.is_in_use.get() } pub fn is_linked(&self) -> bool { @@ -100,12 +139,13 @@ impl WebGLProgram { } /// glLinkProgram - pub fn link(&self) -> WebGLResult<()> { - if self.is_deleted() { - return Err(WebGLError::InvalidOperation); - } + pub fn link(&self) -> WebGLResult<()> { self.linked.set(false); - self.link_called.set(true); + self.link_generation + .set(self.link_generation.get().checked_add(1).unwrap()); + *self.active_attribs.borrow_mut() = Box::new([]); + *self.active_uniforms.borrow_mut() = Box::new([]); + *self.active_uniform_blocks.borrow_mut() = Box::new([]); match self.fragment_shader.get() { Some(ref shader) if shader.successfully_compiled() => {}, @@ -117,22 +157,63 @@ impl WebGLProgram { _ => return Ok(()), // callers use gl.LINK_STATUS to check link errors } - self.linked.set(true); - self.renderer.send(CanvasMsg::WebGL(WebGLCommand::LinkProgram(self.id))).unwrap(); + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::LinkProgram(self.id, sender)); + let link_info = receiver.recv().unwrap(); + + { + let mut used_locs = FnvHashSet::default(); + let mut used_names = FnvHashSet::default(); + for active_attrib in &*link_info.active_attribs { + if active_attrib.location == -1 { + continue; + } + let columns = match active_attrib.type_ { + constants::FLOAT_MAT2 => 2, + constants::FLOAT_MAT3 => 3, + constants::FLOAT_MAT4 => 4, + _ => 1, + }; + assert!(used_names.insert(&*active_attrib.name)); + for column in 0..columns { + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.31 + if !used_locs.insert(active_attrib.location as u32 + column) { + return Ok(()); + } + } + } + for active_uniform in &*link_info.active_uniforms { + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.41 + if !used_names.insert(&*active_uniform.base_name) { + return Ok(()); + } + } + } + + self.linked.set(link_info.linked); + self.link_called.set(true); + self.transform_feedback_varyings_length + .set(link_info.transform_feedback_length); + self.transform_feedback_mode + .set(link_info.transform_feedback_mode); + *self.active_attribs.borrow_mut() = link_info.active_attribs; + *self.active_uniforms.borrow_mut() = link_info.active_uniforms; + *self.active_uniform_blocks.borrow_mut() = link_info.active_uniform_blocks; Ok(()) } - /// glUseProgram - pub fn use_program(&self) -> WebGLResult<()> { - if self.is_deleted() { - return Err(WebGLError::InvalidOperation); - } - if !self.linked.get() { - return Err(WebGLError::InvalidOperation); - } + pub fn active_attribs(&self) -> Ref<[ActiveAttribInfo]> { + Ref::map(self.active_attribs.borrow(), |attribs| &**attribs) + } - self.renderer.send(CanvasMsg::WebGL(WebGLCommand::UseProgram(self.id))).unwrap(); - Ok(()) + pub fn active_uniforms(&self) -> Ref<[ActiveUniformInfo]> { + Ref::map(self.active_uniforms.borrow(), |uniforms| &**uniforms) + } + + pub fn active_uniform_blocks(&self) -> Ref<[ActiveUniformBlockInfo]> { + Ref::map(self.active_uniform_blocks.borrow(), |blocks| &**blocks) } /// glValidateProgram @@ -140,7 +221,9 @@ impl WebGLProgram { if self.is_deleted() { return Err(WebGLError::InvalidOperation); } - self.renderer.send(CanvasMsg::WebGL(WebGLCommand::ValidateProgram(self.id))).unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::ValidateProgram(self.id)); Ok(()) } @@ -155,11 +238,9 @@ impl WebGLProgram { _ => { error!("detachShader: Unexpected shader type"); return Err(WebGLError::InvalidValue); - } + }, }; - // TODO(emilio): Differentiate between same shader already assigned and other previous - // shader. if shader_slot.get().is_some() { return Err(WebGLError::InvalidOperation); } @@ -167,7 +248,9 @@ impl WebGLProgram { shader_slot.set(Some(shader)); shader.increment_attached_counter(); - self.renderer.send(CanvasMsg::WebGL(WebGLCommand::AttachShader(self.id, shader.id()))).unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::AttachShader(self.id, shader.id())); Ok(()) } @@ -180,24 +263,23 @@ impl WebGLProgram { let shader_slot = match shader.gl_type() { constants::FRAGMENT_SHADER => &self.fragment_shader, constants::VERTEX_SHADER => &self.vertex_shader, - _ => { - error!("detachShader: Unexpected shader type"); - return Err(WebGLError::InvalidValue); - } + _ => return Err(WebGLError::InvalidValue), }; match shader_slot.get() { - Some(ref attached_shader) if attached_shader.id() != shader.id() => - return Err(WebGLError::InvalidOperation), - None => - return Err(WebGLError::InvalidOperation), - _ => {} + Some(ref attached_shader) if attached_shader.id() != shader.id() => { + return Err(WebGLError::InvalidOperation); + }, + None => return Err(WebGLError::InvalidOperation), + _ => {}, } shader_slot.set(None); shader.decrement_attached_counter(); - self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DetachShader(self.id, shader.id()))).unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::DetachShader(self.id, shader.id())); Ok(()) } @@ -207,123 +289,424 @@ impl WebGLProgram { if self.is_deleted() { return Err(WebGLError::InvalidOperation); } - if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN { - return Err(WebGLError::InvalidValue); - } - // Check if the name is reserved - if name.starts_with("gl_") || name.starts_with("webgl") || name.starts_with("_webgl_") { + if !validate_glsl_name(&name)? { + return Ok(()); + } + if name.starts_with("gl_") { return Err(WebGLError::InvalidOperation); } - self.renderer - .send(CanvasMsg::WebGL(WebGLCommand::BindAttribLocation(self.id, index, String::from(name)))) - .unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::BindAttribLocation( + self.id, + index, + name.into(), + )); Ok(()) } - pub fn get_active_uniform(&self, index: u32) -> WebGLResult<Root<WebGLActiveInfo>> { + pub fn get_active_uniform(&self, index: u32) -> WebGLResult<DomRoot<WebGLActiveInfo>> { if self.is_deleted() { return Err(WebGLError::InvalidValue); } - let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); - self.renderer - .send(CanvasMsg::WebGL(WebGLCommand::GetActiveUniform(self.id, index, sender))) - .unwrap(); - - receiver.recv().unwrap().map(|(size, ty, name)| - WebGLActiveInfo::new(self.global().as_window(), size, ty, DOMString::from(name))) + let uniforms = self.active_uniforms.borrow(); + let data = uniforms + .get(index as usize) + .ok_or(WebGLError::InvalidValue)?; + Ok(WebGLActiveInfo::new( + self.global().as_window(), + data.size.unwrap_or(1), + data.type_, + data.name().into(), + )) } /// glGetActiveAttrib - pub fn get_active_attrib(&self, index: u32) -> WebGLResult<Root<WebGLActiveInfo>> { + pub fn get_active_attrib(&self, index: u32) -> WebGLResult<DomRoot<WebGLActiveInfo>> { if self.is_deleted() { return Err(WebGLError::InvalidValue); } - let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); - self.renderer - .send(CanvasMsg::WebGL(WebGLCommand::GetActiveAttrib(self.id, index, sender))) - .unwrap(); - - receiver.recv().unwrap().map(|(size, ty, name)| - WebGLActiveInfo::new(self.global().as_window(), size, ty, DOMString::from(name))) + let attribs = self.active_attribs.borrow(); + let data = attribs + .get(index as usize) + .ok_or(WebGLError::InvalidValue)?; + Ok(WebGLActiveInfo::new( + self.global().as_window(), + data.size, + data.type_, + data.name.clone().into(), + )) } /// glGetAttribLocation - pub fn get_attrib_location(&self, name: DOMString) -> WebGLResult<Option<i32>> { + pub fn get_attrib_location(&self, name: DOMString) -> WebGLResult<i32> { if !self.is_linked() || self.is_deleted() { return Err(WebGLError::InvalidOperation); } - if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN { - return Err(WebGLError::InvalidValue); + + if !validate_glsl_name(&name)? { + return Ok(-1); + } + if name.starts_with("gl_") { + return Ok(-1); + } + + let location = self + .active_attribs + .borrow() + .iter() + .find(|attrib| attrib.name == &*name) + .map_or(-1, |attrib| attrib.location); + Ok(location) + } + + /// glGetFragDataLocation + pub fn get_frag_data_location(&self, name: DOMString) -> WebGLResult<i32> { + if !self.is_linked() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); } - // Check if the name is reserved + if !validate_glsl_name(&name)? { + return Ok(-1); + } if name.starts_with("gl_") { + return Ok(-1); + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetFragDataLocation( + self.id, + name.into(), + sender, + )); + Ok(receiver.recv().unwrap()) + } + + /// glGetUniformLocation + pub fn get_uniform_location( + &self, + name: DOMString, + ) -> WebGLResult<Option<DomRoot<WebGLUniformLocation>>> { + if !self.is_linked() || self.is_deleted() { return Err(WebGLError::InvalidOperation); } - if name.starts_with("webgl") || name.starts_with("_webgl_") { + if !validate_glsl_name(&name)? { + return Ok(None); + } + if name.starts_with("gl_") { return Ok(None); } - let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); - self.renderer - .send(CanvasMsg::WebGL(WebGLCommand::GetAttribLocation(self.id, String::from(name), sender))) - .unwrap(); + let (size, type_) = { + let (base_name, array_index) = match parse_uniform_name(&name) { + Some((name, index)) if index.map_or(true, |i| i >= 0) => (name, index), + _ => return Ok(None), + }; + + let uniforms = self.active_uniforms.borrow(); + match uniforms + .iter() + .find(|attrib| &*attrib.base_name == base_name) + { + Some(uniform) if array_index.is_none() || array_index < uniform.size => ( + uniform + .size + .map(|size| size - array_index.unwrap_or_default()), + uniform.type_, + ), + _ => return Ok(None), + } + }; + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetUniformLocation( + self.id, + name.into(), + sender, + )); + let location = receiver.recv().unwrap(); + let context_id = self.upcast::<WebGLObject>().context().context_id(); + + Ok(Some(WebGLUniformLocation::new( + self.global().as_window(), + location, + context_id, + self.id, + self.link_generation.get(), + size, + type_, + ))) + } + + pub fn get_uniform_block_index(&self, name: DOMString) -> WebGLResult<u32> { + if !self.link_called.get() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + if !validate_glsl_name(&name)? { + return Ok(constants2::INVALID_INDEX); + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetUniformBlockIndex( + self.id, + name.into(), + sender, + )); Ok(receiver.recv().unwrap()) } - /// glGetUniformLocation - pub fn get_uniform_location(&self, name: DOMString) -> WebGLResult<Option<i32>> { + pub fn get_uniform_indices(&self, names: Vec<DOMString>) -> WebGLResult<Vec<u32>> { + if !self.link_called.get() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + let validation_errors = names + .iter() + .map(|name| validate_glsl_name(&name)) + .collect::<Vec<_>>(); + let first_validation_error = validation_errors.iter().find(|result| result.is_err()); + if let Some(error) = first_validation_error { + return Err(error.unwrap_err()); + } + + let names = names + .iter() + .map(|name| name.to_string()) + .collect::<Vec<_>>(); + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetUniformIndices(self.id, names, sender)); + Ok(receiver.recv().unwrap()) + } + + pub fn get_active_uniforms(&self, indices: Vec<u32>, pname: u32) -> WebGLResult<Vec<i32>> { if !self.is_linked() || self.is_deleted() { return Err(WebGLError::InvalidOperation); } - if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN { + + match pname { + constants2::UNIFORM_TYPE | + constants2::UNIFORM_SIZE | + constants2::UNIFORM_BLOCK_INDEX | + constants2::UNIFORM_OFFSET | + constants2::UNIFORM_ARRAY_STRIDE | + constants2::UNIFORM_MATRIX_STRIDE | + constants2::UNIFORM_IS_ROW_MAJOR => {}, + _ => return Err(WebGLError::InvalidEnum), + } + + if indices.len() > self.active_uniforms.borrow().len() { return Err(WebGLError::InvalidValue); } - // Check if the name is reserved - if name.starts_with("webgl") || name.starts_with("_webgl_") { - return Ok(None); + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetActiveUniforms( + self.id, indices, pname, sender, + )); + Ok(receiver.recv().unwrap()) + } + + pub fn get_active_uniform_block_parameter( + &self, + block_index: u32, + pname: u32, + ) -> WebGLResult<Vec<i32>> { + if !self.link_called.get() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + if block_index as usize >= self.active_uniform_blocks.borrow().len() { + return Err(WebGLError::InvalidValue); } - let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); - self.renderer - .send(CanvasMsg::WebGL(WebGLCommand::GetUniformLocation(self.id, String::from(name), sender))) - .unwrap(); + match pname { + constants2::UNIFORM_BLOCK_BINDING | + constants2::UNIFORM_BLOCK_DATA_SIZE | + constants2::UNIFORM_BLOCK_ACTIVE_UNIFORMS | + constants2::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES | + constants2::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER | + constants2::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {}, + _ => return Err(WebGLError::InvalidEnum), + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>().context().send_command( + WebGLCommand::GetActiveUniformBlockParameter(self.id, block_index, pname, sender), + ); Ok(receiver.recv().unwrap()) } + pub fn get_active_uniform_block_name(&self, block_index: u32) -> WebGLResult<String> { + if !self.link_called.get() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + if block_index as usize >= self.active_uniform_blocks.borrow().len() { + return Err(WebGLError::InvalidValue); + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>().context().send_command( + WebGLCommand::GetActiveUniformBlockName(self.id, block_index, sender), + ); + Ok(receiver.recv().unwrap()) + } + + pub fn bind_uniform_block(&self, block_index: u32, block_binding: u32) -> WebGLResult<()> { + if block_index as usize >= self.active_uniform_blocks.borrow().len() { + return Err(WebGLError::InvalidValue); + } + + let mut active_uniforms = self.active_uniforms.borrow_mut(); + if active_uniforms.len() > block_binding as usize { + active_uniforms[block_binding as usize].bind_index = Some(block_binding); + } + + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::UniformBlockBinding( + self.id, + block_index, + block_binding, + )); + Ok(()) + } + /// glGetProgramInfoLog pub fn get_info_log(&self) -> WebGLResult<String> { if self.is_deleted() { - return Err(WebGLError::InvalidOperation); + return Err(WebGLError::InvalidValue); } if self.link_called.get() { let shaders_compiled = match (self.fragment_shader.get(), self.vertex_shader.get()) { (Some(fs), Some(vs)) => fs.successfully_compiled() && vs.successfully_compiled(), - _ => false + _ => false, }; if !shaders_compiled { return Ok("One or more shaders failed to compile".to_string()); } } - let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); - self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GetProgramInfoLog(self.id, sender))).unwrap(); + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetProgramInfoLog(self.id, sender)); Ok(receiver.recv().unwrap()) } - /// glGetProgramParameter - pub fn parameter(&self, param_id: u32) -> WebGLResult<WebGLParameter> { - let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); - self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GetProgramParameter(self.id, param_id, sender))).unwrap(); - receiver.recv().unwrap() + pub fn attached_shaders(&self) -> WebGLResult<Vec<DomRoot<WebGLShader>>> { + if self.marked_for_deletion.get() { + return Err(WebGLError::InvalidValue); + } + Ok( + match (self.vertex_shader.get(), self.fragment_shader.get()) { + (Some(vertex_shader), Some(fragment_shader)) => { + vec![vertex_shader, fragment_shader] + }, + (Some(shader), None) | (None, Some(shader)) => vec![shader], + (None, None) => vec![], + }, + ) + } + + pub fn link_generation(&self) -> u64 { + self.link_generation.get() + } + + pub fn transform_feedback_varyings_length(&self) -> i32 { + self.transform_feedback_varyings_length.get() + } + + pub fn transform_feedback_buffer_mode(&self) -> i32 { + self.transform_feedback_mode.get() } } impl Drop for WebGLProgram { fn drop(&mut self) { - self.delete(); + self.in_use(false); + self.mark_for_deletion(Operation::Fallible); } } + +fn validate_glsl_name(name: &str) -> WebGLResult<bool> { + if name.is_empty() { + return Ok(false); + } + if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN { + return Err(WebGLError::InvalidValue); + } + for c in name.chars() { + validate_glsl_char(c)?; + } + if name.starts_with("webgl_") || name.starts_with("_webgl_") { + return Err(WebGLError::InvalidOperation); + } + Ok(true) +} + +fn validate_glsl_char(c: char) -> WebGLResult<()> { + match c { + 'a'..='z' | + 'A'..='Z' | + '0'..='9' | + ' ' | + '\t' | + '\u{11}' | + '\u{12}' | + '\r' | + '\n' | + '_' | + '.' | + '+' | + '-' | + '/' | + '*' | + '%' | + '<' | + '>' | + '[' | + ']' | + '(' | + ')' | + '{' | + '}' | + '^' | + '|' | + '&' | + '~' | + '=' | + '!' | + ':' | + ';' | + ',' | + '?' => Ok(()), + _ => Err(WebGLError::InvalidValue), + } +} + +fn parse_uniform_name(name: &str) -> Option<(&str, Option<i32>)> { + if !name.ends_with(']') { + return Some((name, None)); + } + let bracket_pos = name[..name.len() - 1].rfind('[')?; + let index = name[(bracket_pos + 1)..(name.len() - 1)] + .parse::<i32>() + .ok()?; + Some((&name[..bracket_pos], Some(index))) +} + +pub const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256; |