aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2016-04-22 11:25:29 -0700
committerbors-servo <lbergstrom+bors@mozilla.com>2016-04-22 11:25:29 -0700
commitf1defb446e8cd4e36231acae77f11c72c74964b0 (patch)
treefdf0d92dbc5d74c5c2efad7ebfaa504408a68489 /components/script/dom
parentd926b5d3762a101d5280266f806f2b979f73b695 (diff)
parent9fff9d45d1108fcb2a594feb091e0f64d2cab7e4 (diff)
downloadservo-f1defb446e8cd4e36231acae77f11c72c74964b0.tar.gz
servo-f1defb446e8cd4e36231acae77f11c72c74964b0.zip
Auto merge of #10443 - emilio:webgl-teximage2d-overload, r=jdm
webgl: Implement the pending texImage2D overload, and add more validation This is a large-ish refactor of the Texture2D code, but it should be easier to read and of course more correct. I tried to annotate every error condition with a spec paragraph. I made just a reftest to ensure this works as intended, since I expect #10373 to land pretty soon. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10443) <!-- Reviewable:end -->
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/webglprogram.rs22
-rw-r--r--components/script/dom/webglrenderingcontext.rs496
-rw-r--r--components/script/dom/webglshader.rs5
-rw-r--r--components/script/dom/webgltexture.rs39
-rw-r--r--components/script/dom/webgluniformlocation.rs3
-rw-r--r--components/script/dom/webidls/WebGLRenderingContext.webidl4
6 files changed, 451 insertions, 118 deletions
diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs
index 0458e2ae2ca..6cc122cf459 100644
--- a/components/script/dom/webglprogram.rs
+++ b/components/script/dom/webglprogram.rs
@@ -23,6 +23,7 @@ pub struct WebGLProgram {
webgl_object: WebGLObject,
id: u32,
is_deleted: Cell<bool>,
+ linked: Cell<bool>,
fragment_shader: MutNullableHeap<JS<WebGLShader>>,
vertex_shader: MutNullableHeap<JS<WebGLShader>>,
#[ignore_heap_size_of = "Defined in ipc-channel"]
@@ -35,6 +36,7 @@ impl WebGLProgram {
webgl_object: WebGLObject::new_inherited(),
id: id,
is_deleted: Cell::new(false),
+ linked: Cell::new(false),
fragment_shader: Default::default(),
vertex_shader: Default::default(),
renderer: renderer,
@@ -71,19 +73,27 @@ impl WebGLProgram {
/// glLinkProgram
pub fn link(&self) {
- self.renderer.send(CanvasMsg::WebGL(WebGLCommand::LinkProgram(self.id))).unwrap();
- }
+ self.linked.set(false);
- /// glUseProgram
- pub fn use_program(&self) -> WebGLResult<()> {
match self.fragment_shader.get() {
Some(ref shader) if shader.successfully_compiled() => {},
- _ => return Err(WebGLError::InvalidOperation),
+ _ => return,
}
match self.vertex_shader.get() {
Some(ref shader) if shader.successfully_compiled() => {},
- _ => return Err(WebGLError::InvalidOperation),
+ _ => return,
+ }
+
+ self.linked.set(true);
+
+ self.renderer.send(CanvasMsg::WebGL(WebGLCommand::LinkProgram(self.id))).unwrap();
+ }
+
+ /// glUseProgram
+ pub fn use_program(&self) -> WebGLResult<()> {
+ if !self.linked.get() {
+ return Err(WebGLError::InvalidOperation);
}
self.renderer.send(CanvasMsg::WebGL(WebGLCommand::UseProgram(self.id))).unwrap();
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs
index 5f1b0589af4..bd2f8191720 100644
--- a/components/script/dom/webglrenderingcontext.rs
+++ b/components/script/dom/webglrenderingcontext.rs
@@ -3,12 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use canvas_traits::{CanvasCommonMsg, CanvasMsg};
-use dom::bindings::codegen::Bindings::WebGLActiveInfoBinding::WebGLActiveInfoMethods;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{WebGLRenderingContextMethods};
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes};
use dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
-use dom::bindings::conversions::{ToJSValConvertible, array_buffer_view_to_vec_checked, array_buffer_view_to_vec};
+use dom::bindings::conversions::{ToJSValConvertible, array_buffer_view_data_checked};
+use dom::bindings::conversions::{array_buffer_view_to_vec_checked, array_buffer_view_to_vec};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
@@ -67,45 +67,6 @@ bitflags! {
}
}
-pub enum UniformType {
- Int,
- IntVec2,
- IntVec3,
- IntVec4,
- Float,
- FloatVec2,
- FloatVec3,
- FloatVec4,
-}
-
-impl UniformType {
- fn element_count(&self) -> usize {
- match *self {
- UniformType::Int => 1,
- UniformType::IntVec2 => 2,
- UniformType::IntVec3 => 3,
- UniformType::IntVec4 => 4,
- UniformType::Float => 1,
- UniformType::FloatVec2 => 2,
- UniformType::FloatVec3 => 3,
- UniformType::FloatVec4 => 4,
- }
- }
-
- fn as_gl_constant(&self) -> u32 {
- match *self {
- UniformType::Int => constants::INT,
- UniformType::IntVec2 => constants::INT_VEC2,
- UniformType::IntVec3 => constants::INT_VEC3,
- UniformType::IntVec4 => constants::INT_VEC4,
- UniformType::Float => constants::FLOAT,
- UniformType::FloatVec2 => constants::FLOAT_VEC2,
- UniformType::FloatVec3 => constants::FLOAT_VEC3,
- UniformType::FloatVec4 => constants::FLOAT_VEC4,
- }
- }
-}
-
#[dom_struct]
pub struct WebGLRenderingContext {
reflector_: Reflector,
@@ -224,7 +185,7 @@ impl WebGLRenderingContext {
// https://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf#nameddest=section-2.10.4
fn validate_uniform_parameters<T>(&self,
uniform: Option<&WebGLUniformLocation>,
- type_: UniformType,
+ uniform_type: UniformSetterType,
data: Option<&[T]>) -> bool {
let uniform = match uniform {
Some(uniform) => uniform,
@@ -232,8 +193,8 @@ impl WebGLRenderingContext {
};
let program = self.current_program.get();
- let program = match program {
- Some(ref program) if program.id() == uniform.program_id() => program,
+ match program {
+ Some(ref program) if program.id() == uniform.program_id() => {},
_ => {
self.webgl_error(InvalidOperation);
return false;
@@ -248,28 +209,200 @@ impl WebGLRenderingContext {
},
};
- // TODO(autrilla): Don't request this every time, cache it
- let active_uniform = match program.get_active_uniform(
- uniform.id() as u32) {
- Ok(active_uniform) => active_uniform,
- Err(_) => {
- self.webgl_error(InvalidOperation);
+ // TODO(emilio): Get more complex uniform info from ANGLE, and use it to
+ // properly validate that the uniform setter type is compatible with the
+ // uniform type, and that the uniform size matches.
+ if data.len() % uniform_type.element_count() != 0 {
+ self.webgl_error(InvalidOperation);
+ return false;
+ }
+
+ true
+ }
+
+ fn validate_tex_image_parameters(&self,
+ target: u32,
+ level: i32,
+ internal_format: u32,
+ width: i32,
+ height: i32,
+ border: i32,
+ format: u32,
+ data_type: u32) -> bool {
+ // GL_INVALID_ENUM is generated if target is not GL_TEXTURE_2D,
+ // GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ // GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ // GL_TEXTURE_CUBE_MAP_POSITIVE_Z, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.
+ let texture = match target {
+ constants::TEXTURE_2D
+ => self.bound_texture_2d.get(),
+ constants::TEXTURE_CUBE_MAP_POSITIVE_X |
+ constants::TEXTURE_CUBE_MAP_NEGATIVE_X |
+ constants::TEXTURE_CUBE_MAP_POSITIVE_Y |
+ constants::TEXTURE_CUBE_MAP_NEGATIVE_Y |
+ constants::TEXTURE_CUBE_MAP_POSITIVE_Z |
+ constants::TEXTURE_CUBE_MAP_NEGATIVE_Z
+ => self.bound_texture_cube_map.get(),
+ _ => {
+ self.webgl_error(InvalidEnum);
return false;
},
};
- if data.len() % type_.element_count() != 0 ||
- (data.len() / type_.element_count() > active_uniform.Size() as usize) {
- self.webgl_error(InvalidOperation);
+ // If an attempt is made to call this function with no
+ // WebGLTexture bound, an INVALID_OPERATION error is generated.
+ if texture.is_none() {
+ self.webgl_error(InvalidOperation);
+ return false;
+ }
+
+ // GL_INVALID_ENUM is generated if data_type is not an accepted value.
+ match data_type {
+ constants::UNSIGNED_BYTE |
+ constants::UNSIGNED_SHORT_4_4_4_4 |
+ constants::UNSIGNED_SHORT_5_5_5_1 |
+ constants::UNSIGNED_SHORT_5_6_5 => {},
+ _ => {
+ self.webgl_error(InvalidEnum);
return false;
+ },
+ }
+
+
+ // TODO(emilio): GL_INVALID_VALUE may be generated if
+ // level is greater than log_2(max), where max is
+ // the returned value of GL_MAX_TEXTURE_SIZE when
+ // target is GL_TEXTURE_2D or GL_MAX_CUBE_MAP_TEXTURE_SIZE
+ // when target is not GL_TEXTURE_2D.
+ let is_cubic = target != constants::TEXTURE_2D;
+
+ // GL_INVALID_VALUE is generated if target is one of the
+ // six cube map 2D image targets and the width and height
+ // parameters are not equal.
+ if is_cubic && width != height {
+ self.webgl_error(InvalidValue);
+ return false;
}
- if type_.as_gl_constant() != active_uniform.Type() {
+ // GL_INVALID_VALUE is generated if internal_format is not an
+ // accepted format.
+ match internal_format {
+ constants::DEPTH_COMPONENT |
+ constants::ALPHA |
+ constants::RGB |
+ constants::RGBA |
+ constants::LUMINANCE |
+ constants::LUMINANCE_ALPHA => {},
+
+ _ => {
+ self.webgl_error(InvalidValue);
+ return false;
+ },
+ }
+
+ // GL_INVALID_OPERATION is generated if format does not
+ // match internal_format.
+ if format != internal_format {
self.webgl_error(InvalidOperation);
return false;
}
- return true;
+ // GL_INVALID_VALUE is generated if level is less than 0.
+ //
+ // GL_INVALID_VALUE is generated if width or height is less than 0
+ // or greater than GL_MAX_TEXTURE_SIZE when target is GL_TEXTURE_2D or
+ // GL_MAX_CUBE_MAP_TEXTURE_SIZE when target is not GL_TEXTURE_2D.
+ //
+ // TODO(emilio): Check limits
+ if width < 0 || height < 0 || level < 0 {
+ self.webgl_error(InvalidValue);
+ return false;
+ }
+
+ // GL_INVALID_VALUE is generated if level is greater than zero and the
+ // texture is not power of two.
+ if level > 0 &&
+ (!(width as u32).is_power_of_two() ||
+ !(height as u32).is_power_of_two()) {
+ self.webgl_error(InvalidValue);
+ return false;
+ }
+
+ // GL_INVALID_VALUE is generated if border is not 0.
+ if border != 0 {
+ self.webgl_error(InvalidValue);
+ return false;
+ }
+
+ // GL_INVALID_OPERATION is generated if type is GL_UNSIGNED_SHORT_4_4_4_4 or
+ // GL_UNSIGNED_SHORT_5_5_5_1 and format is not GL_RGBA.
+ //
+ // GL_INVALID_OPERATION is generated if type is
+ // GL_UNSIGNED_SHORT_5_6_5 and format is not GL_RGB.
+ match data_type {
+ constants::UNSIGNED_SHORT_4_4_4_4 |
+ constants::UNSIGNED_SHORT_5_5_5_1 if format != constants::RGBA => {
+ self.webgl_error(InvalidOperation);
+ return false;
+ },
+ constants::UNSIGNED_SHORT_5_6_5 if format != constants::RGB => {
+ self.webgl_error(InvalidOperation);
+ return false;
+ },
+ _ => {},
+ }
+
+ true
+ }
+
+ fn tex_image_2d(&self,
+ target: u32,
+ level: i32,
+ internal_format: u32,
+ width: i32,
+ height: i32,
+ border: i32,
+ format: u32,
+ data_type: u32,
+ pixels: Vec<u8>) { // NB: pixels should NOT be premultipied
+ // This should be validated before reaching this function
+ debug_assert!(self.validate_tex_image_parameters(target, level,
+ internal_format,
+ width, height,
+ border, format,
+ data_type));
+
+ let slot = match target {
+ constants::TEXTURE_2D
+ => self.bound_texture_2d.get(),
+ _ => self.bound_texture_cube_map.get(),
+ };
+
+ let texture = slot.as_ref().expect("No bound texture found after validation");
+
+ if format == constants::RGBA &&
+ data_type == constants::UNSIGNED_BYTE &&
+ self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA) {
+ // TODO(emilio): premultiply here.
+ }
+
+ // TODO(emilio): Flip Y axis if necessary here
+
+ // TexImage2D depth is always equal to 1
+ handle_potential_webgl_error!(self, texture.initialize(target,
+ width as u32,
+ height as u32, 1,
+ internal_format,
+ level as u32));
+
+
+ // TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
+ let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32,
+ width, height, format, data_type, pixels);
+
+ self.ipc_renderer
+ .send(CanvasMsg::WebGL(msg))
+ .unwrap()
}
}
@@ -483,6 +616,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Err(e) => return self.webgl_error(e),
}
} else {
+ slot.set(None);
// Unbind the current buffer
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::BindBuffer(target, 0)))
@@ -526,7 +660,6 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
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),
};
@@ -566,20 +699,24 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
constants::ELEMENT_ARRAY_BUFFER => self.bound_buffer_element_array.get(),
_ => return self.webgl_error(InvalidEnum),
};
+
let bound_buffer = match bound_buffer {
Some(bound_buffer) => bound_buffer,
None => return self.webgl_error(InvalidValue),
};
+
match usage {
constants::STREAM_DRAW |
constants::STATIC_DRAW |
constants::DYNAMIC_DRAW => (),
_ => return self.webgl_error(InvalidEnum),
}
+
let data = match data {
Some(data) => data,
None => return self.webgl_error(InvalidValue),
};
+
if let Some(data_vec) = array_buffer_view_to_vec::<u8>(data) {
handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data_vec, usage));
} else {
@@ -1109,7 +1246,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn Uniform1f(&self,
uniform: Option<&WebGLUniformLocation>,
val: f32) {
- if self.validate_uniform_parameters(uniform, UniformType::Float, Some(&[val])) {
+ if self.validate_uniform_parameters(uniform, UniformSetterType::Float, Some(&[val])) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform1f(uniform.unwrap().id(), val)))
.unwrap()
@@ -1120,7 +1257,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn Uniform1i(&self,
uniform: Option<&WebGLUniformLocation>,
val: i32) {
- if self.validate_uniform_parameters(uniform, UniformType::Int, Some(&[val])) {
+ if self.validate_uniform_parameters(uniform, UniformSetterType::Int, Some(&[val])) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform1i(uniform.unwrap().id(), val)))
.unwrap()
@@ -1133,7 +1270,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
uniform: Option<&WebGLUniformLocation>,
data: Option<*mut JSObject>) {
let data_vec = data.and_then(|d| array_buffer_view_to_vec::<i32>(d));
- if self.validate_uniform_parameters(uniform, UniformType::Int, data_vec.as_ref().map(Vec::as_slice)) {
+ if self.validate_uniform_parameters(uniform, UniformSetterType::Int, data_vec.as_ref().map(Vec::as_slice)) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform1iv(uniform.unwrap().id(), data_vec.unwrap())))
.unwrap()
@@ -1146,7 +1283,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
uniform: Option<&WebGLUniformLocation>,
data: Option<*mut JSObject>) {
let data_vec = data.and_then(|d| array_buffer_view_to_vec::<f32>(d));
- if self.validate_uniform_parameters(uniform, UniformType::Float, data_vec.as_ref().map(Vec::as_slice)) {
+ if self.validate_uniform_parameters(uniform, UniformSetterType::Float, data_vec.as_ref().map(Vec::as_slice)) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform1fv(uniform.unwrap().id(), data_vec.unwrap())))
.unwrap()
@@ -1157,7 +1294,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn Uniform2f(&self,
uniform: Option<&WebGLUniformLocation>,
x: f32, y: f32) {
- if self.validate_uniform_parameters(uniform, UniformType::FloatVec2, Some(&[x, y])) {
+ if self.validate_uniform_parameters(uniform, UniformSetterType::FloatVec2, Some(&[x, y])) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform2f(uniform.unwrap().id(), x, y)))
.unwrap()
@@ -1170,7 +1307,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
uniform: Option<&WebGLUniformLocation>,
data: Option<*mut JSObject>) {
let data_vec = data.and_then(|d| array_buffer_view_to_vec::<f32>(d));
- if self.validate_uniform_parameters(uniform, UniformType::FloatVec2, data_vec.as_ref().map(Vec::as_slice)) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::FloatVec2,
+ data_vec.as_ref().map(Vec::as_slice)) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform2fv(uniform.unwrap().id(), data_vec.unwrap())))
.unwrap()
@@ -1181,7 +1320,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn Uniform2i(&self,
uniform: Option<&WebGLUniformLocation>,
x: i32, y: i32) {
- if self.validate_uniform_parameters(uniform, UniformType::IntVec2, Some(&[x, y])) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::IntVec2,
+ Some(&[x, y])) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform2i(uniform.unwrap().id(), x, y)))
.unwrap()
@@ -1194,7 +1335,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
uniform: Option<&WebGLUniformLocation>,
data: Option<*mut JSObject>) {
let data_vec = data.and_then(|d| array_buffer_view_to_vec::<i32>(d));
- if self.validate_uniform_parameters(uniform, UniformType::IntVec2, data_vec.as_ref().map(Vec::as_slice)) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::IntVec2,
+ data_vec.as_ref().map(Vec::as_slice)) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform2iv(uniform.unwrap().id(), data_vec.unwrap())))
.unwrap()
@@ -1205,7 +1348,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn Uniform3f(&self,
uniform: Option<&WebGLUniformLocation>,
x: f32, y: f32, z: f32) {
- if self.validate_uniform_parameters(uniform, UniformType::FloatVec3, Some(&[x, y, z])) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::FloatVec3,
+ Some(&[x, y, z])) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform3f(uniform.unwrap().id(), x, y, z)))
.unwrap()
@@ -1218,7 +1363,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
uniform: Option<&WebGLUniformLocation>,
data: Option<*mut JSObject>) {
let data_vec = data.and_then(|d| array_buffer_view_to_vec::<f32>(d));
- if self.validate_uniform_parameters(uniform, UniformType::FloatVec3, data_vec.as_ref().map(Vec::as_slice)) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::FloatVec3,
+ data_vec.as_ref().map(Vec::as_slice)) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform3fv(uniform.unwrap().id(), data_vec.unwrap())))
.unwrap()
@@ -1229,7 +1376,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn Uniform3i(&self,
uniform: Option<&WebGLUniformLocation>,
x: i32, y: i32, z: i32) {
- if self.validate_uniform_parameters(uniform, UniformType::IntVec3, Some(&[x, y, z])) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::IntVec3,
+ Some(&[x, y, z])) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform3i(uniform.unwrap().id(), x, y, z)))
.unwrap()
@@ -1242,7 +1391,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
uniform: Option<&WebGLUniformLocation>,
data: Option<*mut JSObject>) {
let data_vec = data.and_then(|d| array_buffer_view_to_vec::<i32>(d));
- if self.validate_uniform_parameters(uniform, UniformType::IntVec3, data_vec.as_ref().map(Vec::as_slice)) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::IntVec3,
+ data_vec.as_ref().map(Vec::as_slice)) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform3iv(uniform.unwrap().id(), data_vec.unwrap())))
.unwrap()
@@ -1253,7 +1404,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn Uniform4i(&self,
uniform: Option<&WebGLUniformLocation>,
x: i32, y: i32, z: i32, w: i32) {
- if self.validate_uniform_parameters(uniform, UniformType::IntVec4, Some(&[x, y, z, w])) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::IntVec4,
+ Some(&[x, y, z, w])) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform4i(uniform.unwrap().id(), x, y, z, w)))
.unwrap()
@@ -1267,7 +1420,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
uniform: Option<&WebGLUniformLocation>,
data: Option<*mut JSObject>) {
let data_vec = data.and_then(|d| array_buffer_view_to_vec::<i32>(d));
- if self.validate_uniform_parameters(uniform, UniformType::IntVec4, data_vec.as_ref().map(Vec::as_slice)) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::IntVec4,
+ data_vec.as_ref().map(Vec::as_slice)) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform4iv(uniform.unwrap().id(), data_vec.unwrap())))
.unwrap()
@@ -1278,7 +1433,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn Uniform4f(&self,
uniform: Option<&WebGLUniformLocation>,
x: f32, y: f32, z: f32, w: f32) {
- if self.validate_uniform_parameters(uniform, UniformType::FloatVec4, Some(&[x, y, z, w])) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::FloatVec4,
+ Some(&[x, y, z, w])) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform4f(uniform.unwrap().id(), x, y, z, w)))
.unwrap()
@@ -1291,7 +1448,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
uniform: Option<&WebGLUniformLocation>,
data: Option<*mut JSObject>) {
let data_vec = data.and_then(|d| array_buffer_view_to_vec::<f32>(d));
- if self.validate_uniform_parameters(uniform, UniformType::FloatVec4, data_vec.as_ref().map(Vec::as_slice)) {
+ if self.validate_uniform_parameters(uniform,
+ UniformSetterType::FloatVec4,
+ data_vec.as_ref().map(Vec::as_slice)) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform4fv(uniform.unwrap().id(), data_vec.unwrap())))
.unwrap()
@@ -1400,28 +1559,112 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
.unwrap()
}
+ #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
fn TexImage2D(&self,
+ _cx: *mut JSContext,
target: u32,
level: i32,
internal_format: u32,
+ width: i32,
+ height: i32,
+ border: i32,
format: u32,
data_type: u32,
- 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(),
- _ => return self.webgl_error(InvalidEnum),
+ data: Option<*mut JSObject>) {
+ if !self.validate_tex_image_parameters(target,
+ level,
+ internal_format,
+ width, height,
+ border,
+ format,
+ data_type) {
+ return; // Error handled in validate()
+ }
+
+ // TODO(emilio, #10693): Add type-safe wrappers to validations
+ let (element_size, components_per_element) = match data_type {
+ constants::UNSIGNED_BYTE => (1, 1),
+ constants::UNSIGNED_SHORT_5_6_5 => (2, 3),
+ constants::UNSIGNED_SHORT_5_5_5_1 |
+ constants::UNSIGNED_SHORT_4_4_4_4 => (2, 4),
+ _ => unreachable!(), // previously validated
};
- if texture.is_none() {
+
+ let components = match format {
+ constants::DEPTH_COMPONENT => 1,
+ constants::ALPHA => 1,
+ constants::LUMINANCE => 1,
+ constants::LUMINANCE_ALPHA => 2,
+ constants::RGB => 3,
+ constants::RGBA => 4,
+ _ => unreachable!(), // previously validated
+ };
+
+ // If data is non-null, the type of pixels must match the type of the
+ // data to be read.
+ // If it is UNSIGNED_BYTE, a Uint8Array must be supplied;
+ // if it is UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4,
+ // or UNSIGNED_SHORT_5_5_5_1, a Uint16Array must be supplied.
+ // If the types do not match, an INVALID_OPERATION error is generated.
+ let received_size = if let Some(data) = data {
+ if unsafe { array_buffer_view_data_checked::<u16>(data).is_some() } {
+ 2
+ } else if unsafe { array_buffer_view_data_checked::<u8>(data).is_some() } {
+ 1
+ } else {
+ return self.webgl_error(InvalidOperation);
+ }
+ } else {
+ element_size
+ };
+
+ if received_size != element_size {
return self.webgl_error(InvalidOperation);
}
- // TODO(emilio): Validate more parameters
+
+ // NOTE: width and height are positive or zero due to validate()
+ let expected_byte_length = width * height * element_size * components / components_per_element;
+
+
+ // If data is null, a buffer of sufficient size
+ // initialized to 0 is passed.
+ let buff = if let Some(data) = data {
+ array_buffer_view_to_vec::<u8>(data)
+ .expect("Can't reach here without being an ArrayBufferView!")
+ } else {
+ vec![0u8; expected_byte_length as usize]
+ };
+
+ if buff.len() != expected_byte_length as usize {
+ return self.webgl_error(InvalidOperation);
+ }
+
+ self.tex_image_2d(target, level,
+ internal_format,
+ width, height, border,
+ format, data_type, buff)
+ }
+
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
+ fn TexImage2D_(&self,
+ target: u32,
+ level: i32,
+ internal_format: u32,
+ format: u32,
+ data_type: u32,
+ source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
let source = match source {
Some(s) => s,
None => return,
};
+
+ // NOTE: Getting the pixels probably can be short-circuited if some
+ // parameter is invalid.
+ //
+ // Nontheless, since it's the error case, I'm not totally sure the
+ // complexity is worth it.
let (pixels, size) = match source {
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => {
let global = self.global();
@@ -1443,7 +1686,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
};
let size = Size2D::new(img.width as i32, img.height as i32);
- // TODO(emilio): Validate that the format argument is coherent with the image.
+
+ // TODO(emilio): Validate that the format argument
+ // is coherent with the image.
+ //
// RGB8 should be easy to support too
let mut data = match img.format {
PixelFormat::RGBA8 => img.bytes.to_vec(),
@@ -1454,8 +1700,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
(data, size)
},
- // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D, but
- // we need to refactor it moving it to `HTMLCanvasElement` and supporting WebGLContext
+ // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D,
+ // but we need to refactor it moving it to `HTMLCanvasElement` and support
+ // WebGLContext (probably via GetPixels()).
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLCanvasElement(canvas) => {
let canvas = canvas.r();
if let Some((mut data, size)) = canvas.fetch_all_data() {
@@ -1469,25 +1716,17 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
=> unimplemented!(),
};
- if size.width < 0 || size.height < 0 || level < 0 {
- self.webgl_error(WebGLError::InvalidOperation);
+ // NB: Border must be zero
+ if !self.validate_tex_image_parameters(target, level, internal_format,
+ size.width, size.height, 0,
+ format, data_type) {
+ return; // Error handled in validate()
}
- // 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()
+ self.tex_image_2d(target, level,
+ internal_format,
+ size.width, size.height, 0,
+ format, data_type, pixels)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
@@ -1512,3 +1751,62 @@ impl LayoutCanvasWebGLRenderingContextHelpers for LayoutJS<WebGLRenderingContext
(*self.unsafe_get()).ipc_renderer.clone()
}
}
+
+#[derive(Debug, PartialEq)]
+pub enum UniformSetterType {
+ Int,
+ IntVec2,
+ IntVec3,
+ IntVec4,
+ Float,
+ FloatVec2,
+ FloatVec3,
+ FloatVec4,
+}
+
+impl UniformSetterType {
+ pub fn element_count(&self) -> usize {
+ match *self {
+ UniformSetterType::Int => 1,
+ UniformSetterType::IntVec2 => 2,
+ UniformSetterType::IntVec3 => 3,
+ UniformSetterType::IntVec4 => 4,
+ UniformSetterType::Float => 1,
+ UniformSetterType::FloatVec2 => 2,
+ UniformSetterType::FloatVec3 => 3,
+ UniformSetterType::FloatVec4 => 4,
+ }
+ }
+
+ pub fn is_compatible_with(&self, gl_type: u32) -> bool {
+ gl_type == self.as_gl_constant() || match *self {
+ // Sampler uniform variables have an index value (the index of the
+ // texture), and as such they have to be set as ints
+ UniformSetterType::Int => gl_type == constants::SAMPLER_2D ||
+ gl_type == constants::SAMPLER_CUBE,
+ // Don't ask me why, but it seems we must allow setting bool
+ // uniforms with uniform1f.
+ //
+ // See the WebGL conformance test
+ // conformance/uniforms/gl-uniform-bool.html
+ UniformSetterType::Float => gl_type == constants::BOOL,
+ UniformSetterType::FloatVec2 => gl_type == constants::BOOL_VEC2,
+ UniformSetterType::FloatVec3 => gl_type == constants::BOOL_VEC3,
+ UniformSetterType::FloatVec4 => gl_type == constants::BOOL_VEC4,
+ _ => false,
+ }
+ }
+
+ fn as_gl_constant(&self) -> u32 {
+ match *self {
+ UniformSetterType::Int => constants::INT,
+ UniformSetterType::IntVec2 => constants::INT_VEC2,
+ UniformSetterType::IntVec3 => constants::INT_VEC3,
+ UniformSetterType::IntVec4 => constants::INT_VEC4,
+ UniformSetterType::Float => constants::FLOAT,
+ UniformSetterType::FloatVec2 => constants::FLOAT_VEC2,
+ UniformSetterType::FloatVec3 => constants::FLOAT_VEC3,
+ UniformSetterType::FloatVec4 => constants::FLOAT_VEC4,
+ }
+ }
+}
diff --git a/components/script/dom/webglshader.rs b/components/script/dom/webglshader.rs
index 495417065d7..eb930a69f13 100644
--- a/components/script/dom/webglshader.rs
+++ b/components/script/dom/webglshader.rs
@@ -116,6 +116,11 @@ impl WebGLShader {
}
*self.info_log.borrow_mut() = Some(validator.info_log());
+ // TODO(emilio): More data (like uniform data) should be collected
+ // here to properly validate uniforms.
+ //
+ // This requires a more complex interface with ANGLE, using C++
+ // bindings and being extremely cautious about destructing things.
}
}
diff --git a/components/script/dom/webgltexture.rs b/components/script/dom/webgltexture.rs
index 5bb7d5ff734..e92435ce304 100644
--- a/components/script/dom/webgltexture.rs
+++ b/components/script/dom/webgltexture.rs
@@ -33,7 +33,6 @@ 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]>,
@@ -51,7 +50,6 @@ 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]),
@@ -105,7 +103,13 @@ impl WebGLTexture {
Ok(())
}
- pub fn initialize(&self, width: u32, height: u32, depth: u32, internal_format: u32, level: u32) -> WebGLResult<()> {
+ pub fn initialize(&self,
+ target: u32,
+ width: u32,
+ height: u32,
+ depth: u32,
+ internal_format: u32,
+ level: u32) -> WebGLResult<()> {
let image_info = ImageInfo {
width: width,
height: height,
@@ -113,10 +117,18 @@ impl WebGLTexture {
internal_format: Some(internal_format),
is_initialized: true,
};
- self.set_image_infos_at_level(level, image_info);
- self.is_initialized.set(true);
+ let face = match target {
+ constants::TEXTURE_2D | constants::TEXTURE_CUBE_MAP_POSITIVE_X => 0,
+ constants::TEXTURE_CUBE_MAP_NEGATIVE_X => 1,
+ constants::TEXTURE_CUBE_MAP_POSITIVE_Y => 2,
+ constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => 3,
+ constants::TEXTURE_CUBE_MAP_POSITIVE_Z => 4,
+ constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => 5,
+ _ => unreachable!(),
+ };
+ self.set_image_infos_at_level_and_face(level, face, image_info);
Ok(())
}
@@ -130,12 +142,12 @@ impl WebGLTexture {
};
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() {
+ let is_cubic = target == constants::TEXTURE_CUBE_MAP;
+ if is_cubic && !self.is_cube_complete() {
return Err(WebGLError::InvalidOperation);
}
@@ -262,6 +274,8 @@ impl WebGLTexture {
}
fn is_cube_complete(&self) -> bool {
+ debug_assert!(self.face_count.get() == 6);
+
let image_info = self.base_image_info().unwrap();
if !image_info.is_defined() {
return false;
@@ -294,11 +308,16 @@ impl WebGLTexture {
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;
+ self.set_image_infos_at_level_and_face(level, face, image_info);
}
}
+ fn set_image_infos_at_level_and_face(&self, level: u32, face: u8, image_info: ImageInfo) {
+ debug_assert!(face < 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);
@@ -341,7 +360,7 @@ impl ImageInfo {
}
fn is_defined(&self) -> bool {
- !self.internal_format.is_none()
+ self.internal_format.is_some()
}
fn get_max_mimap_levels(&self) -> u32 {
diff --git a/components/script/dom/webgluniformlocation.rs b/components/script/dom/webgluniformlocation.rs
index 43244ec4a33..2f8e3331cd2 100644
--- a/components/script/dom/webgluniformlocation.rs
+++ b/components/script/dom/webgluniformlocation.rs
@@ -28,10 +28,7 @@ impl WebGLUniformLocation {
reflect_dom_object(
box WebGLUniformLocation::new_inherited(id, program_id), global, WebGLUniformLocationBinding::Wrap)
}
-}
-
-impl WebGLUniformLocation {
pub fn id(&self) -> i32 {
self.id
}
diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl
index 6c36844f313..97cd853417f 100644
--- a/components/script/dom/webidls/WebGLRenderingContext.webidl
+++ b/components/script/dom/webidls/WebGLRenderingContext.webidl
@@ -632,6 +632,10 @@ interface WebGLRenderingContextBase
//void texImage2D(GLenum target, GLint level, GLenum internalformat,
// GLsizei width, GLsizei height, GLint border, GLenum format,
// GLenum type, ArrayBufferView? pixels);
+ // FIXME: SM interface arguments
+ void texImage2D(GLenum target, GLint level, GLenum internalformat,
+ GLsizei width, GLsizei height, GLint border, GLenum format,
+ GLenum type, optional object data);
void texImage2D(GLenum target, GLint level, GLenum internalformat,
GLenum format, GLenum type, TexImageSource? source); // May throw DOMException