diff options
author | David Zbarsky <dzbarsky@gmail.com> | 2015-11-28 18:38:14 -0800 |
---|---|---|
committer | David Zbarsky <dzbarsky@gmail.com> | 2015-12-13 17:39:20 -0800 |
commit | 0f4d6d58aa994b14ac70cc098099ac68b6269631 (patch) | |
tree | 4abbaf37f55ba94b2d8db4b991af5385efe956e8 /components/script/dom/webglrenderingcontext.rs | |
parent | a1fb12616a29d2190baadd20d7763af1d9c02260 (diff) | |
download | servo-0f4d6d58aa994b14ac70cc098099ac68b6269631.tar.gz servo-0f4d6d58aa994b14ac70cc098099ac68b6269631.zip |
Implement various WebGL functions
Diffstat (limited to 'components/script/dom/webglrenderingcontext.rs')
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 279 |
1 files changed, 209 insertions, 70 deletions
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 0efce2afce5..1d104bba61b 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -4,7 +4,7 @@ use canvas_traits::WebGLError::*; use canvas_traits::{CanvasCommonMsg, CanvasMsg, CanvasWebGLMsg, WebGLError}; -use canvas_traits::{WebGLFramebufferBindingRequest, WebGLShaderParameter}; +use canvas_traits::{WebGLFramebufferBindingRequest, WebGLParameter}; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{WebGLRenderingContextMethods}; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes}; @@ -30,7 +30,7 @@ use euclid::size::Size2D; use ipc_channel::ipc::{self, IpcSender}; use js::jsapi::{JSContext, JSObject, RootedValue}; use js::jsapi::{JS_GetFloat32ArrayData, JS_GetObjectAsArrayBufferView}; -use js::jsval::{BooleanValue, Int32Value, JSVal, NullValue, UndefinedValue}; +use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue}; use net_traits::image::base::PixelFormat; use net_traits::image_cache_task::ImageResponse; use offscreen_gl_context::GLContextAttributes; @@ -145,18 +145,43 @@ impl WebGLRenderingContext { } } - pub fn bound_texture_for(&self, target: u32) -> Option<Root<WebGLTexture>> { - match target { + fn tex_parameter(&self, target: u32, name: u32, value: TexParameterValue) { + let texture = match target { constants::TEXTURE_2D => self.bound_texture_2d.get(), constants::TEXTURE_CUBE_MAP => self.bound_texture_cube_map.get(), - - _ => unreachable!(), + _ => return self.webgl_error(InvalidEnum), + }; + if let Some(texture) = texture { + handle_potential_webgl_error!(self, texture.tex_parameter(target, name, value)); + } else { + return self.webgl_error(InvalidOperation); } } fn mark_as_dirty(&self) { self.canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage); } + + #[allow(unsafe_code)] + fn float32_array_to_slice(&self, data: *mut JSObject) -> Option<Vec<f32>> { + unsafe { + let mut length = 0; + let mut ptr = ptr::null_mut(); + let buffer_data = JS_GetObjectAsArrayBufferView(data, &mut length, &mut ptr); + if buffer_data.is_null() { + self.webgl_error(InvalidValue); // https://github.com/servo/servo/issues/5014 + return None; + } + let data_f32 = JS_GetFloat32ArrayData(data, ptr::null()); + Some(slice::from_raw_parts(data_f32, length as usize).to_vec()) + } + } + + fn vertex_attrib(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) { + self.ipc_renderer + .send(CanvasMsg::WebGL(CanvasWebGLMsg::VertexAttrib(indx, x, y, z, w))) + .unwrap(); + } } impl Drop for WebGLRenderingContext { @@ -190,20 +215,40 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } #[allow(unsafe_code)] + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 + fn GetBufferParameter(&self, _cx: *mut JSContext, target: u32, parameter: u32) -> JSVal { + let (sender, receiver) = ipc::channel().unwrap(); + self.ipc_renderer + .send(CanvasMsg::WebGL(CanvasWebGLMsg::GetBufferParameter(target, parameter, sender))) + .unwrap(); + match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) { + WebGLParameter::Int(val) => Int32Value(val), + WebGLParameter::Bool(_) => panic!("Buffer parameter should not be bool"), + WebGLParameter::Float(_) => panic!("Buffer parameter should not be float"), + WebGLParameter::String(_) => panic!("Buffer parameter should not be string"), + WebGLParameter::Invalid => NullValue(), + } + } + + #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 fn GetParameter(&self, cx: *mut JSContext, parameter: u32) -> JSVal { - // TODO(ecoal95): Implement the missing parameters from the spec - unsafe { - let mut rval = RootedValue::new(cx, UndefinedValue()); - match parameter { - constants::VERSION => - "WebGL 1.0".to_jsval(cx, rval.handle_mut()), - constants::RENDERER | - constants::VENDOR => - "Mozilla/Servo".to_jsval(cx, rval.handle_mut()), - _ => rval.ptr = NullValue(), + let (sender, receiver) = ipc::channel().unwrap(); + self.ipc_renderer + .send(CanvasMsg::WebGL(CanvasWebGLMsg::GetParameter(parameter, sender))) + .unwrap(); + match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) { + WebGLParameter::Int(val) => Int32Value(val), + WebGLParameter::Bool(val) => BooleanValue(val), + WebGLParameter::Float(val) => DoubleValue(val as f64), + WebGLParameter::String(val) => { + let mut rval = RootedValue::new(cx, UndefinedValue()); + unsafe { + val.to_jsval(cx, rval.handle_mut()); + } + rval.ptr } - rval.ptr + WebGLParameter::Invalid => NullValue(), } } @@ -303,6 +348,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn BindAttribLocation(&self, program: Option<&WebGLProgram>, + index: u32, name: DOMString) { + if let Some(program) = program { + handle_potential_webgl_error!(self, program.bind_attrib_location(index, name)); + } + } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) { match target { @@ -393,18 +446,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Some(data) => data, None => return self.webgl_error(InvalidValue), }; - let data_vec = unsafe { - let mut length = 0; - let mut ptr = ptr::null_mut(); - let buffer_data = JS_GetObjectAsArrayBufferView(data, &mut length, &mut ptr); - if buffer_data.is_null() { - return self.webgl_error(InvalidValue) // https://github.com/servo/servo/issues/5014 - } - slice::from_raw_parts(ptr, length as usize).to_vec() - }; - self.ipc_renderer - .send(CanvasMsg::WebGL(CanvasWebGLMsg::BufferData(target, data_vec, usage))) - .unwrap() + if let Some(data_vec) = self.float32_array_to_slice(data) { + self.ipc_renderer + .send(CanvasMsg::WebGL(CanvasWebGLMsg::BufferData(target, data_vec, usage))) + .unwrap() + } } #[allow(unsafe_code)] @@ -669,6 +715,44 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 + fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) { + let type_size = match type_ { + constants::BYTE | constants::UNSIGNED_BYTE => 1, + constants::SHORT | constants::UNSIGNED_SHORT => 2, + constants::INT | constants::UNSIGNED_INT | constants::FLOAT => 4, + _ => return self.webgl_error(InvalidEnum), + }; + + if offset % type_size != 0 { + return self.webgl_error(InvalidOperation); + } + + if count <= 0 { + return self.webgl_error(InvalidOperation); + } + + if offset < 0 { + return self.webgl_error(InvalidValue); + } + + // TODO ensure a non-null WebGLBuffer must be bound to the ELEMENT_ARRAY_BUFFER + // TODO(ecoal95): Check the CURRENT_PROGRAM when we keep track of it, and if it's + // null generate an InvalidOperation error + match mode { + constants::POINTS | constants::LINE_STRIP | + constants::LINE_LOOP | constants::LINES | + constants::TRIANGLE_STRIP | constants::TRIANGLE_FAN | + constants::TRIANGLES => { + self.ipc_renderer + .send(CanvasMsg::WebGL(CanvasWebGLMsg::DrawElements(mode, count, type_, offset))) + .unwrap(); + self.mark_as_dirty(); + }, + _ => self.webgl_error(InvalidEnum), + } + } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 fn EnableVertexAttribArray(&self, attrib_id: u32) { self.ipc_renderer @@ -686,21 +770,34 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 - fn GetShaderInfoLog(&self, shader: Option<&WebGLShader>) -> Option<DOMString> { - if let Some(shader) = shader { - shader.info_log().map(DOMString::from) + fn GetProgramParameter(&self, _: *mut JSContext, program: Option<&WebGLProgram>, param_id: u32) -> JSVal { + if let Some(program) = program { + match handle_potential_webgl_error!(self, program.parameter(param_id), WebGLParameter::Invalid) { + WebGLParameter::Int(val) => Int32Value(val), + WebGLParameter::Bool(val) => BooleanValue(val), + WebGLParameter::String(_) => panic!("Program parameter should not be string"), + WebGLParameter::Float(_) => panic!("Program parameter should not be float"), + WebGLParameter::Invalid => NullValue(), + } } else { - None + NullValue() } } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetShaderInfoLog(&self, shader: Option<&WebGLShader>) -> Option<DOMString> { + shader.and_then(|s| s.info_log()).map(DOMString::from) + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 fn GetShaderParameter(&self, _: *mut JSContext, shader: Option<&WebGLShader>, param_id: u32) -> JSVal { if let Some(shader) = shader { - match handle_potential_webgl_error!(self, shader.parameter(param_id), WebGLShaderParameter::Invalid) { - WebGLShaderParameter::Int(val) => Int32Value(val), - WebGLShaderParameter::Bool(val) => BooleanValue(val), - WebGLShaderParameter::Invalid => NullValue(), + match handle_potential_webgl_error!(self, shader.parameter(param_id), WebGLParameter::Invalid) { + WebGLParameter::Int(val) => Int32Value(val), + WebGLParameter::Bool(val) => BooleanValue(val), + WebGLParameter::String(_) => panic!("Shader parameter should not be string"), + WebGLParameter::Float(_) => panic!("Shader parameter should not be float"), + WebGLParameter::Invalid => NullValue(), } } else { NullValue() @@ -848,13 +945,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { None => return, }; - let data_vec = unsafe { - let data_f32 = JS_GetFloat32ArrayData(data, ptr::null()); - slice::from_raw_parts(data_f32, 4).to_vec() - }; - self.ipc_renderer - .send(CanvasMsg::WebGL(CanvasWebGLMsg::Uniform4fv(uniform_id, data_vec))) - .unwrap() + if let Some(data_vec) = self.float32_array_to_slice(data) { + if data_vec.len() < 4 { + return self.webgl_error(InvalidOperation); + } + + self.ipc_renderer + .send(CanvasMsg::WebGL(CanvasWebGLMsg::Uniform4fv(uniform_id, data_vec))) + .unwrap() + } } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 @@ -865,6 +964,70 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib1f(&self, indx: u32, x: f32) { + self.vertex_attrib(indx, x, 0f32, 0f32, 1f32) + } + + #[allow(unsafe_code)] + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib1fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) { + if let Some(data_vec) = self.float32_array_to_slice(data) { + if data_vec.len() < 4 { + return self.webgl_error(InvalidOperation); + } + self.vertex_attrib(indx, data_vec[0], 0f32, 0f32, 1f32) + } + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib2f(&self, indx: u32, x: f32, y: f32) { + self.vertex_attrib(indx, x, y, 0f32, 1f32) + } + + #[allow(unsafe_code)] + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib2fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) { + if let Some(data_vec) = self.float32_array_to_slice(data) { + if data_vec.len() < 2 { + return self.webgl_error(InvalidOperation); + } + self.vertex_attrib(indx, data_vec[0], data_vec[1], 0f32, 1f32) + } + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib3f(&self, indx: u32, x: f32, y: f32, z: f32) { + self.vertex_attrib(indx, x, y, z, 1f32) + } + + #[allow(unsafe_code)] + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib3fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) { + if let Some(data_vec) = self.float32_array_to_slice(data) { + if data_vec.len() < 3 { + return self.webgl_error(InvalidOperation); + } + self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], 1f32) + } + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib4f(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) { + self.vertex_attrib(indx, x, y, z, w) + } + + #[allow(unsafe_code)] + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib4fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) { + if let Some(data_vec) = self.float32_array_to_slice(data) { + if data_vec.len() < 4 { + return self.webgl_error(InvalidOperation); + } + self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], data_vec[3]) + } + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 fn VertexAttribPointer(&self, attrib_id: u32, size: i32, data_type: u32, normalized: bool, stride: i32, offset: i64) { if let constants::FLOAT = data_type { @@ -962,36 +1125,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn TexParameterf(&self, target: u32, name: u32, value: f32) { - match target { - constants::TEXTURE_2D | - constants::TEXTURE_CUBE_MAP => { - if let Some(texture) = self.bound_texture_for(target) { - let result = texture.tex_parameter(target, name, TexParameterValue::Float(value)); - handle_potential_webgl_error!(self, result); - } else { - return self.webgl_error(InvalidOperation); - } - }, - - _ => self.webgl_error(InvalidEnum), - } + self.tex_parameter(target, name, TexParameterValue::Float(value)) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn TexParameteri(&self, target: u32, name: u32, value: i32) { - match target { - constants::TEXTURE_2D | - constants::TEXTURE_CUBE_MAP => { - if let Some(texture) = self.bound_texture_for(target) { - let result = texture.tex_parameter(target, name, TexParameterValue::Int(value)); - handle_potential_webgl_error!(self, result); - } else { - return self.webgl_error(InvalidOperation); - } - }, - - _ => self.webgl_error(InvalidEnum), - } + self.tex_parameter(target, name, TexParameterValue::Int(value)) } } |