From 3fd7634f545871603577d83a08950ab6f7026f1c Mon Sep 17 00:00:00 2001 From: Connor Brewster Date: Sat, 26 Mar 2016 18:23:16 -0600 Subject: [PATCH] webgl: finish, flush, detachShader, generateMipmap, Uniform1i --- components/script/dom/macros.rs | 8 + components/script/dom/webglprogram.rs | 31 ++- .../script/dom/webglrenderingcontext.rs | 72 +++++- components/script/dom/webglshader.rs | 2 +- components/script/dom/webgltexture.rs | 206 ++++++++++++++++++ .../dom/webidls/WebGLRenderingContext.webidl | 11 +- components/servo/Cargo.lock | 2 +- ports/cef/Cargo.lock | 2 +- ports/gonk/Cargo.lock | 2 +- tests/html/rust-power-of-two.png | Bin 0 -> 10043 bytes tests/html/test_webgl_texture_mipmaps.html | 160 ++++++++++++++ tests/wpt/mozilla/meta/MANIFEST.json | 24 ++ .../mozilla/webgl/tex_image_2d_mipmap.html | 116 ++++++++++ .../webgl/tex_image_2d_mipmap_ref.html | 10 + 14 files changed, 634 insertions(+), 12 deletions(-) create mode 100644 tests/html/rust-power-of-two.png create mode 100644 tests/html/test_webgl_texture_mipmaps.html create mode 100644 tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap.html create mode 100644 tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap_ref.html 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(); @@ -331,6 +347,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) { @@ -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) { @@ -949,6 +989,25 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { .unwrap() } + // 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>, @@ -1107,7 +1166,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { internal_format: u32, format: u32, data_type: u32, - source: Option) { + source: Option) { 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>, is_deleted: Cell, + is_initialized: Cell, + /// 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, + base_mipmap_level: u32, #[ignore_heap_size_of = "Defined in ipc-channel"] renderer: IpcSender, } @@ -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 { + 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, + 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 v); - //void uniform1i(WebGLUniformLocation? location, GLint x); + void uniform1i(WebGLUniformLocation? location, GLint x); //void uniform1iv(WebGLUniformLocation? location, Int32Array v); //void uniform1iv(WebGLUniformLocation? location, sequence v); //void uniform2f(WebGLUniformLocation? location, GLfloat x, GLfloat y); @@ -717,4 +717,3 @@ interface WebGLRenderingContext { }; WebGLRenderingContext implements WebGLRenderingContextBase; - diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index fc36769ea0f..7020a5b21fe 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -73,7 +73,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "angle" version = "0.1.0" -source = "git+https://github.com/emilio/angle?branch=servo#ebe29683474ac13a448cc03f772d33179c030cc2" +source = "git+https://github.com/emilio/angle?branch=servo#eefe3506ae13e8ace811ca544fd6b4a5f0db0a04" dependencies = [ "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index cd447b68a3a..1b12c26674a 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -57,7 +57,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "angle" version = "0.1.0" -source = "git+https://github.com/emilio/angle?branch=servo#ebe29683474ac13a448cc03f772d33179c030cc2" +source = "git+https://github.com/emilio/angle?branch=servo#eefe3506ae13e8ace811ca544fd6b4a5f0db0a04" dependencies = [ "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index c26ed50b438..0a2117f8986 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -50,7 +50,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "angle" version = "0.1.0" -source = "git+https://github.com/emilio/angle?branch=servo#ebe29683474ac13a448cc03f772d33179c030cc2" +source = "git+https://github.com/emilio/angle?branch=servo#eefe3506ae13e8ace811ca544fd6b4a5f0db0a04" dependencies = [ "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/tests/html/rust-power-of-two.png b/tests/html/rust-power-of-two.png new file mode 100644 index 0000000000000000000000000000000000000000..b4929ecee01194b786be34e062841f3bc6d498dd GIT binary patch literal 10043 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 literal 0 HcmV?d00001 diff --git a/tests/html/test_webgl_texture_mipmaps.html b/tests/html/test_webgl_texture_mipmaps.html new file mode 100644 index 00000000000..f641ddeceee --- /dev/null +++ b/tests/html/test_webgl_texture_mipmaps.html @@ -0,0 +1,160 @@ + + + + + WebGL Texture Mipmap + + +
+ SEVO +
+ + + + + diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 40b18461303..c1e702f7be8 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -5635,6 +5635,18 @@ "url": "/_mozilla/mozilla/webgl/tex_image_2d_canvas_no_context.html" } ], + "mozilla/webgl/tex_image_2d_mipmap.html": [ + { + "path": "mozilla/webgl/tex_image_2d_mipmap.html", + "references": [ + [ + "/_mozilla/mozilla/webgl/tex_image_2d_simple_ref.html", + "==" + ] + ], + "url": "/_mozilla/mozilla/webgl/tex_image_2d_mipmap.html" + } + ], "mozilla/webgl/tex_image_2d_simple.html": [ { "path": "mozilla/webgl/tex_image_2d_simple.html", @@ -12069,6 +12081,18 @@ "url": "/_mozilla/mozilla/webgl/tex_image_2d_canvas_no_context.html" } ], + "mozilla/webgl/tex_image_2d_mipmap.html": [ + { + "path": "mozilla/webgl/tex_image_2d_mipmap.html", + "references": [ + [ + "/_mozilla/mozilla/webgl/tex_image_2d_simple_ref.html", + "==" + ] + ], + "url": "/_mozilla/mozilla/webgl/tex_image_2d_mipmap.html" + } + ], "mozilla/webgl/tex_image_2d_simple.html": [ { "path": "mozilla/webgl/tex_image_2d_simple.html", diff --git a/tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap.html b/tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap.html new file mode 100644 index 00000000000..ed8779c4252 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap.html @@ -0,0 +1,116 @@ + + + + + WebGL texture test + + + + + + + + diff --git a/tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap_ref.html b/tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap_ref.html new file mode 100644 index 00000000000..5f74c0c923a --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap_ref.html @@ -0,0 +1,10 @@ + + +WebGL texture test + + +