webgl: finish, flush, detachShader, generateMipmap, Uniform1i

This commit is contained in:
Connor Brewster 2016-03-26 18:23:16 -06:00
parent d0f692b2c5
commit 3fd7634f54
14 changed files with 634 additions and 12 deletions

View file

@ -266,6 +266,14 @@ macro_rules! make_nonzero_dimension_setter(
/// For use on non-jsmanaged types /// For use on non-jsmanaged types
/// Use #[derive(JSTraceable)] on JS managed types /// Use #[derive(JSTraceable)] on JS managed types
macro_rules! no_jsmanaged_fields( 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),+) => ( ($($ty:ident),+) => (
$( $(
impl $crate::dom::bindings::trace::JSTraceable for $ty { impl $crate::dom::bindings::trace::JSTraceable for $ty {

View file

@ -94,7 +94,10 @@ impl WebGLProgram {
let shader_slot = match shader.gl_type() { let shader_slot = match shader.gl_type() {
constants::FRAGMENT_SHADER => &self.fragment_shader, constants::FRAGMENT_SHADER => &self.fragment_shader,
constants::VERTEX_SHADER => &self.vertex_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 // TODO(emilio): Differentiate between same shader already assigned and other previous
@ -110,6 +113,32 @@ impl WebGLProgram {
Ok(()) 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 /// glBindAttribLocation
pub fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> { pub fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> {
if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN { if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {

View file

@ -181,6 +181,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Root::from_ref(&*self.canvas) 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 // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1
fn DrawingBufferWidth(&self) -> i32 { fn DrawingBufferWidth(&self) -> i32 {
let (sender, receiver) = ipc::channel().unwrap(); 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 // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn BindAttribLocation(&self, program: Option<&WebGLProgram>, fn BindAttribLocation(&self, program: Option<&WebGLProgram>,
index: u32, name: DOMString) { 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)] #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 // 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) { fn BufferData(&self, _cx: *mut JSContext, target: u32, data: Option<*mut JSObject>, usage: u32) {
@ -949,6 +989,25 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
.unwrap() .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 // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn Uniform1fv(&self, fn Uniform1fv(&self,
uniform: Option<&WebGLUniformLocation>, uniform: Option<&WebGLUniformLocation>,
@ -1107,7 +1166,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
internal_format: u32, internal_format: u32,
format: u32, format: u32,
data_type: u32, data_type: u32,
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement >) { source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
let texture = match target { let texture = match target {
constants::TEXTURE_2D => self.bound_texture_2d.get(), constants::TEXTURE_2D => self.bound_texture_2d.get(),
constants::TEXTURE_CUBE_MAP => self.bound_texture_cube_map.get(), constants::TEXTURE_CUBE_MAP => self.bound_texture_cube_map.get(),
@ -1169,11 +1228,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
=> unimplemented!(), => 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 // TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32, let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32,
size.width, size.height, size.width, size.height,
format, data_type, pixels); 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 self.ipc_renderer
.send(CanvasMsg::WebGL(msg)) .send(CanvasMsg::WebGL(msg))
.unwrap() .unwrap()

View file

@ -99,7 +99,7 @@ impl WebGLShader {
let validator = ShaderValidator::for_webgl(self.gl_type, let validator = ShaderValidator::for_webgl(self.gl_type,
SHADER_OUTPUT_FORMAT, SHADER_OUTPUT_FORMAT,
&BuiltInResources::default()).unwrap(); &BuiltInResources::default()).unwrap();
match validator.compile_and_translate(&[source.as_bytes()]) { match validator.compile_and_translate(&[source]) {
Ok(translated_source) => { Ok(translated_source) => {
// NOTE: At this point we should be pretty sure that the compilation in the paint thread // NOTE: At this point we should be pretty sure that the compilation in the paint thread
// will succeed. // will succeed.

View file

@ -4,6 +4,7 @@
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg; use canvas_traits::CanvasMsg;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::codegen::Bindings::WebGLTextureBinding; use dom::bindings::codegen::Bindings::WebGLTextureBinding;
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
@ -12,6 +13,7 @@ use dom::bindings::reflector::reflect_dom_object;
use dom::webglobject::WebGLObject; use dom::webglobject::WebGLObject;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use std::cell::Cell; use std::cell::Cell;
use std::cmp;
use webrender_traits::{WebGLCommand, WebGLError, WebGLResult}; use webrender_traits::{WebGLCommand, WebGLError, WebGLResult};
pub enum TexParameterValue { pub enum TexParameterValue {
@ -19,6 +21,11 @@ pub enum TexParameterValue {
Int(i32), 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] #[dom_struct]
pub struct WebGLTexture { pub struct WebGLTexture {
webgl_object: WebGLObject, webgl_object: WebGLObject,
@ -26,6 +33,13 @@ pub struct WebGLTexture {
/// The target to which this texture was bound the first time /// The target to which this texture was bound the first time
target: Cell<Option<u32>>, target: Cell<Option<u32>>,
is_deleted: Cell<bool>, 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"] #[ignore_heap_size_of = "Defined in ipc-channel"]
renderer: IpcSender<CanvasMsg>, renderer: IpcSender<CanvasMsg>,
} }
@ -37,6 +51,10 @@ impl WebGLTexture {
id: id, id: id,
target: Cell::new(None), target: Cell::new(None),
is_deleted: Cell::new(false), 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, renderer: renderer,
} }
} }
@ -63,11 +81,22 @@ impl WebGLTexture {
// NB: Only valid texture targets come here // NB: Only valid texture targets come here
pub fn bind(&self, target: u32) -> WebGLResult<()> { 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 let Some(previous_target) = self.target.get() {
if target != previous_target { if target != previous_target {
return Err(WebGLError::InvalidOperation); return Err(WebGLError::InvalidOperation);
} }
} else { } 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)); self.target.set(Some(target));
} }
@ -76,6 +105,58 @@ impl WebGLTexture {
Ok(()) 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) { pub fn delete(&self) {
if !self.is_deleted.get() { if !self.is_deleted.get() {
self.is_deleted.set(true); self.is_deleted.set(true);
@ -145,6 +226,84 @@ impl WebGLTexture {
_ => Err(WebGLError::InvalidEnum), _ => 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 { impl Drop for WebGLTexture {
@ -152,3 +311,50 @@ impl Drop for WebGLTexture {
self.delete(); 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
}
}

View file

@ -548,7 +548,7 @@ interface WebGLRenderingContextBase
void depthFunc(GLenum func); void depthFunc(GLenum func);
void depthMask(GLboolean flag); void depthMask(GLboolean flag);
void depthRange(GLclampf zNear, GLclampf zFar); void depthRange(GLclampf zNear, GLclampf zFar);
//void detachShader(WebGLProgram? program, WebGLShader? shader); void detachShader(WebGLProgram? program, WebGLShader? shader);
void disable(GLenum cap); void disable(GLenum cap);
//void disableVertexAttribArray(GLuint index); //void disableVertexAttribArray(GLuint index);
void drawArrays(GLenum mode, GLint first, GLsizei count); void drawArrays(GLenum mode, GLint first, GLsizei count);
@ -556,8 +556,8 @@ interface WebGLRenderingContextBase
void enable(GLenum cap); void enable(GLenum cap);
void enableVertexAttribArray(GLuint index); void enableVertexAttribArray(GLuint index);
//void finish(); void finish();
//void flush(); void flush();
//void framebufferRenderbuffer(GLenum target, GLenum attachment, //void framebufferRenderbuffer(GLenum target, GLenum attachment,
// GLenum renderbuffertarget, // GLenum renderbuffertarget,
// WebGLRenderbuffer? renderbuffer); // WebGLRenderbuffer? renderbuffer);
@ -565,7 +565,7 @@ interface WebGLRenderingContextBase
// WebGLTexture? texture, GLint level); // WebGLTexture? texture, GLint level);
void frontFace(GLenum mode); void frontFace(GLenum mode);
//void generateMipmap(GLenum target); void generateMipmap(GLenum target);
//WebGLActiveInfo? getActiveAttrib(WebGLProgram? program, GLuint index); //WebGLActiveInfo? getActiveAttrib(WebGLProgram? program, GLuint index);
//WebGLActiveInfo? getActiveUniform(WebGLProgram? program, GLuint index); //WebGLActiveInfo? getActiveUniform(WebGLProgram? program, GLuint index);
@ -647,7 +647,7 @@ interface WebGLRenderingContextBase
void uniform1f(WebGLUniformLocation? location, GLfloat x); void uniform1f(WebGLUniformLocation? location, GLfloat x);
//void uniform1fv(WebGLUniformLocation? location, Float32Array v); //void uniform1fv(WebGLUniformLocation? location, Float32Array v);
void uniform1fv(WebGLUniformLocation? location, sequence<GLfloat> 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, Int32Array v);
//void uniform1iv(WebGLUniformLocation? location, sequence<long> v); //void uniform1iv(WebGLUniformLocation? location, sequence<long> v);
//void uniform2f(WebGLUniformLocation? location, GLfloat x, GLfloat y); //void uniform2f(WebGLUniformLocation? location, GLfloat x, GLfloat y);
@ -717,4 +717,3 @@ interface WebGLRenderingContext
{ {
}; };
WebGLRenderingContext implements WebGLRenderingContextBase; WebGLRenderingContext implements WebGLRenderingContextBase;

View file

@ -73,7 +73,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "angle" name = "angle"
version = "0.1.0" 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 = [ dependencies = [
"libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]

2
ports/cef/Cargo.lock generated
View file

@ -57,7 +57,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "angle" name = "angle"
version = "0.1.0" 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 = [ dependencies = [
"libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]

2
ports/gonk/Cargo.lock generated
View file

@ -50,7 +50,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "angle" name = "angle"
version = "0.1.0" 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 = [ dependencies = [
"libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

@ -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>

View file

@ -5635,6 +5635,18 @@
"url": "/_mozilla/mozilla/webgl/tex_image_2d_canvas_no_context.html" "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": [ "mozilla/webgl/tex_image_2d_simple.html": [
{ {
"path": "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" "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": [ "mozilla/webgl/tex_image_2d_simple.html": [
{ {
"path": "mozilla/webgl/tex_image_2d_simple.html", "path": "mozilla/webgl/tex_image_2d_simple.html",

View file

@ -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>

View file

@ -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">