aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorConnor Brewster <brewsterc@my.caspercollege.edu>2016-03-26 18:23:16 -0600
committerConnor Brewster <brewsterc@my.caspercollege.edu>2016-04-02 12:31:47 -0600
commit3fd7634f545871603577d83a08950ab6f7026f1c (patch)
treef3175b69fc6b945e362547688bd5799c28d19c74
parentd0f692b2c51bc5c3e7509f850258781f50bdc7b3 (diff)
downloadservo-3fd7634f545871603577d83a08950ab6f7026f1c.tar.gz
servo-3fd7634f545871603577d83a08950ab6f7026f1c.zip
webgl: finish, flush, detachShader, generateMipmap, Uniform1i
-rw-r--r--components/script/dom/macros.rs8
-rw-r--r--components/script/dom/webglprogram.rs31
-rw-r--r--components/script/dom/webglrenderingcontext.rs72
-rw-r--r--components/script/dom/webglshader.rs2
-rw-r--r--components/script/dom/webgltexture.rs206
-rw-r--r--components/script/dom/webidls/WebGLRenderingContext.webidl11
-rw-r--r--components/servo/Cargo.lock2
-rw-r--r--ports/cef/Cargo.lock2
-rw-r--r--ports/gonk/Cargo.lock2
-rw-r--r--tests/html/rust-power-of-two.pngbin0 -> 10043 bytes
-rw-r--r--tests/html/test_webgl_texture_mipmaps.html160
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json24
-rw-r--r--tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap.html116
-rw-r--r--tests/wpt/mozilla/tests/mozilla/webgl/tex_image_2d_mipmap_ref.html10
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
new file mode 100644
index 00000000000..b4929ecee01
--- /dev/null
+++ b/tests/html/rust-power-of-two.png
Binary files differ
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">