diff options
author | Connor Brewster <brewsterc@my.caspercollege.edu> | 2016-03-26 18:23:16 -0600 |
---|---|---|
committer | Connor Brewster <brewsterc@my.caspercollege.edu> | 2016-04-02 12:31:47 -0600 |
commit | 3fd7634f545871603577d83a08950ab6f7026f1c (patch) | |
tree | f3175b69fc6b945e362547688bd5799c28d19c74 | |
parent | d0f692b2c51bc5c3e7509f850258781f50bdc7b3 (diff) | |
download | servo-3fd7634f545871603577d83a08950ab6f7026f1c.tar.gz servo-3fd7634f545871603577d83a08950ab6f7026f1c.zip |
webgl: finish, flush, detachShader, generateMipmap, Uniform1i
-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 | ||||
-rw-r--r-- | components/servo/Cargo.lock | 2 | ||||
-rw-r--r-- | ports/cef/Cargo.lock | 2 | ||||
-rw-r--r-- | ports/gonk/Cargo.lock | 2 | ||||
-rw-r--r-- | tests/html/rust-power-of-two.png | bin | 0 -> 10043 bytes | |||
-rw-r--r-- | tests/html/test_webgl_texture_mipmaps.html | 160 | ||||
-rw-r--r-- | tests/wpt/mozilla/meta/MANIFEST.json | 24 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap.html | 116 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap_ref.html | 10 |
14 files changed, 634 insertions, 12 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; - 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 Binary files differnew file mode 100644 index 00000000000..b4929ecee01 --- /dev/null +++ b/tests/html/rust-power-of-two.png 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 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8" /> + <title>WebGL Texture Mipmap</title> +</head> +<body> +<div style="text-align: center"> + SE<canvas id="canvas" width="128" height="128"></canvas>VO +</div> +<script id="vertexshader" type="x-shader"> + attribute vec2 aVertexPosition; + attribute vec2 aTextureCoord; + + varying vec2 vTextureCoord; + + uniform float uTime; + + void main() { + vTextureCoord = aTextureCoord; + mat4 rotMat = mat4(sin(uTime), 0.0, 0.0, 0.0, + 0.0, sin(uTime), 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + + gl_Position = rotMat * vec4(aVertexPosition, 0.0, 1.0); + } +</script> +<script id="fragmentshader" type="x-shader"> + precision mediump float; + varying vec2 vTextureCoord; + + uniform sampler2D uSampler; + + void main() { + gl_FragColor = texture2D(uSampler, vTextureCoord); + } +</script> +<script type="text/javascript"> + + var canvas; + function initWebGL() + { + canvas = document.getElementById("canvas"); + var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); + if (!gl) return null; // can't initialize WebGL + return gl; + } + + var gl = initWebGL(); + + // Setup Shaders: + var v = document.getElementById("vertexshader").firstChild.nodeValue; + var f = document.getElementById("fragmentshader").firstChild.nodeValue; + + var vs = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vs, v); + gl.compileShader(vs); + + if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) { + console.log(gl.getShaderInfoLog(vs)); + } + + var fs = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fs, f); + gl.compileShader(fs); + + if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) { + console.log(gl.getShaderInfoLog(fs)); + } + + program = gl.createProgram(); + gl.attachShader(program, vs); + gl.attachShader(program, fs); + gl.linkProgram(program); + + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + console.log(gl.getProgramInfoLog(program)); + } + + gl.useProgram(program); + + program.aVertexPosition = gl.getAttribLocation(program, "aVertexPosition"); + gl.enableVertexAttribArray(program.aVertexPosition); + + program.aTextureCoord = gl.getAttribLocation(program, "aTextureCoord"); + gl.enableVertexAttribArray(program.aTextureCoord); + + var rustTexture = gl.createTexture(); + var rustImage = new Image(); + rustImage.onload = function() { handleTextureLoaded(rustImage, rustTexture); } + rustImage.src = "rust-power-of-two.png"; + + + // Setup Geometry + var vertices = new Float32Array([ + -1.0, -1.0, + -1.0, 1.0, + 1.0, -1.0, + 1.0, 1.0 // Square-Coordinates + ]); + + var textureCoords = new Float32Array([ + 0.0, 0.0, + 0.0, 1.0, + 1.0, 0.0, + 1.0, 1.0 + ]); + + vbuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer); + gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); + + uvbuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, uvbuffer); + gl.bufferData(gl.ARRAY_BUFFER, textureCoords, gl.STATIC_DRAW); + + itemSize = 2; // we have 2 coordinates (x,y) + numItems = vertices.length / itemSize; // number of vertices + + // Viewport + gl.viewport(0, 0, canvas.width, canvas.height); + + program.time = gl.getUniformLocation(program, "uTime"); + + var start_time = new Date().getTime() / 1000; + + setInterval(function () { + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Draw + + gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer); + gl.vertexAttribPointer(program.aVertexPosition, itemSize, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, uvbuffer); + gl.vertexAttribPointer(program.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, rustTexture); + var dt = new Date().getTime() / 1000 - start_time; + gl.uniform1f(program.time, dt); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, numItems); + }, 15); + + function handleTextureLoaded(image, texture) { + console.log("handleTextureLoaded, image = " + image); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); + gl.generateMipmap(gl.TEXTURE_2D); + gl.bindTexture(gl.TEXTURE_2D, null); + } + +</script> +</body> +</html> 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 @@ +<!doctype html> +<html class="reftest-wait"> + <link rel="match" href="tex_image_2d_simple_ref.html"></link> + <meta charset="utf-8"> + <title>WebGL texture test</title> + <!-- + This test should show a 256x256 rust logo + --> + <style> + html, body { margin: 0 } + </style> + <canvas id="c" width="256" height="256"></canvas> + <script id="vertex_shader" type="x-shader/x-vertex"> + precision mediump float; + attribute vec2 a_texCoord; + attribute vec2 a_position; + varying vec2 v_texCoord; + + void main() { + gl_Position = vec4(a_position, 0, 1); + v_texCoord = a_texCoord; + } + </script> + + <script id="fragment_shader" type="x-shader/x-fragment"> + precision mediump float; + uniform sampler2D u_image; + varying vec2 v_texCoord; + void main() { + gl_FragColor = texture2D(u_image, v_texCoord); + } + </script> + <script> + var gl = document.getElementById('c').getContext('webgl'); + + // Clear white + gl.clearColor(1, 1, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Create the program + var vertex_shader = gl.createShader(gl.VERTEX_SHADER), + fragment_shader = gl.createShader(gl.FRAGMENT_SHADER), + program = gl.createProgram(); + + gl.shaderSource(vertex_shader, + document.getElementById('vertex_shader').textContent); + gl.shaderSource(fragment_shader, + document.getElementById('fragment_shader').textContent); + gl.compileShader(vertex_shader); + gl.compileShader(fragment_shader); + gl.attachShader(program, vertex_shader); + gl.attachShader(program, fragment_shader); + console.log(gl.getShaderInfoLog(vertex_shader)); + console.log(gl.getShaderInfoLog(fragment_shader)); + gl.linkProgram(program); + gl.useProgram(program); + + // Get the position from the fragment shader + var position = gl.getAttribLocation(program, "a_position"); + var tex_position = gl.getAttribLocation(program, "a_texCoord"); + + var texture_coordinates = new Float32Array([ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 1.0, 1.0 + ]); + + var texture_buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texture_buffer); + gl.bufferData(gl.ARRAY_BUFFER, texture_coordinates, gl.STATIC_DRAW); + gl.enableVertexAttribArray(tex_position); + gl.vertexAttribPointer(tex_position, 2, gl.FLOAT, false, 0, 0); + + var square_data = new Float32Array([ + -1.0, 1.0, // top left + 1.0, 1.0, // top right + -1.0, -1.0, // bottom left + -1.0, -1.0, // bottom left + 1.0, 1.0, // top right + 1.0, -1.0 // bottom right + ]); + + // Create a buffer for the square with the square + // vertex data + var square_buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, square_buffer); + gl.bufferData(gl.ARRAY_BUFFER, square_data, gl.STATIC_DRAW); + + gl.enableVertexAttribArray(position); + gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0); + + // Load the texture and draw + var image = new Image(); + image.width = image.height = 256; + // Base-64 to allow the reftest to finish + image.src = "img/rust-logo-256x256.png"; + + image.onload = function () { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + console.log(gl.getError() == gl.NO_ERROR); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + gl.generateMipmap(gl.TEXTURE_2D); + + gl.drawArrays(gl.TRIANGLES, 0, 6); + document.documentElement.classList.remove("reftest-wait"); + } + </script> +</html> 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 @@ +<!doctype html> +<meta charset="utf-8"> +<title>WebGL texture test</title> +<!-- + This test should show a 256x256 rust logo +--> +<style> + html, body { margin: 0 } +</style> +<img src="img/rust-logo-256x256.png"> |