aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/webglprogram.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/webglprogram.rs')
-rw-r--r--components/script/dom/webglprogram.rs665
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;