diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-04-03 00:08:47 +0530 |
---|---|---|
committer | bors-servo <lbergstrom+bors@mozilla.com> | 2016-04-03 00:08:47 +0530 |
commit | 8b32e63db843b4ac534c33a87ed53a4acf9b6da7 (patch) | |
tree | a96e3c8742dae55815e6691369fdd1bc1d91e590 /components/script | |
parent | 85f9f9626eaff12b66299eb6190955f2726b432c (diff) | |
parent | 3fd7634f545871603577d83a08950ab6f7026f1c (diff) | |
download | servo-8b32e63db843b4ac534c33a87ed53a4acf9b6da7.tar.gz servo-8b32e63db843b4ac534c33a87ed53a4acf9b6da7.zip |
Auto merge of #10215 - ConnorGBrewster:webgl_finish, r=emilio
WebGL: Finish, Flush, DetachShader, GenerateMipmap
Implements #10212 and #10213
r? @emilio
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10215)
<!-- Reviewable:end -->
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/macros.rs | 8 | ||||
-rw-r--r-- | components/script/dom/webglprogram.rs | 31 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 72 | ||||
-rw-r--r-- | components/script/dom/webglshader.rs | 2 | ||||
-rw-r--r-- | components/script/dom/webgltexture.rs | 206 | ||||
-rw-r--r-- | components/script/dom/webidls/WebGLRenderingContext.webidl | 11 |
6 files changed, 321 insertions, 9 deletions
diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index 57966e37ae6..d3c157cf134 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -266,6 +266,14 @@ macro_rules! make_nonzero_dimension_setter( /// For use on non-jsmanaged types /// Use #[derive(JSTraceable)] on JS managed types macro_rules! no_jsmanaged_fields( + ([$ty:ident; $count:expr]) => ( + impl $crate::dom::bindings::trace::JSTraceable for [$ty; $count] { + #[inline] + fn trace(&self, _: *mut ::js::jsapi::JSTracer) { + // Do nothing + } + } + ); ($($ty:ident),+) => ( $( impl $crate::dom::bindings::trace::JSTraceable for $ty { diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs index 338a1e8d46b..378039b03eb 100644 --- a/components/script/dom/webglprogram.rs +++ b/components/script/dom/webglprogram.rs @@ -94,7 +94,10 @@ impl WebGLProgram { let shader_slot = match shader.gl_type() { constants::FRAGMENT_SHADER => &self.fragment_shader, constants::VERTEX_SHADER => &self.vertex_shader, - _ => return Err(WebGLError::InvalidOperation), + _ => { + error!("detachShader: Unexpected shader type"); + return Err(WebGLError::InvalidValue); + } }; // TODO(emilio): Differentiate between same shader already assigned and other previous @@ -110,6 +113,32 @@ impl WebGLProgram { Ok(()) } + /// glDetachShader + pub fn detach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> { + 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); + } + }; + + match shader_slot.get() { + Some(ref attached_shader) if attached_shader.id() != shader.id() => + return Err(WebGLError::InvalidOperation), + None => + return Err(WebGLError::InvalidOperation), + _ => {} + } + + shader_slot.set(None); + + self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DetachShader(self.id, shader.id()))).unwrap(); + + Ok(()) + } + /// glBindAttribLocation pub fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> { if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN { diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index e614b7afc6f..d08a9456624 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -181,6 +181,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Root::from_ref(&*self.canvas) } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 + fn Flush(&self) { + self.ipc_renderer + .send(CanvasMsg::WebGL(WebGLCommand::Flush)) + .unwrap(); + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 + fn Finish(&self) { + let (sender, receiver) = ipc::channel().unwrap(); + self.ipc_renderer + .send(CanvasMsg::WebGL(WebGLCommand::Finish(sender))) + .unwrap(); + receiver.recv().unwrap() + } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1 fn DrawingBufferWidth(&self) -> i32 { let (sender, receiver) = ipc::channel().unwrap(); @@ -332,6 +348,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn DetachShader(&self, program: Option<&WebGLProgram>, shader: Option<&WebGLShader>) { + if let Some(program) = program { + if let Some(shader) = shader { + handle_potential_webgl_error!(self, program.detach_shader(shader)); + } + } + } + + // 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 { @@ -414,6 +439,21 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn GenerateMipmap(&self, target: u32) { + let slot = match target { + constants::TEXTURE_2D => &self.bound_texture_2d, + constants::TEXTURE_CUBE_MAP => &self.bound_texture_cube_map, + + _ => return self.webgl_error(InvalidEnum), + }; + + match slot.get() { + Some(texture) => handle_potential_webgl_error!(self, texture.generate_mipmap()), + None => self.webgl_error(InvalidOperation) + } + } + #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn BufferData(&self, _cx: *mut JSContext, target: u32, data: Option<*mut JSObject>, usage: u32) { @@ -950,6 +990,25 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform1i(&self, + uniform: Option<&WebGLUniformLocation>, + val: i32) { + let uniform = match uniform { + Some(uniform) => uniform, + None => return, + }; + + match self.current_program.get() { + Some(ref program) if program.id() == uniform.program_id() => {}, + _ => return self.webgl_error(InvalidOperation), + }; + + self.ipc_renderer + .send(CanvasMsg::WebGL(WebGLCommand::Uniform1i(uniform.id(), val))) + .unwrap() + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 fn Uniform1fv(&self, uniform: Option<&WebGLUniformLocation>, data: Vec<f32>) { @@ -1107,7 +1166,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { internal_format: u32, format: u32, data_type: u32, - source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement >) { + source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) { let texture = match target { constants::TEXTURE_2D => self.bound_texture_2d.get(), constants::TEXTURE_CUBE_MAP => self.bound_texture_cube_map.get(), @@ -1169,11 +1228,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { => unimplemented!(), }; + if size.width < 0 || size.height < 0 || level < 0 { + self.webgl_error(WebGLError::InvalidOperation); + } + // TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32, size.width, size.height, format, data_type, pixels); + // depth is always 1 when coming from html elements + handle_potential_webgl_error!(self, texture.unwrap().initialize(size.width as u32, + size.height as u32, + 1, + internal_format, + level as u32)); + self.ipc_renderer .send(CanvasMsg::WebGL(msg)) .unwrap() diff --git a/components/script/dom/webglshader.rs b/components/script/dom/webglshader.rs index 03c39b698b0..804dc3cdcf8 100644 --- a/components/script/dom/webglshader.rs +++ b/components/script/dom/webglshader.rs @@ -99,7 +99,7 @@ impl WebGLShader { let validator = ShaderValidator::for_webgl(self.gl_type, SHADER_OUTPUT_FORMAT, &BuiltInResources::default()).unwrap(); - match validator.compile_and_translate(&[source.as_bytes()]) { + match validator.compile_and_translate(&[source]) { Ok(translated_source) => { // NOTE: At this point we should be pretty sure that the compilation in the paint thread // will succeed. diff --git a/components/script/dom/webgltexture.rs b/components/script/dom/webgltexture.rs index e40938d84f1..5bb7d5ff734 100644 --- a/components/script/dom/webgltexture.rs +++ b/components/script/dom/webgltexture.rs @@ -4,6 +4,7 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl use canvas_traits::CanvasMsg; +use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use dom::bindings::codegen::Bindings::WebGLTextureBinding; use dom::bindings::global::GlobalRef; @@ -12,6 +13,7 @@ use dom::bindings::reflector::reflect_dom_object; use dom::webglobject::WebGLObject; use ipc_channel::ipc::{self, IpcSender}; use std::cell::Cell; +use std::cmp; use webrender_traits::{WebGLCommand, WebGLError, WebGLResult}; pub enum TexParameterValue { @@ -19,6 +21,11 @@ pub enum TexParameterValue { Int(i32), } +const MAX_LEVEL_COUNT: usize = 31; +const MAX_FACE_COUNT: usize = 6; + +no_jsmanaged_fields!([ImageInfo; MAX_LEVEL_COUNT * MAX_FACE_COUNT]); + #[dom_struct] pub struct WebGLTexture { webgl_object: WebGLObject, @@ -26,6 +33,13 @@ pub struct WebGLTexture { /// The target to which this texture was bound the first time target: Cell<Option<u32>>, is_deleted: Cell<bool>, + is_initialized: Cell<bool>, + /// Stores information about mipmap levels and cubemap faces. + #[ignore_heap_size_of = "Arrays are cumbersome"] + image_info_array: DOMRefCell<[ImageInfo; MAX_LEVEL_COUNT * MAX_FACE_COUNT]>, + /// Face count can only be 1 or 6 + face_count: Cell<u8>, + base_mipmap_level: u32, #[ignore_heap_size_of = "Defined in ipc-channel"] renderer: IpcSender<CanvasMsg>, } @@ -37,6 +51,10 @@ impl WebGLTexture { id: id, target: Cell::new(None), is_deleted: Cell::new(false), + is_initialized: Cell::new(false), + face_count: Cell::new(0), + base_mipmap_level: 0, + image_info_array: DOMRefCell::new([ImageInfo::new(); MAX_LEVEL_COUNT * MAX_FACE_COUNT]), renderer: renderer, } } @@ -63,11 +81,22 @@ impl WebGLTexture { // NB: Only valid texture targets come here pub fn bind(&self, target: u32) -> WebGLResult<()> { + if self.is_deleted.get() { + return Err(WebGLError::InvalidOperation); + } + if let Some(previous_target) = self.target.get() { if target != previous_target { return Err(WebGLError::InvalidOperation); } } else { + // This is the first time binding + let face_count = match target { + constants::TEXTURE_2D => 1, + constants::TEXTURE_CUBE_MAP => 6, + _ => return Err(WebGLError::InvalidOperation) + }; + self.face_count.set(face_count); self.target.set(Some(target)); } @@ -76,6 +105,58 @@ impl WebGLTexture { Ok(()) } + pub fn initialize(&self, width: u32, height: u32, depth: u32, internal_format: u32, level: u32) -> WebGLResult<()> { + let image_info = ImageInfo { + width: width, + height: height, + depth: depth, + internal_format: Some(internal_format), + is_initialized: true, + }; + self.set_image_infos_at_level(level, image_info); + + self.is_initialized.set(true); + + Ok(()) + } + + pub fn generate_mipmap(&self) -> WebGLResult<()> { + let target = match self.target.get() { + Some(target) => target, + None => { + error!("Cannot generate mipmap on texture that has no target!"); + return Err(WebGLError::InvalidOperation); + } + }; + + let base_image_info = self.base_image_info().unwrap(); + + if !base_image_info.is_initialized() { + return Err(WebGLError::InvalidOperation); + } + + if target == constants::TEXTURE_CUBE_MAP && !self.is_cube_complete() { + return Err(WebGLError::InvalidOperation); + } + + if !base_image_info.is_power_of_two() { + return Err(WebGLError::InvalidOperation); + } + + if base_image_info.is_compressed_format() { + return Err(WebGLError::InvalidOperation); + } + + self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GenerateMipmap(target))).unwrap(); + + if self.base_mipmap_level + base_image_info.get_max_mimap_levels() == 0 { + return Err(WebGLError::InvalidOperation); + } + + let last_level = self.base_mipmap_level + base_image_info.get_max_mimap_levels() - 1; + self.populate_mip_chain(self.base_mipmap_level, last_level) + } + pub fn delete(&self) { if !self.is_deleted.get() { self.is_deleted.set(true); @@ -145,6 +226,84 @@ impl WebGLTexture { _ => Err(WebGLError::InvalidEnum), } } + + pub fn populate_mip_chain(&self, first_level: u32, last_level: u32) -> WebGLResult<()> { + let base_image_info = self.image_info_at_face(0, first_level); + if !base_image_info.is_initialized() { + return Err(WebGLError::InvalidOperation); + } + + let mut ref_width = base_image_info.width; + let mut ref_height = base_image_info.height; + + if ref_width == 0 || ref_height == 0 { + return Err(WebGLError::InvalidOperation); + } + + for level in (first_level + 1)..last_level { + if ref_width == 1 && ref_height == 1 { + break; + } + + ref_width = cmp::max(1, ref_width / 2); + ref_height = cmp::max(1, ref_height / 2); + + let image_info = ImageInfo { + width: ref_width, + height: ref_height, + depth: 0, + internal_format: base_image_info.internal_format, + is_initialized: base_image_info.is_initialized(), + }; + + self.set_image_infos_at_level(level, image_info); + } + Ok(()) + } + + fn is_cube_complete(&self) -> bool { + let image_info = self.base_image_info().unwrap(); + if !image_info.is_defined() { + return false; + } + + let ref_width = image_info.width; + let ref_format = image_info.internal_format; + + for face in 0..self.face_count.get() { + let current_image_info = self.image_info_at_face(face, self.base_mipmap_level); + if !current_image_info.is_defined() { + return false; + } + + // Compares height with width to enforce square dimensions + if current_image_info.internal_format != ref_format || + current_image_info.width != ref_width || + current_image_info.height != ref_width { + return false; + } + } + + true + } + + fn image_info_at_face(&self, face: u8, level: u32) -> ImageInfo { + let pos = (level * self.face_count.get() as u32) + face as u32; + self.image_info_array.borrow()[pos as usize] + } + + fn set_image_infos_at_level(&self, level: u32, image_info: ImageInfo) { + for face in 0..self.face_count.get() { + let pos = (level * self.face_count.get() as u32) + face as u32; + self.image_info_array.borrow_mut()[pos as usize] = image_info; + } + } + + fn base_image_info(&self) -> Option<ImageInfo> { + assert!((self.base_mipmap_level as usize) < MAX_LEVEL_COUNT); + + Some(self.image_info_at_face(0, self.base_mipmap_level)) + } } impl Drop for WebGLTexture { @@ -152,3 +311,50 @@ impl Drop for WebGLTexture { self.delete(); } } + +#[derive(Clone, Copy, PartialEq, Debug, JSTraceable, HeapSizeOf)] +struct ImageInfo { + width: u32, + height: u32, + depth: u32, + internal_format: Option<u32>, + is_initialized: bool, +} + +impl ImageInfo { + fn new() -> ImageInfo { + ImageInfo { + width: 0, + height: 0, + depth: 0, + internal_format: None, + is_initialized: false, + } + } + + fn is_power_of_two(&self) -> bool { + self.width.is_power_of_two() && self.height.is_power_of_two() && self.depth.is_power_of_two() + } + + fn is_initialized(&self) -> bool { + self.is_initialized + } + + fn is_defined(&self) -> bool { + !self.internal_format.is_none() + } + + fn get_max_mimap_levels(&self) -> u32 { + let largest = cmp::max(cmp::max(self.width, self.height), self.depth); + if largest == 0 { + return 0; + } + // FloorLog2(largest) + 1 + (largest as f64).log2() as u32 + 1 + } + + fn is_compressed_format(&self) -> bool { + // TODO: Once Servo supports compressed formats, check for them here + false + } +} diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl index ea49e38d4bd..39e13835c29 100644 --- a/components/script/dom/webidls/WebGLRenderingContext.webidl +++ b/components/script/dom/webidls/WebGLRenderingContext.webidl @@ -548,7 +548,7 @@ interface WebGLRenderingContextBase void depthFunc(GLenum func); void depthMask(GLboolean flag); void depthRange(GLclampf zNear, GLclampf zFar); - //void detachShader(WebGLProgram? program, WebGLShader? shader); + void detachShader(WebGLProgram? program, WebGLShader? shader); void disable(GLenum cap); //void disableVertexAttribArray(GLuint index); void drawArrays(GLenum mode, GLint first, GLsizei count); @@ -556,8 +556,8 @@ interface WebGLRenderingContextBase void enable(GLenum cap); void enableVertexAttribArray(GLuint index); - //void finish(); - //void flush(); + void finish(); + void flush(); //void framebufferRenderbuffer(GLenum target, GLenum attachment, // GLenum renderbuffertarget, // WebGLRenderbuffer? renderbuffer); @@ -565,7 +565,7 @@ interface WebGLRenderingContextBase // WebGLTexture? texture, GLint level); void frontFace(GLenum mode); - //void generateMipmap(GLenum target); + void generateMipmap(GLenum target); //WebGLActiveInfo? getActiveAttrib(WebGLProgram? program, GLuint index); //WebGLActiveInfo? getActiveUniform(WebGLProgram? program, GLuint index); @@ -647,7 +647,7 @@ interface WebGLRenderingContextBase void uniform1f(WebGLUniformLocation? location, GLfloat x); //void uniform1fv(WebGLUniformLocation? location, Float32Array v); void uniform1fv(WebGLUniformLocation? location, sequence<GLfloat> v); - //void uniform1i(WebGLUniformLocation? location, GLint x); + void uniform1i(WebGLUniformLocation? location, GLint x); //void uniform1iv(WebGLUniformLocation? location, Int32Array v); //void uniform1iv(WebGLUniformLocation? location, sequence<long> v); //void uniform2f(WebGLUniformLocation? location, GLfloat x, GLfloat y); @@ -717,4 +717,3 @@ interface WebGLRenderingContext { }; WebGLRenderingContext implements WebGLRenderingContextBase; - |